aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2016-08-13 21:20:06 +0000
committerMartin Matuska <mm@FreeBSD.org>2016-08-13 21:20:06 +0000
commit5b0ba629933ba7fd02398bcd8600d27a17fdf9b0 (patch)
treecf724fc79f53afcd95989182e3bd92b1e90e31ee
parent50fc3a26ef19f9dbc718e1d27615c28c5ad6743d (diff)
downloadsrc-5b0ba629933ba7fd02398bcd8600d27a17fdf9b0.tar.gz
src-5b0ba629933ba7fd02398bcd8600d27a17fdf9b0.zip
Update vendor/libarchive to git 6a0d970f70102fe50ee9f1e51a2e4c048985e616
Vendor issues fixed: Issue #744: Very long pathnames evade symlink checks Issue #748: libarchive can compress, but cannot decompress zip some files PR #750: ustar: fix out of bounds read on empty string ("") filename PR #755: fix use of acl_get_flagset_np() on FreeBSD
Notes
Notes: svn path=/vendor/libarchive/dist/; revision=304060
-rw-r--r--CMakeLists.txt8
-rw-r--r--Makefile.am4
-rw-r--r--build/cmake/config.h.in6
-rw-r--r--cat/test/test_version.c2
-rw-r--r--cpio/cmdline.c1
-rw-r--r--cpio/test/test_option_version.c2
-rw-r--r--libarchive/archive_match.c2
-rw-r--r--libarchive/archive_ppmd7_private.h2
-rw-r--r--libarchive/archive_read_add_passphrase.c22
-rw-r--r--libarchive/archive_read_disk_entry_from_file.c17
-rw-r--r--libarchive/archive_read_disk_posix.c6
-rw-r--r--libarchive/archive_read_disk_windows.c6
-rw-r--r--libarchive/archive_read_private.h2
-rw-r--r--libarchive/archive_read_support_filter_lz4.c2
-rw-r--r--libarchive/archive_read_support_format_lha.c7
-rw-r--r--libarchive/archive_read_support_format_warc.c2
-rw-r--r--libarchive/archive_util.c2
-rw-r--r--libarchive/archive_write_disk_acl.c18
-rw-r--r--libarchive/archive_write_disk_posix.c4
-rw-r--r--libarchive/archive_write_set_format_iso9660.c2
-rw-r--r--libarchive/archive_write_set_format_ustar.c2
-rw-r--r--libarchive/test/CMakeLists.txt6
-rw-r--r--libarchive/test/main.c25
-rw-r--r--libarchive/test/test.h3
-rw-r--r--libarchive/test/test_archive_string_conversion.c4
-rw-r--r--libarchive/test/test_fuzz.c10
-rw-r--r--libarchive/test/test_read_format_rar.c2
-rw-r--r--libarchive/test/test_read_format_zip_high_compression.c143
-rw-r--r--libarchive/test/test_read_format_zip_high_compression.zip.uu18
-rw-r--r--libarchive/test/test_write_disk_secure744.c95
-rw-r--r--libarchive/test/test_write_disk_secure745.c76
-rw-r--r--libarchive/test/test_write_disk_secure746.c125
-rw-r--r--tar/cmdline.c1
-rw-r--r--tar/test/test_version.c2
34 files changed, 576 insertions, 53 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 66a054a1e0d8..f5917fbae06d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1274,6 +1274,10 @@ CHECK_FUNCTION_EXISTS(wmemmove HAVE_WMEMMOVE)
CMAKE_POP_CHECK_STATE() # Restore the state of the variables
+CHECK_C_SOURCE_COMPILES(
+ "#include <sys/types.h>\n#include <sys/mount.h>\nint main(void) { struct vfsconf v; return sizeof(v);}"
+ HAVE_STRUCT_VFSCONF)
+
# Make sure we have the POSIX version of readdir_r, not the
# older 2-argument version.
CHECK_C_SOURCE_COMPILES(
@@ -1299,6 +1303,10 @@ CHECK_C_SOURCE_COMPILES(
"#include <sys/sysmacros.h>\nint main() { return major(256); }"
MAJOR_IN_SYSMACROS)
+CHECK_C_SOURCE_COMPILES(
+ "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}"
+ HAVE_LZMA_STREAM_ENCODER_MT)
+
IF(HAVE_STRERROR_R)
SET(HAVE_DECL_STRERROR_R 1)
ENDIF(HAVE_STRERROR_R)
diff --git a/Makefile.am b/Makefile.am
index d26eac1bbb83..e161c5d3be4c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -475,6 +475,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_zip_encryption_partially.c \
libarchive/test/test_read_format_zip_encryption_header.c \
libarchive/test/test_read_format_zip_filename.c \
+ libarchive/test/test_read_format_zip_high_compression.c \
libarchive/test/test_read_format_zip_mac_metadata.c \
libarchive/test/test_read_format_zip_malformed.c \
libarchive/test/test_read_format_zip_msdos.c \
@@ -509,6 +510,9 @@ libarchive_test_SOURCES= \
libarchive/test/test_write_disk_no_hfs_compression.c \
libarchive/test/test_write_disk_perms.c \
libarchive/test/test_write_disk_secure.c \
+ libarchive/test/test_write_disk_secure744.c \
+ libarchive/test/test_write_disk_secure745.c \
+ libarchive/test/test_write_disk_secure746.c \
libarchive/test/test_write_disk_sparse.c \
libarchive/test/test_write_disk_symlink.c \
libarchive/test/test_write_disk_times.c \
diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in
index 64f4d4df1789..e6a9c517573e 100644
--- a/build/cmake/config.h.in
+++ b/build/cmake/config.h.in
@@ -721,6 +721,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the <lzma.h> header file. */
#cmakedefine HAVE_LZMA_H 1
+/* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */
+#cmakedefine HAVE_LZMA_STREAM_ENCODER_MT 1
+
/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
#cmakedefine HAVE_LZO_LZO1X_H 1
@@ -923,6 +926,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
#cmakedefine HAVE_STRUCT_TM___TM_GMTOFF 1
+/* Define to 1 if you have `struct vfsconf'. */
+#cmakedefine HAVE_STRUCT_VFSCONF 1
+
/* Define to 1 if you have the `symlink' function. */
#cmakedefine HAVE_SYMLINK 1
diff --git a/cat/test/test_version.c b/cat/test/test_version.c
index e7c93630b499..e587b3440c6d 100644
--- a/cat/test/test_version.c
+++ b/cat/test/test_version.c
@@ -83,7 +83,7 @@ DEFINE_TEST(test_version)
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
/* Skip arbitrary third-party version numbers. */
- while (s > 0 && (*q == ' ' || *q == '/' || *q == '.' || isalnum(*q))) {
+ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
++q;
--s;
}
diff --git a/cpio/cmdline.c b/cpio/cmdline.c
index 7e59536957a4..0c10b2cdcf8c 100644
--- a/cpio/cmdline.c
+++ b/cpio/cmdline.c
@@ -63,6 +63,7 @@ static const struct option {
} cpio_longopts[] = {
{ "b64encode", 0, OPTION_B64ENCODE },
{ "create", 0, 'o' },
+ { "dereference", 0, 'L' },
{ "dot", 0, 'V' },
{ "extract", 0, 'i' },
{ "file", 1, 'F' },
diff --git a/cpio/test/test_option_version.c b/cpio/test/test_option_version.c
index 32ba3000477e..ac58cefda11f 100644
--- a/cpio/test/test_option_version.c
+++ b/cpio/test/test_option_version.c
@@ -75,7 +75,7 @@ verify(const char *p, size_t s)
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
/* Skip arbitrary third-party version numbers. */
- while (s > 0 && (*q == ' ' || *q == '/' || *q == '.' || isalnum(*q))) {
+ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
++q;
--s;
}
diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c
index 4c41badf1f86..0719cbd54736 100644
--- a/libarchive/archive_match.c
+++ b/libarchive/archive_match.c
@@ -655,7 +655,7 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
}
}
- /* If something error happend, report it immediately. */
+ /* If an error occurred, report it immediately. */
if (r < ARCHIVE_OK) {
archive_copy_error(&(a->archive), ar);
archive_read_free(ar);
diff --git a/libarchive/archive_ppmd7_private.h b/libarchive/archive_ppmd7_private.h
index 3a6b9eb4190f..06c99e828ab6 100644
--- a/libarchive/archive_ppmd7_private.h
+++ b/libarchive/archive_ppmd7_private.h
@@ -19,7 +19,7 @@ If you need the compatibility with original PPMd var.H, you can use external Ran
#define PPMD7_MAX_ORDER 64
#define PPMD7_MIN_MEM_SIZE (1 << 11)
-#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3)
struct CPpmd7_Context_;
diff --git a/libarchive/archive_read_add_passphrase.c b/libarchive/archive_read_add_passphrase.c
index f67f1ebc6e27..cf821b5d483c 100644
--- a/libarchive/archive_read_add_passphrase.c
+++ b/libarchive/archive_read_add_passphrase.c
@@ -125,7 +125,7 @@ void
__archive_read_reset_passphrase(struct archive_read *a)
{
- a->passphrases.candiate = -1;
+ a->passphrases.candidate = -1;
}
/*
@@ -137,31 +137,31 @@ __archive_read_next_passphrase(struct archive_read *a)
struct archive_read_passphrase *p;
const char *passphrase;
- if (a->passphrases.candiate < 0) {
+ if (a->passphrases.candidate < 0) {
/* Count out how many passphrases we have. */
int cnt = 0;
for (p = a->passphrases.first; p != NULL; p = p->next)
cnt++;
- a->passphrases.candiate = cnt;
+ a->passphrases.candidate = cnt;
p = a->passphrases.first;
- } else if (a->passphrases.candiate > 1) {
+ } else if (a->passphrases.candidate > 1) {
/* Rotate a passphrase list. */
- a->passphrases.candiate--;
+ a->passphrases.candidate--;
p = remove_passphrases_from_head(a);
add_passphrase_to_tail(a, p);
- /* Pick a new passphrase candiate up. */
+ /* Pick a new passphrase candidate up. */
p = a->passphrases.first;
- } else if (a->passphrases.candiate == 1) {
- /* This case is that all cadiates failed to decryption. */
- a->passphrases.candiate = 0;
+ } else if (a->passphrases.candidate == 1) {
+ /* This case is that all candidates failed to decrypt. */
+ a->passphrases.candidate = 0;
if (a->passphrases.first->next != NULL) {
/* Rotate a passphrase list. */
p = remove_passphrases_from_head(a);
add_passphrase_to_tail(a, p);
}
p = NULL;
- } else /* There is no passphrase candaite. */
+ } else /* There is no passphrase candidate. */
p = NULL;
if (p != NULL)
@@ -177,7 +177,7 @@ __archive_read_next_passphrase(struct archive_read *a)
if (p == NULL)
return (NULL);
insert_passphrase_to_head(a, p);
- a->passphrases.candiate = 1;
+ a->passphrases.candidate = 1;
}
} else
passphrase = NULL;
diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index 74fe353d9d34..18963ce11bf5 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -642,13 +642,16 @@ translate_acl(struct archive_read_disk *a,
* Libarchive stores "flag" (NFSv4 inheritance bits)
* in the ae_perm bitmap.
*/
- acl_get_flagset_np(acl_entry, &acl_flagset);
- for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
- if (acl_get_flag_np(acl_flagset,
- acl_inherit_map[i].platform_inherit))
- ae_perm |= acl_inherit_map[i].archive_inherit;
-
- }
+ // XXX acl_get_flagset_np on FreeBSD returns EINVAL for
+ // non-NFSv4 ACLs
+ r = acl_get_flagset_np(acl_entry, &acl_flagset);
+ if (r == 0) {
+ for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+ if (acl_get_flag_np(acl_flagset,
+ acl_inherit_map[i].platform_inherit))
+ ae_perm |= acl_inherit_map[i].archive_inherit;
+ }
+ }
#endif
acl_get_permset(acl_entry, &acl_permset);
diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c
index f54cda69c125..5901a879c497 100644
--- a/libarchive/archive_read_disk_posix.c
+++ b/libarchive/archive_read_disk_posix.c
@@ -938,7 +938,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_path_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -1041,7 +1041,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_time_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -1067,7 +1067,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_owner_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c
index 566d264e9a41..1fd158ffd5f4 100644
--- a/libarchive/archive_read_disk_windows.c
+++ b/libarchive/archive_read_disk_windows.c
@@ -803,7 +803,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_path_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -875,7 +875,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_time_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -901,7 +901,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_owner_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Faild : %s", archive_error_string(a->matching));
+ "Failed : %s", archive_error_string(a->matching));
return (r);
}
if (r) {
diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h
index 9b61a5380a2e..8eb5435bdc80 100644
--- a/libarchive/archive_read_private.h
+++ b/libarchive/archive_read_private.h
@@ -221,7 +221,7 @@ struct archive_read {
struct {
struct archive_read_passphrase *first;
struct archive_read_passphrase **last;
- int candiate;
+ int candidate;
archive_passphrase_callback *callback;
void *client_data;
} passphrases;
diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c
index e877917b940b..37b2f59004b1 100644
--- a/libarchive/archive_read_support_filter_lz4.c
+++ b/libarchive/archive_read_support_filter_lz4.c
@@ -595,7 +595,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
#endif
}
- /* Check if an error happend in decompression process. */
+ /* Check if an error occurred in the decompression process. */
if (uncompressed_size < 0) {
archive_set_error(&(self->archive->archive),
ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c
index dbfc1cd87c86..a7f1d8d949f5 100644
--- a/libarchive/archive_read_support_format_lha.c
+++ b/libarchive/archive_read_support_format_lha.c
@@ -1715,8 +1715,11 @@ lha_crc16(uint16_t crc, const void *pp, size_t len)
#undef bswap16
#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */
# define bswap16(x) _byteswap_ushort(x)
-#elif (defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8) \
- || defined(__clang__)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)
+/* GCC 4.8 and later has __builtin_bswap16() */
+# define bswap16(x) __builtin_bswap16(x)
+#elif defined(__clang__)
+/* All clang versions have __builtin_bswap16() */
# define bswap16(x) __builtin_bswap16(x)
#else
# define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c
index 9d8013287568..deeaa9e6d93d 100644
--- a/libarchive/archive_read_support_format_warc.c
+++ b/libarchive/archive_read_support_format_warc.c
@@ -318,7 +318,7 @@ start_over:
}
memcpy(w->pool.str, fnam.str, fnam.len);
w->pool.str[fnam.len] = '\0';
- /* let noone else know about the pool, it's a secret, shhh */
+ /* let no one else know about the pool, it's a secret, shhh */
fnam.str = w->pool.str;
/* snarf mtime or deduce from rtime
diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c
index cc3d1c460728..6b3bd61163d9 100644
--- a/libarchive/archive_util.c
+++ b/libarchive/archive_util.c
@@ -580,7 +580,7 @@ void
__archive_ensure_cloexec_flag(int fd)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
- (void)fd; /* UNSED */
+ (void)fd; /* UNUSED */
#else
int flags;
diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c
index 5cbba54f08fd..54a96696f285 100644
--- a/libarchive/archive_write_disk_acl.c
+++ b/libarchive/archive_write_disk_acl.c
@@ -145,7 +145,7 @@ set_acl(struct archive *a, int fd, const char *name,
gid_t ae_gid;
const char *ae_name;
int entries;
- int i;
+ int i, r;
ret = ARCHIVE_OK;
entries = archive_acl_reset(abstract_acl, ae_requested_type);
@@ -223,12 +223,16 @@ set_acl(struct archive *a, int fd, const char *name,
}
#ifdef ACL_TYPE_NFS4
- acl_get_flagset_np(acl_entry, &acl_flagset);
- acl_clear_flags_np(acl_flagset);
- for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
- if (ae_permset & acl_inherit_map[i].archive_inherit)
- acl_add_flag_np(acl_flagset,
- acl_inherit_map[i].platform_inherit);
+ // XXX acl_get_flagset_np on FreeBSD returns EINVAL for
+ // non-NFSv4 ACLs
+ r = acl_get_flagset_np(acl_entry, &acl_flagset);
+ if (r == 0) {
+ acl_clear_flags_np(acl_flagset);
+ for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+ if (ae_permset & acl_inherit_map[i].archive_inherit)
+ acl_add_flag_np(acl_flagset,
+ acl_inherit_map[i].platform_inherit);
+ }
}
#endif
}
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index 67aacf15a004..39ee3b67a4a8 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -1796,7 +1796,7 @@ edit_deep_directories(struct archive_write_disk *a)
char *tail = a->name;
/* If path is short, avoid the open() below. */
- if (strlen(tail) <= PATH_MAX)
+ if (strlen(tail) < PATH_MAX)
return;
/* Try to record our starting dir. */
@@ -1806,7 +1806,7 @@ edit_deep_directories(struct archive_write_disk *a)
return;
/* As long as the path is too long... */
- while (strlen(tail) > PATH_MAX) {
+ while (strlen(tail) >= PATH_MAX) {
/* Locate a dir prefix shorter than PATH_MAX. */
tail += PATH_MAX - 8;
while (tail > a->name && *tail != '/')
diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c
index cb3e54e8d21b..c20e0885236e 100644
--- a/libarchive/archive_write_set_format_iso9660.c
+++ b/libarchive/archive_write_set_format_iso9660.c
@@ -436,7 +436,7 @@ struct iso_option {
* Type : string
* Default: Auto detect
* : We check a size of boot image;
- * : If ths size is just 1.22M/1.44M/2.88M,
+ * : If the size is just 1.22M/1.44M/2.88M,
* : we assume boot_type is 'fd';
* : otherwise boot_type is 'no-emulation'.
* COMPAT :
diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c
index 484ab34b2305..797b5334bd47 100644
--- a/libarchive/archive_write_set_format_ustar.c
+++ b/libarchive/archive_write_set_format_ustar.c
@@ -307,7 +307,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
* case getting WCS failed. On POSIX, this is a
* normal operation.
*/
- if (p != NULL && p[strlen(p) - 1] != '/') {
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
struct archive_string as;
archive_string_init(&as);
diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
index 124aa3a8b1ba..345e42af2fc7 100644
--- a/libarchive/test/CMakeLists.txt
+++ b/libarchive/test/CMakeLists.txt
@@ -6,7 +6,7 @@
IF(ENABLE_TEST)
SET(libarchive_test_SOURCES
../../test_utils/test_utils.c
- main.c
+ main.c
read_open_memory.c
test.h
test_acl_freebsd_nfs4.c
@@ -164,6 +164,7 @@ IF(ENABLE_TEST)
test_read_format_zip_encryption_header.c
test_read_format_zip_encryption_partially.c
test_read_format_zip_filename.c
+ test_read_format_zip_high_compression.c
test_read_format_zip_mac_metadata.c
test_read_format_zip_malformed.c
test_read_format_zip_msdos.c
@@ -198,6 +199,9 @@ IF(ENABLE_TEST)
test_write_disk_no_hfs_compression.c
test_write_disk_perms.c
test_write_disk_secure.c
+ test_write_disk_secure744.c
+ test_write_disk_secure745.c
+ test_write_disk_secure746.c
test_write_disk_sparse.c
test_write_disk_symlink.c
test_write_disk_times.c
diff --git a/libarchive/test/main.c b/libarchive/test/main.c
index 7c266857be90..9fa26fbf8640 100644
--- a/libarchive/test/main.c
+++ b/libarchive/test/main.c
@@ -1440,6 +1440,31 @@ assertion_file_size(const char *file, int line, const char *pathname, long size)
return (0);
}
+/* Verify mode of 'pathname'. */
+int
+assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode)
+{
+ int mode;
+ int r;
+
+ assertion_count(file, line);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ failure_start(file, line, "assertFileMode not yet implemented for Windows");
+#else
+ {
+ struct stat st;
+ r = lstat(pathname, &st);
+ mode = (int)(st.st_mode & 0777);
+ }
+ if (r == 0 && mode == expected_mode)
+ return (1);
+ failure_start(file, line, "File %s has mode %o, expected %o",
+ pathname, mode, expected_mode);
+#endif
+ failure_finish(NULL);
+ return (0);
+}
+
/* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */
int
assertion_is_dir(const char *file, int line, const char *pathname, int mode)
diff --git a/libarchive/test/test.h b/libarchive/test/test.h
index 1117d6a77760..2fe09ff169b7 100644
--- a/libarchive/test/test.h
+++ b/libarchive/test/test.h
@@ -182,6 +182,8 @@
assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks)
#define assertFileSize(pathname, size) \
assertion_file_size(__FILE__, __LINE__, pathname, size)
+#define assertFileMode(pathname, mode) \
+ assertion_file_mode(__FILE__, __LINE__, pathname, mode)
#define assertTextFileContents(text, pathname) \
assertion_text_file_contents(__FILE__, __LINE__, text, pathname)
#define assertFileContainsLinesAnyOrder(pathname, lines) \
@@ -246,6 +248,7 @@ int assertion_file_mtime_recent(const char *, int, const char *);
int assertion_file_nlinks(const char *, int, const char *, int);
int assertion_file_not_exists(const char *, int, const char *);
int assertion_file_size(const char *, int, const char *, long);
+int assertion_file_mode(const char *, int, const char *, int);
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 *);
diff --git a/libarchive/test/test_archive_string_conversion.c b/libarchive/test/test_archive_string_conversion.c
index fea141d4ab0d..e86f97c8a492 100644
--- a/libarchive/test/test_archive_string_conversion.c
+++ b/libarchive/test/test_archive_string_conversion.c
@@ -800,8 +800,8 @@ DEFINE_TEST(test_archive_string_conversion)
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
assert((fp = fopen(testdata, "w")) != NULL);
while ((size = archive_read_data(a, buff, 512)) > 0)
- fwrite(buff, 1, size, fp);
- fclose(fp);
+ assertEqualInt(size, fwrite(buff, 1, size, fp));
+ assertEqualInt(0, fclose(fp));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
test_archive_string_normalization_nfc(testdata);
diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c
index 76fda6282fa6..602b894661cc 100644
--- a/libarchive/test/test_fuzz.c
+++ b/libarchive/test/test_fuzz.c
@@ -110,13 +110,17 @@ test_fuzz(const struct files *filesets)
for (i = 0; filesets[n].names[i] != NULL; ++i)
{
tmp = slurpfile(&size, filesets[n].names[i]);
- rawimage = (char *)realloc(rawimage, oldsize + size);
+ char *newraw = (char *)realloc(rawimage, oldsize + size);
+ if (!assert(newraw != NULL))
+ {
+ free(rawimage);
+ continue;
+ }
+ rawimage = newraw;
memcpy(rawimage + oldsize, tmp, size);
oldsize += size;
size = oldsize;
free(tmp);
- if (!assert(rawimage != NULL))
- continue;
}
}
if (size == 0)
diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c
index 587032309c47..6392d8f50ff8 100644
--- a/libarchive/test/test_read_format_rar.c
+++ b/libarchive/test/test_read_format_rar.c
@@ -3603,7 +3603,7 @@ DEFINE_TEST(test_read_format_rar_multivolume_uncompressed_files)
assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff)));
/*
- * Eigth header.
+ * Eighth header.
*/
assertA(0 == archive_read_next_header(a, &ae));
assertEqualString("testdir/testsymlink6", archive_entry_pathname(ae));
diff --git a/libarchive/test/test_read_format_zip_high_compression.c b/libarchive/test/test_read_format_zip_high_compression.c
new file mode 100644
index 000000000000..6c8aa8eee1b2
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_high_compression.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 2016 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"
+__FBSDID("$FreeBSD");
+
+#include <locale.h>
+
+
+/*
+ * Github Issue 748 reported problems with end-of-entry handling
+ * with highly-compressible data. This resulted in the end of the
+ * data being truncated (extracted as zero bytes).
+ */
+
+/*
+ * Extract the specific test archive that was used to diagnose
+ * Issue 748:
+ */
+DEFINE_TEST(test_read_format_zip_high_compression)
+{
+ const char *refname = "test_read_format_zip_high_compression.zip";
+ char *p;
+ size_t archive_size;
+ struct archive *a;
+ struct archive_entry *entry;
+
+ const void *pv;
+ size_t s;
+ int64_t o;
+
+ extract_reference_file(refname);
+ p = slurpfile(&archive_size, refname);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
+ assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, archive_size, 16 * 1024));
+ assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_data_block(a, &pv, &s, &o));
+ assertEqualInt(262144, s);
+ assertEqualInt(0, o);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_data_block(a, &pv, &s, &o));
+ assertEqualInt(160, s);
+ assertEqualInt(262144, o);
+
+ assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &pv, &s, &o));
+
+ assertEqualInt(ARCHIVE_OK, archive_free(a));
+ free(p);
+}
+
+/*
+ * Synthesize a lot of varying inputs that are highly compressible.
+ */
+DEFINE_TEST(test_read_format_zip_high_compression2)
+{
+ const size_t body_size = 1024 * 1024;
+ const size_t buff_size = 2 * 1024 * 1024;
+ char *body, *body_read, *buff;
+ int n;
+
+ assert((body = malloc(body_size)) != NULL);
+ assert((body_read = malloc(body_size)) != NULL);
+ assert((buff = malloc(buff_size)) != NULL);
+
+ /* Highly-compressible data: all bytes 255, except for a
+ * single 1 byte.
+ * The body is always 256k + 6 bytes long (the internal deflation
+ * buffer is exactly 256k).
+ */
+
+ for(n = 1024; n < (int)body_size; n += 1024) {
+ struct archive *a;
+ struct archive_entry *entry;
+ size_t used = 0;
+ const void *pv;
+ size_t s;
+ int64_t o;
+
+ memset(body, 255, body_size);
+ body[n] = 1;
+
+ /* Write an archive with a single entry of n bytes. */
+ assert((a = archive_write_new()) != NULL);
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_zip(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_open_memory(a, buff, buff_size, &used));
+
+ entry = archive_entry_new2(a);
+ archive_entry_set_pathname(entry, "test");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 262150);
+ assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+ archive_entry_free(entry);
+ assertEqualInt(262150, archive_write_data(a, body, 262150));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ /* Read back the entry and verify the contents. */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, buff, used, 17));
+ assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_data_block(a, &pv, &s, &o));
+ assertEqualInt(262144, s);
+ assertEqualInt(0, o);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_data_block(a, &pv, &s, &o));
+ assertEqualInt(6, s);
+ assertEqualInt(262144, o);
+
+ assertEqualInt(ARCHIVE_EOF, archive_read_data_block(a, &pv, &s, &o));
+
+ assertEqualInt(ARCHIVE_OK, archive_free(a));
+ }
+
+ free(body);
+ free(body_read);
+ free(buff);
+}
diff --git a/libarchive/test/test_read_format_zip_high_compression.zip.uu b/libarchive/test/test_read_format_zip_high_compression.zip.uu
new file mode 100644
index 000000000000..094288033d97
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_high_compression.zip.uu
@@ -0,0 +1,18 @@
+begin 644 test_read_format_zip_high_compression.zip
+M4$L#!!0`"``(`*=Y]4@``````````*``!``(`"``8VAA<BYB:6Y55`T`!\L>
+MD5>>))%7GB215W5X"P`!!/8!```$%````.W=06K#,!`%T&E)P8LL?*2XC@N%
+M)#5QO>AM<K0<+2=0:!OBP>@9WDJ6!%\6$K/Q6T3LAX]N/GQ'Z9G&KA^*K1'S
+M.`[GOIM*[TP_Q_>O0[G_:3X.Y\^^V/X2<<GT&IM=$]OK?[71_LJ],3;1+*(T
+M_U)99\T+````````````````````````````````````````L`:E?]*S#FVT
+MJY:='SPB>_]DR\X?`("_R:X_U"Y[_:F;;Q``ZN+L!P"H3W;]$_5G`&!9V?</
+M````X/FRZP_9LO,'@`S9YR^0P_Z'NF7?/P``````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+MJ%L3VVM-LO,&8/UN4$L'"!<='%^U`0``H``$`%!+`0(4`Q0`"``(`*=Y]4@7
+M'1Q?M0$``*``!``(`"````````````"D@0````!C:&%R+F)I;E54#0`'RQZ1
+M5YXDD5>>))%7=7@+``$$]@$```04````4$L%!@`````!``$`5@````L"````
+!````
+`
+end
diff --git a/libarchive/test/test_write_disk_secure744.c b/libarchive/test/test_write_disk_secure744.c
new file mode 100644
index 000000000000..08c725e12b80
--- /dev/null
+++ b/libarchive/test/test_write_disk_secure744.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2003-2007,2016 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"
+__FBSDID("$FreeBSD$");
+
+#define UMASK 022
+
+/*
+ * Github Issue #744 describes a bug in the sandboxing code that
+ * causes very long pathnames to not get checked for symlinks.
+ */
+
+DEFINE_TEST(test_write_disk_secure744)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ skipping("archive_write_disk security checks not supported on Windows");
+#else
+ struct archive *a;
+ struct archive_entry *ae;
+ size_t buff_size = 8192;
+ char *buff = malloc(buff_size);
+ char *p = buff;
+ int n = 0;
+ int t;
+
+ assert(buff != NULL);
+
+ /* Start with a known umask. */
+ assertUmask(UMASK);
+
+ /* Create an archive_write_disk object. */
+ assert((a = archive_write_disk_new()) != NULL);
+ archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
+
+ while (p + 500 < buff + buff_size) {
+ memset(p, 'x', 100);
+ p += 100;
+ p[0] = '\0';
+
+ buff[0] = ((n / 1000) % 10) + '0';
+ buff[1] = ((n / 100) % 10)+ '0';
+ buff[2] = ((n / 10) % 10)+ '0';
+ buff[3] = ((n / 1) % 10)+ '0';
+ buff[4] = '_';
+ ++n;
+
+ /* Create a symlink pointing to the testworkdir */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, buff);
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ archive_entry_copy_symlink(ae, testworkdir);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ *p++ = '/';
+ sprintf(p, "target%d", n);
+
+ /* Try to create a file through the symlink, should fail. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, buff);
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+
+ t = archive_write_header(a, ae);
+ archive_entry_free(ae);
+ failure("Attempt to create target%d via %d-character symlink should have failed", n, (int)strlen(buff));
+ if(!assertEqualInt(ARCHIVE_FAILED, t)) {
+ break;
+ }
+ }
+ archive_free(a);
+ free(buff);
+#endif
+}
diff --git a/libarchive/test/test_write_disk_secure745.c b/libarchive/test/test_write_disk_secure745.c
new file mode 100644
index 000000000000..fa6939b8cedc
--- /dev/null
+++ b/libarchive/test/test_write_disk_secure745.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2003-2007,2016 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"
+__FBSDID("$FreeBSD$");
+
+#define UMASK 022
+
+/*
+ * Github Issue #745 describes a bug in the sandboxing code that
+ * allows one to use a symlink to edit the permissions on a file or
+ * directory outside of the sandbox.
+ */
+
+DEFINE_TEST(test_write_disk_secure745)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ skipping("archive_write_disk security checks not supported on Windows");
+#else
+ struct archive *a;
+ struct archive_entry *ae;
+
+ /* Start with a known umask. */
+ assertUmask(UMASK);
+
+ /* Create an archive_write_disk object. */
+ assert((a = archive_write_disk_new()) != NULL);
+ archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
+
+ /* The target dir: The one we're going to try to change permission on */
+ assertMakeDir("target", 0700);
+
+ /* The sandbox dir we're going to run inside of. */
+ assertMakeDir("sandbox", 0700);
+ assertChdir("sandbox");
+
+ /* Create a symlink pointing to the target directory */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "sym");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ archive_entry_copy_symlink(ae, "../target");
+ assert(0 == archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Try to alter the target dir through the symlink; this should fail. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "sym");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Permission of target dir should not have changed. */
+ assertFileMode("../target", 0700);
+#endif
+}
diff --git a/libarchive/test/test_write_disk_secure746.c b/libarchive/test/test_write_disk_secure746.c
new file mode 100644
index 000000000000..0daf1b09aaa8
--- /dev/null
+++ b/libarchive/test/test_write_disk_secure746.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2003-2007,2016 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"
+__FBSDID("$FreeBSD$");
+
+#define UMASK 022
+
+/*
+ * Github Issue #746 describes a problem in which hardlink targets are
+ * not adequately checked and can be used to modify entries outside of
+ * the sandbox.
+ */
+
+/*
+ * Verify that ARCHIVE_EXTRACT_SECURE_NODOTDOT disallows '..' in hardlink
+ * targets.
+ */
+DEFINE_TEST(test_write_disk_secure746a)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ skipping("archive_write_disk security checks not supported on Windows");
+#else
+ struct archive *a;
+ struct archive_entry *ae;
+
+ /* Start with a known umask. */
+ assertUmask(UMASK);
+
+ /* The target directory we're going to try to affect. */
+ assertMakeDir("target", 0700);
+ assertMakeFile("target/foo", 0700, "unmodified");
+
+ /* The sandbox dir we're going to work within. */
+ assertMakeDir("sandbox", 0700);
+ assertChdir("sandbox");
+
+ /* Create an archive_write_disk object. */
+ assert((a = archive_write_disk_new()) != NULL);
+ archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NODOTDOT);
+
+ /* Attempt to hardlink to the target directory. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "bar");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ archive_entry_set_size(ae, 8);
+ archive_entry_copy_hardlink(ae, "../target/foo");
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ assertEqualInt(ARCHIVE_FAILED, archive_write_data(a, "modified", 8));
+ archive_entry_free(ae);
+
+ /* Verify that target file contents are unchanged. */
+ assertTextFileContents("unmodified", "../target/foo");
+#endif
+}
+
+/*
+ * Verify that ARCHIVE_EXTRACT_SECURE_NOSYMLINK disallows symlinks in hardlink
+ * targets.
+ */
+DEFINE_TEST(test_write_disk_secure746b)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ skipping("archive_write_disk security checks not supported on Windows");
+#else
+ struct archive *a;
+ struct archive_entry *ae;
+
+ /* Start with a known umask. */
+ assertUmask(UMASK);
+
+ /* The target directory we're going to try to affect. */
+ assertMakeDir("target", 0700);
+ assertMakeFile("target/foo", 0700, "unmodified");
+
+ /* The sandbox dir we're going to work within. */
+ assertMakeDir("sandbox", 0700);
+ assertChdir("sandbox");
+
+ /* Create an archive_write_disk object. */
+ assert((a = archive_write_disk_new()) != NULL);
+ archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
+
+ /* Create a symlink to the target directory. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "symlink");
+ archive_entry_copy_symlink(ae, "../target");
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Attempt to hardlink to the target directory via the symlink. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "bar");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ archive_entry_set_size(ae, 8);
+ archive_entry_copy_hardlink(ae, "symlink/foo");
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ assertEqualInt(ARCHIVE_FAILED, archive_write_data(a, "modified", 8));
+ archive_entry_free(ae);
+
+ /* Verify that target file contents are unchanged. */
+ assertTextFileContents("unmodified", "../target/foo");
+#endif
+}
diff --git a/tar/cmdline.c b/tar/cmdline.c
index fd0712a0dd6c..c87741cc565e 100644
--- a/tar/cmdline.c
+++ b/tar/cmdline.c
@@ -68,6 +68,7 @@ static const struct bsdtar_option {
{ "auto-compress", 0, 'a' },
{ "b64encode", 0, OPTION_B64ENCODE },
{ "block-size", 1, 'b' },
+ { "blocking-factor", 1, 'b' },
{ "bunzip2", 0, 'j' },
{ "bzip", 0, 'j' },
{ "bzip2", 0, 'j' },
diff --git a/tar/test/test_version.c b/tar/test/test_version.c
index 5474261d207a..04d9e6c7ca92 100644
--- a/tar/test/test_version.c
+++ b/tar/test/test_version.c
@@ -88,7 +88,7 @@ DEFINE_TEST(test_version)
if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
++q;
/* Skip arbitrary third-party version numbers. */
- while (s > 0 && (*q == ' ' || *q == '/' || *q == '.' || isalnum(*q))) {
+ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
++q;
--s;
}