From ef49bbb786515369f27e449605bba7b804137fb0 Mon Sep 17 00:00:00 2001 From: mm Date: Tue, 4 Jun 2019 10:36:26 +0000 Subject: [PATCH] MFC r347990: Sync libarchive with vendor. Relevant vendor changes: Issue #795: XAR - do not try to add xattrs without an allocated name PR #812: non-recursive option for extract and list PR #958: support reading metadata from compressed files PR #999: add --exclude-vcs option to bsdtar Issue #1062: treat empty archives with a GNU volume header as valid PR #1074: Handle ZIP files with trailing 0s in the extra fields (Android APK archives) PR #1109: Ignore padding in Zip extra field data (Android APK archives) PR #1167: fix problems related to unreadable directories Issue #1168: fix handling of strtol() and strtoul() PR #1172: RAR5 - fix invalid window buffer read in E8E9 filter PR #1174: ZIP reader - fix of MSZIP signature parsing PR #1175: gzip filter - fix reading files larger than 4GB from memory PR #1177: gzip filter - fix memory leak with repeated header reads PR #1180: ZIP reader - add support for Info-ZIP Unicode Path Extra Field PR #1181: RAR5 - fix merge_block() recursion (OSS-Fuzz 12999, 13029, 13144, 13478, 13490) PR #1183: fix memory leak when decompressing ZIP files with LZMA PR #1184: fix RAR5 OSS-Fuzz issues 12466, 14490, 14491, 12817 OSS-Fuzz 12466: RAR5 - fix buffer overflow when parsing huffman tables OSS-Fuzz 14490, 14491: RAR5 - fix bad shift-left operations OSS-Fuzz 12817: RAR5 - handle a case with truncated huffman tables PR #1186: RAR5 - fix invalid type used for dictionary size mask (OSS-Fuzz 14537) PR #1187: RAR5 - fix integer overflow (OSS-Fuzz 14555) PR #1190: RAR5 - RAR5 don't try to unpack entries marked as directories (OSS-Fuzz 14574) PR #1196: RAR5 - fix a potential SIGSEGV on 32-bit builds OSS-Fuzz 2582: RAR - fix use after free if there is an invalid entry OSS-Fuzz 14331: RAR5 - fix maximum owner name length OSS-Fuzz 13965: RAR5 - use unsigned int for volume number + range check Additional RAR5 reader changes: - support symlinks, hardlinks, file owner, file group, versioned files - change ARCHIVE_FORMAT_RAR_V5 to 0x100000 - set correct mode for readonly directories - support readonly, hidden and system Windows file attributes MFC r347999: Install missing data file for lib.libarchive.functional_test.test_read_format_zip_utf8_paths git-svn-id: svn://svn.freebsd.org/base/stable/10@348608 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- contrib/libarchive/NEWS | 10 + contrib/libarchive/cpio/test/test_basic.c | 4 +- .../libarchive/cpio/test/test_format_newc.c | 7 +- .../libarchive/cpio/test/test_gcpio_compat.c | 2 +- .../cpio/test/test_option_L_upper.c | 14 +- contrib/libarchive/cpio/test/test_option_a.c | 5 + contrib/libarchive/cpio/test/test_option_c.c | 2 +- contrib/libarchive/libarchive/archive.h | 4 +- contrib/libarchive/libarchive/archive_entry.c | 30 + contrib/libarchive/libarchive/archive_entry.h | 10 +- .../libarchive/archive_entry_misc.3 | 62 + .../libarchive/archive_entry_private.h | 3 + contrib/libarchive/libarchive/archive_hmac.c | 1 + contrib/libarchive/libarchive/archive_match.c | 33 +- .../libarchive/libarchive/archive_platform.h | 2 + contrib/libarchive/libarchive/archive_read.c | 9 + .../archive_read_disk_entry_from_file.c | 2 +- .../libarchive/archive_read_disk_posix.c | 39 +- .../libarchive/archive_read_private.h | 3 + .../libarchive/archive_read_set_format.c | 3 + .../archive_read_support_filter_gzip.c | 52 +- .../archive_read_support_format_cab.c | 4 +- .../archive_read_support_format_mtree.c | 15 +- .../archive_read_support_format_rar.c | 4 +- .../archive_read_support_format_rar5.c | 5931 +++++++++-------- .../archive_read_support_format_raw.c | 4 +- .../archive_read_support_format_tar.c | 29 +- .../archive_read_support_format_warc.c | 3 +- .../archive_read_support_format_xar.c | 17 +- .../archive_read_support_format_zip.c | 300 +- contrib/libarchive/libarchive/archive_util.c | 2 +- .../libarchive/archive_write_add_filter_xz.c | 7 +- .../libarchive/archive_write_disk_posix.c | 10 +- .../libarchive/archive_write_set_format_pax.c | 15 + .../libarchive/archive_write_set_format_xar.c | 7 +- .../libarchive/libarchive/test/test_entry.c | 37 +- .../libarchive/libarchive/test/test_fuzz.c | 8 + .../test_read_disk_directory_traversals.c | 299 +- .../libarchive/test/test_read_extract.c | 2 +- .../libarchive/test/test_read_format_mtree.c | 24 + .../test_read_format_mtree_noprint.mtree.uu | 4 + .../libarchive/test/test_read_format_rar.c | 39 + .../libarchive/test/test_read_format_rar5.c | 1590 +++-- ..._read_format_rar5_distance_overflow.rar.uu | 9 + ...ead_format_rar5_extra_field_version.rar.uu | 10 + .../test_read_format_rar5_fileattr.rar.uu | 13 + .../test_read_format_rar5_hardlink.rar.uu | 6 + ..._format_rar5_invalid_dict_reference.rar.uu | 9 + .../test_read_format_rar5_leftshift1.rar.uu | 9 + .../test_read_format_rar5_leftshift2.rar.uu | 6 + ...ead_format_rar5_nonempty_dir_stream.rar.uu | 9 + .../test/test_read_format_rar5_owner.rar.uu | 8 + ...ead_format_rar5_readtables_overflow.rar.uu | 15 + .../test/test_read_format_rar5_symlink.rar.uu | 8 + ...est_read_format_rar5_truncated_huff.rar.uu | 7 + .../test/test_read_format_rar5_win32.rar.uu | 131 +- ...read_format_rar_ppmd_use_after_free.rar.uu | 10 + .../libarchive/test/test_read_format_raw.c | 27 + .../test/test_read_format_raw.data.gz.uu | 4 + ...test_read_format_tar_empty_with_gnulabel.c | 53 + ...read_format_tar_empty_with_gnulabel.tar.uu | 231 + .../libarchive/test/test_read_format_zip.c | 165 +- .../test_read_format_zip_7075_utf8_paths.c | 102 + ...est_read_format_zip_7075_utf8_paths.zip.uu | 20 + .../test/test_read_format_zip_extra_padding.c | 93 + .../test_read_format_zip_extra_padding.zip.uu | 7 + ...st_read_format_zip_lzma_alone_leak.zipx.uu | 5 + .../libarchive/test/test_sparse_basic.c | 81 +- .../libarchive/test/test_write_disk_symlink.c | 156 +- contrib/libarchive/tar/bsdtar.1 | 15 +- contrib/libarchive/tar/bsdtar.c | 43 +- contrib/libarchive/tar/bsdtar.h | 1 + contrib/libarchive/tar/cmdline.c | 1 + contrib/libarchive/tar/test/test_basic.c | 4 +- contrib/libarchive/tar/test/test_copy.c | 4 +- .../libarchive/tar/test/test_option_C_mtree.c | 18 +- .../libarchive/tar/test/test_option_H_upper.c | 36 +- .../libarchive/tar/test/test_option_L_upper.c | 28 +- .../libarchive/tar/test/test_option_U_upper.c | 18 +- .../tar/test/test_option_exclude_vcs.c | 230 + contrib/libarchive/tar/test/test_option_n.c | 81 + contrib/libarchive/tar/test/test_option_s.c | 18 +- .../tar/test/test_strip_components.c | 11 +- .../libarchive/tar/test/test_symlink_dir.c | 26 +- contrib/libarchive/test_utils/test_common.h | 14 +- contrib/libarchive/test_utils/test_main.c | 278 +- lib/libarchive/Makefile | 1 + lib/libarchive/tests/Makefile | 32 +- usr.bin/tar/tests/Makefile | 1 + 89 files changed, 7059 insertions(+), 3643 deletions(-) create mode 100644 contrib/libarchive/libarchive/archive_entry_misc.3 create mode 100644 contrib/libarchive/libarchive/test/test_read_format_mtree_noprint.mtree.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_extra_field_version.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_fileattr.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_hardlink.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift1.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift2.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_owner.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_symlink.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar5_truncated_huff.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_raw.data.gz.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_tar_empty_with_gnulabel.c create mode 100644 contrib/libarchive/libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_zip_7075_utf8_paths.c create mode 100644 contrib/libarchive/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.c create mode 100644 contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.zip.uu create mode 100644 contrib/libarchive/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu create mode 100644 contrib/libarchive/tar/test/test_option_exclude_vcs.c diff --git a/contrib/libarchive/NEWS b/contrib/libarchive/NEWS index 5d782e6fc..e2d96aebd 100644 --- a/contrib/libarchive/NEWS +++ b/contrib/libarchive/NEWS @@ -1,3 +1,13 @@ +Apr 16, 2019: Support for non-recursive list and extract + +Apr 14, 2019: New tar option: --exclude-vcs + +Mar 27, 2019: Support for file and directory symlinks on Windows + +Mar 12, 2019: Important fixes for storing file attributes and flags + +Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 compression in zip archives + Oct 06, 2018: RAR 5.0 reader Sep 03, 2018: libarchive 3.3.3 released diff --git a/contrib/libarchive/cpio/test/test_basic.c b/contrib/libarchive/cpio/test/test_basic.c index afee337d1..1c2447856 100644 --- a/contrib/libarchive/cpio/test/test_basic.c +++ b/contrib/libarchive/cpio/test/test_basic.c @@ -46,7 +46,7 @@ verify_files(const char *msg) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* Another file with 1 link and different permissions. */ failure(msg); @@ -173,7 +173,7 @@ DEFINE_TEST(test_basic) /* Symlink to above file. */ if (canSymlink()) { - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); if (is_LargeInode("symlink")) { strncat(result, diff --git a/contrib/libarchive/cpio/test/test_format_newc.c b/contrib/libarchive/cpio/test/test_format_newc.c index 8ef5657b4..c9d6f1d21 100644 --- a/contrib/libarchive/cpio/test/test_format_newc.c +++ b/contrib/libarchive/cpio/test/test_format_newc.c @@ -114,7 +114,7 @@ DEFINE_TEST(test_format_newc) /* "symlink" */ if (canSymlink()) { - assertMakeSymlink("symlink", "file1"); + assertMakeSymlink("symlink", "file1", 0); fprintf(list, "symlink\n"); } @@ -233,7 +233,12 @@ DEFINE_TEST(test_format_newc) assert(is_hex(e, 110)); assertEqualMem(e + 0, "070701", 6); /* Magic */ assert(is_hex(e + 6, 8)); /* ino */ +#if defined(_WIN32) && !defined(CYGWIN) + /* Mode: Group members bits and others bits do not work. */ + assertEqualInt(0xa180, from_hex(e + 14, 8) & 0xffc0); +#else assertEqualInt(0xa1ff, from_hex(e + 14, 8)); /* Mode */ +#endif assertEqualInt(from_hex(e + 22, 8), uid); /* uid */ assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */ assertEqualMem(e + 38, "00000001", 8); /* nlink */ diff --git a/contrib/libarchive/cpio/test/test_gcpio_compat.c b/contrib/libarchive/cpio/test/test_gcpio_compat.c index 3d992c488..0a6894fa8 100644 --- a/contrib/libarchive/cpio/test/test_gcpio_compat.c +++ b/contrib/libarchive/cpio/test/test_gcpio_compat.c @@ -71,7 +71,7 @@ unpack_test(const char *from, const char *options, const char *se) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ assertIsDir("dir", 0775); diff --git a/contrib/libarchive/cpio/test/test_option_L_upper.c b/contrib/libarchive/cpio/test/test_option_L_upper.c index 0acc100ec..1b0ae7c3e 100644 --- a/contrib/libarchive/cpio/test/test_option_L_upper.c +++ b/contrib/libarchive/cpio/test/test_option_L_upper.c @@ -30,8 +30,10 @@ __FBSDID("$FreeBSD$"); * tests won't run on Windows. */ #if defined(_WIN32) && !defined(__CYGWIN__) #define CAT "type" +#define SEP "\\" #else #define CAT "cat" +#define SEP "/" #endif DEFINE_TEST(test_option_L_upper) @@ -51,7 +53,7 @@ DEFINE_TEST(test_option_L_upper) fprintf(filelist, "file\n"); /* Symlink to above file. */ - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); fclose(filelist); @@ -61,7 +63,7 @@ DEFINE_TEST(test_option_L_upper) assertTextFileContents("1 block\n", "copy.err"); failure("Regular -p without -L should preserve symlinks."); - assertIsSymlink("copy/symlink", NULL); + assertIsSymlink("copy/symlink", NULL, 0); r = systemf(CAT " filelist | %s -pd -L copy-L >copy-L.out 2>copy-L.err", testprog); assertEqualInt(r, 0); @@ -77,13 +79,14 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("unpack", 0755); assertChdir("unpack"); - r = systemf(CAT " ../archive.out | %s -i >unpack.out 2>unpack.err", testprog); + r = systemf(CAT " .." SEP "archive.out | %s -i >unpack.out 2>unpack.err", testprog); + failure("Error invoking %s -i", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "unpack.err"); assertChdir(".."); - assertIsSymlink("unpack/symlink", NULL); + assertIsSymlink("unpack/symlink", NULL, 0); r = systemf(CAT " filelist | %s -oL >archive-L.out 2>archive-L.err", testprog); failure("Error invoking %s -oL", testprog); @@ -92,7 +95,8 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("unpack-L", 0755); assertChdir("unpack-L"); - r = systemf(CAT " ../archive-L.out | %s -i >unpack-L.out 2>unpack-L.err", testprog); + r = systemf(CAT " .." SEP "archive-L.out | %s -i >unpack-L.out 2>unpack-L.err", testprog); + failure("Error invoking %s -i < archive-L.out", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "unpack-L.err"); diff --git a/contrib/libarchive/cpio/test/test_option_a.c b/contrib/libarchive/cpio/test/test_option_a.c index 94bcc6a61..71345d62a 100644 --- a/contrib/libarchive/cpio/test/test_option_a.c +++ b/contrib/libarchive/cpio/test/test_option_a.c @@ -71,8 +71,13 @@ test_create(void) * #ifdef this section out. Most of the test below is * still valid. */ memset(×, 0, sizeof(times)); +#if defined(_WIN32) && !defined(CYGWIN) + times.actime = 86400; + times.modtime = 86400; +#else times.actime = 1; times.modtime = 3; +#endif assertEqualInt(0, utime(files[i].name, ×)); /* Record whatever atime the file ended up with. */ diff --git a/contrib/libarchive/cpio/test/test_option_c.c b/contrib/libarchive/cpio/test/test_option_c.c index fa47b7e27..013caed56 100644 --- a/contrib/libarchive/cpio/test/test_option_c.c +++ b/contrib/libarchive/cpio/test/test_option_c.c @@ -85,7 +85,7 @@ DEFINE_TEST(test_option_c) /* "symlink" */ if (canSymlink()) { - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); } diff --git a/contrib/libarchive/libarchive/archive.h b/contrib/libarchive/libarchive/archive.h index 230228355..55c9ce38d 100644 --- a/contrib/libarchive/libarchive/archive.h +++ b/contrib/libarchive/libarchive/archive.h @@ -338,9 +338,9 @@ typedef const char *archive_passphrase_callback(struct archive *, #define ARCHIVE_FORMAT_LHA 0xB0000 #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 -#define ARCHIVE_FORMAT_RAR_V5 (ARCHIVE_FORMAT_RAR | 1) #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 +#define ARCHIVE_FORMAT_RAR_V5 0x100000 /* * Codes returned by archive_read_format_capabilities(). @@ -1095,6 +1095,8 @@ __LA_DECL int archive_match_excluded(struct archive *, */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); +/* Control recursive inclusion of directory content when directory is included. Default on. */ +__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, diff --git a/contrib/libarchive/libarchive/archive_entry.c b/contrib/libarchive/libarchive/archive_entry.c index 9525105ff..fa662dd61 100644 --- a/contrib/libarchive/libarchive/archive_entry.c +++ b/contrib/libarchive/libarchive/archive_entry.c @@ -168,6 +168,7 @@ archive_entry_clear(struct archive_entry *entry) archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; memset(entry, 0, sizeof(*entry)); return entry; } @@ -202,6 +203,9 @@ archive_entry_clone(struct archive_entry *entry) entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); + /* Copy symlink type */ + entry2->ae_symlink_type = entry->ae_symlink_type; + /* Copy encryption status */ entry2->encryption = entry->encryption; @@ -253,6 +257,7 @@ archive_entry_new2(struct archive *a) if (entry == NULL) return (NULL); entry->archive = a; + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; return (entry); } @@ -675,6 +680,12 @@ archive_entry_symlink(struct archive_entry *entry) return (NULL); } +int +archive_entry_symlink_type(struct archive_entry *entry) +{ + return (entry->ae_symlink_type); +} + const char * archive_entry_symlink_utf8(struct archive_entry *entry) { @@ -1245,6 +1256,12 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) entry->ae_set &= ~AE_SET_SYMLINK; } +void +archive_entry_set_symlink_type(struct archive_entry *entry, int type) +{ + entry->ae_symlink_type = type; +} + void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { @@ -1749,6 +1766,10 @@ static const struct flag { { "nohidden", L"nohidden", UF_HIDDEN, 0}, { "nouhidden", L"nouhidden", UF_HIDDEN, 0}, #endif +#ifdef FILE_ATTRIBUTE_HIDDEN + { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0}, + { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0}, +#endif #ifdef UF_OFFLINE { "nooffline", L"nooffline", UF_OFFLINE, 0}, { "nouoffline", L"nouoffline", UF_OFFLINE, 0}, @@ -1758,6 +1779,11 @@ static const struct flag { { "nourdonly", L"nourdonly", UF_READONLY, 0}, { "noreadonly", L"noreadonly", UF_READONLY, 0}, #endif +#ifdef FILE_ATTRIBUTE_READONLY + { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0}, + { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0}, + { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0}, +#endif #ifdef UF_SPARSE { "nosparse", L"nosparse", UF_SPARSE, 0}, { "nousparse", L"nousparse", UF_SPARSE, 0}, @@ -1770,6 +1796,10 @@ static const struct flag { { "nosystem", L"nosystem", UF_SYSTEM, 0}, { "nousystem", L"nousystem", UF_SYSTEM, 0}, #endif +#ifdef FILE_ATTRIBUTE_SYSTEM + { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0}, + { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0}, +#endif #if defined(FS_UNRM_FL) /* 'u' */ { "noundel", L"noundel", FS_UNRM_FL, 0}, #elif defined(EXT2_UNRM_FL) diff --git a/contrib/libarchive/libarchive/archive_entry.h b/contrib/libarchive/libarchive/archive_entry.h index 82539b351..25300e927 100644 --- a/contrib/libarchive/libarchive/archive_entry.h +++ b/contrib/libarchive/libarchive/archive_entry.h @@ -190,6 +190,13 @@ struct archive_entry; #define AE_IFDIR ((__LA_MODE_T)0040000) #define AE_IFIFO ((__LA_MODE_T)0010000) +/* + * Symlink types + */ +#define AE_SYMLINK_TYPE_UNDEFINED 0 +#define AE_SYMLINK_TYPE_FILE 1 +#define AE_SYMLINK_TYPE_DIRECTORY 2 + /* * Basic object manipulation */ @@ -275,6 +282,7 @@ __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); +__LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); @@ -350,6 +358,7 @@ __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); @@ -692,7 +701,6 @@ __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); - #ifdef __cplusplus } #endif diff --git a/contrib/libarchive/libarchive/archive_entry_misc.3 b/contrib/libarchive/libarchive/archive_entry_misc.3 new file mode 100644 index 000000000..9b1e3ea20 --- /dev/null +++ b/contrib/libarchive/libarchive/archive_entry_misc.3 @@ -0,0 +1,62 @@ +.\" Copyright (c) 2019 Martin Matuska +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 15, 2019 +.Dt ARCHIVE_ENTRY_MISC 3 +.Os +.Sh NAME +.Nm archive_entry_symlink_type , +.Nm archive_entry_set_symlink_type +.Nd miscellaneous functions for manipulating properties of archive_entry. +.Sh LIBRARY +Streaming Archive Library (libarchive, -larchive) +.Sh SYNOPSIS +.In archive_entry.h +.Ft int +.Fn archive_entry_symlink_type "struct archive_entry *a" +.Ft void +.Fn archive_entry_set_symlink_type "struct archive_entry *a" "int" +.Sh DESCRIPTION +The function +.Fn archive_entry_symlink_type +returns and the function +.Fn archive_entry_set_symlink_type +sets the type of the symbolic link stored in an archive entry. These functions +have special meaning on operating systems that support multiple symbolic link +types (e.g. Microsoft Windows). +.Pp +Supported values are: +.Bl -tag -width "AE_SYMLINK_TYPE_DIRECTORY" -compact +.It AE_SYMLINK_TYPE_UNDEFINED +Symbolic link target type is not defined (default on unix systems) +.It AE_SYMLINK_TYPE_FILE +Symbolic link points to a file +.It AE_SYMLINK_TYPE_DIRECTORY +Symbolic link points to a directory +.El +.Sh SEE ALSO +.Xr archive_entry 3 , +.Xr archive_entry_paths 3 , +.Xr archive_entry_stat 3 , +.Xr libarchive 3 diff --git a/contrib/libarchive/libarchive/archive_entry_private.h b/contrib/libarchive/libarchive/archive_entry_private.h index 1100fb9fd..d6206a066 100644 --- a/contrib/libarchive/libarchive/archive_entry_private.h +++ b/contrib/libarchive/libarchive/archive_entry_private.h @@ -176,6 +176,9 @@ struct archive_entry { /* Miscellaneous. */ char strmode[12]; + + /* Symlink type support */ + int ae_symlink_type; }; #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/contrib/libarchive/libarchive/archive_hmac.c b/contrib/libarchive/libarchive/archive_hmac.c index f29965577..392916de5 100644 --- a/contrib/libarchive/libarchive/archive_hmac.c +++ b/contrib/libarchive/libarchive/archive_hmac.c @@ -83,6 +83,7 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { +#pragma GCC diagnostic ignored "-Wcast-qual" BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; diff --git a/contrib/libarchive/libarchive/archive_match.c b/contrib/libarchive/libarchive/archive_match.c index f150e8224..04747b1f6 100644 --- a/contrib/libarchive/libarchive/archive_match.c +++ b/contrib/libarchive/libarchive/archive_match.c @@ -93,6 +93,9 @@ struct archive_match { /* exclusion/inclusion set flag. */ int setflag; + /* Recursively include directory content? */ + int recursive_include; + /* * Matching filename patterns. */ @@ -223,6 +226,7 @@ archive_match_new(void) return (NULL); a->archive.magic = ARCHIVE_MATCH_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; + a->recursive_include = 1; match_list_init(&(a->inclusions)); match_list_init(&(a->exclusions)); __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs); @@ -470,6 +474,28 @@ archive_match_path_excluded(struct archive *_a, #endif } +/* + * When recursive inclusion of directory content is enabled, + * an inclusion pattern that matches a directory will also + * include everything beneath that directory. Enabled by default. + * + * For compatibility with GNU tar, exclusion patterns always + * match if a subset of the full patch matches (i.e., they are + * are not rooted at the beginning of the path) and thus there + * is no corresponding non-recursive exclusion mode. + */ +int +archive_match_set_inclusion_recursion(struct archive *_a, int enabled) +{ + struct archive_match *a; + + archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, + ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion"); + a = (struct archive_match *)_a; + a->recursive_include = enabled; + return (ARCHIVE_OK); +} + /* * Utility functions to get statistic information for inclusion patterns. */ @@ -781,7 +807,10 @@ static int match_path_inclusion(struct archive_match *a, struct match *m, int mbs, const void *pn) { - int flag = PATHMATCH_NO_ANCHOR_END; + /* Recursive operation requires only a prefix match. */ + int flag = a->recursive_include ? + PATHMATCH_NO_ANCHOR_END : + 0; int r; if (mbs) { @@ -1232,7 +1261,7 @@ set_timefilter_pathname_mbs(struct archive_match *a, int timetype, archive_set_error(&(a->archive), EINVAL, "pathname is empty"); return (ARCHIVE_FAILED); } - if (stat(path, &st) != 0) { + if (la_stat(path, &st) != 0) { archive_set_error(&(a->archive), errno, "Failed to stat()"); return (ARCHIVE_FAILED); } diff --git a/contrib/libarchive/libarchive/archive_platform.h b/contrib/libarchive/libarchive/archive_platform.h index 9f952bfd5..e7e6466b2 100644 --- a/contrib/libarchive/libarchive/archive_platform.h +++ b/contrib/libarchive/libarchive/archive_platform.h @@ -69,6 +69,8 @@ * either Windows or Posix APIs. */ #if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) #include "archive_windows.h" +#else +#define la_stat(path,stref) stat(path,stref) #endif /* diff --git a/contrib/libarchive/libarchive/archive_read.c b/contrib/libarchive/libarchive/archive_read.c index 76be14463..cf5230ef4 100644 --- a/contrib/libarchive/libarchive/archive_read.c +++ b/contrib/libarchive/libarchive/archive_read.c @@ -611,6 +611,15 @@ choose_filters(struct archive_read *a) return (ARCHIVE_FATAL); } +int +__archive_read_header(struct archive_read *a, struct archive_entry *entry) +{ + if (a->filter->read_header) + return a->filter->read_header(a->filter, entry); + else + return (ARCHIVE_OK); +} + /* * Read header of next entry. */ diff --git a/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c index 9700607cb..c89bc384e 100644 --- a/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c +++ b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c @@ -191,7 +191,7 @@ archive_read_disk_entry_from_file(struct archive *_a, } } else #endif - if (stat(path, &s) != 0) { + if (la_stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); diff --git a/contrib/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libarchive/libarchive/archive_read_disk_posix.c index 31d2429c4..c4df6c943 100644 --- a/contrib/libarchive/libarchive/archive_read_disk_posix.c +++ b/contrib/libarchive/libarchive/archive_read_disk_posix.c @@ -909,7 +909,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, } } break; - } + } } while (lst == NULL); #ifdef __APPLE__ @@ -1295,10 +1295,23 @@ archive_read_disk_descend(struct archive *_a) if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); + /* + * We must not treat the initial specified path as a physical dir, + * because if we do then we will try and ascend out of it by opening + * ".." which is (a) wrong and (b) causes spurious permissions errors + * if ".." is not readable by us. Instead, treat it as if it were a + * symlink. (This uses an extra fd, but it can only happen once at the + * top level of a traverse.) But we can't necessarily assume t->st is + * valid here (though t->lst is), which complicates the logic a + * little. + */ if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); - t->stack->flags |= isDir; + if (t->stack->parent->parent != NULL) + t->stack->flags |= isDir; + else + t->stack->flags |= isDirLink; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); @@ -2151,6 +2164,17 @@ tree_open(const char *path, int symlink_mode, int restore_time) static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { +#if defined(O_PATH) + /* Linux */ + const int o_flag = O_PATH; +#elif defined(O_SEARCH) + /* SunOS */ + const int o_flag = O_SEARCH; +#elif defined(O_EXEC) + /* FreeBSD */ + const int o_flag = O_EXEC; +#endif + t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; @@ -2172,6 +2196,15 @@ tree_reopen(struct tree *t, const char *path, int restore_time) t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); +#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC) + /* + * Most likely reason to fail opening "." is that it's not readable, + * so try again for execute. The consequences of not opening this are + * unhelpful and unnecessary errors later. + */ + if (t->initial_dir_fd < 0) + t->initial_dir_fd = open(".", o_flag | O_CLOEXEC); +#endif __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); @@ -2479,7 +2512,7 @@ tree_current_stat(struct tree *t) #else if (tree_enter_working_dir(t) != 0) return NULL; - if (stat(tree_current_access_path(t), &t->st) != 0) + if (la_stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; diff --git a/contrib/libarchive/libarchive/archive_read_private.h b/contrib/libarchive/libarchive/archive_read_private.h index be16dfc43..f29d95d62 100644 --- a/contrib/libarchive/libarchive/archive_read_private.h +++ b/contrib/libarchive/libarchive/archive_read_private.h @@ -98,6 +98,8 @@ struct archive_read_filter { int (*close)(struct archive_read_filter *self); /* Function that handles switching from reading one block to the next/prev */ int (*sswitch)(struct archive_read_filter *self, unsigned int iindex); + /* Read any header metadata if available. */ + int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry); /* My private data. */ void *data; @@ -250,6 +252,7 @@ int64_t __archive_read_seek(struct archive_read*, int64_t, int); int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int); int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); +int __archive_read_header(struct archive_read *, struct archive_entry *); int __archive_read_program(struct archive_read_filter *, const char *); void __archive_read_free_filters(struct archive_read *); struct archive_read_extract *__archive_read_get_extract(struct archive_read *); diff --git a/contrib/libarchive/libarchive/archive_read_set_format.c b/contrib/libarchive/libarchive/archive_read_set_format.c index 190f4369d..1d3e49d16 100644 --- a/contrib/libarchive/libarchive/archive_read_set_format.c +++ b/contrib/libarchive/libarchive/archive_read_set_format.c @@ -73,6 +73,9 @@ archive_read_set_format(struct archive *_a, int code) case ARCHIVE_FORMAT_RAR: strcpy(str, "rar"); break; + case ARCHIVE_FORMAT_RAR_V5: + strcpy(str, "rar5"); + break; case ARCHIVE_FORMAT_TAR: strcpy(str, "tar"); break; diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_gzip.c b/contrib/libarchive/libarchive/archive_read_support_filter_gzip.c index fa8c675de..458b6f729 100644 --- a/contrib/libarchive/libarchive/archive_read_support_filter_gzip.c +++ b/contrib/libarchive/libarchive/archive_read_support_filter_gzip.c @@ -37,6 +37,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif @@ -45,6 +48,8 @@ __FBSDID("$FreeBSD$"); #endif #include "archive.h" +#include "archive_entry.h" +#include "archive_endian.h" #include "archive_private.h" #include "archive_read_private.h" @@ -56,6 +61,8 @@ struct private_data { size_t out_block_size; int64_t total_out; unsigned long crc; + uint32_t mtime; + char *name; char eof; /* True = found end of compressed data. */ }; @@ -123,7 +130,8 @@ archive_read_support_filter_gzip(struct archive *_a) * count of bits verified, suitable for use by bidder. */ static ssize_t -peek_at_header(struct archive_read_filter *filter, int *pbits) +peek_at_header(struct archive_read_filter *filter, int *pbits, + struct private_data *state) { const unsigned char *p; ssize_t avail, len; @@ -144,7 +152,9 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) return (0); bits += 3; header_flags = p[3]; - /* Bytes 4-7 are mod time. */ + /* Bytes 4-7 are mod time in little endian. */ + if (state) + state->mtime = archive_le32dec(p + 4); /* Byte 8 is deflate flags. */ /* XXXX TODO: return deflate flags back to consume_header for use in initializing the decompressor. */ @@ -161,6 +171,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) /* Null-terminated optional filename. */ if (header_flags & 8) { + ssize_t file_start = len; do { ++len; if (avail < len) @@ -169,6 +180,12 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) if (p == NULL) return (0); } while (p[len - 1] != 0); + + if (state) { + /* Reset the name in case of repeat header reads. */ + free(state->name); + state->name = strdup((const char *)&p[file_start]); + } } /* Null-terminated optional comment. */ @@ -214,11 +231,28 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self, (void)self; /* UNUSED */ - if (peek_at_header(filter, &bits_checked)) + if (peek_at_header(filter, &bits_checked, NULL)) return (bits_checked); return (0); } +static int +gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry) +{ + struct private_data *state; + + state = (struct private_data *)self->data; + + /* A mtime of 0 is considered invalid/missing. */ + if (state->mtime != 0) + archive_entry_set_mtime(entry, state->mtime, 0); + + /* If the name is available, extract it. */ + if (state->name) + archive_entry_set_pathname(entry, state->name); + + return (ARCHIVE_OK); +} #ifndef HAVE_ZLIB_H @@ -272,6 +306,7 @@ gzip_bidder_init(struct archive_read_filter *self) self->read = gzip_filter_read; self->skip = NULL; /* not supported */ self->close = gzip_filter_close; + self->read_header = gzip_read_header; state->in_stream = 0; /* We're not actually within a stream yet. */ @@ -289,7 +324,7 @@ consume_header(struct archive_read_filter *self) state = (struct private_data *)self->data; /* If this is a real header, consume it. */ - len = peek_at_header(self->upstream, NULL); + len = peek_at_header(self->upstream, NULL, state); if (len == 0) return (ARCHIVE_EOF); __archive_read_filter_consume(self->upstream, len); @@ -374,7 +409,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; size_t decompressed; - ssize_t avail_in; + ssize_t avail_in, max_in; int ret; state = (struct private_data *)self->data; @@ -408,6 +443,12 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) "truncated gzip input"); return (ARCHIVE_FATAL); } + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (avail_in > max_in) + avail_in = max_in; state->stream.avail_in = (uInt)avail_in; /* Decompress and consume some of that data. */ @@ -469,6 +510,7 @@ gzip_filter_close(struct archive_read_filter *self) } } + free(state->name); free(state->out_block); free(state); return (ret); diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cab.c b/contrib/libarchive/libarchive/archive_read_support_format_cab.c index e5ff5a12c..a6475308e 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_cab.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_cab.c @@ -1509,8 +1509,8 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) goto nomszip; - else if (cab->stream.next_in[0] != 0x43 || - cab->stream.next_in[1] != 0x4b) + else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 || + cab->stream.next_in[1] != 0x4b)) goto nomszip; cab->stream.next_in += mszip; cab->stream.avail_in -= mszip; diff --git a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c index 7c0bbaf78..35d8a4d47 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c @@ -45,6 +45,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_CTYPE_H +#include +#endif #include "archive.h" #include "archive_entry.h" @@ -1011,7 +1014,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; - char *p; + char *p, *s; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; @@ -1025,6 +1028,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { + r = ARCHIVE_OK; len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; @@ -1045,6 +1049,15 @@ read_mtree(struct archive_read *a, struct mtree *mtree) continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; + /* Non-printable characters are not allowed */ + for (s = p;s < p + len - 1; s++) { + if (!isprint(*s)) { + r = ARCHIVE_FATAL; + break; + } + } + if (r != ARCHIVE_OK) + break; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libarchive/libarchive/archive_read_support_format_rar.c index 234522229..a32720e0c 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_rar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_rar.c @@ -1023,8 +1023,10 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, case COMPRESS_METHOD_GOOD: case COMPRESS_METHOD_BEST: ret = read_data_compressed(a, buff, size, offset); - if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) + if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) { __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); + rar->start_new_table = 1; + } break; default: diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar5.c b/contrib/libarchive/libarchive/archive_read_support_format_rar5.c index 28543c656..7c24627b1 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_rar5.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_rar5.c @@ -24,6 +24,7 @@ */ #include "archive_platform.h" +#include "archive_endian.h" #ifdef HAVE_ERRNO_H #include @@ -32,6 +33,9 @@ #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif +#ifdef HAVE_LIMITS_H +#include +#endif #include "archive.h" #ifndef HAVE_ZLIB_H @@ -77,230 +81,275 @@ static unsigned char rar5_signature[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; static const ssize_t rar5_signature_size = sizeof(rar5_signature); -/* static const size_t g_unpack_buf_chunk_size = 1024; */ static const size_t g_unpack_window_size = 0x20000; +/* These could have been static const's, but they aren't, because of + * Visual Studio. */ +#define MAX_NAME_IN_CHARS 2048 +#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS) + struct file_header { - ssize_t bytes_remaining; - ssize_t unpacked_size; - int64_t last_offset; /* Used in sanity checks. */ - int64_t last_size; /* Used in sanity checks. */ - - uint8_t solid : 1; /* Is this a solid stream? */ - uint8_t service : 1; /* Is this file a service data? */ - - /* Optional time fields. */ - uint64_t e_mtime; - uint64_t e_ctime; - uint64_t e_atime; - uint32_t e_unix_ns; - - /* Optional hash fields. */ - uint32_t stored_crc32; - uint32_t calculated_crc32; - uint8_t blake2sp[32]; - blake2sp_state b2state; - char has_blake2; + ssize_t bytes_remaining; + ssize_t unpacked_size; + int64_t last_offset; /* Used in sanity checks. */ + int64_t last_size; /* Used in sanity checks. */ + + uint8_t solid : 1; /* Is this a solid stream? */ + uint8_t service : 1; /* Is this file a service data? */ + uint8_t eof : 1; /* Did we finish unpacking the file? */ + uint8_t dir : 1; /* Is this file entry a directory? */ + + /* Optional time fields. */ + uint64_t e_mtime; + uint64_t e_ctime; + uint64_t e_atime; + uint32_t e_unix_ns; + + /* Optional hash fields. */ + uint32_t stored_crc32; + uint32_t calculated_crc32; + uint8_t blake2sp[32]; + blake2sp_state b2state; + char has_blake2; + + /* Optional redir fields */ + uint64_t redir_type; + uint64_t redir_flags; +}; + +enum EXTRA { + EX_CRYPT = 0x01, + EX_HASH = 0x02, + EX_HTIME = 0x03, + EX_VERSION = 0x04, + EX_REDIR = 0x05, + EX_UOWNER = 0x06, + EX_SUBDATA = 0x07 +}; + +#define REDIR_SYMLINK_IS_DIR 1 + +enum REDIR_TYPE { + REDIR_TYPE_NONE = 0, + REDIR_TYPE_UNIXSYMLINK = 1, + REDIR_TYPE_WINSYMLINK = 2, + REDIR_TYPE_JUNCTION = 3, + REDIR_TYPE_HARDLINK = 4, + REDIR_TYPE_FILECOPY = 5, }; +#define OWNER_USER_NAME 0x01 +#define OWNER_GROUP_NAME 0x02 +#define OWNER_USER_UID 0x04 +#define OWNER_GROUP_GID 0x08 +#define OWNER_MAXNAMELEN 256 + enum FILTER_TYPE { - FILTER_DELTA = 0, /* Generic pattern. */ - FILTER_E8 = 1, /* Intel x86 code. */ - FILTER_E8E9 = 2, /* Intel x86 code. */ - FILTER_ARM = 3, /* ARM code. */ - FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ - FILTER_RGB = 5, /* Color palette, not used in RARv5. */ - FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ - FILTER_PPM = 7, /* Predictive pattern matching, not used in RARv5. */ - FILTER_NONE = 8, + FILTER_DELTA = 0, /* Generic pattern. */ + FILTER_E8 = 1, /* Intel x86 code. */ + FILTER_E8E9 = 2, /* Intel x86 code. */ + FILTER_ARM = 3, /* ARM code. */ + FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ + FILTER_RGB = 5, /* Color palette, not used in RARv5. */ + FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ + FILTER_PPM = 7, /* Predictive pattern matching, not used in + RARv5. */ + FILTER_NONE = 8, }; struct filter_info { - int type; - int channels; - int pos_r; + int type; + int channels; + int pos_r; - int64_t block_start; - ssize_t block_length; - uint16_t width; + int64_t block_start; + ssize_t block_length; + uint16_t width; }; struct data_ready { - char used; - const uint8_t* buf; - size_t size; - int64_t offset; + char used; + const uint8_t* buf; + size_t size; + int64_t offset; }; struct cdeque { - uint16_t beg_pos; - uint16_t end_pos; - uint16_t cap_mask; - uint16_t size; - size_t* arr; + uint16_t beg_pos; + uint16_t end_pos; + uint16_t cap_mask; + uint16_t size; + size_t* arr; }; struct decode_table { - uint32_t size; - int32_t decode_len[16]; - uint32_t decode_pos[16]; - uint32_t quick_bits; - uint8_t quick_len[1 << 10]; - uint16_t quick_num[1 << 10]; - uint16_t decode_num[306]; + uint32_t size; + int32_t decode_len[16]; + uint32_t decode_pos[16]; + uint32_t quick_bits; + uint8_t quick_len[1 << 10]; + uint16_t quick_num[1 << 10]; + uint16_t decode_num[306]; }; struct comp_state { - /* Flag used to specify if unpacker needs to reinitialize the uncompression - * context. */ - uint8_t initialized : 1; - - /* Flag used when applying filters. */ - uint8_t all_filters_applied : 1; - - /* Flag used to skip file context reinitialization, used when unpacker is - * skipping through different multivolume archives. */ - uint8_t switch_multivolume : 1; - - /* Flag used to specify if unpacker has processed the whole data block or - * just a part of it. */ - uint8_t block_parsing_finished : 1; - - int notused : 4; - - int flags; /* Uncompression flags. */ - int method; /* Uncompression algorithm method. */ - int version; /* Uncompression algorithm version. */ - ssize_t window_size; /* Size of window_buf. */ - uint8_t* window_buf; /* Circular buffer used during - decompression. */ - uint8_t* filtered_buf; /* Buffer used when applying filters. */ - const uint8_t* block_buf; /* Buffer used when merging blocks. */ - size_t window_mask; /* Convinience field; window_size - 1. */ - int64_t write_ptr; /* This amount of data has been unpacked in - the window buffer. */ - int64_t last_write_ptr; /* This amount of data has been stored in - the output file. */ - int64_t last_unstore_ptr; /* Counter of bytes extracted during - unstoring. This is separate from - last_write_ptr because of how SERVICE - base blocks are handled during skipping - in solid multiarchive archives. */ - int64_t solid_offset; /* Additional offset inside the window - buffer, used in unpacking solid - archives. */ - ssize_t cur_block_size; /* Size of current data block. */ - int last_len; /* Flag used in lzss decompression. */ - - /* Decode tables used during lzss uncompression. */ + /* Flag used to specify if unpacker needs to reinitialize the + uncompression context. */ + uint8_t initialized : 1; + + /* Flag used when applying filters. */ + uint8_t all_filters_applied : 1; + + /* Flag used to skip file context reinitialization, used when unpacker + is skipping through different multivolume archives. */ + uint8_t switch_multivolume : 1; + + /* Flag used to specify if unpacker has processed the whole data block + or just a part of it. */ + uint8_t block_parsing_finished : 1; + + int notused : 4; + + int flags; /* Uncompression flags. */ + int method; /* Uncompression algorithm method. */ + int version; /* Uncompression algorithm version. */ + ssize_t window_size; /* Size of window_buf. */ + uint8_t* window_buf; /* Circular buffer used during + decompression. */ + uint8_t* filtered_buf; /* Buffer used when applying filters. */ + const uint8_t* block_buf; /* Buffer used when merging blocks. */ + size_t window_mask; /* Convenience field; window_size - 1. */ + int64_t write_ptr; /* This amount of data has been unpacked + in the window buffer. */ + int64_t last_write_ptr; /* This amount of data has been stored in + the output file. */ + int64_t last_unstore_ptr; /* Counter of bytes extracted during + unstoring. This is separate from + last_write_ptr because of how SERVICE + base blocks are handled during skipping + in solid multiarchive archives. */ + int64_t solid_offset; /* Additional offset inside the window + buffer, used in unpacking solid + archives. */ + ssize_t cur_block_size; /* Size of current data block. */ + int last_len; /* Flag used in lzss decompression. */ + + /* Decode tables used during lzss uncompression. */ #define HUFF_BC 20 - struct decode_table bd; /* huffman bit lengths */ + struct decode_table bd; /* huffman bit lengths */ #define HUFF_NC 306 - struct decode_table ld; /* literals */ + struct decode_table ld; /* literals */ #define HUFF_DC 64 - struct decode_table dd; /* distances */ + struct decode_table dd; /* distances */ #define HUFF_LDC 16 - struct decode_table ldd; /* lower bits of distances */ + struct decode_table ldd; /* lower bits of distances */ #define HUFF_RC 44 - struct decode_table rd; /* repeating distances */ + struct decode_table rd; /* repeating distances */ #define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC) - /* Circular deque for storing filters. */ - struct cdeque filters; - int64_t last_block_start; /* Used for sanity checking. */ - ssize_t last_block_length; /* Used for sanity checking. */ + /* Circular deque for storing filters. */ + struct cdeque filters; + int64_t last_block_start; /* Used for sanity checking. */ + ssize_t last_block_length; /* Used for sanity checking. */ - /* Distance cache used during lzss uncompression. */ - int dist_cache[4]; + /* Distance cache used during lzss uncompression. */ + int dist_cache[4]; - /* Data buffer stack. */ - struct data_ready dready[2]; + /* Data buffer stack. */ + struct data_ready dready[2]; }; /* Bit reader state. */ struct bit_reader { - int8_t bit_addr; /* Current bit pointer inside current byte. */ - int in_addr; /* Current byte pointer. */ + int8_t bit_addr; /* Current bit pointer inside current byte. */ + int in_addr; /* Current byte pointer. */ }; -/* RARv5 block header structure. */ +/* RARv5 block header structure. Use bf_* functions to get values from + * block_flags_u8 field. I.e. bf_byte_count, etc. */ struct compressed_block_header { - union { - struct { - uint8_t bit_size : 3; - uint8_t byte_count : 3; - uint8_t is_last_block : 1; - uint8_t is_table_present : 1; - } block_flags; - uint8_t block_flags_u8; - }; - - uint8_t block_cksum; + /* block_flags_u8 contain fields encoded in little-endian bitfield: + * + * - table present flag (shr 7, and 1), + * - last block flag (shr 6, and 1), + * - byte_count (shr 3, and 7), + * - bit_size (shr 0, and 7). + */ + uint8_t block_flags_u8; + uint8_t block_cksum; }; /* RARv5 main header structure. */ struct main_header { - /* Does the archive contain solid streams? */ - uint8_t solid : 1; + /* Does the archive contain solid streams? */ + uint8_t solid : 1; - /* If this a multi-file archive? */ - uint8_t volume : 1; - uint8_t endarc : 1; - uint8_t notused : 5; + /* If this a multi-file archive? */ + uint8_t volume : 1; + uint8_t endarc : 1; + uint8_t notused : 5; - int vol_no; + unsigned int vol_no; }; struct generic_header { - uint8_t split_after : 1; - uint8_t split_before : 1; - uint8_t padding : 6; - int size; - int last_header_id; + uint8_t split_after : 1; + uint8_t split_before : 1; + uint8_t padding : 6; + int size; + int last_header_id; }; struct multivolume { - int expected_vol_no; - uint8_t* push_buf; + unsigned int expected_vol_no; + uint8_t* push_buf; }; /* Main context structure. */ struct rar5 { - int header_initialized; - - /* Set to 1 if current file is positioned AFTER the magic value - * of the archive file. This is used in header reading functions. */ - int skipped_magic; - - /* Set to not zero if we're in skip mode (either by calling rar5_data_skip - * function or when skipping over solid streams). Set to 0 when in - * extraction mode. This is used during checksum calculation functions. */ - int skip_mode; - - /* An offset to QuickOpen list. This is not supported by this unpacker, - * becuase we're focusing on streaming interface. QuickOpen is designed - * to make things quicker for non-stream interfaces, so it's not our - * use case. */ - uint64_t qlist_offset; - - /* An offset to additional Recovery data. This is not supported by this - * unpacker. Recovery data are additional Reed-Solomon codes that could - * be used to calculate bytes that are missing in archive or are - * corrupted. */ - uint64_t rr_offset; - - /* Various context variables grouped to different structures. */ - struct generic_header generic; - struct main_header main; - struct comp_state cstate; - struct file_header file; - struct bit_reader bits; - struct multivolume vol; - - /* The header of currently processed RARv5 block. Used in main - * decompression logic loop. */ - struct compressed_block_header last_block_hdr; + int header_initialized; + + /* Set to 1 if current file is positioned AFTER the magic value + * of the archive file. This is used in header reading functions. */ + int skipped_magic; + + /* Set to not zero if we're in skip mode (either by calling + * rar5_data_skip function or when skipping over solid streams). + * Set to 0 when in * extraction mode. This is used during checksum + * calculation functions. */ + int skip_mode; + + /* Set to not zero if we're in block merging mode (i.e. when switching + * to another file in multivolume archive, last block from 1st archive + * needs to be merged with 1st block from 2nd archive). This flag + * guards against recursive use of the merging function, which doesn't + * support recursive calls. */ + int merge_mode; + + /* An offset to QuickOpen list. This is not supported by this unpacker, + * because we're focusing on streaming interface. QuickOpen is designed + * to make things quicker for non-stream interfaces, so it's not our + * use case. */ + uint64_t qlist_offset; + + /* An offset to additional Recovery data. This is not supported by this + * unpacker. Recovery data are additional Reed-Solomon codes that could + * be used to calculate bytes that are missing in archive or are + * corrupted. */ + uint64_t rr_offset; + + /* Various context variables grouped to different structures. */ + struct generic_header generic; + struct main_header main; + struct comp_state cstate; + struct file_header file; + struct bit_reader bits; + struct multivolume vol; + + /* The header of currently processed RARv5 block. Used in main + * decompression logic loop. */ + struct compressed_block_header last_block_hdr; }; /* Forward function declarations. */ @@ -308,323 +357,341 @@ struct rar5 { static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); static int push_data_ready(struct archive_read* a, struct rar5* rar, - const uint8_t* buf, size_t size, int64_t offset); + const uint8_t* buf, size_t size, int64_t offset); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { - CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, + CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, }; /* Clears the contents of this circular deque. */ static void cdeque_clear(struct cdeque* d) { - d->size = 0; - d->beg_pos = 0; - d->end_pos = 0; + d->size = 0; + d->beg_pos = 0; + d->end_pos = 0; } /* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32, * 64, 256, etc. When the user will add another item above current capacity, * the circular deque will overwrite the oldest entry. */ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { - if(d == NULL || max_capacity_power_of_2 == 0) - return CDE_PARAM; + if(d == NULL || max_capacity_power_of_2 == 0) + return CDE_PARAM; - d->cap_mask = max_capacity_power_of_2 - 1; - d->arr = NULL; + d->cap_mask = max_capacity_power_of_2 - 1; + d->arr = NULL; - if((max_capacity_power_of_2 & d->cap_mask) > 0) - return CDE_PARAM; + if((max_capacity_power_of_2 & d->cap_mask) > 0) + return CDE_PARAM; - cdeque_clear(d); - d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); + cdeque_clear(d); + d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); - return d->arr ? CDE_OK : CDE_ALLOC; + return d->arr ? CDE_OK : CDE_ALLOC; } /* Return the current size (not capacity) of circular deque `d`. */ static size_t cdeque_size(struct cdeque* d) { - return d->size; + return d->size; } /* Returns the first element of current circular deque. Note that this function * doesn't perform any bounds checking. If you need bounds checking, use * `cdeque_front()` function instead. */ static void cdeque_front_fast(struct cdeque* d, void** value) { - *value = (void*) d->arr[d->beg_pos]; + *value = (void*) d->arr[d->beg_pos]; } /* Returns the first element of current circular deque. This function * performs bounds checking. */ static int cdeque_front(struct cdeque* d, void** value) { - if(d->size > 0) { - cdeque_front_fast(d, value); - return CDE_OK; - } else - return CDE_OUT_OF_BOUNDS; + if(d->size > 0) { + cdeque_front_fast(d, value); + return CDE_OK; + } else + return CDE_OUT_OF_BOUNDS; } /* Pushes a new element into the end of this circular deque object. If current * size will exceed capacity, the oldest element will be overwritten. */ static int cdeque_push_back(struct cdeque* d, void* item) { - if(d == NULL) - return CDE_PARAM; + if(d == NULL) + return CDE_PARAM; - if(d->size == d->cap_mask + 1) - return CDE_OUT_OF_BOUNDS; + if(d->size == d->cap_mask + 1) + return CDE_OUT_OF_BOUNDS; - d->arr[d->end_pos] = (size_t) item; - d->end_pos = (d->end_pos + 1) & d->cap_mask; - d->size++; + d->arr[d->end_pos] = (size_t) item; + d->end_pos = (d->end_pos + 1) & d->cap_mask; + d->size++; - return CDE_OK; + return CDE_OK; } /* Pops a front element of this circular deque object and returns its value. * This function doesn't perform any bounds checking. */ static void cdeque_pop_front_fast(struct cdeque* d, void** value) { - *value = (void*) d->arr[d->beg_pos]; - d->beg_pos = (d->beg_pos + 1) & d->cap_mask; - d->size--; + *value = (void*) d->arr[d->beg_pos]; + d->beg_pos = (d->beg_pos + 1) & d->cap_mask; + d->size--; } -/* Pops a front element of this cicrular deque object and returns its value. +/* Pops a front element of this circular deque object and returns its value. * This function performs bounds checking. */ static int cdeque_pop_front(struct cdeque* d, void** value) { - if(!d || !value) - return CDE_PARAM; + if(!d || !value) + return CDE_PARAM; - if(d->size == 0) - return CDE_OUT_OF_BOUNDS; + if(d->size == 0) + return CDE_OUT_OF_BOUNDS; - cdeque_pop_front_fast(d, value); - return CDE_OK; + cdeque_pop_front_fast(d, value); + return CDE_OK; } -/* Convinience function to cast filter_info** to void **. */ +/* Convenience function to cast filter_info** to void **. */ static void** cdeque_filter_p(struct filter_info** f) { - return (void**) (size_t) f; + return (void**) (size_t) f; } -/* Convinience function to cast filter_info* to void *. */ +/* Convenience function to cast filter_info* to void *. */ static void* cdeque_filter(struct filter_info* f) { - return (void**) (size_t) f; + return (void**) (size_t) f; } -/* Destroys this circular deque object. Dellocates the memory of the collection - * buffer, but doesn't deallocate the memory of any pointer passed to this - * deque as a value. */ +/* Destroys this circular deque object. Deallocates the memory of the + * collection buffer, but doesn't deallocate the memory of any pointer passed + * to this deque as a value. */ static void cdeque_free(struct cdeque* d) { - if(!d) - return; + if(!d) + return; - if(!d->arr) - return; + if(!d->arr) + return; - free(d->arr); + free(d->arr); - d->arr = NULL; - d->beg_pos = -1; - d->end_pos = -1; - d->cap_mask = 0; + d->arr = NULL; + d->beg_pos = -1; + d->end_pos = -1; + d->cap_mask = 0; } -static inline struct rar5* get_context(struct archive_read* a) { - return (struct rar5*) a->format->data; +static inline +uint8_t bf_bit_size(const struct compressed_block_header* hdr) { + return hdr->block_flags_u8 & 7; } -// TODO: make sure these functions return a little endian number +static inline +uint8_t bf_byte_count(const struct compressed_block_header* hdr) { + return (hdr->block_flags_u8 >> 3) & 7; +} -/* Convinience functions used by filter implementations. */ +static inline +uint8_t bf_is_table_present(const struct compressed_block_header* hdr) { + return (hdr->block_flags_u8 >> 7) & 1; +} -static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { - uint32_t* dptr = (uint32_t*) &rar->cstate.window_buf[offset]; - // TODO: bswap if big endian - return *dptr; +static inline struct rar5* get_context(struct archive_read* a) { + return (struct rar5*) a->format->data; } -static void write_filter_data(struct rar5* rar, uint32_t offset, - uint32_t value) +/* Convenience functions used by filter implementations. */ +static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, + int64_t start, int64_t end) { - uint32_t* dptr = (uint32_t*) &rar->cstate.filtered_buf[offset]; - // TODO: bswap if big endian - *dptr = value; + if((start & mask) > (end & mask)) { + ssize_t len1 = mask + 1 - (start & mask); + ssize_t len2 = end & mask; + + memcpy(dst, &window[start & mask], len1); + memcpy(dst + len1, window, len2); + } else { + memcpy(dst, &window[start & mask], (size_t) (end - start)); + } } -static void circular_memcpy(uint8_t* dst, uint8_t* window, const int mask, - int64_t start, int64_t end) -{ - if((start & mask) > (end & mask)) { - ssize_t len1 = mask + 1 - (start & mask); - ssize_t len2 = end & mask; +static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { + uint8_t linear_buf[4]; + circular_memcpy(linear_buf, rar->cstate.window_buf, + rar->cstate.window_mask, offset, offset + 4); + return archive_le32dec(linear_buf); +} - memcpy(dst, &window[start & mask], len1); - memcpy(dst + len1, window, len2); - } else { - memcpy(dst, &window[start & mask], (size_t) (end - start)); - } +static void write_filter_data(struct rar5* rar, uint32_t offset, + uint32_t value) +{ + archive_le32enc(&rar->cstate.filtered_buf[offset], value); } /* Allocates a new filter descriptor and adds it to the filter array. */ static struct filter_info* add_new_filter(struct rar5* rar) { - struct filter_info* f = - (struct filter_info*) calloc(1, sizeof(struct filter_info)); + struct filter_info* f = + (struct filter_info*) calloc(1, sizeof(struct filter_info)); - if(!f) { - return NULL; - } + if(!f) { + return NULL; + } - cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); - return f; + cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); + return f; } static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { - int i; - ssize_t dest_pos, src_pos = 0; + int i; + ssize_t dest_pos, src_pos = 0; - for(i = 0; i < flt->channels; i++) { - uint8_t prev_byte = 0; - for(dest_pos = i; - dest_pos < flt->block_length; - dest_pos += flt->channels) - { - uint8_t byte; + for(i = 0; i < flt->channels; i++) { + uint8_t prev_byte = 0; + for(dest_pos = i; + dest_pos < flt->block_length; + dest_pos += flt->channels) + { + uint8_t byte; - byte = rar->cstate.window_buf[(rar->cstate.solid_offset + - flt->block_start + src_pos) & rar->cstate.window_mask]; + byte = rar->cstate.window_buf[ + (rar->cstate.solid_offset + flt->block_start + + src_pos) & rar->cstate.window_mask]; - prev_byte -= byte; - rar->cstate.filtered_buf[dest_pos] = prev_byte; - src_pos++; - } - } + prev_byte -= byte; + rar->cstate.filtered_buf[dest_pos] = prev_byte; + src_pos++; + } + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, - int extended) + int extended) { - const uint32_t file_size = 0x1000000; - ssize_t i; - - const int mask = (int)rar->cstate.window_mask; - circular_memcpy(rar->cstate.filtered_buf, - rar->cstate.window_buf, - mask, - rar->cstate.solid_offset + flt->block_start, - rar->cstate.solid_offset + flt->block_start + flt->block_length); - - for(i = 0; i < flt->block_length - 4;) { - uint8_t b = rar->cstate.window_buf[(rar->cstate.solid_offset + - flt->block_start + i++) & mask]; - - /* 0xE8 = x86's call (function call) - * 0xE9 = x86's jmp (unconditional jump) */ - if(b == 0xE8 || (extended && b == 0xE9)) { - - uint32_t addr; - uint32_t offset = (i + flt->block_start) % file_size; - - addr = read_filter_data(rar, (uint32_t)(rar->cstate.solid_offset + - flt->block_start + i) & rar->cstate.window_mask); - - if(addr & 0x80000000) { - if(((addr + offset) & 0x80000000) == 0) { - write_filter_data(rar, (uint32_t)i, addr + file_size); - } - } else { - if((addr - file_size) & 0x80000000) { - uint32_t naddr = addr - offset; - write_filter_data(rar, (uint32_t)i, naddr); - } - } - - i += 4; - } - } - - return ARCHIVE_OK; + const uint32_t file_size = 0x1000000; + ssize_t i; + + circular_memcpy(rar->cstate.filtered_buf, + rar->cstate.window_buf, rar->cstate.window_mask, + rar->cstate.solid_offset + flt->block_start, + rar->cstate.solid_offset + flt->block_start + flt->block_length); + + for(i = 0; i < flt->block_length - 4;) { + uint8_t b = rar->cstate.window_buf[ + (rar->cstate.solid_offset + flt->block_start + + i++) & rar->cstate.window_mask]; + + /* + * 0xE8 = x86's call (function call) + * 0xE9 = x86's jmp (unconditional jump) + */ + if(b == 0xE8 || (extended && b == 0xE9)) { + + uint32_t addr; + uint32_t offset = (i + flt->block_start) % file_size; + + addr = read_filter_data(rar, + (uint32_t)(rar->cstate.solid_offset + + flt->block_start + i) & rar->cstate.window_mask); + + if(addr & 0x80000000) { + if(((addr + offset) & 0x80000000) == 0) { + write_filter_data(rar, (uint32_t)i, + addr + file_size); + } + } else { + if((addr - file_size) & 0x80000000) { + uint32_t naddr = addr - offset; + write_filter_data(rar, (uint32_t)i, + naddr); + } + } + + i += 4; + } + } + + return ARCHIVE_OK; } static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { - ssize_t i = 0; - uint32_t offset; - const int mask = (int)rar->cstate.window_mask; + ssize_t i = 0; + uint32_t offset; - circular_memcpy(rar->cstate.filtered_buf, - rar->cstate.window_buf, - mask, - rar->cstate.solid_offset + flt->block_start, - rar->cstate.solid_offset + flt->block_start + flt->block_length); + circular_memcpy(rar->cstate.filtered_buf, + rar->cstate.window_buf, rar->cstate.window_mask, + rar->cstate.solid_offset + flt->block_start, + rar->cstate.solid_offset + flt->block_start + flt->block_length); - for(i = 0; i < flt->block_length - 3; i += 4) { - uint8_t* b = &rar->cstate.window_buf[(rar->cstate.solid_offset + - flt->block_start + i) & mask]; + for(i = 0; i < flt->block_length - 3; i += 4) { + uint8_t* b = &rar->cstate.window_buf[ + (rar->cstate.solid_offset + + flt->block_start + i) & rar->cstate.window_mask]; - if(b[3] == 0xEB) { - /* 0xEB = ARM's BL (branch + link) instruction. */ - offset = read_filter_data(rar, (rar->cstate.solid_offset + - flt->block_start + i) & mask) & 0x00ffffff; + if(b[3] == 0xEB) { + /* 0xEB = ARM's BL (branch + link) instruction. */ + offset = read_filter_data(rar, + (rar->cstate.solid_offset + flt->block_start + i) & + rar->cstate.window_mask) & 0x00ffffff; - offset -= (uint32_t) ((i + flt->block_start) / 4); - offset = (offset & 0x00ffffff) | 0xeb000000; - write_filter_data(rar, (uint32_t)i, offset); - } - } + offset -= (uint32_t) ((i + flt->block_start) / 4); + offset = (offset & 0x00ffffff) | 0xeb000000; + write_filter_data(rar, (uint32_t)i, offset); + } + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static int run_filter(struct archive_read* a, struct filter_info* flt) { - int ret; - struct rar5* rar = get_context(a); - - free(rar->cstate.filtered_buf); - - rar->cstate.filtered_buf = malloc(flt->block_length); - if(!rar->cstate.filtered_buf) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for " - "filter data."); - return ARCHIVE_FATAL; - } - - switch(flt->type) { - case FILTER_DELTA: - ret = run_delta_filter(rar, flt); - break; - - case FILTER_E8: - /* fallthrough */ - case FILTER_E8E9: - ret = run_e8e9_filter(rar, flt, flt->type == FILTER_E8E9); - break; - - case FILTER_ARM: - ret = run_arm_filter(rar, flt); - break; - - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported filter type: 0x%02x", flt->type); - return ARCHIVE_FATAL; - } - - if(ret != ARCHIVE_OK) { - /* Filter has failed. */ - return ret; - } - - if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, - flt->block_length, rar->cstate.last_write_ptr)) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Stack overflow when submitting unpacked data"); - - return ARCHIVE_FATAL; - } - - rar->cstate.last_write_ptr += flt->block_length; - return ARCHIVE_OK; + int ret; + struct rar5* rar = get_context(a); + + free(rar->cstate.filtered_buf); + + rar->cstate.filtered_buf = malloc(flt->block_length); + if(!rar->cstate.filtered_buf) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for filter data."); + return ARCHIVE_FATAL; + } + + switch(flt->type) { + case FILTER_DELTA: + ret = run_delta_filter(rar, flt); + break; + + case FILTER_E8: + /* fallthrough */ + case FILTER_E8E9: + ret = run_e8e9_filter(rar, flt, + flt->type == FILTER_E8E9); + break; + + case FILTER_ARM: + ret = run_arm_filter(rar, flt); + break; + + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported filter type: 0x%x", flt->type); + return ARCHIVE_FATAL; + } + + if(ret != ARCHIVE_OK) { + /* Filter has failed. */ + return ret; + } + + if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, + flt->block_length, rar->cstate.last_write_ptr)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Stack overflow when submitting unpacked data"); + + return ARCHIVE_FATAL; + } + + rar->cstate.last_write_ptr += flt->block_length; + return ARCHIVE_OK; } /* The `push_data` function submits the selected data range to the user. @@ -632,199 +699,205 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { * that are specified here. These arguments are pushed to the FIFO stack here, * and popped from the stack by the `use_data` function. */ static void push_data(struct archive_read* a, struct rar5* rar, - const uint8_t* buf, int64_t idx_begin, int64_t idx_end) + const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { - const int wmask = (int)rar->cstate.window_mask; - const ssize_t solid_write_ptr = (rar->cstate.solid_offset + - rar->cstate.last_write_ptr) & wmask; - - idx_begin += rar->cstate.solid_offset; - idx_end += rar->cstate.solid_offset; - - /* Check if our unpacked data is wrapped inside the window circular buffer. - * If it's not wrapped, it can be copied out by using a single memcpy, - * but when it's wrapped, we need to copy the first part with one - * memcpy, and the second part with another memcpy. */ - - if((idx_begin & wmask) > (idx_end & wmask)) { - /* The data is wrapped (begin offset sis bigger than end offset). */ - const ssize_t frag1_size = rar->cstate.window_size - (idx_begin & wmask); - const ssize_t frag2_size = idx_end & wmask; - - /* Copy the first part of the buffer first. */ - push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, - rar->cstate.last_write_ptr); - - /* Copy the second part of the buffer. */ - push_data_ready(a, rar, buf, frag2_size, - rar->cstate.last_write_ptr + frag1_size); - - rar->cstate.last_write_ptr += frag1_size + frag2_size; - } else { - /* Data is not wrapped, so we can just use one call to copy the - * data. */ - push_data_ready(a, rar, - buf + solid_write_ptr, - (idx_end - idx_begin) & wmask, - rar->cstate.last_write_ptr); - - rar->cstate.last_write_ptr += idx_end - idx_begin; - } -} - -/* Convinience function that submits the data to the user. It uses the + const uint64_t wmask = rar->cstate.window_mask; + const ssize_t solid_write_ptr = (rar->cstate.solid_offset + + rar->cstate.last_write_ptr) & wmask; + + idx_begin += rar->cstate.solid_offset; + idx_end += rar->cstate.solid_offset; + + /* Check if our unpacked data is wrapped inside the window circular + * buffer. If it's not wrapped, it can be copied out by using + * a single memcpy, but when it's wrapped, we need to copy the first + * part with one memcpy, and the second part with another memcpy. */ + + if((idx_begin & wmask) > (idx_end & wmask)) { + /* The data is wrapped (begin offset sis bigger than end + * offset). */ + const ssize_t frag1_size = rar->cstate.window_size - + (idx_begin & wmask); + const ssize_t frag2_size = idx_end & wmask; + + /* Copy the first part of the buffer first. */ + push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, + rar->cstate.last_write_ptr); + + /* Copy the second part of the buffer. */ + push_data_ready(a, rar, buf, frag2_size, + rar->cstate.last_write_ptr + frag1_size); + + rar->cstate.last_write_ptr += frag1_size + frag2_size; + } else { + /* Data is not wrapped, so we can just use one call to copy the + * data. */ + push_data_ready(a, rar, + buf + solid_write_ptr, (idx_end - idx_begin) & wmask, + rar->cstate.last_write_ptr); + + rar->cstate.last_write_ptr += idx_end - idx_begin; + } +} + +/* Convenience function that submits the data to the user. It uses the * unpack window buffer as a source location. */ static void push_window_data(struct archive_read* a, struct rar5* rar, - int64_t idx_begin, int64_t idx_end) + int64_t idx_begin, int64_t idx_end) { - push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); + push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); } static int apply_filters(struct archive_read* a) { - struct filter_info* flt; - struct rar5* rar = get_context(a); - int ret; - - rar->cstate.all_filters_applied = 0; - - /* Get the first filter that can be applied to our data. The data needs to - * be fully unpacked before the filter can be run. */ - if(CDE_OK == - cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) - { - /* Check if our unpacked data fully covers this filter's range. */ - if(rar->cstate.write_ptr > flt->block_start && - rar->cstate.write_ptr >= flt->block_start + flt->block_length) - { - /* Check if we have some data pending to be written right before - * the filter's start offset. */ - if(rar->cstate.last_write_ptr == flt->block_start) { - /* Run the filter specified by descriptor `flt`. */ - ret = run_filter(a, flt); - if(ret != ARCHIVE_OK) { - /* Filter failure, return error. */ - return ret; - } - - /* Filter descriptor won't be needed anymore after it's used, - * so remove it from the filter list and free its memory. */ - (void) cdeque_pop_front(&rar->cstate.filters, - cdeque_filter_p(&flt)); - - free(flt); - } else { - /* We can't run filters yet, dump the memory right before the - * filter. */ - push_window_data(a, rar, rar->cstate.last_write_ptr, - flt->block_start); - } - - /* Return 'filter applied or not needed' state to the caller. */ - return ARCHIVE_RETRY; - } - } - - rar->cstate.all_filters_applied = 1; - return ARCHIVE_OK; + struct filter_info* flt; + struct rar5* rar = get_context(a); + int ret; + + rar->cstate.all_filters_applied = 0; + + /* Get the first filter that can be applied to our data. The data + * needs to be fully unpacked before the filter can be run. */ + if(CDE_OK == cdeque_front(&rar->cstate.filters, + cdeque_filter_p(&flt))) { + /* Check if our unpacked data fully covers this filter's + * range. */ + if(rar->cstate.write_ptr > flt->block_start && + rar->cstate.write_ptr >= flt->block_start + + flt->block_length) { + /* Check if we have some data pending to be written + * right before the filter's start offset. */ + if(rar->cstate.last_write_ptr == flt->block_start) { + /* Run the filter specified by descriptor + * `flt`. */ + ret = run_filter(a, flt); + if(ret != ARCHIVE_OK) { + /* Filter failure, return error. */ + return ret; + } + + /* Filter descriptor won't be needed anymore + * after it's used, * so remove it from the + * filter list and free its memory. */ + (void) cdeque_pop_front(&rar->cstate.filters, + cdeque_filter_p(&flt)); + + free(flt); + } else { + /* We can't run filters yet, dump the memory + * right before the filter. */ + push_window_data(a, rar, + rar->cstate.last_write_ptr, + flt->block_start); + } + + /* Return 'filter applied or not needed' state to the + * caller. */ + return ARCHIVE_RETRY; + } + } + + rar->cstate.all_filters_applied = 1; + return ARCHIVE_OK; } static void dist_cache_push(struct rar5* rar, int value) { - int* q = rar->cstate.dist_cache; + int* q = rar->cstate.dist_cache; - q[3] = q[2]; - q[2] = q[1]; - q[1] = q[0]; - q[0] = value; + q[3] = q[2]; + q[2] = q[1]; + q[1] = q[0]; + q[0] = value; } -static int dist_cache_touch(struct rar5* rar, int index) { - int* q = rar->cstate.dist_cache; - int i, dist = q[index]; +static int dist_cache_touch(struct rar5* rar, int idx) { + int* q = rar->cstate.dist_cache; + int i, dist = q[idx]; - for(i = index; i > 0; i--) - q[i] = q[i - 1]; + for(i = idx; i > 0; i--) + q[i] = q[i - 1]; - q[0] = dist; - return dist; + q[0] = dist; + return dist; } static void free_filters(struct rar5* rar) { - struct cdeque* d = &rar->cstate.filters; + struct cdeque* d = &rar->cstate.filters; - /* Free any remaining filters. All filters should be naturally consumed by - * the unpacking function, so remaining filters after unpacking normally - * mean that unpacking wasn't successfull. But still of course we shouldn't - * leak memory in such case. */ + /* Free any remaining filters. All filters should be naturally + * consumed by the unpacking function, so remaining filters after + * unpacking normally mean that unpacking wasn't successful. + * But still of course we shouldn't leak memory in such case. */ - /* cdeque_size() is a fast operation, so we can use it as a loop - * expression. */ - while(cdeque_size(d) > 0) { - struct filter_info* f = NULL; + /* cdeque_size() is a fast operation, so we can use it as a loop + * expression. */ + while(cdeque_size(d) > 0) { + struct filter_info* f = NULL; - /* Pop_front will also decrease the collection's size. */ - if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) - free(f); - } + /* Pop_front will also decrease the collection's size. */ + if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) + free(f); + } - cdeque_clear(d); + cdeque_clear(d); - /* Also clear out the variables needed for sanity checking. */ - rar->cstate.last_block_start = 0; - rar->cstate.last_block_length = 0; + /* Also clear out the variables needed for sanity checking. */ + rar->cstate.last_block_start = 0; + rar->cstate.last_block_length = 0; } static void reset_file_context(struct rar5* rar) { - memset(&rar->file, 0, sizeof(rar->file)); - blake2sp_init(&rar->file.b2state, 32); + memset(&rar->file, 0, sizeof(rar->file)); + blake2sp_init(&rar->file.b2state, 32); + + if(rar->main.solid) { + rar->cstate.solid_offset += rar->cstate.write_ptr; + } else { + rar->cstate.solid_offset = 0; + } - if(rar->main.solid) { - rar->cstate.solid_offset += rar->cstate.write_ptr; - } else { - rar->cstate.solid_offset = 0; - } + rar->cstate.write_ptr = 0; + rar->cstate.last_write_ptr = 0; + rar->cstate.last_unstore_ptr = 0; - rar->cstate.write_ptr = 0; - rar->cstate.last_write_ptr = 0; - rar->cstate.last_unstore_ptr = 0; + rar->file.redir_type = REDIR_TYPE_NONE; + rar->file.redir_flags = 0; - free_filters(rar); + free_filters(rar); } static inline int get_archive_read(struct archive* a, - struct archive_read** ar) + struct archive_read** ar) { - *ar = (struct archive_read*) a; - archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "archive_read_support_format_rar5"); + *ar = (struct archive_read*) a; + archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_support_format_rar5"); - return ARCHIVE_OK; + return ARCHIVE_OK; } static int read_ahead(struct archive_read* a, size_t how_many, - const uint8_t** ptr) + const uint8_t** ptr) { - if(!ptr) - return 0; - - ssize_t avail = -1; - *ptr = __archive_read_ahead(a, how_many, &avail); + if(!ptr) + return 0; - if(*ptr == NULL) { - return 0; - } + ssize_t avail = -1; + *ptr = __archive_read_ahead(a, how_many, &avail); + if(*ptr == NULL) { + return 0; + } - return 1; + return 1; } static int consume(struct archive_read* a, int64_t how_many) { - int ret; + int ret; - ret = - how_many == __archive_read_consume(a, how_many) - ? ARCHIVE_OK - : ARCHIVE_FATAL; + ret = how_many == __archive_read_consume(a, how_many) + ? ARCHIVE_OK + : ARCHIVE_FATAL; - return ret; + return ret; } /** @@ -845,750 +918,1070 @@ static int consume(struct archive_read* a, int64_t how_many) { */ static int read_var(struct archive_read* a, uint64_t* pvalue, - uint64_t* pvalue_len) + uint64_t* pvalue_len) { - uint64_t result = 0; - size_t shift, i; - const uint8_t* p; - uint8_t b; - - /* We will read maximum of 8 bytes. We don't have to handle the situation - * to read the RAR5 variable-sized value stored at the end of the file, - * because such situation will never happen. */ - if(!read_ahead(a, 8, &p)) - return 0; - - for(shift = 0, i = 0; i < 8; i++, shift += 7) { - b = p[i]; - - /* Strip the MSB from the input byte and add the resulting number - * to the `result`. */ - result += (b & (uint64_t)0x7F) << shift; - - /* MSB set to 1 means we need to continue decoding process. MSB set - * to 0 means we're done. - * - * This conditional checks for the second case. */ - if((b & 0x80) == 0) { - if(pvalue) { - *pvalue = result; - } - - /* If the caller has passed the `pvalue_len` pointer, store the - * number of consumed bytes in it and do NOT consume those bytes, - * since the caller has all the information it needs to perform - * the consuming process itself. */ - if(pvalue_len) { - *pvalue_len = 1 + i; - } else { - /* If the caller did not provide the `pvalue_len` pointer, - * it will not have the possibility to advance the file - * pointer, because it will not know how many bytes it needs - * to consume. This is why we handle such situation here - * autmatically. */ - if(ARCHIVE_OK != consume(a, 1 + i)) { - return 0; - } - } - - /* End of decoding process, return success. */ - return 1; - } - } - - /* The decoded value takes the maximum number of 8 bytes. It's a maximum - * number of bytes, so end decoding process here even if the first bit - * of last byte is 1. */ - if(pvalue) { - *pvalue = result; - } - - if(pvalue_len) { - *pvalue_len = 9; - } else { - if(ARCHIVE_OK != consume(a, 9)) { - return 0; - } - } - - return 1; + uint64_t result = 0; + size_t shift, i; + const uint8_t* p; + uint8_t b; + + /* We will read maximum of 8 bytes. We don't have to handle the + * situation to read the RAR5 variable-sized value stored at the end of + * the file, because such situation will never happen. */ + if(!read_ahead(a, 8, &p)) + return 0; + + for(shift = 0, i = 0; i < 8; i++, shift += 7) { + b = p[i]; + + /* Strip the MSB from the input byte and add the resulting + * number to the `result`. */ + result += (b & (uint64_t)0x7F) << shift; + + /* MSB set to 1 means we need to continue decoding process. + * MSB set to 0 means we're done. + * + * This conditional checks for the second case. */ + if((b & 0x80) == 0) { + if(pvalue) { + *pvalue = result; + } + + /* If the caller has passed the `pvalue_len` pointer, + * store the number of consumed bytes in it and do NOT + * consume those bytes, since the caller has all the + * information it needs to perform */ + if(pvalue_len) { + *pvalue_len = 1 + i; + } else { + /* If the caller did not provide the + * `pvalue_len` pointer, it will not have the + * possibility to advance the file pointer, + * because it will not know how many bytes it + * needs to consume. This is why we handle + * such situation here automatically. */ + if(ARCHIVE_OK != consume(a, 1 + i)) { + return 0; + } + } + + /* End of decoding process, return success. */ + return 1; + } + } + + /* The decoded value takes the maximum number of 8 bytes. + * It's a maximum number of bytes, so end decoding process here + * even if the first bit of last byte is 1. */ + if(pvalue) { + *pvalue = result; + } + + if(pvalue_len) { + *pvalue_len = 9; + } else { + if(ARCHIVE_OK != consume(a, 9)) { + return 0; + } + } + + return 1; } static int read_var_sized(struct archive_read* a, size_t* pvalue, - size_t* pvalue_len) + size_t* pvalue_len) { - uint64_t v; - uint64_t v_size; + uint64_t v; + uint64_t v_size = 0; - const int ret = pvalue_len - ? read_var(a, &v, &v_size) - : read_var(a, &v, NULL); + const int ret = pvalue_len ? read_var(a, &v, &v_size) + : read_var(a, &v, NULL); - if(ret == 1 && pvalue) { - *pvalue = (size_t) v; - } + if(ret == 1 && pvalue) { + *pvalue = (size_t) v; + } - if(pvalue_len) { - /* Possible data truncation should be safe. */ - *pvalue_len = (size_t) v_size; - } + if(pvalue_len) { + /* Possible data truncation should be safe. */ + *pvalue_len = (size_t) v_size; + } - return ret; + return ret; } static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { - uint32_t bits = p[rar->bits.in_addr] << 24; - bits |= p[rar->bits.in_addr + 1] << 16; - bits |= p[rar->bits.in_addr + 2] << 8; - bits |= p[rar->bits.in_addr + 3]; - bits <<= rar->bits.bit_addr; - bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); - *value = bits; - return ARCHIVE_OK; + uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; + bits |= p[rar->bits.in_addr + 1] << 16; + bits |= p[rar->bits.in_addr + 2] << 8; + bits |= p[rar->bits.in_addr + 3]; + bits <<= rar->bits.bit_addr; + bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); + *value = bits; + return ARCHIVE_OK; } static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { - int bits = (int) p[rar->bits.in_addr] << 16; - bits |= (int) p[rar->bits.in_addr + 1] << 8; - bits |= (int) p[rar->bits.in_addr + 2]; - bits >>= (8 - rar->bits.bit_addr); - *value = bits & 0xffff; - return ARCHIVE_OK; + int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; + bits |= (int) p[rar->bits.in_addr + 1] << 8; + bits |= (int) p[rar->bits.in_addr + 2]; + bits >>= (8 - rar->bits.bit_addr); + *value = bits & 0xffff; + return ARCHIVE_OK; } static void skip_bits(struct rar5* rar, int bits) { - const int new_bits = rar->bits.bit_addr + bits; - rar->bits.in_addr += new_bits >> 3; - rar->bits.bit_addr = new_bits & 7; + const int new_bits = rar->bits.bit_addr + bits; + rar->bits.in_addr += new_bits >> 3; + rar->bits.bit_addr = new_bits & 7; } /* n = up to 16 */ static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, - int* value) + int* value) { - uint16_t v; - int ret, num; + uint16_t v; + int ret, num; - if(n == 0 || n > 16) { - /* This is a programmer error and should never happen in runtime. */ - return ARCHIVE_FATAL; - } + if(n == 0 || n > 16) { + /* This is a programmer error and should never happen + * in runtime. */ + return ARCHIVE_FATAL; + } - ret = read_bits_16(rar, p, &v); - if(ret != ARCHIVE_OK) - return ret; + ret = read_bits_16(rar, p, &v); + if(ret != ARCHIVE_OK) + return ret; - num = (int) v; - num >>= 16 - n; + num = (int) v; + num >>= 16 - n; - skip_bits(rar, n); + skip_bits(rar, n); - if(value) - *value = num; + if(value) + *value = num; - return ARCHIVE_OK; + return ARCHIVE_OK; } static int read_u32(struct archive_read* a, uint32_t* pvalue) { - const uint8_t* p; - if(!read_ahead(a, 4, &p)) - return 0; + const uint8_t* p; + if(!read_ahead(a, 4, &p)) + return 0; - *pvalue = *(const uint32_t*)p; - - return ARCHIVE_OK == consume(a, 4) ? 1 : 0; + *pvalue = archive_le32dec(p); + return ARCHIVE_OK == consume(a, 4) ? 1 : 0; } static int read_u64(struct archive_read* a, uint64_t* pvalue) { - const uint8_t* p; - if(!read_ahead(a, 8, &p)) - return 0; - - *pvalue = *(const uint64_t*)p; + const uint8_t* p; + if(!read_ahead(a, 8, &p)) + return 0; - return ARCHIVE_OK == consume(a, 8) ? 1 : 0; + *pvalue = archive_le64dec(p); + return ARCHIVE_OK == consume(a, 8) ? 1 : 0; } static int bid_standard(struct archive_read* a) { - const uint8_t* p; + const uint8_t* p; - if(!read_ahead(a, rar5_signature_size, &p)) - return -1; + if(!read_ahead(a, rar5_signature_size, &p)) + return -1; - if(!memcmp(rar5_signature, p, rar5_signature_size)) - return 30; + if(!memcmp(rar5_signature, p, rar5_signature_size)) + return 30; - return -1; + return -1; } static int rar5_bid(struct archive_read* a, int best_bid) { - int my_bid; + int my_bid; - if(best_bid > 30) - return -1; + if(best_bid > 30) + return -1; - my_bid = bid_standard(a); - if(my_bid > -1) { - return my_bid; - } + my_bid = bid_standard(a); + if(my_bid > -1) { + return my_bid; + } - return -1; + return -1; } -static int rar5_options(struct archive_read *a, const char *key, const char *val) { - (void) a; - (void) key; - (void) val; +static int rar5_options(struct archive_read *a, const char *key, + const char *val) { + (void) a; + (void) key; + (void) val; - /* No options supported in this version. Return the ARCHIVE_WARN code to - * signal the options supervisor that the unpacker didn't handle setting - * this option. */ + /* No options supported in this version. Return the ARCHIVE_WARN code + * to signal the options supervisor that the unpacker didn't handle + * setting this option. */ - return ARCHIVE_WARN; + return ARCHIVE_WARN; } static void init_header(struct archive_read* a) { - a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; - a->archive.archive_format_name = "RAR5"; + a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; + a->archive.archive_format_name = "RAR5"; } enum HEADER_FLAGS { - HFL_EXTRA_DATA = 0x0001, HFL_DATA = 0x0002, HFL_SKIP_IF_UNKNOWN = 0x0004, - HFL_SPLIT_BEFORE = 0x0008, HFL_SPLIT_AFTER = 0x0010, HFL_CHILD = 0x0020, - HFL_INHERITED = 0x0040 + HFL_EXTRA_DATA = 0x0001, + HFL_DATA = 0x0002, + HFL_SKIP_IF_UNKNOWN = 0x0004, + HFL_SPLIT_BEFORE = 0x0008, + HFL_SPLIT_AFTER = 0x0010, + HFL_CHILD = 0x0020, + HFL_INHERITED = 0x0040 }; static int process_main_locator_extra_block(struct archive_read* a, - struct rar5* rar) + struct rar5* rar) { - uint64_t locator_flags; + uint64_t locator_flags; - if(!read_var(a, &locator_flags, NULL)) { - return ARCHIVE_EOF; - } + if(!read_var(a, &locator_flags, NULL)) { + return ARCHIVE_EOF; + } - enum LOCATOR_FLAGS { - QLIST = 0x01, RECOVERY = 0x02, - }; + enum LOCATOR_FLAGS { + QLIST = 0x01, RECOVERY = 0x02, + }; - if(locator_flags & QLIST) { - if(!read_var(a, &rar->qlist_offset, NULL)) { - return ARCHIVE_EOF; - } + if(locator_flags & QLIST) { + if(!read_var(a, &rar->qlist_offset, NULL)) { + return ARCHIVE_EOF; + } - /* qlist is not used */ - } + /* qlist is not used */ + } - if(locator_flags & RECOVERY) { - if(!read_var(a, &rar->rr_offset, NULL)) { - return ARCHIVE_EOF; - } + if(locator_flags & RECOVERY) { + if(!read_var(a, &rar->rr_offset, NULL)) { + return ARCHIVE_EOF; + } - /* rr is not used */ - } + /* rr is not used */ + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, - ssize_t* extra_data_size) + ssize_t* extra_data_size) { - size_t hash_type; - size_t value_len; + size_t hash_type; + size_t value_len; - if(!read_var_sized(a, &hash_type, &value_len)) - return ARCHIVE_EOF; + if(!read_var_sized(a, &hash_type, &value_len)) + return ARCHIVE_EOF; - *extra_data_size -= value_len; - if(ARCHIVE_OK != consume(a, value_len)) { - return ARCHIVE_EOF; - } + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) { + return ARCHIVE_EOF; + } - enum HASH_TYPE { - BLAKE2sp = 0x00 - }; + enum HASH_TYPE { + BLAKE2sp = 0x00 + }; - /* The file uses BLAKE2sp checksum algorithm instead of plain old - * CRC32. */ - if(hash_type == BLAKE2sp) { - const uint8_t* p; - const int hash_size = sizeof(rar->file.blake2sp); + /* The file uses BLAKE2sp checksum algorithm instead of plain old + * CRC32. */ + if(hash_type == BLAKE2sp) { + const uint8_t* p; + const int hash_size = sizeof(rar->file.blake2sp); - if(!read_ahead(a, hash_size, &p)) - return ARCHIVE_EOF; + if(!read_ahead(a, hash_size, &p)) + return ARCHIVE_EOF; - rar->file.has_blake2 = 1; - memcpy(&rar->file.blake2sp, p, hash_size); + rar->file.has_blake2 = 1; + memcpy(&rar->file.blake2sp, p, hash_size); - if(ARCHIVE_OK != consume(a, hash_size)) { - return ARCHIVE_EOF; - } + if(ARCHIVE_OK != consume(a, hash_size)) { + return ARCHIVE_EOF; + } - *extra_data_size -= hash_size; - } else { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported hash type (0x%02x)", (int) hash_type); - return ARCHIVE_FATAL; - } + *extra_data_size -= hash_size; + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported hash type (0x%x)", (int) hash_type); + return ARCHIVE_FATAL; + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static uint64_t time_win_to_unix(uint64_t win_time) { - const size_t ns_in_sec = 10000000; - const uint64_t sec_to_unix = 11644473600LL; - return win_time / ns_in_sec - sec_to_unix; + const size_t ns_in_sec = 10000000; + const uint64_t sec_to_unix = 11644473600LL; + return win_time / ns_in_sec - sec_to_unix; } static int parse_htime_item(struct archive_read* a, char unix_time, - uint64_t* where, ssize_t* extra_data_size) + uint64_t* where, ssize_t* extra_data_size) { - if(unix_time) { - uint32_t time_val; - if(!read_u32(a, &time_val)) - return ARCHIVE_EOF; + if(unix_time) { + uint32_t time_val; + if(!read_u32(a, &time_val)) + return ARCHIVE_EOF; - *extra_data_size -= 4; - *where = (uint64_t) time_val; - } else { - uint64_t windows_time; - if(!read_u64(a, &windows_time)) - return ARCHIVE_EOF; + *extra_data_size -= 4; + *where = (uint64_t) time_val; + } else { + uint64_t windows_time; + if(!read_u64(a, &windows_time)) + return ARCHIVE_EOF; - *where = time_win_to_unix(windows_time); - *extra_data_size -= 8; - } + *where = time_win_to_unix(windows_time); + *extra_data_size -= 8; + } - return ARCHIVE_OK; + return ARCHIVE_OK; } -static int parse_file_extra_htime(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, - ssize_t* extra_data_size) +static int parse_file_extra_version(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) { - char unix_time = 0; - size_t flags; - size_t value_len; + size_t flags = 0; + size_t version = 0; + size_t value_len = 0; + struct archive_string version_string; + struct archive_string name_utf8_string; - enum HTIME_FLAGS { - IS_UNIX = 0x01, - HAS_MTIME = 0x02, - HAS_CTIME = 0x04, - HAS_ATIME = 0x08, - HAS_UNIX_NS = 0x10, - }; + /* Flags are ignored. */ + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; - if(!read_var_sized(a, &flags, &value_len)) - return ARCHIVE_EOF; + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; - *extra_data_size -= value_len; - if(ARCHIVE_OK != consume(a, value_len)) { - return ARCHIVE_EOF; - } + if(!read_var_sized(a, &version, &value_len)) + return ARCHIVE_EOF; - unix_time = flags & IS_UNIX; + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; - if(flags & HAS_MTIME) { - parse_htime_item(a, unix_time, &rar->file.e_mtime, extra_data_size); - archive_entry_set_mtime(e, rar->file.e_mtime, 0); - } + /* extra_data_size should be zero here. */ - if(flags & HAS_CTIME) { - parse_htime_item(a, unix_time, &rar->file.e_ctime, extra_data_size); - archive_entry_set_ctime(e, rar->file.e_ctime, 0); - } + const char* cur_filename = archive_entry_pathname_utf8(e); + if(cur_filename == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Version entry without file name"); + return ARCHIVE_FATAL; + } - if(flags & HAS_ATIME) { - parse_htime_item(a, unix_time, &rar->file.e_atime, extra_data_size); - archive_entry_set_atime(e, rar->file.e_atime, 0); - } + archive_string_init(&version_string); + archive_string_init(&name_utf8_string); - if(flags & HAS_UNIX_NS) { - if(!read_u32(a, &rar->file.e_unix_ns)) - return ARCHIVE_EOF; + /* Prepare a ;123 suffix for the filename, where '123' is the version + * value of this file. */ + archive_string_sprintf(&version_string, ";%zu", version); - *extra_data_size -= 4; - } + /* Build the new filename. */ + archive_strcat(&name_utf8_string, cur_filename); + archive_strcat(&name_utf8_string, version_string.s); - return ARCHIVE_OK; + /* Apply the new filename into this file's context. */ + archive_entry_update_pathname_utf8(e, name_utf8_string.s); + + /* Free buffers. */ + archive_string_free(&version_string); + archive_string_free(&name_utf8_string); + return ARCHIVE_OK; +} + +static int parse_file_extra_htime(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) +{ + char unix_time = 0; + size_t flags; + size_t value_len; + + enum HTIME_FLAGS { + IS_UNIX = 0x01, + HAS_MTIME = 0x02, + HAS_CTIME = 0x04, + HAS_ATIME = 0x08, + HAS_UNIX_NS = 0x10, + }; + + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) { + return ARCHIVE_EOF; + } + + unix_time = flags & IS_UNIX; + + if(flags & HAS_MTIME) { + parse_htime_item(a, unix_time, &rar->file.e_mtime, + extra_data_size); + archive_entry_set_mtime(e, rar->file.e_mtime, 0); + } + + if(flags & HAS_CTIME) { + parse_htime_item(a, unix_time, &rar->file.e_ctime, + extra_data_size); + archive_entry_set_ctime(e, rar->file.e_ctime, 0); + } + + if(flags & HAS_ATIME) { + parse_htime_item(a, unix_time, &rar->file.e_atime, + extra_data_size); + archive_entry_set_atime(e, rar->file.e_atime, 0); + } + + if(flags & HAS_UNIX_NS) { + if(!read_u32(a, &rar->file.e_unix_ns)) + return ARCHIVE_EOF; + + *extra_data_size -= 4; + } + + return ARCHIVE_OK; +} + +static int parse_file_extra_redir(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) +{ + uint64_t value_size = 0; + size_t target_size = 0; + char target_utf8_buf[MAX_NAME_IN_BYTES]; + const uint8_t* p; + + if(!read_var(a, &rar->file.redir_type, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var(a, &rar->file.redir_flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var_sized(a, &target_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= target_size + 1; + + if(!read_ahead(a, target_size, &p)) + return ARCHIVE_EOF; + + if(target_size > (MAX_NAME_IN_CHARS - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Link target is too long"); + return ARCHIVE_FATAL; + } + + if(target_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No link target specified"); + return ARCHIVE_FATAL; + } + + memcpy(target_utf8_buf, p, target_size); + target_utf8_buf[target_size] = 0; + + if(ARCHIVE_OK != consume(a, (int64_t)target_size)) + return ARCHIVE_EOF; + + switch(rar->file.redir_type) { + case REDIR_TYPE_UNIXSYMLINK: + case REDIR_TYPE_WINSYMLINK: + archive_entry_set_filetype(e, AE_IFLNK); + archive_entry_update_symlink_utf8(e, target_utf8_buf); + if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_DIRECTORY); + } else { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_FILE); + } + break; + + case REDIR_TYPE_HARDLINK: + archive_entry_set_filetype(e, AE_IFREG); + archive_entry_update_hardlink_utf8(e, target_utf8_buf); + break; + + default: + /* Unknown redir type, skip it. */ + break; + } + return ARCHIVE_OK; +} + +static int parse_file_extra_owner(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + uint64_t flags = 0; + uint64_t value_size = 0; + uint64_t id = 0; + size_t name_len = 0; + size_t name_size = 0; + char namebuf[OWNER_MAXNAMELEN]; + const uint8_t* p; + + if(!read_var(a, &flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if ((flags & OWNER_USER_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if (name_size >= OWNER_MAXNAMELEN) { + name_len = OWNER_MAXNAMELEN - 1; + } else { + name_len = name_size; + } + + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_uname(e, namebuf); + } + if ((flags & OWNER_GROUP_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if (name_size >= OWNER_MAXNAMELEN) { + name_len = OWNER_MAXNAMELEN - 1; + } else { + name_len = name_size; + } + + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_gname(e, namebuf); + } + if ((flags & OWNER_USER_UID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_uid(e, (la_int64_t)id); + } + if ((flags & OWNER_GROUP_GID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_gid(e, (la_int64_t)id); + } + return ARCHIVE_OK; } static int process_head_file_extra(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, - ssize_t extra_data_size) + struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) { - size_t extra_field_size; - size_t extra_field_id; - int ret = ARCHIVE_FATAL; - size_t var_size; - - enum EXTRA { - CRYPT = 0x01, HASH = 0x02, HTIME = 0x03, VERSION_ = 0x04, - REDIR = 0x05, UOWNER = 0x06, SUBDATA = 0x07 - }; - - while(extra_data_size > 0) { - if(!read_var_sized(a, &extra_field_size, &var_size)) - return ARCHIVE_EOF; - - extra_data_size -= var_size; - if(ARCHIVE_OK != consume(a, var_size)) { - return ARCHIVE_EOF; - } - - if(!read_var_sized(a, &extra_field_id, &var_size)) - return ARCHIVE_EOF; - - extra_data_size -= var_size; - if(ARCHIVE_OK != consume(a, var_size)) { - return ARCHIVE_EOF; - } - - switch(extra_field_id) { - case HASH: - ret = parse_file_extra_hash(a, rar, &extra_data_size); - break; - case HTIME: - ret = parse_file_extra_htime(a, e, rar, &extra_data_size); - break; - case CRYPT: - /* fallthrough */ - case VERSION_: - /* fallthrough */ - case REDIR: - /* fallthrough */ - case UOWNER: - /* fallthrough */ - case SUBDATA: - /* fallthrough */ - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unknown extra field in file/service block: 0x%02x", - (int) extra_field_id); - return ARCHIVE_FATAL; - } - } - - if(ret != ARCHIVE_OK) { - /* Attribute not implemented. */ - return ret; - } - - return ARCHIVE_OK; + size_t extra_field_size; + size_t extra_field_id = 0; + int ret = ARCHIVE_FATAL; + size_t var_size; + + while(extra_data_size > 0) { + if(!read_var_sized(a, &extra_field_size, &var_size)) + return ARCHIVE_EOF; + + extra_data_size -= var_size; + if(ARCHIVE_OK != consume(a, var_size)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &extra_field_id, &var_size)) + return ARCHIVE_EOF; + + extra_data_size -= var_size; + if(ARCHIVE_OK != consume(a, var_size)) { + return ARCHIVE_EOF; + } + + switch(extra_field_id) { + case EX_HASH: + ret = parse_file_extra_hash(a, rar, + &extra_data_size); + break; + case EX_HTIME: + ret = parse_file_extra_htime(a, e, rar, + &extra_data_size); + break; + case EX_REDIR: + ret = parse_file_extra_redir(a, e, rar, + &extra_data_size); + break; + case EX_UOWNER: + ret = parse_file_extra_owner(a, e, + &extra_data_size); + break; + case EX_VERSION: + ret = parse_file_extra_version(a, e, + &extra_data_size); + break; + case EX_CRYPT: + /* fallthrough */ + case EX_SUBDATA: + /* fallthrough */ + default: + /* Skip unsupported entry. */ + return consume(a, extra_data_size); + } + } + + if(ret != ARCHIVE_OK) { + /* Attribute not implemented. */ + return ret; + } + + return ARCHIVE_OK; } static int process_head_file(struct archive_read* a, struct rar5* rar, - struct archive_entry* entry, size_t block_flags) + struct archive_entry* entry, size_t block_flags) { - ssize_t extra_data_size = 0; - size_t data_size, file_flags, file_attr, compression_info, host_os, - name_size; - uint64_t unpacked_size; - uint32_t mtime = 0, crc; - int c_method = 0, c_version = 0, is_dir; - char name_utf8_buf[2048 * 4]; - const uint8_t* p; - - archive_entry_clear(entry); - - /* Do not reset file context if we're switching archives. */ - if(!rar->cstate.switch_multivolume) { - reset_file_context(rar); - } - - if(block_flags & HFL_EXTRA_DATA) { - size_t edata_size; - if(!read_var_sized(a, &edata_size, NULL)) - return ARCHIVE_EOF; - - /* Intentional type cast from unsigned to signed. */ - extra_data_size = (ssize_t) edata_size; - } - - if(block_flags & HFL_DATA) { - if(!read_var_sized(a, &data_size, NULL)) - return ARCHIVE_EOF; - - rar->file.bytes_remaining = data_size; - } else { - rar->file.bytes_remaining = 0; - - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "no data found in file/service block"); - return ARCHIVE_FATAL; - } - - enum FILE_FLAGS { - DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, - UNKNOWN_UNPACKED_SIZE = 0x0008, - }; - - enum COMP_INFO_FLAGS { - SOLID = 0x0040, - }; - - if(!read_var_sized(a, &file_flags, NULL)) - return ARCHIVE_EOF; - - if(!read_var(a, &unpacked_size, NULL)) - return ARCHIVE_EOF; - - if(file_flags & UNKNOWN_UNPACKED_SIZE) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Files with unknown unpacked size are not supported"); - return ARCHIVE_FATAL; - } - - is_dir = (int) (file_flags & DIRECTORY); - - if(!read_var_sized(a, &file_attr, NULL)) - return ARCHIVE_EOF; - - if(file_flags & UTIME) { - if(!read_u32(a, &mtime)) - return ARCHIVE_EOF; - } - - if(file_flags & CRC32) { - if(!read_u32(a, &crc)) - return ARCHIVE_EOF; - } - - if(!read_var_sized(a, &compression_info, NULL)) - return ARCHIVE_EOF; - - c_method = (int) (compression_info >> 7) & 0x7; - c_version = (int) (compression_info & 0x3f); - - rar->cstate.window_size = is_dir ? - 0 : - g_unpack_window_size << ((compression_info >> 10) & 15); - rar->cstate.method = c_method; - rar->cstate.version = c_version + 50; - - rar->file.solid = (compression_info & SOLID) > 0; - rar->file.service = 0; - - if(!read_var_sized(a, &host_os, NULL)) - return ARCHIVE_EOF; - - enum HOST_OS { - HOST_WINDOWS = 0, - HOST_UNIX = 1, - }; - - if(host_os == HOST_WINDOWS) { - /* Host OS is Windows */ - - unsigned short mode = 0660; - - if(is_dir) - mode |= AE_IFDIR; - else - mode |= AE_IFREG; - - archive_entry_set_mode(entry, mode); - } else if(host_os == HOST_UNIX) { - /* Host OS is Unix */ - archive_entry_set_mode(entry, (unsigned short) file_attr); - } else { - /* Unknown host OS */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported Host OS: 0x%02x", (int) host_os); - - return ARCHIVE_FATAL; - } - - if(!read_var_sized(a, &name_size, NULL)) - return ARCHIVE_EOF; - - if(!read_ahead(a, name_size, &p)) - return ARCHIVE_EOF; - - if(name_size > 2047) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Filename is too long"); - - return ARCHIVE_FATAL; - } - - if(name_size == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "No filename specified"); - - return ARCHIVE_FATAL; - } - - memcpy(name_utf8_buf, p, name_size); - name_utf8_buf[name_size] = 0; - if(ARCHIVE_OK != consume(a, name_size)) { - return ARCHIVE_EOF; - } - - if(extra_data_size > 0) { - int ret = process_head_file_extra(a, entry, rar, extra_data_size); - - /* Sanity check. */ - if(extra_data_size < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "File extra data size is not zero"); - return ARCHIVE_FATAL; - } - - if(ret != ARCHIVE_OK) - return ret; - } - - if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { - rar->file.unpacked_size = (ssize_t) unpacked_size; - archive_entry_set_size(entry, unpacked_size); - } - - if(file_flags & UTIME) { - archive_entry_set_mtime(entry, (time_t) mtime, 0); - } - - if(file_flags & CRC32) { - rar->file.stored_crc32 = crc; - } - - archive_entry_update_pathname_utf8(entry, name_utf8_buf); - - if(!rar->cstate.switch_multivolume) { - /* Do not reinitialize unpacking state if we're switching archives. */ - rar->cstate.block_parsing_finished = 1; - rar->cstate.all_filters_applied = 1; - rar->cstate.initialized = 0; - } - - if(rar->generic.split_before > 0) { - /* If now we're standing on a header that has a 'split before' mark, - * it means we're standing on a 'continuation' file header. Signal - * the caller that if it wants to move to another file, it must call - * rar5_read_header() function again. */ - - return ARCHIVE_RETRY; - } else { - return ARCHIVE_OK; - } + ssize_t extra_data_size = 0; + size_t data_size = 0; + size_t file_flags = 0; + size_t file_attr = 0; + size_t compression_info = 0; + size_t host_os = 0; + size_t name_size = 0; + uint64_t unpacked_size, window_size; + uint32_t mtime = 0, crc = 0; + int c_method = 0, c_version = 0; + char name_utf8_buf[MAX_NAME_IN_BYTES]; + const uint8_t* p; + + archive_entry_clear(entry); + + /* Do not reset file context if we're switching archives. */ + if(!rar->cstate.switch_multivolume) { + reset_file_context(rar); + } + + if(block_flags & HFL_EXTRA_DATA) { + size_t edata_size = 0; + if(!read_var_sized(a, &edata_size, NULL)) + return ARCHIVE_EOF; + + /* Intentional type cast from unsigned to signed. */ + extra_data_size = (ssize_t) edata_size; + } + + if(block_flags & HFL_DATA) { + if(!read_var_sized(a, &data_size, NULL)) + return ARCHIVE_EOF; + + rar->file.bytes_remaining = data_size; + } else { + rar->file.bytes_remaining = 0; + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "no data found in file/service block"); + return ARCHIVE_FATAL; + } + + enum FILE_FLAGS { + DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, + UNKNOWN_UNPACKED_SIZE = 0x0008, + }; + + enum FILE_ATTRS { + ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4, + ATTR_DIRECTORY = 0x10, + }; + + enum COMP_INFO_FLAGS { + SOLID = 0x0040, + }; + + if(!read_var_sized(a, &file_flags, NULL)) + return ARCHIVE_EOF; + + if(!read_var(a, &unpacked_size, NULL)) + return ARCHIVE_EOF; + + if(file_flags & UNKNOWN_UNPACKED_SIZE) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Files with unknown unpacked size are not supported"); + return ARCHIVE_FATAL; + } + + rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); + + if(!read_var_sized(a, &file_attr, NULL)) + return ARCHIVE_EOF; + + if(file_flags & UTIME) { + if(!read_u32(a, &mtime)) + return ARCHIVE_EOF; + } + + if(file_flags & CRC32) { + if(!read_u32(a, &crc)) + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &compression_info, NULL)) + return ARCHIVE_EOF; + + c_method = (int) (compression_info >> 7) & 0x7; + c_version = (int) (compression_info & 0x3f); + + /* RAR5 seems to limit the dictionary size to 64MB. */ + window_size = (rar->file.dir > 0) ? + 0 : + g_unpack_window_size << ((compression_info >> 10) & 15); + rar->cstate.method = c_method; + rar->cstate.version = c_version + 50; + + /* Check if window_size is a sane value. Also, if the file is not + * declared as a directory, disallow window_size == 0. */ + if(window_size > (64 * 1024 * 1024) || + (rar->file.dir == 0 && window_size == 0)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Declared dictionary size is not supported."); + return ARCHIVE_FATAL; + } + + /* Values up to 64M should fit into ssize_t on every + * architecture. */ + rar->cstate.window_size = (ssize_t) window_size; + + rar->file.solid = (compression_info & SOLID) > 0; + rar->file.service = 0; + + if(!read_var_sized(a, &host_os, NULL)) + return ARCHIVE_EOF; + + enum HOST_OS { + HOST_WINDOWS = 0, + HOST_UNIX = 1, + }; + + if(host_os == HOST_WINDOWS) { + /* Host OS is Windows */ + + __LA_MODE_T mode; + + if(file_attr & ATTR_DIRECTORY) { + if (file_attr & ATTR_READONLY) { + mode = 0555 | AE_IFDIR; + } else { + mode = 0755 | AE_IFDIR; + } + } else { + if (file_attr & ATTR_READONLY) { + mode = 0444 | AE_IFREG; + } else { + mode = 0644 | AE_IFREG; + } + } + + archive_entry_set_mode(entry, mode); + + if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { + char *fflags_text, *ptr; + /* allocate for "rdonly,hidden,system," */ + fflags_text = malloc(22 * sizeof(char)); + if (fflags_text != NULL) { + ptr = fflags_text; + if (file_attr & ATTR_READONLY) { + strcpy(ptr, "rdonly,"); + ptr = ptr + 7; + } + if (file_attr & ATTR_HIDDEN) { + strcpy(ptr, "hidden,"); + ptr = ptr + 7; + } + if (file_attr & ATTR_SYSTEM) { + strcpy(ptr, "system,"); + ptr = ptr + 7; + } + if (ptr > fflags_text) { + /* Delete trailing comma */ + *(ptr - 1) = '\0'; + archive_entry_copy_fflags_text(entry, + fflags_text); + } + free(fflags_text); + } + } + } else if(host_os == HOST_UNIX) { + /* Host OS is Unix */ + archive_entry_set_mode(entry, (__LA_MODE_T) file_attr); + } else { + /* Unknown host OS */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported Host OS: 0x%x", (int) host_os); + + return ARCHIVE_FATAL; + } + + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + + if(name_size > (MAX_NAME_IN_CHARS - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Filename is too long"); + + return ARCHIVE_FATAL; + } + + if(name_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No filename specified"); + + return ARCHIVE_FATAL; + } + + memcpy(name_utf8_buf, p, name_size); + name_utf8_buf[name_size] = 0; + if(ARCHIVE_OK != consume(a, name_size)) { + return ARCHIVE_EOF; + } + + archive_entry_update_pathname_utf8(entry, name_utf8_buf); + + if(extra_data_size > 0) { + int ret = process_head_file_extra(a, entry, rar, + extra_data_size); + + /* Sanity check. */ + if(extra_data_size < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "File extra data size is not zero"); + return ARCHIVE_FATAL; + } + + if(ret != ARCHIVE_OK) + return ret; + } + + if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { + rar->file.unpacked_size = (ssize_t) unpacked_size; + if(rar->file.redir_type == REDIR_TYPE_NONE) + archive_entry_set_size(entry, unpacked_size); + } + + if(file_flags & UTIME) { + archive_entry_set_mtime(entry, (time_t) mtime, 0); + } + + if(file_flags & CRC32) { + rar->file.stored_crc32 = crc; + } + + if(!rar->cstate.switch_multivolume) { + /* Do not reinitialize unpacking state if we're switching + * archives. */ + rar->cstate.block_parsing_finished = 1; + rar->cstate.all_filters_applied = 1; + rar->cstate.initialized = 0; + } + + if(rar->generic.split_before > 0) { + /* If now we're standing on a header that has a 'split before' + * mark, it means we're standing on a 'continuation' file + * header. Signal the caller that if it wants to move to + * another file, it must call rar5_read_header() function + * again. */ + + return ARCHIVE_RETRY; + } else { + return ARCHIVE_OK; + } } static int process_head_service(struct archive_read* a, struct rar5* rar, - struct archive_entry* entry, size_t block_flags) + struct archive_entry* entry, size_t block_flags) { - /* Process this SERVICE block the same way as FILE blocks. */ - int ret = process_head_file(a, rar, entry, block_flags); - if(ret != ARCHIVE_OK) - return ret; + /* Process this SERVICE block the same way as FILE blocks. */ + int ret = process_head_file(a, rar, entry, block_flags); + if(ret != ARCHIVE_OK) + return ret; - rar->file.service = 1; + rar->file.service = 1; - /* But skip the data part automatically. It's no use for the user anyway. - * It contains only service data, not even needed to properly unpack the - * file. */ - ret = rar5_read_data_skip(a); - if(ret != ARCHIVE_OK) - return ret; + /* But skip the data part automatically. It's no use for the user + * anyway. It contains only service data, not even needed to + * properly unpack the file. */ + ret = rar5_read_data_skip(a); + if(ret != ARCHIVE_OK) + return ret; - /* After skipping, try parsing another block automatically. */ - return ARCHIVE_RETRY; + /* After skipping, try parsing another block automatically. */ + return ARCHIVE_RETRY; } static int process_head_main(struct archive_read* a, struct rar5* rar, - struct archive_entry* entry, size_t block_flags) + struct archive_entry* entry, size_t block_flags) { - (void) entry; - - int ret; - size_t extra_data_size, - extra_field_size, - extra_field_id, - archive_flags; - - if(block_flags & HFL_EXTRA_DATA) { - if(!read_var_sized(a, &extra_data_size, NULL)) - return ARCHIVE_EOF; - } else { - extra_data_size = 0; - } - - if(!read_var_sized(a, &archive_flags, NULL)) { - return ARCHIVE_EOF; - } - - enum MAIN_FLAGS { - VOLUME = 0x0001, /* multi-volume archive */ - VOLUME_NUMBER = 0x0002, /* volume number, first vol doesnt have it */ - SOLID = 0x0004, /* solid archive */ - PROTECT = 0x0008, /* contains Recovery info */ - LOCK = 0x0010, /* readonly flag, not used */ - }; - - rar->main.volume = (archive_flags & VOLUME) > 0; - rar->main.solid = (archive_flags & SOLID) > 0; - - if(archive_flags & VOLUME_NUMBER) { - size_t v; - if(!read_var_sized(a, &v, NULL)) { - return ARCHIVE_EOF; - } - - rar->main.vol_no = (int) v; - } else { - rar->main.vol_no = 0; - } - - if(rar->vol.expected_vol_no > 0 && - rar->main.vol_no != rar->vol.expected_vol_no) - { - /* Returning EOF instead of FATAL because of strange libarchive - * behavior. When opening multiple files via - * archive_read_open_filenames(), after reading up the whole last file, - * the __archive_read_ahead function wraps up to the first archive - * instead of returning EOF. */ - return ARCHIVE_EOF; - } - - if(extra_data_size == 0) { - /* Early return. */ - return ARCHIVE_OK; - } - - if(!read_var_sized(a, &extra_field_size, NULL)) { - return ARCHIVE_EOF; - } - - if(!read_var_sized(a, &extra_field_id, NULL)) { - return ARCHIVE_EOF; - } - - if(extra_field_size == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid extra field size"); - return ARCHIVE_FATAL; - } - - enum MAIN_EXTRA { - // Just one attribute here. - LOCATOR = 0x01, - }; - - switch(extra_field_id) { - case LOCATOR: - ret = process_main_locator_extra_block(a, rar); - if(ret != ARCHIVE_OK) { - /* Error while parsing main locator extra block. */ - return ret; - } - - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported extra type (0x%02x)", (int) extra_field_id); - return ARCHIVE_FATAL; - } - - return ARCHIVE_OK; + (void) entry; + + int ret; + size_t extra_data_size = 0; + size_t extra_field_size = 0; + size_t extra_field_id = 0; + size_t archive_flags = 0; + + if(block_flags & HFL_EXTRA_DATA) { + if(!read_var_sized(a, &extra_data_size, NULL)) + return ARCHIVE_EOF; + } else { + extra_data_size = 0; + } + + if(!read_var_sized(a, &archive_flags, NULL)) { + return ARCHIVE_EOF; + } + + enum MAIN_FLAGS { + VOLUME = 0x0001, /* multi-volume archive */ + VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't + * have it */ + SOLID = 0x0004, /* solid archive */ + PROTECT = 0x0008, /* contains Recovery info */ + LOCK = 0x0010, /* readonly flag, not used */ + }; + + rar->main.volume = (archive_flags & VOLUME) > 0; + rar->main.solid = (archive_flags & SOLID) > 0; + + if(archive_flags & VOLUME_NUMBER) { + size_t v = 0; + if(!read_var_sized(a, &v, NULL)) { + return ARCHIVE_EOF; + } + + if (v > UINT_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid volume number"); + return ARCHIVE_FATAL; + } + + rar->main.vol_no = (unsigned int) v; + } else { + rar->main.vol_no = 0; + } + + if(rar->vol.expected_vol_no > 0 && + rar->main.vol_no != rar->vol.expected_vol_no) + { + /* Returning EOF instead of FATAL because of strange + * libarchive behavior. When opening multiple files via + * archive_read_open_filenames(), after reading up the whole + * last file, the __archive_read_ahead function wraps up to + * the first archive instead of returning EOF. */ + return ARCHIVE_EOF; + } + + if(extra_data_size == 0) { + /* Early return. */ + return ARCHIVE_OK; + } + + if(!read_var_sized(a, &extra_field_size, NULL)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &extra_field_id, NULL)) { + return ARCHIVE_EOF; + } + + if(extra_field_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid extra field size"); + return ARCHIVE_FATAL; + } + + enum MAIN_EXTRA { + // Just one attribute here. + LOCATOR = 0x01, + }; + + switch(extra_field_id) { + case LOCATOR: + ret = process_main_locator_extra_block(a, rar); + if(ret != ARCHIVE_OK) { + /* Error while parsing main locator extra + * block. */ + return ret; + } + + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported extra type (0x%x)", + (int) extra_field_id); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +static int skip_unprocessed_bytes(struct archive_read* a) { + struct rar5* rar = get_context(a); + int ret; + + if(rar->file.bytes_remaining) { + /* Use different skipping method in block merging mode than in + * normal mode. If merge mode is active, rar5_read_data_skip + * can't be used, because it could allow recursive use of + * merge_block() * function, and this function doesn't support + * recursive use. */ + if(rar->merge_mode) { + /* Discard whole merged block. This is valid in solid + * mode as well, because the code will discard blocks + * only if those blocks are safe to discard (i.e. + * they're not FILE blocks). */ + ret = consume(a, rar->file.bytes_remaining); + if(ret != ARCHIVE_OK) { + return ret; + } + rar->file.bytes_remaining = 0; + } else { + /* If we're not in merge mode, use safe skipping code. + * This will ensure we'll handle solid archives + * properly. */ + ret = rar5_read_data_skip(a); + if(ret != ARCHIVE_OK) { + return ret; + } + } + } + + return ARCHIVE_OK; } static int scan_for_signature(struct archive_read* a); @@ -1639,1016 +2032,1117 @@ static int scan_for_signature(struct archive_read* a); */ static int process_base_block(struct archive_read* a, - struct archive_entry* entry) + struct archive_entry* entry) { - struct rar5* rar = get_context(a); - uint32_t hdr_crc, computed_crc; - size_t raw_hdr_size, hdr_size_len, hdr_size; - size_t header_id, header_flags; - const uint8_t* p; - int ret; - - /* Skip any unprocessed data for this file. */ - if(rar->file.bytes_remaining) { - ret = rar5_read_data_skip(a); - if(ret != ARCHIVE_OK) { - return ret; - } - } - - /* Read the expected CRC32 checksum. */ - if(!read_u32(a, &hdr_crc)) { - return ARCHIVE_EOF; - } - - /* Read header size. */ - if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { - return ARCHIVE_EOF; - } - - /* Sanity check, maximum header size for RAR5 is 2MB. */ - if(raw_hdr_size > (2 * 1024 * 1024)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Base block header is too large"); - - return ARCHIVE_FATAL; - } - - hdr_size = raw_hdr_size + hdr_size_len; - - /* Read the whole header data into memory, maximum memory use here is - * 2MB. */ - if(!read_ahead(a, hdr_size, &p)) { - return ARCHIVE_EOF; - } - - /* Verify the CRC32 of the header data. */ - computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); - if(computed_crc != hdr_crc) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Header CRC error"); - - return ARCHIVE_FATAL; - } - - /* If the checksum is OK, we proceed with parsing. */ - if(ARCHIVE_OK != consume(a, hdr_size_len)) { - return ARCHIVE_EOF; - } - - if(!read_var_sized(a, &header_id, NULL)) - return ARCHIVE_EOF; - - if(!read_var_sized(a, &header_flags, NULL)) - return ARCHIVE_EOF; - - rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; - rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; - rar->generic.size = (int)hdr_size; - rar->generic.last_header_id = (int)header_id; - rar->main.endarc = 0; - - /* Those are possible header ids in RARv5. */ - enum HEADER_TYPE { - HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, - HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, - HEAD_UNKNOWN = 0xff, - }; - - switch(header_id) { - case HEAD_MAIN: - ret = process_head_main(a, rar, entry, header_flags); - - /* Main header doesn't have any files in it, so it's pointless - * to return to the caller. Retry to next header, which should be - * HEAD_FILE/HEAD_SERVICE. */ - if(ret == ARCHIVE_OK) - return ARCHIVE_RETRY; - - return ret; - case HEAD_SERVICE: - ret = process_head_service(a, rar, entry, header_flags); - return ret; - case HEAD_FILE: - ret = process_head_file(a, rar, entry, header_flags); - return ret; - case HEAD_CRYPT: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Encryption is not supported"); - return ARCHIVE_FATAL; - case HEAD_ENDARC: - rar->main.endarc = 1; - - /* After encountering an end of file marker, we need to take - * into consideration if this archive is continued in another - * file (i.e. is it part01.rar: is there a part02.rar?) */ - if(rar->main.volume) { - /* In case there is part02.rar, position the read pointer - * in a proper place, so we can resume parsing. */ - - ret = scan_for_signature(a); - if(ret == ARCHIVE_FATAL) { - return ARCHIVE_EOF; - } else { - rar->vol.expected_vol_no = rar->main.vol_no + 1; - return ARCHIVE_OK; - } - } else { - return ARCHIVE_EOF; - } - case HEAD_MARK: - return ARCHIVE_EOF; - default: - if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Header type error"); - return ARCHIVE_FATAL; - } else { - /* If the block is marked as 'skip if unknown', do as the flag - * says: skip the block instead on failing on it. */ - return ARCHIVE_RETRY; - } - } + struct rar5* rar = get_context(a); + uint32_t hdr_crc, computed_crc; + size_t raw_hdr_size = 0, hdr_size_len, hdr_size; + size_t header_id = 0; + size_t header_flags = 0; + const uint8_t* p; + int ret; + + /* Skip any unprocessed data for this file. */ + ret = skip_unprocessed_bytes(a); + if(ret != ARCHIVE_OK) + return ret; + + /* Read the expected CRC32 checksum. */ + if(!read_u32(a, &hdr_crc)) { + return ARCHIVE_EOF; + } + + /* Read header size. */ + if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { + return ARCHIVE_EOF; + } + + /* Sanity check, maximum header size for RAR5 is 2MB. */ + if(raw_hdr_size > (2 * 1024 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Base block header is too large"); + + return ARCHIVE_FATAL; + } + + hdr_size = raw_hdr_size + hdr_size_len; + + /* Read the whole header data into memory, maximum memory use here is + * 2MB. */ + if(!read_ahead(a, hdr_size, &p)) { + return ARCHIVE_EOF; + } + + /* Verify the CRC32 of the header data. */ + computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); + if(computed_crc != hdr_crc) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Header CRC error"); + + return ARCHIVE_FATAL; + } + + /* If the checksum is OK, we proceed with parsing. */ + if(ARCHIVE_OK != consume(a, hdr_size_len)) { + return ARCHIVE_EOF; + } + + if(!read_var_sized(a, &header_id, NULL)) + return ARCHIVE_EOF; + + if(!read_var_sized(a, &header_flags, NULL)) + return ARCHIVE_EOF; + + rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; + rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; + rar->generic.size = (int)hdr_size; + rar->generic.last_header_id = (int)header_id; + rar->main.endarc = 0; + + /* Those are possible header ids in RARv5. */ + enum HEADER_TYPE { + HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, + HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, + HEAD_UNKNOWN = 0xff, + }; + + switch(header_id) { + case HEAD_MAIN: + ret = process_head_main(a, rar, entry, header_flags); + + /* Main header doesn't have any files in it, so it's + * pointless to return to the caller. Retry to next + * header, which should be HEAD_FILE/HEAD_SERVICE. */ + if(ret == ARCHIVE_OK) + return ARCHIVE_RETRY; + + return ret; + case HEAD_SERVICE: + ret = process_head_service(a, rar, entry, header_flags); + return ret; + case HEAD_FILE: + ret = process_head_file(a, rar, entry, header_flags); + return ret; + case HEAD_CRYPT: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Encryption is not supported"); + return ARCHIVE_FATAL; + case HEAD_ENDARC: + rar->main.endarc = 1; + + /* After encountering an end of file marker, we need + * to take into consideration if this archive is + * continued in another file (i.e. is it part01.rar: + * is there a part02.rar?) */ + if(rar->main.volume) { + /* In case there is part02.rar, position the + * read pointer in a proper place, so we can + * resume parsing. */ + ret = scan_for_signature(a); + if(ret == ARCHIVE_FATAL) { + return ARCHIVE_EOF; + } else { + if(rar->vol.expected_vol_no == + UINT_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Header error"); + return ARCHIVE_FATAL; + } + + rar->vol.expected_vol_no = + rar->main.vol_no + 1; + return ARCHIVE_OK; + } + } else { + return ARCHIVE_EOF; + } + case HEAD_MARK: + return ARCHIVE_EOF; + default: + if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Header type error"); + return ARCHIVE_FATAL; + } else { + /* If the block is marked as 'skip if unknown', + * do as the flag says: skip the block + * instead on failing on it. */ + return ARCHIVE_RETRY; + } + } #if !defined WIN32 - // Not reached. - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Internal unpacker error"); - return ARCHIVE_FATAL; + // Not reached. + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal unpacker error"); + return ARCHIVE_FATAL; #endif } static int skip_base_block(struct archive_read* a) { - int ret; - struct rar5* rar = get_context(a); + int ret; + struct rar5* rar = get_context(a); - /* Create a new local archive_entry structure that will be operated on - * by header reader; operations on this archive_entry will be discarded. - */ - struct archive_entry* entry = archive_entry_new(); - ret = process_base_block(a, entry); + /* Create a new local archive_entry structure that will be operated on + * by header reader; operations on this archive_entry will be discarded. + */ + struct archive_entry* entry = archive_entry_new(); + ret = process_base_block(a, entry); - /* Discard operations on this archive_entry structure. */ - archive_entry_free(entry); + /* Discard operations on this archive_entry structure. */ + archive_entry_free(entry); + if(ret == ARCHIVE_FATAL) + return ret; - if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) - return ARCHIVE_OK; + if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) + return ARCHIVE_OK; - if(ret == ARCHIVE_OK) - return ARCHIVE_RETRY; - else - return ret; + if(ret == ARCHIVE_OK) + return ARCHIVE_RETRY; + else + return ret; } static int rar5_read_header(struct archive_read *a, - struct archive_entry *entry) + struct archive_entry *entry) { - struct rar5* rar = get_context(a); - int ret; + struct rar5* rar = get_context(a); + int ret; - if(rar->header_initialized == 0) { - init_header(a); - rar->header_initialized = 1; - } + if(rar->header_initialized == 0) { + init_header(a); + rar->header_initialized = 1; + } - if(rar->skipped_magic == 0) { - if(ARCHIVE_OK != consume(a, rar5_signature_size)) { - return ARCHIVE_EOF; - } + if(rar->skipped_magic == 0) { + if(ARCHIVE_OK != consume(a, rar5_signature_size)) { + return ARCHIVE_EOF; + } - rar->skipped_magic = 1; - } + rar->skipped_magic = 1; + } - do { - ret = process_base_block(a, entry); - } while(ret == ARCHIVE_RETRY || - (rar->main.endarc > 0 && ret == ARCHIVE_OK)); + do { + ret = process_base_block(a, entry); + } while(ret == ARCHIVE_RETRY || + (rar->main.endarc > 0 && ret == ARCHIVE_OK)); - return ret; + return ret; } static void init_unpack(struct rar5* rar) { - rar->file.calculated_crc32 = 0; - if (rar->cstate.window_size) - rar->cstate.window_mask = rar->cstate.window_size - 1; - else - rar->cstate.window_mask = 0; + rar->file.calculated_crc32 = 0; + if (rar->cstate.window_size) + rar->cstate.window_mask = rar->cstate.window_size - 1; + else + rar->cstate.window_mask = 0; - free(rar->cstate.window_buf); + free(rar->cstate.window_buf); + free(rar->cstate.filtered_buf); - free(rar->cstate.filtered_buf); + if(rar->cstate.window_size > 0) { + rar->cstate.window_buf = calloc(1, rar->cstate.window_size); + rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); + } else { + rar->cstate.window_buf = NULL; + rar->cstate.filtered_buf = NULL; + } - rar->cstate.window_buf = calloc(1, rar->cstate.window_size); - rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); + rar->cstate.write_ptr = 0; + rar->cstate.last_write_ptr = 0; - rar->cstate.write_ptr = 0; - rar->cstate.last_write_ptr = 0; - - memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); - memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); - memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); - memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); - memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); + memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); + memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); + memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); + memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); + memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); } static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { int verify_crc; - if(rar->skip_mode) { + if(rar->skip_mode) { #if defined CHECK_CRC_ON_SOLID_SKIP - verify_crc = 1; + verify_crc = 1; #else - verify_crc = 0; + verify_crc = 0; #endif - } else - verify_crc = 1; - - if(verify_crc) { - /* Don't update CRC32 if the file doesn't have the `stored_crc32` info - filled in. */ - if(rar->file.stored_crc32 > 0) { - rar->file.calculated_crc32 = - crc32(rar->file.calculated_crc32, p, to_read); - } - - /* Check if the file uses an optional BLAKE2sp checksum algorithm. */ - if(rar->file.has_blake2 > 0) { - /* Return value of the `update` function is always 0, so we can - * explicitly ignore it here. */ - (void) blake2sp_update(&rar->file.b2state, p, to_read); - } - } + } else + verify_crc = 1; + + if(verify_crc) { + /* Don't update CRC32 if the file doesn't have the + * `stored_crc32` info filled in. */ + if(rar->file.stored_crc32 > 0) { + rar->file.calculated_crc32 = + crc32(rar->file.calculated_crc32, p, to_read); + } + + /* Check if the file uses an optional BLAKE2sp checksum + * algorithm. */ + if(rar->file.has_blake2 > 0) { + /* Return value of the `update` function is always 0, + * so we can explicitly ignore it here. */ + (void) blake2sp_update(&rar->file.b2state, p, to_read); + } + } } static int create_decode_tables(uint8_t* bit_length, - struct decode_table* table, - int size) + struct decode_table* table, int size) { - int code, upper_limit = 0, i, lc[16]; - uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; - ssize_t cur_len, quick_data_size; + int code, upper_limit = 0, i, lc[16]; + uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; + ssize_t cur_len, quick_data_size; - memset(&lc, 0, sizeof(lc)); - memset(table->decode_num, 0, sizeof(table->decode_num)); - table->size = size; - table->quick_bits = size == HUFF_NC ? 10 : 7; + memset(&lc, 0, sizeof(lc)); + memset(table->decode_num, 0, sizeof(table->decode_num)); + table->size = size; + table->quick_bits = size == HUFF_NC ? 10 : 7; - for(i = 0; i < size; i++) { - lc[bit_length[i] & 15]++; - } + for(i = 0; i < size; i++) { + lc[bit_length[i] & 15]++; + } - lc[0] = 0; - table->decode_pos[0] = 0; - table->decode_len[0] = 0; + lc[0] = 0; + table->decode_pos[0] = 0; + table->decode_len[0] = 0; - for(i = 1; i < 16; i++) { - upper_limit += lc[i]; + for(i = 1; i < 16; i++) { + upper_limit += lc[i]; - table->decode_len[i] = upper_limit << (16 - i); - table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; + table->decode_len[i] = upper_limit << (16 - i); + table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; - upper_limit <<= 1; - } + upper_limit <<= 1; + } - memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); + memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); - for(i = 0; i < size; i++) { - uint8_t clen = bit_length[i] & 15; - if(clen > 0) { - int last_pos = decode_pos_clone[clen]; - table->decode_num[last_pos] = i; - decode_pos_clone[clen]++; - } - } + for(i = 0; i < size; i++) { + uint8_t clen = bit_length[i] & 15; + if(clen > 0) { + int last_pos = decode_pos_clone[clen]; + table->decode_num[last_pos] = i; + decode_pos_clone[clen]++; + } + } - quick_data_size = (int64_t)1 << table->quick_bits; - cur_len = 1; - for(code = 0; code < quick_data_size; code++) { - int bit_field = code << (16 - table->quick_bits); - int dist, pos; + quick_data_size = (int64_t)1 << table->quick_bits; + cur_len = 1; + for(code = 0; code < quick_data_size; code++) { + int bit_field = code << (16 - table->quick_bits); + int dist, pos; - while(cur_len < rar5_countof(table->decode_len) && - bit_field >= table->decode_len[cur_len]) { - cur_len++; - } + while(cur_len < rar5_countof(table->decode_len) && + bit_field >= table->decode_len[cur_len]) { + cur_len++; + } - table->quick_len[code] = (uint8_t) cur_len; + table->quick_len[code] = (uint8_t) cur_len; - dist = bit_field - table->decode_len[cur_len - 1]; - dist >>= (16 - cur_len); + dist = bit_field - table->decode_len[cur_len - 1]; + dist >>= (16 - cur_len); - pos = table->decode_pos[cur_len] + dist; - if(cur_len < rar5_countof(table->decode_pos) && pos < size) { - table->quick_num[code] = table->decode_num[pos]; - } else { - table->quick_num[code] = 0; - } - } + pos = table->decode_pos[cur_len & 15] + dist; + if(cur_len < rar5_countof(table->decode_pos) && pos < size) { + table->quick_num[code] = table->decode_num[pos]; + } else { + table->quick_num[code] = 0; + } + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static int decode_number(struct archive_read* a, struct decode_table* table, - const uint8_t* p, uint16_t* num) + const uint8_t* p, uint16_t* num) { - int i, bits, dist; - uint16_t bitfield; - uint32_t pos; - struct rar5* rar = get_context(a); + int i, bits, dist; + uint16_t bitfield; + uint32_t pos; + struct rar5* rar = get_context(a); - if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { - return ARCHIVE_EOF; - } + if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { + return ARCHIVE_EOF; + } - bitfield &= 0xfffe; + bitfield &= 0xfffe; - if(bitfield < table->decode_len[table->quick_bits]) { - int code = bitfield >> (16 - table->quick_bits); - skip_bits(rar, table->quick_len[code]); - *num = table->quick_num[code]; - return ARCHIVE_OK; - } + if(bitfield < table->decode_len[table->quick_bits]) { + int code = bitfield >> (16 - table->quick_bits); + skip_bits(rar, table->quick_len[code]); + *num = table->quick_num[code]; + return ARCHIVE_OK; + } - bits = 15; + bits = 15; - for(i = table->quick_bits + 1; i < 15; i++) { - if(bitfield < table->decode_len[i]) { - bits = i; - break; - } - } + for(i = table->quick_bits + 1; i < 15; i++) { + if(bitfield < table->decode_len[i]) { + bits = i; + break; + } + } - skip_bits(rar, bits); + skip_bits(rar, bits); - dist = bitfield - table->decode_len[bits - 1]; - dist >>= (16 - bits); - pos = table->decode_pos[bits] + dist; + dist = bitfield - table->decode_len[bits - 1]; + dist >>= (16 - bits); + pos = table->decode_pos[bits] + dist; - if(pos >= table->size) - pos = 0; + if(pos >= table->size) + pos = 0; - *num = table->decode_num[pos]; - return ARCHIVE_OK; + *num = table->decode_num[pos]; + return ARCHIVE_OK; } /* Reads and parses Huffman tables from the beginning of the block. */ static int parse_tables(struct archive_read* a, struct rar5* rar, - const uint8_t* p) + const uint8_t* p) { - int ret, value, i, w, idx = 0; - uint8_t bit_length[HUFF_BC], - table[HUFF_TABLE_SIZE], - nibble_mask = 0xF0, - nibble_shift = 4; - - enum { ESCAPE = 15 }; - - /* The data for table generation is compressed using a simple RLE-like - * algorithm when storing zeroes, so we need to unpack it first. */ - for(w = 0, i = 0; w < HUFF_BC;) { - value = (p[i] & nibble_mask) >> nibble_shift; - - if(nibble_mask == 0x0F) - ++i; - - nibble_mask ^= 0xFF; - nibble_shift ^= 4; - - /* Values smaller than 15 is data, so we write it directly. Value 15 - * is a flag telling us that we need to unpack more bytes. */ - if(value == ESCAPE) { - value = (p[i] & nibble_mask) >> nibble_shift; - if(nibble_mask == 0x0F) - ++i; - nibble_mask ^= 0xFF; - nibble_shift ^= 4; - - if(value == 0) { - /* We sometimes need to write the actual value of 15, so this - * case handles that. */ - bit_length[w++] = ESCAPE; - } else { - int k; - - /* Fill zeroes. */ - for(k = 0; k < value + 2; k++) { - bit_length[w++] = 0; - } - } - } else { - bit_length[w++] = value; - } - } - - rar->bits.in_addr = i; - rar->bits.bit_addr = nibble_shift ^ 4; - - ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Decoding huffman tables failed"); - return ARCHIVE_FATAL; - } - - for(i = 0; i < HUFF_TABLE_SIZE;) { - uint16_t num; - - ret = decode_number(a, &rar->cstate.bd, p, &num); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Decoding huffman tables failed"); - return ARCHIVE_FATAL; - } - - if(num < 16) { - /* 0..15: store directly */ - table[i] = (uint8_t) num; - i++; - continue; - } - - if(num < 18) { - /* 16..17: repeat previous code */ - uint16_t n; - if(ARCHIVE_OK != read_bits_16(rar, p, &n)) - return ARCHIVE_EOF; - - if(num == 16) { - n >>= 13; - n += 3; - skip_bits(rar, 3); - } else { - n >>= 9; - n += 11; - skip_bits(rar, 7); - } - - if(i > 0) { - while(n-- > 0 && i < HUFF_TABLE_SIZE) { - table[i] = table[i - 1]; - i++; - } - } else { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unexpected error when decoding huffman tables"); - return ARCHIVE_FATAL; - } - - continue; - } - - /* other codes: fill with zeroes `n` times */ - uint16_t n; - if(ARCHIVE_OK != read_bits_16(rar, p, &n)) - return ARCHIVE_EOF; - - if(num == 18) { - n >>= 13; - n += 3; - skip_bits(rar, 3); - } else { - n >>= 9; - n += 11; - skip_bits(rar, 7); - } - - while(n-- > 0 && i < HUFF_TABLE_SIZE) - table[i++] = 0; - } - - ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to create literal table"); - return ARCHIVE_FATAL; - } - - idx += HUFF_NC; - - ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to create distance table"); - return ARCHIVE_FATAL; - } - - idx += HUFF_DC; - - ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to create lower bits of distances table"); - return ARCHIVE_FATAL; - } - - idx += HUFF_LDC; - - ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); - if(ret != ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to create repeating distances table"); - return ARCHIVE_FATAL; - } - - return ARCHIVE_OK; + int ret, value, i, w, idx = 0; + uint8_t bit_length[HUFF_BC], + table[HUFF_TABLE_SIZE], + nibble_mask = 0xF0, + nibble_shift = 4; + + enum { ESCAPE = 15 }; + + /* The data for table generation is compressed using a simple RLE-like + * algorithm when storing zeroes, so we need to unpack it first. */ + for(w = 0, i = 0; w < HUFF_BC;) { + if(i >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables"); + return ARCHIVE_FATAL; + } + + value = (p[i] & nibble_mask) >> nibble_shift; + + if(nibble_mask == 0x0F) + ++i; + + nibble_mask ^= 0xFF; + nibble_shift ^= 4; + + /* Values smaller than 15 is data, so we write it directly. + * Value 15 is a flag telling us that we need to unpack more + * bytes. */ + if(value == ESCAPE) { + value = (p[i] & nibble_mask) >> nibble_shift; + if(nibble_mask == 0x0F) + ++i; + nibble_mask ^= 0xFF; + nibble_shift ^= 4; + + if(value == 0) { + /* We sometimes need to write the actual value + * of 15, so this case handles that. */ + bit_length[w++] = ESCAPE; + } else { + int k; + + /* Fill zeroes. */ + for(k = 0; (k < value + 2) && (w < HUFF_BC); + k++) { + bit_length[w++] = 0; + } + } + } else { + bit_length[w++] = value; + } + } + + rar->bits.in_addr = i; + rar->bits.bit_addr = nibble_shift ^ 4; + + ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Decoding huffman tables failed"); + return ARCHIVE_FATAL; + } + + for(i = 0; i < HUFF_TABLE_SIZE;) { + uint16_t num; + + if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables (#2)"); + return ARCHIVE_FATAL; + } + + ret = decode_number(a, &rar->cstate.bd, p, &num); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Decoding huffman tables failed"); + return ARCHIVE_FATAL; + } + + if(num < 16) { + /* 0..15: store directly */ + table[i] = (uint8_t) num; + i++; + continue; + } + + if(num < 18) { + /* 16..17: repeat previous code */ + uint16_t n; + if(ARCHIVE_OK != read_bits_16(rar, p, &n)) + return ARCHIVE_EOF; + + if(num == 16) { + n >>= 13; + n += 3; + skip_bits(rar, 3); + } else { + n >>= 9; + n += 11; + skip_bits(rar, 7); + } + + if(i > 0) { + while(n-- > 0 && i < HUFF_TABLE_SIZE) { + table[i] = table[i - 1]; + i++; + } + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unexpected error when decoding " + "huffman tables"); + return ARCHIVE_FATAL; + } + + continue; + } + + /* other codes: fill with zeroes `n` times */ + uint16_t n; + if(ARCHIVE_OK != read_bits_16(rar, p, &n)) + return ARCHIVE_EOF; + + if(num == 18) { + n >>= 13; + n += 3; + skip_bits(rar, 3); + } else { + n >>= 9; + n += 11; + skip_bits(rar, 7); + } + + while(n-- > 0 && i < HUFF_TABLE_SIZE) + table[i++] = 0; + } + + ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create literal table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_NC; + + ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create distance table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_DC; + + ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create lower bits of distances table"); + return ARCHIVE_FATAL; + } + + idx += HUFF_LDC; + + ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); + if(ret != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Failed to create repeating distances table"); + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; } /* Parses the block header, verifies its CRC byte, and saves the header * fields inside the `hdr` pointer. */ static int parse_block_header(struct archive_read* a, const uint8_t* p, - ssize_t* block_size, struct compressed_block_header* hdr) + ssize_t* block_size, struct compressed_block_header* hdr) { - memcpy(hdr, p, sizeof(struct compressed_block_header)); - - if(hdr->block_flags.byte_count > 2) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported block header size (was %d, max is 2)", - hdr->block_flags.byte_count); - return ARCHIVE_FATAL; - } - - /* This should probably use bit reader interface in order to be more - * future-proof. */ - *block_size = 0; - switch(hdr->block_flags.byte_count) { - /* 1-byte block size */ - case 0: - *block_size = *(const uint8_t*) &p[2]; - break; - - /* 2-byte block size */ - case 1: - *block_size = *(const uint16_t*) &p[2]; - break; - - /* 3-byte block size */ - case 2: - *block_size = *(const uint32_t*) &p[2]; - *block_size &= 0x00FFFFFF; - break; - - /* Other block sizes are not supported. This case is not reached, - * because we have an 'if' guard before the switch that makes sure - * of it. */ - default: - return ARCHIVE_FATAL; - } - - /* Verify the block header checksum. 0x5A is a magic value and is always - * constant. */ - uint8_t calculated_cksum = 0x5A - ^ (uint8_t) hdr->block_flags_u8 - ^ (uint8_t) *block_size - ^ (uint8_t) (*block_size >> 8) - ^ (uint8_t) (*block_size >> 16); - - if(calculated_cksum != hdr->block_cksum) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Block checksum error: got 0x%02x, expected 0x%02x", - hdr->block_cksum, calculated_cksum); - - return ARCHIVE_FATAL; - } - - return ARCHIVE_OK; -} - -/* Convinience function used during filter processing. */ + memcpy(hdr, p, sizeof(struct compressed_block_header)); + + if(bf_byte_count(hdr) > 2) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported block header size (was %d, max is 2)", + bf_byte_count(hdr)); + return ARCHIVE_FATAL; + } + + /* This should probably use bit reader interface in order to be more + * future-proof. */ + *block_size = 0; + switch(bf_byte_count(hdr)) { + /* 1-byte block size */ + case 0: + *block_size = *(const uint8_t*) &p[2]; + break; + + /* 2-byte block size */ + case 1: + *block_size = archive_le16dec(&p[2]); + break; + + /* 3-byte block size */ + case 2: + *block_size = archive_le32dec(&p[2]); + *block_size &= 0x00FFFFFF; + break; + + /* Other block sizes are not supported. This case is not + * reached, because we have an 'if' guard before the switch + * that makes sure of it. */ + default: + return ARCHIVE_FATAL; + } + + /* Verify the block header checksum. 0x5A is a magic value and is + * always * constant. */ + uint8_t calculated_cksum = 0x5A + ^ (uint8_t) hdr->block_flags_u8 + ^ (uint8_t) *block_size + ^ (uint8_t) (*block_size >> 8) + ^ (uint8_t) (*block_size >> 16); + + if(calculated_cksum != hdr->block_cksum) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Block checksum error: got 0x%x, expected 0x%x", + hdr->block_cksum, calculated_cksum); + + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; +} + +/* Convenience function used during filter processing. */ static int parse_filter_data(struct rar5* rar, const uint8_t* p, - uint32_t* filter_data) + uint32_t* filter_data) { - int i, bytes; - uint32_t data = 0; + int i, bytes; + uint32_t data = 0; - if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) + return ARCHIVE_EOF; - bytes++; + bytes++; - for(i = 0; i < bytes; i++) { - uint16_t byte; + for(i = 0; i < bytes; i++) { + uint16_t byte; - if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { - return ARCHIVE_EOF; - } + if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { + return ARCHIVE_EOF; + } - data += (byte >> 8) << (i * 8); - skip_bits(rar, 8); - } + /* Cast to uint32_t will ensure the shift operation will not + * produce undefined result. */ + data += ((uint32_t) byte >> 8) << (i * 8); + skip_bits(rar, 8); + } - *filter_data = data; - return ARCHIVE_OK; + *filter_data = data; + return ARCHIVE_OK; } /* Function is used during sanity checking. */ static int is_valid_filter_block_start(struct rar5* rar, - uint32_t start) + uint32_t start) { - const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; - const int64_t last_bs = rar->cstate.last_block_start; - const ssize_t last_bl = rar->cstate.last_block_length; + const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; + const int64_t last_bs = rar->cstate.last_block_start; + const ssize_t last_bl = rar->cstate.last_block_length; - if(last_bs == 0 || last_bl == 0) { - /* We didn't have any filters yet, so accept this offset. */ - return 1; - } + if(last_bs == 0 || last_bl == 0) { + /* We didn't have any filters yet, so accept this offset. */ + return 1; + } - if(block_start >= last_bs + last_bl) { - /* Current offset is bigger than last block's end offset, so - * accept current offset. */ - return 1; - } + if(block_start >= last_bs + last_bl) { + /* Current offset is bigger than last block's end offset, so + * accept current offset. */ + return 1; + } - /* Any other case is not a normal situation and we should fail. */ - return 0; + /* Any other case is not a normal situation and we should fail. */ + return 0; } /* The function will create a new filter, read its parameters from the input * stream and add it to the filter collection. */ static int parse_filter(struct archive_read* ar, const uint8_t* p) { - uint32_t block_start, block_length; - uint16_t filter_type; - struct rar5* rar = get_context(ar); + uint32_t block_start, block_length; + uint16_t filter_type; + struct rar5* rar = get_context(ar); - /* Read the parameters from the input stream. */ - if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) - return ARCHIVE_EOF; + /* Read the parameters from the input stream. */ + if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) + return ARCHIVE_EOF; - if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) + return ARCHIVE_EOF; - if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) + return ARCHIVE_EOF; - filter_type >>= 13; - skip_bits(rar, 3); + filter_type >>= 13; + skip_bits(rar, 3); - /* Perform some sanity checks on this filter parameters. Note that we - * allow only DELTA, E8/E9 and ARM filters here, because rest of filters - * are not used in RARv5. */ + /* Perform some sanity checks on this filter parameters. Note that we + * allow only DELTA, E8/E9 and ARM filters here, because rest of + * filters are not used in RARv5. */ - if(block_length < 4 || - block_length > 0x400000 || - filter_type > FILTER_ARM || - !is_valid_filter_block_start(rar, block_start)) - { - archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid " - "filter encountered"); - return ARCHIVE_FATAL; - } + if(block_length < 4 || + block_length > 0x400000 || + filter_type > FILTER_ARM || + !is_valid_filter_block_start(rar, block_start)) + { + archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid filter encountered"); + return ARCHIVE_FATAL; + } - /* Allocate a new filter. */ - struct filter_info* filt = add_new_filter(rar); - if(filt == NULL) { - archive_set_error(&ar->archive, ENOMEM, "Can't allocate memory for a " - "filter descriptor."); - return ARCHIVE_FATAL; - } + /* Allocate a new filter. */ + struct filter_info* filt = add_new_filter(rar); + if(filt == NULL) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate memory for a filter descriptor."); + return ARCHIVE_FATAL; + } - filt->type = filter_type; - filt->block_start = rar->cstate.write_ptr + block_start; - filt->block_length = block_length; + filt->type = filter_type; + filt->block_start = rar->cstate.write_ptr + block_start; + filt->block_length = block_length; - rar->cstate.last_block_start = filt->block_start; - rar->cstate.last_block_length = filt->block_length; + rar->cstate.last_block_start = filt->block_start; + rar->cstate.last_block_length = filt->block_length; - /* Read some more data in case this is a DELTA filter. Other filter types - * don't require any additional data over what was already read. */ - if(filter_type == FILTER_DELTA) { - int channels; + /* Read some more data in case this is a DELTA filter. Other filter + * types don't require any additional data over what was already + * read. */ + if(filter_type == FILTER_DELTA) { + int channels; - if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) + return ARCHIVE_EOF; - filt->channels = channels + 1; - } + filt->channels = channels + 1; + } - return ARCHIVE_OK; + return ARCHIVE_OK; } static int decode_code_length(struct rar5* rar, const uint8_t* p, - uint16_t code) + uint16_t code) { - int lbits, length = 2; - if(code < 8) { - lbits = 0; - length += code; - } else { - lbits = code / 4 - 1; - length += (4 | (code & 3)) << lbits; - } + int lbits, length = 2; + if(code < 8) { + lbits = 0; + length += code; + } else { + lbits = code / 4 - 1; + length += (4 | (code & 3)) << lbits; + } - if(lbits > 0) { - int add; + if(lbits > 0) { + int add; - if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) - return -1; + if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) + return -1; - length += add; - } + length += add; + } - return length; + return length; } static int copy_string(struct archive_read* a, int len, int dist) { - struct rar5* rar = get_context(a); - const int cmask = (int)rar->cstate.window_mask; - const int64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; - int i; + struct rar5* rar = get_context(a); + const uint64_t cmask = rar->cstate.window_mask; + const uint64_t write_ptr = rar->cstate.write_ptr + + rar->cstate.solid_offset; + int i; + + if (rar->cstate.window_buf == NULL) + return ARCHIVE_FATAL; - /* The unpacker spends most of the time in this function. It would be - * a good idea to introduce some optimizations here. - * - * Just remember that this loop treats buffers that overlap differently - * than buffers that do not overlap. This is why a simple memcpy(3) call - * will not be enough. */ + /* The unpacker spends most of the time in this function. It would be + * a good idea to introduce some optimizations here. + * + * Just remember that this loop treats buffers that overlap differently + * than buffers that do not overlap. This is why a simple memcpy(3) + * call will not be enough. */ - 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]; - } + for(i = 0; i < len; i++) { + const ssize_t write_idx = (write_ptr + i) & cmask; + const ssize_t read_idx = (write_ptr + i - dist) & cmask; + rar->cstate.window_buf[write_idx] = + rar->cstate.window_buf[read_idx]; + } - rar->cstate.write_ptr += len; - return ARCHIVE_OK; + rar->cstate.write_ptr += len; + return ARCHIVE_OK; } static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { - struct rar5* rar = get_context(a); - uint16_t num; - int ret; - - const int cmask = (int)rar->cstate.window_mask; - const struct compressed_block_header* hdr = &rar->last_block_hdr; - const uint8_t bit_size = 1 + hdr->block_flags.bit_size; - - while(1) { - if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > - (rar->cstate.window_size >> 1)) { - - /* Don't allow growing data by more than half of the window size - * at a time. In such case, break the loop; next call to this - * function will continue processing from this moment. */ - - break; - } - - if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || - (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && - rar->bits.bit_addr >= bit_size)) - { - /* If the program counter is here, it means the function has - * finished processing the block. */ - rar->cstate.block_parsing_finished = 1; - break; - } - - /* Decode the next literal. */ - if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { - return ARCHIVE_EOF; - } - - /* Num holds a decompression literal, or 'command code'. - * - * - Values lower than 256 are just bytes. Those codes can be stored - * in the output buffer directly. - * - * - Code 256 defines a new filter, which is later used to transform - * the data block accordingly to the filter type. The data block - * needs to be fully uncompressed first. - * - * - Code bigger than 257 and smaller than 262 define a repetition - * pattern that should be copied from an already uncompressed chunk - * of data. - */ - - if(num < 256) { - /* Directly store the byte. */ - - int64_t write_idx = rar->cstate.solid_offset + - rar->cstate.write_ptr++; - - rar->cstate.window_buf[write_idx & cmask] = (uint8_t) num; - continue; - } else if(num >= 262) { - uint16_t dist_slot; - int len = decode_code_length(rar, p, num - 262), - dbits, - dist = 1; - - if(len == -1) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Failed to decode the code length"); - - return ARCHIVE_FATAL; - } - - if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, &dist_slot)) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Failed to decode the distance slot"); - - return ARCHIVE_FATAL; - } - - if(dist_slot < 4) { - dbits = 0; - dist += dist_slot; - } else { - dbits = dist_slot / 2 - 1; - dist += (2 | (dist_slot & 1)) << dbits; - } - - if(dbits > 0) { - if(dbits >= 4) { - uint32_t add = 0; - uint16_t low_dist; - - if(dbits > 4) { - if(ARCHIVE_OK != read_bits_32(rar, p, &add)) { - /* Return EOF if we can't read more data. */ - return ARCHIVE_EOF; - } - - skip_bits(rar, dbits - 4); - add = (add >> (36 - dbits)) << 4; - dist += add; - } - - if(ARCHIVE_OK != decode_number(a, &rar->cstate.ldd, p, - &low_dist)) - { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_PROGRAMMER, - "Failed to decode the distance slot"); - - return ARCHIVE_FATAL; - } - - dist += low_dist; - } else { - /* dbits is one of [0,1,2,3] */ - int add; - - if(ARCHIVE_OK != read_consume_bits(rar, p, dbits, &add)) { - /* Return EOF if we can't read more data. */ - return ARCHIVE_EOF; - } - - dist += add; - } - } - - if(dist > 0x100) { - len++; - - if(dist > 0x2000) { - len++; - - if(dist > 0x40000) { - len++; - } - } - } - - dist_cache_push(rar, dist); - rar->cstate.last_len = len; - - if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; - - continue; - } else if(num == 256) { - /* Create a filter. */ - ret = parse_filter(a, p); - if(ret != ARCHIVE_OK) - return ret; - - continue; - } else if(num == 257) { - if(rar->cstate.last_len != 0) { - if(ARCHIVE_OK != copy_string(a, rar->cstate.last_len, - rar->cstate.dist_cache[0])) - { - return ARCHIVE_FATAL; - } - } - - continue; - } else if(num < 262) { - const int index = num - 258; - const int dist = dist_cache_touch(rar, index); - - uint16_t len_slot; - int len; - - if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) { - return ARCHIVE_FATAL; - } - - len = decode_code_length(rar, p, len_slot); - rar->cstate.last_len = len; - - if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; - - continue; - } - - /* The program counter shouldn't reach here. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported block code: 0x%02x", num); - - return ARCHIVE_FATAL; - } - - return ARCHIVE_OK; + struct rar5* rar = get_context(a); + uint16_t num; + int ret; + + const uint64_t cmask = rar->cstate.window_mask; + const struct compressed_block_header* hdr = &rar->last_block_hdr; + const uint8_t bit_size = 1 + bf_bit_size(hdr); + + while(1) { + if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > + (rar->cstate.window_size >> 1)) { + /* Don't allow growing data by more than half of the + * window size at a time. In such case, break the loop; + * next call to this function will continue processing + * from this moment. */ + break; + } + + if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || + (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && + rar->bits.bit_addr >= bit_size)) + { + /* If the program counter is here, it means the + * function has finished processing the block. */ + rar->cstate.block_parsing_finished = 1; + break; + } + + /* Decode the next literal. */ + if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { + return ARCHIVE_EOF; + } + + /* Num holds a decompression literal, or 'command code'. + * + * - Values lower than 256 are just bytes. Those codes + * can be stored in the output buffer directly. + * + * - Code 256 defines a new filter, which is later used to + * ransform the data block accordingly to the filter type. + * The data block needs to be fully uncompressed first. + * + * - Code bigger than 257 and smaller than 262 define + * a repetition pattern that should be copied from + * an already uncompressed chunk of data. + */ + + if(num < 256) { + /* Directly store the byte. */ + int64_t write_idx = rar->cstate.solid_offset + + rar->cstate.write_ptr++; + + rar->cstate.window_buf[write_idx & cmask] = + (uint8_t) num; + continue; + } else if(num >= 262) { + uint16_t dist_slot; + int len = decode_code_length(rar, p, num - 262), + dbits, + dist = 1; + + if(len == -1) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the code length"); + + return ARCHIVE_FATAL; + } + + if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, + &dist_slot)) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the distance slot"); + + return ARCHIVE_FATAL; + } + + if(dist_slot < 4) { + dbits = 0; + dist += dist_slot; + } else { + dbits = dist_slot / 2 - 1; + + /* Cast to uint32_t will make sure the shift + * left operation won't produce undefined + * result. Then, the uint32_t type will + * be implicitly casted to int. */ + dist += (uint32_t) (2 | + (dist_slot & 1)) << dbits; + } + + if(dbits > 0) { + if(dbits >= 4) { + uint32_t add = 0; + uint16_t low_dist; + + if(dbits > 4) { + if(ARCHIVE_OK != read_bits_32( + rar, p, &add)) { + /* Return EOF if we + * can't read more + * data. */ + return ARCHIVE_EOF; + } + + skip_bits(rar, dbits - 4); + add = (add >> ( + 36 - dbits)) << 4; + dist += add; + } + + if(ARCHIVE_OK != decode_number(a, + &rar->cstate.ldd, p, &low_dist)) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Failed to decode the " + "distance slot"); + + return ARCHIVE_FATAL; + } + + if(dist >= INT_MAX - low_dist - 1) { + /* This only happens in + * invalid archives. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Distance pointer " + "overflow"); + return ARCHIVE_FATAL; + } + + dist += low_dist; + } else { + /* dbits is one of [0,1,2,3] */ + int add; + + if(ARCHIVE_OK != read_consume_bits(rar, + p, dbits, &add)) { + /* Return EOF if we can't read + * more data. */ + return ARCHIVE_EOF; + } + + dist += add; + } + } + + if(dist > 0x100) { + len++; + + if(dist > 0x2000) { + len++; + + if(dist > 0x40000) { + len++; + } + } + } + + dist_cache_push(rar, dist); + rar->cstate.last_len = len; + + if(ARCHIVE_OK != copy_string(a, len, dist)) + return ARCHIVE_FATAL; + + continue; + } else if(num == 256) { + /* Create a filter. */ + ret = parse_filter(a, p); + if(ret != ARCHIVE_OK) + return ret; + + continue; + } else if(num == 257) { + if(rar->cstate.last_len != 0) { + if(ARCHIVE_OK != copy_string(a, + rar->cstate.last_len, + rar->cstate.dist_cache[0])) + { + return ARCHIVE_FATAL; + } + } + + continue; + } else if(num < 262) { + const int idx = num - 258; + const int dist = dist_cache_touch(rar, idx); + + uint16_t len_slot; + int len; + + if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, + &len_slot)) { + return ARCHIVE_FATAL; + } + + len = decode_code_length(rar, p, len_slot); + rar->cstate.last_len = len; + + if(ARCHIVE_OK != copy_string(a, len, dist)) + return ARCHIVE_FATAL; + + continue; + } + + /* The program counter shouldn't reach here. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported block code: 0x%x", num); + + return ARCHIVE_FATAL; + } + + return ARCHIVE_OK; } /* Binary search for the RARv5 signature. */ static int scan_for_signature(struct archive_read* a) { - const uint8_t* p; - const int chunk_size = 512; - ssize_t i; - - /* If we're here, it means we're on an 'unknown territory' data. - * There's no indication what kind of data we're reading here. It could be - * some text comment, any kind of binary data, digital sign, dragons, etc. - * - * We want to find a valid RARv5 magic header inside this unknown data. */ - - /* Is it possible in libarchive to just skip everything until the - * end of the file? If so, it would be a better approach than the - * current implementation of this function. */ - - while(1) { - if(!read_ahead(a, chunk_size, &p)) - return ARCHIVE_EOF; - - for(i = 0; i < chunk_size - rar5_signature_size; i++) { - if(memcmp(&p[i], rar5_signature, rar5_signature_size) == 0) { - /* Consume the number of bytes we've used to search for the - * signature, as well as the number of bytes used by the - * signature itself. After this we should be standing on a - * valid base block header. */ - (void) consume(a, i + rar5_signature_size); - return ARCHIVE_OK; - } - } - - consume(a, chunk_size); - } - - return ARCHIVE_FATAL; + const uint8_t* p; + const int chunk_size = 512; + ssize_t i; + + /* If we're here, it means we're on an 'unknown territory' data. + * There's no indication what kind of data we're reading here. + * It could be some text comment, any kind of binary data, + * digital sign, dragons, etc. + * + * We want to find a valid RARv5 magic header inside this unknown + * data. */ + + /* Is it possible in libarchive to just skip everything until the + * end of the file? If so, it would be a better approach than the + * current implementation of this function. */ + + while(1) { + if(!read_ahead(a, chunk_size, &p)) + return ARCHIVE_EOF; + + for(i = 0; i < chunk_size - rar5_signature_size; i++) { + if(memcmp(&p[i], rar5_signature, + rar5_signature_size) == 0) { + /* Consume the number of bytes we've used to + * search for the signature, as well as the + * number of bytes used by the signature + * itself. After this we should be standing + * on a valid base block header. */ + (void) consume(a, i + rar5_signature_size); + return ARCHIVE_OK; + } + } + + consume(a, chunk_size); + } + + return ARCHIVE_FATAL; } /* This function will switch the multivolume archive file to another file, * i.e. from part03 to part 04. */ static int advance_multivolume(struct archive_read* a) { - int lret; - struct rar5* rar = get_context(a); - - /* A small state machine that will skip unnecessary data, needed to - * switch from one multivolume to another. Such skipping is needed if - * we want to be an stream-oriented (instead of file-oriented) - * unpacker. - * - * The state machine starts with `rar->main.endarc` == 0. It also - * assumes that current stream pointer points to some base block header. - * - * The `endarc` field is being set when the base block parsing function - * encounters the 'end of archive' marker. - */ - - while(1) { - if(rar->main.endarc == 1) { - rar->main.endarc = 0; - while(ARCHIVE_RETRY == skip_base_block(a)); - break; - } else { - /* Skip current base block. In order to properly skip it, - * we really need to simply parse it and discard the results. */ - - lret = skip_base_block(a); - - /* The `skip_base_block` function tells us if we should continue - * with skipping, or we should stop skipping. We're trying to skip - * everything up to a base FILE block. */ - - if(lret != ARCHIVE_RETRY) { - /* If there was an error during skipping, or we have just - * skipped a FILE base block... */ - - if(rar->main.endarc == 0) { - return lret; - } else { - continue; - } - } - } - } - - return ARCHIVE_OK; + int lret; + struct rar5* rar = get_context(a); + + /* A small state machine that will skip unnecessary data, needed to + * switch from one multivolume to another. Such skipping is needed if + * we want to be an stream-oriented (instead of file-oriented) + * unpacker. + * + * The state machine starts with `rar->main.endarc` == 0. It also + * assumes that current stream pointer points to some base block + * header. + * + * The `endarc` field is being set when the base block parsing + * function encounters the 'end of archive' marker. + */ + + while(1) { + if(rar->main.endarc == 1) { + int looping = 1; + + rar->main.endarc = 0; + + while(looping) { + lret = skip_base_block(a); + switch(lret) { + case ARCHIVE_RETRY: + /* Continue looping. */ + break; + case ARCHIVE_OK: + /* Break loop. */ + looping = 0; + break; + default: + /* Forward any errors to the + * caller. */ + return lret; + } + } + + break; + } else { + /* Skip current base block. In order to properly skip + * it, we really need to simply parse it and discard + * the results. */ + + lret = skip_base_block(a); + if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED) + return lret; + + /* The `skip_base_block` function tells us if we + * should continue with skipping, or we should stop + * skipping. We're trying to skip everything up to + * a base FILE block. */ + + if(lret != ARCHIVE_RETRY) { + /* If there was an error during skipping, or we + * have just skipped a FILE base block... */ + + if(rar->main.endarc == 0) { + return lret; + } else { + continue; + } + } + } + } + + return ARCHIVE_OK; } /* Merges the partial block from the first multivolume archive file, and @@ -2656,228 +3150,253 @@ static int advance_multivolume(struct archive_read* a) { * a chunk of memory containing the whole block, and the stream pointer * is advanced to the next block in the second multivolume archive file. */ static int merge_block(struct archive_read* a, ssize_t block_size, - const uint8_t** p) + const uint8_t** p) { - struct rar5* rar = get_context(a); - ssize_t cur_block_size, partial_offset = 0; - const uint8_t* lp; - int ret; - - /* Set a flag that we're in the switching mode. */ - rar->cstate.switch_multivolume = 1; - - /* Reallocate the memory which will hold the whole block. */ - if(rar->vol.push_buf) - free((void*) rar->vol.push_buf); - - /* Increasing the allocation block by 8 is due to bit reading functions, - * which are using additional 2 or 4 bytes. Allocating the block size - * by exact value would make bit reader perform reads from invalid memory - * block when reading the last byte from the buffer. */ - rar->vol.push_buf = malloc(block_size + 8); - if(!rar->vol.push_buf) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a " - "merge block buffer."); - return ARCHIVE_FATAL; - } - - /* Valgrind complains if the extension block for bit reader is not - * initialized, so initialize it. */ - memset(&rar->vol.push_buf[block_size], 0, 8); - - /* A single block can span across multiple multivolume archive files, - * so we use a loop here. This loop will consume enough multivolume - * archive files until the whole block is read. */ - - while(1) { - /* Get the size of current block chunk in this multivolume archive - * file and read it. */ - cur_block_size = - rar5_min(rar->file.bytes_remaining, block_size - partial_offset); - - if(!read_ahead(a, cur_block_size, &lp)) - return ARCHIVE_EOF; - - /* Sanity check; there should never be a situation where this function - * reads more data than the block's size. */ - if(partial_offset + cur_block_size > block_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Consumed too much data when merging blocks."); - return ARCHIVE_FATAL; - } - - /* Merge previous block chunk with current block chunk, or create - * first block chunk if this is our first iteration. */ - memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); - - /* Advance the stream read pointer by this block chunk size. */ - if(ARCHIVE_OK != consume(a, cur_block_size)) - return ARCHIVE_EOF; - - /* Update the pointers. `partial_offset` contains information about - * the sum of merged block chunks. */ - partial_offset += cur_block_size; - rar->file.bytes_remaining -= cur_block_size; - - /* If `partial_offset` is the same as `block_size`, this means we've - * merged all block chunks and we have a valid full block. */ - if(partial_offset == block_size) { - break; - } - - /* If we don't have any bytes to read, this means we should switch - * to another multivolume archive file. */ - if(rar->file.bytes_remaining == 0) { - ret = advance_multivolume(a); - if(ret != ARCHIVE_OK) - return ret; - } - } - - *p = rar->vol.push_buf; - - /* If we're here, we can resume unpacking by processing the block pointed - * to by the `*p` memory pointer. */ - - return ARCHIVE_OK; + struct rar5* rar = get_context(a); + ssize_t cur_block_size, partial_offset = 0; + const uint8_t* lp; + int ret; + + if(rar->merge_mode) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Recursive merge is not allowed"); + + return ARCHIVE_FATAL; + } + + /* Set a flag that we're in the switching mode. */ + rar->cstate.switch_multivolume = 1; + + /* Reallocate the memory which will hold the whole block. */ + if(rar->vol.push_buf) + free((void*) rar->vol.push_buf); + + /* Increasing the allocation block by 8 is due to bit reading functions, + * which are using additional 2 or 4 bytes. Allocating the block size + * by exact value would make bit reader perform reads from invalid + * memory block when reading the last byte from the buffer. */ + rar->vol.push_buf = malloc(block_size + 8); + if(!rar->vol.push_buf) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a merge block buffer."); + return ARCHIVE_FATAL; + } + + /* Valgrind complains if the extension block for bit reader is not + * initialized, so initialize it. */ + memset(&rar->vol.push_buf[block_size], 0, 8); + + /* A single block can span across multiple multivolume archive files, + * so we use a loop here. This loop will consume enough multivolume + * archive files until the whole block is read. */ + + while(1) { + /* Get the size of current block chunk in this multivolume + * archive file and read it. */ + cur_block_size = rar5_min(rar->file.bytes_remaining, + block_size - partial_offset); + + if(cur_block_size == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Encountered block size == 0 during block merge"); + return ARCHIVE_FATAL; + } + + if(!read_ahead(a, cur_block_size, &lp)) + return ARCHIVE_EOF; + + /* Sanity check; there should never be a situation where this + * function reads more data than the block's size. */ + if(partial_offset + cur_block_size > block_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Consumed too much data when merging blocks."); + return ARCHIVE_FATAL; + } + + /* Merge previous block chunk with current block chunk, + * or create first block chunk if this is our first + * iteration. */ + memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); + + /* Advance the stream read pointer by this block chunk size. */ + if(ARCHIVE_OK != consume(a, cur_block_size)) + return ARCHIVE_EOF; + + /* Update the pointers. `partial_offset` contains information + * about the sum of merged block chunks. */ + partial_offset += cur_block_size; + rar->file.bytes_remaining -= cur_block_size; + + /* If `partial_offset` is the same as `block_size`, this means + * we've merged all block chunks and we have a valid full + * block. */ + if(partial_offset == block_size) { + break; + } + + /* If we don't have any bytes to read, this means we should + * switch to another multivolume archive file. */ + if(rar->file.bytes_remaining == 0) { + rar->merge_mode++; + ret = advance_multivolume(a); + rar->merge_mode--; + if(ret != ARCHIVE_OK) { + return ret; + } + } + } + + *p = rar->vol.push_buf; + + /* If we're here, we can resume unpacking by processing the block + * pointed to by the `*p` memory pointer. */ + + return ARCHIVE_OK; } static int process_block(struct archive_read* a) { - const uint8_t* p; - struct rar5* rar = get_context(a); - int ret; - - /* If we don't have any data to be processed, this most probably means - * we need to switch to the next volume. */ - if(rar->main.volume && rar->file.bytes_remaining == 0) { - ret = advance_multivolume(a); - if(ret != ARCHIVE_OK) - return ret; - } - - if(rar->cstate.block_parsing_finished) { - ssize_t block_size; - - rar->cstate.block_parsing_finished = 0; - - /* The header size won't be bigger than 6 bytes. */ - if(!read_ahead(a, 6, &p)) { - /* Failed to prefetch data block header. */ - return ARCHIVE_EOF; - } - - /* - * Read block_size by parsing block header. Validate the header by - * calculating CRC byte stored inside the header. Size of the header is - * not constant (block size can be stored either in 1 or 2 bytes), - * that's why block size is left out from the `compressed_block_header` - * structure and returned by `parse_block_header` as the second - * argument. */ - - ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr); - if(ret != ARCHIVE_OK) - return ret; - - /* Skip block header. Next data is huffman tables, if present. */ - ssize_t to_skip = sizeof(struct compressed_block_header) + - rar->last_block_hdr.block_flags.byte_count + 1; - - if(ARCHIVE_OK != consume(a, to_skip)) - return ARCHIVE_EOF; - - rar->file.bytes_remaining -= to_skip; - - /* The block size gives information about the whole block size, but - * the block could be stored in split form when using multi-volume - * archives. In this case, the block size will be bigger than the - * actual data stored in this file. Remaining part of the data will - * be in another file. */ - - ssize_t cur_block_size = - rar5_min(rar->file.bytes_remaining, block_size); - - if(block_size > rar->file.bytes_remaining) { - /* If current blocks' size is bigger than our data size, this - * means we have a multivolume archive. In this case, skip - * all base headers until the end of the file, proceed to next - * "partXXX.rar" volume, find its signature, skip all headers up - * to the first FILE base header, and continue from there. - * - * Note that `merge_block` will update the `rar` context structure - * quite extensively. */ - - ret = merge_block(a, block_size, &p); - if(ret != ARCHIVE_OK) { - return ret; - } - - cur_block_size = block_size; - - /* Current stream pointer should be now directly *after* the - * block that spanned through multiple archive files. `p` pointer - * should have the data of the *whole* block (merged from - * partial blocks stored in multiple archives files). */ - } else { - rar->cstate.switch_multivolume = 0; - - /* Read the whole block size into memory. This can take up to - * 8 megabytes of memory in theoretical cases. Might be worth to - * optimize this and use a standard chunk of 4kb's. */ - - if(!read_ahead(a, 4 + cur_block_size, &p)) { - /* Failed to prefetch block data. */ - return ARCHIVE_EOF; - } - } - - rar->cstate.block_buf = p; - rar->cstate.cur_block_size = cur_block_size; - - rar->bits.in_addr = 0; - rar->bits.bit_addr = 0; - - if(rar->last_block_hdr.block_flags.is_table_present) { - /* Load Huffman tables. */ - ret = parse_tables(a, rar, p); - if(ret != ARCHIVE_OK) { - /* Error during decompression of Huffman tables. */ - return ret; - } - } - } else { - p = rar->cstate.block_buf; - } - - /* Uncompress the block, or a part of it, depending on how many bytes - * will be generated by uncompressing the block. - * - * In case too many bytes will be generated, calling this function again - * will resume the uncompression operation. */ - ret = do_uncompress_block(a, p); - if(ret != ARCHIVE_OK) { - return ret; - } - - if(rar->cstate.block_parsing_finished && - rar->cstate.switch_multivolume == 0 && - rar->cstate.cur_block_size > 0) - { - /* If we're processing a normal block, consume the whole block. We - * can do this because we've already read the whole block to memory. - */ - if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) - return ARCHIVE_FATAL; - - rar->file.bytes_remaining -= rar->cstate.cur_block_size; - } else if(rar->cstate.switch_multivolume) { - /* Don't consume the block if we're doing multivolume processing. - * The volume switching function will consume the proper count of - * bytes instead. */ - - rar->cstate.switch_multivolume = 0; - } - - return ARCHIVE_OK; + const uint8_t* p; + struct rar5* rar = get_context(a); + int ret; + + /* If we don't have any data to be processed, this most probably means + * we need to switch to the next volume. */ + if(rar->main.volume && rar->file.bytes_remaining == 0) { + ret = advance_multivolume(a); + if(ret != ARCHIVE_OK) + return ret; + } + + if(rar->cstate.block_parsing_finished) { + ssize_t block_size; + + /* The header size won't be bigger than 6 bytes. */ + if(!read_ahead(a, 6, &p)) { + /* Failed to prefetch data block header. */ + return ARCHIVE_EOF; + } + + /* + * Read block_size by parsing block header. Validate the header + * by calculating CRC byte stored inside the header. Size of + * the header is not constant (block size can be stored either + * in 1 or 2 bytes), that's why block size is left out from the + * `compressed_block_header` structure and returned by + * `parse_block_header` as the second argument. */ + + ret = parse_block_header(a, p, &block_size, + &rar->last_block_hdr); + if(ret != ARCHIVE_OK) { + return ret; + } + + /* Skip block header. Next data is huffman tables, + * if present. */ + ssize_t to_skip = sizeof(struct compressed_block_header) + + bf_byte_count(&rar->last_block_hdr) + 1; + + if(ARCHIVE_OK != consume(a, to_skip)) + return ARCHIVE_EOF; + + rar->file.bytes_remaining -= to_skip; + + /* The block size gives information about the whole block size, + * but the block could be stored in split form when using + * multi-volume archives. In this case, the block size will be + * bigger than the actual data stored in this file. Remaining + * part of the data will be in another file. */ + + ssize_t cur_block_size = + rar5_min(rar->file.bytes_remaining, block_size); + + if(block_size > rar->file.bytes_remaining) { + /* If current blocks' size is bigger than our data + * size, this means we have a multivolume archive. + * In this case, skip all base headers until the end + * of the file, proceed to next "partXXX.rar" volume, + * find its signature, skip all headers up to the first + * FILE base header, and continue from there. + * + * Note that `merge_block` will update the `rar` + * context structure quite extensively. */ + + ret = merge_block(a, block_size, &p); + if(ret != ARCHIVE_OK) { + return ret; + } + + cur_block_size = block_size; + + /* Current stream pointer should be now directly + * *after* the block that spanned through multiple + * archive files. `p` pointer should have the data of + * the *whole* block (merged from partial blocks + * stored in multiple archives files). */ + } else { + rar->cstate.switch_multivolume = 0; + + /* Read the whole block size into memory. This can take + * up to 8 megabytes of memory in theoretical cases. + * Might be worth to optimize this and use a standard + * chunk of 4kb's. */ + if(!read_ahead(a, 4 + cur_block_size, &p)) { + /* Failed to prefetch block data. */ + return ARCHIVE_EOF; + } + } + + rar->cstate.block_buf = p; + rar->cstate.cur_block_size = cur_block_size; + rar->cstate.block_parsing_finished = 0; + + rar->bits.in_addr = 0; + rar->bits.bit_addr = 0; + + if(bf_is_table_present(&rar->last_block_hdr)) { + /* Load Huffman tables. */ + ret = parse_tables(a, rar, p); + if(ret != ARCHIVE_OK) { + /* Error during decompression of Huffman + * tables. */ + return ret; + } + } + } else { + /* Block parsing not finished, reuse previous memory buffer. */ + p = rar->cstate.block_buf; + } + + /* Uncompress the block, or a part of it, depending on how many bytes + * will be generated by uncompressing the block. + * + * In case too many bytes will be generated, calling this function + * again will resume the uncompression operation. */ + ret = do_uncompress_block(a, p); + if(ret != ARCHIVE_OK) { + return ret; + } + + if(rar->cstate.block_parsing_finished && + rar->cstate.switch_multivolume == 0 && + rar->cstate.cur_block_size > 0) + { + /* If we're processing a normal block, consume the whole + * block. We can do this because we've already read the whole + * block to memory. */ + if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) + return ARCHIVE_FATAL; + + rar->file.bytes_remaining -= rar->cstate.cur_block_size; + } else if(rar->cstate.switch_multivolume) { + /* Don't consume the block if we're doing multivolume + * processing. The volume switching function will consume + * the proper count of bytes instead. */ + rar->cstate.switch_multivolume = 0; + } + + return ARCHIVE_OK; } /* Pops the `buf`, `size` and `offset` from the "data ready" stack. @@ -2885,78 +3404,78 @@ static int process_block(struct archive_read* a) { * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY * when there is no data on the stack. */ static int use_data(struct rar5* rar, const void** buf, size_t* size, - int64_t* offset) + int64_t* offset) { - int i; + int i; - for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { - struct data_ready *d = &rar->cstate.dready[i]; + for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { + struct data_ready *d = &rar->cstate.dready[i]; - if(d->used) { - if(buf) *buf = d->buf; - if(size) *size = d->size; - if(offset) *offset = d->offset; + if(d->used) { + if(buf) *buf = d->buf; + if(size) *size = d->size; + if(offset) *offset = d->offset; - d->used = 0; - return ARCHIVE_OK; - } - } + d->used = 0; + return ARCHIVE_OK; + } + } - return ARCHIVE_RETRY; + return ARCHIVE_RETRY; } /* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ static int push_data_ready(struct archive_read* a, struct rar5* rar, - const uint8_t* buf, size_t size, int64_t offset) + const uint8_t* buf, size_t size, int64_t offset) { - int i; - - /* Don't push if we're in skip mode. This is needed because solid - * streams need full processing even if we're skipping data. After fully - * processing the stream, we need to discard the generated bytes, because - * we're interested only in the side effect: building up the internal - * window circular buffer. This window buffer will be used later during - * unpacking of requested data. */ - if(rar->skip_mode) - return ARCHIVE_OK; - - /* Sanity check. */ - if(offset != rar->file.last_offset + rar->file.last_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity " - "check error: output stream is not continuous"); - return ARCHIVE_FATAL; - } - - for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { - struct data_ready* d = &rar->cstate.dready[i]; - if(!d->used) { - d->used = 1; - d->buf = buf; - d->size = size; - d->offset = offset; - - /* These fields are used only in sanity checking. */ - rar->file.last_offset = offset; - rar->file.last_size = size; - - /* Calculate the checksum of this new block before submitting - * data to libarchive's engine. */ - update_crc(rar, d->buf, d->size); - - return ARCHIVE_OK; - } - } - - /* Program counter will reach this code if the `rar->cstate.data_ready` - * stack will be filled up so that no new entries will be allowed. The - * code shouldn't allow such situation to occur. So we treat this case - * as an internal error. */ - - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Error: " - "premature end of data_ready stack"); - return ARCHIVE_FATAL; + int i; + + /* Don't push if we're in skip mode. This is needed because solid + * streams need full processing even if we're skipping data. After + * fully processing the stream, we need to discard the generated bytes, + * because we're interested only in the side effect: building up the + * internal window circular buffer. This window buffer will be used + * later during unpacking of requested data. */ + if(rar->skip_mode) + return ARCHIVE_OK; + + /* Sanity check. */ + if(offset != rar->file.last_offset + rar->file.last_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Sanity check error: output stream is not continuous"); + return ARCHIVE_FATAL; + } + + for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { + struct data_ready* d = &rar->cstate.dready[i]; + if(!d->used) { + d->used = 1; + d->buf = buf; + d->size = size; + d->offset = offset; + + /* These fields are used only in sanity checking. */ + rar->file.last_offset = offset; + rar->file.last_size = size; + + /* Calculate the checksum of this new block before + * submitting data to libarchive's engine. */ + update_crc(rar, d->buf, d->size); + + return ARCHIVE_OK; + } + } + + /* Program counter will reach this code if the `rar->cstate.data_ready` + * stack will be filled up so that no new entries will be allowed. The + * code shouldn't allow such situation to occur. So we treat this case + * as an internal error. */ + + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Error: premature end of data_ready stack"); + return ARCHIVE_FATAL; } /* This function uncompresses the data that is stored in the base @@ -3002,468 +3521,510 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar, * */ static int do_uncompress_file(struct archive_read* a) { - struct rar5* rar = get_context(a); - int ret; - int64_t max_end_pos; - - if(!rar->cstate.initialized) { - /* Don't perform full context reinitialization if we're processing - * a solid archive. */ - if(!rar->main.solid || !rar->cstate.window_buf) { - init_unpack(rar); - } - - rar->cstate.initialized = 1; - } - - if(rar->cstate.all_filters_applied == 1) { - /* We use while(1) here, but standard case allows for just 1 iteration. - * The loop will iterate if process_block() didn't generate any data at - * all. This can happen if the block contains only filter definitions - * (this is common in big files). */ - - while(1) { - ret = process_block(a); - if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) - return ret; - - if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) { - /* The block didn't generate any new data, so just process - * a new block. */ - continue; - } - - /* The block has generated some new data, so break the loop. */ - break; - } - } - - /* Try to run filters. If filters won't be applied, it means that - * insufficient data was generated. */ - ret = apply_filters(a); - if(ret == ARCHIVE_RETRY) { - return ARCHIVE_OK; - } else if(ret == ARCHIVE_FATAL) { - return ARCHIVE_FATAL; - } - - /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ - - if(cdeque_size(&rar->cstate.filters) > 0) { - /* Check if we can write something before hitting first filter. */ - struct filter_info* flt; - - /* Get the block_start offset from the first filter. */ - if(CDE_OK != cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Can't read first filter"); - return ARCHIVE_FATAL; - } - - max_end_pos = rar5_min(flt->block_start, rar->cstate.write_ptr); - } else { - /* There are no filters defined, or all filters were applied. This - * means we can just store the data without any postprocessing. */ - max_end_pos = rar->cstate.write_ptr; - } - - if(max_end_pos == rar->cstate.last_write_ptr) { - /* We can't write anything yet. The block uncompression function did - * not generate enough data, and no filter can be applied. At the same - * time we don't have any data that can be stored without filter - * postprocessing. This means we need to wait for more data to be - * generated, so we can apply the filters. - * - * Signal the caller that we need more data to be able to do anything. - */ - return ARCHIVE_RETRY; - } else { - /* We can write the data before hitting the first filter. So let's - * do it. The push_window_data() function will effectively return - * the selected data block to the user application. */ - push_window_data(a, rar, rar->cstate.last_write_ptr, max_end_pos); - rar->cstate.last_write_ptr = max_end_pos; - } - - return ARCHIVE_OK; + struct rar5* rar = get_context(a); + int ret; + int64_t max_end_pos; + + if(!rar->cstate.initialized) { + /* Don't perform full context reinitialization if we're + * processing a solid archive. */ + if(!rar->main.solid || !rar->cstate.window_buf) { + init_unpack(rar); + } + + rar->cstate.initialized = 1; + } + + if(rar->cstate.all_filters_applied == 1) { + /* We use while(1) here, but standard case allows for just 1 + * iteration. The loop will iterate if process_block() didn't + * generate any data at all. This can happen if the block + * contains only filter definitions (this is common in big + * files). */ + while(1) { + ret = process_block(a); + if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) + return ret; + + if(rar->cstate.last_write_ptr == + rar->cstate.write_ptr) { + /* The block didn't generate any new data, + * so just process a new block. */ + continue; + } + + /* The block has generated some new data, so break + * the loop. */ + break; + } + } + + /* Try to run filters. If filters won't be applied, it means that + * insufficient data was generated. */ + ret = apply_filters(a); + if(ret == ARCHIVE_RETRY) { + return ARCHIVE_OK; + } else if(ret == ARCHIVE_FATAL) { + return ARCHIVE_FATAL; + } + + /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ + + if(cdeque_size(&rar->cstate.filters) > 0) { + /* Check if we can write something before hitting first + * filter. */ + struct filter_info* flt; + + /* Get the block_start offset from the first filter. */ + if(CDE_OK != cdeque_front(&rar->cstate.filters, + cdeque_filter_p(&flt))) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Can't read first filter"); + return ARCHIVE_FATAL; + } + + max_end_pos = rar5_min(flt->block_start, + rar->cstate.write_ptr); + } else { + /* There are no filters defined, or all filters were applied. + * This means we can just store the data without any + * postprocessing. */ + max_end_pos = rar->cstate.write_ptr; + } + + if(max_end_pos == rar->cstate.last_write_ptr) { + /* We can't write anything yet. The block uncompression + * function did not generate enough data, and no filter can be + * applied. At the same time we don't have any data that can be + * stored without filter postprocessing. This means we need to + * wait for more data to be generated, so we can apply the + * filters. + * + * Signal the caller that we need more data to be able to do + * anything. + */ + return ARCHIVE_RETRY; + } else { + /* We can write the data before hitting the first filter. + * So let's do it. The push_window_data() function will + * effectively return the selected data block to the user + * application. */ + push_window_data(a, rar, rar->cstate.last_write_ptr, + max_end_pos); + rar->cstate.last_write_ptr = max_end_pos; + } + + return ARCHIVE_OK; } static int uncompress_file(struct archive_read* a) { - int ret; + int ret; - while(1) { - /* Sometimes the uncompression function will return a 'retry' signal. - * If this will happen, we have to retry the function. */ - ret = do_uncompress_file(a); - if(ret != ARCHIVE_RETRY) - return ret; - } + while(1) { + /* Sometimes the uncompression function will return a + * 'retry' signal. If this will happen, we have to retry + * the function. */ + ret = do_uncompress_file(a); + if(ret != ARCHIVE_RETRY) + return ret; + } } static int do_unstore_file(struct archive_read* a, - struct rar5* rar, - const void** buf, - size_t* size, - int64_t* offset) + struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { - const uint8_t* p; + const uint8_t* p; - if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && - rar->generic.split_after > 0) - { - int ret; + if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && + rar->generic.split_after > 0) + { + int ret; - rar->cstate.switch_multivolume = 1; - ret = advance_multivolume(a); - rar->cstate.switch_multivolume = 0; + rar->cstate.switch_multivolume = 1; + ret = advance_multivolume(a); + rar->cstate.switch_multivolume = 0; - if(ret != ARCHIVE_OK) { - /* Failed to advance to next multivolume archive file. */ - return ret; - } - } + if(ret != ARCHIVE_OK) { + /* Failed to advance to next multivolume archive + * file. */ + return ret; + } + } - size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); + size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); + if(to_read == 0) { + return ARCHIVE_EOF; + } - if(!read_ahead(a, to_read, &p)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "I/O error " - "when unstoring file"); - return ARCHIVE_FATAL; - } + if(!read_ahead(a, to_read, &p)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "I/O error when unstoring file"); + return ARCHIVE_FATAL; + } - if(ARCHIVE_OK != consume(a, to_read)) { - return ARCHIVE_EOF; - } + if(ARCHIVE_OK != consume(a, to_read)) { + return ARCHIVE_EOF; + } - if(buf) *buf = p; - if(size) *size = to_read; - if(offset) *offset = rar->cstate.last_unstore_ptr; + if(buf) *buf = p; + if(size) *size = to_read; + if(offset) *offset = rar->cstate.last_unstore_ptr; - rar->file.bytes_remaining -= to_read; - rar->cstate.last_unstore_ptr += to_read; + rar->file.bytes_remaining -= to_read; + rar->cstate.last_unstore_ptr += to_read; - update_crc(rar, p, to_read); - return ARCHIVE_OK; + update_crc(rar, p, to_read); + return ARCHIVE_OK; } static int do_unpack(struct archive_read* a, struct rar5* rar, - const void** buf, size_t* size, int64_t* offset) + const void** buf, size_t* size, int64_t* offset) { - enum COMPRESSION_METHOD { - STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, BEST = 5 - }; - - if(rar->file.service > 0) { - return do_unstore_file(a, rar, buf, size, offset); - } else { - switch(rar->cstate.method) { - case STORE: - return do_unstore_file(a, rar, buf, size, offset); - case FASTEST: - /* fallthrough */ - case FAST: - /* fallthrough */ - case NORMAL: - /* fallthrough */ - case GOOD: - /* fallthrough */ - case BEST: - return uncompress_file(a); - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Compression method not supported: 0x%08x", - rar->cstate.method); - - return ARCHIVE_FATAL; - } - } + enum COMPRESSION_METHOD { + STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, + BEST = 5 + }; + + if(rar->file.service > 0) { + return do_unstore_file(a, rar, buf, size, offset); + } else { + switch(rar->cstate.method) { + case STORE: + return do_unstore_file(a, rar, buf, size, + offset); + case FASTEST: + /* fallthrough */ + case FAST: + /* fallthrough */ + case NORMAL: + /* fallthrough */ + case GOOD: + /* fallthrough */ + case BEST: + return uncompress_file(a); + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Compression method not supported: 0x%x", + rar->cstate.method); + + return ARCHIVE_FATAL; + } + } #if !defined WIN32 - /* Not reached. */ - return ARCHIVE_OK; + /* Not reached. */ + return ARCHIVE_OK; #endif } static int verify_checksums(struct archive_read* a) { - int verify_crc; - struct rar5* rar = get_context(a); - - /* Check checksums only when actually unpacking the data. There's no need - * to calculate checksum when we're skipping data in solid archives - * (skipping in solid archives is the same thing as unpacking compressed - * data and discarding the result). */ - - if(!rar->skip_mode) { - /* Always check checkums if we're not in skip mode */ - verify_crc = 1; - } else { - /* We can override the logic above with a compile-time option - * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, and it - * will check checksums of unpacked data even when we're skipping it. - */ + int verify_crc; + struct rar5* rar = get_context(a); + + /* Check checksums only when actually unpacking the data. There's no + * need to calculate checksum when we're skipping data in solid archives + * (skipping in solid archives is the same thing as unpacking compressed + * data and discarding the result). */ + + if(!rar->skip_mode) { + /* Always check checksums if we're not in skip mode */ + verify_crc = 1; + } else { + /* We can override the logic above with a compile-time option + * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, + * and it will check checksums of unpacked data even when + * we're skipping it. */ #if defined CHECK_CRC_ON_SOLID_SKIP - /* Debug case */ - verify_crc = 1; + /* Debug case */ + verify_crc = 1; #else - /* Normal case */ - verify_crc = 0; + /* Normal case */ + verify_crc = 0; #endif - } - - if(verify_crc) { - /* During unpacking, on each unpacked block we're calling the - * update_crc() function. Since we are here, the unpacking process is - * already over and we can check if calculated checksum (CRC32 or - * BLAKE2sp) is the same as what is stored in the archive. - */ - if(rar->file.stored_crc32 > 0) { - /* Check CRC32 only when the file contains a CRC32 value for this - * file. */ - - if(rar->file.calculated_crc32 != rar->file.stored_crc32) { - /* Checksums do not match; the unpacked file is corrupted. */ - - DEBUG_CODE { - printf("Checksum error: CRC32 (was: %08x, expected: %08x)\n", - rar->file.calculated_crc32, rar->file.stored_crc32); - } + } + + if(verify_crc) { + /* During unpacking, on each unpacked block we're calling the + * update_crc() function. Since we are here, the unpacking + * process is already over and we can check if calculated + * checksum (CRC32 or BLAKE2sp) is the same as what is stored + * in the archive. */ + if(rar->file.stored_crc32 > 0) { + /* Check CRC32 only when the file contains a CRC32 + * value for this file. */ + + if(rar->file.calculated_crc32 != + rar->file.stored_crc32) { + /* Checksums do not match; the unpacked file + * is corrupted. */ + + DEBUG_CODE { + printf("Checksum error: CRC32 " + "(was: %08x, expected: %08x)\n", + rar->file.calculated_crc32, + rar->file.stored_crc32); + } #ifndef DONT_FAIL_ON_CRC_ERROR - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Checksum error: CRC32"); - return ARCHIVE_FATAL; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Checksum error: CRC32"); + return ARCHIVE_FATAL; #endif - } else { - DEBUG_CODE { - printf("Checksum OK: CRC32 (%08x/%08x)\n", - rar->file.stored_crc32, - rar->file.calculated_crc32); - } - } - } - - if(rar->file.has_blake2 > 0) { - /* BLAKE2sp is an optional checksum algorithm that is added to - * RARv5 archives when using the `-htb` switch during creation of - * archive. - * - * We now finalize the hash calculation by calling the `final` - * function. This will generate the final hash value we can use to - * compare it with the BLAKE2sp checksum that is stored in the - * archive. - * - * The return value of this `final` function is not very helpful, - * as it guards only against improper use. This is why we're - * explicitly ignoring it. */ - - uint8_t b2_buf[32]; - (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); - - if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { + } else { + DEBUG_CODE { + printf("Checksum OK: CRC32 " + "(%08x/%08x)\n", + rar->file.stored_crc32, + rar->file.calculated_crc32); + } + } + } + + if(rar->file.has_blake2 > 0) { + /* BLAKE2sp is an optional checksum algorithm that is + * added to RARv5 archives when using the `-htb` switch + * during creation of archive. + * + * We now finalize the hash calculation by calling the + * `final` function. This will generate the final hash + * value we can use to compare it with the BLAKE2sp + * checksum that is stored in the archive. + * + * The return value of this `final` function is not + * very helpful, as it guards only against improper use. + * This is why we're explicitly ignoring it. */ + + uint8_t b2_buf[32]; + (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); + + if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { #ifndef DONT_FAIL_ON_CRC_ERROR - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Checksum error: BLAKE2"); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Checksum error: BLAKE2"); - return ARCHIVE_FATAL; + return ARCHIVE_FATAL; #endif - } - } - } + } + } + } - /* Finalization for this file has been successfully completed. */ - return ARCHIVE_OK; + /* Finalization for this file has been successfully completed. */ + return ARCHIVE_OK; } static int verify_global_checksums(struct archive_read* a) { - return verify_checksums(a); + return verify_checksums(a); } static int rar5_read_data(struct archive_read *a, const void **buff, - size_t *size, int64_t *offset) { - int ret; - struct rar5* rar = get_context(a); - - if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Unpacker has written too many bytes"); - return ARCHIVE_FATAL; - } - - ret = use_data(rar, buff, size, offset); - if(ret == ARCHIVE_OK) - return ret; - - ret = do_unpack(a, rar, buff, size, offset); - if(ret != ARCHIVE_OK) { - return ret; - } - - if(rar->file.bytes_remaining == 0 && - rar->cstate.last_write_ptr == rar->file.unpacked_size) - { - /* If all bytes of current file were processed, run finalization. - * - * Finalization will check checksum against proper values. If - * some of the checksums will not match, we'll return an error - * value in the last `archive_read_data` call to signal an error - * to the user. */ - - return verify_global_checksums(a); - } - - return ARCHIVE_OK; + size_t *size, int64_t *offset) { + int ret; + struct rar5* rar = get_context(a); + + if(rar->file.dir > 0) { + /* Don't process any data if this file entry was declared + * as a directory. This is needed, because entries marked as + * directory doesn't have any dictionary buffer allocated, so + * it's impossible to perform any decompression. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't decompress an entry marked as a directory"); + return ARCHIVE_FAILED; + } + + if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Unpacker has written too many bytes"); + return ARCHIVE_FATAL; + } + + ret = use_data(rar, buff, size, offset); + if(ret == ARCHIVE_OK) { + return ret; + } + + if(rar->file.eof == 1) { + return ARCHIVE_EOF; + } + + ret = do_unpack(a, rar, buff, size, offset); + if(ret != ARCHIVE_OK) { + return ret; + } + + if(rar->file.bytes_remaining == 0 && + rar->cstate.last_write_ptr == rar->file.unpacked_size) + { + /* If all bytes of current file were processed, run + * finalization. + * + * Finalization will check checksum against proper values. If + * some of the checksums will not match, we'll return an error + * value in the last `archive_read_data` call to signal an error + * to the user. */ + + rar->file.eof = 1; + return verify_global_checksums(a); + } + + return ARCHIVE_OK; } static int rar5_read_data_skip(struct archive_read *a) { - struct rar5* rar = get_context(a); - - if(rar->main.solid) { - /* In solid archives, instead of skipping the data, we need to extract - * it, and dispose the result. The side effect of this operation will - * be setting up the initial window buffer state needed to be able to - * extract the selected file. */ - - int ret; - - /* Make sure to process all blocks in the compressed stream. */ - while(rar->file.bytes_remaining > 0) { - /* Setting the "skip mode" will allow us to skip checksum checks - * during data skipping. Checking the checksum of skipped data - * isn't really necessary and it's only slowing things down. - * - * This is incremented instead of setting to 1 because this data - * skipping function can be called recursively. */ - rar->skip_mode++; - - /* We're disposing 1 block of data, so we use triple NULLs in - * arguments. - */ - ret = rar5_read_data(a, NULL, NULL, NULL); - - /* Turn off "skip mode". */ - rar->skip_mode--; - - if(ret < 0) { - /* Propagate any potential error conditions to the caller. */ - return ret; - } - } - } else { - /* In standard archives, we can just jump over the compressed stream. - * Each file in non-solid archives starts from an empty window buffer. - */ - - if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { - return ARCHIVE_FATAL; - } - - rar->file.bytes_remaining = 0; - } - - return ARCHIVE_OK; + struct rar5* rar = get_context(a); + + if(rar->main.solid) { + /* In solid archives, instead of skipping the data, we need to + * extract it, and dispose the result. The side effect of this + * operation will be setting up the initial window buffer state + * needed to be able to extract the selected file. */ + + int ret; + + /* Make sure to process all blocks in the compressed stream. */ + while(rar->file.bytes_remaining > 0) { + /* Setting the "skip mode" will allow us to skip + * checksum checks during data skipping. Checking the + * checksum of skipped data isn't really necessary and + * it's only slowing things down. + * + * This is incremented instead of setting to 1 because + * this data skipping function can be called + * recursively. */ + rar->skip_mode++; + + /* We're disposing 1 block of data, so we use triple + * NULLs in arguments. */ + ret = rar5_read_data(a, NULL, NULL, NULL); + + /* Turn off "skip mode". */ + rar->skip_mode--; + + if(ret < 0) { + /* Propagate any potential error conditions + * to the caller. */ + return ret; + } + } + } else { + /* In standard archives, we can just jump over the compressed + * stream. Each file in non-solid archives starts from an empty + * window buffer. */ + + if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { + return ARCHIVE_FATAL; + } + + rar->file.bytes_remaining = 0; + } + + return ARCHIVE_OK; } static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, - int whence) + int whence) { - (void) a; - (void) offset; - (void) whence; + (void) a; + (void) offset; + (void) whence; - /* We're a streaming unpacker, and we don't support seeking. */ + /* We're a streaming unpacker, and we don't support seeking. */ - return ARCHIVE_FATAL; + return ARCHIVE_FATAL; } static int rar5_cleanup(struct archive_read *a) { - struct rar5* rar = get_context(a); - - free(rar->cstate.window_buf); + struct rar5* rar = get_context(a); - free(rar->cstate.filtered_buf); + free(rar->cstate.window_buf); + free(rar->cstate.filtered_buf); - free(rar->vol.push_buf); + free(rar->vol.push_buf); - free_filters(rar); - cdeque_free(&rar->cstate.filters); + free_filters(rar); + cdeque_free(&rar->cstate.filters); - free(rar); - a->format->data = NULL; + free(rar); + a->format->data = NULL; - return ARCHIVE_OK; + return ARCHIVE_OK; } static int rar5_capabilities(struct archive_read * a) { - (void) a; - return 0; + (void) a; + return 0; } static int rar5_has_encrypted_entries(struct archive_read *_a) { - (void) _a; + (void) _a; - /* Unsupported for now. */ - return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; + /* Unsupported for now. */ + return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; } static int rar5_init(struct rar5* rar) { - ssize_t i; + ssize_t i; - memset(rar, 0, sizeof(struct rar5)); + memset(rar, 0, sizeof(struct rar5)); - /* Decrypt the magic signature pattern. Check the comment near the - * `rar5_signature` symbol to read the rationale behind this. */ + /* Decrypt the magic signature pattern. Check the comment near the + * `rar5_signature` symbol to read the rationale behind this. */ - if(rar5_signature[0] == 243) { - for(i = 0; i < rar5_signature_size; i++) { - rar5_signature[i] ^= 0xA1; - } - } + if(rar5_signature[0] == 243) { + for(i = 0; i < rar5_signature_size; i++) { + rar5_signature[i] ^= 0xA1; + } + } - if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) - return ARCHIVE_FATAL; + if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) + return ARCHIVE_FATAL; - return ARCHIVE_OK; + return ARCHIVE_OK; } int archive_read_support_format_rar5(struct archive *_a) { - struct archive_read* ar; - int ret; - struct rar5* rar; - - if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) - return ret; - - rar = malloc(sizeof(*rar)); - if(rar == NULL) { - archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data"); - return ARCHIVE_FATAL; - } - - if(ARCHIVE_OK != rar5_init(rar)) { - archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter " - "buffer"); - return ARCHIVE_FATAL; - } - - ret = __archive_read_register_format(ar, - rar, - "rar5", - rar5_bid, - rar5_options, - rar5_read_header, - rar5_read_data, - rar5_read_data_skip, - rar5_seek_data, - rar5_cleanup, - rar5_capabilities, - rar5_has_encrypted_entries); - - if(ret != ARCHIVE_OK) { - (void) rar5_cleanup(ar); - } - - return ret; + struct archive_read* ar; + int ret; + struct rar5* rar; + + if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) + return ret; + + rar = malloc(sizeof(*rar)); + if(rar == NULL) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate rar5 data"); + return ARCHIVE_FATAL; + } + + if(ARCHIVE_OK != rar5_init(rar)) { + archive_set_error(&ar->archive, ENOMEM, + "Can't allocate rar5 filter buffer"); + return ARCHIVE_FATAL; + } + + ret = __archive_read_register_format(ar, + rar, + "rar5", + rar5_bid, + rar5_options, + rar5_read_header, + rar5_read_data, + rar5_read_data_skip, + rar5_seek_data, + rar5_cleanup, + rar5_capabilities, + rar5_has_encrypted_entries); + + if(ret != ARCHIVE_OK) { + (void) rar5_cleanup(ar); + } + + return ret; } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_raw.c b/contrib/libarchive/libarchive/archive_read_support_format_raw.c index 6d6faede7..b6328a952 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_raw.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_raw.c @@ -120,7 +120,9 @@ archive_read_format_raw_read_header(struct archive_read *a, archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_perm(entry, 0644); /* I'm deliberately leaving most fields unset here. */ - return (ARCHIVE_OK); + + /* Let the filter fill out any fields it might have. */ + return __archive_read_header(a, entry); } static int diff --git a/contrib/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libarchive/libarchive/archive_read_support_format_tar.c index 606381f9c..cac46f246 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_tar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_tar.c @@ -694,11 +694,13 @@ tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, size_t *unconsumed) { ssize_t bytes; - int err; + int err, eof_vol_header; const char *h; const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; + eof_vol_header = 0; + /* Loop until we find a workable header record. */ for (;;) { tar_flush_unconsumed(a, unconsumed); @@ -788,6 +790,8 @@ tar_read_header(struct archive_read *a, struct tar *tar, break; case 'V': /* GNU volume header */ err = header_volume(a, tar, entry, h, unconsumed); + if (err == ARCHIVE_EOF) + eof_vol_header = 1; break; case 'X': /* Used by SUN tar; same as 'x'. */ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; @@ -862,9 +866,17 @@ tar_read_header(struct archive_read *a, struct tar *tar, } return (err); } - if (err == ARCHIVE_EOF) - /* EOF when recursively reading a header is bad. */ - archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); + if (err == ARCHIVE_EOF) { + if (!eof_vol_header) { + /* EOF when recursively reading a header is bad. */ + archive_set_error(&a->archive, EINVAL, + "Damaged tar archive"); + } else { + /* If we encounter just a GNU volume header treat + * this situation as an empty archive */ + return (ARCHIVE_EOF); + } + } return (ARCHIVE_FATAL); } @@ -1944,6 +1956,15 @@ pax_attribute(struct archive_read *a, struct tar *tar, pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } + if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) { + if (strcmp(value, "file") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_FILE); + } else if (strcmp(value, "dir") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_DIRECTORY); + } + } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; diff --git a/contrib/libarchive/libarchive/archive_read_support_format_warc.c b/contrib/libarchive/libarchive/archive_read_support_format_warc.c index e8753853f..f076e48c4 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_warc.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_warc.c @@ -739,8 +739,9 @@ _warc_rdlen(const char *buf, size_t bsz) /* there must be at least one digit */ if (!isdigit((unsigned char)*val)) return -1; + errno = 0; len = strtol(val, &on, 10); - if (on != eol) { + if (errno != 0 || on != eol) { /* line must end here */ return -1; } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libarchive/libarchive/archive_read_support_format_xar.c index 6ff9cc4be..34253a52f 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_xar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_xar.c @@ -798,7 +798,8 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) xattr = file->xattr_list; while (xattr != NULL) { const void *d; - size_t outbytes, used; + size_t outbytes = 0; + size_t used = 0; r = move_reading_point(a, xattr->offset); if (r != ARCHIVE_OK) @@ -820,8 +821,18 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) r = checksum_final(a, xattr->a_sum.val, xattr->a_sum.len, xattr->e_sum.val, xattr->e_sum.len); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "Xattr checksum error"); + r = ARCHIVE_WARN; + break; + } + if (xattr->name.s == NULL) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "Xattr name error"); + r = ARCHIVE_WARN; break; + } archive_entry_xattr_add_entry(entry, xattr->name.s, d, outbytes); xattr = xattr->next; @@ -847,7 +858,7 @@ xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct xar *xar; - size_t used; + size_t used = 0; int r; xar = (struct xar *)(a->format->data); diff --git a/contrib/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libarchive/libarchive/archive_read_support_format_zip.c index 52ab358bb..7bc270c2a 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_zip.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_zip.c @@ -472,27 +472,49 @@ zip_time(const char *p) * triplets. id and size are 2 bytes each. */ static int -process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry) +process_extra(struct archive_read *a, struct archive_entry *entry, + const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; + struct zip *zip = (struct zip *)(a->format->data); if (extra_length == 0) { return ARCHIVE_OK; } if (extra_length < 4) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length); - return ARCHIVE_FAILED; + size_t i = 0; + /* Some ZIP files may have trailing 0 bytes. Let's check they + * are all 0 and ignore them instead of returning an error. + * + * This is not techincally correct, but some ZIP files look + * like this and other tools support those files - so let's + * also support them. + */ + for (; i < extra_length; i++) { + if (p[i] != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Too-small extra data: " + "Need at least 4 bytes, " + "but only found %d bytes", + (int)extra_length); + return ARCHIVE_FAILED; + } + } + + return ARCHIVE_OK; } + while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Extra data overflow: Need %d bytes but only found %d bytes", + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: " + "Need %d bytes but only found %d bytes", (int)datasize, (int)(extra_length - offset)); return ARCHIVE_FAILED; } @@ -507,9 +529,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->uncompressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit uncompressed size"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "uncompressed size"); return ARCHIVE_FAILED; } zip_entry->uncompressed_size = t; @@ -519,9 +544,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->compressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit compressed size"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "compressed size"); return ARCHIVE_FAILED; } zip_entry->compressed_size = t; @@ -531,9 +559,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct if (zip_entry->local_header_offset == 0xffffffff) { uint64_t t = 0; if (datasize < 8 - || (t = archive_le64dec(p + offset)) > INT64_MAX) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed 64-bit local header offset"); + || (t = archive_le64dec(p + offset)) > + INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed 64-bit " + "local header offset"); return ARCHIVE_FAILED; } zip_entry->local_header_offset = t; @@ -566,7 +597,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct /* Extended time field "UT". */ int flags; if (datasize == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete extended time field"); return ARCHIVE_FAILED; } @@ -648,7 +680,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct * if bitmap & 1, 2 byte "version made by" * if bitmap & 2, 2 byte "internal file attributes" * if bitmap & 4, 4 byte "external file attributes" - * if bitmap & 8, 2 byte comment length + n byte comment + * if bitmap & 8, 2 byte comment length + n byte + * comment */ int bitmap, bitmap_last; @@ -699,13 +732,18 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit - if (0x10 == (external_attributes & 0x10)) { - zip_entry->mode = AE_IFDIR | 0775; + if (0x10 == (external_attributes & + 0x10)) { + zip_entry->mode = + AE_IFDIR | 0775; } else { - zip_entry->mode = AE_IFREG | 0664; + zip_entry->mode = + AE_IFREG | 0664; } - if (0x01 == (external_attributes & 0x01)) { - // Read-only bit; strip write permissions + if (0x01 == (external_attributes & + 0x01)) { + /* Read-only bit; + * strip write permissions */ zip_entry->mode &= 0555; } } else { @@ -732,6 +770,59 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } break; } + case 0x7075: + { + /* Info-ZIP Unicode Path Extra Field. */ + if (datasize < 5 || entry == NULL) + break; + offset += 5; + datasize -= 5; + + /* The path name in this field is always encoded + * in UTF-8. */ + if (zip->sconv_utf8 == NULL) { + zip->sconv_utf8 = + archive_string_conversion_from_charset( + &a->archive, "UTF-8", 1); + /* If the converter from UTF-8 is not + * available, then the path name from the main + * field will more likely be correct. */ + if (zip->sconv_utf8 == NULL) + break; + } + + /* Make sure the CRC32 of the filename matches. */ + if (!zip->ignore_crc32) { + const char *cp = archive_entry_pathname(entry); + if (cp) { + unsigned long file_crc = + zip->crc32func(0, cp, strlen(cp)); + unsigned long utf_crc = + archive_le32dec(p + offset - 4); + if (file_crc != utf_crc) { +#ifdef DEBUG + fprintf(stderr, + "CRC filename mismatch; " + "CDE is %lx, but UTF8 " + "is outdated with %lx\n", + file_crc, utf_crc); +#endif + break; + } + } + } + + if (archive_entry_copy_pathname_l(entry, + p + offset, datasize, zip->sconv_utf8) != 0) { + /* Ignore the error, and fallback to the path + * name from the main field. */ +#ifdef DEBUG + fprintf(stderr, "Failed to read the ZIP " + "0x7075 extra field path.\n"); +#endif + } + break; + } case 0x7855: /* Info-ZIP Unix Extra Field (type 2) "Ux". */ #ifdef DEBUG @@ -766,7 +857,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ - gidsize = 0xff & (int)p[offset+2+uidsize]; + gidsize = 0xff & + (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( @@ -783,7 +875,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct case 0x9901: /* WinZip AES extra data field. */ if (datasize < 6) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete AES field"); return ARCHIVE_FAILED; } @@ -803,12 +896,6 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } offset += datasize; } - if (offset != extra_length) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed extra data: Consumed %d bytes of %d bytes", - (int)offset, (int)extra_length); - return ARCHIVE_FAILED; - } return ARCHIVE_OK; } @@ -928,7 +1015,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } - if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) { + if (ARCHIVE_OK != process_extra(a, entry, h, extra_length, + zip_entry)) { return ARCHIVE_FATAL; } __archive_read_consume(a, extra_length); @@ -945,8 +1033,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip_entry->mode |= 0664; } - /* Windows archivers sometimes use backslash as the directory separator. - Normalize to slash. */ + /* Windows archivers sometimes use backslash as the directory + * separator. Normalize to slash. */ if (zip_entry->system == 0 && (wp = archive_entry_pathname_w(entry)) != NULL) { if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) { @@ -1255,7 +1343,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, zip->entry->crc32 = archive_le32dec(p + 4); compressed = archive_le64dec(p + 8); uncompressed = archive_le64dec(p + 16); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + if (compressed > INT64_MAX || uncompressed > + INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); @@ -1372,7 +1461,8 @@ consume_optional_marker(struct archive_read *a, struct zip *zip) zip->entry->crc32 = archive_le32dec(p); compressed = archive_le64dec(p + 4); uncompressed = archive_le64dec(p + 12); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + if (compressed > INT64_MAX || + uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); @@ -1444,12 +1534,16 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) } alone_header; #pragma pack(pop) - /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma that - * is a part of XZ Utils. The stream format stored inside ZIPX file is a - * modified "lzma alone" file format, that was used by the `lzma` utility - * which was later deprecated in favour of `xz` utility. Since those - * formats are nearly the same, we can use a standard "lzma alone" decoder - * from XZ Utils. */ + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma + * that is a part of XZ Utils. The stream format stored inside ZIPX + * file is a modified "lzma alone" file format, that was used by the + * `lzma` utility which was later deprecated in favour of `xz` utility. * Since those formats are nearly the same, we can use a standard + * "lzma alone" decoder from XZ Utils. */ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); @@ -1477,8 +1571,8 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * lzma_params is a 5-byte blob that has to be decoded to extract * parameters of this LZMA stream. The uncompressed_size field is an * uint64_t value that contains information about the size of the - * uncompressed file, or UINT64_MAX if this value is unknown. The - * part is the actual lzma-compressed data stream. + * uncompressed file, or UINT64_MAX if this value is unknown. + * The part is the actual lzma-compressed data stream. * * Now here's the structure of the stream inside the ZIPX file: * @@ -1488,17 +1582,17 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * 2byte 2byte 5 bytes n bytes * * - * This means that the ZIPX file contains an additional magic1 and magic2 - * headers, the lzma_params field contains the same parameter set as in the - * "lzma alone" format, and the field is the same as in the "lzma - * alone" format as well. Note that also the zipx format is missing the - * uncompressed_size field. + * This means that the ZIPX file contains an additional magic1 and + * magic2 headers, the lzma_params field contains the same parameter + * set as in the "lzma alone" format, and the field is the + * same as in the "lzma alone" format as well. Note that also the zipx + * format is missing the uncompressed_size field. * - * So, in order to use the "lzma alone" decoder for the zipx lzma stream, - * we simply need to shuffle around some fields, prepare a new lzma alone - * header, feed it into lzma alone decoder so it will initialize itself - * properly, and then we can start feeding normal zipx lzma stream into the - * decoder. + * So, in order to use the "lzma alone" decoder for the zipx lzma + * stream, we simply need to shuffle around some fields, prepare a new + * lzma alone header, feed it into lzma alone decoder so it will + * initialize itself properly, and then we can start feeding normal + * zipx lzma stream into the decoder. */ /* Read magic1,magic2,lzma_params from the ZIPX stream. */ @@ -1514,8 +1608,8 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) return (ARCHIVE_FATAL); } - /* Prepare an lzma alone header: copy the lzma_params blob into a proper - * place into the lzma alone header. */ + /* Prepare an lzma alone header: copy the lzma_params blob into + * a proper place into the lzma alone header. */ memcpy(&alone_header.bytes[0], p + 4, 5); /* Initialize the 'uncompressed size' field to unknown; we'll manually @@ -1541,8 +1635,9 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; zip->zipx_lzma_stream.total_out = 0; - /* Feed only the header into the lzma alone decoder. This will effectively - * initialize the decoder, and will not produce any output bytes yet. */ + /* Feed only the header into the lzma alone decoder. This will + * effectively initialize the decoder, and will not produce any + * output bytes yet. */ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); if (r != LZMA_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, @@ -1617,7 +1712,8 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, "xz premature end of stream"); return (ARCHIVE_FATAL); } @@ -1662,12 +1758,13 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, return (ret); } - /* Fetch more compressed data. The same note as in deflate handler applies - * here as well: + /* Fetch more compressed data. The same note as in deflate handler + * applies here as well: * * Note: '1' here is a performance optimization. Recall that the - * decompression layer returns a count of available bytes; asking for more - * than that forces the decompressor to combine reads by copying data. + * decompression layer returns a count of available bytes; asking for + * more than that forces the decompressor to combine reads by copying + * data. */ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail < 0) { @@ -1684,8 +1781,9 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; zip->zipx_lzma_stream.avail_out = - /* These lzma_alone streams lack end of stream marker, so let's make - * sure the unpacker won't try to unpack more than it's supposed to. */ + /* These lzma_alone streams lack end of stream marker, so let's + * make sure the unpacker won't try to unpack more than it's + * supposed to. */ zipmin((int64_t) zip->uncompressed_buffer_size, zip->entry->uncompressed_size - zip->entry_uncompressed_bytes_read); @@ -1810,7 +1908,8 @@ zipx_ppmd8_init(struct archive_read *a, struct zip *zip) return (ARCHIVE_FATAL); } - __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, restore_method); + __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, + restore_method); /* Allocate the buffer that will hold uncompressed data. */ free(zip->uncompressed_buffer); @@ -1856,8 +1955,8 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, return ret; } - /* Fetch for more data. We're reading 1 byte here, but libarchive should - * prefetch more bytes. */ + /* Fetch for more data. We're reading 1 byte here, but libarchive + * should prefetch more bytes. */ (void) __archive_read_ahead(a, 1, &bytes_avail); if(bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -1871,7 +1970,8 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, /* Decompression loop. */ do { - int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(&zip->ppmd8); + int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol( + &zip->ppmd8); if(sym < 0) { zip->end_of_entry = 1; break; @@ -1880,8 +1980,9 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, /* This field is set by ppmd_read() when there was no more data * to be read. */ if(zip->ppmd8_stream_failed) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated PPMd8 file body"); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); return (ARCHIVE_FATAL); } @@ -1985,9 +2086,10 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); if(in_bytes < 1) { - /* libbz2 doesn't complain when caller feeds avail_in == 0. It will - * actually return success in this case, which is undesirable. This is - * why we need to make this check manually. */ + /* libbz2 doesn't complain when caller feeds avail_in == 0. + * It will actually return success in this case, which is + * undesirable. This is why we need to make this check + * manually. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated bzip2 file body"); @@ -2014,16 +2116,18 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, case BZ_OK: break; default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up bzip2 decompressor"); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up bzip2 " + "decompressor"); return ARCHIVE_FATAL; } zip->end_of_entry = 1; break; case BZ_OK: - /* The decompressor has successfully decoded this chunk of - * data, but more data is still in queue. */ + /* The decompressor has successfully decoded this + * chunk of data, but more data is still in queue. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2131,8 +2235,10 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, if (zip->tctx_valid || zip->cctx_valid) { if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { size_t buff_remaining = - (zip->decrypted_buffer + zip->decrypted_buffer_size) - - (zip->decrypted_ptr + zip->decrypted_bytes_remaining); + (zip->decrypted_buffer + + zip->decrypted_buffer_size) + - (zip->decrypted_ptr + + zip->decrypted_bytes_remaining); if (buff_remaining > (size_t)bytes_avail) buff_remaining = (size_t)bytes_avail; @@ -2143,12 +2249,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, + buff_remaining) > zip->entry_bytes_remaining) { if (zip->entry_bytes_remaining < - (int64_t)zip->decrypted_bytes_remaining) + (int64_t)zip->decrypted_bytes_remaining) buff_remaining = 0; else buff_remaining = (size_t)zip->entry_bytes_remaining - - zip->decrypted_bytes_remaining; + - zip->decrypted_bytes_remaining; } } if (buff_remaining > 0) { @@ -2167,7 +2273,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, + zip->decrypted_bytes_remaining, &dsize); } - zip->decrypted_bytes_remaining += buff_remaining; + zip->decrypted_bytes_remaining += + buff_remaining; } } bytes_avail = zip->decrypted_bytes_remaining; @@ -2751,7 +2858,7 @@ archive_read_format_zip_cleanup(struct archive_read *a) inflateEnd(&zip->stream); #endif -#if HAVA_LZMA_H && HAVE_LIBLZMA +#if HAVE_LZMA_H && HAVE_LIBLZMA if (zip->zipx_lzma_valid) { lzma_end(&zip->zipx_lzma_stream); } @@ -3391,7 +3498,8 @@ expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) } static int -slurp_central_directory(struct archive_read *a, struct zip *zip) +slurp_central_directory(struct archive_read *a, struct archive_entry* entry, + struct zip *zip) { ssize_t i; unsigned found; @@ -3501,8 +3609,10 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); - /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ - /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ + /* disk_start = archive_le16dec(p + 34); + * Better be zero. + * internal_attributes = archive_le16dec(p + 36); + * text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; @@ -3538,7 +3648,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) "Truncated ZIP file header"); return ARCHIVE_FATAL; } - if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) { + if (ARCHIVE_OK != process_extra(a, entry, p + filename_length, + extra_length, zip_entry)) { return ARCHIVE_FATAL; } @@ -3560,7 +3671,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) * a directory. We should treat it as a non * resource fork file to expose it. */ if (name[filename_length-1] != '/' && - (r - name < 3 || r[0] != '.' || r[1] != '_')) { + (r - name < 3 || r[0] != '.' || + r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ @@ -3637,8 +3749,10 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, switch(rsrc->compression) { case 0: /* No compression. */ if (rsrc->uncompressed_size != rsrc->compressed_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed OS X metadata entry: inconsistent size"); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed OS X metadata entry: " + "inconsistent size"); return (ARCHIVE_FATAL); } #ifdef HAVE_ZLIB_H @@ -3797,7 +3911,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, a->archive.archive_format_name = "ZIP"; if (zip->zip_entries == NULL) { - r = slurp_central_directory(a, zip); + r = slurp_central_directory(a, entry, zip); if (r != ARCHIVE_OK) return r; /* Get first entry whose local header offset is lower than @@ -3827,8 +3941,8 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, __archive_read_reset_passphrase(a); /* File entries are sorted by the header offset, we should mostly - * use __archive_read_consume to advance a read point to avoid redundant - * data reading. */ + * use __archive_read_consume to advance a read point to avoid + * redundant data reading. */ offset = archive_filter_bytes(&a->archive, 0); if (offset < zip->entry->local_header_offset) __archive_read_consume(a, diff --git a/contrib/libarchive/libarchive/archive_util.c b/contrib/libarchive/libarchive/archive_util.c index 7d7535f50..913c83f86 100644 --- a/contrib/libarchive/libarchive/archive_util.c +++ b/contrib/libarchive/libarchive/archive_util.c @@ -449,7 +449,7 @@ __archive_mktemp(const char *tmpdir) temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } - if (stat(temp_name.s, &st) < 0) + if (la_stat(temp_name.s, &st) < 0) goto exit_tmpfile; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_xz.c b/contrib/libarchive/libarchive/archive_write_add_filter_xz.c index b0f25a6ef..0f7c8cfc3 100644 --- a/contrib/libarchive/libarchive/archive_write_add_filter_xz.c +++ b/contrib/libarchive/libarchive/archive_write_add_filter_xz.c @@ -390,10 +390,13 @@ archive_compressor_xz_options(struct archive_write_filter *f, data->compression_level = 6; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_WARN); - data->threads = (int)strtoul(value, NULL, 10); - if (data->threads == 0 && errno != 0) { + errno = 0; + data->threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { data->threads = 1; return (ARCHIVE_WARN); } diff --git a/contrib/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libarchive/libarchive/archive_write_disk_posix.c index 6d9e7fcc9..dbc6d3534 100644 --- a/contrib/libarchive/libarchive/archive_write_disk_posix.c +++ b/contrib/libarchive/libarchive/archive_write_disk_posix.c @@ -2016,7 +2016,7 @@ restore_entry(struct archive_write_disk *a) * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) - r = stat(a->name, &a->st); + r = la_stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. @@ -2182,7 +2182,7 @@ create_filesystem_object(struct archive_write_disk *a) #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else - r = stat(a->name, &st); + r = la_stat(a->name, &st); #endif if (r != 0) r = errno; @@ -2687,7 +2687,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * This is needed to extract hardlinks over * symlinks. */ - r = stat(head, &st); + r = la_stat(head, &st); if (r != 0) { tail[0] = c; if (errno == ENOENT) { @@ -3027,7 +3027,7 @@ create_dir(struct archive_write_disk *a, char *path) * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ - if (stat(path, &st) == 0) { + if (la_stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { @@ -3085,7 +3085,7 @@ create_dir(struct archive_write_disk *a, char *path) * don't add it to the fixup list here, as it's already been * added. */ - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", diff --git a/contrib/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libarchive/libarchive/archive_write_set_format_pax.c index 2c34f203c..bb99f1fff 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_pax.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_pax.c @@ -1112,6 +1112,10 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && acl_types != 0) need_extension = 1; + /* If the symlink type is defined, we need an extension */ + if (!need_extension && archive_entry_symlink_type(entry_main) > 0) + need_extension = 1; + /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who @@ -1245,6 +1249,17 @@ archive_write_pax_header(struct archive_write *a, archive_string_free(&entry_name); return (ARCHIVE_FATAL); } + + /* Store extended symlink information */ + if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_FILE) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "file"); + } else if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_DIRECTORY) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "dir"); + } } /* Only regular files have data. */ diff --git a/contrib/libarchive/libarchive/archive_write_set_format_xar.c b/contrib/libarchive/libarchive/archive_write_set_format_xar.c index 495f0d441..5b8ce1a07 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_xar.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_xar.c @@ -496,10 +496,13 @@ xar_options(struct archive_write *a, const char *key, const char *value) return (ARCHIVE_OK); } if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_FAILED); - xar->opt_threads = (int)strtoul(value, NULL, 10); - if (xar->opt_threads == 0 && errno != 0) { + errno = 0; + xar->opt_threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { xar->opt_threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, diff --git a/contrib/libarchive/libarchive/test/test_entry.c b/contrib/libarchive/libarchive/test/test_entry.c index 6ca1e2415..3fb17d364 100644 --- a/contrib/libarchive/libarchive/test/test_entry.c +++ b/contrib/libarchive/libarchive/test/test_entry.c @@ -27,6 +27,10 @@ __FBSDID("$FreeBSD$"); #include +#ifdef HAVE_LINUX_FS_H +#include /* for Linux file flags */ +#endif + #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { @@ -337,16 +341,37 @@ DEFINE_TEST(test_entry) /* TODO: Make this system-independent. */ assertEqualString(archive_entry_fflags_text(e), "uappnd,nouchg,nodump,noopaque,uunlnk,nosystem"); +#endif + +#if defined(__FreeBSD__) || defined(__APPLE__) /* Test archive_entry_copy_fflags_text_w() */ - archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,uunlnk"); + archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,hidden"); archive_entry_fflags(e, &set, &clear); - assertEqualInt(16, set); - assertEqualInt(7, clear); + assertEqualInt(UF_HIDDEN, set); + assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear); /* Test archive_entry_copy_fflags_text() */ - archive_entry_copy_fflags_text(e, " ,nouappnd, nouchg, dump,uunlnk"); + archive_entry_copy_fflags_text(e, " ,nouappnd, nouchg, dump,hidden"); + archive_entry_fflags(e, &set, &clear); + assertEqualInt(UF_HIDDEN, set); + assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear); +#elif defined(_WIN32) && !defined(CYGWIN) + archive_entry_copy_fflags_text_w(e, L"rdonly,hidden,nosystem"); + archive_entry_fflags(e, &set, &clear); + assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set); + assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear); + archive_entry_copy_fflags_text(e, "rdonly,hidden,nosystem"); + archive_entry_fflags(e, &set, &clear); + assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set); + assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear); +#elif defined FS_IOC_GETFLAGS /* Linux */ + archive_entry_copy_fflags_text_w(e, L"sappnd,schg,dump,noundel"); + archive_entry_fflags(e, &set, &clear); + assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set); + assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear); + archive_entry_copy_fflags_text(e, "sappnd,schg,dump,noundel"); archive_entry_fflags(e, &set, &clear); - assertEqualInt(16, set); - assertEqualInt(7, clear); + assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set); + assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear); #endif /* See test_acl_basic.c for tests of ACL set/get consistency. */ diff --git a/contrib/libarchive/libarchive/test/test_fuzz.c b/contrib/libarchive/libarchive/test/test_fuzz.c index 76557d541..a9be5ee1c 100644 --- a/contrib/libarchive/libarchive/test/test_fuzz.c +++ b/contrib/libarchive/libarchive/test/test_fuzz.c @@ -58,6 +58,14 @@ test_fuzz(const struct files *filesets) size_t blk_size; int64_t blk_offset; int n; + const char *skip_fuzz_tests; + + skip_fuzz_tests = getenv("SKIP_TEST_FUZZ"); + if (skip_fuzz_tests != NULL) { + skipping("Skipping fuzz tests due to SKIP_TEST_FUZZ " + "environment variable"); + return; + } for (n = 0; filesets[n].names != NULL; ++n) { const size_t buffsize = 30000000; diff --git a/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c b/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c index 705b3d989..7dd19157d 100644 --- a/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c +++ b/contrib/libarchive/libarchive/test/test_read_disk_directory_traversals.c @@ -40,7 +40,30 @@ atimeIsUpdated(void) { const char *fn = "fs_noatime"; struct stat st; - +#if defined(_WIN32) && !defined(CYGWIN) + char *buff = NULL; + char *ptr; + int r; + + r = systemf("fsutil behavior query disableLastAccess > query_atime"); + if (r == 0) { + buff = slurpfile(NULL, "query_atime"); + if (buff != NULL) { + ptr = buff; + while(*ptr != '\0' && !isdigit(*ptr)) { + ptr++; + } + if (*ptr == '0') { + free(buff); + return(1); + } else if (*ptr == '1' || *ptr == '2') { + free(buff); + return(0); + } + free(buff); + } + } +#endif if (!assertMakeFile(fn, 0666, "a")) return (0); if (!assertUtimes(fn, 1, 0, 1, 0)) @@ -570,13 +593,13 @@ test_symlink_hybrid(void) assertMakeDir("h", 0755); assertChdir("h"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); @@ -727,13 +750,13 @@ test_symlink_logical(void) assertMakeDir("l", 0755); assertChdir("l"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Note: this test uses archive_read_next_header() @@ -961,8 +984,8 @@ test_symlink_logical_loop(void) assertMakeDir("d1/d2/d3", 0755); assertMakeDir("d2", 0755); assertMakeFile("d2/file1", 0644, "d2/file1"); - assertMakeSymlink("d1/d2/ld1", "../../d1"); - assertMakeSymlink("d1/d2/ld2", "../../d2"); + assertMakeSymlink("d1/d2/ld1", "../../d1", 1); + assertMakeSymlink("d1/d2/ld2", "../../d2", 1); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); @@ -1567,6 +1590,254 @@ test_nodump(void) archive_entry_free(ae); } +static void +test_parent(void) +{ + struct archive *a; + struct archive_entry *ae; + const void *p; + size_t size; + int64_t offset; + int file_count; + int match_count; + int r; + + assertMakeDir("lock", 0311); + assertMakeDir("lock/dir1", 0755); + assertMakeFile("lock/dir1/f1", 0644, "0123456789"); + assertMakeDir("lock/lock2", 0311); + assertMakeDir("lock/lock2/dir1", 0755); + assertMakeFile("lock/lock2/dir1/f1", 0644, "0123456789"); + + assert((ae = archive_entry_new()) != NULL); + assert((a = archive_read_disk_new()) != NULL); + + /* + * Test1: Traverse lock/dir1 as . + */ + assertChdir("lock/dir1"); + + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), ".") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + assertChdir("../.."); + + /* + * Test2: Traverse lock/dir1 directly + */ + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), "lock/dir1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + /* + * Test3: Traverse lock/dir1/. + */ + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1/.")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), "lock/dir1/.") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/./f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + /* + * Test4: Traverse lock/lock2/dir1 from inside lock. + * + * This test is expected to fail on platforms with no O_EXEC or + * equivalent (e.g. O_PATH on Linux or O_SEARCH on SunOS), because + * the current traversal code can't handle the case where it can't + * obtain an open fd for the initial current directory. We need to + * check that condition here, because if O_EXEC _does_ exist, we don't + * want to overlook any failure. + */ + assertChdir("lock"); + + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock2/dir1")); + + archive_entry_clear(ae); + r = archive_read_next_header2(a, ae); + if (r == ARCHIVE_FAILED) { +#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC) + assertEqualIntA(a, ARCHIVE_OK, r); +#endif + /* Close the disk object. */ + archive_read_close(a); + } else { + file_count = 2; + match_count = 0; + while (file_count--) { + if (file_count == 0) + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), + "lock2/dir1") == 0) { + assertEqualInt(archive_entry_filetype(ae), + AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), + "lock2/dir1/f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), + AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, + &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, + &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory + * traverse tries to ascend past the initial directory, since + * it lacks permission to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, + archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + } + + assertChdir(".."); + + /* Destroy the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + archive_entry_free(ae); +} + DEFINE_TEST(test_read_disk_directory_traversals) { /* Basic test. */ @@ -1583,4 +1854,6 @@ DEFINE_TEST(test_read_disk_directory_traversals) test_callbacks(); /* Test nodump. */ test_nodump(); + /* Test parent overshoot. */ + test_parent(); } diff --git a/contrib/libarchive/libarchive/test/test_read_extract.c b/contrib/libarchive/libarchive/test/test_read_extract.c index d4cae3f94..1f0e86d9e 100644 --- a/contrib/libarchive/libarchive/test/test_read_extract.c +++ b/contrib/libarchive/libarchive/test/test_read_extract.c @@ -161,7 +161,7 @@ DEFINE_TEST(test_read_extract) assertIsDir("dir4/b", 0755); assertIsDir("dir4/c", 0711); if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); free(buff); free(file_buff); diff --git a/contrib/libarchive/libarchive/test/test_read_format_mtree.c b/contrib/libarchive/libarchive/test/test_read_format_mtree.c index f6f71a361..865dda874 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_mtree.c +++ b/contrib/libarchive/libarchive/test/test_read_format_mtree.c @@ -717,4 +717,28 @@ DEFINE_TEST(test_read_format_mtree_nonexistent_contents_file) assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +/* + * Check mtree file with non-printable ascii characters + */ +DEFINE_TEST(test_read_format_mtree_noprint) +{ + const char reffile[] = "test_read_format_mtree_noprint.mtree"; + struct archive_entry *ae; + struct archive *a; + extract_reference_file(reffile); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, reffile, 11)); + + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assertEqualString("Can't parse line 3", archive_error_string(a)); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/contrib/libarchive/libarchive/test/test_read_format_mtree_noprint.mtree.uu b/contrib/libarchive/libarchive/test/test_read_format_mtree_noprint.mtree.uu new file mode 100644 index 000000000..7965cdf29 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_mtree_noprint.mtree.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_mtree_noprint.mtree +K(VUT7!E/61I<@ID:7)?P[;%H<2'('1Y<&4]9&ER"@`` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar.c b/contrib/libarchive/libarchive/test/test_read_format_rar.c index 6392d8f50..f08b06bc6 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_rar.c +++ b/contrib/libarchive/libarchive/test/test_read_format_rar.c @@ -28,6 +28,22 @@ #include +DEFINE_TEST(test_read_format_rar_set_format) +{ + struct archive *a; + struct archive_entry *ae; + const char reffile[] = "test_read_format_rar.rar"; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR)); + assertA(0 == archive_read_open_filename(a, reffile, 10240)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_rar_basic) { char buff[64]; @@ -3740,3 +3756,26 @@ DEFINE_TEST(test_read_format_rar_multivolume_uncompressed_files) assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } + +DEFINE_TEST(test_read_format_rar_ppmd_use_after_free) +{ + uint8_t buf[16]; + const char* reffile = "test_read_format_rar_ppmd_use_after_free.rar"; + + struct archive_entry *ae; + struct archive *a; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_open_filename(a, reffile, 10240)); + + assertA(ARCHIVE_OK == archive_read_next_header(a, &ae)); + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + assertA(ARCHIVE_OK == archive_read_next_header(a, &ae)); + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5.c b/contrib/libarchive/libarchive/test/test_read_format_rar5.c index 9610b8320..1408f37c4 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_rar5.c +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5.c @@ -28,173 +28,189 @@ * help. */ #define __LIBARCHIVE_BUILD #include +#include #define PROLOGUE(reffile) \ - struct archive_entry *ae; \ - struct archive *a; \ - \ - (void) a; /* Make the compiler happy if we won't use this variables */ \ - (void) ae; /* in the test cases. */ \ - \ - extract_reference_file(reffile); \ - assert((a = archive_read_new()) != NULL); \ - assertA(0 == archive_read_support_filter_all(a)); \ - assertA(0 == archive_read_support_format_all(a)); \ - assertA(0 == archive_read_open_filename(a, reffile, 10240)) + struct archive_entry *ae; \ + struct archive *a; \ + \ + (void) a; /* Make the compiler happy if we won't use this variables */ \ + (void) ae; /* in the test cases. */ \ + \ + extract_reference_file(reffile); \ + assert((a = archive_read_new()) != NULL); \ + assertA(0 == archive_read_support_filter_all(a)); \ + assertA(0 == archive_read_support_format_all(a)); \ + assertA(0 == archive_read_open_filename(a, reffile, 10240)) #define PROLOGUE_MULTI(reffile) \ - struct archive_entry *ae; \ - struct archive *a; \ - \ - (void) a; \ - (void) ae; \ - \ - extract_reference_files(reffile); \ - assert((a = archive_read_new()) != NULL); \ - assertA(0 == archive_read_support_filter_all(a)); \ - assertA(0 == archive_read_support_format_all(a)); \ - assertA(0 == archive_read_open_filenames(a, reffile, 10240)) + struct archive_entry *ae; \ + struct archive *a; \ + \ + (void) a; \ + (void) ae; \ + \ + extract_reference_files(reffile); \ + assert((a = archive_read_new()) != NULL); \ + assertA(0 == archive_read_support_filter_all(a)); \ + assertA(0 == archive_read_support_format_all(a)); \ + assertA(0 == archive_read_open_filenames(a, reffile, 10240)) #define EPILOGUE() \ - assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); \ - assertEqualInt(ARCHIVE_OK, archive_read_free(a)) + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); \ + assertEqualInt(ARCHIVE_OK, archive_read_free(a)) static int verify_data(const uint8_t* data_ptr, int magic, int size) { - int i = 0; + int i = 0; - /* This is how the test data inside test files was generated; - * we are re-generating it here and we check if our re-generated - * test data is the same as in the test file. If this test is - * failing it's either because there's a bug in the test case, - * or the unpacked data is corrupted. */ + /* This is how the test data inside test files was generated; + * we are re-generating it here and we check if our re-generated + * test data is the same as in the test file. If this test is + * failing it's either because there's a bug in the test case, + * or the unpacked data is corrupted. */ - for(i = 0; i < size / 4; ++i) { - const int k = i + 1; - const signed int* lptr = (const signed int*) &data_ptr[i * 4]; - signed int val = k * k - 3 * k + (1 + magic); + for(i = 0; i < size / 4; ++i) { + const int k = i + 1; + const signed int* lptr = (const signed int*) &data_ptr[i * 4]; + signed int val = k * k - 3 * k + (1 + magic); - if(val < 0) - val = 0; + if(val < 0) + val = 0; - /* *lptr is a value inside unpacked test file, val is the - * value that should be in the unpacked test file. */ + /* *lptr is a value inside unpacked test file, val is the + * value that should be in the unpacked test file. */ - if(*lptr != val) - return 0; - } + if(archive_le32dec(lptr) != (uint32_t) val) + return 0; + } - return 1; + return 1; } static int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) { - la_ssize_t fsize, read; - uint8_t* buf; - int ret = 1; - uint32_t computed_crc; - - fsize = (la_ssize_t) archive_entry_size(ae); - buf = malloc(fsize); - if(buf == NULL) - return 1; - - read = archive_read_data(a, buf, fsize); - if(read != fsize) { - assertEqualInt(read, fsize); - goto fn_exit; - } - - computed_crc = crc32(0, buf, fsize); - assertEqualInt(computed_crc, crc); - ret = 0; + la_ssize_t fsize, bytes_read; + uint8_t* buf; + int ret = 1; + uint32_t computed_crc; + + fsize = (la_ssize_t) archive_entry_size(ae); + buf = malloc(fsize); + if(buf == NULL) + return 1; + + bytes_read = archive_read_data(a, buf, fsize); + if(bytes_read != fsize) { + assertEqualInt(bytes_read, fsize); + goto fn_exit; + } + + computed_crc = crc32(0, buf, fsize); + assertEqualInt(computed_crc, crc); + ret = 0; fn_exit: - free(buf); - return ret; + free(buf); + return ret; } -DEFINE_TEST(test_read_format_rar5_stored) +DEFINE_TEST(test_read_format_rar5_set_format) { - const char helloworld_txt[] = "hello libarchive test suite!\n"; - la_ssize_t file_size = sizeof(helloworld_txt) - 1; - char buff[64]; - - PROLOGUE("test_read_format_rar5_stored.rar"); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("helloworld.txt", archive_entry_pathname(ae)); - assertA((int) archive_entry_mtime(ae) > 0); - assertA((int) archive_entry_ctime(ae) == 0); - assertA((int) archive_entry_atime(ae) == 0); - assertEqualInt(file_size, archive_entry_size(ae)); - assertEqualInt(33188, archive_entry_mode(ae)); - assertA(file_size == archive_read_data(a, buff, file_size)); - assertEqualMem(buff, helloworld_txt, file_size); - assertEqualInt(archive_entry_is_encrypted(ae), 0); - - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + struct archive *a; + struct archive_entry *ae; + const char reffile[] = "test_read_format_rar5_stored.rar"; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR_V5)); + assertA(0 == archive_read_open_filename(a, reffile, 10240)); + assertA(0 == archive_read_next_header(a, &ae)); + EPILOGUE(); +} - EPILOGUE(); +DEFINE_TEST(test_read_format_rar5_stored) +{ + const char helloworld_txt[] = "hello libarchive test suite!\n"; + la_ssize_t file_size = sizeof(helloworld_txt) - 1; + char buff[64]; + + PROLOGUE("test_read_format_rar5_stored.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("helloworld.txt", archive_entry_pathname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertA((int) archive_entry_ctime(ae) == 0); + assertA((int) archive_entry_atime(ae) == 0); + assertEqualInt(file_size, archive_entry_size(ae)); + assertEqualInt(33188, archive_entry_mode(ae)); + assertA(file_size == archive_read_data(a, buff, file_size)); + assertEqualMem(buff, helloworld_txt, file_size); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_compressed) { - const int DATA_SIZE = 1200; - uint8_t buff[1200]; + const int DATA_SIZE = 1200; + uint8_t buff[1200]; - PROLOGUE("test_read_format_rar5_compressed.rar"); + PROLOGUE("test_read_format_rar5_compressed.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA((int) archive_entry_mtime(ae) > 0); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - verify_data(buff, 0, DATA_SIZE); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + verify_data(buff, 0, DATA_SIZE); - EPILOGUE(); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiple_files) { - const int DATA_SIZE = 4096; - uint8_t buff[4096]; - - PROLOGUE("test_read_format_rar5_multiple_files.rar"); - - /* There should be 4 files inside this test file. Check for their - * existence, and also check the contents of those test files. */ - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 1, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 2, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 3, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 4, DATA_SIZE)); - - /* There should be no more files in this archive. */ - - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const int DATA_SIZE = 4096; + uint8_t buff[4096]; + + PROLOGUE("test_read_format_rar5_multiple_files.rar"); + + /* There should be 4 files inside this test file. Check for their + * existence, and also check the contents of those test files. */ + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 1, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 2, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 3, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 4, DATA_SIZE)); + + /* There should be no more files in this archive. */ + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } /* This test is really the same as the test above, but it deals with a solid @@ -205,291 +221,342 @@ DEFINE_TEST(test_read_format_rar5_multiple_files) DEFINE_TEST(test_read_format_rar5_multiple_files_solid) { - const int DATA_SIZE = 4096; - uint8_t buff[4096]; - - PROLOGUE("test_read_format_rar5_multiple_files_solid.rar"); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 1, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 2, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 3, DATA_SIZE)); - - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertEqualInt(DATA_SIZE, archive_entry_size(ae)); - assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); - assertA(verify_data(buff, 4, DATA_SIZE)); - - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const int DATA_SIZE = 4096; + uint8_t buff[4096]; + + PROLOGUE("test_read_format_rar5_multiple_files_solid.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 1, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 2, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 3, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + assertA(verify_data(buff, 4, DATA_SIZE)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive.part01.rar", - "test_read_format_rar5_multiarchive.part02.rar", - "test_read_format_rar5_multiarchive.part03.rar", - "test_read_format_rar5_multiarchive.part04.rar", - "test_read_format_rar5_multiarchive.part05.rar", - "test_read_format_rar5_multiarchive.part06.rar", - "test_read_format_rar5_multiarchive.part07.rar", - "test_read_format_rar5_multiarchive.part08.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdtar_test", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive.part01.rar", + "test_read_format_rar5_multiarchive.part02.rar", + "test_read_format_rar5_multiarchive.part03.rar", + "test_read_format_rar5_multiarchive.part04.rar", + "test_read_format_rar5_multiarchive.part05.rar", + "test_read_format_rar5_multiarchive.part06.rar", + "test_read_format_rar5_multiarchive.part07.rar", + "test_read_format_rar5_multiarchive.part08.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdtar_test", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_first) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive.part01.rar", - "test_read_format_rar5_multiarchive.part02.rar", - "test_read_format_rar5_multiarchive.part03.rar", - "test_read_format_rar5_multiarchive.part04.rar", - "test_read_format_rar5_multiarchive.part05.rar", - "test_read_format_rar5_multiarchive.part06.rar", - "test_read_format_rar5_multiarchive.part07.rar", - "test_read_format_rar5_multiarchive.part08.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertA(0 == extract_one(a, ae, 0x35277473)); - assertA(0 == archive_read_next_header(a, &ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive.part01.rar", + "test_read_format_rar5_multiarchive.part02.rar", + "test_read_format_rar5_multiarchive.part03.rar", + "test_read_format_rar5_multiarchive.part04.rar", + "test_read_format_rar5_multiarchive.part05.rar", + "test_read_format_rar5_multiarchive.part06.rar", + "test_read_format_rar5_multiarchive.part07.rar", + "test_read_format_rar5_multiarchive.part08.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertA(0 == extract_one(a, ae, 0x35277473)); + assertA(0 == archive_read_next_header(a, &ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_second) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive.part01.rar", - "test_read_format_rar5_multiarchive.part02.rar", - "test_read_format_rar5_multiarchive.part03.rar", - "test_read_format_rar5_multiarchive.part04.rar", - "test_read_format_rar5_multiarchive.part05.rar", - "test_read_format_rar5_multiarchive.part06.rar", - "test_read_format_rar5_multiarchive.part07.rar", - "test_read_format_rar5_multiarchive.part08.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertA(0 == extract_one(a, ae, 0xE59665F8)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive.part01.rar", + "test_read_format_rar5_multiarchive.part02.rar", + "test_read_format_rar5_multiarchive.part03.rar", + "test_read_format_rar5_multiarchive.part04.rar", + "test_read_format_rar5_multiarchive.part05.rar", + "test_read_format_rar5_multiarchive.part06.rar", + "test_read_format_rar5_multiarchive.part07.rar", + "test_read_format_rar5_multiarchive.part08.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertA(0 == extract_one(a, ae, 0xE59665F8)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_blake2) { - const la_ssize_t proper_size = 814; - uint8_t buf[814]; + const la_ssize_t proper_size = 814; + uint8_t buf[814]; - PROLOGUE("test_read_format_rar5_blake2.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualInt(proper_size, archive_entry_size(ae)); + PROLOGUE("test_read_format_rar5_blake2.rar"); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(proper_size, archive_entry_size(ae)); - /* Should blake2 calculation fail, we'll get a failure return - * value from archive_read_data(). */ + /* Should blake2 calculation fail, we'll get a failure return + * value from archive_read_data(). */ - assertA(proper_size == archive_read_data(a, buf, proper_size)); + assertA(proper_size == archive_read_data(a, buf, proper_size)); - /* To be extra pedantic, let's also check crc32 of the poem. */ - assertEqualInt(crc32(0, buf, proper_size), 0x7E5EC49E); + /* To be extra pedantic, let's also check crc32 of the poem. */ + assertEqualInt(crc32(0, buf, proper_size), 0x7E5EC49E); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_arm_filter) { - /* This test unpacks a file that uses an ARM filter. The DELTA - * and X86 filters are tested implicitly in the "multiarchive_skip" - * test. */ + /* This test unpacks a file that uses an ARM filter. The DELTA + * and X86 filters are tested implicitly in the "multiarchive_skip" + * test. */ - const la_ssize_t proper_size = 90808; - uint8_t buf[90808]; + const la_ssize_t proper_size = 90808; + uint8_t buf[90808]; - PROLOGUE("test_read_format_rar5_arm.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualInt(proper_size, archive_entry_size(ae)); - assertA(proper_size == archive_read_data(a, buf, proper_size)); + PROLOGUE("test_read_format_rar5_arm.rar"); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(proper_size, archive_entry_size(ae)); + assertA(proper_size == archive_read_data(a, buf, proper_size)); - /* Yes, RARv5 unpacker itself should calculate the CRC, but in case - * the DONT_FAIL_ON_CRC_ERROR define option is enabled during compilation, - * let's still fail the test if the unpacked data is wrong. */ - assertEqualInt(crc32(0, buf, proper_size), 0x886F91EB); + /* Yes, RARv5 unpacker itself should calculate the CRC, but in case + * the DONT_FAIL_ON_CRC_ERROR define option is enabled during compilation, + * let's still fail the test if the unpacked data is wrong. */ + assertEqualInt(crc32(0, buf, proper_size), 0x886F91EB); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all) { - const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; - - PROLOGUE(fname); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; + + PROLOGUE(fname); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_in_part) { - const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; - char buf[6]; - - /* Skip first, extract in part rest. */ - - PROLOGUE(fname); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(6 == archive_read_data(a, buf, 6)); - assertEqualInt(0, memcmp(buf, "Cebula", 6)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(4 == archive_read_data(a, buf, 4)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; + char buf[6]; + + /* Skip first, extract in part rest. */ + + PROLOGUE(fname); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(6 == archive_read_data(a, buf, 6)); + assertEqualInt(0, memcmp(buf, "Cebula", 6)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(4 == archive_read_data(a, buf, 4)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all_but_first) { - const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; - char buf[405]; - - /* Extract first, skip rest. */ - - PROLOGUE(fname); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); - assertA(405 == archive_read_data(a, buf, sizeof(buf))); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; + char buf[405]; + + /* Extract first, skip rest. */ + + PROLOGUE(fname); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); + assertA(405 == archive_read_data(a, buf, sizeof(buf))); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_stored_skip_all_in_part) { - const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; - char buf[4]; - - /* Extract in part all */ + const char* fname = "test_read_format_rar5_stored_manyfiles.rar"; + char buf[4]; + + /* Extract in part all */ + + PROLOGUE(fname); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); + assertA(4 == archive_read_data(a, buf, 4)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(4 == archive_read_data(a, buf, 4)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(4 == archive_read_data(a, buf, 4)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); +} - PROLOGUE(fname); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("make_uue.tcl", archive_entry_pathname(ae)); - assertA(4 == archive_read_data(a, buf, 4)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(4 == archive_read_data(a, buf, 4)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(4 == archive_read_data(a, buf, 4)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); +DEFINE_TEST(test_read_format_rar5_multiarchive_solid_extr_all) +{ + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7E5EC49E)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7cca70cd)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7e13b2c6)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xf166afcb)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x9fb123d9)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x10c43ed4)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xb9d155f2)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x36a448ff)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x886F91EB)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive_solid.part01.rar", - "test_read_format_rar5_multiarchive_solid.part02.rar", - "test_read_format_rar5_multiarchive_solid.part03.rar", - "test_read_format_rar5_multiarchive_solid.part04.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_first) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive_solid.part01.rar", - "test_read_format_rar5_multiarchive_solid.part02.rar", - "test_read_format_rar5_multiarchive_solid.part03.rar", - "test_read_format_rar5_multiarchive_solid.part04.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7E5EC49E)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7E5EC49E)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } /* "skip_all_but_scnd" -> am I hitting the test name limit here after @@ -497,232 +564,633 @@ DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_first) DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_scnd) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive_solid.part01.rar", - "test_read_format_rar5_multiarchive_solid.part02.rar", - "test_read_format_rar5_multiarchive_solid.part03.rar", - "test_read_format_rar5_multiarchive_solid.part04.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7CCA70CD)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7CCA70CD)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_third) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive_solid.part01.rar", - "test_read_format_rar5_multiarchive_solid.part02.rar", - "test_read_format_rar5_multiarchive_solid.part03.rar", - "test_read_format_rar5_multiarchive_solid.part04.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7E13B2C6)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7E13B2C6)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_last) { - const char* reffiles[] = { - "test_read_format_rar5_multiarchive_solid.part01.rar", - "test_read_format_rar5_multiarchive_solid.part02.rar", - "test_read_format_rar5_multiarchive_solid.part03.rar", - "test_read_format_rar5_multiarchive_solid.part04.rar", - NULL - }; - - PROLOGUE_MULTI(reffiles); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("cebula.txt", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x886F91EB)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x886F91EB)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all) { - const char* reffile = "test_read_format_rar5_solid.rar"; - - /* Skip all */ - - PROLOGUE(reffile); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffile = "test_read_format_rar5_solid.rar"; + + /* Skip all */ + + PROLOGUE(reffile); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first) { - const char* reffile = "test_read_format_rar5_solid.rar"; - - /* Extract first, skip rest */ - - PROLOGUE(reffile); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7CCA70CD)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffile = "test_read_format_rar5_solid.rar"; + + /* Extract first, skip rest */ + + PROLOGUE(reffile); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7CCA70CD)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_second) { - const char* reffile = "test_read_format_rar5_solid.rar"; - - /* Skip first, extract second, skip rest */ - - PROLOGUE(reffile); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7E13B2C6)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffile = "test_read_format_rar5_solid.rar"; + + /* Skip first, extract second, skip rest */ + + PROLOGUE(reffile); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7E13B2C6)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_last) { - const char* reffile = "test_read_format_rar5_solid.rar"; - - /* Skip all but last, extract last */ - - PROLOGUE(reffile); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x36A448FF)); - assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); - EPILOGUE(); + const char* reffile = "test_read_format_rar5_solid.rar"; + + /* Skip all but last, extract last */ + + PROLOGUE(reffile); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x36A448FF)); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); } DEFINE_TEST(test_read_format_rar5_extract_win32) { - PROLOGUE("test_read_format_rar5_win32.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7CCA70CD)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test1.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x7E13B2C6)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test2.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0xF166AFCB)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test3.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x9FB123D9)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test4.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x10C43ED4)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test5.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0xB9D155F2)); - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("test6.bin", archive_entry_pathname(ae)); - assertA(0 == extract_one(a, ae, 0x36A448FF)); - EPILOGUE(); + PROLOGUE("test_read_format_rar5_win32.rar"); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("testdir", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); + assertA(0 == extract_one(a, ae, 0)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0x7CCA70CD)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0x7E13B2C6)); + assertA(0 == archive_read_next_header(a, &ae)); + /* Read only file */ + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0444); + assertA(0 == extract_one(a, ae, 0xF166AFCB)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0x9FB123D9)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0x10C43ED4)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0xB9D155F2)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); + assertA(0 == extract_one(a, ae, 0x36A448FF)); + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_block_by_block) +{ + /* This test uses strange buffer sizes intentionally. */ + + struct archive_entry *ae; + struct archive *a; + uint8_t buf[173]; + int bytes_read; + uint32_t computed_crc = 0; + + extract_reference_file("test_read_format_rar5_compressed.rar"); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_open_filename(a, "test_read_format_rar5_compressed.rar", 130)); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertEqualInt(1200, archive_entry_size(ae)); + + /* File size is 1200 bytes, we're reading it using a buffer of 173 bytes. + * Libarchive is configured to use a buffer of 130 bytes. */ + + while(1) { + /* archive_read_data should return one of: + * a) 0, if there is no more data to be read, + * b) negative value, if there was an error, + * c) positive value, meaning how many bytes were read. + */ + + bytes_read = archive_read_data(a, buf, sizeof(buf)); + assertA(bytes_read >= 0); + if(bytes_read <= 0) + break; + + computed_crc = crc32(computed_crc, buf, bytes_read); + } + + assertEqualInt(computed_crc, 0x7CCA70CD); + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_owner) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_owner.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("root.txt", archive_entry_pathname(ae)); + assertEqualString("root", archive_entry_uname(ae)); + assertEqualString("wheel", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("nobody.txt", archive_entry_pathname(ae)); + assertEqualString("nobody", archive_entry_uname(ae)); + assertEqualString("nogroup", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("numeric.txt", archive_entry_pathname(ae)); + assertEqualInt(9999, archive_entry_uid(ae)); + assertEqualInt(8888, archive_entry_gid(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_symlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_symlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("symlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_FILE, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dirlink", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("dir", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_DIRECTORY, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dir", archive_entry_pathname(ae)); + assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_hardlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_hardlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("hardlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_hardlink(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_extra_field_version) +{ + PROLOGUE("test_read_format_rar5_extra_field_version.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("bin/2to3;1", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xF24181B7)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("bin/2to3", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xF24181B7)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_readtables_overflow) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_readtables_overflow.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * buffer overflow errors during reading rar5 tables. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_leftshift1) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_leftshift1.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_leftshift2) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_leftshift2.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_truncated_huff) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_truncated_huff.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_invalid_dict_reference) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_invalid_dict_reference.rar"); + + /* This test should fail on parsing the header. */ + assertA(archive_read_next_header(a, &ae) != ARCHIVE_OK); + + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to buffer underflow when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_distance_overflow) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_distance_overflow.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to variable overflows when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_nonempty_dir_stream) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_nonempty_dir_stream.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to buffer overflows when using -fsanitize. */ + assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); + + /* This test only cares about not returning success here. */ + assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_fileattr) +{ + unsigned long set, clear, flag; + + flag = 0; + + PROLOGUE("test_read_format_rar5_fileattr.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG); + assertEqualString("readonly.txt", archive_entry_pathname(ae)); + assertEqualString("rdonly", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_READONLY; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_READONLY; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG); + assertEqualString("hidden.txt", archive_entry_pathname(ae)); + assertEqualString("hidden", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_HIDDEN; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_HIDDEN; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG); + assertEqualString("system.txt", archive_entry_pathname(ae)); + assertEqualString("system", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_SYSTEM;; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_SYSTEM; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG); + assertEqualString("ro_hidden.txt", archive_entry_pathname(ae)); + assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_READONLY | UF_HIDDEN; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR); + assertEqualString("dir_readonly", archive_entry_pathname(ae)); + assertEqualString("rdonly", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_READONLY; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_READONLY; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR); + assertEqualString("dir_hidden", archive_entry_pathname(ae)); + assertEqualString("hidden", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_HIDDEN; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_HIDDEN; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR); + assertEqualString("dir_system", archive_entry_pathname(ae)); + assertEqualString("system", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_SYSTEM; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_SYSTEM; +#endif + assertEqualInt(flag, set & flag); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR); + assertEqualString("dir_rohidden", archive_entry_pathname(ae)); + assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae)); + archive_entry_fflags(ae, &set, &clear); +#if defined(__FreeBSD__) + flag = UF_READONLY | UF_HIDDEN; +#elif defined(_WIN32) && !defined(CYGWIN) + flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN; +#endif + assertEqualInt(flag, set & flag); + + EPILOGUE(); } diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu new file mode 100644 index 000000000..8fefa282e --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_distance_overflow.rar +M4F%R(1H'`0"-[P+2``(''/\@("`@_R4``B`@("`@("`@("`@(/__("`@("`@ +M(/\@("`@("`@((9ML63,PX"&AK%:S+?_(/\@_R#_(/\@_R#_(/\@`"``!R`@ +MR!0?*``P$(8FEN +M+S)T;S/%R58E54(_=00]^44QFE=*A'1*>B"DP(_`YD!;_"1"2H/>.\JSNCIR +MJ-=&]E9KQFVN#YI-S-P&5:6-!8((VI)'0OV.CF2X\:/J9V`>ID+B:L^4G\B- +,X$E:P!UW5E$#!00` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_fileattr.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_fileattr.rar.uu new file mode 100644 index 000000000..456b02504 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_fileattr.rar.uu @@ -0,0 +1,13 @@ +begin 640 test_read_format_rar5_fileattr.rar +M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``-$;*1"@"`PN+``2+`"&+W8Z;@``` +M#')E861O;FQY+G1X=`H#`J*C5`BE!M4!,3(S-#4V-S@@#0JYZ,#-)@(#"XL` +M!(L`(F^$/86````*:&ED9&5N+G1X=`H#`G(Q.0^E!M4!,C,T-38W.#D@#0H> +M%_EV)@(#"XL`!(L`)"V,TBB````*$42*0(#"XL`!(L`(RZ?$!V````-0H#`HS,%ABE!M4!Q!O^+"0"`PL`!0`2`````(````ID:7)?:&ED +M9&5N"@,">JRV&:4&U0'&L*8=)`(#"P`%`!0`````@```"F1I7-T96T* +M`P(@+D4;I0;5`2YJ1$0F`@,+``4`$P````"````,9&ER7W)O:&ED9&5N"@," +0CW7S@JT&U0$==U91`P4$```` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_hardlink.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_hardlink.rar.uu new file mode 100644 index 000000000..0ec085d47 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_hardlink.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_hardlink.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!KI,1X'@("A0`&A0"D@P(XC;=<(1>3 +M?8```0AF:6QE+G1X=#$R,S0*[8@"T2X"`PT`!@6D@P(XC;=<`````(```0QH +@87)D;&EN:RYT>'0,!00`"&9I;&4N='AT'7=640,%!``` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu new file mode 100644 index 000000000..9b78c9b36 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_invalid_dict_reference.rar +M4F%R(1H'`0"-[P+2``+#!QR`!P`F^P#_^_O[^_O[^R4``B$<`0(`#@```0`` +M`"#2````_____QH(`/__^P#_W5)04(#_`(:&;;%DS+?,L0```````````+%D +MS+*RLK*R/@``____Y`"R````XP```````!4``````.X````````````````` +M%52$A(2$A(2$A(2$A(2$A(2$Q(2$A(2$A(2$A(2)]( +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift1.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift1.rar.uu new file mode 100644 index 000000000..694a27f6f --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift1.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_leftshift1.rar +M4F%R(1H'`0"-[P+2``(''(`'`"``_R4``B$<`0(`#@```0```"#2````____ +M_P`(`/__^P#_W0`"(8#_`(:&;;%DS+?,L=:NL0#(3`$````````````````` +M``"``````````+!DS+*RL[*RL@```-P``````````````````(`````````` +ML&3,LK*RLK*R````W`````#X____````````````````````````%5H>;&@T +M+3HW"2!SB^)_@`````` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift2.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift2.rar.uu new file mode 100644 index 000000000..57ffad725 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_leftshift2.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_leftshift2.rar +M4F%R(1H'`0"-[P+2``(''(`'`2``_RL``B'+`0(`,O__````-WJ\KR<<)0`" +M(;<*`BY*`!```&;%T#%24%"`_R4`[@K+(2Y*`&$``'__`/\E``(N2@`0`0(` +0(?__`%N&?Q2UH.CHZ.CHZ``` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu new file mode 100644 index 000000000..c508c1f97 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_nonempty_dir_stream.rar +M4F%R(1H'`0"-[P+2``(''($'$7\`_R4``BP<`0(`(0#_Y@```"#2````____ +M_P`(`/__^P#_W0`"(8#_`(:&;;%DS+?,L8```!;(&P#>``#__^_P```4```& +M`````````````+`!`@`A`/_F````(-(```#_____``@`___[`/_=``(A``++ +M``"`]/^`!P!#^_____\"(2$!`@`A````_R4``B$A`0(`@````0```"#&`/_= +M``(A@/\`AH9ML63,M\R`_P```,@;`````!@`````_0`````````!87(A&@<` +5`(WO`M(``O8'&X`'`#C[_X`E``(A +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_owner.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_owner.rar.uu new file mode 100644 index 000000000..285bdb91c --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_owner.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_owner.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!W:E-^+0(##H4`!H4`I(,"_8VW7"$7 +MDWV```$('0-!@,$2YT>'01!@,&;F]B;V1Y!VYO9W)O=7`U +M-CB#;(H```0MN=6UE\"T@`"T@"4RQ_5]0#O0````,L?Q_T``(`" +MT@"4RQ_=V-C8`)3+']W=]0"-\`+2`)3+']WU`(WO`M(``M(`E,L?U?4`[P+2 +M`)3+'\?]``"``M(`E,L?W=C8V`"4RQ_=]0#V`(WO`M'UV,?8V-C8$=C8V-C8 +MV(W8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8!]C8V-C8V-C8V-C8V-C8V-C8 +MV-C8V-C8V-C(V-C8V-C2`)3+']W8V-C8V-C8V-C8V-C8V-C8@-C8V-C8V-C8 +MV/+8V-C8V-C8V-C8`038V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8!-C8 +MV-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8V-C8`(`"V`7V`(WO`M'U`]L? +MW?4`C>\"T@`"T@"4'__U`(WO`N``E,L?W84`C0`0T@"4RQ_=V-C8V-C8V`"4 +MR_\R]0#V`(W8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8V-C8 +MV-C8V-C8V-C8V-C8R-C8V-C8T@"4RQ_=V-C8V-C8V-C8V-C8V-C8V(#8V-C8 +MV-C8````9-C8V-C8V!'8V-C8V-C8]]C8V-C8V-C8V-C8V/+8V-C8V-C8V-C8 +=`038V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-@` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_symlink.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_symlink.rar.uu new file mode 100644 index 000000000..f603e3c04 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_symlink.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_symlink.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`"$O8RN'@("A0`&A0"D@P(XC;=<(1>3 +M?8!``0AF:6QE+G1X=#$R,S0*8QI3.2T"`PT`!@CMPP)7C;=<`````(!``0MS +M>6UL:6YK+G1X=`P%`0`(9FEL92YT>'2.NOQU)`(#"``&`^W#`DF6MUP````` +M@$`!!V1IWMNP$+-5H*^@`!`$OOB]$````0"S5:*@`` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_win32.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_win32.rar.uu index 59920490e..16d9fce1d 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_rar5_win32.rar.uu +++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_win32.rar.uu @@ -1,68 +1,69 @@ begin 644 test_read_format_rar5_win32.rar -M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``/^"F5B0"`POI`@2P"2#-<,I\@`4` -M"'1E#?W= -MS:V=?6U=33TM#/SA0'XZ,BHF'.G#9J&A(*`?GQ[>GAV='-R<6]M:V -MEG96-A7UU;65=54R$W]?R/T"4^B4Y_A$ET6*,&>6%VHX*:8IE!?F0M%VG48R -MPSC`J$?_H)3&:0I?95"L5E8+BP&1:#; -MAX.`8'(/KF$3H*9V&`\#;>A[O8=7T(W\6$"-&#'G"AY-"8-B^.#F.APB!*GA -MA18[8T/Y\5"`;"$-"(32,9R0+R4`8B?AZB4"`POG`@2`("#&LA-^@`4`"71E -M_MZ^GGY>/AW -M]W;V=?5T]'/SEHZ&?G9N9EY63D8^-BXF'A -M8.!?WU[>7=UFYH!9@!;-"-LC0$.A"U@6`X1-8__0K^QS*_>TU/6 -MRJ_(8IKGE-.XIH6GK_HGV@`)6BG`)0(#"^8"!(`@(,NO9O&`!0`)=&5S=#(N -M8FEN"@,"A/;"@`U:U`'/]F(!$&1%9B]7!7]2222222222222222222222222 -M2222222222222222222222222222222222223[\^1N9V\]KM]OI+K^[M;N^9 -MT9N[T[_28S,,Z?,.-S?,\X<_BN=OR`"`"`&IAXZ<-OCW]O7T\_+Q\._N[>SK -MZNGHY^;EY./BX>#?WMWFI:2CHJ&@GYZ=G)N:F9B7EI64DY*1 -MD(^.C8R+BHF(AX:%A(."@8!_?GWWO8^T*7"EJ^DJ%`9V,`+9H1O$:`AT(6L" -MP'")[=I:<$/3LT`LP`MFA&Z1H"'0A:P+`<(GMREIP0].30"S`"V:$;A&@(=" -M%K`L!PB>VZ6G!#TW-`+,`+9H1MD:`AT(6L"P'")K'_Z%?V.97[VFIZV57Y#% -M-<\IIW%-"T]?]$^TVMWAVR4"`POT`@2`("#9([&?@`4`"71EO+LEY]]M9^&\\, -MWO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#848765^/?V]?3S\O'P[^[M[.OJ -MZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z] -MO+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV6E923DI&0 -MCXZ-C(N*B8B'AH6$@X*!@']^??=]3[8AOPALOH4&#J;"`FDX$V"95@TP3;@P -MF6DRPI=O")0\&G@!T,0$TG`FN3*KFER;<&$RTF6%+MV1*'@T[`.AB`FDX$UB -M958T/&FX`#:3+"EVZ(E#P:=`'0Q`32<":I,JJ:'C3<`!M)EA2^4__8C^QK(_ -M>\:'K=J/R*(O+ -MLEY]]M9^&\\,WO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#7=,+*J?'O[>OIY -M^7CX=_=V]G7U=/1S\W+R5DY&/C -M8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3#2X!T,0$TG`FL3*K&EB;<&$RTF6%+MX1*'@T -M\`.AB`FDX$U2954T/&FX`#:3+"EV[(E#P:=@'0Q`32<":A,JH:'C3<`!M)EA -M2^=?_L1_8UD?O>-#UNU'Y%$.=/..;N7KV[)>??;>LS[/'-[UX[_28S,,\?L,TUS/OM&_Q7/7Y`!`!`#6<+ -MK*J?'O[>OIY^7CX=_=V]G7U=/1S\W+R5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14 -M-!/ST[.3MVH_(HAYUR'CV(>#4]?^@ -M_:`_"MBZ)0(#"_P"!(`@(/](I#:`!0`)=&5S=#8N8FEN"@,"%79G@0U:U`'- -M[G@!$&1552]U;J2222222222222222222222222222222222222222222222 -M222222222222222<[SO.\W&:UX\XYNY>O;LEY]]MZS/L\CGYN7DX^+AX- -M_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z]O+NZN;BWMK6TL[ -M*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV6E923DI&0CXZ-C(N*B8B'AH -M6$@X*!@']^??=]Q]N(3A"3;)!`K]0<,EK)2D<7#&%%S2X:>$#;38!T>W8(A: -MV3LP5-G#):R4I'%@QA18TL&GA`VTV`='MT"(6MDZ,%39PR6LE*1Q4,845-*A -MIX0-M-@'1[<@B%K9.3!4V<,EK)2D<4#&%%#2@:>$#;38!T?./_W$?V-9'[WC -;0];M1^11#SKD/'L0\&IZ_]!^T!UW5E$#!00` +M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``EV&"+"$"`PL`!0`0`````(````=T +M97-T9&ER"@,"5NNI_W0?[W +MVO?=>:\Z7^@`0`$`R""?GX]O7T\O'O[NSKZNGHY^7DX^+AX-_=W-K9U];5U- +M/2T,_-S,K)R,?&Q<3#P;^]O+NYN+>VM;2SLK&OKJVKJJFGI:2CHJ&@GYV'9T$&P`\Q!(R">9A6-`NFH9;8-YN'@X!@<@^N +M81.@IG88#P-MZ'N]AU?0C?Q80(T8,><*'DT)@V+XX.8Z'"($J>&%%CMC0_GQ +M4(!L(0T(A-(QG)`O)0`QGD%J)0(#"^L"!(`@(,:R$WZ``P`)=&5S=#$N8FEN +M"@,"$I6A@`U:U`'-\6<7#G7SNUN[YG1F[ +MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&PPZ<-FGU\_'O[>OIY^7CX=_=V +M]G7U=/1S\W+R5DY&/C8N)AX6#@ +M7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3FYH!9@!;-"-LC0$.A +M"U@6`X1/;5+3@AZ:F@%F`%LT(VB-`0Z$+6!8#A$UA]Z%,?GS*>SVE?X8*>=C +M4\-BG=.J=HXIZ?]R?M!(:PP')0(#"^L"!(`@(<7#G7SNUN[YG1F[ +MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&IATX;-/KY^/?V]?3S\O'P[^[M +M[.OJZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'` +MO[Z]O+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV6E923 +MDI&0CXZ-C(N*B8B'AH6$@X*!@'_WO8_D*7"EJ_4J%`9V,`+9H1ND:`AT(6L" +MP'")[\[SO-RUK/'AS>Z]>]72\[VUGX; +MSPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!LK,L+N/CW]O7T\_+Q\._N +M[>SKZNGHY^;EY./BX>#?WMWFI:2CHJ&@GYZ=G)N:F9B7EI64 +MDY*1D(^.C8R+BHF(AX:%A(."@8!_?O_[[UOQB$^$)+[%!@ZJP@)I6!-DF59- +M,DTP85+::84MW9$H?1AV`=#$!-*P)L$RK!I@FF#"I;33"ENZ(E#Z,.@#H8@) +MI6!-UH>G1#RK4/$L0^/_<7F@SHER0)0(#"_<"!(`@(-0^Q!"` +M`P`)=&5S=#0N8FEN"@,"`HP9@0U:U`'/YW,!$&1552]7!7XDDDDDDDDDDDDD +MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG.\[SG-RUK/' +MASFZ]>]72\[VUGX;SPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!L*,KK +M*_'O[>OIY^7CX=_=V]G7U=/1S\W+R5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-! +M/ST[.3$.%]B +M@P=380$TG`FR3*LFF2;D&$RVFF%+=@B4/HPP`=#$!-)P)KDRJYI<SKZNGHY^;EY./BX>#?WMWFI:2CHJ&@GYZ=G)N:F9B7EI64DY*1D(^.C8R+BHF(AX:%A(."@8!_? +MO_[[W'XXA.D)1LT$"OW!PR6LE*QQ<,847-+AIH@;:C`.CV[!$+6S=F"ILX9+ +M62E8XL&,*+&E@TT0-M1@'1[=`B%K9NC!4V<,EK)2L<5#&%%32H::(&VHP#H] +MN01"ULW)@J;.&2UDI6.*!C"BAI0--$#;48!T?G'][B''U_9#Z?:1^AHA[VM# +MT\$/*RH>)D0^/_<?EX^'?W= +MO9U]73T<_-R\G'QUL[&OK:NIIZ6CH9^=FYF7E9.1CXV+B8>%@X +M%_?7MY=W5S<6]M:VEG96-A7UU;65=54U%/34M)1T5#03\].SDW-3,Q+RTK*2 +MW((A:V;DP5-G#):R4K'%`QA10TH&FB!M +KJ,`Z/SC^]Q#CZ_LA]/M(_0T0][6AZ>"'E94/$R(?'_N'S0`==U91`P4$```` ` end diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu new file mode 100644 index 000000000..136338653 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu @@ -0,0 +1,10 @@ +begin 644 test_read_format_rar_ppmd_use_after_free.rar +M4F%R(1H'``1G=$Q26`!W````>U!+`P0R`'#_J7\`+@TU'`#]`0`7__]"0D)" +M+W5NTQ26`!W=&@`[E!+ +M`P0Q`'#_(````"`@(+<@!/T`("`@("`@("`@("`@("`@("`@("`@("`@("`@ +M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@ +M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(`1G=$Q26`!W```` +M>U!+`P0R`'#_J7\`+@TU'`#]`0`7__]"0D)"+W5NTQ26`!W=&@`[E!+`P0Q`'`` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_raw.c b/contrib/libarchive/libarchive/test/test_read_format_raw.c index 29465fdac..ecdca21d2 100644 --- a/contrib/libarchive/libarchive/test/test_read_format_raw.c +++ b/contrib/libarchive/libarchive/test/test_read_format_raw.c @@ -36,6 +36,7 @@ DEFINE_TEST(test_read_format_raw) const char *reffile1 = "test_read_format_raw.data"; const char *reffile2 = "test_read_format_raw.data.Z"; const char *reffile3 = "test_read_format_raw.bufr"; + const char *reffile4 = "test_read_format_raw.data.gz"; /* First, try pulling data out of an uninterpretable file. */ extract_reference_file(reffile1); @@ -117,4 +118,30 @@ DEFINE_TEST(test_read_format_raw) assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* Fourth, try with gzip which has metadata. */ + extract_reference_file(reffile4); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, reffile4, 1)); + + /* First (and only!) Entry */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("test-file-name.data", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); + assert(archive_entry_mtime_is_set(ae)); + assertEqualIntA(a, archive_entry_mtime(ae), 0x5cbafd25); + /* Most fields should be unset (unknown) */ + assert(!archive_entry_size_is_set(ae)); + assert(!archive_entry_atime_is_set(ae)); + assert(!archive_entry_ctime_is_set(ae)); + + /* Test EOF */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } diff --git a/contrib/libarchive/libarchive/test/test_read_format_raw.data.gz.uu b/contrib/libarchive/libarchive/test/test_read_format_raw.data.gz.uu new file mode 100644 index 000000000..cf1f7b307 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_raw.data.gz.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_raw.data.gz +L'XL(""7]NEP``W1E + +static void +verify(struct archive *a) { + struct archive_entry *ae; + const char *p; + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assert((p = archive_entry_pathname_utf8(ae)) != NULL); + assertEqualUTF8String(p, "File 1.txt"); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assert((p = archive_entry_pathname_utf8(ae)) != NULL); +#if defined(__APPLE__) + /* Compare NFD string. */ + assertEqualUTF8String(p, "File 2 - o\xCC\x88.txt"); +#else + /* Compare NFC string. */ + assertEqualUTF8String(p, "File 2 - \xC3\xB6.txt"); +#endif + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assert((p = archive_entry_pathname_utf8(ae)) != NULL); +#if defined(__APPLE__) + /* Compare NFD string. */ + assertEqualUTF8String(p, "File 3 - a\xCC\x88.txt"); +#else + /* Compare NFC string. */ + assertEqualUTF8String(p, "File 3 - \xC3\xA4.txt"); +#endif + + /* The CRC of the filename fails, so fall back to CDE. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assert((p = archive_entry_pathname_utf8(ae)) != NULL); + assertEqualUTF8String(p, "File 4 - xx.txt"); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); +} + +DEFINE_TEST(test_read_format_zip_utf8_paths) +{ + const char *refname = "test_read_format_zip_7075_utf8_paths.zip"; + struct archive *a; + char *p; + size_t s; + + extract_reference_file(refname); + + if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + skipping("en_US.UTF-8 locale not available on this system."); + return; + } + + /* Verify with seeking reader. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); + verify(a); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); + + /* Verify with streaming reader. */ + p = slurpfile(&s, refname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31)); + verify(a); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_free(a)); + free(p); +} diff --git a/contrib/libarchive/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu b/contrib/libarchive/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu new file mode 100644 index 000000000..332f99689 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu @@ -0,0 +1,20 @@ +begin 644 test_read_format_zip_7075_utf8_paths.zip +M4$L#!!0````(`,$^9D5BZ95P"P````D````*````1FEL92`Q+G1X=`M)+2Y1 +M2,O,204`4$L#!!0````(`,$^9D5BZ95P"P````D````/`!@`1FEL92`R("T@ +M>'@N='AT=7`4``$NSPQQ1FEL92`R("T@P[8N='AT"TDM+E%(R\Q)!0!02P,$ +M%`````@`P3YF16+IE7`+````"0````\`&`!&:6QE(#,@+2!X>"YT>'1U'0+22TN44C+S$D%`%!+`P04````"`#!/F9% +M8NF5<`L````)````#P`8`$9I;&4@-"`M('AX+G1X='5P%``!G[AP'$9I;&4@ +M-"`M(,.E+G1X=`M)+2Y12,O,204`4$L!`A\`%`````@`P3YF16+IE7`+```` +M"0````H`)``````````@`````````$9I;&4@,2YT>'0*`"````````$`&``Q +M6UASCOG/`5^OQVV.^<\!7Z_';8[YSP%02P$"'P`4````"`#!/F9%8NF5<`L` +M```)````#@`\`````````"`````S````1FEL92`R("T@E"YT>'0*`"`````` +M``$`&``Q6UASCOG/`2M.B72.^<\!*TZ)=([YSP%U'102P$"'P`4````"`#!/F9%8NF5<`L````)````#@`\```````` +M`"````"#````1FEL92`S("T@A"YT>'0*`"````````$`&``Q6UASCOG/`5<$ +M&W>.^<\!5P0;=X[YSP%U'102P$"'P`4 +M````"`#!/F9%8NF5<`L````)````#@`\`````````"````#3````1FEL92`T +M("T@ABYT>'0*`"````````$`&``Q6UASCOG/`6#)ZG:.^<\!8,GJ=H[YSP%U +M'102P4&``````0`!`#$`0``(P$````` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.c b/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.c new file mode 100644 index 000000000..54f7fa04e --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.c @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2003-2018 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" + +/* + * Test archive verifies that we ignore padding in the extra field. + * + * APPNOTE.txt does not provide any provision for padding the extra + * field, so libarchive used to error when there were unconsumed + * bytes. Apparently, some Zip writers do routinely put zero padding + * in the extra field. + * + * The extra fields in this test (for both the local file header + * and the central directory entry) are formatted as follows: + * + * 0000 0000 - unrecognized field with type zero, zero bytes + * 5554 0900 03d258155cdb58155c - UX field with length 9 + * 0000 0400 00000000 - unrecognized field with type zero, four bytes + * 000000 - three bytes padding + * + * The two valid type zero fields should be skipped and ignored, as + * should the three bytes padding (which is too short to be a valid + * extra data object). If there were no errors and we read the UX + * field correctly, then we've correctly handled all of the padding + * fields above. + */ + + +static void verify(struct archive *a) { + struct archive_entry *ae; + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("a", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae)); + assertEqualInt(0x5c1558d2, archive_entry_mtime(ae)); + assertEqualInt(0, archive_entry_ctime(ae)); + assertEqualInt(0x5c1558db, archive_entry_atime(ae)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); +} + +DEFINE_TEST(test_read_format_zip_extra_padding) +{ + const char *refname = "test_read_format_zip_extra_padding.zip"; + struct archive *a; + char *p; + size_t s; + + extract_reference_file(refname); + + /* Verify with seeking reader. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 7)); + verify(a); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* Verify with streaming reader. */ + p = slurpfile(&s, refname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 3)); + verify(a); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + free(p); +} diff --git a/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.zip.uu b/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.zip.uu new file mode 100644 index 000000000..496bd3c49 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_zip_extra_padding.zip.uu @@ -0,0 +1,7 @@ +begin 644 test_read_format_zip_extra_padding.zip +M4$L#!`H``````"-=CTW$\L?V`@````(````!`!P`80````!55`D``])8%5S; +M6!5<```$``````````!B"E!+`0(>`PH``````"-=CTW$\L?V`@````(````! +M`!@```````$```"D@0````!A`````%54"0`#TE@57-M8%5P```0``````$L% +3!@`````!``$`1P```#T````````` +` +end diff --git a/contrib/libarchive/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu b/contrib/libarchive/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu new file mode 100644 index 000000000..5fce21355 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu @@ -0,0 +1,5 @@ +begin 644 test_read_format_zip_lzma_alone_leak.zipx +M4$L#!"`@6B`.("`@("`@("`@%0```"`@("````4``0`!`"`@(`4``"`````` +J(/\@("`@("`@("!02P,$("`@(`X@(/________\@("`@("`@(``````@ +` +end diff --git a/contrib/libarchive/libarchive/test/test_sparse_basic.c b/contrib/libarchive/libarchive/test/test_sparse_basic.c index 0df0f1d32..f12b6af48 100644 --- a/contrib/libarchive/libarchive/test/test_sparse_basic.c +++ b/contrib/libarchive/libarchive/test/test_sparse_basic.c @@ -68,6 +68,14 @@ struct sparse { static void create_sparse_file(const char *, const struct sparse *); +#if defined(__APPLE__) +/* On APFS holes need to be at least 4096x4097 bytes */ +#define MIN_HOLE 16781312 +#else +/* Elsewhere we work with 4096*10 bytes */ +#define MIN_HOLE 409600 +#endif + #if defined(_WIN32) && !defined(__CYGWIN__) #include /* @@ -491,6 +499,7 @@ DEFINE_TEST(test_sparse_basic) { char *cwd; struct archive *a; + const char *skip_sparse_tests; /* * The alignment of the hole of sparse files deeply depends * on filesystem. In my experience, sparse_file2 test with @@ -501,42 +510,42 @@ DEFINE_TEST(test_sparse_basic) */ const struct sparse sparse_file0[] = { // 0 // 1024 - { DATA, 1024 }, { HOLE, 2048000 }, + { DATA, 1024 }, { HOLE, MIN_HOLE + 1638400 }, // 2049024 // 2051072 - { DATA, 2048 }, { HOLE, 2048000 }, + { DATA, 2048 }, { HOLE, MIN_HOLE + 1638400 }, // 4099072 // 4103168 - { DATA, 4096 }, { HOLE, 20480000 }, + { DATA, 4096 }, { HOLE, MIN_HOLE + 20070400 }, // 24583168 // 24591360 - { DATA, 8192 }, { HOLE, 204800000 }, + { DATA, 8192 }, { HOLE, MIN_HOLE + 204390400 }, // 229391360 // 229391361 { DATA, 1 }, { END, 0 } }; const struct sparse sparse_file1[] = { - { HOLE, 409600 }, { DATA, 1 }, - { HOLE, 409600 }, { DATA, 1 }, - { HOLE, 409600 }, { END, 0 } + { HOLE, MIN_HOLE }, { DATA, 1 }, + { HOLE, MIN_HOLE }, { DATA, 1 }, + { HOLE, MIN_HOLE }, { END, 0 } }; const struct sparse sparse_file2[] = { - { HOLE, 409600 * 1 }, { DATA, 1024 }, - { HOLE, 409600 * 2 }, { DATA, 1024 }, - { HOLE, 409600 * 3 }, { DATA, 1024 }, - { HOLE, 409600 * 4 }, { DATA, 1024 }, - { HOLE, 409600 * 5 }, { DATA, 1024 }, - { HOLE, 409600 * 6 }, { DATA, 1024 }, - { HOLE, 409600 * 7 }, { DATA, 1024 }, - { HOLE, 409600 * 8 }, { DATA, 1024 }, - { HOLE, 409600 * 9 }, { DATA, 1024 }, - { HOLE, 409600 * 10}, { DATA, 1024 },/* 10 */ - { HOLE, 409600 * 1 }, { DATA, 1024 * 1 }, - { HOLE, 409600 * 2 }, { DATA, 1024 * 2 }, - { HOLE, 409600 * 3 }, { DATA, 1024 * 3 }, - { HOLE, 409600 * 4 }, { DATA, 1024 * 4 }, - { HOLE, 409600 * 5 }, { DATA, 1024 * 5 }, - { HOLE, 409600 * 6 }, { DATA, 1024 * 6 }, - { HOLE, 409600 * 7 }, { DATA, 1024 * 7 }, - { HOLE, 409600 * 8 }, { DATA, 1024 * 8 }, - { HOLE, 409600 * 9 }, { DATA, 1024 * 9 }, - { HOLE, 409600 * 10}, { DATA, 1024 * 10},/* 20 */ + { HOLE, MIN_HOLE }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */ + { HOLE, MIN_HOLE }, { DATA, 1024 * 1 }, + { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 }, + { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 }, + { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 }, + { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 }, + { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 }, + { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 }, + { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 }, + { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 }, + { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */ { END, 0 } }; const struct sparse sparse_file3[] = { @@ -553,6 +562,13 @@ DEFINE_TEST(test_sparse_basic) */ test_sparse_whole_file_data(); + skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); + if (skip_sparse_tests != NULL) { + skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " + "environment variable"); + return; + } + /* Check if the filesystem where CWD on can * report the number of the holes of a sparse file. */ #ifdef PATH_MAX @@ -599,10 +615,19 @@ DEFINE_TEST(test_fully_sparse_files) { char *cwd; struct archive *a; + const char *skip_sparse_tests; const struct sparse sparse_file[] = { - { HOLE, 409600 }, { END, 0 } + { HOLE, MIN_HOLE }, { END, 0 } }; + + skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); + if (skip_sparse_tests != NULL) { + skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " + "environment variable"); + return; + } + /* Check if the filesystem where CWD on can * report the number of the holes of a sparse file. */ #ifdef PATH_MAX diff --git a/contrib/libarchive/libarchive/test/test_write_disk_symlink.c b/contrib/libarchive/libarchive/test/test_write_disk_symlink.c index 7ebd6ff83..b6f0b3c85 100644 --- a/contrib/libarchive/libarchive/test/test_write_disk_symlink.c +++ b/contrib/libarchive/libarchive/test/test_write_disk_symlink.c @@ -99,6 +99,139 @@ DEFINE_TEST(test_write_disk_symlink) assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); + /* Symbolic link: dot -> . */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: dotdot -> .. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, ".."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: slash -> / */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldot -> /. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldotdot -> /.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Dir: d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1"); + archive_entry_set_mode(ae, AE_IFDIR | 0777); + archive_entry_unset_size(ae); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1nosl -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1nosl"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slash -> d1/ */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1sldot -> d1/. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slddot -> d1/.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slddot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1dir -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1dir"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1file -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1file"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_FILE); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); /* Test the entries on disk. */ @@ -107,11 +240,30 @@ DEFINE_TEST(test_write_disk_symlink) assertIsReg("link1a", -1); assertFileSize("link1a", sizeof(data)); assertFileNLinks("link1a", 1); - assertIsSymlink("link1b", "link1a"); + assertIsSymlink("link1b", "link1a", 0); /* Test #2: Should produce identical results to test #1 */ assertIsReg("link2a", -1); assertFileSize("link2a", sizeof(data)); assertFileNLinks("link2a", 1); - assertIsSymlink("link2b", "link2a"); + assertIsSymlink("link2b", "link2a", 0); + + /* Test #3: Special symlinks */ + assertIsSymlink("dot", ".", 1); + assertIsSymlink("dotdot", "..", 1); + assertIsSymlink("slash", "/", 1); + assertIsSymlink("sldot", "/.", 1); + assertIsSymlink("sldotdot", "/..", 1); + + /* Test #4: Directory symlink mixed with . and .. */ + assertIsDir("d1", -1); + /* On Windows, d1nosl should be a file symlink */ + assertIsSymlink("d1nosl", "d1", 0); + assertIsSymlink("d1slash", "d1/", 1); + assertIsSymlink("d1sldot", "d1/.", 1); + assertIsSymlink("d1slddot", "d1/..", 1); + + /* Test #5: symlink_type is set */ + assertIsSymlink("d1dir", "d1", 1); + assertIsSymlink("d1file", "d1", 0); } diff --git a/contrib/libarchive/tar/bsdtar.1 b/contrib/libarchive/tar/bsdtar.1 index 132e11457..828405470 100644 --- a/contrib/libarchive/tar/bsdtar.1 +++ b/contrib/libarchive/tar/bsdtar.1 @@ -204,6 +204,18 @@ Do not process files or directories that match the specified pattern. Note that exclusions take precedence over patterns or filenames specified on the command line. +.It Fl Fl exclude-vcs +Do not process files or directories internally used by the +version control systems +.Sq CVS , +.Sq RCS , +.Sq SCCS , +.Sq SVN , +.Sq Arch , +.Sq Bazaar , +.Sq Mercurial +and +.Sq Darcs . .It Fl Fl fflags (c, r, u, x modes only) Archive or extract file flags. This is the reverse of @@ -386,8 +398,7 @@ and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl n , Fl Fl norecurse , Fl Fl no-recursion -(c, r, u modes only) -Do not recursively archive the contents of directories. +Do not operate recursively on the content of directories. .It Fl Fl newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. diff --git a/contrib/libarchive/tar/bsdtar.c b/contrib/libarchive/tar/bsdtar.c index 481f475a1..20a121a4b 100644 --- a/contrib/libarchive/tar/bsdtar.c +++ b/contrib/libarchive/tar/bsdtar.c @@ -129,6 +129,28 @@ static void version(void) __LA_DEAD; (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ | ARCHIVE_EXTRACT_SECURE_NODOTDOT) +static char const * const vcs_files[] = { + /* CVS */ + "CVS", ".cvsignore", + /* RCS */ + "RCS", + /* SCCS */ + "SCCS", + /* SVN */ + ".svn", + /* git */ + ".git", ".gitignore", ".gitattributes", ".gitmodules", + /* Arch */ + ".arch-ids", "{arch}", "=RELEASE-ID", "=meta-update", "=update", + /* Bazaar */ + ".bzr", ".bzrignore", ".bzrtags", + /* Mercurial */ + ".hg", ".hgignore", ".hgtags", + /* darcs */ + "_darcs", + NULL +}; + int main(int argc, char **argv) { @@ -318,6 +340,15 @@ main(int argc, char **argv) lafe_errc(1, 0, "Couldn't exclude %s\n", bsdtar->argument); break; + case OPTION_EXCLUDE_VCS: /* GNU tar */ + for(t=0; vcs_files[t]; t++) { + if (archive_match_exclude_pattern( + bsdtar->matching, + vcs_files[t]) != ARCHIVE_OK) + lafe_errc(1, 0, "Couldn't " + "exclude %s\n", vcs_files[t]); + } + break; case OPTION_FFLAGS: bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS; @@ -808,8 +839,6 @@ main(int argc, char **argv) break; } } - if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) - only_mode(bsdtar, "-n", "cru"); if (bsdtar->flags & OPTFLAG_STDOUT) only_mode(bsdtar, "-O", "xt"); if (bsdtar->flags & OPTFLAG_UNLINK_FIRST) @@ -859,6 +888,16 @@ main(int argc, char **argv) only_mode(bsdtar, buff, "cru"); } + /* + * When creating an archive from a directory tree, the directory + * walking code will already avoid entering directories when + * recursive inclusion of directory content is disabled, therefore + * changing the matching behavior has no effect for creation modes. + * It is relevant for extraction or listing. + */ + archive_match_set_inclusion_recursion(bsdtar->matching, + !(bsdtar->flags & OPTFLAG_NO_SUBDIRS)); + /* Filename "-" implies stdio. */ if (strcmp(bsdtar->filename, "-") == 0) bsdtar->filename = NULL; diff --git a/contrib/libarchive/tar/bsdtar.h b/contrib/libarchive/tar/bsdtar.h index 612c2f67b..82f8462d8 100644 --- a/contrib/libarchive/tar/bsdtar.h +++ b/contrib/libarchive/tar/bsdtar.h @@ -135,6 +135,7 @@ enum { OPTION_CHROOT, OPTION_CLEAR_NOCHANGE_FFLAGS, OPTION_EXCLUDE, + OPTION_EXCLUDE_VCS, OPTION_FFLAGS, OPTION_FORMAT, OPTION_GID, diff --git a/contrib/libarchive/tar/cmdline.c b/contrib/libarchive/tar/cmdline.c index 66cf4c2d1..21558e12d 100644 --- a/contrib/libarchive/tar/cmdline.c +++ b/contrib/libarchive/tar/cmdline.c @@ -85,6 +85,7 @@ static const struct bsdtar_option { { "disable-copyfile", 0, OPTION_NO_MAC_METADATA }, { "exclude", 1, OPTION_EXCLUDE }, { "exclude-from", 1, 'X' }, + { "exclude-vcs", 0, OPTION_EXCLUDE_VCS }, { "extract", 0, 'x' }, { "fast-read", 0, 'q' }, { "fflags", 0, OPTION_FFLAGS }, diff --git a/contrib/libarchive/tar/test/test_basic.c b/contrib/libarchive/tar/test/test_basic.c index a88c43099..c483b673f 100644 --- a/contrib/libarchive/tar/test/test_basic.c +++ b/contrib/libarchive/tar/test/test_basic.c @@ -42,7 +42,7 @@ make_files(void) /* Symlink to above file. */ if (canSymlink()) - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); /* Directory. */ assertMakeDir("dir", 0775); @@ -78,7 +78,7 @@ verify_files(const char *target) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ failure("%s", target); diff --git a/contrib/libarchive/tar/test/test_copy.c b/contrib/libarchive/tar/test/test_copy.c index bc4166d6d..43ea24faf 100644 --- a/contrib/libarchive/tar/test/test_copy.c +++ b/contrib/libarchive/tar/test/test_copy.c @@ -176,7 +176,7 @@ create_tree(void) sprintf(buff, "s/%s", filenames[i]); sprintf(buff2, "../f/%s", filenames[i]); failure("buff=\"%s\" buff2=\"%s\"", buff, buff2); - assertMakeSymlink(buff, buff2); + assertMakeSymlink(buff, buff2, 0); } /* Create a dir named "d/abcdef...". */ buff[0] = 'd'; @@ -222,7 +222,7 @@ verify_tree(size_t limit) sprintf(name1, "s/%s", filenames[i]); sprintf(name2, "../f/%s", filenames[i]); if (strlen(name2) <= limit) - assertIsSymlink(name1, name2); + assertIsSymlink(name1, name2, 0); } /* Verify dir "d/abcdef...". */ diff --git a/contrib/libarchive/tar/test/test_option_C_mtree.c b/contrib/libarchive/tar/test/test_option_C_mtree.c index caf8044bf..ccadc3899 100644 --- a/contrib/libarchive/tar/test/test_option_C_mtree.c +++ b/contrib/libarchive/tar/test/test_option_C_mtree.c @@ -36,6 +36,9 @@ DEFINE_TEST(test_option_C_mtree) p0 = NULL; char *content = "./foo type=file uname=root gname=root mode=0755\n"; char *filename = "output.tar"; +#if defined(_WIN32) && !defined(CYGWIN) + char *p; +#endif /* an absolute path to mtree file */ char *mtree_file = "/METALOG.mtree"; @@ -48,9 +51,21 @@ DEFINE_TEST(test_option_C_mtree) assertMakeDir("bar", 0775); assertMakeFile("bar/foo", 0777, "abc"); - r = systemf("%s -cf %s -C bar \"@%s\" >step1.out 2>step1.err", testprog, filename, absolute_path); +#if defined(_WIN32) && !defined(CYGWIN) + p = absolute_path; + while(*p != '\0') { + if (*p == '/') + *p = '\\'; + p++; + } + r = systemf("%s -cf %s -C bar @%s >step1.out 2>step1.err", testprog, filename, absolute_path); failure("Error invoking %s -cf %s -C bar @%s", testprog, filename, absolute_path); +#else + r = systemf("%s -cf %s -C bar \"@%s\" >step1.out 2>step1.err", testprog, filename, absolute_path); + failure("Error invoking %s -cf %s -C bar \"@%s\"", testprog, filename, absolute_path); +#endif + assertEqualInt(r, 0); assertEmptyFile("step1.out"); assertEmptyFile("step1.err"); @@ -68,6 +83,7 @@ DEFINE_TEST(test_option_C_mtree) assertEqualMem(p0 + 1536, "\0\0\0\0\0\0\0\0", 8); done: free(p0); + free(absolute_path); } diff --git a/contrib/libarchive/tar/test/test_option_H_upper.c b/contrib/libarchive/tar/test/test_option_H_upper.c index adc294b55..2c2ad33ce 100644 --- a/contrib/libarchive/tar/test/test_option_H_upper.c +++ b/contrib/libarchive/tar/test/test_option_H_upper.c @@ -39,13 +39,13 @@ DEFINE_TEST(test_option_H_upper) assertMakeDir("in", 0755); assertChdir("in"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 1: Without -H */ @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -H, no symlink on command line. */ @@ -69,11 +69,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test2"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -H, some symlinks on command line. */ @@ -84,9 +84,9 @@ DEFINE_TEST(test_option_H_upper) assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("d1/link1", "file1"); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("d1/link1", "file1", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/contrib/libarchive/tar/test/test_option_L_upper.c b/contrib/libarchive/tar/test/test_option_L_upper.c index f5a3c5ab4..5697b0f29 100644 --- a/contrib/libarchive/tar/test/test_option_L_upper.c +++ b/contrib/libarchive/tar/test/test_option_L_upper.c @@ -39,13 +39,13 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("in", 0755); assertChdir("in"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 1: Without -L */ @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_L_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -L, no symlink on command line. */ @@ -71,9 +71,9 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -L, some symlinks on command line. */ @@ -85,8 +85,8 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/contrib/libarchive/tar/test/test_option_U_upper.c b/contrib/libarchive/tar/test/test_option_U_upper.c index 2c43e002d..d864e13c4 100644 --- a/contrib/libarchive/tar/test/test_option_U_upper.c +++ b/contrib/libarchive/tar/test/test_option_U_upper.c @@ -79,10 +79,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test3", 0755); assertChdir("test3"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); r = systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog); assert(r != 0); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileNotExists("d1/file1"); assertEmptyFile("test.out"); assertNonEmptyFile("test.err"); @@ -92,7 +92,7 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test4", 0755); assertChdir("test4"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xUf ../archive.tar >test.out 2>test.err", testprog)); assertIsDir("d1", -1); @@ -105,10 +105,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test5", 0755); assertChdir("test5"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); @@ -118,10 +118,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test6", 0755); assertChdir("test6"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); @@ -132,7 +132,7 @@ DEFINE_TEST(test_option_U_upper) assertChdir("test7"); assertMakeDir("d1", 0755); assertMakeFile("d1/realfile1", 0644, "realfile1"); - assertMakeSymlink("d1/file1", "d1/realfile1"); + assertMakeSymlink("d1/file1", "d1/realfile1", 0); assertEqualInt(0, systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); assertIsReg("d1/file1", umasked(0644)); @@ -147,7 +147,7 @@ DEFINE_TEST(test_option_U_upper) assertChdir("test8"); assertMakeDir("d1", 0755); assertMakeFile("d1/realfile1", 0644, "realfile1"); - assertMakeSymlink("d1/file1", "d1/realfile1"); + assertMakeSymlink("d1/file1", "d1/realfile1", 0); assertEqualInt(0, systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); assertIsReg("d1/file1", umasked(0644)); diff --git a/contrib/libarchive/tar/test/test_option_exclude_vcs.c b/contrib/libarchive/tar/test/test_option_exclude_vcs.c new file mode 100644 index 000000000..202151139 --- /dev/null +++ b/contrib/libarchive/tar/test/test_option_exclude_vcs.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2019 Martin Matuska + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_option_exclude_vcs) +{ + assertMakeDir("in", 0755); + assertChdir("in"); + assertMakeFile("file", 0644, ""); + assertMakeDir("dir", 0755); + assertMakeDir("CVS", 0755); + assertMakeFile("CVS/fileattr", 0644, ""); + assertMakeFile(".cvsignore", 0644, ""); + assertMakeDir("RCS", 0755); + assertMakeFile("RCS/somefile", 0655, ""); + assertMakeDir("SCCS", 0755); + assertMakeFile("SCCS/somefile", 0655, ""); + assertMakeDir(".svn", 0755); + assertMakeFile(".svn/format", 0655, ""); + assertMakeDir(".git", 0755); + assertMakeFile(".git/config", 0655, ""); + assertMakeFile(".gitignore", 0644, ""); + assertMakeFile(".gitattributes", 0644, ""); + assertMakeFile(".gitmodules", 0644, ""); + assertMakeDir(".arch-ids", 0755); + assertMakeFile(".arch-ids/somefile", 0644, ""); + assertMakeDir("{arch}", 0755); + assertMakeFile("{arch}/somefile", 0644, ""); + assertMakeFile("=RELEASE-ID", 0644, ""); + assertMakeFile("=meta-update", 0644, ""); + assertMakeFile("=update", 0644, ""); + assertMakeDir(".bzr", 0755); + assertMakeDir(".bzr/checkout", 0755); + assertMakeFile(".bzrignore", 0644, ""); + assertMakeFile(".bzrtags", 0644, ""); + assertMakeDir(".hg", 0755); + assertMakeFile(".hg/dirstate", 0644, ""); + assertMakeFile(".hgignore", 0644, ""); + assertMakeFile(".hgtags", 0644, ""); + assertMakeDir("_darcs", 0755); + assertMakeFile("_darcs/format", 0644, ""); + assertChdir(".."); + + assertEqualInt(0, systemf("%s -c -C in -f included.tar .", testprog)); + assertEqualInt(0, + systemf("%s -c --exclude-vcs -C in -f excluded.tar .", testprog)); + + /* No flags, archive with vcs files */ + assertMakeDir("vcs-noexclude", 0755); + assertEqualInt(0, systemf("%s -x -C vcs-noexclude -f included.tar", + testprog)); + assertChdir("vcs-noexclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertIsDir("CVS", 0755); + assertFileExists("CVS/fileattr"); + assertFileExists(".cvsignore"); + assertIsDir("RCS", 0755); + assertFileExists("RCS/somefile"); + assertIsDir("SCCS", 0755); + assertFileExists("SCCS/somefile"); + assertIsDir(".svn", 0755); + assertFileExists(".svn/format"); + assertIsDir(".git", 0755); + assertFileExists(".git/config"); + assertFileExists(".gitignore"); + assertFileExists(".gitattributes"); + assertFileExists(".gitmodules"); + assertIsDir(".arch-ids", 0755); + assertFileExists(".arch-ids/somefile"); + assertIsDir("{arch}", 0755); + assertFileExists("{arch}/somefile"); + assertFileExists("=RELEASE-ID"); + assertFileExists("=meta-update"); + assertFileExists("=update"); + assertIsDir(".bzr", 0755); + assertIsDir(".bzr/checkout", 0755); + assertFileExists(".bzrignore"); + assertFileExists(".bzrtags"); + assertIsDir(".hg", 0755); + assertFileExists(".hg/dirstate"); + assertFileExists(".hgignore"); + assertFileExists(".hgtags"); + assertIsDir("_darcs", 0755); + assertFileExists("_darcs/format"); + assertChdir(".."); + + /* --exclude-vcs, archive with vcs files */ + assertMakeDir("vcs-exclude", 0755); + assertEqualInt(0, + systemf("%s -x --exclude-vcs -C vcs-exclude -f included.tar", testprog)); + assertChdir("vcs-exclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); + assertChdir(".."); + + /* --exclude-vcs, archive without vcs files */ + assertMakeDir("novcs-exclude", 0755); + assertEqualInt(0, + systemf("%s -x --exclude-vcs -C novcs-exclude -f excluded.tar", + testprog)); + assertChdir("novcs-exclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); + assertChdir(".."); + + /* No flags, archive without vcs files */ + assertMakeDir("novcs-noexclude", 0755); + assertEqualInt(0, + systemf("%s -x -C novcs-noexclude -f excluded.tar", testprog)); + assertChdir("novcs-noexclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); +} diff --git a/contrib/libarchive/tar/test/test_option_n.c b/contrib/libarchive/tar/test/test_option_n.c index 18ab6142d..e474ac1d5 100644 --- a/contrib/libarchive/tar/test/test_option_n.c +++ b/contrib/libarchive/tar/test/test_option_n.c @@ -25,8 +25,14 @@ #include "test.h" __FBSDID("$FreeBSD$"); +#ifdef HAVE_SYS_WAIT_H +#include +#endif + DEFINE_TEST(test_option_n) { + int status; + assertMakeDir("d1", 0755); assertMakeFile("d1/file1", 0644, "d1/file1"); @@ -58,4 +64,79 @@ DEFINE_TEST(test_option_n) assertIsDir("d1", umasked(0755)); assertFileNotExists("d1/file1"); assertChdir(".."); + + /* + * Create a test archive with the following content: + * d1/ + * d1/file1 + * d1/file2 + * file3 + * d2/file4 + * + * Extracting uses the same code as listing and thus does not + * get tested separately. This also covers the + * archive_match_set_inclusion_recursion() + * API. + */ + assertMakeFile("d1/file2", 0644, "d1/file2"); + assertMakeFile("file3", 0644, "file3"); + assertMakeDir("d2", 0755); + assertMakeFile("d2/file4", 0644, "d2/file4"); + assertEqualInt(0, + systemf("%s -cnf partial-archive.tar d1 d1/file1 d1/file2 file3 " + "d2/file4 >c.out 2>c.err", testprog)); + + /* Test 3: -t without other options */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar >test3.out 2>test3.err", + testprog)); + assertEmptyFile("test3.err"); + assertTextFileContents("d1/\n" + "d1/file1\n" + "d1/file2\n" + "file3\n" + "d2/file4\n", + "test3.out"); + + /* Test 4: -t without -n and some entries selected */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar d1 file3 d2/file4 " + ">test4.out 2>test4.err", testprog)); + assertEmptyFile("test4.err"); + assertTextFileContents("d1/\n" + "d1/file1\n" + "d1/file2\n" + "file3\n" + "d2/file4\n", + "test4.out"); + + /* Test 5: -t with -n and some entries selected */ + assertEqualInt(0, + systemf("%s -tnf partial-archive.tar d1 file3 d2/file4 " + ">test5.out 2>test5.err", testprog)); + assertEmptyFile("test5.err"); + assertTextFileContents("d1/\n" + "file3\n" + "d2/file4\n", + "test5.out"); + + /* Test 6: -t without -n and non-existant directory selected */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar d2 >test6.out 2>test6.err", + testprog)); + assertEmptyFile("test6.err"); + assertTextFileContents("d2/file4\n", + "test6.out"); + + /* Test 7: -t with -n and non-existant directory selected */ + status = systemf("%s -tnf partial-archive.tar d2 " + ">test7.out 2>test7.err", testprog); + assert(status); + assert(status != -1); +#if !defined(_WIN32) || defined(__CYGWIN__) + assert(WIFEXITED(status)); + assertEqualInt(1, WEXITSTATUS(status)); +#endif + assertNonEmptyFile("test7.err"); + assertEmptyFile("test7.out"); } diff --git a/contrib/libarchive/tar/test/test_option_s.c b/contrib/libarchive/tar/test/test_option_s.c index c39f71d4d..d9cfd9e53 100644 --- a/contrib/libarchive/tar/test/test_option_s.c +++ b/contrib/libarchive/tar/test/test_option_s.c @@ -36,7 +36,7 @@ DEFINE_TEST(test_option_s) assertMakeFile("in/d1/bar", 0644, "bar"); if (canSymlink()) { assertMakeFile("in/d1/realfile", 0644, "realfile"); - assertMakeSymlink("in/d1/symlink", "realfile"); + assertMakeSymlink("in/d1/symlink", "realfile", 0); } assertMakeFile("in/d1/hardlink1", 0644, "hardlinkedfile"); assertMakeHardlink("in/d1/hardlink2", "in/d1/hardlink1"); @@ -109,14 +109,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test6a/in/d2/realfile"); assertFileContents("realfile", 8, "test6a/in/d2/symlink"); - assertIsSymlink("test6a/in/d2/symlink", "realfile"); + assertIsSymlink("test6a/in/d2/symlink", "realfile", 0); /* At creation time. */ assertMakeDir("test6b", 0755); systemf("%s -cf - -s /d1/d2/ in/d1 | %s -xf - -C test6b", testprog, testprog); assertFileContents("realfile", 8, "test6b/in/d2/realfile"); assertFileContents("realfile", 8, "test6b/in/d2/symlink"); - assertIsSymlink("test6b/in/d2/symlink", "realfile"); + assertIsSymlink("test6b/in/d2/symlink", "realfile", 0); } /* @@ -129,14 +129,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test7a/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7a/in/d1/symlink"); - assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed", 0); /* At creation. */ assertMakeDir("test7b", 0755); systemf("%s -cf - -s /realfile/realfile-renamed/ in/d1 | %s -xf - -C test7b", testprog, testprog); assertFileContents("realfile", 8, "test7b/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7b/in/d1/symlink"); - assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed", 0); } /* @@ -192,7 +192,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10a/in/d1/foo"); assertFileContents("foo", 3, "test10a/in/d1/realfile"); assertFileContents("foo", 3, "test10a/in/d1/symlink"); - assertIsSymlink("test10a/in/d1/symlink", "realfile"); + assertIsSymlink("test10a/in/d1/symlink", "realfile", 0); /* At creation. */ assertMakeDir("test10b", 0755); systemf("%s -cf - -s /realfile/foo/S -s /foo/realfile/ in/d1 | %s -xf - -C test10b", @@ -200,7 +200,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10b/in/d1/foo"); assertFileContents("foo", 3, "test10b/in/d1/realfile"); assertFileContents("foo", 3, "test10b/in/d1/symlink"); - assertIsSymlink("test10b/in/d1/symlink", "realfile"); + assertIsSymlink("test10b/in/d1/symlink", "realfile", 0); } /* @@ -214,7 +214,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11a/in/d1/foo"); assertFileContents("realfile", 8, "test11a/in/d1/realfile"); assertFileContents("foo", 3, "test11a/in/d1/symlink"); - assertIsSymlink("test11a/in/d1/symlink", "foo"); + assertIsSymlink("test11a/in/d1/symlink", "foo", 0); /* At creation. */ assertMakeDir("test11b", 0755); systemf("%s -cf - -s /realfile/foo/R in/d1 | %s -xf - -C test11b", @@ -222,7 +222,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11b/in/d1/foo"); assertFileContents("realfile", 8, "test11b/in/d1/realfile"); assertFileContents("foo", 3, "test11b/in/d1/symlink"); - assertIsSymlink("test11b/in/d1/symlink", "foo"); + assertIsSymlink("test11b/in/d1/symlink", "foo", 0); } /* diff --git a/contrib/libarchive/tar/test/test_strip_components.c b/contrib/libarchive/tar/test/test_strip_components.c index 25ff1ead5..ac19abf47 100644 --- a/contrib/libarchive/tar/test/test_strip_components.c +++ b/contrib/libarchive/tar/test/test_strip_components.c @@ -36,8 +36,8 @@ DEFINE_TEST(test_strip_components) assertMakeHardlink("l1", "d1/d2/f1"); assertMakeHardlink("d1/l2", "d1/d2/f1"); if (canSymlink()) { - assertMakeSymlink("s1", "d1/d2/f1"); - assertMakeSymlink("d1/s2", "d2/f1"); + assertMakeSymlink("s1", "d1/d2/f1", 0); + assertMakeSymlink("d1/s2", "d2/f1", 0); } assertChdir(".."); @@ -64,9 +64,10 @@ DEFINE_TEST(test_strip_components) failure("d0/d1/s2 is a symlink to something that won't be extracted"); /* If platform supports symlinks, target/s2 is a broken symlink. */ /* If platform does not support symlink, target/s2 doesn't exist. */ - assertFileNotExists("target/s2"); if (canSymlink()) - assertIsSymlink("target/s2", "d2/f1"); + assertIsSymlink("target/s2", "d2/f1", 0); + else + assertFileNotExists("target/s2"); failure("d0/d1/d2 should be extracted"); assertIsDir("target/d2", -1); @@ -122,7 +123,7 @@ DEFINE_TEST(test_strip_components) /* If platform supports symlinks, target/s2 is included. */ if (canSymlink()) { failure("d0/d1/s2 is a symlink to something included in archive"); - assertIsSymlink("target2/s2", "d2/f1"); + assertIsSymlink("target2/s2", "d2/f1", 0); } failure("d0/d1/d2 should be archived"); assertIsDir("target2/d2", -1); diff --git a/contrib/libarchive/tar/test/test_symlink_dir.c b/contrib/libarchive/tar/test/test_symlink_dir.c index beadb3c2e..df529d04a 100644 --- a/contrib/libarchive/tar/test/test_symlink_dir.c +++ b/contrib/libarchive/tar/test/test_symlink_dir.c @@ -66,22 +66,22 @@ DEFINE_TEST(test_symlink_dir) /* "dir" is a symlink to an existing "dest1/real_dir" */ assertMakeDir("dest1/real_dir", 0755); if (canSymlink()) { - assertMakeSymlink("dest1/dir", "real_dir"); + assertMakeSymlink("dest1/dir", "real_dir", 1); /* "dir2" is a symlink to a non-existing "real_dir2" */ - assertMakeSymlink("dest1/dir2", "real_dir2"); + assertMakeSymlink("dest1/dir2", "real_dir2", 1); } else { skipping("Symlinks are not supported on this platform"); } /* "dir3" is a symlink to an existing "non_dir3" */ assertMakeFile("dest1/non_dir3", 0755, "abcdef"); if (canSymlink()) - assertMakeSymlink("dest1/dir3", "non_dir3"); + assertMakeSymlink("dest1/dir3", "non_dir3", 1); /* "file" is a symlink to existing "real_file" */ assertMakeFile("dest1/real_file", 0755, "abcdefg"); if (canSymlink()) { - assertMakeSymlink("dest1/file", "real_file"); + assertMakeSymlink("dest1/file", "real_file", 0); /* "file2" is a symlink to non-existing "real_file2" */ - assertMakeSymlink("dest1/file2", "real_file2"); + assertMakeSymlink("dest1/file2", "real_file2", 0); } assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog)); @@ -108,32 +108,32 @@ DEFINE_TEST(test_symlink_dir) /* "dir" is a symlink to existing "real_dir" */ assertMakeDir("dest2/real_dir", 0755); if (canSymlink()) - assertMakeSymlink("dest2/dir", "real_dir"); + assertMakeSymlink("dest2/dir", "real_dir", 1); /* "dir2" is a symlink to a non-existing "real_dir2" */ if (canSymlink()) - assertMakeSymlink("dest2/dir2", "real_dir2"); + assertMakeSymlink("dest2/dir2", "real_dir2", 1); /* "dir3" is a symlink to an existing "non_dir3" */ assertMakeFile("dest2/non_dir3", 0755, "abcdefgh"); if (canSymlink()) - assertMakeSymlink("dest2/dir3", "non_dir3"); + assertMakeSymlink("dest2/dir3", "non_dir3", 1); /* "file" is a symlink to existing "real_file" */ assertMakeFile("dest2/real_file", 0755, "abcdefghi"); if (canSymlink()) - assertMakeSymlink("dest2/file", "real_file"); + assertMakeSymlink("dest2/file", "real_file", 0); /* "file2" is a symlink to non-existing "real_file2" */ if (canSymlink()) - assertMakeSymlink("dest2/file2", "real_file2"); + assertMakeSymlink("dest2/file2", "real_file2", 0); assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog)); /* "dir4" is a symlink to existing "real_dir" */ if (canSymlink()) - assertMakeSymlink("dest2/dir4", "real_dir"); + assertMakeSymlink("dest2/dir4", "real_dir", 1); assertEqualInt(0, systemf("%s -xPf test2.tar -C dest2", testprog)); /* dest2/dir and dest2/dir4 symlinks should be followed */ if (canSymlink()) { - assertIsSymlink("dest2/dir", "real_dir"); - assertIsSymlink("dest2/dir4", "real_dir"); + assertIsSymlink("dest2/dir", "real_dir", 1); + assertIsSymlink("dest2/dir4", "real_dir", 1); assertIsDir("dest2/real_dir", -1); } diff --git a/contrib/libarchive/test_utils/test_common.h b/contrib/libarchive/test_utils/test_common.h index 69fd44f72..713760aa4 100644 --- a/contrib/libarchive/test_utils/test_common.h +++ b/contrib/libarchive/test_utils/test_common.h @@ -83,7 +83,9 @@ #include #endif #ifdef HAVE_WINDOWS_H +#define NOCRYPT #include +#include #endif /* @@ -218,8 +220,8 @@ assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsReg(pathname, mode) \ assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) +#define assertIsSymlink(pathname, contents, isdir) \ + assertion_is_symlink(__FILE__, __LINE__, pathname, contents, isdir) /* Create a directory, report error if it fails. */ #define assertMakeDir(dirname, mode) \ assertion_make_dir(__FILE__, __LINE__, dirname, mode) @@ -229,8 +231,8 @@ assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) #define assertMakeHardlink(newfile, oldfile) \ assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) +#define assertMakeSymlink(newfile, linkto, targetIsDir) \ + assertion_make_symlink(__FILE__, __LINE__, newfile, linkto, targetIsDir) #define assertSetNodump(path) \ assertion_set_nodump(__FILE__, __LINE__, path) #define assertUmask(mask) \ @@ -287,11 +289,11 @@ int assertion_is_dir(const char *, int, const char *, int); int assertion_is_hardlink(const char *, int, const char *, const char *); int assertion_is_not_hardlink(const char *, int, const char *, const char *); int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); +int assertion_is_symlink(const char *, int, const char *, const char *, int); int assertion_make_dir(const char *, int, const char *, int); int assertion_make_file(const char *, int, const char *, int, int, const void *); int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); +int assertion_make_symlink(const char *, int, const char *newpath, const char *, int); int assertion_non_empty_file(const char *, int, const char *); int assertion_set_nodump(const char *, int, const char *); int assertion_text_file_contents(const char *, int, const char *buff, const char *f); diff --git a/contrib/libarchive/test_utils/test_main.c b/contrib/libarchive/test_utils/test_main.c index b360b2a14..8226f1587 100644 --- a/contrib/libarchive/test_utils/test_main.c +++ b/contrib/libarchive/test_utils/test_main.c @@ -168,6 +168,32 @@ static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + static void * GetFunctionKernel32(const char *name) { @@ -185,15 +211,101 @@ GetFunctionKernel32(const char *name) } static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) +my_CreateSymbolicLinkA(const char *linkname, const char *target, + int targetIsDir) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); + DWORD attrs; static int set; + int ret, tmpflags, llen, tlen; + int flags = 0; + char *src, *tgt, *p; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } - return f == NULL ? 0 : (*f)(linkname, target, flags); + if (f == NULL) + return (0); + + tlen = strlen(target); + llen = strlen(linkname); + + if (tlen == 0 || llen == 0) + return (0); + + tgt = malloc((tlen + 1) * sizeof(char)); + if (tgt == NULL) + return (0); + src = malloc((llen + 1) * sizeof(char)); + if (src == NULL) { + free(tgt); + return (0); + } + + /* + * Translate slashes to backslashes + */ + p = src; + while(*linkname != '\0') { + if (*linkname == '/') + *p = '\\'; + else + *p = *linkname; + linkname++; + p++; + } + *p = '\0'; + + p = tgt; + while(*target != '\0') { + if (*target == '/') + *p = '\\'; + else + *p = *target; + target++; + p++; + } + *p = '\0'; + + /* + * Each test has to specify if a file or a directory symlink + * should be created. + */ + if (targetIsDir) { +#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; +#else + flags |= 0x1; +#endif + } + +#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) + tmpflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +#else + tmpflags = flags | 0x2; +#endif + /* + * Windows won't overwrite existing links + */ + attrs = GetFileAttributesA(linkname); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + RemoveDirectoryA(linkname); + else + DeleteFileA(linkname); + } + + ret = (*f)(src, tgt, tmpflags); + /* + * Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + * is not undestood + */ + if (!ret) + ret = (*f)(src, tgt, flags); + + free(src); + free(tgt); + return (ret); } static int @@ -1599,26 +1711,146 @@ assertion_is_reg(const char *file, int line, const char *pathname, int mode) return (1); } -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ +/* + * Check whether 'pathname' is a symbolic link. If 'contents' is + * non-NULL, verify that the symlink has those contents. + * + * On platforms with directory symlinks, set isdir to 0 to test for a file + * symlink and to 1 to test for a directory symlink. On other platforms + * the variable is ignored. + */ static int is_symlink(const char *file, int line, - const char *pathname, const char *contents) + const char *pathname, const char *contents, int isdir) { #if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); + HANDLE h; + DWORD inbytes; + REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; + size_t len, len2; + wchar_t *linknamew, *contentsw; + const char *p; + char *s, *pn; + int ret = 0; + BYTE *indata; + const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT; + + /* Replace slashes with backslashes in pathname */ + pn = malloc((strlen(pathname) + 1) * sizeof(char)); + p = pathname; + s = pn; + while(*p != '\0') { + if(*p == '/') + *s = '\\'; + else + *s = *p; + p++; + s++; + } + *s = '\0'; + + h = CreateFileA(pn, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + flag, NULL); + free(pn); + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); + return (0); + } + ret = GetFileInformationByHandle(h, &st); + if (ret == 0) { + failure_start(file, line, + "Can't stat: %s", pathname); + failure_finish(NULL); + } else if ((st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (isdir && ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) { + failure_start(file, line, + "Not a directory symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (!isdir && + ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { + failure_start(file, line, + "Not a file symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (ret == 0) { + CloseHandle(h); + return (0); + } + + indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, + 1024, &inbytes, NULL); + CloseHandle(h); + if (ret == 0) { + free(indata); + failure_start(file, line, + "Could not retrieve symlink target: %s", pathname); + failure_finish(NULL); + return (0); + } + + buf = (REPARSE_DATA_BUFFER *) indata; + if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + free(indata); + /* File is not a symbolic link */ + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); + return (0); + } + + if (contents == NULL) { + free(indata); + return (1); + } + + len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength; + + linknamew = malloc(len + sizeof(wchar_t)); + if (linknamew == NULL) { + free(indata); + return (0); + } + + memcpy(linknamew, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer) + [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len); + free(indata); + + linknamew[len / sizeof(wchar_t)] = L'\0'; + + contentsw = malloc(len + sizeof(wchar_t)); + if (contentsw == NULL) { + free(linknamew); + return (0); + } + + len2 = mbsrtowcs(contentsw, &contents, (len + sizeof(wchar_t) + / sizeof(wchar_t)), NULL); + + if (len2 > 0 && wcscmp(linknamew, contentsw) != 0) + ret = 1; + + free(linknamew); + free(contentsw); + return (ret); #else char buff[300]; struct stat st; ssize_t linklen; int r; + (void)isdir; /* UNUSED */ assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { @@ -1647,9 +1879,9 @@ is_symlink(const char *file, int line, /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) + const char *path, const char *contents, int isdir) { - if (is_symlink(file, line, path, contents)) + if (is_symlink(file, line, path, contents, isdir)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", @@ -1777,20 +2009,26 @@ assertion_make_hardlink(const char *file, int line, return(0); } -/* Create a symlink and report any failures. */ +/* + * Create a symlink and report any failures. + * + * Windows symlinks need to know if the target is a directory. + */ int assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) + const char *newpath, const char *linkto, int targetIsDir) { #if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK + (void)targetIsDir; /* UNUSED */ assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); +#else + (void)targetIsDir; /* UNUSED */ #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); @@ -2217,10 +2455,12 @@ canSymlink(void) * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0", + 0); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0", + 0); #endif return (value); } diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 4fe0f4e5c..2de133141 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -169,6 +169,7 @@ SRCS= archive_acl.c \ MAN= archive_entry.3 \ archive_entry_acl.3 \ archive_entry_linkify.3 \ + archive_entry_misc.3 \ archive_entry_paths.3 \ archive_entry_perms.3 \ archive_entry_stat.3 \ diff --git a/lib/libarchive/tests/Makefile b/lib/libarchive/tests/Makefile index 76889dc23..39675976f 100644 --- a/lib/libarchive/tests/Makefile +++ b/lib/libarchive/tests/Makefile @@ -169,6 +169,7 @@ TESTS_SRCS= \ test_read_format_tar_concatenated.c \ test_read_format_tar_empty_filename.c \ test_read_format_tar_empty_pax.c \ + test_read_format_tar_empty_with_gnulabel.c \ test_read_format_tar_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ @@ -179,10 +180,12 @@ TESTS_SRCS= \ test_read_format_warc.c \ test_read_format_xar.c \ test_read_format_zip.c \ + test_read_format_zip_7075_utf8_paths.c \ test_read_format_zip_comment_stored.c \ test_read_format_zip_encryption_data.c \ test_read_format_zip_encryption_header.c \ test_read_format_zip_encryption_partially.c \ + test_read_format_zip_extra_padding.c \ test_read_format_zip_filename.c \ test_read_format_zip_high_compression.c \ test_read_format_zip_jar.c \ @@ -489,6 +492,7 @@ FILES+= test_read_format_mtree_crash747.mtree.bz2.uu FILES+= test_read_format_mtree_nomagic.mtree.uu FILES+= test_read_format_mtree_nomagic2.mtree.uu FILES+= test_read_format_mtree_nomagic3.mtree.uu +FILES+= test_read_format_mtree_noprint.mtree.uu FILES+= test_read_format_rar.rar.uu FILES+= test_read_format_rar_binary_data.rar.uu FILES+= test_read_format_rar_compress_best.rar.uu @@ -504,6 +508,7 @@ FILES+= test_read_format_rar_multivolume.part0003.rar.uu FILES+= test_read_format_rar_multivolume.part0004.rar.uu FILES+= test_read_format_rar_noeof.rar.uu FILES+= test_read_format_rar_ppmd_lzss_conversion.rar.uu +FILES+= test_read_format_rar_ppmd_use_after_free.rar.uu FILES+= test_read_format_rar_sfx.exe.uu FILES+= test_read_format_rar_subblock.rar.uu FILES+= test_read_format_rar_unicode.rar.uu @@ -511,6 +516,13 @@ FILES+= test_read_format_rar_windows.rar.uu FILES+= test_read_format_rar5_arm.rar.uu FILES+= test_read_format_rar5_blake2.rar.uu FILES+= test_read_format_rar5_compressed.rar.uu +FILES+= test_read_format_rar5_distance_overflow.rar.uu +FILES+= test_read_format_rar5_extra_field_version.rar.uu +FILES+= test_read_format_rar5_fileattr.rar.uu +FILES+= test_read_format_rar5_hardlink.rar.uu +FILES+= test_read_format_rar5_invalid_dict_reference.rar.uu +FILES+= test_read_format_rar5_leftshift1.rar.uu +FILES+= test_read_format_rar5_leftshift2.rar.uu FILES+= test_read_format_rar5_multiarchive.part01.rar.uu FILES+= test_read_format_rar5_multiarchive.part02.rar.uu FILES+= test_read_format_rar5_multiarchive.part03.rar.uu @@ -519,28 +531,36 @@ FILES+= test_read_format_rar5_multiarchive.part05.rar.uu FILES+= test_read_format_rar5_multiarchive.part06.rar.uu FILES+= test_read_format_rar5_multiarchive.part07.rar.uu FILES+= test_read_format_rar5_multiarchive.part08.rar.uu -FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu -FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu -FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu -FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu +FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu +FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu +FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu +FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu FILES+= test_read_format_rar5_multiple_files.rar.uu -FILES+= test_read_format_rar5_multiple_files_solid.rar.uu +FILES+= test_read_format_rar5_multiple_files_solid.rar.uu +FILES+= test_read_format_rar5_nonempty_dir_stream.rar.uu +FILES+= test_read_format_rar5_owner.rar.uu +FILES+= test_read_format_rar5_readtables_overflow.rar.uu FILES+= test_read_format_rar5_solid.rar.uu FILES+= test_read_format_rar5_stored.rar.uu FILES+= test_read_format_rar5_stored_manyfiles.rar.uu +FILES+= test_read_format_rar5_symlink.rar.uu +FILES+= test_read_format_rar5_truncated_huff.rar.uu FILES+= test_read_format_rar5_win32.rar.uu FILES+= test_read_format_raw.bufr.uu FILES+= test_read_format_raw.data.Z.uu +FILES+= test_read_format_raw.data.gz.uu FILES+= test_read_format_raw.data.uu FILES+= test_read_format_tar_concatenated.tar.uu FILES+= test_read_format_tar_empty_filename.tar.uu FILES+= test_read_format_tar_empty_pax.tar.Z.uu +FILES+= test_read_format_tar_empty_with_gnulabel.tar.uu FILES+= test_read_format_tar_filename_koi8r.tar.Z.uu FILES+= test_read_format_ustar_filename_cp866.tar.Z.uu FILES+= test_read_format_ustar_filename_eucjp.tar.Z.uu FILES+= test_read_format_ustar_filename_koi8r.tar.Z.uu FILES+= test_read_format_warc.warc.uu FILES+= test_read_format_zip.zip.uu +FILES+= test_read_format_zip_7075_utf8_paths.zip.uu FILES+= test_read_format_zip_bz2_hang.zip.uu FILES+= test_read_format_zip_bzip2.zipx.uu FILES+= test_read_format_zip_bzip2_multi.zipx.uu @@ -549,6 +569,7 @@ FILES+= test_read_format_zip_comment_stored_2.zip.uu FILES+= test_read_format_zip_encryption_data.zip.uu FILES+= test_read_format_zip_encryption_header.zip.uu FILES+= test_read_format_zip_encryption_partially.zip.uu +FILES+= test_read_format_zip_extra_padding.zip.uu FILES+= test_read_format_zip_filename_cp866.zip.uu FILES+= test_read_format_zip_filename_cp932.zip.uu FILES+= test_read_format_zip_filename_koi8r.zip.uu @@ -558,6 +579,7 @@ FILES+= test_read_format_zip_filename_utf8_ru2.zip.uu FILES+= test_read_format_zip_high_compression.zip.uu FILES+= test_read_format_zip_jar.jar.uu FILES+= test_read_format_zip_length_at_end.zip.uu +FILES+= test_read_format_zip_lzma_alone_leak.zipx.uu FILES+= test_read_format_zip_lzma.zipx.uu FILES+= test_read_format_zip_lzma_multi.zipx.uu FILES+= test_read_format_zip_mac_metadata.zip.uu diff --git a/usr.bin/tar/tests/Makefile b/usr.bin/tar/tests/Makefile index 97c43c7c4..74a4d4bce 100644 --- a/usr.bin/tar/tests/Makefile +++ b/usr.bin/tar/tests/Makefile @@ -54,6 +54,7 @@ TESTS_SRCS= \ test_option_b.c \ test_option_b64encode.c \ test_option_exclude.c \ + test_option_exclude_vcs.c \ test_option_fflags.c \ test_option_gid_gname.c \ test_option_grzip.c \ -- 2.42.0