diff --git a/.travis.yml b/.travis.yml index 3a7aae334b61..34665e6ed86d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,6 @@ env: - BUILD_SYSTEM=cmake - BUILD_SYSTEM=autotools install: - - sudo apt-get install -y libbz2-dev libzip-dev liblzma-dev + - sudo apt-get install -y libbz2-dev libzip-dev liblzma-dev liblzo2-dev script: - build/ci_build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index a376fce5dc70..fa651eb23381 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -452,34 +452,28 @@ MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES) # Find LZMA # IF(ENABLE_LZMA) - FIND_PACKAGE(LZMA) + FIND_PACKAGE(LibLZMA) ELSE() - SET(LZMA_FOUND FALSE) # Override cached value - SET(LZMADEC_FOUND FALSE) # Override cached value + SET(LIBZMA_FOUND FALSE) # Override cached value ENDIF() -IF(LZMA_FOUND) +IF(LIBLZMA_FOUND) SET(HAVE_LIBLZMA 1) SET(HAVE_LZMA_H 1) - INCLUDE_DIRECTORIES(${LZMA_INCLUDE_DIR}) - LIST(APPEND ADDITIONAL_LIBS ${LZMA_LIBRARIES}) + INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) + LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES}) # Test if a macro is needed for the library. TRY_MACRO_FOR_LIBRARY( - "${LZMA_INCLUDE_DIR}" "${LZMA_LIBRARIES}" + "${LIBLZMA_INCLUDE_DIRS}" "${LIBLZMA_LIBRARIES}" COMPILES "#include \nint main() {return (int)lzma_version_number(); }" "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC") IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) ADD_DEFINITIONS(-DLZMA_API_STATIC) ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) -ELSEIF(LZMADEC_FOUND) - SET(HAVE_LIBLZMADEC 1) - SET(HAVE_LZMADEC_H 1) - INCLUDE_DIRECTORIES(${LZMADEC_INCLUDE_DIR}) - LIST(APPEND ADDITIONAL_LIBS ${LZMADEC_LIBRARIES}) -ELSE(LZMA_FOUND) +ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. -ENDIF(LZMA_FOUND) +ENDIF(LIBLZMA_FOUND) # # Find LZO2 # diff --git a/Makefile.am b/Makefile.am index 5fe2d4947e64..441bdbb9e17b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,7 +23,7 @@ TESTS_ENVIRONMENT= $(libarchive_TESTS_ENVIRONMENT) $(bsdtar_TESTS_ENVIRONMENT) $ DISTCHECK_CONFIGURE_FLAGS = --enable-bsdtar --enable-bsdcpio # The next line is commented out by default in shipping libarchive releases. # It is uncommented by default in trunk. -# DEV_CFLAGS=-Werror -Wextra -Wunused -Wshadow -Wmissing-prototypes -Wcast-qual -g +DEV_CFLAGS=-Werror -Wextra -Wunused -Wshadow -Wmissing-prototypes -Wcast-qual -g AM_CFLAGS=$(DEV_CFLAGS) PLATFORMCPPFLAGS = @PLATFORMCPPFLAGS@ AM_CPPFLAGS=$(PLATFORMCPPFLAGS) @@ -33,6 +33,7 @@ AM_CPPFLAGS=$(PLATFORMCPPFLAGS) # EXTRA_DIST= \ CMakeLists.txt \ + README.md \ build/autogen.sh \ build/bump-version.sh \ build/clean.sh \ @@ -371,6 +372,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_compat_pax_libarchive_2x.c \ libarchive/test/test_compat_solaris_tar_acl.c \ libarchive/test/test_compat_solaris_pax_sparse.c \ + libarchive/test/test_compat_star_acl_posix1e.c \ libarchive/test/test_compat_tar_hardlink.c \ libarchive/test/test_compat_uudecode.c \ libarchive/test/test_compat_uudecode_large.c \ @@ -625,6 +627,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \ libarchive/test/test_compat_solaris_pax_sparse_2.pax.Z.uu \ libarchive/test/test_compat_solaris_tar_acl.tar.uu \ + libarchive/test/test_compat_star_acl_posix1e.tar.uu \ libarchive/test/test_compat_tar_hardlink_1.tar.uu \ libarchive/test/test_compat_uudecode_large.tar.Z.uu \ libarchive/test/test_compat_xz_1.txz.uu \ diff --git a/NEWS b/NEWS index dd4cfd59fb0c..6f16b889ac86 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +Oct 26, 2016: Remove liblzmadec support + Oct 23, 2016: libarchive 3.2.2 released Security release diff --git a/build/ci_build.sh b/build/ci_build.sh index 624e09cde31d..ff8ef2dab414 100755 --- a/build/ci_build.sh +++ b/build/ci_build.sh @@ -89,8 +89,15 @@ for action in ${ACTIONS}; do ;; test) case "${BUILD_SYSTEM}" in - autotools) make ${MAKE_ARGS} check ;; - cmake) make ${MAKE_ARGS} test ;; + autotools) + if ! make ${MAKE_ARGS} check; then + cat test-suite.log + exit 1 + fi + ;; + cmake) + make ${MAKE_ARGS} test + ;; esac RET="$?" ;; diff --git a/build/cmake/FindLZMA.cmake b/build/cmake/FindLZMA.cmake deleted file mode 100644 index 0b46b2cdd125..000000000000 --- a/build/cmake/FindLZMA.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# - Find lzma and lzmadec -# Find the native LZMA includes and library -# -# LZMA_INCLUDE_DIR - where to find lzma.h, etc. -# LZMA_LIBRARIES - List of libraries when using liblzma. -# LZMA_FOUND - True if liblzma found. -# LZMADEC_INCLUDE_DIR - where to find lzmadec.h, etc. -# LZMADEC_LIBRARIES - List of libraries when using liblzmadec. -# LZMADEC_FOUND - True if liblzmadec found. - -IF (LZMA_INCLUDE_DIR) - # Already in cache, be silent - SET(LZMA_FIND_QUIETLY TRUE) -ENDIF (LZMA_INCLUDE_DIR) - -FIND_PATH(LZMA_INCLUDE_DIR lzma.h) -FIND_LIBRARY(LZMA_LIBRARY NAMES lzma liblzma) - -# handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMA DEFAULT_MSG LZMA_LIBRARY LZMA_INCLUDE_DIR) - -IF(LZMA_FOUND) - SET( LZMA_LIBRARIES ${LZMA_LIBRARY} ) -ELSE(LZMA_FOUND) - SET( LZMA_LIBRARIES ) - - IF (LZMADEC_INCLUDE_DIR) - # Already in cache, be silent - SET(LZMADEC_FIND_QUIETLY TRUE) - ENDIF (LZMADEC_INCLUDE_DIR) - - FIND_PATH(LZMADEC_INCLUDE_DIR lzmadec.h) - FIND_LIBRARY(LZMADEC_LIBRARY NAMES lzmadec ) - - # handle the QUIETLY and REQUIRED arguments and set LZMADEC_FOUND to TRUE if - # all listed variables are TRUE - INCLUDE(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZMADEC DEFAULT_MSG LZMADEC_LIBRARY - LZMADEC_INCLUDE_DIR) - - IF(LZMADEC_FOUND) - SET( LZMADEC_LIBRARIES ${LZMADEC_LIBRARY} ) - ELSE(LZMADEC_FOUND) - SET( LZMADEC_LIBRARIES ) - ENDIF(LZMADEC_FOUND) -ENDIF(LZMA_FOUND) diff --git a/configure.ac b/configure.ac index 008b04de0b5c..86f87e07404a 100644 --- a/configure.ac +++ b/configure.ac @@ -326,14 +326,6 @@ if test "x$with_bz2lib" != "xno"; then esac fi -AC_ARG_WITH([lzmadec], - AS_HELP_STRING([--without-lzmadec], [Don't build support for lzma through lzmadec])) - -if test "x$with_lzmadec" != "xno"; then - AC_CHECK_HEADERS([lzmadec.h]) - AC_CHECK_LIB(lzmadec,lzmadec_decode) -fi - AC_ARG_WITH([iconv], AS_HELP_STRING([--without-iconv], [Don't try to link against iconv])) @@ -711,7 +703,8 @@ if test "x$enable_acl" != "xno"; then # (It's a pretty obvious oversight; otherwise, there's no way to # test for specific permissions in a permset.) Linux uses the obvious # name, FreeBSD adds _np to mark it as "non-Posix extension." - # Test for both as a double-check that we really have POSIX-style ACL support. + # Test for both as a double-check that we really have POSIX-style ACL + # support. AC_CHECK_FUNCS(acl_get_perm_np acl_get_perm acl_get_link acl_get_link_np,,, [#if HAVE_SYS_TYPES_H #include @@ -721,6 +714,16 @@ if test "x$enable_acl" != "xno"; then #endif ]) + # Check for acl_is_trivial_np on FreeBSD + AC_CHECK_FUNCS(acl_is_trivial_np,,, + [#if HAVE_SYS_TYPES_H + #include + #endif + #if HAVE_SYS_ACL_H + #include + #endif + ]) + # MacOS has an acl.h that isn't POSIX. It can be detected by # checking for ACL_USER AC_CHECK_DECL([ACL_USER], diff --git a/contrib/android/config/windows_host.h b/contrib/android/config/windows_host.h index decc87f2a361..2d899d1e75c6 100644 --- a/contrib/android/config/windows_host.h +++ b/contrib/android/config/windows_host.h @@ -427,9 +427,6 @@ /* Define to 1 if you have the `lzma' library (-llzma). */ /* #undef HAVE_LIBLZMA */ -/* Define to 1 if you have the `lzmadec' library (-llzmadec). */ -/* #undef HAVE_LIBLZMADEC */ - /* Define to 1 if you have the `lzo2' library (-llzo2). */ /* #undef HAVE_LIBLZO2 */ diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c index d128920a5d91..bda841714cb0 100644 --- a/libarchive/archive_acl.c +++ b/libarchive/archive_acl.c @@ -94,6 +94,7 @@ archive_acl_clear(struct archive_acl *acl) acl->acl_text = NULL; } acl->acl_p = NULL; + acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ } @@ -284,8 +285,11 @@ acl_new_entry(struct archive_acl *acl, aq = NULL; while (ap != NULL) { if (ap->type == type && ap->tag == tag && ap->id == id) { - ap->permset = permset; - return (ap); + if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && + tag != ARCHIVE_ENTRY_ACL_GROUP)) { + ap->permset = permset; + return (ap); + } } aq = ap; ap = ap->next; diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 4ac196608938..88ce3976d995 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -1441,6 +1441,15 @@ archive_entry_acl_add_entry_w(struct archive_entry *entry, type, permset, tag, id, name, wcslen(name)); } +/* + * Return a bitmask of ACL types in an archive entry ACL list + */ +int +archive_entry_acl_types(struct archive_entry *entry) +{ + return ((&entry->acl)->acl_types); +} + /* * Return a count of entries matching "want_type". */ diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 71b1e87e8d57..b6a5d89c5d6f 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -508,6 +508,9 @@ __LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, __LA_DECL const char *archive_entry_acl_text(struct archive_entry *, int /* flags */); +/* Return bitmask of ACL types in an archive entry */ +__LA_DECL int archive_entry_acl_types(struct archive_entry *); + /* Return a count of entries matching 'want_type' */ __LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3 index 5aff99682352..e85c4ded1627 100644 --- a/libarchive/archive_entry_acl.3 +++ b/libarchive/archive_entry_acl.3 @@ -33,7 +33,8 @@ .Nm archive_entry_acl_next , .Nm archive_entry_acl_next_w , .Nm archive_entry_acl_reset , -.Nm archive_entry_acl_text_w +.Nm archive_entry_acl_text_w , +.Nm archive_entry_acl_types .Nd functions for manipulating Access Control Lists in archive entry descriptions .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -85,6 +86,8 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_entry_acl_reset "struct archive_entry *a" "int type" .Ft const wchar_t * .Fn archive_entry_acl_text_w "struct archive_entry *a" "int flags" +.Ft int +.Fn archive_entry_acl_types "struct archive_entry *a" .\" enum? .Sh DESCRIPTION An @@ -192,6 +195,11 @@ The returned long string is valid until the next call to .Fn archive_entry_acl_add_entry_w or .Fn archive_entry_acl_text_w . +.Pp +.Fn archive_entry_acl_types +get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4 +ACL entries cannot be mixed, this function is a very efficient way to detect if +an ACL already contains POSIX.1e or NFSv4 ACL entries. .Sh RETURN VALUES .Fn archive_entry_acl_count and @@ -225,6 +233,9 @@ The returned long string is valid until the next call to .Fn archive_entry_acl_add_entry_w or .Fn archive_entry_acl_text_w . +.Pp +.Fn archive_entry_acl_types +returns a bitmask of ACL entry types or 0 if archive entry has no ACL entries. .Sh SEE ALSO .Xr archive_entry 3 .Xr libarchive 3 , diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 4c7f04873d06..a537798f8862 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -125,6 +125,10 @@ static int setup_xattrs(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_sparse(struct archive_read_disk *, struct archive_entry *, int *fd); +#if defined(HAVE_LINUX_FIEMAP_H) +static int setup_sparse_fiemap(struct archive_read_disk *, + struct archive_entry *, int *fd); +#endif int archive_read_disk_entry_from_file(struct archive *_a, @@ -1125,7 +1129,7 @@ setup_xattrs(struct archive_read_disk *a, #if defined(HAVE_LINUX_FIEMAP_H) /* - * Linux sparse interface. + * Linux FIEMAP sparse interface. * * The FIEMAP ioctl returns an "extent" for each physical allocation * on disk. We need to process those to generate a more compact list @@ -1140,7 +1144,7 @@ setup_xattrs(struct archive_read_disk *a, */ static int -setup_sparse(struct archive_read_disk *a, +setup_sparse_fiemap(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char buff[4096]; @@ -1192,7 +1196,7 @@ setup_sparse(struct archive_read_disk *a, /* When something error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */ - goto exit_setup_sparse; + goto exit_setup_sparse_fiemap; } if (fm->fm_mapped_extents == 0) { if (iters == 0) { @@ -1227,14 +1231,24 @@ setup_sparse(struct archive_read_disk *a, } else break; } -exit_setup_sparse: +exit_setup_sparse_fiemap: return (exit_sts); } -#elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE) +#if !defined(SEEK_HOLE) || !defined(SEEK_DATA) +static int +setup_sparse(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + return setup_sparse_fiemap(a, entry, fd); +} +#endif +#endif /* defined(HAVE_LINUX_FIEMAP_H) */ + +#if defined(SEEK_HOLE) && defined(SEEK_DATA) /* - * FreeBSD and Solaris sparse interface. + * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) */ static int @@ -1242,8 +1256,8 @@ setup_sparse(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int64_t size; - off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */ - off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */ + off_t initial_off; + off_t off_s, off_e; int exit_sts = ARCHIVE_OK; int check_fully_sparse = 0; @@ -1269,8 +1283,10 @@ setup_sparse(struct archive_read_disk *a, } if (*fd >= 0) { +#ifdef _PC_MIN_HOLE_SIZE if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); +#endif initial_off = lseek(*fd, 0, SEEK_CUR); if (initial_off != 0) lseek(*fd, 0, SEEK_SET); @@ -1281,8 +1297,10 @@ setup_sparse(struct archive_read_disk *a, if (path == NULL) path = archive_entry_pathname(entry); +#ifdef _PC_MIN_HOLE_SIZE if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); +#endif *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, @@ -1293,6 +1311,19 @@ setup_sparse(struct archive_read_disk *a, initial_off = 0; } +#ifndef _PC_MIN_HOLE_SIZE + /* Check if the underlying filesystem supports seek hole */ + off_s = lseek(*fd, 0, SEEK_HOLE); + if (off_s < 0) +#if defined(HAVE_LINUX_FIEMAP_H) + return setup_sparse_fiemap(a, entry, fd); +#else + goto exit_setup_sparse; +#endif + else if (off_s > 0) + lseek(*fd, 0, SEEK_SET); +#endif + off_s = 0; size = archive_entry_size(entry); while (off_s < size) { @@ -1342,7 +1373,7 @@ setup_sparse(struct archive_read_disk *a, return (exit_sts); } -#else +#elif !defined(HAVE_LINUX_FIEMAP_H) /* * Generic (stub) sparse support. diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c index 4e0a95feeb0f..023c349d81f0 100644 --- a/libarchive/archive_read_support_filter_xz.c +++ b/libarchive/archive_read_support_filter_xz.c @@ -43,8 +43,6 @@ __FBSDID("$FreeBSD$"); #endif #if HAVE_LZMA_H #include -#elif HAVE_LZMADEC_H -#include #endif #include "archive.h" @@ -82,19 +80,6 @@ static ssize_t xz_filter_read(struct archive_read_filter *, const void **); static int xz_filter_close(struct archive_read_filter *); static int xz_lzma_bidder_init(struct archive_read_filter *); -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - -struct private_data { - lzmadec_stream stream; - unsigned char *out_block; - size_t out_block_size; - int64_t total_out; - char eof; /* True = found end of compressed data. */ -}; - -/* Lzma-only filter */ -static ssize_t lzma_filter_read(struct archive_read_filter *, const void **); -static int lzma_filter_close(struct archive_read_filter *); #endif /* @@ -178,8 +163,6 @@ archive_read_support_filter_lzma(struct archive *_a) bidder->free = NULL; #if HAVE_LZMA_H && HAVE_LIBLZMA return (ARCHIVE_OK); -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzma program for lzma decompression"); @@ -763,175 +746,6 @@ xz_filter_close(struct archive_read_filter *self) #else -#if HAVE_LZMADEC_H && HAVE_LIBLZMADEC - -/* - * If we have the older liblzmadec library, then we can handle - * LZMA streams but not XZ streams. - */ - -/* - * Setup the callbacks. - */ -static int -lzma_bidder_init(struct archive_read_filter *self) -{ - static const size_t out_block_size = 64 * 1024; - void *out_block; - struct private_data *state; - ssize_t ret, avail_in; - - self->code = ARCHIVE_FILTER_LZMA; - self->name = "lzma"; - - state = (struct private_data *)calloc(sizeof(*state), 1); - out_block = (unsigned char *)malloc(out_block_size); - if (state == NULL || out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, - "Can't allocate data for lzma decompression"); - free(out_block); - free(state); - return (ARCHIVE_FATAL); - } - - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->read = lzma_filter_read; - self->skip = NULL; /* not supported */ - self->close = lzma_filter_close; - - /* Prime the lzma library with 18 bytes of input. */ - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 18, &avail_in); - if (state->stream.next_in == NULL) - return (ARCHIVE_FATAL); - state->stream.avail_in = avail_in; - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; - - /* Initialize compression library. */ - ret = lzmadec_init(&(state->stream)); - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); - if (ret == LZMADEC_OK) - return (ARCHIVE_OK); - - /* Library setup failed: Clean up. */ - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing lzma library"); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case LZMADEC_HEADER_ERROR: - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid header"); - break; - case LZMADEC_MEM_ERROR: - archive_set_error(&self->archive->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - } - - free(state->out_block); - free(state); - self->data = NULL; - return (ARCHIVE_FATAL); -} - -/* - * Return the next block of decompressed data. - */ -static ssize_t -lzma_filter_read(struct archive_read_filter *self, const void **p) -{ - struct private_data *state; - size_t decompressed; - ssize_t avail_in, ret; - - state = (struct private_data *)self->data; - - /* Empty our output buffer. */ - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; - - /* Try to fill the output buffer. */ - while (state->stream.avail_out > 0 && !state->eof) { - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 1, &avail_in); - if (state->stream.next_in == NULL && avail_in < 0) { - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "truncated lzma input"); - return (ARCHIVE_FATAL); - } - state->stream.avail_in = avail_in; - - /* Decompress as much as we can in one pass. */ - ret = lzmadec_decode(&(state->stream), avail_in == 0); - switch (ret) { - case LZMADEC_STREAM_END: /* Found end of stream. */ - state->eof = 1; - /* FALL THROUGH */ - case LZMADEC_OK: /* Decompressor made some progress. */ - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); - break; - case LZMADEC_BUF_ERROR: /* Insufficient input data? */ - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Insufficient compressed data"); - return (ARCHIVE_FATAL); - default: - /* Return an error. */ - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, - "Lzma decompression failed"); - return (ARCHIVE_FATAL); - } - } - - decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; - if (decompressed == 0) - *p = NULL; - else - *p = state->out_block; - return (decompressed); -} - -/* - * Clean up the decompressor. - */ -static int -lzma_filter_close(struct archive_read_filter *self) -{ - struct private_data *state; - int ret; - - state = (struct private_data *)self->data; - ret = ARCHIVE_OK; - switch (lzmadec_end(&(state->stream))) { - case LZMADEC_OK: - break; - default: - archive_set_error(&(self->archive->archive), - ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - self->archive->archive.compression_name); - ret = ARCHIVE_FATAL; - } - - free(state->out_block); - free(state); - return (ret); -} - -#else - /* * * If we have no suitable library on this system, we can't actually do @@ -953,9 +767,6 @@ lzma_bidder_init(struct archive_read_filter *self) return (r); } -#endif /* HAVE_LZMADEC_H */ - - static int xz_bidder_init(struct archive_read_filter *self) { @@ -984,5 +795,4 @@ lzip_bidder_init(struct archive_read_filter *self) return (r); } - #endif /* HAVE_LZMA_H */ diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 469666e8e5eb..0ee511ea1ae8 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -294,6 +294,46 @@ archive_read_format_tar_cleanup(struct archive_read *a) return (ARCHIVE_OK); } +static int +validate_number_field(const char* p_field, size_t i_size) +{ + unsigned char marker = (unsigned char)p_field[0]; + /* octal? */ + if ((marker >= '0' && marker <= '7') || marker == ' ') { + size_t i = 0; + int octal_found = 0; + for (i = 0; i < i_size; ++i) { + switch (p_field[i]) + { + case ' ': /* skip any leading spaces and trailing space*/ + if (octal_found == 0 || i == i_size - 1) { + continue; + } + break; + case '\0': /* null is allowed only at the end */ + if (i != i_size - 1) { + return 0; + } + break; + /* rest must be octal digits */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + ++octal_found; + break; + } + } + return octal_found > 0; + } + /* base 256 (i.e. binary number) */ + else if (marker == 128 || marker == 255 || marker == 0) { + /* nothing to check */ + return 1; + } + /* not a number field */ + else { + return 0; + } +} static int archive_read_format_tar_bid(struct archive_read *a, int best_bid) @@ -346,23 +386,23 @@ archive_read_format_tar_bid(struct archive_read *a, int best_bid) return (0); bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */ - /* Sanity check: Look at first byte of mode field. */ - switch (255 & (unsigned)header->mode[0]) { - case 0: case 255: - /* Base-256 value: No further verification possible! */ - break; - case ' ': /* Not recommended, but not illegal, either. */ - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - /* Octal Value. */ - /* TODO: Check format of remainder of this field. */ - break; - default: - /* Not a valid mode; bail out here. */ - return (0); + /* + * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields. + * These are usually octal numbers but GNU tar encodes "big" values as + * base256 and leading zeroes are sometimes replaced by spaces. + * Even the null terminator is sometimes omitted. Anyway, must be checked + * to avoid false positives. + */ + if (bid > 0 && + (validate_number_field(header->mode, sizeof(header->mode)) == 0 || + validate_number_field(header->uid, sizeof(header->uid)) == 0 || + validate_number_field(header->gid, sizeof(header->gid)) == 0 || + validate_number_field(header->mtime, sizeof(header->mtime)) == 0 || + validate_number_field(header->size, sizeof(header->size)) == 0 || + validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0 || + validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) { + bid = 0; } - /* TODO: Sanity test uid/gid/size/mtime/rdevmajor/rdevminor fields. */ return (bid); } diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c index ab887505ce17..47ed064bc627 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -43,8 +43,6 @@ __FBSDID("$FreeBSD$"); #endif #if HAVE_LZMA_H #include -#elif HAVE_LZMADEC_H -#include #endif #ifdef HAVE_ZLIB_H #include @@ -334,9 +332,6 @@ struct xar { #if HAVE_LZMA_H && HAVE_LIBLZMA lzma_stream lzstream; int lzstream_valid; -#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC - lzmadec_stream lzstream; - int lzstream_valid; #endif /* * For Checksum data. @@ -1526,34 +1521,6 @@ decompression_init(struct archive_read *a, enum enctype encoding) xar->lzstream.total_in = 0; xar->lzstream.total_out = 0; break; -#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC) - case LZMA: - if (xar->lzstream_valid) - lzmadec_end(&(xar->lzstream)); - r = lzmadec_init(&(xar->lzstream)); - if (r != LZMADEC_OK) { - switch (r) { - case LZMADEC_HEADER_ERROR: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: " - "invalid header"); - break; - case LZMADEC_MEM_ERROR: - archive_set_error(&a->archive, - ENOMEM, - "Internal error initializing " - "compression library: " - "out of memory"); - break; - } - return (ARCHIVE_FATAL); - } - xar->lzstream_valid = 1; - xar->lzstream.total_in = 0; - xar->lzstream.total_out = 0; - break; #endif /* * Unsupported compression. @@ -1563,9 +1530,7 @@ decompression_init(struct archive_read *a, enum enctype encoding) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) -#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC) case LZMA: -#endif case XZ: #endif switch (xar->entry_encoding) { @@ -1685,46 +1650,12 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, *used = avail_in - xar->lzstream.avail_in; *outbytes = avail_out - xar->lzstream.avail_out; break; -#elif defined(HAVE_LZMADEC_H) && defined(HAVE_LIBLZMADEC) - case LZMA: - xar->lzstream.next_in = (unsigned char *)(uintptr_t)b; - xar->lzstream.avail_in = avail_in; - xar->lzstream.next_out = (unsigned char *)outbuff; - xar->lzstream.avail_out = avail_out; - r = lzmadec_decode(&(xar->lzstream), 0); - switch (r) { - case LZMADEC_STREAM_END: /* Found end of stream. */ - switch (lzmadec_end(&(xar->lzstream))) { - case LZMADEC_OK: - break; - default: - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "Failed to clean up lzmadec decompressor"); - return (ARCHIVE_FATAL); - } - xar->lzstream_valid = 0; - /* FALLTHROUGH */ - case LZMADEC_OK: /* Decompressor made some progress. */ - break; - default: - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "lzmadec decompression failed(%d)", - r); - return (ARCHIVE_FATAL); - } - *used = avail_in - xar->lzstream.avail_in; - *outbytes = avail_out - xar->lzstream.avail_out; - break; #endif #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) -#if !defined(HAVE_LZMADEC_H) || !defined(HAVE_LIBLZMADEC) case LZMA: -#endif case XZ: #endif case NONE: diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 17c23b004457..afcadbbe2571 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -336,14 +336,19 @@ struct archive_write_disk { #define HFS_BLOCKS(s) ((s) >> 12) -static int check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); +static void fsobj_error(int *, struct archive_string *, int, const char *, + const char *); +static int check_symlinks_fsobj(char *, int *, struct archive_string *, + int); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); -static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); +static struct fixup_entry *current_fixup(struct archive_write_disk *, + const char *pathname); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif -static int cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags); +static int cleanup_pathname_fsobj(char *, int *, struct archive_string *, + int); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); @@ -374,11 +379,14 @@ static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); -static int _archive_write_disk_header(struct archive *, struct archive_entry *); +static int _archive_write_disk_header(struct archive *, + struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); -static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); -static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); +static ssize_t _archive_write_disk_data(struct archive *, const void *, + size_t); +static ssize_t _archive_write_disk_data_block(struct archive *, const void *, + size_t, int64_t); static int lazy_stat(struct archive_write_disk *a) @@ -649,7 +657,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { - archive_set_error(&a->archive, errno, "chdir() failure"); + archive_set_error(&a->archive, errno, + "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); @@ -697,7 +706,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); - fe->birthtime_nanos = archive_entry_birthtime_nsec(entry); + fe->birthtime_nanos = archive_entry_birthtime_nsec( + entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; @@ -723,7 +733,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { - memcpy(fe->mac_metadata, metadata, metadata_size); + memcpy(fe->mac_metadata, metadata, + metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } @@ -1480,7 +1491,8 @@ _archive_write_disk_data_block(struct archive *_a, return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, - "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); + "Too much data: Truncating file at %ju bytes", + (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 @@ -2005,8 +2017,9 @@ restore_entry(struct archive_write_disk *a) if (en) { /* Everything failed; give up here. */ - archive_set_error(&a->archive, en, "Can't create '%s'", - a->name); + if ((&a->archive)->error == NULL) + archive_set_error(&a->archive, en, "Can't create '%s'", + a->name); return (ARCHIVE_FAILED); } @@ -2043,19 +2056,32 @@ create_filesystem_object(struct archive_write_disk *a) if (linkname_copy == NULL) { return (EPERM); } - /* TODO: consider using the cleaned-up path as the link target? */ - r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); + /* + * TODO: consider using the cleaned-up path as the link + * target? + */ + r = cleanup_pathname_fsobj(linkname_copy, &error_number, + &error_string, a->flags); if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, error_number, "%s", error_string.s); + archive_set_error(&a->archive, error_number, "%s", + error_string.s); free(linkname_copy); - /* EPERM is more appropriate than error_number for our callers */ + /* + * EPERM is more appropriate than error_number for our + * callers + */ return (EPERM); } - r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); + r = check_symlinks_fsobj(linkname_copy, &error_number, + &error_string, a->flags); if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, error_number, "%s", error_string.s); + archive_set_error(&a->archive, error_number, "%s", + error_string.s); free(linkname_copy); - /* EPERM is more appropriate than error_number for our callers */ + /* + * EPERM is more appropriate than error_number for our + * callers + */ return (EPERM); } free(linkname_copy); @@ -2076,8 +2102,8 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { - a->fd = open(a->name, - O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); + a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY + | O_CLOEXEC | O_NOFOLLOW); __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; @@ -2388,6 +2414,17 @@ current_fixup(struct archive_write_disk *a, const char *pathname) return (a->current_fixup); } +/* Error helper for new *_fsobj functions */ +static void +fsobj_error(int *a_eno, struct archive_string *a_estr, + int err, const char *errstr, const char *path) +{ + if (a_eno) + *a_eno = err; + if (a_estr) + archive_string_sprintf(a_estr, errstr, path); +} + /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other @@ -2400,7 +2437,8 @@ current_fixup(struct archive_write_disk *a, const char *pathname) * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ static int -check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) +check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, + int flags) { #if !defined(HAVE_LSTAT) /* Platform doesn't have lstat, so we can't look for symlinks. */ @@ -2433,7 +2471,8 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error * - if it's a directory and it's not the last chunk, cd into it * As we go: * head points to the current (relative) path - * tail points to the temporary \0 terminating the segment we're currently examining + * tail points to the temporary \0 terminating the segment we're + * currently examining * c holds what used to be in *tail * last is 1 if this is the last tail */ @@ -2455,7 +2494,9 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error * Exiting the loop with break is okay; continue is not. */ while (!last) { - /* Skip the separator we just consumed, plus any adjacent ones */ + /* + * Skip the separator we just consumed, plus any adjacent ones + */ while (*tail == '/') ++tail; /* Skip the next path element. */ @@ -2474,19 +2515,20 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error if (errno == ENOENT) { break; } else { - /* Treat any other error as fatal - best to be paranoid here - * Note: This effectively disables deep directory - * support when security checks are enabled. - * Otherwise, very long pathnames that trigger - * an error here could evade the sandbox. - * TODO: We could do better, but it would probably - * require merging the symlink checks with the - * deep-directory editing. */ - if (error_number) *error_number = errno; - if (error_string) - archive_string_sprintf(error_string, - "Could not stat %s", - path); + /* + * Treat any other error as fatal - best to be + * paranoid here. + * Note: This effectively disables deep + * directory support when security checks are + * enabled. Otherwise, very long pathnames that + * trigger an error here could evade the + * sandbox. + * TODO: We could do better, but it would + * probably require merging the symlink checks + * with the deep-directory editing. + */ + fsobj_error(a_eno, a_estr, errno, + "Could not stat %s", path); res = ARCHIVE_FAILED; break; } @@ -2494,11 +2536,8 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error if (!last) { if (chdir(head) != 0) { tail[0] = c; - if (error_number) *error_number = errno; - if (error_string) - archive_string_sprintf(error_string, - "Could not chdir %s", - path); + fsobj_error(a_eno, a_estr, errno, + "Could not chdir %s", path); res = (ARCHIVE_FATAL); break; } @@ -2514,11 +2553,9 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error */ if (unlink(head)) { tail[0] = c; - if (error_number) *error_number = errno; - if (error_string) - archive_string_sprintf(error_string, - "Could not remove symlink %s", - path); + fsobj_error(a_eno, a_estr, errno, + "Could not remove symlink %s", + path); res = ARCHIVE_FAILED; break; } @@ -2529,13 +2566,14 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error * symlink with another symlink. */ tail[0] = c; - /* FIXME: not sure how important this is to restore + /* + * FIXME: not sure how important this is to + * restore + */ + /* if (!S_ISLNK(path)) { - if (error_number) *error_number = 0; - if (error_string) - archive_string_sprintf(error_string, - "Removing symlink %s", - path); + fsobj_error(a_eno, a_estr, 0, + "Removing symlink %s", path); } */ /* Symlink gone. No more problem! */ @@ -2545,22 +2583,60 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error /* User asked us to remove problems. */ if (unlink(head) != 0) { tail[0] = c; - if (error_number) *error_number = 0; - if (error_string) - archive_string_sprintf(error_string, - "Cannot remove intervening symlink %s", - path); + fsobj_error(a_eno, a_estr, 0, + "Cannot remove intervening " + "symlink %s", path); res = ARCHIVE_FAILED; break; } tail[0] = c; + } else if ((flags & + ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) { + /* + * We are not the last element and we want to + * follow symlinks if they are a directory. + * + * This is needed to extract hardlinks over + * symlinks. + */ + r = stat(head, &st); + if (r != 0) { + tail[0] = c; + if (errno == ENOENT) { + break; + } else { + fsobj_error(a_eno, a_estr, + errno, + "Could not stat %s", path); + res = (ARCHIVE_FAILED); + break; + } + } else if (S_ISDIR(st.st_mode)) { + if (chdir(head) != 0) { + tail[0] = c; + fsobj_error(a_eno, a_estr, + errno, + "Could not chdir %s", path); + res = (ARCHIVE_FATAL); + break; + } + /* + * Our view is now from inside + * this dir: + */ + head = tail + 1; + } else { + tail[0] = c; + fsobj_error(a_eno, a_estr, 0, + "Cannot extract through " + "symlink %s", path); + res = ARCHIVE_FAILED; + break; + } } else { tail[0] = c; - if (error_number) *error_number = 0; - if (error_string) - archive_string_sprintf(error_string, - "Cannot extract through symlink %s", - path); + fsobj_error(a_eno, a_estr, 0, + "Cannot extract through symlink %s", path); res = ARCHIVE_FAILED; break; } @@ -2577,10 +2653,8 @@ check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error if (restore_pwd >= 0) { r = fchdir(restore_pwd); if (r != 0) { - if(error_number) *error_number = errno; - if(error_string) - archive_string_sprintf(error_string, - "chdir() failure"); + fsobj_error(a_eno, a_estr, errno, + "chdir() failure", ""); } close(restore_pwd); restore_pwd = -1; @@ -2605,9 +2679,11 @@ check_symlinks(struct archive_write_disk *a) int error_number; int rc; archive_string_init(&error_string); - rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); + rc = check_symlinks_fsobj(a->name, &error_number, &error_string, + a->flags); if (rc != ARCHIVE_OK) { - archive_set_error(&a->archive, error_number, "%s", error_string.s); + archive_set_error(&a->archive, error_number, "%s", + error_string.s); } archive_string_free(&error_string); a->pst = NULL; /* to be safe */ @@ -2688,17 +2764,16 @@ cleanup_pathname_win(struct archive_write_disk *a) * is set) if the path is absolute. */ static int -cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags) +cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, + int flags) { char *dest, *src; char separator = '\0'; dest = src = path; if (*src == '\0') { - if (error_number) *error_number = ARCHIVE_ERRNO_MISC; - if (error_string) - archive_string_sprintf(error_string, - "Invalid empty pathname"); + fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, + "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } @@ -2708,10 +2783,8 @@ cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *err /* Skip leading '/'. */ if (*src == '/') { if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { - if (error_number) *error_number = ARCHIVE_ERRNO_MISC; - if (error_string) - archive_string_sprintf(error_string, - "Path is absolute"); + fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, + "Path is ", "absolute"); return (ARCHIVE_FAILED); } @@ -2738,11 +2811,11 @@ cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *err } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ - if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { - if (error_number) *error_number = ARCHIVE_ERRNO_MISC; - if (error_string) - archive_string_sprintf(error_string, - "Path contains '..'"); + if (flags + & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { + fsobj_error(a_eno, a_estr, + ARCHIVE_ERRNO_MISC, + "Path contains ", "'..'"); return (ARCHIVE_FAILED); } } @@ -2795,9 +2868,11 @@ cleanup_pathname(struct archive_write_disk *a) int error_number; int rc; archive_string_init(&error_string); - rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); + rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, + a->flags); if (rc != ARCHIVE_OK) { - archive_set_error(&a->archive, error_number, "%s", error_string.s); + archive_set_error(&a->archive, error_number, "%s", + error_string.s); } archive_string_free(&error_string); return rc; @@ -2881,7 +2956,8 @@ create_dir(struct archive_write_disk *a, char *path) } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ - archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); + archive_set_error(&a->archive, errno, + "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; @@ -3406,7 +3482,8 @@ clear_nochange_fflags(struct archive_write_disk *a) nochange_flags |= EXT2_IMMUTABLE_FL; #endif - return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); + return (set_fflags_platform(a, a->fd, a->name, mode, 0, + nochange_flags)); } @@ -3931,7 +4008,8 @@ set_xattrs(struct archive_write_disk *a) if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; - archive_set_error(&a->archive, errno, + archive_set_error(&a->archive, + errno, "Cannot restore extended " "attributes on this file " "system"); @@ -3942,7 +4020,8 @@ set_xattrs(struct archive_write_disk *a) ret = ARCHIVE_WARN; } } else { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } @@ -3986,19 +4065,22 @@ set_xattrs(struct archive_write_disk *a) errno = 0; #if HAVE_EXTATTR_SET_FD if (a->fd >= 0) - e = extattr_set_fd(a->fd, namespace, name, value, size); + e = extattr_set_fd(a->fd, namespace, name, + value, size); else #endif /* TODO: should we use extattr_set_link() instead? */ { - e = extattr_set_file(archive_entry_pathname(entry), - namespace, name, value, size); + e = extattr_set_file( + archive_entry_pathname(entry), namespace, + name, value, size); } if (e != (int)size) { if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; - archive_set_error(&a->archive, errno, + archive_set_error(&a->archive, + errno, "Cannot restore extended " "attributes on this file " "system"); diff --git a/libarchive/config_freebsd.h b/libarchive/config_freebsd.h index 665cc58c113b..9edea419110e 100644 --- a/libarchive/config_freebsd.h +++ b/libarchive/config_freebsd.h @@ -40,6 +40,7 @@ #define HAVE_EXTATTR_LIST_FILE 1 #define HAVE_EXTATTR_SET_FD 1 #define HAVE_EXTATTR_SET_FILE 1 +#define HAVE_STRUCT_XVFSCONF 1 #define HAVE_SYS_ACL_H 1 #define HAVE_SYS_EXTATTR_H 1 #endif diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 1cb21f9c6b71..9d2622b0cda5 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -60,6 +60,7 @@ IF(ENABLE_TEST) test_compat_pax_libarchive_2x.c test_compat_solaris_pax_sparse.c test_compat_solaris_tar_acl.c + test_compat_star_acl_posix1e.c test_compat_tar_hardlink.c test_compat_uudecode.c test_compat_uudecode_large.c diff --git a/libarchive/test/test_compat_gtar.c b/libarchive/test/test_compat_gtar.c index 3d5e456e56e6..def24aae5220 100644 --- a/libarchive/test/test_compat_gtar.c +++ b/libarchive/test/test_compat_gtar.c @@ -106,10 +106,48 @@ test_compat_gtar_1(void) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +/* + * test_compat_gtar_2.tar exercises reading of UID = 2097152 as base256 + * and GID = 2097152 as octal without null terminator. + */ +static void +test_compat_gtar_2(void) +{ + char name[] = "test_compat_gtar_2.tar"; + struct archive_entry *ae; + struct archive *a; + int r; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + extract_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); + + /* Read first entry. */ + assertEqualIntA(a, ARCHIVE_OK, r = archive_read_next_header(a, &ae)); + if (r != ARCHIVE_OK) { + archive_read_free(a); + return; + } + + /* Check UID and GID */ + assertEqualInt(2097152, archive_entry_uid(ae)); + assertEqualInt(2097152, archive_entry_gid(ae)); + + /* Verify the end-of-archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify that the format detection worked. */ + assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_NONE); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR); + +} DEFINE_TEST(test_compat_gtar) { test_compat_gtar_1(); + test_compat_gtar_2(); } diff --git a/libarchive/test/test_compat_gtar_2.tar.uu b/libarchive/test/test_compat_gtar_2.tar.uu new file mode 100644 index 000000000000..7843a2cbaeb6 --- /dev/null +++ b/libarchive/test/test_compat_gtar_2.tar.uu @@ -0,0 +1,49 @@ +begin 660 test_compat_gtar_2.tar.uu +M9FEL95]W:71H7V)I9U]U:61?9VED```````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#`V-C8`@``````@```Q,#`P,#`P,#`P,#`P,#`P,38W +M`#$S,#$T-Ctype) + return (0); + if (permset != acl->permset) + return (0); + if (tag != acl->tag) + return (0); + if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_OTHER) + return (1); + if (tag == ARCHIVE_ENTRY_ACL_MASK) + return (1); + if (name == NULL) + return (acl->name == NULL || acl->name[0] == '\0'); + if (acl->name == NULL) + return (name == NULL || name[0] == '\0'); + return (0 == strcmp(name, acl->name)); +} + +static void +compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode, + int want_type) +{ + int *marker = malloc(sizeof(marker[0]) * n); + int i; + int r; + int type, permset, tag, qual; + int matched; + const char *name; + + for (i = 0; i < n; i++) + marker[i] = i; + + while (0 == (r = archive_entry_acl_next(ae, want_type, + &type, &permset, &tag, &qual, &name))) { + for (i = 0, matched = 0; i < n && !matched; i++) { + if (acl_match(&acls[marker[i]], type, permset, + tag, name)) { + /* We found a match; remove it. */ + marker[i] = marker[n - 1]; + n--; + matched = 1; + } + } + if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) { + if (!matched) printf("No match for user_obj perm\n"); + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + failure("USER_OBJ permset (%02o) != user mode (%02o)", + permset, 07 & (mode >> 6)); + assert((permset << 6) == (mode & 0700)); + } + } else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { + if (!matched) printf("No match for group_obj perm\n"); + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + failure("GROUP_OBJ permset %02o != group mode %02o", + permset, 07 & (mode >> 3)); + assert((permset << 3) == (mode & 0070)); + } + } else if (tag == ARCHIVE_ENTRY_ACL_OTHER) { + if (!matched) printf("No match for other perm\n"); + if (want_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { + failure("OTHER permset (%02o) != other mode (%02o)", + permset, mode & 07); + assert((permset << 0) == (mode & 0007)); + } + } else if (tag != ARCHIVE_ENTRY_ACL_MASK) { + failure("Could not find match for ACL " + "(type=%d,permset=%d,tag=%d,name=``%s'')", + type, permset, tag, name); + assert(matched == 1); + } + } + assertEqualInt(ARCHIVE_EOF, r); + assert((mode_t)(mode & 0777) == (archive_entry_mode(ae) & 0777)); + failure("Could not find match for ACL " + "(type=%d,permset=%d,tag=%d,name=``%s'')", + acls[marker[0]].type, acls[marker[0]].permset, + acls[marker[0]].tag, acls[marker[0]].name); + assert(n == 0); /* Number of ACLs not matched should == 0 */ + free(marker); +} + +DEFINE_TEST(test_compat_star_acl_posix1e) +{ + char name[] = "test_compat_star_acl_posix1e.tar"; + struct archive *a; + struct archive_entry *ae; + + /* Read archive file */ + assert(NULL != (a = archive_read_new())); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + extract_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); + + /* First item has a few ACLs */ + assertA(0 == archive_read_next_header(a, &ae)); + failure("One extended ACL should flag all ACLs to be returned."); + assertEqualInt(5, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + compare_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + failure("Basic ACLs should set mode to 0142, not %04o", + archive_entry_mode(ae)&0777); + assert((archive_entry_mode(ae) & 0777) == 0142); + + /* Second item has pretty extensive ACLs */ + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(7, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS)); + compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0543, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + failure("Basic ACLs should set mode to 0543, not %04o", + archive_entry_mode(ae)&0777); + assert((archive_entry_mode(ae) & 0777) == 0543); + + /* Third item has default ACLs */ + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)); + compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0142, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + failure("Basic ACLs should set mode to 0142, not %04o", + archive_entry_mode(ae)&0777); + assert((archive_entry_mode(ae) & 0777) == 0142); + + /* Close the archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_compat_star_acl_posix1e.tar.uu b/libarchive/test/test_compat_star_acl_posix1e.tar.uu new file mode 100644 index 000000000000..81b771b3f378 --- /dev/null +++ b/libarchive/test/test_compat_star_acl_posix1e.tar.uu @@ -0,0 +1,231 @@ +begin 644 test_compat_star_acl_posix1e.tar +M+B\N+T!087A(96%D97(````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#`V,#`@,#`P,#`P,"`P,#`P,#`P(#`P,#`P,#`P,C"QU +M"QU"QM87-K.CIR+7@L;W1H97(Z.BUW+0H` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````!D:7(Q+P`````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M,#`P,#$T,B`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,34T +M-S8@,#`Q-#8R-B`U```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````'5S=&%R`#`PF%Z897!E<&5P97!L*&PH;"AL*',`;AYN'F +MX>;B`B("(@(B`B(<@AR"'((<@C?B-^(WXC?B4L)2PE+"4L)N(FXB;B)N(HCB +MB.*(XHCBH\*CPJ/"H\BZQK3$>Z`M3P```96P=EB)(F#:0C*.B=E8YJH8[61W +MV+Z)DM;2)*Q8Z4V[T6O1*GO&@Y3UWQHV$QNX==:0U"G4XC@H"!]-(J(@'D/? +M@(SYTVUR+"E#9NEU?4F/)3&DAC2@Z%6S^G>N30VJGI%L62FLU1@^LI27SAJ\ +MAE7)"Z[IC/VMTH6><,,*6=1B_>=>!,RS3:UOL>+6(/[321O:>SY39A-MM;Q$AVID_52A79OI7GLR$& +MSF*I)!#&@`%Q#EY!`,.RR%*'&\:EF1QI5(J]X(-6,[J'"6>D\2[%OX:S*/?P46S?=U=PV5MOW^%-?Q +M"-,Z'1F[U]*D5IJX<+]WA,(L9NKCB>[D-C3H[!`U7VI[+9C9M9:A +M3'3YOJJE-5E\Z$7102[J,RG1VXIIC',\]YQ3C.:890?))T[97M[+-*8XQY&W +MIH_F5(+RXB"?$"!HHBA%2^8,)-!3&H29["AH7VT*^HIG\Y^_U;GAW,\!)WGV +M&],S7-,TY;['3(@ZG`VXB\VVM&^?"X!RVCO\T02FIP`U;[V.JPQGC64RBRT4 +MUY0&N\%%_CDP*E%B_.L)Q%IB\M:L5+[&I0_%2T.JE&DSJ\F.HTZ_HGFV49Y: +MC`"$AQ,D=<3^/R?B54&T*A&33@,V;Q7#IJ$==HB_(T7H(#)O*9>_":$02^K" +MF*F5#21OUT-KRQ]\L0"FP^9B'>[1/3**.N@"OB````7M\#3O$=.;64K*60;( +M,]2>I1YH\R)I$TL26+"'A#Q(8D+O!W@^'?#M86L+?5OJO77KL:6-2E)2DKC5 +MQHT4:*6!+"=3.IGX3\1=8NN9D,R$7"+A.DG2+C%R#&/"^9?*X#<"-<&M[Y5\L[_=_1,(F$):$K+4EJR-I&M(FD3/ +MDGR64K*=:6M(.$Q](FI95+*I%-(>I9E++I$-(4I9U+,I"](+I:=+1I!](`I; +M-+6I`E'RI;]+;H^U'CI==+FH]%'1I?=+TH[%&WIAU,$HX%&8IEE,?HTE%NIK +MU-)HO]$GIU=.(HHE"HJ(-/[H<4]LJX=3^H!L:'H>H[!FC6*`3@CA)3!E[+&6LWYN# +M1&I/L>\[9XD4HC0@A=+*4T@I(4.H%."=E9*L4XJ);BUECK,8*O]>"]F6LH8R +MQYJK4&@M&;ZWAM+;G2N@J)4JUMI[%61P#?J[-X,QY/7S^ +M!!A0XD6-'D294N9-G3Z%&E3J5:U>Q9M6[EV]?P8<6/)ES9]&G5KV;=V_AQY< +M^G7MW`%/B`"Y@?X"'Q```0!\T``'S@$_P``3_0(0T``R`!`,@&0````$#__X +M'_^!__@?@?_X'_^!^!__@?_X'X'_^!__@?@?_X'_^!^!__@?_X'X'_^!__@? +M@?_X'_^!^!__@?_X'X'_^!__@?@``"!__@?_^```@#YH``9`!R@\C(R&ZKFQ +ML]VG'6NK;7Y'YR_*2;6$.#`H3IT%@:D4DL9DAIUJ.-A"5-S[1?*=.G\Y*:'1 +M1KZ6E`1&3E36NH7)B!2(Z,%9;J:8I;R%A;D%KE3`J3R4O.]L0J^):=TM0O*. +M\2.HMA=FQ.2(#*``+0#__RQ`^FC6'9C\M-VY1W%.XY7!&\H5`"+,W&V_=W\^ +M_77O*T?R-^O4X)'`Z3U.Z1X5.M50:RX+XQ#';7;^I6W+O"/4^>9R1RG!Z1^: +M:>8FK#]*MUP\U+YH#IT%O_MOI9`M^L8R)"\'@`\Q$O@``4=AV`+M`SU9^H'X +M9ZFNQQ-0CUX*.""6+X'2R]-H;+96%JR-5HW)NY[/(B9T/2$Z,30K8-_0/9C+ +M-7:)?(99;O+XYLW$/Y(?+U8G^=LTGNZ:2Z'"G8R['JXC>ZW8VK#*5P[&]Y!7 +M(4XT+*1XM?)29BO)=9)?%%8T1___PX77'!5!9,V\L_>>[=7X1I8K?."<=)*) +MMQ(IE +M3*?-I+]6DJZ___G?+6HC]^T+#5G,/$>(M"IY_F>+.3THA1TWV#8R2@W:,SB% +M;4+BDGXR0^$/44-L-'" +M),O9S0>/PWNA#L#-&!*T9TSX?AZEJ&$LLL@]$`#L,&2TZA;RY*]+\74Q1=#P +ME9*X;4Y9E##F*M+0M5N0U#)+.2>*)+#6#3IAVY5R<69:&V"?__ +M]XEC]%^&4693BM$.(L;Y'RKS/)&2$@A$K(7BL)4:TA=:_V'/=C@T9I[K7UP? +M>S`:!,T9`N`;I41,6P[ +MJG&?KE50XF!\1TYPJE_`5R<0X#12A0_M]+_HOO(>:]EYCGFVN0=:U]ES/FDP +M4?@WZ+:X5(JC6VN%)2A&IG\->?E(Q'R.%.+<"\``@Q2/__^`RJ5L!E6DGZ?Z +M@J6*'B7N.GG>N`X):Y*6+>5DU'8EN%A,]=6C7U>%38)2D),4T4Z+@U4]2HB" +MI0`=!:($>Q@'\(1+`8$Q.`0!`7!4`!T%4(`N((JRA(0MD&/4=$I.X[3K1A+4 +M82-/$Y3=)%L5-2U;6A9UD7-H&07U@E365H$TBJ$G\B*0)9C-_:NI^@Z6R&YK +M6P/=-.QC3'___7922)<0Y5,>Q2VKBW36LFTSS.VZSGO.^+T/I`KYOX_<)1G` +MT+QG#T+P-'#QR7+D7O^^\7R6F#1QY*D)R8]L)M\>,%$Z>B]$$7Z,(\1*>+JN +MJ.+`V"_,"X\60HGD=Q'`[R0?$D8Q4^S_PW`CJ/2_+S.*WSNNFR#1N4S*YK

KVVW5BL'"S3RI&ZR$\M4* +MK5<+$V&@H80`)A,%0F?C4/BJ@TJBT&G5HSD(MV>MU6GUHM%4DU0JE,L6(N%8 +MRF2+:D3:CV7C+:$#@+#4#EC;)0*`1B+,!&*Q_'Q,*A4%@M%0C%0G%HV +M%XQ&`Q$@I&`J$8F%HP$@D%@^(`V%(\(@X%1#)))(!')H^)Q8(8_&9'?__^OD +MT:L1&`T*B4ZF25ZK^1PJ,1N0-%`MJ&-U<-A[K@GM1Y.!7)=@L)!E4EL,L&UC +MK@LF$\`4$!88`05#@`#\9"DF#<2B\D%46!$:&0Z'0WG=:IDUL`WIA3JIA*E; +MK]C,=,-)F,QH-1Q.!F-MW.-O)!2M%!/1_/N!.2!O9I0>1Q!^QB2R2.PZ,1>% +M/___UB$XV>8$F+"4I'EB-+R$A(2)%E%8@*SPP'C^7(@P%)T,E&4^)A@QA3P8 +M*.C%&E7*!7V988%Y94SQ-8E,I*28F*P89+09/)!4H`!P*%0@2-0T +M3#@H9$!08'0X/$X=S.8\K&!D<2BM7,#Z-,L4HU"E.@1IU`8:5`(3YQ$-/FPYP +MZ;,)41Y.R4(U;___X8@5#U%6!PL`"0DF1@D/$0\+)FP[#B`]*!,I@Q\+#`>GIK 0); -} - -#elif defined(__linux__)&& defined(HAVE_LINUX_FIEMAP_H) - +#if defined(HAVE_LINUX_FIEMAP_H) /* * FIEMAP, which can detect 'hole' of a sparse file, has * been supported from 2.6.28 */ static int -is_sparse_supported(const char *path) +is_sparse_supported_fiemap(const char *path) { const struct sparse sparse_file[] = { /* This hole size is too small to create a sparse @@ -198,7 +184,58 @@ is_sparse_supported(const char *path) return (r >= 0); } -#else +#if !defined(SEEK_HOLE) || !defined(SEEK_DATA) +static int +is_sparse_supported(const char *path) +{ + return is_sparse_supported_fiemap(path); +} +#endif +#endif + +#if defined(_PC_MIN_HOLE_SIZE) + +/* + * FreeBSD and Solaris can detect 'hole' of a sparse file + * through lseek(HOLE) on ZFS. (UFS does not support yet) + */ + +static int +is_sparse_supported(const char *path) +{ + return (pathconf(path, _PC_MIN_HOLE_SIZE) > 0); +} + +#elif defined(SEEK_HOLE) && defined(SEEK_DATA) + +static int +is_sparse_supported(const char *path) +{ + const struct sparse sparse_file[] = { + /* This hole size is too small to create a sparse + * files for almost filesystem. */ + { HOLE, 1024 }, { DATA, 10240 }, + { END, 0 } + }; + int fd, r; + const char *testfile = "can_sparse"; + + (void)path; /* UNUSED */ + create_sparse_file(testfile, sparse_file); + fd = open(testfile, O_RDWR); + if (fd < 0) + return (0); + r = lseek(fd, 0, SEEK_HOLE); + close(fd); + unlink(testfile); +#if defined(HAVE_LINUX_FIEMAP_H) + if (r < 0) + return (is_sparse_supported_fiemap(path)); +#endif + return (r >= 0); +} + +#elif !defined(HAVE_LINUX_FIEMAP_H) /* * Other system may do not have the API such as lseek(HOLE), diff --git a/tar/test/test_symlink_dir.c b/tar/test/test_symlink_dir.c index 25bd8b162a16..852e00b37c64 100644 --- a/tar/test/test_symlink_dir.c +++ b/tar/test/test_symlink_dir.c @@ -47,11 +47,18 @@ DEFINE_TEST(test_symlink_dir) assertMakeDir("source/dir3", 0755); assertMakeDir("source/dir3/d3", 0755); assertMakeFile("source/dir3/f3", 0755, "abcde"); + assertMakeDir("source/dir4", 0755); + assertMakeFile("source/dir4/file3", 0755, "abcdef"); + assertMakeHardlink("source/dir4/file4", "source/dir4/file3"); assertEqualInt(0, systemf("%s -cf test.tar -C source dir dir2 dir3 file file2", testprog)); + /* Second archive with hardlinks */ + assertEqualInt(0, + systemf("%s -cf test2.tar -C source dir4", testprog)); + /* * Extract with -x and without -P. */ @@ -118,9 +125,15 @@ DEFINE_TEST(test_symlink_dir) assertMakeSymlink("dest2/file2", "real_file2"); assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog)); - /* dest2/dir symlink should be followed */ + /* "dir4" is a symlink to existing "real_dir" */ + if (canSymlink()) + assertMakeSymlink("dest2/dir4", "real_dir"); + 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"); assertIsDir("dest2/real_dir", -1); } @@ -141,4 +154,7 @@ DEFINE_TEST(test_symlink_dir) /* dest2/file2 symlink should be removed */ failure("Symlink to non-existing file should be removed"); assertIsReg("dest2/file2", -1); + + /* dest2/dir4/file3 and dest2/dir4/file4 should be hard links */ + assertIsHardlink("dest2/dir4/file3", "dest2/dir4/file4"); }