Merge branch 'master' of https://github.com/libarchive/libarchive into cng_enable
diff --git a/.travis.yml b/.travis.yml
index 34665e6..d7fe837 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,30 @@
 language: C
-sudo: required
+sudo: false
 dist: trusty
+addons:
+  apt:
+    packages:
+      - libacl1-dev
+      - libbz2-dev
+      - liblzma-dev
+      - libzip-dev
+      - lzop
+os:
+  - linux
+  - osx
 compiler:
   - gcc
   - clang
 env:
   - BUILD_SYSTEM=cmake
   - BUILD_SYSTEM=autotools
+matrix:
+  exclude:
+    - os: osx
+      compiler: gcc
+before_install:
+  - if [ `uname` = "Darwin" ]; then brew update; fi
 install:
-  - sudo apt-get install -y libbz2-dev libzip-dev liblzma-dev liblzo2-dev
+  - if [ `uname` = "Darwin" ]; then brew install xz lzop lz4; fi
 script:
   - build/ci_build.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d9a48fa..47cf004 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,13 +44,13 @@
 #
 FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/build/version _version)
 STRING(REGEX REPLACE
- "^([0-9])[0-9][0-9][0-9][0-9][0-9][0-9][a-z]?$" "\\1" _major ${_version})
+ "^([0-9])[0-9][0-9][0-9][0-9][0-9][0-9][a-z]*$" "\\1" _major ${_version})
 STRING(REGEX REPLACE
- "^[0-9]([0-9][0-9][0-9])[0-9][0-9][0-9][a-z]?$" "\\1" _minor ${_version})
+ "^[0-9]([0-9][0-9][0-9])[0-9][0-9][0-9][a-z]*$" "\\1" _minor ${_version})
 STRING(REGEX REPLACE
- "^[0-9][0-9][0-9][0-9]([0-9][0-9][0-9])[a-z]?$" "\\1" _revision ${_version})
+ "^[0-9][0-9][0-9][0-9]([0-9][0-9][0-9])[a-z]*$" "\\1" _revision ${_version})
 STRING(REGEX REPLACE
- "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]([a-z]?)$" "\\1" _quality ${_version})
+ "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]([a-z]*)$" "\\1" _quality ${_version})
 SET(_version_number ${_major}${_minor}${_revision})
 STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_minor ${_minor})
 STRING(REGEX REPLACE "[0]*([^0]*[0-9])$" "\\1" _trimmed_revision ${_revision})
@@ -179,13 +179,15 @@
 
 OPTION(ENABLE_NETTLE "Enable use of Nettle" ON)
 OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
-OPTION(ENABLE_LZMA "Enable the use of the system found LZMA library if found" ON)
-OPTION(ENABLE_ZLIB "Enable the use of the system found ZLIB library if found" ON)
-OPTION(ENABLE_BZip2 "Enable the use of the system found BZip2 library if found" ON)
-OPTION(ENABLE_LIBXML2 "Enable the use of the system found libxml2 library if found" ON)
-OPTION(ENABLE_EXPAT "Enable the use of the system found EXPAT library if found" ON)
-OPTION(ENABLE_PCREPOSIX "Enable the use of the system found PCREPOSIX library if found" ON)
-OPTION(ENABLE_LibGCC "Enable the use of the system found LibGCC library if found" ON)
+OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF)
+OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON)
+
+OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON)
+OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON)
+OPTION(ENABLE_LIBXML2 "Enable the use of the system libxml2 library if found" ON)
+OPTION(ENABLE_EXPAT "Enable the use of the system EXPAT library if found" ON)
+OPTION(ENABLE_PCREPOSIX "Enable the use of the system PCREPOSIX library if found" ON)
+OPTION(ENABLE_LibGCC "Enable the use of the system LibGCC library if found" ON)
 # CNG is used for encrypt/decrypt Zip archives on Windows.
 OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON)
 
@@ -460,6 +462,8 @@
 IF(LIBLZMA_FOUND)
   SET(HAVE_LIBLZMA 1)
   SET(HAVE_LZMA_H 1)
+  SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR})
+  SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES})
   INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS})
   LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES})
   # Test if a macro is needed for the library.
@@ -477,15 +481,19 @@
 #
 # Find LZO2
 #
-IF (LZO2_INCLUDE_DIR)
-  # Already in cache, be silent
-  SET(LZO2_FIND_QUIETLY TRUE)
-ENDIF (LZO2_INCLUDE_DIR)
+IF(ENABLE_LZO)
+  IF (LZO2_INCLUDE_DIR)
+    # Already in cache, be silent
+    SET(LZO2_FIND_QUIETLY TRUE)
+  ENDIF (LZO2_INCLUDE_DIR)
 
-FIND_PATH(LZO2_INCLUDE_DIR lzo/lzoconf.h)
-FIND_LIBRARY(LZO2_LIBRARY NAMES lzo2 liblzo2)
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZO2 DEFAULT_MSG LZO2_LIBRARY LZO2_INCLUDE_DIR)
+  FIND_PATH(LZO2_INCLUDE_DIR lzo/lzoconf.h)
+  FIND_LIBRARY(LZO2_LIBRARY NAMES lzo2 liblzo2)
+  INCLUDE(FindPackageHandleStandardArgs)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZO2 DEFAULT_MSG LZO2_LIBRARY LZO2_INCLUDE_DIR)
+ELSE(ENABLE_LZO)
+  SET(LIBZMA_FOUND FALSE) # Override cached value
+ENDIF(ENABLE_LZO)
 IF(LZO2_FOUND)
   SET(HAVE_LIBLZO2 1)
   SET(HAVE_LZO_LZOCONF_H 1)
@@ -564,8 +572,14 @@
 LA_CHECK_INCLUDE_FILE("linux/types.h" HAVE_LINUX_TYPES_H)
 LA_CHECK_INCLUDE_FILE("linux/fiemap.h" HAVE_LINUX_FIEMAP_H)
 LA_CHECK_INCLUDE_FILE("linux/fs.h" HAVE_LINUX_FS_H)
+
+CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
+#include <linux/fs.h>
+int main(void) { return FS_IOC_GETFLAGS; }" HAVE_WORKING_FS_IOC_GETFLAGS)
+
 LA_CHECK_INCLUDE_FILE("linux/magic.h" HAVE_LINUX_MAGIC_H)
 LA_CHECK_INCLUDE_FILE("locale.h" HAVE_LOCALE_H)
+LA_CHECK_INCLUDE_FILE("membership.h" HAVE_MEMBERSHIP_H)
 LA_CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H)
 LA_CHECK_INCLUDE_FILE("paths.h" HAVE_PATHS_H)
 LA_CHECK_INCLUDE_FILE("poll.h" HAVE_POLL_H)
@@ -611,7 +625,7 @@
 ELSE(ENABLE_CNG)
   UNSET(HAVE_BCRYPT_H CACHE)
 ENDIF(ENABLE_CNG)
-# Following files need windwos.h, so we should test it after windows.h test.
+# Following files need windows.h, so we should test it after windows.h test.
 LA_CHECK_INCLUDE_FILE("wincrypt.h" HAVE_WINCRYPT_H)
 LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H)
 
@@ -1608,10 +1622,29 @@
   CHECK_FUNCTION_EXISTS(acl_get_link_np HAVE_ACL_GET_LINK_NP)
   CHECK_FUNCTION_EXISTS(acl_is_trivial_np HAVE_ACL_IS_TRIVIAL_NP)
   CHECK_FUNCTION_EXISTS(acl_set_link_np HAVE_ACL_SET_LINK_NP)
+  CHECK_SYMBOL_EXISTS(ACL_TYPE_NFS4 "${INCLUDES}" HAVE_DECL_ACL_TYPE_NFS4)
 
   # MacOS has an acl.h that isn't POSIX.  It can be detected by
   # checking for ACL_USER
-  CHECK_SYMBOL_EXISTS(ACL_USER "${INCLUDES}" HAVE_ACL_USER)
+  CHECK_SYMBOL_EXISTS(ACL_USER "${INCLUDES}" HAVE_DECL_ACL_USER)
+  CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
+#include <sys/acl.h>
+int main(void) { return ACL_TYPE_EXTENDED; }" HAVE_DECL_ACL_TYPE_EXTENDED)
+  CHECK_C_SOURCE_COMPILES("#include <sys/types.h>
+#include <sys/acl.h>
+int main(void) { return ACL_SYNCHRONIZE; }" HAVE_DECL_ACL_SYNCHRONIZE)
+
+  # Solaris and derivates ACLs
+  CHECK_TYPE_EXISTS(aclent_t "${INCLUDES}" HAVE_ACLENT_T)
+  CHECK_TYPE_EXISTS(ace_t "${INCLUDES}" HAVE_ACE_T)
+  CHECK_FUNCTION_EXISTS(acl HAVE_ACL)
+  CHECK_FUNCTION_EXISTS(facl HAVE_FACL)
+  CHECK_SYMBOL_EXISTS(GETACL "${INCLUDES}" HAVE_DECL_GETACL)
+  CHECK_SYMBOL_EXISTS(GETACLCNT "${INCLUDES}" HAVE_DECL_GETACLCNT)
+  CHECK_SYMBOL_EXISTS(SETACL "${INCLUDES}" HAVE_DECL_SETACL)
+  CHECK_SYMBOL_EXISTS(ACE_GETACL "${INCLUDES}" HAVE_DECL_ACE_GETACL)
+  CHECK_SYMBOL_EXISTS(ACE_GETACLCNT "${INCLUDES}" HAVE_DECL_ACE_GETACLCNT)
+  CHECK_SYMBOL_EXISTS(ACE_SETACL "${INCLUDES}" HAVE_DECL_ACE_SETACL)
 ELSE(ENABLE_ACL)
   # If someone runs cmake, then disables ACL support, we need
   # to forcibly override the cached values for these.
@@ -1626,7 +1659,20 @@
   SET(HAVE_ACL_SET_FD FALSE)
   SET(HAVE_ACL_SET_FD_NP FALSE)
   SET(HAVE_ACL_SET_FILE FALSE)
-  SET(HAVE_ACL_USER FALSE)
+  SET(HAVE_ACL_TYPE_EXTENDED FALSE)
+  SET(HAVE_ACLENT_T FALSE)
+  SET(HAVE_ACE_T FALSE)
+  SET(HAVE_DECL_ACL_TYPE_NFS4 FALSE)
+  SET(HAVE_DECL_ACL_USER FALSE)
+  SET(HAVE_DECL_ACL_SYNCHRONIZE FALSE)
+  SET(HAVE_DECL_GETACL FALSE)
+  SET(HAVE_DECL_GETACLCNT FALSE)
+  SET(HAVE_DECL_SETACL FALSE)
+  SET(HAVE_DECL_ACE_GETACL FALSE)
+  SET(HAVE_DECL_ACE_GETACLCNT FALSE)
+  SET(HAVE_DECL_ACE_SETACL FALSE)
+  SET(HAVE_ACL FALSE)
+  SET(HAVE_FACL FALSE)
 ENDIF(ENABLE_ACL)
 
 #
diff --git a/Makefile.am b/Makefile.am
index 614f864..b5a41f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -310,7 +310,9 @@
 # Sources needed by all test programs
 test_utils_SOURCES= \
 	test_utils/test_utils.c \
-	test_utils/test_utils.h
+	test_utils/test_utils.h \
+	test_utils/test_main.c \
+	test_utils/test_common.h
 
 #
 #
@@ -320,14 +322,14 @@
 libarchive_test_SOURCES= \
 	$(libarchive_la_SOURCES) \
 	$(test_utils_SOURCES) \
-	libarchive/test/main.c \
 	libarchive/test/read_open_memory.c \
 	libarchive/test/test.h \
-	libarchive/test/test_acl_freebsd_posix1e.c \
-	libarchive/test/test_acl_freebsd_nfs4.c \
 	libarchive/test/test_acl_nfs4.c \
 	libarchive/test/test_acl_pax.c \
+	libarchive/test/test_acl_platform_nfs4.c \
+	libarchive/test/test_acl_platform_posix1e.c \
 	libarchive/test/test_acl_posix1e.c \
+	libarchive/test/test_acl_text.c \
 	libarchive/test/test_archive_api_feature.c \
 	libarchive/test/test_archive_clear_error.c \
 	libarchive/test/test_archive_cmdline.c \
@@ -376,7 +378,7 @@
 	libarchive/test/test_compat_plexus_archiver_tar.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_star_acl.c \
 	libarchive/test/test_compat_tar_hardlink.c \
 	libarchive/test/test_compat_uudecode.c \
 	libarchive/test/test_compat_uudecode_large.c \
@@ -483,6 +485,7 @@
 	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_jar.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 \
@@ -495,6 +498,7 @@
 	libarchive/test/test_read_format_zip_winzip_aes_large.c \
 	libarchive/test/test_read_format_zip_zip64.c \
 	libarchive/test/test_read_large.c \
+	libarchive/test/test_read_pax_schily_xattr.c \
 	libarchive/test/test_read_pax_truncated.c \
 	libarchive/test/test_read_position.c \
 	libarchive/test/test_read_set_format.c \
@@ -583,7 +587,13 @@
 	libarchive/test/test_write_read_format_zip.c \
 	libarchive/test/test_zip_filename_encoding.c
 
-libarchive_test_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/test_utils -I$(top_builddir)/libarchive/test -DLIBARCHIVE_STATIC $(PLATFORMCPPFLAGS)
+libarchive_test_CPPFLAGS= \
+	-I$(top_srcdir)/libarchive \
+	-I$(top_srcdir)/libarchive/test \
+	-I$(top_srcdir)/test_utils \
+	-I$(top_builddir)/libarchive/test \
+	-DLIBARCHIVE_STATIC $(PLATFORMCPPFLAGS)
+
 libarchive_test_LDADD= $(LTLIBICONV)
 
 # The "list.h" file just lists all of the tests defined in all of the sources.
@@ -597,7 +607,8 @@
 
 libarchive_test_EXTRA_DIST=\
 	libarchive/test/list.h \
-	libarchive/test/test_acl_pax.tar.uu \
+	libarchive/test/test_acl_pax_posix1e.tar.uu \
+	libarchive/test/test_acl_pax_nfs4.tar.uu \
 	libarchive/test/test_archive_string_conversion.txt.Z.uu \
 	libarchive/test/test_compat_bzip2_1.tbz.uu \
 	libarchive/test/test_compat_bzip2_2.tbz.uu \
@@ -630,10 +641,11 @@
 	libarchive/test/test_compat_mac-2.tar.Z.uu \
 	libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu \
 	libarchive/test/test_compat_perl_archive_tar.tar.uu \
-	libarchive/test/test_compat_plexus_archiver_tar.uu \
+	libarchive/test/test_compat_plexus_archiver_tar.tar.uu \
 	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_nfs4.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 \
@@ -801,6 +813,7 @@
 	libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \
 	libarchive/test/test_read_format_zip_high_compression.zip.uu \
 	libarchive/test/test_read_format_zip_length_at_end.zip.uu \
+	libarchive/test/test_read_format_zip_jar.jar.uu \
 	libarchive/test/test_read_format_zip_mac_metadata.zip.uu \
 	libarchive/test/test_read_format_zip_malformed1.zip.uu \
 	libarchive/test/test_read_format_zip_msdos.zip.uu \
@@ -824,6 +837,7 @@
 	libarchive/test/test_read_large_splitted_rar_ac.uu \
 	libarchive/test/test_read_large_splitted_rar_ad.uu \
 	libarchive/test/test_read_large_splitted_rar_ae.uu \
+	libarchive/test/test_read_pax_schily_xattr.tar.uu \
 	libarchive/test/test_read_splitted_rar_aa.uu \
 	libarchive/test/test_read_splitted_rar_ab.uu \
 	libarchive/test/test_read_splitted_rar_ac.uu \
@@ -911,7 +925,6 @@
 
 bsdtar_test_SOURCES= \
 	$(test_utils_SOURCES) \
-	tar/test/main.c \
 	tar/test/test.h \
 	tar/test/test_0.c \
 	tar/test/test_basic.c \
@@ -938,10 +951,12 @@
 	tar/test/test_option_T_upper.c \
 	tar/test/test_option_U_upper.c \
 	tar/test/test_option_X_upper.c \
+	tar/test/test_option_acls.c \
 	tar/test/test_option_a.c \
 	tar/test/test_option_b.c \
 	tar/test/test_option_b64encode.c \
 	tar/test/test_option_exclude.c \
+	tar/test/test_option_fflags.c \
 	tar/test/test_option_gid_gname.c \
 	tar/test/test_option_grzip.c \
 	tar/test/test_option_j.c \
@@ -974,7 +989,8 @@
 bsdtar_test_CPPFLAGS=\
 	-I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \
 	-I$(top_srcdir)/test_utils \
-	-I$(top_srcdir)/tar -I$(top_builddir)/tar/test \
+	-I$(top_srcdir)/tar -I$(top_srcdir)/tar/test \
+	-I$(top_builddir)/tar/test \
 	$(PLATFORMCPPFLAGS)
 
 tar/test/list.h: Makefile
@@ -1069,7 +1085,6 @@
 bsdcpio_test_SOURCES= \
 	$(test_utils_SOURCES) \
 	cpio/cmdline.c \
-	cpio/test/main.c \
 	cpio/test/test.h \
 	cpio/test/test_0.c \
 	cpio/test/test_basic.c \
@@ -1121,7 +1136,8 @@
 bsdcpio_test_CPPFLAGS= \
 	-I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \
 	-I$(top_srcdir)/test_utils \
-	-I$(top_srcdir)/cpio -I$(top_builddir)/cpio/test \
+	-I$(top_srcdir)/cpio -I$(top_srcdir)/cpio/test \
+	-I$(top_builddir)/cpio/test \
 	$(PLATFORMCPPFLAGS)
 bsdcpio_test_LDADD=libarchive_fe.la
 
@@ -1216,7 +1232,6 @@
 
 bsdcat_test_SOURCES= \
 	$(test_utils_SOURCES) \
-	cat/test/main.c \
 	cat/test/test.h \
 	cat/test/test_0.c \
 	cat/test/test_empty_gz.c \
@@ -1237,7 +1252,8 @@
 bsdcat_test_CPPFLAGS= \
 	-I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe \
 	-I$(top_srcdir)/test_utils \
-	-I$(top_srcdir)/cat -I$(top_builddir)/cat/test \
+	-I$(top_srcdir)/cat -I$(top_srcdir)/cat/test \
+	-I$(top_builddir)/cat/test \
 	$(PLATFORMCPPFLAGS)
 bsdcat_test_LDADD=libarchive_fe.la
 
diff --git a/NEWS b/NEWS
index 6f16b88..fdd971d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,18 @@
+Feb 26, 2017: libarchive 3.3.1 released
+    Security & Feature release
+
+Feb 19, 2017: libarchive 3.3.0 released
+    Security & Feature release
+
+Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin)
+
+Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates
+
+Dec 27, 2016: NFSv4 ACL read and write support for pax
+    Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w()
+
+Nov, 2016: libarchive is now being tested by the OSS-Fuzz project
+
 Oct 26, 2016: Remove liblzmadec support
 
 Oct 23, 2016: libarchive 3.2.2 released
@@ -278,7 +293,7 @@
 	* libarchive: Mark which entry strings are set; be accurate about
 	  distinguishing empty strings ("") from unset ones (NULL)
 	* tar: Don't crash reading entries with empty filenames
-	* libarchive_test, bsdtar_test, bsdcpio_test:  Better detaults:
+	* libarchive_test, bsdtar_test, bsdcpio_test:  Better defaults:
 	  run all tests, delete temp dirs, summarize repeated failures
 	* -no-undefined to libtool for Cygwin
 	* libarchive_test: Skip large file tests on systems with 32-bit off_t
diff --git a/build/autogen.sh b/build/autogen.sh
index 64767b6..e720e9b 100755
--- a/build/autogen.sh
+++ b/build/autogen.sh
@@ -51,6 +51,7 @@
 # Remove developer CFLAGS if a release build is being made
 if [ -n "${MAKE_LIBARCHIVE_RELEASE}" ]; then
   perl -p -i -e "s/^(DEV_CFLAGS.*)/# \$1/" Makefile.am
+  perl -p -i -e 's/CMAKE_BUILD_TYPE "[A-Za-z]*"/CMAKE_BUILD_TYPE "Release"/' CMakeLists.txt
 fi
 
 set -xe
diff --git a/build/ci_build.sh b/build/ci_build.sh
index ff8ef2d..65e5ceb 100755
--- a/build/ci_build.sh
+++ b/build/ci_build.sh
@@ -12,6 +12,8 @@
 
 ACTIONS=
 BUILD_SYSTEM="${BUILD_SYSTEM:-autotools}"
+MAKE="${MAKE:-make}"
+CMAKE="${CMAKE:-cmake}"
 CURDIR=`pwd`
 SRCDIR="${SRCDIR:-`pwd`}"
 RET=0
@@ -61,7 +63,7 @@
 	inputerror "Missing type (-t) parameter"
 fi
 if [ -z "${BUILDDIR}" ]; then
-	BUILDDIR="${CURDIR}/BUILD/${BUILD_SYSTEM}"
+	BUILDDIR="${CURDIR}/build_ci/${BUILD_SYSTEM}"
 fi
 mkdir -p "${BUILDDIR}"
 for action in ${ACTIONS}; do
@@ -79,24 +81,21 @@
 		configure)
 			case "${BUILD_SYSTEM}" in
 				autotools) "${SRCDIR}/configure" ${CONFIGURE_ARGS} ;;
-				cmake) cmake ${CONFIGURE_ARGS} "${SRCDIR}" ;;
+				cmake) ${CMAKE} ${CONFIGURE_ARGS} "${SRCDIR}" ;;
 			esac
 			RET="$?"
 		;;
 		build)
-			make ${MAKE_ARGS}
+			${MAKE} ${MAKE_ARGS}
 			RET="$?"
 		;;
 		test)
 			case "${BUILD_SYSTEM}" in
 				autotools)
-					if ! make ${MAKE_ARGS} check; then
-						cat test-suite.log
-						exit 1
-					fi
+					${MAKE} ${MAKE_ARGS} check LOG_DRIVER="${SRCDIR}/build/ci_test_driver"
 					;;
 				cmake)
-					make ${MAKE_ARGS} test
+					${MAKE} ${MAKE_ARGS} test
 					;;
 			esac
 			RET="$?"
diff --git a/build/ci_test_driver b/build/ci_test_driver
new file mode 100755
index 0000000..69a5463
--- /dev/null
+++ b/build/ci_test_driver
@@ -0,0 +1,148 @@
+#! /bin/sh
+# test-driver - basic testsuite driver script.
+
+scriptversion=2013-07-13.22; # UTC
+
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+# Make unconditional expansion of undefined variables an error.  This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+usage_error ()
+{
+  echo "$0: $*" >&2
+  print_usage >&2
+  exit 2
+}
+
+print_usage ()
+{
+  cat <<END
+Usage:
+  test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
+              [--expect-failure={yes|no}] [--color-tests={yes|no}]
+              [--enable-hard-errors={yes|no}] [--]
+              TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+END
+}
+
+test_name= # Used for reporting.
+log_file=  # Where to save the output of the test script.
+trs_file=  # Where to save the metadata of the test run.
+expect_failure=no
+color_tests=no
+enable_hard_errors=yes
+while test $# -gt 0; do
+  case $1 in
+  --help) print_usage; exit $?;;
+  --version) echo "test-driver $scriptversion"; exit $?;;
+  --test-name) test_name=$2; shift;;
+  --log-file) log_file=$2; shift;;
+  --trs-file) trs_file=$2; shift;;
+  --color-tests) color_tests=$2; shift;;
+  --expect-failure) expect_failure=$2; shift;;
+  --enable-hard-errors) enable_hard_errors=$2; shift;;
+  --) shift; break;;
+  -*) usage_error "invalid option: '$1'";;
+   *) break;;
+  esac
+  shift
+done
+
+missing_opts=
+test x"$test_name" = x && missing_opts="$missing_opts --test-name"
+test x"$log_file"  = x && missing_opts="$missing_opts --log-file"
+test x"$trs_file"  = x && missing_opts="$missing_opts --trs-file"
+if test x"$missing_opts" != x; then
+  usage_error "the following mandatory options are missing:$missing_opts"
+fi
+
+if test $# -eq 0; then
+  usage_error "missing argument"
+fi
+
+if test $color_tests = yes; then
+  # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
+  red='' # Red.
+  grn='' # Green.
+  lgn='' # Light green.
+  blu='' # Blue.
+  mgn='' # Magenta.
+  std=''     # No color.
+else
+  red= grn= lgn= blu= mgn= std=
+fi
+
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
+trap "st=129; $do_exit" 1
+trap "st=130; $do_exit" 2
+trap "st=141; $do_exit" 13
+trap "st=143; $do_exit" 15
+
+# Test script is run here.
+( "$@"; echo "$?" > $log_file.s ) | tee $log_file 2>&1
+estatus=`cat $log_file.s`
+
+if test $enable_hard_errors = no && test $estatus -eq 99; then
+  tweaked_estatus=1
+else
+  tweaked_estatus=$estatus
+fi
+
+case $tweaked_estatus:$expect_failure in
+  0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
+  0:*)   col=$grn res=PASS  recheck=no  gcopy=no;;
+  77:*)  col=$blu res=SKIP  recheck=no  gcopy=yes;;
+  99:*)  col=$mgn res=ERROR recheck=yes gcopy=yes;;
+  *:yes) col=$lgn res=XFAIL recheck=no  gcopy=yes;;
+  *:*)   col=$red res=FAIL  recheck=yes gcopy=yes;;
+esac
+
+# Report the test outcome and exit status in the logs, so that one can
+# know whether the test passed or failed simply by looking at the '.log'
+# file, without the need of also peaking into the corresponding '.trs'
+# file (automake bug#11814).
+echo "$res $test_name (exit status: $estatus)" >>$log_file
+
+# Report outcome to console.
+echo "${col}${res}${std}: $test_name"
+
+# Register the test result, and other relevant metadata.
+echo ":test-result: $res" > $trs_file
+echo ":global-test-result: $res" >> $trs_file
+echo ":recheck: $recheck" >> $trs_file
+echo ":copy-in-global-log: $gcopy" >> $trs_file
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in
index 053d205..8c18edf 100644
--- a/build/cmake/config.h.in
+++ b/build/cmake/config.h.in
@@ -326,9 +326,6 @@
 /* Define to 1 if you have the `acl_set_file' function. */
 #cmakedefine HAVE_ACL_SET_FILE 1
 
-/* True for systems with POSIX ACL support */
-#cmakedefine HAVE_ACL_USER 1
-
 /* Define to 1 if you have the `arc4random_buf' function. */
 #cmakedefine HAVE_ARC4RANDOM_BUF 1
 
@@ -365,6 +362,34 @@
 /* Define to 1 if you have the `cygwin_conv_path' function. */
 #cmakedefine HAVE_CYGWIN_CONV_PATH 1
 
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_GETACL 1
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_GETACLCNT 1
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACE_SETACL 1
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+   you don't. */
+#cmakedefine HAVE_DECL_ACL_SYNCHRONIZE 1
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+   you don't. */
+#cmakedefine HAVE_DECL_ACL_TYPE_EXTENDED 1
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACL_TYPE_NFS4 1
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_ACL_USER 1
+
 /* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
    don't. */
 #cmakedefine HAVE_DECL_INT32_MAX 1
@@ -389,6 +414,10 @@
    don't. */
 #cmakedefine HAVE_DECL_INTMAX_MIN 1
 
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+   */
+#cmakedefine HAVE_DECL_SETACL 1
+
 /* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
    don't. */
 #cmakedefine HAVE_DECL_SIZE_MAX 1
@@ -462,6 +491,14 @@
 /* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */
 #cmakedefine HAVE_DECL_EXTATTR_NAMESPACE_USER 1
 
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+   */
+#cmakedefine HAVE_DECL_GETACL 1
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+   don't. */
+#cmakedefine HAVE_DECL_GETACLCNT 1
+
 /* Define to 1 if you have the `fchdir' function. */
 #cmakedefine HAVE_FCHDIR 1
 
@@ -736,6 +773,9 @@
 /* Define to 1 if you have the `mbrtowc' function. */
 #cmakedefine HAVE_MBRTOWC 1
 
+/* Define to 1 if you have the <membership.h> header file. */
+#cmakedefine HAVE_MEMBERSHIP_H 1
+
 /* Define to 1 if you have the `memmove' function. */
 #cmakedefine HAVE_MEMMOVE 1
 
@@ -1093,6 +1133,9 @@
 /* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
 #cmakedefine HAVE_WORKING_EXT2_IOC_GETFLAGS 1
 
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+#cmakedefine HAVE_WORKING_FS_IOC_GETFLAGS 1
+
 /* Define to 1 if you have the <zlib.h> header file. */
 #cmakedefine HAVE_ZLIB_H 1
 
diff --git a/build/version b/build/version
index f54ddaa..6d36e9c 100644
--- a/build/version
+++ b/build/version
@@ -1 +1 @@
-3002002
+3003002dev
diff --git a/cat/test/CMakeLists.txt b/cat/test/CMakeLists.txt
index c4642ae..0623cc3 100644
--- a/cat/test/CMakeLists.txt
+++ b/cat/test/CMakeLists.txt
@@ -6,7 +6,7 @@
 IF(ENABLE_CAT AND ENABLE_TEST)
   SET(bsdcat_test_SOURCES
     ../../test_utils/test_utils.c
-    main.c
+    ../../test_utils/test_main.c
     test.h
     test_0.c
     test_empty_gz.c
@@ -29,6 +29,11 @@
   # Register target
   #
   ADD_EXECUTABLE(bsdcat_test ${bsdcat_test_SOURCES})
+  IF(ENABLE_ACL)
+    IF(HAVE_LIBACL)
+      TARGET_LINK_LIBRARIES(bsdcat_test ${ACL_LIBRARY})
+    ENDIF(HAVE_LIBACL)
+  ENDIF(ENABLE_ACL)
   SET_PROPERTY(TARGET bsdcat_test PROPERTY COMPILE_DEFINITIONS LIST_H)
 
   #
@@ -54,6 +59,7 @@
   INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h)
   INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils)
+  INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cat/test)
 
   # Experimental new test handling
   ADD_CUSTOM_TARGET(run_bsdcat_test
diff --git a/cat/test/test.h b/cat/test/test.h
index 3a23a05..350bcad 100644
--- a/cat/test/test.h
+++ b/cat/test/test.h
@@ -22,333 +22,19 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/tar/test/test.h,v 1.4 2008/08/21 07:04:57 kientzle Exp $
+ * $FreeBSD$
  */
 
 /* Every test program should #include "test.h" as the first thing. */
 
-/*
- * The goal of this file (and the matching test.c) is to
- * simplify the very repetitive test-*.c test programs.
- */
-#if defined(HAVE_CONFIG_H)
-/* Most POSIX platforms use the 'configure' script to build config.h */
-#include "config.h"
-#elif defined(__FreeBSD__)
-/* Building as part of FreeBSD system requires a pre-built config.h. */
-#include "config_freebsd.h"
-#elif defined(_WIN32) && !defined(__CYGWIN__)
-/* Win32 can't run the 'configure' script. */
-#include "config_windows.h"
-#else
-/* Warn if the library hasn't been (automatically or manually) configured. */
-#error Oops: No config.h and no pre-built configuration in test.h.
-#endif
+#define KNOWNREF       "test_expand.Z.uu"
+#define ENVBASE "BSDCAT"  /* Prefix for environment variables. */
+#define	PROGRAM "bsdcat"  /* Name of program being tested. */
+#define PROGRAM_ALIAS "cat" /* Generic alias for program */
+#undef	LIBRARY		  /* Not testing a library. */
+#undef	EXTRA_DUMP	  /* How to dump extra data */
+#undef	EXTRA_ERRNO	  /* How to dump errno */
+/* How to generate extra version info. */
+#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
 
-#include <sys/types.h>  /* Windows requires this before sys/stat.h */
-#include <sys/stat.h>
-
-#if HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-#ifdef HAVE_DIRECT_H
-#include <direct.h>
-#define dirent direct
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_IO_H
-#include <io.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <wchar.h>
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-/*
- * System-specific tweaks.  We really want to minimize these
- * as much as possible, since they make it harder to understand
- * the mainline code.
- */
-
-/* Windows (including Visual Studio and MinGW but not Cygwin) */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#if !defined(__BORLANDC__)
-#undef chdir
-#define chdir _chdir
-#define strdup _strdup
-#endif
-#endif
-
-/* Visual Studio */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#define snprintf	sprintf_s
-#endif
-
-#if defined(__BORLANDC__)
-#pragma warn -8068	/* Constant out of range in comparison. */
-#endif
-
-/* Haiku OS and QNX */
-#if defined(__HAIKU__) || defined(__QNXNTO__)
-/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
-#include <stdint.h>
-#endif
-
-/* Get a real definition for __FBSDID if we can */
-#if HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-
-/* If not, define it so as to avoid dangling semicolons. */
-#ifndef __FBSDID
-#define	__FBSDID(a)     struct _undefined_hack
-#endif
-
-#ifndef O_BINARY
-#define	O_BINARY 0
-#endif
-
-/*
- * Redefine DEFINE_TEST for use in defining the test functions.
- */
-#undef DEFINE_TEST
-#define DEFINE_TEST(name) void name(void); void name(void)
-
-/* An implementation of the standard assert() macro */
-#define assert(e)   assertion_assert(__FILE__, __LINE__, (e), #e, NULL)
-/* chdir() and error if it fails */
-#define assertChdir(path)  \
-  assertion_chdir(__FILE__, __LINE__, path)
-/* Assert two integers are the same.  Reports value of each one if not. */
-#define assertEqualInt(v1,v2) \
-  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* Assert two strings are the same.  Reports value of each one if not. */
-#define assertEqualString(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
-#define assertEqualUTF8String(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
-/* As above, but v1 and v2 are wchar_t * */
-#define assertEqualWString(v1,v2)   \
-  assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* As above, but raw blocks of bytes. */
-#define assertEqualMem(v1, v2, l)	\
-  assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
-/* Assert that memory is full of a specified byte */
-#define assertMemoryFilledWith(v1, l, b)					\
-  assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL)
-/* Assert two files are the same. */
-#define assertEqualFile(f1, f2)	\
-  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
-/* Assert that a file is empty. */
-#define assertEmptyFile(pathname)	\
-  assertion_empty_file(__FILE__, __LINE__, (pathname))
-/* Assert that a file is not empty. */
-#define assertNonEmptyFile(pathname)		\
-  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
-#define assertFileAtime(pathname, sec, nsec)	\
-  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileAtimeRecent(pathname)	\
-  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
-#define assertFileBirthtime(pathname, sec, nsec)	\
-  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileBirthtimeRecent(pathname) \
-  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
-/* Assert that a file exists; supports printf-style arguments. */
-#define assertFileExists(pathname) \
-  assertion_file_exists(__FILE__, __LINE__, pathname)
-/* Assert that a file exists. */
-#define assertFileNotExists(pathname) \
-  assertion_file_not_exists(__FILE__, __LINE__, pathname)
-/* Assert that file contents match a string. */
-#define assertFileContents(data, data_size, pathname) \
-  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
-/* Verify that a file does not contain invalid strings */
-#define assertFileContainsNoInvalidStrings(pathname, strings) \
-  assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
-#define assertFileMtime(pathname, sec, nsec)	\
-  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileMtimeRecent(pathname) \
-  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
-#define assertFileNLinks(pathname, nlinks)  \
-  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)	\
-  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
-#define assertIsDir(pathname, mode)		\
-  assertion_is_dir(__FILE__, __LINE__, pathname, mode)
-#define assertIsHardlink(path1, path2)	\
-  assertion_is_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsNotHardlink(path1, path2)	\
-  assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsReg(pathname, mode)		\
-  assertion_is_reg(__FILE__, __LINE__, pathname, mode)
-#define assertIsSymlink(pathname, contents)	\
-  assertion_is_symlink(__FILE__, __LINE__, pathname, contents)
-/* Create a directory, report error if it fails. */
-#define assertMakeDir(dirname, mode)	\
-  assertion_make_dir(__FILE__, __LINE__, dirname, mode)
-#define assertMakeFile(path, mode, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents)
-#define assertMakeBinFile(path, mode, csize, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
-#define assertMakeHardlink(newfile, oldfile)	\
-  assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto)	\
-  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
-#define assertNodump(path)      \
-  assertion_nodump(__FILE__, __LINE__, path)
-#define assertUmask(mask)	\
-  assertion_umask(__FILE__, __LINE__, mask)
-#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
-  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
-
-/*
- * This would be simple with C99 variadic macros, but I don't want to
- * require that.  Instead, I insert a function call before each
- * skipping() call to pass the file and line information down.  Crude,
- * but effective.
- */
-#define skipping	\
-  skipping_setup(__FILE__, __LINE__);test_skipping
-
-/* Function declarations.  These are defined in test_utility.c. */
-void failure(const char *fmt, ...);
-int assertion_assert(const char *, int, int, const char *, void *);
-int assertion_chdir(const char *, int, const char *);
-int assertion_empty_file(const char *, int, const char *);
-int assertion_equal_file(const char *, int, const char *, const char *);
-int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
-int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
-int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *);
-int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
-int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
-int assertion_file_atime(const char *, int, const char *, long, long);
-int assertion_file_atime_recent(const char *, int, const char *);
-int assertion_file_birthtime(const char *, int, const char *, long, long);
-int assertion_file_birthtime_recent(const char *, int, const char *);
-int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
-int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
-int assertion_file_contents(const char *, int, const void *, int, const char *);
-int assertion_file_exists(const char *, int, const char *);
-int assertion_file_mode(const char *, int, const char *, int);
-int assertion_file_mtime(const char *, int, const char *, long, long);
-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_is_dir(const char *, int, const char *, int);
-int assertion_is_hardlink(const char *, int, const char *, const char *);
-int assertion_is_not_hardlink(const char *, int, const char *, const char *);
-int assertion_is_reg(const char *, int, const char *, int);
-int assertion_is_symlink(const char *, int, const char *, const char *);
-int assertion_make_dir(const char *, int, const char *, int);
-int assertion_make_file(const char *, int, const char *, int, int, const void *);
-int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
-int assertion_nodump(const char *, int, const char *);
-int assertion_non_empty_file(const char *, int, const char *);
-int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
-int assertion_umask(const char *, int, int);
-int assertion_utimes(const char *, int, const char *, long, long, long, long );
-
-void skipping_setup(const char *, int);
-void test_skipping(const char *fmt, ...);
-
-/* Like sprintf, then system() */
-int systemf(const char * fmt, ...);
-
-/* Delay until time() returns a value after this. */
-void sleepUntilAfter(time_t);
-
-/* Return true if this platform can create symlinks. */
-int canSymlink(void);
-
-/* Return true if this platform can run the "bzip2" program. */
-int canBzip2(void);
-
-/* Return true if this platform can run the "grzip" program. */
-int canGrzip(void);
-
-/* Return true if this platform can run the "gzip" program. */
-int canGzip(void);
-
-/* Return true if this platform can run the specified command. */
-int canRunCommand(const char *);
-
-/* Return true if this platform can run the "lrzip" program. */
-int canLrzip(void);
-
-/* Return true if this platform can run the "lz4" program. */
-int canLz4(void);
-
-/* Return true if this platform can run the "lzip" program. */
-int canLzip(void);
-
-/* Return true if this platform can run the "lzma" program. */
-int canLzma(void);
-
-/* Return true if this platform can run the "lzop" program. */
-int canLzop(void);
-
-/* Return true if this platform can run the "xz" program. */
-int canXz(void);
-
-/* Return true if this filesystem can handle nodump flags. */
-int canNodump(void);
-
-/* Return true if the file has large i-node number(>0xffffffff). */
-int is_LargeInode(const char *);
-
-/* Suck file into string allocated via malloc(). Call free() when done. */
-/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
-char *slurpfile(size_t *, const char *fmt, ...);
-
-/* Dump block of bytes to a file. */
-void dumpfile(const char *filename, void *, size_t);
-
-/* Extracts named reference file to the current directory. */
-void extract_reference_file(const char *);
-/* Copies named reference file to the current directory. */
-void copy_reference_file(const char *);
-
-/* Extracts a list of files to the current directory.
- * List must be NULL terminated.
- */
-void extract_reference_files(const char **);
-
-/* Subtract umask from mode */
-mode_t umasked(mode_t expected_mode);
-
-/* Path to working directory for current test */
-extern const char *testworkdir;
-
-/*
- * Special interfaces for program test harness.
- */
-
-/* Pathname of exe to be tested. */
-extern const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-extern const char *testprog;
-
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
+#include "test_common.h"
diff --git a/cat/test/test_version.c b/cat/test/test_version.c
index e587b34..51a4fd4 100644
--- a/cat/test/test_version.c
+++ b/cat/test/test_version.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,68 +30,5 @@
 
 DEFINE_TEST(test_version)
 {
-	int r;
-	char *p, *q;
-	size_t s;
-
-
-	r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
-	failure("Unable to run %s --version", testprog);
-	if (!assert(r == 0))
-		return;
-
-	/* --version should generate nothing to stdout. */
-	assertEmptyFile("version.stderr");
-	/* Verify format of version message. */
-	q = p = slurpfile(&s, "version.stdout");
-	/* Version message should start with name of program, then space. */
-	assert(s > 6);
-	failure("Version must start with 'bsdcat': ``%s''", p);
-	if (!assertEqualMem(q, "bsdcat ", 7))
-		return;
-	q += 7; s -= 7;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Version number terminated by space. */
-	failure("No space after bsdcat version: ``%s''", p);
-	assert(s > 1);
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	failure("No space after bsdcat version: ``%s''", p);
-	assert(*q == ' ');
-	++q; --s;
-	/* Separator. */
-	failure("No `-' between bsdcat and libarchive versions: ``%s''", p);
-	assertEqualMem(q, "- ", 2);
-	q += 2; s -= 2;
-	/* libarchive name and version number */
-	failure("Not long enough for libarchive version: ``%s''", p);
-	assert(s > 11);
-	failure("Libarchive version must start with `libarchive': ``%s''", p);
-	assertEqualMem(q, "libarchive ", 11);
-	q += 11; s -= 11;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	/* Skip arbitrary third-party version numbers. */
-	while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
-		++q;
-		--s;
-	}
-	/* All terminated by end-of-line. */
-	assert(s >= 1);
-	/* Skip an optional CR character (e.g., Windows) */
-	failure("Version output must end with \\n or \\r\\n");
-	if (*q == '\r') { ++q; --s; }
-	assertEqualMem(q, "\n", 1);
-	free(p);
+	assertVersion(testprog, "bsdcat");
 }
diff --git a/configure.ac b/configure.ac
index c35adc1..ef2b3b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,8 +4,8 @@
 dnl In particular, this allows the version macro to be used in AC_INIT
 
 dnl These first two version numbers are updated automatically on each release.
-m4_define([LIBARCHIVE_VERSION_S],[3.2.2])
-m4_define([LIBARCHIVE_VERSION_N],[3002002])
+m4_define([LIBARCHIVE_VERSION_S],[3.3.2dev])
+m4_define([LIBARCHIVE_VERSION_N],[3003002])
 
 dnl bsdtar and bsdcpio versioning tracks libarchive
 m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S())
@@ -270,6 +270,19 @@
 
 AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h])
 AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h])
+
+AC_CACHE_CHECK([whether FS_IOC_GETFLAGS is usable],
+    [ac_cv_have_decl_FS_IOC_GETFLAGS],
+    [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include <sys/ioctl.h>
+@%:@include <linux/fs.h>],
+                                   [int x = FS_IOC_GETFLAGS])],
+                  [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes])],
+                  [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [no])])])
+
+AS_VAR_IF([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes],
+    [AC_DEFINE_UNQUOTED([HAVE_WORKING_FS_IOC_GETFLAGS], [1],
+                    [Define to 1 if you have a working FS_IOC_GETFLAGS])])
+
 AC_CHECK_HEADERS([locale.h paths.h poll.h pthread.h pwd.h])
 AC_CHECK_HEADERS([readpassphrase.h signal.h spawn.h])
 AC_CHECK_HEADERS([stdarg.h stdint.h stdlib.h string.h])
@@ -378,9 +391,9 @@
 fi
 
 AC_ARG_WITH([lzo2],
-  AS_HELP_STRING([--without-lzo2], [Don't build support for lzop through liblzo2]))
+  AS_HELP_STRING([--with-lzo2], [Build with LZO support from liblzo2]))
 
-if test "x$with_lzo2" != "xno"; then
+if test "x$with_lzo2" = "xyes"; then
   AC_CHECK_HEADERS([lzo/lzoconf.h lzo/lzo1x.h])
   AC_CHECK_LIB(lzo2,lzo1x_decompress_safe)
 fi
@@ -688,6 +701,7 @@
 if test "x$enable_acl" != "xno"; then
    AC_CHECK_HEADERS([acl/libacl.h])
    AC_CHECK_HEADERS([sys/acl.h])
+   AC_CHECK_HEADERS([membership.h])
    AC_CHECK_LIB([acl],[acl_get_file])
    AC_CHECK_FUNCS([acl_create_entry acl_get_fd_np])
    AC_CHECK_FUNCS([acl_init acl_set_fd acl_set_fd_np acl_set_file])
@@ -726,12 +740,23 @@
 	#endif
 	])
 
-    # MacOS has an acl.h that isn't POSIX.  It can be detected by
-    # checking for ACL_USER
-    AC_CHECK_DECL([ACL_USER],
-		[AC_DEFINE(HAVE_ACL_USER, 1, [True for systems with POSIX ACL support])],
-		[],
-		[#include <sys/acl.h>])
+    # FreeBSD and POSIX
+    # MacOS has no ACL_USER in acl.h
+    AC_CHECK_DECLS([ACL_TYPE_NFS4, ACL_USER],
+		[], [],
+		[#include <sys/types.h>
+		#include <sys/acl.h>])
+
+    # FreeBSD and MacOS ACL support
+    AC_CHECK_DECLS([ACL_TYPE_EXTENDED, ACL_SYNCHRONIZE], [], [],
+		[#include <sys/types.h>
+		#include <sys/acl.h>])
+
+    # Solaris and derivates ACLs
+    AC_CHECK_TYPES([aclent_t], [], [], [[#include <sys/acl.h>]])
+    AC_CHECK_TYPES([ace_t], [], [], [[#include <sys/acl.h>]])
+    AC_CHECK_FUNCS(acl facl)
+    AC_CHECK_DECLS([GETACL, SETACL, GETACLCNT, ACE_GETACL, ACE_SETACL, ACE_GETACLCNT], [], [], [#include <sys/acl.h>])
 fi
 
 # Additional requirements
diff --git a/cpio/cpio.c b/cpio/cpio.c
index 373e6da..d7bb9c4 100644
--- a/cpio/cpio.c
+++ b/cpio/cpio.c
@@ -108,22 +108,22 @@
 static int	file_to_archive(struct cpio *, const char *);
 static void	free_cache(struct name_cache *cache);
 static void	list_item_verbose(struct cpio *, struct archive_entry *);
-static void	long_help(void);
+static void	long_help(void) __LA_DEAD;
 static const char *lookup_gname(struct cpio *, gid_t gid);
 static int	lookup_gname_helper(struct cpio *,
 		    const char **name, id_t gid);
 static const char *lookup_uname(struct cpio *, uid_t uid);
 static int	lookup_uname_helper(struct cpio *,
 		    const char **name, id_t uid);
-static void	mode_in(struct cpio *);
-static void	mode_list(struct cpio *);
+static void	mode_in(struct cpio *) __LA_DEAD;
+static void	mode_list(struct cpio *) __LA_DEAD;
 static void	mode_out(struct cpio *);
 static void	mode_pass(struct cpio *, const char *);
 static const char *remove_leading_slash(const char *);
 static int	restore_time(struct cpio *, struct archive_entry *,
 		    const char *, int fd);
-static void	usage(void);
-static void	version(void);
+static void	usage(void) __LA_DEAD;
+static void	version(void) __LA_DEAD;
 static const char * passphrase_callback(struct archive *, void *);
 static void	passphrase_free(char *);
 
@@ -703,6 +703,7 @@
 		lafe_warnc(0, "%s",
 		    archive_error_string(cpio->archive_read_disk));
 	if (r <= ARCHIVE_FAILED) {
+		archive_entry_free(entry);
 		cpio->return_value = 1;
 		return (r);
 	}
diff --git a/cpio/test/CMakeLists.txt b/cpio/test/CMakeLists.txt
index 8ffb542..cc5fe01 100644
--- a/cpio/test/CMakeLists.txt
+++ b/cpio/test/CMakeLists.txt
@@ -8,7 +8,7 @@
     ../cmdline.c
     ../../libarchive_fe/err.c
     ../../test_utils/test_utils.c
-    main.c
+    ../../test_utils/test_main.c
     test.h
     test_0.c
     test_basic.c
@@ -62,6 +62,11 @@
   # Register target
   #
   ADD_EXECUTABLE(bsdcpio_test ${bsdcpio_test_SOURCES})
+  IF(ENABLE_ACL)
+    IF(HAVE_LIBACL)
+      TARGET_LINK_LIBRARIES(bsdcpio_test ${ACL_LIBRARY})
+    ENDIF(HAVE_LIBACL)
+  ENDIF(ENABLE_ACL)
   SET_PROPERTY(TARGET bsdcpio_test PROPERTY COMPILE_DEFINITIONS LIST_H)
 
   #
@@ -87,6 +92,7 @@
   INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h)
   INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils)
+  INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cpio/test)
 
   # Experimental new test handling
   ADD_CUSTOM_TARGET(run_bsdcpio_test
diff --git a/cpio/test/main.c b/cpio/test/main.c
deleted file mode 100644
index 34282f3..0000000
--- a/cpio/test/main.c
+++ /dev/null
@@ -1,3073 +0,0 @@
-/*
- * Copyright (c) 2003-2009 Tim Kientzle
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "test.h"
-#include "test_utils.h"
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#include <errno.h>
-#ifdef HAVE_ICONV_H
-#include <iconv.h>
-#endif
-/*
- * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
- * As the include guards don't agree, the order of include is important.
- */
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h>      /* for Linux file flags */
-#endif
-#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
-#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
-#endif
-#include <limits.h>
-#include <locale.h>
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include <stdarg.h>
-#include <time.h>
-
-/*
- * This same file is used pretty much verbatim for all test harnesses.
- *
- * The next few lines are the only differences.
- * TODO: Move this into a separate configuration header, have all test
- * suites share one copy of this file.
- */
-__FBSDID("$FreeBSD: src/usr.bin/cpio/test/main.c,v 1.3 2008/08/24 04:58:22 kientzle Exp $");
-#define KNOWNREF	"test_option_f.cpio.uu"
-#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */
-#define	PROGRAM "bsdcpio" /* Name of program being tested. */
-#define PROGRAM_ALIAS "cpio" /* Generic alias for program */
-#undef	LIBRARY		  /* Not testing a library. */
-#undef	EXTRA_DUMP	  /* How to dump extra data */
-#undef	EXTRA_ERRNO	  /* How to dump errno */
-/* How to generate extra version info. */
-#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
-
-/*
- *
- * Windows support routines
- *
- * Note: Configuration is a tricky issue.  Using HAVE_* feature macros
- * in the test harness is dangerous because they cover up
- * configuration errors.  The classic example of this is omitting a
- * configure check.  If libarchive and libarchive_test both look for
- * the same feature macro, such errors are hard to detect.  Platform
- * macros (e.g., _WIN32 or __GNUC__) are a little better, but can
- * easily lead to very messy code.  It's best to limit yourself
- * to only the most generic programming techniques in the test harness
- * and thus avoid conditionals altogether.  Where that's not possible,
- * try to minimize conditionals by grouping platform-specific tests in
- * one place (e.g., test_acl_freebsd) or by adding new assert()
- * functions (e.g., assertMakeHardlink()) to cover up platform
- * differences.  Platform-specific coding in libarchive_test is often
- * a symptom that some capability is missing from libarchive itself.
- */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#include <io.h>
-#include <direct.h>
-#include <windows.h>
-#ifndef F_OK
-#define F_OK (0)
-#endif
-#ifndef S_ISDIR
-#define S_ISDIR(m)  ((m) & _S_IFDIR)
-#endif
-#ifndef S_ISREG
-#define S_ISREG(m)  ((m) & _S_IFREG)
-#endif
-#if !defined(__BORLANDC__)
-#define access _access
-#undef chdir
-#define chdir _chdir
-#endif
-#ifndef fileno
-#define fileno _fileno
-#endif
-/*#define fstat _fstat64*/
-#if !defined(__BORLANDC__)
-#define getcwd _getcwd
-#endif
-#define lstat stat
-/*#define lstat _stat64*/
-/*#define stat _stat64*/
-#define rmdir _rmdir
-#if !defined(__BORLANDC__)
-#define strdup _strdup
-#define umask _umask
-#endif
-#define int64_t __int64
-#endif
-
-#if defined(HAVE__CrtSetReportMode)
-# include <crtdbg.h>
-#endif
-
-mode_t umasked(mode_t expected_mode)
-{
-	mode_t mode = umask(0);
-	umask(mode);
-	return expected_mode & ~mode;
-}
-
-/* Path to working directory for current test */
-const char *testworkdir;
-#ifdef PROGRAM
-/* Pathname of exe to be tested. */
-const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-const char *testprog;
-#endif
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static void	*GetFunctionKernel32(const char *);
-static int	 my_CreateSymbolicLinkA(const char *, const char *, int);
-static int	 my_CreateHardLinkA(const char *, const char *);
-static int	 my_GetFileInformationByName(const char *,
-		     BY_HANDLE_FILE_INFORMATION *);
-
-static void *
-GetFunctionKernel32(const char *name)
-{
-	static HINSTANCE lib;
-	static int set;
-	if (!set) {
-		set = 1;
-		lib = LoadLibrary("kernel32.dll");
-	}
-	if (lib == NULL) {
-		fprintf(stderr, "Can't load kernel32.dll?!\n");
-		exit(1);
-	}
-	return (void *)GetProcAddress(lib, name);
-}
-
-static int
-my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateSymbolicLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, flags);
-}
-
-static int
-my_CreateHardLinkA(const char *linkname, const char *target)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateHardLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, NULL);
-}
-
-static int
-my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi)
-{
-	HANDLE h;
-	int r;
-
-	memset(bhfi, 0, sizeof(*bhfi));
-	h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL,
-		OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE)
-		return (0);
-	r = GetFileInformationByHandle(h, bhfi);
-	CloseHandle(h);
-	return (r);
-}
-#endif
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-static void
-invalid_parameter_handler(const wchar_t * expression,
-    const wchar_t * function, const wchar_t * file,
-    unsigned int line, uintptr_t pReserved)
-{
-	/* nop */
-}
-#endif
-
-/*
- *
- * OPTIONS FLAGS
- *
- */
-
-/* Enable core dump on failure. */
-static int dump_on_failure = 0;
-/* Default is to remove temp dirs and log data for successful tests. */
-static int keep_temp_files = 0;
-/* Default is to run the specified tests once and report errors. */
-static int until_failure = 0;
-/* Default is to just report pass/fail for each test. */
-static int verbosity = 0;
-#define	VERBOSITY_SUMMARY_ONLY -1 /* -q */
-#define VERBOSITY_PASSFAIL 0   /* Default */
-#define VERBOSITY_LIGHT_REPORT 1 /* -v */
-#define VERBOSITY_FULL 2 /* -vv */
-/* A few places generate even more output for verbosity > VERBOSITY_FULL,
- * mostly for debugging the test harness itself. */
-/* Cumulative count of assertion failures. */
-static int failures = 0;
-/* Cumulative count of reported skips. */
-static int skips = 0;
-/* Cumulative count of assertions checked. */
-static int assertions = 0;
-
-/* Directory where uuencoded reference files can be found. */
-static const char *refdir;
-
-/*
- * Report log information selectively to console and/or disk log.
- */
-static int log_console = 0;
-static FILE *logfile;
-static void
-vlogprintf(const char *fmt, va_list ap)
-{
-#ifdef va_copy
-	va_list lfap;
-	va_copy(lfap, ap);
-#endif
-	if (log_console)
-		vfprintf(stdout, fmt, ap);
-	if (logfile != NULL)
-#ifdef va_copy
-		vfprintf(logfile, fmt, lfap);
-	va_end(lfap);
-#else
-		vfprintf(logfile, fmt, ap);
-#endif
-}
-
-static void
-logprintf(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	vlogprintf(fmt, ap);
-	va_end(ap);
-}
-
-/* Set up a message to display only if next assertion fails. */
-static char msgbuff[4096];
-static const char *msg, *nextmsg;
-void
-failure(const char *fmt, ...)
-{
-	va_list ap;
-	if (fmt == NULL) {
-		nextmsg = NULL;
-	} else {
-		va_start(ap, fmt);
-		vsprintf(msgbuff, fmt, ap);
-		va_end(ap);
-		nextmsg = msgbuff;
-	}
-}
-
-/*
- * Copy arguments into file-local variables.
- * This was added to permit vararg assert() functions without needing
- * variadic wrapper macros.  Turns out that the vararg capability is almost
- * never used, so almost all of the vararg assertions can be simplified
- * by removing the vararg capability and reworking the wrapper macro to
- * pass __FILE__, __LINE__ directly into the function instead of using
- * this hook.  I suspect this machinery is used so rarely that we
- * would be better off just removing it entirely.  That would simplify
- * the code here noticeably.
- */
-static const char *skipping_filename;
-static int skipping_line;
-void skipping_setup(const char *filename, int line)
-{
-	skipping_filename = filename;
-	skipping_line = line;
-}
-
-/* Called at the beginning of each assert() function. */
-static void
-assertion_count(const char *file, int line)
-{
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	++assertions;
-	/* Proper handling of "failure()" message. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* Uncomment to print file:line after every assertion.
-	 * Verbose, but occasionally useful in tracking down crashes. */
-	/* printf("Checked %s:%d\n", file, line); */
-}
-
-/*
- * For each test source file, we remember how many times each
- * assertion was reported.  Cleared before each new test,
- * used by test_summarize().
- */
-static struct line {
-	int count;
-	int skip;
-}  failed_lines[10000];
-const char *failed_filename;
-
-/* Count this failure, setup up log destination and handle initial report. */
-static void
-failure_start(const char *filename, int line, const char *fmt, ...)
-{
-	va_list ap;
-
-	/* Record another failure for this line. */
-	++failures;
-	failed_filename = filename;
-	failed_lines[line].count++;
-
-	/* Determine whether to log header to console. */
-	switch (verbosity) {
-	case VERBOSITY_LIGHT_REPORT:
-		log_console = (failed_lines[line].count < 2);
-		break;
-	default:
-		log_console = (verbosity >= VERBOSITY_FULL);
-	}
-
-	/* Log file:line header for this failure */
-	va_start(ap, fmt);
-#if _MSC_VER
-	logprintf("%s(%d): ", filename, line);
-#else
-	logprintf("%s:%d: ", filename, line);
-#endif
-	vlogprintf(fmt, ap);
-	va_end(ap);
-	logprintf("\n");
-
-	if (msg != NULL && msg[0] != '\0') {
-		logprintf("   Description: %s\n", msg);
-		msg = NULL;
-	}
-
-	/* Determine whether to log details to console. */
-	if (verbosity == VERBOSITY_LIGHT_REPORT)
-		log_console = 0;
-}
-
-/* Complete reporting of failed tests. */
-/*
- * The 'extra' hook here is used by libarchive to include libarchive
- * error messages with assertion failures.  It could also be used
- * to add strerror() output, for example.  Just define the EXTRA_DUMP()
- * macro appropriately.
- */
-static void
-failure_finish(void *extra)
-{
-	(void)extra; /* UNUSED (maybe) */
-#ifdef EXTRA_DUMP
-	if (extra != NULL) {
-		logprintf("    errno: %d\n", EXTRA_ERRNO(extra));
-		logprintf("   detail: %s\n", EXTRA_DUMP(extra));
-	}
-#endif
-
-	if (dump_on_failure) {
-		fprintf(stderr,
-		    " *** forcing core dump so failure can be debugged ***\n");
-		abort();
-	}
-}
-
-/* Inform user that we're skipping some checks. */
-void
-test_skipping(const char *fmt, ...)
-{
-	char buff[1024];
-	va_list ap;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	va_end(ap);
-	/* Use failure() message if set. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* failure_start() isn't quite right, but is awfully convenient. */
-	failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff);
-	--failures; /* Undo failures++ in failure_start() */
-	/* Don't failure_finish() here. */
-	/* Mark as skip, so doesn't count as failed test. */
-	failed_lines[skipping_line].skip = 1;
-	++skips;
-}
-
-/*
- *
- * ASSERTIONS
- *
- */
-
-/* Generic assert() just displays the failed condition. */
-int
-assertion_assert(const char *file, int line, int value,
-    const char *condition, void *extra)
-{
-	assertion_count(file, line);
-	if (!value) {
-		failure_start(file, line, "Assertion failed: %s", condition);
-		failure_finish(extra);
-	}
-	return (value);
-}
-
-/* chdir() and report any errors */
-int
-assertion_chdir(const char *file, int line, const char *pathname)
-{
-	assertion_count(file, line);
-	if (chdir(pathname) == 0)
-		return (1);
-	failure_start(file, line, "chdir(\"%s\")", pathname);
-	failure_finish(NULL);
-	return (0);
-
-}
-
-/* Verify two integers are equal. */
-int
-assertion_equal_int(const char *file, int line,
-    long long v1, const char *e1, long long v2, const char *e2, void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Utility to convert a single UTF-8 sequence.
- */
-static int
-_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
-{
-	static const char utf8_count[256] = {
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
-		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
-		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
-		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
-		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
-	};
-	int ch;
-	int cnt;
-	uint32_t wc;
-
-	*pwc = 0;
-
-	/* Sanity check. */
-	if (n == 0)
-		return (0);
-	/*
-	 * Decode 1-4 bytes depending on the value of the first byte.
-	 */
-	ch = (unsigned char)*s;
-	if (ch == 0)
-		return (0); /* Standard:  return 0 for end-of-string. */
-	cnt = utf8_count[ch];
-
-	/* Invalid sequence or there are not plenty bytes. */
-	if (n < (size_t)cnt)
-		return (-1);
-
-	/* Make a Unicode code point from a single UTF-8 sequence. */
-	switch (cnt) {
-	case 1:	/* 1 byte sequence. */
-		*pwc = ch & 0x7f;
-		return (cnt);
-	case 2:	/* 2 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
-		return (cnt);
-	case 3:	/* 3 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x0f) << 12)
-		    | ((s[1] & 0x3f) << 6)
-		    | (s[2] & 0x3f);
-		if (wc < 0x800)
-			return (-1);/* Overlong sequence. */
-		break;
-	case 4:	/* 4 bytes sequence. */
-		if (n < 4)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		if ((s[3] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x07) << 18)
-		    | ((s[1] & 0x3f) << 12)
-		    | ((s[2] & 0x3f) << 6)
-		    | (s[3] & 0x3f);
-		if (wc < 0x10000)
-			return (-1);/* Overlong sequence. */
-		break;
-	default:
-		return (-1);
-	}
-
-	/* The code point larger than 0x10FFFF is not legal
-	 * Unicode values. */
-	if (wc > 0x10FFFF)
-		return (-1);
-	/* Correctly gets a Unicode, returns used bytes. */
-	*pwc = wc;
-	return (cnt);
-}
-
-static void strdump(const char *e, const char *p, int ewidth, int utf8)
-{
-	const char *q = p;
-
-	logprintf("      %*s = ", ewidth, e);
-	if (p == NULL) {
-		logprintf("NULL\n");
-		return;
-	}
-	logprintf("\"");
-	while (*p != '\0') {
-		unsigned int c = 0xff & *p++;
-		switch (c) {
-		case '\a': logprintf("\\a"); break;
-		case '\b': logprintf("\\b"); break;
-		case '\n': logprintf("\\n"); break;
-		case '\r': logprintf("\\r"); break;
-		default:
-			if (c >= 32 && c < 127)
-				logprintf("%c", c);
-			else
-				logprintf("\\x%02X", c);
-		}
-	}
-	logprintf("\"");
-	logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q));
-
-	/*
-	 * If the current string is UTF-8, dump its code points.
-	 */
-	if (utf8) {
-		size_t len;
-		uint32_t uc;
-		int n;
-		int cnt = 0;
-
-		p = q;
-		len = strlen(p);
-		logprintf(" [");
-		while ((n = _utf8_to_unicode(&uc, p, len)) > 0) {
-			if (p != q)
-				logprintf(" ");
-			logprintf("%04X", uc);
-			p += n;
-			len -= n;
-			cnt++;
-		}
-		logprintf("]");
-		logprintf(" (count %d", cnt);
-		if (n < 0) {
-			logprintf(",unknown %d bytes", len);
-		}
-		logprintf(")");
-
-	}
-	logprintf("\n");
-}
-
-/* Verify two strings are equal, dump them if not. */
-int
-assertion_equal_string(const char *file, int line,
-    const char *v1, const char *e1,
-    const char *v2, const char *e2,
-    void *extra, int utf8)
-{
-	int l1, l2;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0))
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	l1 = (int)strlen(e1);
-	l2 = (int)strlen(e2);
-	if (l1 < l2)
-		l1 = l2;
-	strdump(e1, v1, l1, utf8);
-	strdump(e2, v2, l1, utf8);
-	failure_finish(extra);
-	return (0);
-}
-
-static void
-wcsdump(const char *e, const wchar_t *w)
-{
-	logprintf("      %s = ", e);
-	if (w == NULL) {
-		logprintf("(null)");
-		return;
-	}
-	logprintf("\"");
-	while (*w != L'\0') {
-		unsigned int c = *w++;
-		if (c >= 32 && c < 127)
-			logprintf("%c", c);
-		else if (c < 256)
-			logprintf("\\x%02X", c);
-		else if (c < 0x10000)
-			logprintf("\\u%04X", c);
-		else
-			logprintf("\\U%08X", c);
-	}
-	logprintf("\"\n");
-}
-
-#ifndef HAVE_WCSCMP
-static int
-wcscmp(const wchar_t *s1, const wchar_t *s2)
-{
-
-	while (*s1 == *s2++) {
-		if (*s1++ == L'\0')
-			return 0;
-	}
-	if (*s1 > *--s2)
-		return 1;
-	else
-		return -1;
-}
-#endif
-
-/* Verify that two wide strings are equal, dump them if not. */
-int
-assertion_equal_wstring(const char *file, int line,
-    const wchar_t *v1, const char *e1,
-    const wchar_t *v2, const char *e2,
-    void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	wcsdump(e1, v1);
-	wcsdump(e2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
- * any bytes in p that differ from ref will be highlighted with '_'
- * before and after the hex value.
- */
-static void
-hexdump(const char *p, const char *ref, size_t l, size_t offset)
-{
-	size_t i, j;
-	char sep;
-
-	if (p == NULL) {
-		logprintf("(null)\n");
-		return;
-	}
-	for(i=0; i < l; i+=16) {
-		logprintf("%04x", (unsigned)(i + offset));
-		sep = ' ';
-		for (j = 0; j < 16 && i + j < l; j++) {
-			if (ref != NULL && p[i + j] != ref[i + j])
-				sep = '_';
-			logprintf("%c%02x", sep, 0xff & (int)p[i+j]);
-			if (ref != NULL && p[i + j] == ref[i + j])
-				sep = ' ';
-		}
-		for (; j < 16; j++) {
-			logprintf("%c  ", sep);
-			sep = ' ';
-		}
-		logprintf("%c", sep);
-		for (j=0; j < 16 && i + j < l; j++) {
-			int c = p[i + j];
-			if (c >= ' ' && c <= 126)
-				logprintf("%c", c);
-			else
-				logprintf(".");
-		}
-		logprintf("\n");
-	}
-}
-
-/* Verify that two blocks of memory are the same, display the first
- * block of differences if they're not. */
-int
-assertion_equal_mem(const char *file, int line,
-    const void *_v1, const char *e1,
-    const void *_v2, const char *e2,
-    size_t l, const char *ld, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	const char *v2 = (const char *)_v2;
-	size_t offset;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0))
-		return (1);
-	if (v1 == NULL || v2 == NULL)
-		return (0);
-
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      size %s = %d\n", ld, (int)l);
-	/* Dump 48 bytes (3 lines) so that the first difference is
-	 * in the second line. */
-	offset = 0;
-	while (l > 64 && memcmp(v1, v2, 32) == 0) {
-		/* Two lines agree, so step forward one line. */
-		v1 += 16;
-		v2 += 16;
-		l -= 16;
-		offset += 16;
-	}
-	logprintf("      Dump of %s\n", e1);
-	hexdump(v1, v2, l < 128 ? l : 128, offset);
-	logprintf("      Dump of %s\n", e2);
-	hexdump(v2, v1, l < 128 ? l : 128, offset);
-	logprintf("\n");
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that a block of memory is filled with the specified byte. */
-int
-assertion_memory_filled_with(const char *file, int line,
-    const void *_v1, const char *vd,
-    size_t l, const char *ld,
-    char b, const char *bd, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	size_t c = 0;
-	size_t i;
-	(void)ld; /* UNUSED */
-
-	assertion_count(file, line);
-
-	for (i = 0; i < l; ++i) {
-		if (v1[i] == b) {
-			++c;
-		}
-	}
-	if (c == l)
-		return (1);
-
-	failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd);
-	logprintf("   Only %d bytes were correct\n", (int)c);
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that the named file exists and is empty. */
-int
-assertion_empty_file(const char *filename, int line, const char *f1)
-{
-	char buff[1024];
-	struct stat st;
-	ssize_t s;
-	FILE *f;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0)
-		return (1);
-
-	failure_start(filename, line, "File should be empty: %s", f1);
-	logprintf("    File size: %d\n", (int)st.st_size);
-	logprintf("    Contents:\n");
-	f = fopen(f1, "rb");
-	if (f == NULL) {
-		logprintf("    Unable to open %s\n", f1);
-	} else {
-		s = ((off_t)sizeof(buff) < st.st_size) ?
-		    (ssize_t)sizeof(buff) : (ssize_t)st.st_size;
-		s = fread(buff, 1, s, f);
-		hexdump(buff, NULL, s, 0);
-		fclose(f);
-	}
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file exists and is not empty. */
-int
-assertion_non_empty_file(const char *filename, int line, const char *f1)
-{
-	struct stat st;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0) {
-		failure_start(filename, line, "File empty: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify that two files have the same contents. */
-/* TODO: hexdump the first bytes that actually differ. */
-int
-assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2)
-{
-	char buff1[1024];
-	char buff2[1024];
-	FILE *f1, *f2;
-	int n1, n2;
-
-	assertion_count(filename, line);
-
-	f1 = fopen(fn1, "rb");
-	f2 = fopen(fn2, "rb");
-	if (f1 == NULL || f2 == NULL) {
-		if (f1) fclose(f1);
-		if (f2) fclose(f2);
-		return (0);
-	}
-	for (;;) {
-		n1 = (int)fread(buff1, 1, sizeof(buff1), f1);
-		n2 = (int)fread(buff2, 1, sizeof(buff2), f2);
-		if (n1 != n2)
-			break;
-		if (n1 == 0 && n2 == 0) {
-			fclose(f1);
-			fclose(f2);
-			return (1);
-		}
-		if (memcmp(buff1, buff2, n1) != 0)
-			break;
-	}
-	fclose(f1);
-	fclose(f2);
-	failure_start(filename, line, "Files not identical");
-	logprintf("  file1=\"%s\"\n", fn1);
-	logprintf("  file2=\"%s\"\n", fn2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file does exist. */
-int
-assertion_file_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (!_access(f, 0))
-		return (1);
-#else
-	if (!access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file doesn't exist. */
-int
-assertion_file_not_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (_access(f, 0))
-		return (1);
-#else
-	if (access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should not exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Compare the contents of a file to a block of memory. */
-int
-assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn)
-{
-	char *contents;
-	FILE *f;
-	int n;
-
-	assertion_count(filename, line);
-
-	f = fopen(fn, "rb");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File should exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	contents = malloc(s * 2);
-	n = (int)fread(contents, 1, s * 2, f);
-	fclose(f);
-	if (n == s && memcmp(buff, contents, s) == 0) {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "File contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0)
-		hexdump(contents, buff, n > 512 ? 512 : n, 0);
-	else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s > 512 ? 512 : s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Check the contents of a text file, being tolerant of line endings. */
-int
-assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn)
-{
-	char *contents;
-	const char *btxt, *ftxt;
-	FILE *f;
-	int n, s;
-
-	assertion_count(filename, line);
-	f = fopen(fn, "r");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File doesn't exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	s = (int)strlen(buff);
-	contents = malloc(s * 2 + 128);
-	n = (int)fread(contents, 1, s * 2 + 128 - 1, f);
-	if (n >= 0)
-		contents[n] = '\0';
-	fclose(f);
-	/* Compare texts. */
-	btxt = buff;
-	ftxt = (const char *)contents;
-	while (*btxt != '\0' && *ftxt != '\0') {
-		if (*btxt == *ftxt) {
-			++btxt;
-			++ftxt;
-			continue;
-		}
-		if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') {
-			/* Pass over different new line characters. */
-			++btxt;
-			ftxt += 2;
-			continue;
-		}
-		break;
-	}
-	if (*btxt == '\0' && *ftxt == '\0') {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "Contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0) {
-		hexdump(contents, buff, n, 0);
-		logprintf("  expected\n", fn);
-		hexdump(buff, contents, s, 0);
-	} else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Verify that a text file contains the specified lines, regardless of order */
-/* This could be more efficient if we sorted both sets of lines, etc, but
- * since this is used only for testing and only ever deals with a dozen or so
- * lines at a time, this relatively crude approach is just fine. */
-int
-assertion_file_contains_lines_any_order(const char *file, int line,
-    const char *pathname, const char *lines[])
-{
-	char *buff;
-	size_t buff_size;
-	size_t expected_count, actual_count, i, j;
-	char **expected = NULL;
-	char *p, **actual = NULL;
-	char c;
-	int expected_failure = 0, actual_failure = 0;
-
-	assertion_count(file, line);
-
-	buff = slurpfile(&buff_size, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(pathname, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	/* Make a copy of the provided lines and count up the expected
-	 * file size. */
-	for (i = 0; lines[i] != NULL; ++i) {
-	}
-	expected_count = i;
-	if (expected_count) {
-		expected = malloc(sizeof(char *) * expected_count);
-		if (expected == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			free(expected);
-			return (0);
-		}
-		for (i = 0; lines[i] != NULL; ++i) {
-			expected[i] = strdup(lines[i]);
-		}
-	}
-
-	/* Break the file into lines */
-	actual_count = 0;
-	for (c = '\0', p = buff; p < buff + buff_size; ++p) {
-		if (*p == '\x0d' || *p == '\x0a')
-			*p = '\0';
-		if (c == '\0' && *p != '\0')
-			++actual_count;
-		c = *p;
-	}
-	if (actual_count) {
-		actual = calloc(sizeof(char *), actual_count);
-		if (actual == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			free(expected);
-			return (0);
-		}
-		for (j = 0, p = buff; p < buff + buff_size;
-		    p += 1 + strlen(p)) {
-			if (*p != '\0') {
-				actual[j] = p;
-				++j;
-			}
-		}
-	}
-
-	/* Erase matching lines from both lists */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] == NULL)
-			continue;
-		for (j = 0; j < actual_count; ++j) {
-			if (actual[j] == NULL)
-				continue;
-			if (strcmp(expected[i], actual[j]) == 0) {
-				free(expected[i]);
-				expected[i] = NULL;
-				actual[j] = NULL;
-				break;
-			}
-		}
-	}
-
-	/* If there's anything left, it's a failure */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL)
-			++expected_failure;
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			++actual_failure;
-	}
-	if (expected_failure == 0 && actual_failure == 0) {
-		free(buff);
-		free(expected);
-		free(actual);
-		return (1);
-	}
-	failure_start(file, line, "File doesn't match: %s", pathname);
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL) {
-			logprintf("  Expected but not present: %s\n", expected[i]);
-			free(expected[i]);
-		}
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			logprintf("  Present but not expected: %s\n", actual[j]);
-	}
-	failure_finish(NULL);
-	free(buff);
-	free(expected);
-	free(actual);
-	return (0);
-}
-
-/* Verify that a text file does not contains the specified strings */
-int
-assertion_file_contains_no_invalid_strings(const char *file, int line,
-    const char *pathname, const char *strings[])
-{
-	char *buff;
-	int i;
-
-	buff = slurpfile(NULL, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(file, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	for (i = 0; strings[i] != NULL; ++i) {
-		if (strstr(buff, strings[i]) != NULL) {
-			failure_start(file, line, "Invalid string in %s: %s", pathname,
-			    strings[i]);
-			failure_finish(NULL);
-			free(buff);
-			return(0);
-		}
-	}
-
-	free(buff);
-	return (0);
-}
-
-/* Test that two paths point to the same file. */
-/* As a side-effect, asserts that both files exist. */
-static int
-is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(path1, &bhfi1);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = my_GetFileInformationByName(path2, &bhfi2);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
-		&& bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
-		&& bhfi1.nFileIndexLow == bhfi2.nFileIndexLow);
-#else
-	struct stat st1, st2;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(path1, &st1);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = lstat(path2, &st2);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev);
-#endif
-}
-
-int
-assertion_is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s are not hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-int
-assertion_is_not_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (!is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s should not be hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify a/b/mtime of 'pathname'. */
-/* If 'recent', verify that it's within last 10 seconds. */
-static int
-assertion_file_time(const char *file, int line,
-    const char *pathname, long t, long nsec, char type, int recent)
-{
-	long long filet, filet_nsec;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define EPOC_TIME	(116444736000000000ULL)
-	FILETIME fxtime, fbirthtime, fatime, fmtime;
-	ULARGE_INTEGER wintm;
-	HANDLE h;
-	fxtime.dwLowDateTime = 0;
-	fxtime.dwHighDateTime = 0;
-
-	assertion_count(file, line);
-	/* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open
-	 * a directory file. If not, CreateFile() will fail when
-	 * the pathname is a directory. */
-	h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
-	    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = GetFileTime(h, &fbirthtime, &fatime, &fmtime);
-	switch (type) {
-	case 'a': fxtime = fatime; break;
-	case 'b': fxtime = fbirthtime; break;
-	case 'm': fxtime = fmtime; break;
-	}
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't GetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	wintm.LowPart = fxtime.dwLowDateTime;
-	wintm.HighPart = fxtime.dwHighDateTime;
-	filet = (wintm.QuadPart - EPOC_TIME) / 10000000;
-	filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100;
-	nsec = (nsec / 100) * 100; /* Round the request */
-#else
-	struct stat st;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	switch (type) {
-	case 'a': filet = st.st_atime; break;
-	case 'm': filet = st.st_mtime; break;
-	case 'b': filet = 0; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-#if defined(__FreeBSD__)
-	switch (type) {
-	case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
-	case 'b': filet = st.st_birthtime;
-		/* FreeBSD filesystems that don't support birthtime
-		 * (e.g., UFS1) always return -1 here. */
-		if (filet == -1) {
-			return (1);
-		}
-		filet_nsec = st.st_birthtimespec.tv_nsec; break;
-	case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-	/* FreeBSD generally only stores to microsecond res, so round. */
-	filet_nsec = (filet_nsec / 1000) * 1000;
-	nsec = (nsec / 1000) * 1000;
-#else
-	filet_nsec = nsec = 0;	/* Generic POSIX only has whole seconds. */
-	if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */
-#if defined(__HAIKU__)
-	if (type == 'a') return (1); /* Haiku doesn't have atime. */
-#endif
-#endif
-#endif
-	if (recent) {
-		/* Check that requested time is up-to-date. */
-		time_t now = time(NULL);
-		if (filet < now - 10 || filet > now + 1) {
-			failure_start(file, line,
-			    "File %s has %ctime %lld, %lld seconds ago\n",
-			    pathname, type, filet, now - filet);
-			failure_finish(NULL);
-			return (0);
-		}
-	} else if (filet != t || filet_nsec != nsec) {
-		failure_start(file, line,
-		    "File %s has %ctime %lld.%09lld, expected %lld.%09lld",
-		    pathname, type, filet, filet_nsec, t, nsec);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify atime of 'pathname'. */
-int
-assertion_file_atime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'a', 0);
-}
-
-/* Verify atime of 'pathname' is up-to-date. */
-int
-assertion_file_atime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'a', 1);
-}
-
-/* Verify birthtime of 'pathname'. */
-int
-assertion_file_birthtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'b', 0);
-}
-
-/* Verify birthtime of 'pathname' is up-to-date. */
-int
-assertion_file_birthtime_recent(const char *file, int line,
-    const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
-}
-
-/* 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");
-	(void)mode; /* UNUSED */
-	(void)r; /* UNUSED */
-#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);
-}
-
-/* Verify mtime of 'pathname'. */
-int
-assertion_file_mtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'm', 0);
-}
-
-/* Verify mtime of 'pathname' is up-to-date. */
-int
-assertion_file_mtime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'm', 1);
-}
-
-/* Verify number of links to 'pathname'. */
-int
-assertion_file_nlinks(const char *file, int line,
-    const char *pathname, int nlinks)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(pathname, &bhfi);
-	if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, bhfi.nNumberOfLinks, nlinks);
-	failure_finish(NULL);
-	return (0);
-#else
-	struct stat st;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r == 0 && (int)st.st_nlink == nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, st.st_nlink, nlinks);
-	failure_finish(NULL);
-	return (0);
-#endif
-}
-
-/* Verify size of 'pathname'. */
-int
-assertion_file_size(const char *file, int line, const char *pathname, long size)
-{
-	int64_t filesize;
-	int r;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	{
-		BY_HANDLE_FILE_INFORMATION bhfi;
-		r = !my_GetFileInformationByName(pathname, &bhfi);
-		filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow;
-	}
-#else
-	{
-		struct stat st;
-		r = lstat(pathname, &st);
-		filesize = st.st_size;
-	}
-#endif
-	if (r == 0 && filesize == size)
-			return (1);
-	failure_start(file, line, "File %s has size %ld, expected %ld",
-	    pathname, (long)filesize, (long)size);
-	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)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Dir should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISDIR(st.st_mode)) {
-		failure_start(file, line, "%s is not a dir", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "Dir %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Verify that 'pathname' is a regular file.  If 'mode' is >= 0,
- * verify that too. */
-int
-assertion_is_reg(const char *file, int line, const char *pathname, int mode)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0 || !S_ISREG(st.st_mode)) {
-		failure_start(file, line, "File should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "File %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Check whether 'pathname' is a symbolic link.  If 'contents' is
- * non-NULL, verify that the symlink has those contents. */
-static int
-is_symlink(const char *file, int line,
-    const char *pathname, const char *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)pathname; /* UNUSED */
-	(void)contents; /* UNUSED */
-	assertion_count(file, line);
-	/* Windows sort-of has real symlinks, but they're only usable
-	 * by privileged users and are crippled even then, so there's
-	 * really not much point in bothering with this. */
-	return (0);
-#else
-	char buff[300];
-	struct stat st;
-	ssize_t linklen;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line,
-		    "Symlink should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISLNK(st.st_mode))
-		return (0);
-	if (contents == NULL)
-		return (1);
-	linklen = readlink(pathname, buff, sizeof(buff));
-	if (linklen < 0) {
-		failure_start(file, line, "Can't read symlink %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	buff[linklen] = '\0';
-	if (strcmp(buff, contents) != 0)
-		return (0);
-	return (1);
-#endif
-}
-
-/* Assert that path is a symlink that (optionally) contains contents. */
-int
-assertion_is_symlink(const char *file, int line,
-    const char *path, const char *contents)
-{
-	if (is_symlink(file, line, path, contents))
-		return (1);
-	if (contents)
-		failure_start(file, line, "File %s is not a symlink to %s",
-		    path, contents);
-	else
-		failure_start(file, line, "File %s is not a symlink", path);
-	failure_finish(NULL);
-	return (0);
-}
-
-
-/* Create a directory and report any errors. */
-int
-assertion_make_dir(const char *file, int line, const char *dirname, int mode)
-{
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-	if (0 == _mkdir(dirname))
-		return (1);
-#else
-	if (0 == mkdir(dirname, mode)) {
-		if (0 == chmod(dirname, mode)) {
-			assertion_file_mode(file, line, dirname, mode);
-			return (1);
-		}
-	}
-#endif
-	failure_start(file, line, "Could not create directory %s", dirname);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a file with the specified contents and report any failures. */
-int
-assertion_make_file(const char *file, int line,
-    const char *path, int mode, int csize, const void *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	/* TODO: Rework this to set file mode as well. */
-	FILE *f;
-	(void)mode; /* UNUSED */
-	assertion_count(file, line);
-	f = fopen(path, "wb");
-	if (f == NULL) {
-		failure_start(file, line, "Could not create file %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (contents != NULL) {
-		size_t wsize;
-
-		if (csize < 0)
-			wsize = strlen(contents);
-		else
-			wsize = (size_t)csize;
-		if (wsize != fwrite(contents, 1, wsize, f)) {
-			fclose(f);
-			failure_start(file, line,
-			    "Could not write file %s", path);
-			failure_finish(NULL);
-			return (0);
-		}
-	}
-	fclose(f);
-	return (1);
-#else
-	int fd;
-	assertion_count(file, line);
-	fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644);
-	if (fd < 0) {
-		failure_start(file, line, "Could not create %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (0 != chmod(path, mode)) {
-		failure_start(file, line, "Could not chmod %s", path);
-		failure_finish(NULL);
-		close(fd);
-		return (0);
-	}
-	if (contents != NULL) {
-		ssize_t wsize;
-
-		if (csize < 0)
-			wsize = (ssize_t)strlen(contents);
-		else
-			wsize = (ssize_t)csize;
-		if (wsize != write(fd, contents, wsize)) {
-			close(fd);
-			failure_start(file, line,
-			    "Could not write to %s", path);
-			failure_finish(NULL);
-			close(fd);
-			return (0);
-		}
-	}
-	close(fd);
-	assertion_file_mode(file, line, path, mode);
-	return (1);
-#endif
-}
-
-/* Create a hardlink and report any failures. */
-int
-assertion_make_hardlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-	int succeeded;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	succeeded = my_CreateHardLinkA(newpath, linkto);
-#elif HAVE_LINK
-	succeeded = !link(linkto, newpath);
-#else
-	succeeded = 0;
-#endif
-	if (succeeded)
-		return (1);
-	failure_start(file, line, "Could not create hardlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a symlink and report any failures. */
-int
-assertion_make_symlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	int targetIsDir = 0;  /* TODO: Fix this */
-	assertion_count(file, line);
-	if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
-		return (1);
-#elif HAVE_SYMLINK
-	assertion_count(file, line);
-	if (0 == symlink(linkto, newpath))
-		return (1);
-#endif
-	failure_start(file, line, "Could not create symlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Set umask, report failures. */
-int
-assertion_umask(const char *file, int line, int mask)
-{
-	assertion_count(file, line);
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	umask(mask);
-	return (1);
-}
-
-/* Set times, report failures. */
-int
-assertion_utimes(const char *file, int line,
-    const char *pathname, long at, long at_nsec, long mt, long mt_nsec)
-{
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
-	 + (((nsec)/1000)*10))
-	HANDLE h;
-	ULARGE_INTEGER wintm;
-	FILETIME fatime, fmtime;
-	FILETIME *pat, *pmt;
-
-	assertion_count(file, line);
-	h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE,
-		    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-		    FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (at > 0 || at_nsec > 0) {
-		wintm.QuadPart = WINTIME(at, at_nsec);
-		fatime.dwLowDateTime = wintm.LowPart;
-		fatime.dwHighDateTime = wintm.HighPart;
-		pat = &fatime;
-	} else
-		pat = NULL;
-	if (mt > 0 || mt_nsec > 0) {
-		wintm.QuadPart = WINTIME(mt, mt_nsec);
-		fmtime.dwLowDateTime = wintm.LowPart;
-		fmtime.dwHighDateTime = wintm.HighPart;
-		pmt = &fmtime;
-	} else
-		pmt = NULL;
-	if (pat != NULL || pmt != NULL)
-		r = SetFileTime(h, NULL, pat, pmt);
-	else
-		r = 1;
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't SetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#else /* defined(_WIN32) && !defined(__CYGWIN__) */
-	struct stat st;
-	struct timeval times[2];
-
-#if !defined(__FreeBSD__)
-	mt_nsec = at_nsec = 0;	/* Generic POSIX only has whole seconds. */
-#endif
-	if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0)
-		return (1);
-
-	r = lstat(pathname, &st);
-	if (r < 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (mt == 0 && mt_nsec == 0) {
-		mt = st.st_mtime;
-#if defined(__FreeBSD__)
-		mt_nsec = st.st_mtimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		mt_nsec = (mt_nsec / 1000) * 1000;
-#endif
-	}
-	if (at == 0 && at_nsec == 0) {
-		at = st.st_atime;
-#if defined(__FreeBSD__)
-		at_nsec = st.st_atimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		at_nsec = (at_nsec / 1000) * 1000;
-#endif
-	}
-
-	times[1].tv_sec = mt;
-	times[1].tv_usec = mt_nsec / 1000;
-
-	times[0].tv_sec = at;
-	times[0].tv_usec = at_nsec / 1000;
-
-#ifdef HAVE_LUTIMES
-	r = lutimes(pathname, times);
-#else
-	r = utimes(pathname, times);
-#endif
-	if (r < 0) {
-		failure_start(file, line, "Can't utimes %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
-}
-
-/* Set nodump, report failures. */
-int
-assertion_nodump(const char *file, int line, const char *pathname)
-{
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-	int r;
-
-	assertion_count(file, line);
-	r = chflags(pathname, UF_NODUMP);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-	int fd, r, flags;
-
-	assertion_count(file, line);
-	fd = open(pathname, O_RDONLY | O_NONBLOCK);
-	if (fd < 0) {
-		failure_start(file, line, "Can't open %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't get flags %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	close(fd);
-#else
-	(void)pathname; /* UNUSED */
-	assertion_count(file, line);
-#endif
-	return (1);
-}
-
-/*
- *
- *  UTILITIES for use by tests.
- *
- */
-
-/*
- * Check whether platform supports symlinks.  This is intended
- * for tests to use in deciding whether to bother testing symlink
- * support; if the platform doesn't support symlinks, there's no point
- * in checking whether the program being tested can create them.
- *
- * Note that the first time this test is called, we actually go out to
- * disk to create and verify a symlink.  This is necessary because
- * symlink support is actually a property of a particular filesystem
- * and can thus vary between directories on a single system.  After
- * the first call, this returns the cached result from memory, so it's
- * safe to call it as often as you wish.
- */
-int
-canSymlink(void)
-{
-	/* Remember the test result */
-	static int value = 0, tested = 0;
-	if (tested)
-		return (value);
-
-	++tested;
-	assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a");
-	/* Note: Cygwin has its own symlink() emulation that does not
-	 * use the Win32 CreateSymbolicLink() function. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0)
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0");
-#elif HAVE_SYMLINK
-	value = (0 == symlink("canSymlink.0", "canSymlink.1"))
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0");
-#endif
-	return (value);
-}
-
-/* Platform-dependent options for hiding the output of a subcommand. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */
-#else
-static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */
-#endif
-/*
- * Can this platform run the bzip2 program?
- */
-int
-canBzip2(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("bzip2 -d -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the grzip program?
- */
-int
-canGrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("grzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the gzip program?
- */
-int
-canGzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("gzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lrzip program?
- */
-int
-canRunCommand(const char *cmd)
-{
-  static int tested = 0, value = 0;
-  if (!tested) {
-    tested = 1;
-    if (systemf("%s %s", cmd, redirectArgs) == 0)
-      value = 1;
-  }
-  return (value);
-}
-
-int
-canLrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lrzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lz4 program?
- */
-int
-canLz4(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lz4 -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzip program?
- */
-int
-canLzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzma program?
- */
-int
-canLzma(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzma -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzop program?
- */
-int
-canLzop(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzop -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the xz program?
- */
-int
-canXz(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("xz -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this filesystem handle nodump flags.
- */
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	struct stat sb;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	if (chflags(path, UF_NODUMP) < 0)
-		return (0);
-	if (stat(path, &sb) < 0)
-		return (0);
-	if (sb.st_flags & UF_NODUMP)
-		return (1);
-	return (0);
-}
-
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	int fd, r, flags;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	if (flags & EXT2_NODUMP_FL)
-		return (1);
-	return (0);
-}
-
-#else
-
-int
-canNodump()
-{
-	return (0);
-}
-
-#endif
-
-/*
- * Sleep as needed; useful for verifying disk timestamp changes by
- * ensuring that the wall-clock time has actually changed before we
- * go back to re-read something from disk.
- */
-void
-sleepUntilAfter(time_t t)
-{
-	while (t >= time(NULL))
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		Sleep(500);
-#else
-		sleep(1);
-#endif
-}
-
-/*
- * Call standard system() call, but build up the command line using
- * sprintf() conventions.
- */
-int
-systemf(const char *fmt, ...)
-{
-	char buff[8192];
-	va_list ap;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	if (verbosity > VERBOSITY_FULL)
-		logprintf("Cmd: %s\n", buff);
-	r = system(buff);
-	va_end(ap);
-	return (r);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-char *
-slurpfile(size_t * sizep, const char *fmt, ...)
-{
-	char filename[8192];
-	struct stat st;
-	va_list ap;
-	char *p;
-	ssize_t bytes_read;
-	FILE *f;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(filename, fmt, ap);
-	va_end(ap);
-
-	f = fopen(filename, "rb");
-	if (f == NULL) {
-		/* Note: No error; non-existent file is okay here. */
-		return (NULL);
-	}
-	r = fstat(fileno(f), &st);
-	if (r != 0) {
-		logprintf("Can't stat file %s\n", filename);
-		fclose(f);
-		return (NULL);
-	}
-	p = malloc((size_t)st.st_size + 1);
-	if (p == NULL) {
-		logprintf("Can't allocate %ld bytes of memory to read file %s\n",
-		    (long int)st.st_size, filename);
-		fclose(f);
-		return (NULL);
-	}
-	bytes_read = fread(p, 1, (size_t)st.st_size, f);
-	if (bytes_read < st.st_size) {
-		logprintf("Can't read file %s\n", filename);
-		fclose(f);
-		free(p);
-		return (NULL);
-	}
-	p[st.st_size] = '\0';
-	if (sizep != NULL)
-		*sizep = (size_t)st.st_size;
-	fclose(f);
-	return (p);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-void
-dumpfile(const char *filename, void *data, size_t len)
-{
-	ssize_t bytes_written;
-	FILE *f;
-
-	f = fopen(filename, "wb");
-	if (f == NULL) {
-		logprintf("Can't open file %s for writing\n", filename);
-		return;
-	}
-	bytes_written = fwrite(data, 1, len, f);
-	if (bytes_written < (ssize_t)len)
-		logprintf("Can't write file %s\n", filename);
-	fclose(f);
-}
-
-/* Read a uuencoded file from the reference directory, decode, and
- * write the result into the current directory. */
-#define VALID_UUDECODE(c) (c >= 32 && c <= 96)
-#define	UUDECODE(c) (((c) - 0x20) & 0x3f)
-void
-extract_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-
-	sprintf(buff, "%s/%s.uu", refdir, name);
-	in = fopen(buff, "r");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Read up to and including the 'begin' line. */
-	for (;;) {
-		if (fgets(buff, sizeof(buff), in) == NULL) {
-			/* TODO: This is a failure. */
-			return;
-		}
-		if (memcmp(buff, "begin ", 6) == 0)
-			break;
-	}
-	/* Now, decode the rest and write it. */
-	out = fopen(name, "wb");
-	while (fgets(buff, sizeof(buff), in) != NULL) {
-		char *p = buff;
-		int bytes;
-
-		if (memcmp(buff, "end", 3) == 0)
-			break;
-
-		bytes = UUDECODE(*p++);
-		while (bytes > 0) {
-			int n = 0;
-			/* Write out 1-3 bytes from that. */
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				assert(VALID_UUDECODE(p[1]));
-				n = UUDECODE(*p++) << 18;
-				n |= UUDECODE(*p++) << 12;
-				fputc(n >> 16, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++) << 6;
-				fputc((n >> 8) & 0xFF, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++);
-				fputc(n & 0xFF, out);
-				--bytes;
-			}
-		}
-	}
-	fclose(out);
-	fclose(in);
-}
-
-void
-copy_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-	size_t rbytes;
-
-	sprintf(buff, "%s/%s", refdir, name);
-	in = fopen(buff, "rb");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Now, decode the rest and write it. */
-	/* Not a lot of error checking here; the input better be right. */
-	out = fopen(name, "wb");
-	while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) {
-		if (fwrite(buff, 1, rbytes, out) != rbytes) {
-			logprintf("Error: fwrite\n");
-			break;
-		}
-	}
-	fclose(out);
-	fclose(in);
-}
-
-int
-is_LargeInode(const char *file)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	r = my_GetFileInformationByName(file, &bhfi);
-	if (r != 0)
-		return (0);
-	return (bhfi.nFileIndexHigh & 0x0000FFFFUL);
-#else
-	struct stat st;
-	int64_t ino;
-
-	if (stat(file, &st) < 0)
-		return (0);
-	ino = (int64_t)st.st_ino;
-	return (ino > 0xffffffff);
-#endif
-}
-
-void
-extract_reference_files(const char **names)
-{
-	while (names && *names)
-		extract_reference_file(*names++);
-}
-
-/*
- *
- * TEST management
- *
- */
-
-/*
- * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has
- * a line like
- *      DEFINE_TEST(test_function)
- * for each test.
- */
-
-/* Use "list.h" to declare all of the test functions. */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(name) void name(void);
-#include "list.h"
-
-/* Use "list.h" to create a list of all tests (functions and names). */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(n) { n, #n, 0 },
-struct test_list_t tests[] = {
-	#include "list.h"
-};
-
-/*
- * Summarize repeated failures in the just-completed test.
- */
-static void
-test_summarize(int failed, int skips_num)
-{
-	unsigned int i;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY:
-		printf(failed ? "E" : ".");
-		fflush(stdout);
-		break;
-	case VERBOSITY_PASSFAIL:
-		printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n");
-		break;
-	}
-
-	log_console = (verbosity == VERBOSITY_LIGHT_REPORT);
-
-	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
-		if (failed_lines[i].count > 1 && !failed_lines[i].skip)
-			logprintf("%s:%d: Summary: Failed %d times\n",
-			    failed_filename, i, failed_lines[i].count);
-	}
-	/* Clear the failure history for the next file. */
-	failed_filename = NULL;
-	memset(failed_lines, 0, sizeof(failed_lines));
-}
-
-/*
- * Actually run a single test, with appropriate setup and cleanup.
- */
-static int
-test_run(int i, const char *tmpdir)
-{
-	char workdir[1024];
-	char logfilename[64];
-	int failures_before = failures;
-	int skips_before = skips;
-	int oldumask;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */
-		break;
-	case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */
-		printf("%3d: %-64s", i, tests[i].name);
-		fflush(stdout);
-		break;
-	default: /* Title of test, details will follow */
-		printf("%3d: %s\n", i, tests[i].name);
-	}
-
-	/* Chdir to the top-level work directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to top work dir %s\n", tmpdir);
-		exit(1);
-	}
-	/* Create a log file for this test. */
-	sprintf(logfilename, "%s.log", tests[i].name);
-	logfile = fopen(logfilename, "w");
-	fprintf(logfile, "%s\n\n", tests[i].name);
-	/* Chdir() to a work dir for this specific test. */
-	snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name);
-	testworkdir = workdir;
-	if (!assertMakeDir(testworkdir, 0755)
-	    || !assertChdir(testworkdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to work dir %s\n", testworkdir);
-		exit(1);
-	}
-	/* Explicitly reset the locale before each test. */
-	setlocale(LC_ALL, "C");
-	/* Record the umask before we run the test. */
-	umask(oldumask = umask(0));
-	/*
-	 * Run the actual test.
-	 */
-	(*tests[i].func)();
-	/*
-	 * Clean up and report afterwards.
-	 */
-	testworkdir = NULL;
-	/* Restore umask */
-	umask(oldumask);
-	/* Reset locale. */
-	setlocale(LC_ALL, "C");
-	/* Reset directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n",
-		    tmpdir);
-		exit(1);
-	}
-	/* Report per-test summaries. */
-	tests[i].failures = failures - failures_before;
-	test_summarize(tests[i].failures, skips - skips_before);
-	/* Close the per-test log file. */
-	fclose(logfile);
-	logfile = NULL;
-	/* If there were no failures, we can remove the work dir and logfile. */
-	if (tests[i].failures == 0) {
-		if (!keep_temp_files && assertChdir(tmpdir)) {
-#if defined(_WIN32) && !defined(__CYGWIN__)
-			/* Make sure not to leave empty directories.
-			 * Sometimes a processing of closing files used by tests
-			 * is not done, then rmdir will be failed and it will
-			 * leave a empty test directory. So we should wait a few
-			 * seconds and retry rmdir. */
-			int r, t;
-			for (t = 0; t < 10; t++) {
-				if (t > 0)
-					Sleep(1000);
-				r = systemf("rmdir /S /Q %s", tests[i].name);
-				if (r == 0)
-					break;
-			}
-			systemf("del %s", logfilename);
-#else
-			systemf("rm -rf %s", tests[i].name);
-			systemf("rm %s", logfilename);
-#endif
-		}
-	}
-	/* Return appropriate status. */
-	return (tests[i].failures);
-}
-
-/*
- *
- *
- * MAIN and support routines.
- *
- *
- */
-
-static void
-usage(const char *program)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int i;
-
-	printf("Usage: %s [options] <test> <test> ...\n", program);
-	printf("Default is to run all tests.\n");
-	printf("Otherwise, specify the numbers of the tests you wish to run.\n");
-	printf("Options:\n");
-	printf("  -d  Dump core after any failure, for debugging.\n");
-	printf("  -k  Keep all temp files.\n");
-	printf("      Default: temp files for successful tests deleted.\n");
-#ifdef PROGRAM
-	printf("  -p <path>  Path to executable to be tested.\n");
-	printf("      Default: path taken from " ENVBASE " environment variable.\n");
-#endif
-	printf("  -q  Quiet.\n");
-	printf("  -r <dir>   Path to dir containing reference files.\n");
-	printf("      Default: Current directory.\n");
-	printf("  -u  Keep running specifies tests until one fails.\n");
-	printf("  -v  Verbose.\n");
-	printf("Available tests:\n");
-	for (i = 0; i < limit; i++)
-		printf("  %d: %s\n", i, tests[i].name);
-	exit(1);
-}
-
-static char *
-get_refdir(const char *d)
-{
-	size_t tried_size, buff_size;
-	char *buff, *tried, *pwd = NULL, *p = NULL;
-
-#ifdef PATH_MAX
-	buff_size = PATH_MAX;
-#else
-	buff_size = 8192;
-#endif
-	buff = calloc(buff_size, 1);
-	if (buff == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* Allocate a buffer to hold the various directories we checked. */
-	tried_size = buff_size * 2;
-	tried = calloc(tried_size, 1);
-	if (tried == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* If a dir was specified, try that */
-	if (d != NULL) {
-		pwd = NULL;
-		snprintf(buff, buff_size, "%s", d);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-		goto failure;
-	}
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-	/* Look for a known file. */
-	snprintf(buff, buff_size, "%s", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-	snprintf(buff, buff_size, "%s/test", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(LIBRARY)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY);
-#else
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM);
-#endif
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(PROGRAM_ALIAS)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-#endif
-
-	if (memcmp(pwd, "/usr/obj", 8) == 0) {
-		snprintf(buff, buff_size, "%s", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-		snprintf(buff, buff_size, "%s/test", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-	}
-
-failure:
-	printf("Unable to locate known reference file %s\n", KNOWNREF);
-	printf("  Checked following directories:\n%s\n", tried);
-	printf("Use -r option to specify full path to reference directory\n");
-#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
-	DebugBreak();
-#endif
-	exit(1);
-
-success:
-	free(p);
-	free(pwd);
-	free(tried);
-
-	/* Copy result into a fresh buffer to reduce memory usage. */
-	p = strdup(buff);
-	free(buff);
-	return p;
-}
-
-int
-main(int argc, char **argv)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int test_set[sizeof(tests) / sizeof(tests[0])];
-	int i = 0, j = 0, tests_run = 0, tests_failed = 0, option;
-	time_t now;
-	char *refdir_alloc = NULL;
-	const char *progname;
-	char **saved_argv;
-	const char *tmp, *option_arg, *p;
-	char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL;
-	char tmpdir_timestamp[256];
-
-	(void)argc; /* UNUSED */
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-	/* To stop to run the default invalid parameter handler. */
-	_set_invalid_parameter_handler(invalid_parameter_handler);
-	/* Disable annoying assertion message box. */
-	_CrtSetReportMode(_CRT_ASSERT, 0);
-#endif
-
-	/*
-	 * Name of this program, used to build root of our temp directory
-	 * tree.
-	 */
-	progname = p = argv[0];
-	if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL)
-	{
-		fprintf(stderr, "ERROR: Out of memory.");
-		exit(1);
-	}
-	strcpy(testprogdir, progname);
-	while (*p != '\0') {
-		/* Support \ or / dir separators for Windows compat. */
-		if (*p == '/' || *p == '\\')
-		{
-			progname = p + 1;
-			i = j;
-		}
-		++p;
-		j++;
-	}
-	testprogdir[i] = '\0';
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (testprogdir[0] != '/' && testprogdir[0] != '\\' &&
-	    !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') ||
-	       (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) &&
-		testprogdir[1] == ':' &&
-		(testprogdir[2] == '/' || testprogdir[2] == '\\')))
-#else
-	if (testprogdir[0] != '/')
-#endif
-	{
-		/* Fixup path for relative directories. */
-		if ((testprogdir = (char *)realloc(testprogdir,
-			strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		memmove(testprogdir + strlen(pwd) + 1, testprogdir,
-		    strlen(testprogdir) + 1);
-		memcpy(testprogdir, pwd, strlen(pwd));
-		testprogdir[strlen(pwd)] = '/';
-	}
-
-#ifdef PROGRAM
-	/* Get the target program from environment, if available. */
-	testprogfile = getenv(ENVBASE);
-#endif
-
-	if (getenv("TMPDIR") != NULL)
-		tmp = getenv("TMPDIR");
-	else if (getenv("TMP") != NULL)
-		tmp = getenv("TMP");
-	else if (getenv("TEMP") != NULL)
-		tmp = getenv("TEMP");
-	else if (getenv("TEMPDIR") != NULL)
-		tmp = getenv("TEMPDIR");
-	else
-		tmp = "/tmp";
-
-	/* Allow -d to be controlled through the environment. */
-	if (getenv(ENVBASE "_DEBUG") != NULL)
-		dump_on_failure = 1;
-
-	/* Allow -v to be controlled through the environment. */
-	if (getenv("_VERBOSITY_LEVEL") != NULL)
-	{
-		vlevel = getenv("_VERBOSITY_LEVEL");
-		verbosity = atoi(vlevel);
-		if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL)
-		{
-			/* Unsupported verbosity levels are silently ignored */
-			vlevel = NULL;
-			verbosity = VERBOSITY_PASSFAIL;
-		}
-	}
-
-	/* Get the directory holding test files from environment. */
-	refdir = getenv(ENVBASE "_TEST_FILES");
-
-	/*
-	 * Parse options, without using getopt(), which isn't available
-	 * on all platforms.
-	 */
-	++argv; /* Skip program name */
-	while (*argv != NULL) {
-		if (**argv != '-')
-			break;
-		p = *argv++;
-		++p; /* Skip '-' */
-		while (*p != '\0') {
-			option = *p++;
-			option_arg = NULL;
-			/* If 'opt' takes an argument, parse that. */
-			if (option == 'p' || option == 'r') {
-				if (*p != '\0')
-					option_arg = p;
-				else if (*argv == NULL) {
-					fprintf(stderr,
-					    "Option -%c requires argument.\n",
-					    option);
-					usage(progname);
-				} else
-					option_arg = *argv++;
-				p = ""; /* End of this option word. */
-			}
-
-			/* Now, handle the option. */
-			switch (option) {
-			case 'd':
-				dump_on_failure = 1;
-				break;
-			case 'k':
-				keep_temp_files = 1;
-				break;
-			case 'p':
-#ifdef PROGRAM
-				testprogfile = option_arg;
-#else
-				fprintf(stderr, "-p option not permitted\n");
-				usage(progname);
-#endif
-				break;
-			case 'q':
-				if (!vlevel)
-					verbosity--;
-				break;
-			case 'r':
-				refdir = option_arg;
-				break;
-			case 'u':
-				until_failure++;
-				break;
-			case 'v':
-				if (!vlevel)
-					verbosity++;
-				break;
-			default:
-				fprintf(stderr, "Unrecognized option '%c'\n",
-				    option);
-				usage(progname);
-			}
-		}
-	}
-
-	/*
-	 * Sanity-check that our options make sense.
-	 */
-#ifdef PROGRAM
-	if (testprogfile == NULL)
-	{
-		if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 +
-			strlen(PROGRAM) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		strcpy(tmp2, testprogdir);
-		strcat(tmp2, "/");
-		strcat(tmp2, PROGRAM);
-		testprogfile = tmp2;
-	}
-
-	{
-		char *testprg;
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		/* Command.com sometimes rejects '/' separators. */
-		testprg = strdup(testprogfile);
-		for (i = 0; testprg[i] != '\0'; i++) {
-			if (testprg[i] == '/')
-				testprg[i] = '\\';
-		}
-		testprogfile = testprg;
-#endif
-		/* Quote the name that gets put into shell command lines. */
-		testprg = malloc(strlen(testprogfile) + 3);
-		strcpy(testprg, "\"");
-		strcat(testprg, testprogfile);
-		strcat(testprg, "\"");
-		testprog = testprg;
-	}
-#endif
-
-#if !defined(_WIN32) && defined(SIGPIPE)
-	{   /* Ignore SIGPIPE signals */
-		struct sigaction sa;
-		sa.sa_handler = SIG_IGN;
-		sigemptyset(&sa.sa_mask);
-		sa.sa_flags = 0;
-		sigaction(SIGPIPE, &sa, NULL);
-	}
-#endif
-
-	/*
-	 * Create a temp directory for the following tests.
-	 * Include the time the tests started as part of the name,
-	 * to make it easier to track the results of multiple tests.
-	 */
-	now = time(NULL);
-	for (i = 0; ; i++) {
-		strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
-		    "%Y-%m-%dT%H.%M.%S",
-		    localtime(&now));
-		sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname,
-		    tmpdir_timestamp, i);
-		if (assertMakeDir(tmpdir,0755))
-			break;
-		if (i >= 999) {
-			fprintf(stderr,
-			    "ERROR: Unable to create temp directory %s\n",
-			    tmpdir);
-			exit(1);
-		}
-	}
-
-	/*
-	 * If the user didn't specify a directory for locating
-	 * reference files, try to find the reference files in
-	 * the "usual places."
-	 */
-	refdir = refdir_alloc = get_refdir(refdir);
-
-	/*
-	 * Banner with basic information.
-	 */
-	printf("\n");
-	printf("If tests fail or crash, details will be in:\n");
-	printf("   %s\n", tmpdir);
-	printf("\n");
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("Reference files will be read from: %s\n", refdir);
-#ifdef PROGRAM
-		printf("Running tests on: %s\n", testprog);
-#endif
-		printf("Exercising: ");
-		fflush(stdout);
-		printf("%s\n", EXTRA_VERSION);
-	} else {
-		printf("Running ");
-		fflush(stdout);
-	}
-
-	/*
-	 * Run some or all of the individual tests.
-	 */
-	saved_argv = argv;
-	do {
-		argv = saved_argv;
-		do {
-			int test_num;
-
-			test_num = get_test_set(test_set, limit, *argv, tests);
-			if (test_num < 0) {
-				printf("*** INVALID Test %s\n", *argv);
-				free(refdir_alloc);
-				free(testprogdir);
-				usage(progname);
-				return (1);
-			}
-			for (i = 0; i < test_num; i++) {
-				tests_run++;
-				if (test_run(test_set[i], tmpdir)) {
-					tests_failed++;
-					if (until_failure)
-						goto finish;
-				}
-			}
-			if (*argv != NULL)
-				argv++;
-		} while (*argv != NULL);
-	} while (until_failure);
-
-finish:
-	/* Must be freed after all tests run */
-	free(tmp2);
-	free(testprogdir);
-	free(pwd);
-
-	/*
-	 * Report summary statistics.
-	 */
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("\n");
-		printf("Totals:\n");
-		printf("  Tests run:         %8d\n", tests_run);
-		printf("  Tests failed:      %8d\n", tests_failed);
-		printf("  Assertions checked:%8d\n", assertions);
-		printf("  Assertions failed: %8d\n", failures);
-		printf("  Skips reported:    %8d\n", skips);
-	}
-	if (failures) {
-		printf("\n");
-		printf("Failing tests:\n");
-		for (i = 0; i < limit; ++i) {
-			if (tests[i].failures)
-				printf("  %d: %s (%d failures)\n", i,
-				    tests[i].name, tests[i].failures);
-		}
-		printf("\n");
-		printf("Details for failing tests: %s\n", tmpdir);
-		printf("\n");
-	} else {
-		if (verbosity == VERBOSITY_SUMMARY_ONLY)
-			printf("\n");
-		printf("%d tests passed, no failures\n", tests_run);
-	}
-
-	free(refdir_alloc);
-
-	/* If the final tmpdir is empty, we can remove it. */
-	/* This should be the usual case when all tests succeed. */
-	assertChdir("..");
-	rmdir(tmpdir);
-
-	return (tests_failed ? 1 : 0);
-}
diff --git a/cpio/test/test.h b/cpio/test/test.h
index 49fa32c..1dadf68 100644
--- a/cpio/test/test.h
+++ b/cpio/test/test.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2006 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,333 +22,19 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/cpio/test/test.h,v 1.2 2008/06/21 02:17:18 kientzle Exp $
+ * $FreeBSD$
  */
 
 /* Every test program should #include "test.h" as the first thing. */
 
-/*
- * The goal of this file (and the matching test.c) is to
- * simplify the very repetitive test-*.c test programs.
- */
-#if defined(HAVE_CONFIG_H)
-/* Most POSIX platforms use the 'configure' script to build config.h */
-#include "config.h"
-#elif defined(__FreeBSD__)
-/* Building as part of FreeBSD system requires a pre-built config.h. */
-#include "config_freebsd.h"
-#elif defined(_WIN32) && !defined(__CYGWIN__)
-/* Win32 can't run the 'configure' script. */
-#include "config_windows.h"
-#else
-/* Warn if the library hasn't been (automatically or manually) configured. */
-#error Oops: No config.h and no pre-built configuration in test.h.
-#endif
+#define KNOWNREF	"test_option_f.cpio.uu"
+#define ENVBASE "BSDCPIO" /* Prefix for environment variables. */
+#define	PROGRAM "bsdcpio" /* Name of program being tested. */
+#define PROGRAM_ALIAS "cpio" /* Generic alias for program */
+#undef	LIBRARY		  /* Not testing a library. */
+#undef	EXTRA_DUMP	  /* How to dump extra data */
+#undef	EXTRA_ERRNO	  /* How to dump errno */
+/* How to generate extra version info. */
+#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
 
-#include <sys/types.h>  /* Windows requires this before sys/stat.h */
-#include <sys/stat.h>
-
-#if HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-#ifdef HAVE_DIRECT_H
-#include <direct.h>
-#define dirent direct
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_IO_H
-#include <io.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <wchar.h>
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-/*
- * System-specific tweaks.  We really want to minimize these
- * as much as possible, since they make it harder to understand
- * the mainline code.
- */
-
-/* Windows (including Visual Studio and MinGW but not Cygwin) */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#if !defined(__BORLANDC__)
-#undef chdir
-#define chdir _chdir
-#define strdup _strdup
-#endif
-#endif
-
-/* Visual Studio */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#define snprintf	sprintf_s
-#endif
-
-#if defined(__BORLANDC__)
-#pragma warn -8068	/* Constant out of range in comparison. */
-#endif
-
-/* Haiku OS and QNX */
-#if defined(__HAIKU__) || defined(__QNXNTO__)
-/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
-#include <stdint.h>
-#endif
-
-/* Get a real definition for __FBSDID if we can */
-#if HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-
-/* If not, define it so as to avoid dangling semicolons. */
-#ifndef __FBSDID
-#define	__FBSDID(a)     struct _undefined_hack
-#endif
-
-#ifndef O_BINARY
-#define	O_BINARY 0
-#endif
-
-/*
- * Redefine DEFINE_TEST for use in defining the test functions.
- */
-#undef DEFINE_TEST
-#define DEFINE_TEST(name) void name(void); void name(void)
-
-/* An implementation of the standard assert() macro */
-#define assert(e)   assertion_assert(__FILE__, __LINE__, (e), #e, NULL)
-/* chdir() and error if it fails */
-#define assertChdir(path)  \
-  assertion_chdir(__FILE__, __LINE__, path)
-/* Assert two integers are the same.  Reports value of each one if not. */
-#define assertEqualInt(v1,v2) \
-  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* Assert two strings are the same.  Reports value of each one if not. */
-#define assertEqualString(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
-#define assertEqualUTF8String(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
-/* As above, but v1 and v2 are wchar_t * */
-#define assertEqualWString(v1,v2)   \
-  assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* As above, but raw blocks of bytes. */
-#define assertEqualMem(v1, v2, l)	\
-  assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
-/* Assert that memory is full of a specified byte */
-#define assertMemoryFilledWith(v1, l, b)					\
-  assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL)
-/* Assert two files are the same. */
-#define assertEqualFile(f1, f2)	\
-  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
-/* Assert that a file is empty. */
-#define assertEmptyFile(pathname)	\
-  assertion_empty_file(__FILE__, __LINE__, (pathname))
-/* Assert that a file is not empty. */
-#define assertNonEmptyFile(pathname)		\
-  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
-#define assertFileAtime(pathname, sec, nsec)	\
-  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileAtimeRecent(pathname)	\
-  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
-#define assertFileBirthtime(pathname, sec, nsec)	\
-  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileBirthtimeRecent(pathname) \
-  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
-/* Assert that a file exists; supports printf-style arguments. */
-#define assertFileExists(pathname) \
-  assertion_file_exists(__FILE__, __LINE__, pathname)
-/* Assert that a file exists. */
-#define assertFileNotExists(pathname) \
-  assertion_file_not_exists(__FILE__, __LINE__, pathname)
-/* Assert that file contents match a string. */
-#define assertFileContents(data, data_size, pathname) \
-  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
-/* Verify that a file does not contain invalid strings */
-#define assertFileContainsNoInvalidStrings(pathname, strings) \
-  assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
-#define assertFileMtime(pathname, sec, nsec)	\
-  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileMtimeRecent(pathname) \
-  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
-#define assertFileNLinks(pathname, nlinks)  \
-  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)	\
-  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
-#define assertIsDir(pathname, mode)		\
-  assertion_is_dir(__FILE__, __LINE__, pathname, mode)
-#define assertIsHardlink(path1, path2)	\
-  assertion_is_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsNotHardlink(path1, path2)	\
-  assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsReg(pathname, mode)		\
-  assertion_is_reg(__FILE__, __LINE__, pathname, mode)
-#define assertIsSymlink(pathname, contents)	\
-  assertion_is_symlink(__FILE__, __LINE__, pathname, contents)
-/* Create a directory, report error if it fails. */
-#define assertMakeDir(dirname, mode)	\
-  assertion_make_dir(__FILE__, __LINE__, dirname, mode)
-#define assertMakeFile(path, mode, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents)
-#define assertMakeBinFile(path, mode, csize, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
-#define assertMakeHardlink(newfile, oldfile)	\
-  assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto)	\
-  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
-#define assertNodump(path)      \
-  assertion_nodump(__FILE__, __LINE__, path)
-#define assertUmask(mask)	\
-  assertion_umask(__FILE__, __LINE__, mask)
-#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
-  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
-
-/*
- * This would be simple with C99 variadic macros, but I don't want to
- * require that.  Instead, I insert a function call before each
- * skipping() call to pass the file and line information down.  Crude,
- * but effective.
- */
-#define skipping	\
-  skipping_setup(__FILE__, __LINE__);test_skipping
-
-/* Function declarations.  These are defined in test_utility.c. */
-void failure(const char *fmt, ...);
-int assertion_assert(const char *, int, int, const char *, void *);
-int assertion_chdir(const char *, int, const char *);
-int assertion_empty_file(const char *, int, const char *);
-int assertion_equal_file(const char *, int, const char *, const char *);
-int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
-int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
-int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *);
-int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
-int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
-int assertion_file_atime(const char *, int, const char *, long, long);
-int assertion_file_atime_recent(const char *, int, const char *);
-int assertion_file_birthtime(const char *, int, const char *, long, long);
-int assertion_file_birthtime_recent(const char *, int, const char *);
-int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
-int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
-int assertion_file_contents(const char *, int, const void *, int, const char *);
-int assertion_file_exists(const char *, int, const char *);
-int assertion_file_mode(const char *, int, const char *, int);
-int assertion_file_mtime(const char *, int, const char *, long, long);
-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_is_dir(const char *, int, const char *, int);
-int assertion_is_hardlink(const char *, int, const char *, const char *);
-int assertion_is_not_hardlink(const char *, int, const char *, const char *);
-int assertion_is_reg(const char *, int, const char *, int);
-int assertion_is_symlink(const char *, int, const char *, const char *);
-int assertion_make_dir(const char *, int, const char *, int);
-int assertion_make_file(const char *, int, const char *, int, int, const void *);
-int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
-int assertion_nodump(const char *, int, const char *);
-int assertion_non_empty_file(const char *, int, const char *);
-int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
-int assertion_umask(const char *, int, int);
-int assertion_utimes(const char *, int, const char *, long, long, long, long );
-
-void skipping_setup(const char *, int);
-void test_skipping(const char *fmt, ...);
-
-/* Like sprintf, then system() */
-int systemf(const char * fmt, ...);
-
-/* Delay until time() returns a value after this. */
-void sleepUntilAfter(time_t);
-
-/* Return true if this platform can create symlinks. */
-int canSymlink(void);
-
-/* Return true if this platform can run the "bzip2" program. */
-int canBzip2(void);
-
-/* Return true if this platform can run the "grzip" program. */
-int canGrzip(void);
-
-/* Return true if this platform can run the "gzip" program. */
-int canGzip(void);
-
-/* Return true if this platform can run the specified command. */
-int canRunCommand(const char *);
-
-/* Return true if this platform can run the "lrzip" program. */
-int canLrzip(void);
-
-/* Return true if this platform can run the "lz4" program. */
-int canLz4(void);
-
-/* Return true if this platform can run the "lzip" program. */
-int canLzip(void);
-
-/* Return true if this platform can run the "lzma" program. */
-int canLzma(void);
-
-/* Return true if this platform can run the "lzop" program. */
-int canLzop(void);
-
-/* Return true if this platform can run the "xz" program. */
-int canXz(void);
-
-/* Return true if this filesystem can handle nodump flags. */
-int canNodump(void);
-
-/* Return true if the file has large i-node number(>0xffffffff). */
-int is_LargeInode(const char *);
-
-/* Suck file into string allocated via malloc(). Call free() when done. */
-/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
-char *slurpfile(size_t *, const char *fmt, ...);
-
-/* Dump block of bytes to a file. */
-void dumpfile(const char *filename, void *, size_t);
-
-/* Extracts named reference file to the current directory. */
-void extract_reference_file(const char *);
-/* Copies named reference file to the current directory. */
-void copy_reference_file(const char *);
-
-/* Extracts a list of files to the current directory.
- * List must be NULL terminated.
- */
-void extract_reference_files(const char **);
-
-/* Subtract umask from mode */
-mode_t umasked(mode_t expected_mode);
-
-/* Path to working directory for current test */
-extern const char *testworkdir;
-
-/*
- * Special interfaces for program test harness.
- */
-
-/* Pathname of exe to be tested. */
-extern const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-extern const char *testprog;
-
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
+#include "test_common.h"
diff --git a/cpio/test/test_option_version.c b/cpio/test/test_option_version.c
index ac58cef..505db27 100644
--- a/cpio/test/test_option_version.c
+++ b/cpio/test/test_option_version.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,92 +23,8 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD$");
-
-/*
- * Test that --version option works and generates reasonable output.
- */
-
-static void
-verify(const char *p, size_t s)
-{
-	const char *q = p;
-
-	/* Version message should start with name of program, then space. */
-	failure("version message too short:", p);
-	if (!assert(s > 6))
-		return;
-	failure("Version message should begin with 'bsdcpio': %s", p);
-	if (!assertEqualMem(q, "bsdcpio ", 8))
-		/* If we're not testing bsdcpio, don't keep going. */
-		return;
-	q += 8; s -= 8;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Version number terminated by space. */
-	failure("Version: %s", p);
-	assert(s > 1);
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	failure("Version: %s", p);
-	assert(*q == ' ');
-	++q; --s;
-	/* Separator. */
-	failure("Version: %s", p);
-	assertEqualMem(q, "- ", 2);
-	q += 2; s -= 2;
-	/* libarchive name and version number */
-	assert(s > 11);
-	failure("Version: %s", p);
-	assertEqualMem(q, "libarchive ", 11);
-	q += 11; s -= 11;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	/* Skip arbitrary third-party version numbers. */
-	while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
-		++q;
-		--s;
-	}
-	/* All terminated by end-of-line: \r, \r\n, or \n */
-	assert(s >= 1);
-	failure("Version: %s", p);
-	if (*q == '\x0d') {
-		if (q[1] != '\0')
-			assertEqualMem(q, "\x0d\x0a", 2);
-	} else
-		assertEqualMem(q, "\x0a", 1);
-}
-
 
 DEFINE_TEST(test_option_version)
 {
-	int r;
-	char *p;
-	size_t s;
-
-	r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
-	if (r != 0)
-		r = systemf("%s -W version >version.stdout 2>version.stderr",
-		    testprog);
-	failure("Unable to run either %s --version or %s -W version",
-	    testprog, testprog);
-	if (!assert(r == 0))
-		return;
-
-	/* --version should generate nothing to stderr. */
-	assertEmptyFile("version.stderr");
-	/* Verify format of version message. */
-	p = slurpfile(&s, "version.stdout");
-	verify(p, s);
-	free(p);
+	assertVersion(testprog, "bsdcpio");
 }
diff --git a/cpio/test/test_owner_parse.c b/cpio/test/test_owner_parse.c
index a9f6053..bef02da 100644
--- a/cpio/test/test_owner_parse.c
+++ b/cpio/test/test_owner_parse.c
@@ -38,7 +38,7 @@
  * its primary group membership depends on how the user set up
  * their /etc/passwd. Likely values are 513 (None), 545 (Users),
  * or 544 (Administrators). Just check for one of those...
- * TODO: Handle non-English localizations...e.g. French 'Administrateur'
+ * TODO: Handle non-English localizations... e.g. French 'Administrateur'
  *       Use CreateWellKnownSID() and LookupAccountName()?
  */
 #define ROOT "Administrator"
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt
index 744be43..1f85c01 100644
--- a/libarchive/CMakeLists.txt
+++ b/libarchive/CMakeLists.txt
@@ -168,15 +168,33 @@
   archive_entry_time.3
   archive_read.3
   archive_read_add_passphrase.3
+  archive_read_data.3
   archive_read_disk.3
+  archive_read_extract.3
+  archive_read_filter.3
+  archive_read_format.3
+  archive_read_free.3
+  archive_read_header.3
+  archive_read_new.3
+  archive_read_open.3
   archive_read_set_options.3
   archive_util.3
   archive_write.3
+  archive_write_blocksize.3
+  archive_write_data.3
   archive_write_disk.3
+  archive_write_filter.3
+  archive_write_finish_entry.3
+  archive_write_format.3
+  archive_write_free.3
+  archive_write_header.3
+  archive_write_new.3
+  archive_write_open.3
   archive_write_set_options.3
   archive_write_set_passphrase.3
   cpio.5
   libarchive.3
+  libarchive_changes.3
   libarchive_internals.3
   libarchive-formats.5
   mtree.5
diff --git a/libarchive/archive.h b/libarchive/archive.h
index c676858..d6913f3 100644
--- a/libarchive/archive.h
+++ b/libarchive/archive.h
@@ -36,7 +36,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3002002
+#define	ARCHIVE_VERSION_NUMBER 3003002
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -155,7 +155,7 @@
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.2.2"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.3.2dev"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
@@ -1001,6 +1001,10 @@
 #define	ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS	(0x0008)
 /* Default: Xattrs are read from disk. */
 #define	ARCHIVE_READDISK_NO_XATTR		(0x0010)
+/* Default: ACLs are read from disk. */
+#define	ARCHIVE_READDISK_NO_ACL			(0x0020)
+/* Default: File flags are read from disk. */
+#define	ARCHIVE_READDISK_NO_FFLAGS		(0x0040)
 
 __LA_DECL int  archive_read_disk_set_behavior(struct archive *,
 		    int flags);
diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c
index ff5c904..b8b6b63 100644
--- a/libarchive/archive_acl.c
+++ b/libarchive/archive_acl.c
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -55,25 +56,77 @@
 static int	archive_acl_add_entry_len_l(struct archive_acl *acl,
 		    int type, int permset, int tag, int id, const char *name,
 		    size_t len, struct archive_string_conv *sc);
+static int	archive_acl_text_want_type(struct archive_acl *acl, int flags);
+static ssize_t	archive_acl_text_len(struct archive_acl *acl, int want_type,
+		    int flags, int wide, struct archive *a,
+		    struct archive_string_conv *sc);
 static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
 static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
+static int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
+		    int *result);
+static int	is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
+		    int *result);
 static void	next_field_w(const wchar_t **wp, const wchar_t **start,
 		    const wchar_t **end, wchar_t *sep);
-static int	prefix_w(const wchar_t *start, const wchar_t *end,
-		    const wchar_t *test);
-static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
-		    const wchar_t *wname, int perm, int id);
+static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+		    int tag, int flags, const wchar_t *wname, int perm, int id);
 static void	append_id_w(wchar_t **wp, int id);
 static int	isint(const char *start, const char *end, int *result);
 static int	ismode(const char *start, const char *end, int *result);
+static int	is_nfs4_flags(const char *start, const char *end,
+		    int *result);
+static int	is_nfs4_perms(const char *start, const char *end,
+		    int *result);
 static void	next_field(const char **p, const char **start,
 		    const char **end, char *sep);
-static int	prefix_c(const char *start, const char *end,
-		    const char *test);
-static void	append_entry(char **p, const char *prefix, int tag,
-		    const char *name, int perm, int id);
+static void	append_entry(char **p, const char *prefix, int type,
+		    int tag, int flags, const char *name, int perm, int id);
 static void	append_id(char **p, int id);
 
+static const struct {
+	const int perm;
+	const char c;
+	const wchar_t wc;
+} nfsv4_acl_perm_map[] = {
+	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
+	    L'r' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
+	    L'w' },
+	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
+	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+	    'p', L'p' },
+	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
+	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
+	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
+	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
+	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
+	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
+};
+
+static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
+    sizeof(nfsv4_acl_perm_map[0]));
+
+static const struct {
+	const int perm;
+	const char c;
+	const wchar_t wc;
+} nfsv4_acl_flag_map[] = {
+	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
+};
+
+static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
+    sizeof(nfsv4_acl_flag_map[0]));
+
 void
 archive_acl_clear(struct archive_acl *acl)
 {
@@ -280,11 +333,17 @@
 		acl->acl_text = NULL;
 	}
 
-	/* If there's a matching entry already in the list, overwrite it. */
+	/*
+	 * If there's a matching entry already in the list, overwrite it.
+	 * NFSv4 entries may be repeated and are not overwritten.
+	 *
+	 * TODO: compare names of no id is provided (needs more rework)
+	 */
 	ap = acl->acl_head;
 	aq = NULL;
 	while (ap != NULL) {
-		if (ap->type == type && ap->tag == tag && ap->id == id) {
+		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
+		    ap->type == type && ap->tag == tag && ap->id == id) {
 			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
 			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
 				ap->permset = permset;
@@ -334,6 +393,15 @@
 }
 
 /*
+ * Return a bitmask of stored ACL types in an ACL list
+ */
+int
+archive_acl_types(struct archive_acl *acl)
+{
+	return (acl->acl_types);
+}
+
+/*
  * Prepare for reading entries from the ACL data.  Returns a count
  * of entries matching "want_type", or zero if there are no
  * non-extended ACL entries of that type.
@@ -369,8 +437,8 @@
  * standard permissions and include them in the returned list.
  */
 int
-archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
-    int *permset, int *tag, int *id, const char **name)
+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
+    int *type, int *permset, int *tag, int *id, const char **name)
 {
 	*name = NULL;
 	*id = -1;
@@ -435,130 +503,273 @@
 }
 
 /*
- * Generate a text version of the ACL.  The flags parameter controls
- * the style of the generated ACL.
+ * Determine what type of ACL do we want
  */
-const wchar_t *
-archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
+static int
+archive_acl_text_want_type(struct archive_acl *acl, int flags)
+{
+	int want_type;
+
+	/* Check if ACL is NFSv4 */
+	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		/* NFSv4 should never mix with POSIX.1e */
+		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+			return (0);
+		else
+			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+	}
+
+	/* Now deal with POSIX.1e ACLs */
+
+	want_type = 0;
+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+
+	/* By default we want both access and default ACLs */
+	if (want_type == 0)
+		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
+
+	return (want_type);
+}
+
+/*
+ * Calculate ACL text string length
+ */
+static ssize_t
+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
+    int wide, struct archive *a, struct archive_string_conv *sc) {
+	struct archive_acl_entry *ap;
+	const char *name;
+	const wchar_t *wname;
+	int count, idlen, tmp, r;
+	ssize_t length;
+	size_t len;
+
+	count = 0;
+	length = 0;
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		count++;
+		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
+		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			length += 8; /* "default:" */
+		switch (ap->tag) {
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+				length += 6; /* "owner@" */
+				break;
+			}
+			/* FALLTHROUGH */
+		case ARCHIVE_ENTRY_ACL_USER:
+		case ARCHIVE_ENTRY_ACL_MASK:
+			length += 4; /* "user", "mask" */
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+				length += 6; /* "group@" */
+				break;
+			}
+			/* FALLTHROUGH */
+		case ARCHIVE_ENTRY_ACL_GROUP:
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			length += 5; /* "group", "other" */
+			break;
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			length += 9; /* "everyone@" */
+			break;
+		}
+		length += 1; /* colon after tag */
+		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
+			if (wide) {
+				r = archive_mstring_get_wcs(a, &ap->name,
+				    &wname);
+				if (r == 0 && wname != NULL)
+					length += wcslen(wname);
+				else if (r < 0 && errno == ENOMEM)
+					return (0);
+				else
+					length += sizeof(uid_t) * 3 + 1;
+			} else {
+				r = archive_mstring_get_mbs_l(&ap->name, &name,
+				    &len, sc);
+				if (r != 0)
+					return (0);
+				if (len > 0 && name != NULL)
+					length += len;
+				else
+					length += sizeof(uid_t) * 3 + 1;
+			}
+			length += 1; /* colon after user or group name */
+		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+			length += 1; /* 2nd colon empty user,group or other */
+
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
+		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
+		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
+			/* Solaris has no colon after other: and mask: */
+			length = length - 1;
+		}
+
+		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* rwxpdDaARWcCos:fdinSFI:deny */
+			length += 27;
+			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
+				length += 1; /* allow, alarm, audit */
+		} else
+			length += 3; /* rwx */
+
+		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
+			length += 1; /* colon */
+			/* ID digit count */
+			idlen = 1;
+			tmp = ap->id;
+			while (tmp > 9) {
+				tmp = tmp / 10;
+				idlen++;
+			}
+			length += idlen;
+		}
+		length ++; /* entry separator */
+	}
+
+	/* Add filemode-mapping access entries to the length */
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
+			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
+			length += 31;
+		} else {
+			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
+			length += 32;
+		}
+	} else if (count == 0)
+		return (0);
+
+	/* The terminating character is included in count */
+	return (length);
+}
+
+/*
+ * Generate a wide text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+wchar_t *
+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
+    struct archive *a)
 {
 	int count;
-	size_t length;
+	ssize_t length;
+	size_t len;
 	const wchar_t *wname;
 	const wchar_t *prefix;
 	wchar_t separator;
 	struct archive_acl_entry *ap;
-	int id, r;
-	wchar_t *wp;
+	int id, r, want_type;
+	wchar_t *wp, *ws;
 
-	if (acl->acl_text_w != NULL) {
-		free (acl->acl_text_w);
-		acl->acl_text_w = NULL;
-	}
+	want_type = archive_acl_text_want_type(acl, flags);
 
-	separator = L',';
-	count = 0;
-	length = 0;
-	ap = acl->acl_head;
-	while (ap != NULL) {
-		if ((ap->type & flags) != 0) {
-			count++;
-			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
-			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
-				length += 8; /* "default:" */
-			length += 5; /* tag name */
-			length += 1; /* colon */
-			r = archive_mstring_get_wcs(a, &ap->name, &wname);
-			if (r == 0 && wname != NULL)
-				length += wcslen(wname);
-			else if (r < 0 && errno == ENOMEM)
-				return (NULL);
-			else
-				length += sizeof(uid_t) * 3 + 1;
-			length ++; /* colon */
-			length += 3; /* rwx */
-			length += 1; /* colon */
-			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
-			length ++; /* newline */
-		}
-		ap = ap->next;
-	}
-
-	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
-		length += 10; /* "user::rwx\n" */
-		length += 11; /* "group::rwx\n" */
-		length += 11; /* "other::rwx\n" */
-	}
-
-	if (count == 0)
+	/* Both NFSv4 and POSIX.1 types found */
+	if (want_type == 0)
 		return (NULL);
 
+	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
+
+	if (length == 0)
+		return (NULL);
+
+	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+		separator = L',';
+	else
+		separator = L'\n';
+
 	/* Now, allocate the string and actually populate it. */
-	wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
-	if (wp == NULL)
+	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
+	if (wp == NULL) {
+		if (errno == ENOMEM)
+			__archive_errx(1, "No memory");
 		return (NULL);
+	}
 	count = 0;
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
 		    acl->mode & 0700, -1);
-		*wp++ = ',';
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+		*wp++ = separator;
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
 		    acl->mode & 0070, -1);
-		*wp++ = ',';
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+		*wp++ = separator;
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
 		    acl->mode & 0007, -1);
 		count += 3;
-
-		ap = acl->acl_head;
-		while (ap != NULL) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-				r = archive_mstring_get_wcs(a, &ap->name, &wname);
-				if (r == 0) {
-					*wp++ = separator;
-					if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-						id = ap->id;
-					else
-						id = -1;
-					append_entry_w(&wp, NULL, ap->tag, wname,
-					    ap->permset, id);
-					count++;
-				} else if (r < 0 && errno == ENOMEM)
-					return (NULL);
-			}
-			ap = ap->next;
-		}
 	}
 
-
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
 			prefix = L"default:";
 		else
 			prefix = NULL;
-		ap = acl->acl_head;
-		count = 0;
-		while (ap != NULL) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-				r = archive_mstring_get_wcs(a, &ap->name, &wname);
-				if (r == 0) {
-					if (count > 0)
-						*wp++ = separator;
-					if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-						id = ap->id;
-					else
-						id = -1;
-					append_entry_w(&wp, prefix, ap->tag,
-					    wname, ap->permset, id);
-					count ++;
-				} else if (r < 0 && errno == ENOMEM)
-					return (NULL);
-			}
-			ap = ap->next;
-		}
+		r = archive_mstring_get_wcs(a, &ap->name, &wname);
+		if (r == 0) {
+			if (count > 0)
+				*wp++ = separator;
+			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+				id = ap->id;
+			else
+				id = -1;
+			append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
+			    wname, ap->permset, id);
+			count++;
+		} else if (r < 0 && errno == ENOMEM)
+			return (NULL);
 	}
 
-	return (acl->acl_text_w);
-}
+	/* Add terminating character */
+	*wp++ = L'\0';
 
+	len = wcslen(ws);
+
+	if ((ssize_t)len > (length - 1))
+		__archive_errx(1, "Buffer overrun");
+
+	if (text_len != NULL)
+		*text_len = len;
+
+	return (ws);
+}
 
 static void
 append_id_w(wchar_t **wp, int id)
@@ -571,9 +782,11 @@
 }
 
 static void
-append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
-    const wchar_t *wname, int perm, int id)
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+    int tag, int flags, const wchar_t *wname, int perm, int id)
 {
+	int i;
+
 	if (prefix != NULL) {
 		wcscpy(*wp, prefix);
 		*wp += wcslen(*wp);
@@ -582,6 +795,10 @@
 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
 		wname = NULL;
 		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			wcscpy(*wp, L"owner@");
+			break;
+		}
 		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_USER:
 		wcscpy(*wp, L"user");
@@ -589,6 +806,10 @@
 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 		wname = NULL;
 		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			wcscpy(*wp, L"group@");
+			break;
+		}
 		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_GROUP:
 		wcscpy(*wp, L"group");
@@ -603,154 +824,184 @@
 		wname = NULL;
 		id = -1;
 		break;
+	case ARCHIVE_ENTRY_ACL_EVERYONE:
+		wcscpy(*wp, L"everyone@");
+		wname = NULL;
+		id = -1;
+		break;
 	}
 	*wp += wcslen(*wp);
 	*(*wp)++ = L':';
-	if (wname != NULL) {
-		wcscpy(*wp, wname);
-		*wp += wcslen(*wp);
-	} else if (tag == ARCHIVE_ENTRY_ACL_USER
-	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
-		append_id_w(wp, id);
-		id = -1;
+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+	    tag == ARCHIVE_ENTRY_ACL_USER ||
+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+		if (wname != NULL) {
+			wcscpy(*wp, wname);
+			*wp += wcslen(*wp);
+		} else if (tag == ARCHIVE_ENTRY_ACL_USER
+		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+			append_id_w(wp, id);
+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+				id = -1;
+		}
+		/* Solaris style has no second colon after other and mask */
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
+		    && tag != ARCHIVE_ENTRY_ACL_MASK))
+			*(*wp)++ = L':';
 	}
-	*(*wp)++ = L':';
-	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
-	*(*wp)++ = (perm & 0222) ? L'w' : L'-';
-	*(*wp)++ = (perm & 0111) ? L'x' : L'-';
+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* POSIX.1e ACL perms */
+		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
+		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
+		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
+	} else {
+		/* NFSv4 ACL perms */
+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+			if (perm & nfsv4_acl_perm_map[i].perm)
+				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*wp)++ = L'-';
+		}
+		*(*wp)++ = L':';
+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+			if (perm & nfsv4_acl_flag_map[i].perm)
+				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*wp)++ = L'-';
+		}
+		*(*wp)++ = L':';
+		switch (type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			wcscpy(*wp, L"allow");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			wcscpy(*wp, L"deny");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			wcscpy(*wp, L"audit");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			wcscpy(*wp, L"alarm");
+			break;
+		default:
+			break;
+		}
+		*wp += wcslen(*wp);
+	}
 	if (id != -1) {
 		*(*wp)++ = L':';
 		append_id_w(wp, id);
 	}
-	**wp = L'\0';
 }
 
-int
-archive_acl_text_l(struct archive_acl *acl, int flags,
-    const char **acl_text, size_t *acl_text_len,
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+char *
+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
     struct archive_string_conv *sc)
 {
 	int count;
-	size_t length;
+	ssize_t length;
+	size_t len;
 	const char *name;
 	const char *prefix;
 	char separator;
 	struct archive_acl_entry *ap;
-	size_t len;
-	int id, r;
-	char *p;
+	int id, r, want_type;
+	char *p, *s;
 
-	if (acl->acl_text != NULL) {
-		free (acl->acl_text);
-		acl->acl_text = NULL;
-	}
+	want_type = archive_acl_text_want_type(acl, flags);
 
-	*acl_text = NULL;
-	if (acl_text_len != NULL)
-		*acl_text_len = 0;
-	separator = ',';
-	count = 0;
-	length = 0;
-	ap = acl->acl_head;
-	while (ap != NULL) {
-		if ((ap->type & flags) != 0) {
-			count++;
-			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
-			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
-				length += 8; /* "default:" */
-			length += 5; /* tag name */
-			length += 1; /* colon */
-			r = archive_mstring_get_mbs_l(
-			    &ap->name, &name, &len, sc);
-			if (r != 0)
-				return (-1);
-			if (len > 0 && name != NULL)
-				length += len;
-			else
-				length += sizeof(uid_t) * 3 + 1;
-			length ++; /* colon */
-			length += 3; /* rwx */
-			length += 1; /* colon */
-			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
-			length ++; /* newline */
-		}
-		ap = ap->next;
-	}
+	/* Both NFSv4 and POSIX.1 types found */
+	if (want_type == 0)
+		return (NULL);
 
-	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
-		length += 10; /* "user::rwx\n" */
-		length += 11; /* "group::rwx\n" */
-		length += 11; /* "other::rwx\n" */
-	}
+	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
 
-	if (count == 0)
-		return (0);
+	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
+
+	if (length == 0)
+		return (NULL);
+
+	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+		separator = ',';
+	else
+		separator = '\n';
 
 	/* Now, allocate the string and actually populate it. */
-	p = acl->acl_text = (char *)malloc(length);
-	if (p == NULL)
-		return (-1);
+	p = s = (char *)malloc(length * sizeof(char));
+	if (p == NULL) {
+		if (errno == ENOMEM)
+			__archive_errx(1, "No memory");
+		return (NULL);
+	}
 	count = 0;
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
 		    acl->mode & 0700, -1);
-		*p++ = ',';
-		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+		*p++ = separator;
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
 		    acl->mode & 0070, -1);
-		*p++ = ',';
-		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+		*p++ = separator;
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
 		    acl->mode & 0007, -1);
 		count += 3;
-
-		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
-				continue;
-			r = archive_mstring_get_mbs_l(
-			    &ap->name, &name, &len, sc);
-			if (r != 0)
-				return (-1);
-			*p++ = separator;
-			if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
-				id = ap->id;
-			} else {
-				id = -1;
-			}
-			append_entry(&p, NULL, ap->tag, name,
-			    ap->permset, id);
-			count++;
-		}
 	}
 
-
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
 			prefix = "default:";
 		else
 			prefix = NULL;
-		count = 0;
-		for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
-				continue;
-			r = archive_mstring_get_mbs_l(
-			    &ap->name, &name, &len, sc);
-			if (r != 0)
-				return (-1);
-			if (count > 0)
-				*p++ = separator;
-			if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-				id = ap->id;
-			else
-				id = -1;
-			append_entry(&p, prefix, ap->tag,
-			    name, ap->permset, id);
-			count ++;
+		r = archive_mstring_get_mbs_l(
+		    &ap->name, &name, &len, sc);
+		if (r != 0)
+			return (NULL);
+		if (count > 0)
+			*p++ = separator;
+		if (name == NULL ||
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
+			id = ap->id;
+		} else {
+			id = -1;
 		}
+		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
+		    ap->permset, id);
+		count++;
 	}
 
-	*acl_text = acl->acl_text;
-	if (acl_text_len != NULL)
-		*acl_text_len = strlen(acl->acl_text);
-	return (0);
+	/* Add terminating character */
+	*p++ = '\0';
+
+	len = strlen(s);
+
+	if ((ssize_t)len > (length - 1))
+		__archive_errx(1, "Buffer overrun");
+
+	if (text_len != NULL)
+		*text_len = len;
+
+	return (s);
 }
 
 static void
@@ -764,9 +1015,11 @@
 }
 
 static void
-append_entry(char **p, const char *prefix, int tag,
-    const char *name, int perm, int id)
+append_entry(char **p, const char *prefix, int type,
+    int tag, int flags, const char *name, int perm, int id)
 {
+	int i;
+
 	if (prefix != NULL) {
 		strcpy(*p, prefix);
 		*p += strlen(*p);
@@ -775,6 +1028,10 @@
 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
 		name = NULL;
 		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			strcpy(*p, "owner@");
+			break;
+		}
 		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_USER:
 		strcpy(*p, "user");
@@ -782,6 +1039,10 @@
 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 		name = NULL;
 		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			strcpy(*p, "group@");
+			break;
+		}
 		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_GROUP:
 		strcpy(*p, "group");
@@ -796,48 +1057,120 @@
 		name = NULL;
 		id = -1;
 		break;
+	case ARCHIVE_ENTRY_ACL_EVERYONE:
+		strcpy(*p, "everyone@");
+		name = NULL;
+		id = -1;
+		break;
 	}
 	*p += strlen(*p);
 	*(*p)++ = ':';
-	if (name != NULL) {
-		strcpy(*p, name);
-		*p += strlen(*p);
-	} else if (tag == ARCHIVE_ENTRY_ACL_USER
-	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
-		append_id(p, id);
-		id = -1;
+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+	    tag == ARCHIVE_ENTRY_ACL_USER ||
+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+		if (name != NULL) {
+			strcpy(*p, name);
+			*p += strlen(*p);
+		} else if (tag == ARCHIVE_ENTRY_ACL_USER
+		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+			append_id(p, id);
+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+				id = -1;
+		}
+		/* Solaris style has no second colon after other and mask */
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
+		    && tag != ARCHIVE_ENTRY_ACL_MASK))
+			*(*p)++ = ':';
 	}
-	*(*p)++ = ':';
-	*(*p)++ = (perm & 0444) ? 'r' : '-';
-	*(*p)++ = (perm & 0222) ? 'w' : '-';
-	*(*p)++ = (perm & 0111) ? 'x' : '-';
+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* POSIX.1e ACL perms */
+		*(*p)++ = (perm & 0444) ? 'r' : '-';
+		*(*p)++ = (perm & 0222) ? 'w' : '-';
+		*(*p)++ = (perm & 0111) ? 'x' : '-';
+	} else {
+		/* NFSv4 ACL perms */
+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+			if (perm & nfsv4_acl_perm_map[i].perm)
+				*(*p)++ = nfsv4_acl_perm_map[i].c;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*p)++ = '-';
+		}
+		*(*p)++ = ':';
+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+			if (perm & nfsv4_acl_flag_map[i].perm)
+				*(*p)++ = nfsv4_acl_flag_map[i].c;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*p)++ = '-';
+		}
+		*(*p)++ = ':';
+		switch (type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			strcpy(*p, "allow");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			strcpy(*p, "deny");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			strcpy(*p, "audit");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			strcpy(*p, "alarm");
+			break;
+		}
+		*p += strlen(*p);
+	}
 	if (id != -1) {
 		*(*p)++ = ':';
 		append_id(p, id);
 	}
-	**p = '\0';
 }
 
 /*
- * Parse a textual ACL.  This automatically recognizes and supports
- * extensions described above.  The 'type' argument is used to
- * indicate the type that should be used for any entries not
- * explicitly marked as "default:".
+ * Parse a wide ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
  */
 int
-archive_acl_parse_w(struct archive_acl *acl,
-    const wchar_t *text, int default_type)
+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
+    int want_type)
 {
 	struct {
 		const wchar_t *start;
 		const wchar_t *end;
-	} field[4], name;
+	} field[6], name;
 
-	int fields, n;
-	int type, tag, permset, id;
+	const wchar_t *s, *st;
+
+	int numfields, fields, n, r, sol, ret;
+	int type, types, tag, permset, id;
+	size_t len;
 	wchar_t sep;
 
-	while (text != NULL  &&  *text != L'\0') {
+	ret = ARCHIVE_OK;
+	types = 0;
+
+	switch (want_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		numfields = 5;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		numfields = 6;
+		break;
+	default:
+		return (ARCHIVE_FATAL);
+	}
+
+	while (text != NULL && *text != L'\0') {
 		/*
 		 * Parse the fields out of the next entry,
 		 * advance 'text' to start of next entry.
@@ -846,7 +1179,7 @@
 		do {
 			const wchar_t *start, *end;
 			next_field_w(&text, &start, &end, &sep);
-			if (fields < 4) {
+			if (fields < numfields) {
 				field[fields].start = start;
 				field[fields].end = end;
 			}
@@ -854,78 +1187,210 @@
 		} while (sep == L':');
 
 		/* Set remaining fields to blank. */
-		for (n = fields; n < 4; ++n)
+		for (n = fields; n < numfields; ++n)
 			field[n].start = field[n].end = NULL;
 
-		/* Check for a numeric ID in field 1 or 3. */
+		if (field[0].start != NULL && *(field[0].start) == L'#') {
+			/* Comment, skip entry */
+			continue;
+		}
+
+		n = 0;
+		sol = 0;
 		id = -1;
-		isint_w(field[1].start, field[1].end, &id);
-		/* Field 3 is optional. */
-		if (id == -1 && fields > 3)
-			isint_w(field[3].start, field[3].end, &id);
-
-		/*
-		 * Solaris extension:  "defaultuser::rwx" is the
-		 * default ACL corresponding to "user::rwx", etc.
-		 */
-		if (field[0].end - field[0].start > 7
-		    && wmemcmp(field[0].start, L"default", 7) == 0) {
-			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
-			field[0].start += 7;
-		} else
-			type = default_type;
-
+		permset = 0;
 		name.start = name.end = NULL;
-		if (prefix_w(field[0].start, field[0].end, L"user")) {
-			if (!ismode_w(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_USER;
+
+		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* POSIX.1e ACLs */
+			/*
+			 * Default keyword "default:user::rwx"
+			 * if found, we have one more field
+			 *
+			 * We also support old Solaris extension:
+			 * "defaultuser::rwx" is the default ACL corresponding
+			 * to "user::rwx", etc. valid only for first field
+			 */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			if (*s == L'd' && (len == 1 || (len >= 7
+			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+				if (len > 7)
+					field[0].start += 7;
+				else
+					n = 1;
+			} else
+				type = want_type;
+
+			/* Check for a numeric ID in field n+1 or n+3. */
+			isint_w(field[n + 1].start, field[n + 1].end, &id);
+			/* Field n+3 is optional. */
+			if (id == -1 && fields > n+3)
+				isint_w(field[n + 3].start, field[n + 3].end,
+				    &id);
+
+			tag = 0;
+			s = field[n].start;
+			st = field[n].start + 1;
+			len = field[n].end - field[n].start;
+
+			switch (*s) {
+			case L'u':
+				if (len == 1 || (len == 4
+				    && wmemcmp(st, L"ser", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case L'g':
+				if (len == 1 || (len == 5
+				    && wmemcmp(st, L"roup", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case L'o':
+				if (len == 1 || (len == 5
+				    && wmemcmp(st, L"ther", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			case L'm':
+				if (len == 1 || (len == 4
+				    && wmemcmp(st, L"ask", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			default:
+					break;
+			}
+
+			switch (tag) {
+			case ARCHIVE_ENTRY_ACL_OTHER:
+			case ARCHIVE_ENTRY_ACL_MASK:
+				if (fields == (n + 2)
+				    && field[n + 1].start < field[n + 1].end
+				    && ismode_w(field[n + 1].start,
+				    field[n + 1].end, &permset)) {
+					/* This is Solaris-style "other:rwx" */
+					sol = 1;
+				} else if (fields == (n + 3) &&
+				    field[n + 1].start < field[n + 1].end) {
+					/* Invalid mask or other field */
+					ret = ARCHIVE_WARN;
+					continue;
+				}
+				break;
+			case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+				if (id != -1 ||
+				    field[n + 1].start < field[n + 1].end) {
+					name = field[n + 1];
+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+						tag = ARCHIVE_ENTRY_ACL_USER;
+					else
+						tag = ARCHIVE_ENTRY_ACL_GROUP;
+				}
+				break;
+			default:
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			/*
+			 * Without "default:" we expect mode in field 2
+			 * Exception: Solaris other and mask fields
+			 */
+			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
+			    field[n + 2 - sol].end, &permset)) {
+				/* Invalid mode, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+		} else {
+			/* NFS4 ACLs */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			tag = 0;
+
+			switch (len) {
+			case 4:
+				if (wmemcmp(s, L"user", 4) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case 5:
+				if (wmemcmp(s, L"group", 5) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case 6:
+				if (wmemcmp(s, L"owner@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				else if (wmemcmp(s, L"group@", len) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 9:
+				if (wmemcmp(s, L"everyone@", 9) == 0)
+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			default:
+				break;
+			}
+
+			if (tag == 0) {
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+				n = 1;
 				name = field[1];
+				isint_w(name.start, name.end, &id);
 			} else
-				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
-			if (!ismode_w(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_GROUP;
-				name = field[1];
-			} else
-				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode_w(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "other:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode_w(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "other::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_OTHER;
-		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode_w(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "mask:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode_w(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "mask::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_MASK;
-		} else
-			return (ARCHIVE_WARN);
+				n = 0;
+
+			if (!is_nfs4_perms_w(field[1 + n].start,
+			    field[1 + n].end, &permset)) {
+				/* Invalid NFSv4 perms, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			if (!is_nfs4_flags_w(field[2 + n].start,
+			    field[2 + n].end, &permset)) {
+				/* Invalid NFSv4 flags, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			s = field[3 + n].start;
+			len = field[3 + n].end - field[3 + n].start;
+			type = 0;
+			if (len == 4) {
+				if (wmemcmp(s, L"deny", 4) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			} else if (len == 5) {
+				if (wmemcmp(s, L"allow", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				else if (wmemcmp(s, L"audit", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				else if (wmemcmp(s, L"alarm", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+			}
+			if (type == 0) {
+				/* Invalid entry type, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			isint_w(field[4 + n].start, field[4 + n].end, &id);
+		}
 
 		/* Add entry to the internal list. */
-		archive_acl_add_entry_w_len(acl, type, permset,
+		r = archive_acl_add_entry_w_len(acl, type, permset,
 		    tag, id, name.start, name.end - name.start);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r != ARCHIVE_OK)
+			ret = ARCHIVE_WARN;
+		types |= type;
 	}
-	return (ARCHIVE_OK);
+
+	/* Reset ACL */
+	archive_acl_reset(acl, types);
+
+	return (ret);
 }
 
 /*
@@ -971,16 +1436,122 @@
 	*permset = 0;
 	while (p < end) {
 		switch (*p++) {
-		case 'r': case 'R':
+		case L'r': case L'R':
 			*permset |= ARCHIVE_ENTRY_ACL_READ;
 			break;
-		case 'w': case 'W':
+		case L'w': case L'W':
 			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
 			break;
-		case 'x': case 'X':
+		case L'x': case L'X':
 			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
 			break;
-		case '-':
+		case L'-':
+			break;
+		default:
+			return (0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+	const wchar_t *p = start;
+
+	while (p < end) {
+		switch (*p++) {
+		case L'r':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+			break;
+		case L'w':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+			break;
+		case L'x':
+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			break;
+		case L'p':
+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+			break;
+		case L'D':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+			break;
+		case L'd':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
+			break;
+		case L'a':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+			break;
+		case L'A':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+			break;
+		case L'R':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+			break;
+		case L'W':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+			break;
+		case L'c':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+			break;
+		case L'C':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+			break;
+		case L'o':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+			break;
+		case L's':
+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+			break;
+		case L'-':
+			break;
+		default:
+			return(0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+	const wchar_t *p = start;
+
+	while (p < end) {
+		switch(*p++) {
+		case L'f':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+			break;
+		case L'd':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+			break;
+		case L'i':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+			break;
+		case L'n':
+			*permset |=
+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+			break;
+		case L'S':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+			break;
+		case L'F':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+			break;
+		case L'I':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+			break;
+		case L'-':
 			break;
 		default:
 			return (0);
@@ -1027,46 +1598,48 @@
 }
 
 /*
- * Return true if the characters [start...end) are a prefix of 'test'.
- * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
- */
-static int
-prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
-{
-	if (start == end)
-		return (0);
-
-	if (*start++ != *test++)
-		return (0);
-
-	while (start < end  &&  *start++ == *test++)
-		;
-
-	if (start < end)
-		return (0);
-
-	return (1);
-}
-
-/*
- * Parse a textual ACL.  This automatically recognizes and supports
- * extensions described above.  The 'type' argument is used to
- * indicate the type that should be used for any entries not
- * explicitly marked as "default:".
+ * Parse an ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
  */
 int
-archive_acl_parse_l(struct archive_acl *acl,
-    const char *text, int default_type, struct archive_string_conv *sc)
+archive_acl_from_text_l(struct archive_acl *acl, const char *text,
+    int want_type, struct archive_string_conv *sc)
 {
 	struct {
 		const char *start;
 		const char *end;
-	} field[4], name;
+	} field[6], name;
 
-	int fields, n, r, ret = ARCHIVE_OK;
-	int type, tag, permset, id;
+	const char *s, *st;
+	int numfields, fields, n, r, sol, ret;
+	int type, types, tag, permset, id;
+	size_t len;
 	char sep;
 
+	switch (want_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		numfields = 5;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		numfields = 6;
+		break;
+	default:
+		return (ARCHIVE_FATAL);
+	}
+
+	ret = ARCHIVE_OK;
+	types = 0;
+
 	while (text != NULL  &&  *text != '\0') {
 		/*
 		 * Parse the fields out of the next entry,
@@ -1076,7 +1649,7 @@
 		do {
 			const char *start, *end;
 			next_field(&text, &start, &end, &sep);
-			if (fields < 4) {
+			if (fields < numfields) {
 				field[fields].start = start;
 				field[fields].end = end;
 			}
@@ -1084,72 +1657,197 @@
 		} while (sep == ':');
 
 		/* Set remaining fields to blank. */
-		for (n = fields; n < 4; ++n)
+		for (n = fields; n < numfields; ++n)
 			field[n].start = field[n].end = NULL;
 
-		/* Check for a numeric ID in field 1 or 3. */
+		if (field[0].start != NULL && *(field[0].start) == '#') {
+			/* Comment, skip entry */
+			continue;
+		}
+
+		n = 0;
+		sol = 0;
 		id = -1;
-		isint(field[1].start, field[1].end, &id);
-		/* Field 3 is optional. */
-		if (id == -1 && fields > 3)
-			isint(field[3].start, field[3].end, &id);
-
-		/*
-		 * Solaris extension:  "defaultuser::rwx" is the
-		 * default ACL corresponding to "user::rwx", etc.
-		 */
-		if (field[0].end - field[0].start > 7
-		    && memcmp(field[0].start, "default", 7) == 0) {
-			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
-			field[0].start += 7;
-		} else
-			type = default_type;
-
+		permset = 0;
 		name.start = name.end = NULL;
-		if (prefix_c(field[0].start, field[0].end, "user")) {
-			if (!ismode(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_USER;
+
+		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* POSIX.1e ACLs */
+			/*
+			 * Default keyword "default:user::rwx"
+			 * if found, we have one more field
+			 *
+			 * We also support old Solaris extension:
+			 * "defaultuser::rwx" is the default ACL corresponding
+			 * to "user::rwx", etc. valid only for first field
+			 */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			if (*s == 'd' && (len == 1 || (len >= 7
+			    && memcmp((s + 1), "efault", 6) == 0))) {
+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+				if (len > 7)
+					field[0].start += 7;
+				else
+					n = 1;
+			} else
+				type = want_type;
+
+			/* Check for a numeric ID in field n+1 or n+3. */
+			isint(field[n + 1].start, field[n + 1].end, &id);
+			/* Field n+3 is optional. */
+			if (id == -1 && fields > (n + 3))
+				isint(field[n + 3].start, field[n + 3].end,
+				    &id);
+
+			tag = 0;
+			s = field[n].start;
+			st = field[n].start + 1;
+			len = field[n].end - field[n].start;
+
+			switch (*s) {
+			case 'u':
+				if (len == 1 || (len == 4
+				    && memcmp(st, "ser", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case 'g':
+				if (len == 1 || (len == 5
+				    && memcmp(st, "roup", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 'o':
+				if (len == 1 || (len == 5
+				    && memcmp(st, "ther", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			case 'm':
+				if (len == 1 || (len == 4
+				    && memcmp(st, "ask", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			default:
+					break;
+			}
+
+			switch (tag) {
+			case ARCHIVE_ENTRY_ACL_OTHER:
+			case ARCHIVE_ENTRY_ACL_MASK:
+				if (fields == (n + 2)
+				    && field[n + 1].start < field[n + 1].end
+				    && ismode(field[n + 1].start,
+				    field[n + 1].end, &permset)) {
+					/* This is Solaris-style "other:rwx" */
+					sol = 1;
+				} else if (fields == (n + 3) &&
+				    field[n + 1].start < field[n + 1].end) {
+					/* Invalid mask or other field */
+					ret = ARCHIVE_WARN;
+					continue;
+				}
+				break;
+			case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+				if (id != -1 ||
+				    field[n + 1].start < field[n + 1].end) {
+					name = field[n + 1];
+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+						tag = ARCHIVE_ENTRY_ACL_USER;
+					else
+						tag = ARCHIVE_ENTRY_ACL_GROUP;
+				}
+				break;
+			default:
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			/*
+			 * Without "default:" we expect mode in field 3
+			 * Exception: Solaris other and mask fields
+			 */
+			if (permset == 0 && !ismode(field[n + 2 - sol].start,
+			    field[n + 2 - sol].end, &permset)) {
+				/* Invalid mode, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+		} else {
+			/* NFS4 ACLs */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			tag = 0;
+
+			switch (len) {
+			case 4:
+				if (memcmp(s, "user", 4) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case 5:
+				if (memcmp(s, "group", 5) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case 6:
+				if (memcmp(s, "owner@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				else if (memcmp(s, "group@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 9:
+				if (memcmp(s, "everyone@", 9) == 0)
+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+				break;
+			default:
+				break;
+			}
+
+			if (tag == 0) {
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+				n = 1;
 				name = field[1];
+				isint(name.start, name.end, &id);
 			} else
-				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-		} else if (prefix_c(field[0].start, field[0].end, "group")) {
-			if (!ismode(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_GROUP;
-				name = field[1];
-			} else
-				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-		} else if (prefix_c(field[0].start, field[0].end, "other")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "other:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "other::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_OTHER;
-		} else if (prefix_c(field[0].start, field[0].end, "mask")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "mask:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "mask::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_MASK;
-		} else
-			return (ARCHIVE_WARN);
+				n = 0;
+
+			if (!is_nfs4_perms(field[1 + n].start,
+			    field[1 + n].end, &permset)) {
+				/* Invalid NFSv4 perms, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			if (!is_nfs4_flags(field[2 + n].start,
+			    field[2 + n].end, &permset)) {
+				/* Invalid NFSv4 flags, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			s = field[3 + n].start;
+			len = field[3 + n].end - field[3 + n].start;
+			type = 0;
+			if (len == 4) {
+				if (memcmp(s, "deny", 4) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			} else if (len == 5) {
+				if (memcmp(s, "allow", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				else if (memcmp(s, "audit", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				else if (memcmp(s, "alarm", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+			}
+			if (type == 0) {
+				/* Invalid entry type, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			isint(field[4 + n].start, field[4 + n].end,
+			    &id);
+		}
 
 		/* Add entry to the internal list. */
 		r = archive_acl_add_entry_len_l(acl, type, permset,
@@ -1158,7 +1856,12 @@
 			return (r);
 		if (r != ARCHIVE_OK)
 			ret = ARCHIVE_WARN;
+		types |= type;
 	}
+
+	/* Reset ACL */
+	archive_acl_reset(acl, types);
+
 	return (ret);
 }
 
@@ -1224,6 +1927,112 @@
 }
 
 /*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms(const char *start, const char *end, int *permset)
+{
+	const char *p = start;
+
+	while (p < end) {
+		switch (*p++) {
+		case 'r':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+			break;
+		case 'w':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+			break;
+		case 'x':
+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			break;
+		case 'p':
+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+			break;
+		case 'D':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+			break;
+		case 'd':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
+			break;
+		case 'a':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+			break;
+		case 'A':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+			break;
+		case 'R':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+			break;
+		case 'W':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+			break;
+		case 'c':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+			break;
+		case 'C':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+			break;
+		case 'o':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+			break;
+		case 's':
+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+			break;
+		case '-':
+			break;
+		default:
+			return(0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags(const char *start, const char *end, int *permset)
+{
+	const char *p = start;
+
+	while (p < end) {
+		switch(*p++) {
+		case 'f':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+			break;
+		case 'd':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+			break;
+		case 'i':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+			break;
+		case 'n':
+			*permset |=
+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+			break;
+		case 'S':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+			break;
+		case 'F':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+			break;
+		case 'I':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+			break;
+		case '-':
+			break;
+		default:
+			return (0);
+		}
+	}
+	return (1);
+}
+
+/*
  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
  * to point to just after the separator.  *start points to the first
  * character of the matched text and *end just after the last
@@ -1258,25 +2067,3 @@
 	if (**p != '\0')
 		(*p)++;
 }
-
-/*
- * Return true if the characters [start...end) are a prefix of 'test'.
- * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
- */
-static int
-prefix_c(const char *start, const char *end, const char *test)
-{
-	if (start == end)
-		return (0);
-
-	if (*start++ != *test++)
-		return (0);
-
-	while (start < end  &&  *start++ == *test++)
-		;
-
-	if (start < end)
-		return (0);
-
-	return (1);
-}
diff --git a/libarchive/archive_acl_private.h b/libarchive/archive_acl_private.h
index 1421adb..ef0b023 100644
--- a/libarchive/archive_acl_private.h
+++ b/libarchive/archive_acl_private.h
@@ -56,6 +56,7 @@
 void archive_acl_clear(struct archive_acl *);
 void archive_acl_copy(struct archive_acl *, struct archive_acl *);
 int archive_acl_count(struct archive_acl *, int);
+int archive_acl_types(struct archive_acl *);
 int archive_acl_reset(struct archive_acl *, int);
 int archive_acl_next(struct archive *, struct archive_acl *, int,
     int *, int *, int *, int *, const char **);
@@ -66,22 +67,17 @@
 int archive_acl_add_entry_len(struct archive_acl *,
     int, int, int, int, const char *, size_t);
 
-const wchar_t *archive_acl_text_w(struct archive *, struct archive_acl *, int);
-int archive_acl_text_l(struct archive_acl *, int, const char **, size_t *,
+wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,
+    struct archive *);
+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,
     struct archive_string_conv *);
 
 /*
- * Private ACL parser.  This is private because it handles some
- * very weird formats that clients should not be messing with.
- * Clients should only deal with their platform-native formats.
- * Because of the need to support many formats cleanly, new arguments
- * are likely to get added on a regular basis.  Clients who try to use
- * this interface are likely to be surprised when it changes.
+ * ACL text parser.
  */
-int archive_acl_parse_w(struct archive_acl *,
-		    const wchar_t *, int /* type */);
-int archive_acl_parse_l(struct archive_acl *,
-		    const char *, int /* type */,
-		    struct archive_string_conv *);
+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,
+    int /* type */);
+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,
+    int /* type */, struct archive_string_conv *);
 
 #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/libarchive/archive_check_magic.c b/libarchive/archive_check_magic.c
index c695e58..288ce23 100644
--- a/libarchive/archive_check_magic.c
+++ b/libarchive/archive_check_magic.c
@@ -62,7 +62,7 @@
 	}
 }
 
-static void
+static __LA_DEAD void
 diediedie(void)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
diff --git a/libarchive/archive_digest_private.h b/libarchive/archive_digest_private.h
index b58ffb3..b4fd6ca 100644
--- a/libarchive/archive_digest_private.h
+++ b/libarchive/archive_digest_private.h
@@ -143,6 +143,7 @@
   defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
   defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
   defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#include <windows.h>
 #include <wincrypt.h>
 typedef struct {
   int   valid;
diff --git a/libarchive/archive_entry.3 b/libarchive/archive_entry.3
index f5e22af..f75916c 100644
--- a/libarchive/archive_entry.3
+++ b/libarchive/archive_entry.3
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd Feburary 2, 2012
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY 3
 .Os
 .Sh NAME
diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c
index b05eb4d..e268194 100644
--- a/libarchive/archive_entry.c
+++ b/libarchive/archive_entry.c
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -400,7 +401,7 @@
 	return (NULL);
 }
 
-int64_t
+la_int64_t
 archive_entry_gid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_gid);
@@ -501,7 +502,7 @@
 	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));
 }
 
-int64_t
+la_int64_t
 archive_entry_ino(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ino);
@@ -513,7 +514,7 @@
 	return (entry->ae_set & AE_SET_INO);
 }
 
-int64_t
+la_int64_t
 archive_entry_ino64(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ino);
@@ -626,7 +627,7 @@
 		return minor(entry->ae_stat.aest_rdev);
 }
 
-int64_t
+la_int64_t
 archive_entry_size(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_size);
@@ -714,7 +715,7 @@
 	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));
 }
 
-int64_t
+la_int64_t
 archive_entry_uid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_uid);
@@ -818,7 +819,7 @@
 }
 
 void
-archive_entry_set_gid(struct archive_entry *entry, int64_t g)
+archive_entry_set_gid(struct archive_entry *entry, la_int64_t g)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_gid = g;
@@ -867,7 +868,7 @@
 }
 
 void
-archive_entry_set_ino(struct archive_entry *entry, int64_t ino)
+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
 {
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_INO;
@@ -875,7 +876,7 @@
 }
 
 void
-archive_entry_set_ino64(struct archive_entry *entry, int64_t ino)
+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
 {
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_INO;
@@ -1208,7 +1209,7 @@
 }
 
 void
-archive_entry_set_size(struct archive_entry *entry, int64_t s)
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_size = s;
@@ -1305,7 +1306,7 @@
 }
 
 void
-archive_entry_set_uid(struct archive_entry *entry, int64_t u)
+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_uid = u;
@@ -1446,7 +1447,7 @@
 int
 archive_entry_acl_types(struct archive_entry *entry)
 {
-	return ((&entry->acl)->acl_types);
+	return (archive_acl_types(&entry->acl));
 }
 
 /*
@@ -1486,34 +1487,121 @@
 }
 
 /*
- * Generate a text version of the ACL.  The flags parameter controls
+ * Generate a text version of the ACL. The flags parameter controls
  * the style of the generated ACL.
  */
+wchar_t *
+archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len,
+    int flags)
+{
+	return (archive_acl_to_text_w(&entry->acl, len, flags,
+	    entry->archive));
+}
+
+char *
+archive_entry_acl_to_text(struct archive_entry *entry, ssize_t *len,
+    int flags)
+{
+	return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));
+}
+
+char *
+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,
+   int flags, struct archive_string_conv *sc)
+{
+	return (archive_acl_to_text_l(&entry->acl, len, flags, sc));
+}
+
+/*
+ * ACL text parser.
+ */
+int
+archive_entry_acl_from_text_w(struct archive_entry *entry,
+    const wchar_t *wtext, int type)
+{
+	return (archive_acl_from_text_w(&entry->acl, wtext, type));
+}
+
+int
+archive_entry_acl_from_text(struct archive_entry *entry,
+    const char *text, int type)
+{
+	return (archive_acl_from_text_l(&entry->acl, text, type, NULL));
+}
+
+int
+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,
+    int type, struct archive_string_conv *sc)
+{
+	return (archive_acl_from_text_l(&entry->acl, text, type, sc));
+}
+
+/* Deprecated */
+static int
+archive_entry_acl_text_compat(int *flags)
+{
+	if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)
+		return (1);
+
+	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */
+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)
+		*flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;
+
+	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */
+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+		*flags |=  ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+	*flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;
+
+	return (0);
+}
+
+/* Deprecated */
 const wchar_t *
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
-	const wchar_t *r;
-	r = archive_acl_text_w(entry->archive, &entry->acl, flags);
-	if (r == NULL && errno == ENOMEM)
-		__archive_errx(1, "No memory");
-	return (r);
+	if (entry->acl.acl_text_w != NULL) {
+		free(entry->acl.acl_text_w);
+		entry->acl.acl_text_w = NULL;
+	}
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
+		    NULL, flags, entry->archive);
+	return (entry->acl.acl_text_w);
 }
 
+/* Deprecated */
 const char *
 archive_entry_acl_text(struct archive_entry *entry, int flags)
 {
-	const char *p;
-	if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0
-	    && errno == ENOMEM)
-		__archive_errx(1, "No memory");
-	return (p);
+	if (entry->acl.acl_text != NULL) {
+		free(entry->acl.acl_text);
+		entry->acl.acl_text = NULL;
+	}
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
+		    flags, NULL);
+
+	return (entry->acl.acl_text);
 }
 
+/* Deprecated */
 int
 _archive_entry_acl_text_l(struct archive_entry *entry, int flags,
     const char **acl_text, size_t *len, struct archive_string_conv *sc)
 {
-	return (archive_acl_text_l(&entry->acl, flags, acl_text, len, sc));
+	if (entry->acl.acl_text != NULL) {
+		free(entry->acl.acl_text);
+		entry->acl.acl_text = NULL;
+        }
+
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
+		    (ssize_t *)len, flags, sc);
+
+	*acl_text = entry->acl.acl_text;
+
+	return (0);
 }
 
 /*
@@ -1561,7 +1649,10 @@
 	{ "nosappnd",	L"nosappnd",		SF_APPEND,	0 },
 	{ "nosappend",	L"nosappend",		SF_APPEND,	0 },
 #endif
-#ifdef  EXT2_APPEND_FL				/* 'a' */
+#if defined(FS_APPEND_FL)			/* 'a' */
+	{ "nosappnd",	L"nosappnd",		FS_APPEND_FL,	0 },
+	{ "nosappend",	L"nosappend",		FS_APPEND_FL,	0 },
+#elif defined(EXT2_APPEND_FL)			/* 'a' */
 	{ "nosappnd",	L"nosappnd",		EXT2_APPEND_FL,	0 },
 	{ "nosappend",	L"nosappend",		EXT2_APPEND_FL,	0 },
 #endif
@@ -1574,7 +1665,11 @@
 	{ "noschange",	L"noschange",		SF_IMMUTABLE,	0 },
 	{ "nosimmutable",	L"nosimmutable",	SF_IMMUTABLE,	0 },
 #endif
-#ifdef EXT2_IMMUTABLE_FL			/* 'i' */
+#if defined(FS_IMMUTABLE_FL)			/* 'i' */
+	{ "noschg",	L"noschg",		FS_IMMUTABLE_FL,	0 },
+	{ "noschange",	L"noschange",		FS_IMMUTABLE_FL,	0 },
+	{ "nosimmutable",	L"nosimmutable",	FS_IMMUTABLE_FL,	0 },
+#elif defined(EXT2_IMMUTABLE_FL)		/* 'i' */
 	{ "noschg",	L"noschg",		EXT2_IMMUTABLE_FL,	0 },
 	{ "noschange",	L"noschange",		EXT2_IMMUTABLE_FL,	0 },
 	{ "nosimmutable",	L"nosimmutable",	EXT2_IMMUTABLE_FL,	0 },
@@ -1598,7 +1693,9 @@
 #ifdef UF_NODUMP
 	{ "nodump",	L"nodump",		0,		UF_NODUMP},
 #endif
-#ifdef EXT2_NODUMP_FL				/* 'd' */
+#if defined(FS_NODUMP_FL)	/* 'd' */
+	{ "nodump",	L"nodump",		0,		FS_NODUMP_FL},
+#elif defined(EXT2_NODUMP_FL) 	/* 'd' */
 	{ "nodump",	L"nodump",		0,		EXT2_NODUMP_FL},
 #endif
 #ifdef UF_OPAQUE
@@ -1611,65 +1708,124 @@
 #ifdef UF_COMPRESSED
 	{ "nocompressed",L"nocompressed",	UF_COMPRESSED,	0 },
 #endif
-#ifdef EXT2_UNRM_FL
+#if defined(FS_UNRM_FL)
+        { "nouunlink",	L"nouunlink",		FS_UNRM_FL,	0},
+#elif defined(EXT2_UNRM_FL)
         { "nouunlink",	L"nouunlink",		EXT2_UNRM_FL,	0},
 #endif
 
-#ifdef EXT2_BTREE_FL
+#if defined(FS_BTREE_FL)
+        { "nobtree",	L"nobtree",       	FS_BTREE_FL,	0 },
+#elif defined(EXT2_BTREE_FL)
         { "nobtree",	L"nobtree",       	EXT2_BTREE_FL,	0 },
 #endif
 
-#ifdef EXT2_ECOMPR_FL
+#if defined(FS_ECOMPR_FL)
+        { "nocomperr",	L"nocomperr",       	FS_ECOMPR_FL,	0 },
+#elif defined(EXT2_ECOMPR_FL)
         { "nocomperr",	L"nocomperr",       	EXT2_ECOMPR_FL,	0 },
 #endif
 
-#ifdef EXT2_COMPR_FL				/* 'c' */
+#if defined(FS_COMPR_FL)			/* 'c' */
+        { "nocompress",	L"nocompress",       	FS_COMPR_FL,	0 },
+#elif defined(EXT2_COMPR_FL)			/* 'c' */
         { "nocompress",	L"nocompress",       	EXT2_COMPR_FL,	0 },
 #endif
 
-#ifdef EXT2_NOATIME_FL				/* 'A' */
+#if defined(FS_NOATIME_FL)			/* 'A' */
+        { "noatime",	L"noatime",		0,		FS_NOATIME_FL},
+#elif defined(EXT2_NOATIME_FL)			/* 'A' */
         { "noatime",	L"noatime",		0,		EXT2_NOATIME_FL},
 #endif
 
-#ifdef EXT2_DIRTY_FL
+#if defined(FS_DIRTY_FL)
+        { "nocompdirty",L"nocompdirty",		FS_DIRTY_FL,		0},
+#elif defined(EXT2_DIRTY_FL)
         { "nocompdirty",L"nocompdirty",		EXT2_DIRTY_FL,		0},
 #endif
 
-#ifdef EXT2_COMPRBLK_FL
-#ifdef EXT2_NOCOMPR_FL
+#if defined(FS_COMPRBLK_FL)
+#if defined(FS_NOCOMPR_FL)
+        { "nocomprblk",	L"nocomprblk",		FS_COMPRBLK_FL, FS_NOCOMPR_FL},
+#else
+        { "nocomprblk",	L"nocomprblk",		FS_COMPRBLK_FL,	0},
+#endif
+#elif defined(EXT2_COMPRBLK_FL)
+#if defined(EXT2_NOCOMPR_FL)
         { "nocomprblk",	L"nocomprblk",		EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL},
 #else
         { "nocomprblk",	L"nocomprblk",		EXT2_COMPRBLK_FL,	0},
 #endif
 #endif
-#ifdef EXT2_DIRSYNC_FL
+#if defined(FS_DIRSYNC_FL)
+        { "nodirsync",	L"nodirsync",		FS_DIRSYNC_FL,	0},
+#elif defined(EXT2_DIRSYNC_FL)
         { "nodirsync",	L"nodirsync",		EXT2_DIRSYNC_FL,	0},
 #endif
-#ifdef EXT2_INDEX_FL
+#if defined(FS_INDEX_FL)
+        { "nohashidx",	L"nohashidx",		FS_INDEX_FL,		0},
+#elif defined(EXT2_INDEX_FL)
         { "nohashidx",	L"nohashidx",		EXT2_INDEX_FL,		0},
 #endif
-#ifdef EXT2_IMAGIC_FL
+#if defined(FS_IMAGIC_FL)
+        { "noimagic",	L"noimagic",		FS_IMAGIC_FL,		0},
+#elif defined(EXT2_IMAGIC_FL)
         { "noimagic",	L"noimagic",		EXT2_IMAGIC_FL,		0},
 #endif
-#ifdef EXT3_JOURNAL_DATA_FL
+#if defined(FS_JOURNAL_DATA_FL)
+        { "nojournal",	L"nojournal",		FS_JOURNAL_DATA_FL,	0},
+#elif defined(EXT3_JOURNAL_DATA_FL)
         { "nojournal",	L"nojournal",		EXT3_JOURNAL_DATA_FL,	0},
 #endif
-#ifdef EXT2_SECRM_FL
+#if defined(FS_SECRM_FL)
+        { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL,		0},
+#elif defined(EXT2_SECRM_FL)
         { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL,		0},
 #endif
-#ifdef EXT2_SYNC_FL
+#if defined(FS_SYNC_FL)
+        { "nosync",	L"nosync",		FS_SYNC_FL,		0},
+#elif defined(EXT2_SYNC_FL)
         { "nosync",	L"nosync",		EXT2_SYNC_FL,		0},
 #endif
-#ifdef EXT2_NOTAIL_FL
+#if defined(FS_NOTAIL_FL)
+        { "notail",	L"notail",		0,		FS_NOTAIL_FL},
+#elif defined(EXT2_NOTAIL_FL)
         { "notail",	L"notail",		0,		EXT2_NOTAIL_FL},
 #endif
-#ifdef EXT2_TOPDIR_FL
+#if defined(FS_TOPDIR_FL)
+        { "notopdir",	L"notopdir",		FS_TOPDIR_FL,		0},
+#elif defined(EXT2_TOPDIR_FL)
         { "notopdir",	L"notopdir",		EXT2_TOPDIR_FL,		0},
 #endif
-#ifdef EXT2_RESERVED_FL
+#ifdef FS_ENCRYPT_FL
+        { "noencrypt",	L"noencrypt",		FS_ENCRYPT_FL,	0},
+#endif
+#ifdef FS_HUGE_FILE_FL
+        { "nohugefile",	L"nohugefile",		FS_HUGE_FILE_FL,	0},
+#endif
+#ifdef FS_EXTENT_FL
+        { "noextent",	L"noextent",		FS_EXTENT_FL,	0},
+#endif
+#ifdef FS_EA_INODE_FL
+        { "noeainode",	L"noeainode",		FS_EA_INODE_FL,	0},
+#endif
+#ifdef FS_EOFBLOCKS_FL
+        { "noeofblocks",L"noeofblocks",		FS_EOFBLOCKS_FL,	0},
+#endif
+#ifdef FS_NOCOW_FL
+        { "nocow",	L"nocow",		FS_NOCOW_FL,	0},
+#endif
+#ifdef FS_INLINE_DATA_FL
+        { "noinlinedata",L"noinlinedata",	FS_INLINE_DATA_FL,	0},
+#endif
+#ifdef FS_PROJINHERIT_FL
+        { "noprojinherit",L"noprojinherit",	FS_PROJINHERIT_FL,	0},
+#endif
+#if defined(FS_RESERVED_FL)
+        { "noreserved",	L"noreserved",		FS_RESERVED_FL,	0},
+#elif defined(EXT2_RESERVED_FL)
         { "noreserved",	L"noreserved",		EXT2_RESERVED_FL,	0},
 #endif
-
 	{ NULL,		NULL,			0,		0 }
 };
 
diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h
index b6a5d89..bcc2962 100644
--- a/libarchive/archive_entry.h
+++ b/libarchive/archive_entry.h
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +30,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3002002
+#define	ARCHIVE_VERSION_NUMBER 3003002
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
@@ -65,6 +66,27 @@
 # endif
 #endif
 
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+#  elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+#  else
+typedef long la_ssize_t;
+#  endif
+# else
+# include <unistd.h>  /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
 /* Get a suitable definition for mode_t */
 #if ARCHIVE_VERSION_NUMBER >= 3999000
 /* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */
@@ -104,6 +126,12 @@
 # define __LA_DECL
 #endif
 
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -420,6 +448,7 @@
 /*
  * Inheritance values (NFS4 ACLs only); included in permset.
  */
+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000
 #define	ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000
 #define	ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000
 #define	ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000
@@ -433,15 +462,16 @@
 	    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT	\
 	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY		\
 	    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS		\
-	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS)
+	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
 
 /* We need to be able to specify combinations of these. */
-#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	256  /* POSIX.1e only */
-#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	512  /* POSIX.1e only */
-#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	1024 /* NFS4 only */
-#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	2048 /* NFS4 only */
-#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	4096 /* NFS4 only */
-#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	8192 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	0x00000100  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	0x00000200  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	0x00000400 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	0x00000800 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	0x00001000 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	0x00002000 /* NFS4 only */
 #define	ARCHIVE_ENTRY_ACL_TYPE_POSIX1E	(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
 	    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
 #define	ARCHIVE_ENTRY_ACL_TYPE_NFS4	(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
@@ -492,21 +522,48 @@
  * Construct a text-format ACL.  The flags argument is a bitmask that
  * can include any of the following:
  *
+ * Flags only for archive entries with POSIX.1e ACL:
  * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
  * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
- * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - Include NFS4 entries.
- * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
- *    each ACL entry.  ('star' introduced this for POSIX.1e, this flag
- *    also applies to NFS4.)
  * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
- *    default ACL entry, as used in old Solaris ACLs.
+ *    default ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
+ *    "mask" entries.
+ *
+ * Flags only for archive entries with NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for
+ *    unset permissions and flags in NFSv4 ACL permission and flag fields
+ *
+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
+ *    instead of newline.
  */
-#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	1024
-#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
+#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	0x00000001
+#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	0x00000002
+#define	ARCHIVE_ENTRY_ACL_STYLE_SOLARIS		0x00000004
+#define	ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA	0x00000008
+#define	ARCHIVE_ENTRY_ACL_STYLE_COMPACT		0x00000010
+
+__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
+	    la_ssize_t * /* len */, int /* flags */);
+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
+	    la_ssize_t * /* len */, int /* flags */);
+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
+	    const wchar_t * /* wtext */, int /* type */);
+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
+	    const char * /* text */, int /* type */);
+
+/* Deprecated constants */
+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID		1024
+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
+
+/* Deprecated functions */
 __LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
-		    int /* flags */);
+		    int /* flags */) __LA_DEPRECATED;
 __LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
-		    int /* flags */);
+		    int /* flags */) __LA_DEPRECATED;
 
 /* Return bitmask of ACL types in an archive entry */
 __LA_DECL int	 archive_entry_acl_types(struct archive_entry *);
diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3
index e85c4de..534dbfa 100644
--- a/libarchive/archive_entry_acl.3
+++ b/libarchive/archive_entry_acl.3
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2010 Joerg Sonnenberger
+.\" Copyright (c) 2016 Martin Matuska
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 2, 2012
+.Dd February 15, 2017
 .Dt ARCHIVE_ENTRY_ACL 3
 .Os
 .Sh NAME
@@ -30,10 +31,13 @@
 .Nm archive_entry_acl_add_entry_w ,
 .Nm archive_entry_acl_clear ,
 .Nm archive_entry_acl_count ,
+.Nm archive_entry_acl_from_text ,
+.Nm archive_entry_acl_from_text_w ,
 .Nm archive_entry_acl_next ,
 .Nm archive_entry_acl_next_w ,
 .Nm archive_entry_acl_reset ,
-.Nm archive_entry_acl_text_w ,
+.Nm archive_entry_acl_to_text ,
+.Nm archive_entry_acl_to_text_w ,
 .Nm archive_entry_acl_types
 .Nd functions for manipulating Access Control Lists in archive entry descriptions
 .Sh LIBRARY
@@ -63,6 +67,18 @@
 .Ft int
 .Fn archive_entry_acl_count "struct archive_entry *a" "int type"
 .Ft int
+.Fo archive_entry_acl_from_text
+.Fa "struct archive_entry *a"
+.Fa "const char *text"
+.Fa "int type"
+.Fc
+.Ft int
+.Fo archive_entry_acl_from_text_w
+.Fa "struct archive_entry *a"
+.Fa "const wchar_t *text"
+.Fa "int type"
+.Fc
+.Ft int
 .Fo archive_entry_acl_next
 .Fa "struct archive_entry *a"
 .Fa "int type"
@@ -84,33 +100,48 @@
 .Fc
 .Ft int
 .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 char *
+.Fo archive_entry_acl_to_text
+.Fa "struct archive_entry *a"
+.Fa "ssize_t *len_p"
+.Fa "int flags"
+.Fc
+.Ft wchar_t *
+.Fo archive_entry_acl_to_text_w
+.Fa "struct archive_entry *a"
+.Fa "ssize_t *len_p"
+.Fa "int flags"
+.Fc
 .Ft int
 .Fn archive_entry_acl_types "struct archive_entry *a"
 .\" enum?
 .Sh DESCRIPTION
-An
-.Dq Access Control List
-is a generalisation of the classic Unix permission system.
+The
+.Dq Access Control Lists (ACLs)
+extend the standard Unix perssion model.
 The ACL interface of
 .Nm libarchive
-is derived from the POSIX.1e draft, but restricted to simplify dealing
-with practical implementations in various Operating Systems and archive formats.
-.Pp
-An ACL consists of a number of independent entries.
+supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by
+various levels of ACL support in operating systems, file systems and archive
+formats.
+.Ss POSIX.1e Access Control Lists
+A POSIX.1e ACL consists of a number of independent entries.
 Each entry specifies the permission set as bitmask of basic permissions.
-Valid permissions are:
+Valid permissions in the
+.Fa permset
+are:
 .Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_EXECUTE"
-.It Dv ARCHIVE_ENTRY_ACL_EXECUTE
-.It Dv ARCHIVE_ENTRY_ACL_WRITE
-.It Dv ARCHIVE_ENTRY_ACL_READ
+.It Dv ARCHIVE_ENTRY_ACL_READ ( Sy r )
+.It Dv ARCHIVE_ENTRY_ACL_WRITE ( Sy w )
+.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x )
 .El
 The permissions correspond to the normal Unix permissions.
 .Pp
-The tag specifies the principal to which the permission applies.
+The
+.Fa tag
+specifies the principal to which the permission applies.
 Valid values are:
-.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
 .It Dv ARCHIVE_ENTRY_ACL_USER
 The user specified by the name field.
 .It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
@@ -122,8 +153,9 @@
 .It Dv ARCHIVE_ENTRY_ACL_MASK
 The maximum permissions to be obtained via group permissions.
 .It Dv ARCHIVE_ENTRY_ACL_OTHER
-Any principal who doesn't have a user or group entry.
+Any principal who is not file owner or a member of the owning group.
 .El
+.Pp
 The principals
 .Dv ARCHIVE_ENTRY_ACL_USER_OBJ ,
 .Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
@@ -132,19 +164,123 @@
 are equivalent to user, group and other in the classic Unix permission
 model and specify non-extended ACL entries.
 .Pp
-All files have an access ACL
+All files with have an access ACL
 .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS .
 This specifies the permissions required for access to the file itself.
 Directories have an additional ACL
 .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT ,
 which controls the initial access ACL for newly created directory entries.
+.Ss NFSv4 Access Control Lists
+A NFSv4 ACL consists of multiple individual entries called Access Control
+Entries (ACEs).
 .Pp
+There are four possible types of a NFSv4 ACE:
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYE_ALLOW"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW
+Allow principal to perform actions requiring given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY
+Prevent principal from performing actions requiring given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT
+Log access attempts by principal which require given permissions.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM
+Trigger a system alarm on access attempts by principal which require given
+permissions.
+.El
+.Pp
+The
+.Fa tag
+specifies the principal to which the permission applies.
+Valid values are:
+.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_GROUP_OBJ"
+.It Dv ARCHIVE_ENTRY_ACL_USER
+The user specified by the name field.
+.It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
+The owner of the file.
+.It Dv ARCHIVE_ENTRY_ACL_GROUP
+The group specied by the name field.
+.It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
+The group who owns the file.
+.It Dv ARCHIVE_ENTRY_ACL_EVERYONE
+Any principal who is not file owner or a member of the owning group.
+.El
+.Pp
+Entries with the
+.Dv ARCHIVE_ENTRY_ACL_USER
+or
+.Dv ARCHIVE_ENTRY_ACL_GROUP
+tag store the user and group name in the
+.Fa name
+string and optionally the user or group ID in the
+.Fa qualifier
+integer.
+.Pp
+NFSv4 ACE permissions and flags are stored in the same
+.Fa permset
+bitfield. Some permissions share the same constant and permission character but
+have different effect on directories than on files. The following ACE
+permissions are supported:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r )
+Read data (file).
+.It Dv ARCHIVE_ENTRY_ACL_LIST_DIRECTORY ( Sy r )
+List entries (directory).
+.It ARCHIVE_ENTRY_ACL_WRITE_DATA ( Sy w )
+Write data (file).
+.It ARCHIVE_ENTRY_ACL_ADD_FILE ( Sy w )
+Create files (directory).
+.It Dv ARCHIVE_ENTRY_ACL_EXECUTE ( Sy x )
+Execute file or change into a directory.
+.It Dv ARCHIVE_ENTRY_ACL_APPEND_DATA ( Sy p )
+Append data (file).
+.It Dv ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY ( Sy p )
+Create subdirectories (directory).
+.It Dv ARCHIVE_ENTRY_ACL_DELETE_CHILD ( Sy D )
+Remove files and subdirectories inside a directory.
+.It Dv ARCHIVE_ENTRY_ACL_DELETE ( Sy d )
+Remove file or directory.
+.It Dv ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES ( Sy a )
+Read file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES ( Sy A )
+Write file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS ( Sy R )
+Read named file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS ( Sy W )
+Write named file or directory attributes.
+.It Dv ARCHIVE_ENTRY_ACL_READ_ACL ( Sy c )
+Read file or directory ACL.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_ACL ( Sy C )
+Write file or directory ACL.
+.It Dv ARCHIVE_ENTRY_ACL_WRITE_OWNER ( Sy o )
+Change owner of a file or directory.
+.It Dv ARCHIVE_ENTRY_ACL_SYNCHRONIZE ( Sy s )
+Use synchronous I/O.
+.El
+.Pp
+The following NFSv4 ACL inheritance flags are supported:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT ( Sy f )
+Inherit parent directory ACE to files.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT ( Sy d )
+Inherit parent directory ACE to subdirectories.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i )
+Only inherit, do not apply the permission on the directory itself.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n )
+Do not propagate inherit flags. Only first-level entries inherit ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S )
+Trigger alarm or audit on successful access.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F )
+Trigger alarm or audit on failed access.
+.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERITED ( Sy I )
+Mark that ACE was inherited.
+.El
+.Ss Functions
 .Fn archive_entry_acl_add_entry
 and
 .Fn archive_entry_acl_add_entry_w
 add a single ACL entry.
 For the access ACL and non-extended principals, the classic Unix permissions
-are updated.
+are updated. An archive entry cannot contain both POSIX.1e and NFSv4 ACL
+entries.
 .Pp
 .Fn archive_entry_acl_clear
 removes all ACL entries and resets the enumeration pointer.
@@ -153,13 +289,57 @@
 counts the ACL entries that have the given type mask.
 .Fa type
 can be the bitwise-or of
-.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
-and
-.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT .
-If
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+.El
+for POSIX.1e ACLs and
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_ALLOW"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM
+.El
+for NFSv4 ACLs. For POSIX.1e ACLs if
 .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 is included and at least one extended ACL entry is found,
-the three non-extened ACLs are added.
+the three non-extended ACLs are added.
+.Pp
+.Fn archive_entry_acl_from_text
+and
+.Fn archive_entry_acl_from_text_w
+add new
+.Pq or merge with existing
+ACL entries from
+.Pq wide
+text. The argument
+.Fa type
+may take one of the following values:
+.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT"
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4
+.El
+Supports all formats that can be created with
+.Fn archive_entry_acl_to_text
+or respective
+.Fn archive_entry_acl_to_text_w .
+Existing ACL entries are preserved. To get a clean new ACL from text
+.Fn archive_entry_acl_clear
+must be called first. Entries prefixed with
+.Dq default:
+are treated as
+.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+unless
+.Fa type
+is
+.Dv ARCHIVE_ENTRY_ACL_TYPE_NFS4 .
+Invalid entries, non-parseable ACL entries and entries beginning with
+the
+.Sq #
+character
+.Pq comments
+are skipped.
 .Pp
 .Fn archive_entry_acl_next
 and
@@ -182,19 +362,57 @@
 Otherwise, the function returns the same value as
 .Fn archive_entry_acl_count .
 .Pp
-.Fn archive_entry_acl_text_w
-converts the ACL entries for the given type mask into a wide string.
-In addition to the normal type flags,
-.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
+.Fn archive_entry_acl_to_text
 and
-.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
-can be specified to further customize the result.
-The returned long string is valid until the next call to
-.Fn archive_entry_acl_clear ,
-.Fn archive_entry_acl_add_entry ,
-.Fn archive_entry_acl_add_entry_w
+.Fn archive_entry_acl_to_text_w
+convert the ACL entries for the given type into a
+.Pq wide
+string of ACL entries separated by newline. If the pointer
+.Fa len_p
+is not NULL, then the function shall return the length of the string
+.Pq not including the NULL terminator
+in the location pointed to by
+.Fa len_p .
+The
+.Fa flag
+argument is a bitwise-or.
+.Pp
+The following flags are effective only on POSIX.1e ACL:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+Output access ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+Output POSIX.1e default ACLs.
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
+Prefix each default ACL entry with the word
+.Dq default: .
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_SOLARIS
+The mask and other ACLs don not contain a double colon.
+.El
+.Pp
+The following flags are effecive only on NFSv4 ACL:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_COMPACT
+Do not output minus characters for unset permissions and flags in NFSv4 ACL
+permission and flag fields.
+.El
+.Pp
+The following flags are effective on both POSIX.1e and NFSv4 ACL:
+.Bl -tag -offset indent -compact -width ARCHIV
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
+Add an additional colon-separated field containing the user or group id.
+.It Dv ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA
+Separate ACL entries with comma instead of newline.
+.El
+.Pp
+If the archive entry contains NFSv4 ACLs, all types of NFSv4 ACLs are returned.
+It the entry contains POSIX.1e ACLs and none of the flags
+.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 or
-.Fn archive_entry_acl_text_w .
+.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
+are specified, both access and default entries are returned and default entries
+are prefixed with
+.Dq default: .
 .Pp
 .Fn archive_entry_acl_types
 get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4
@@ -205,11 +423,20 @@
 and
 .Fn archive_entry_acl_reset
 returns the number of ACL entries that match the given type mask.
-If the type mask includes
+For POSIX.1e ACLS if the type mask includes
 .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 and at least one extended ACL entry exists, the three classic Unix
 permissions are counted.
 .Pp
+.Fn archive_entry_acl_from_text
+and
+.Fn archive_entry_acl_from_text_w
+return
+.Dv ARCHIVE_OK
+if all entries were successfully parsed and
+.Dv ARCHIVE_WARN
+if one or more entries were invalid or non-parseable.
+.Pp
 .Fn archive_entry_acl_next
 and
 .Fn archive_entry_acl_next_w
@@ -224,23 +451,16 @@
 .Fn archive_entry_acl_reset
 has not been called first.
 .Pp
-.Fn archive_entry_text_w
-returns a wide string representation of the ACL entrise matching the
-given type mask.
-The returned long string is valid until the next call to
-.Fn archive_entry_acl_clear ,
-.Fn archive_entry_acl_add_entry ,
-.Fn archive_entry_acl_add_entry_w
-or
-.Fn archive_entry_acl_text_w .
+.Fn archive_entry_acl_to_text
+returns a string representing the ACL entries matching the given type and
+flags on success or NULL on error.
+.Pp
+.Fn archive_entry_acl_to_text_w
+returns a wide string representing the ACL entries matching the given type
+and flags on success or NULL on error.
 .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 ,
-.Sh BUGS
-.Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID
-and
-.Dv ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT
-are not documented.
+.Xr archive_entry 3 ,
+.Xr libarchive 3
diff --git a/libarchive/archive_entry_locale.h b/libarchive/archive_entry_locale.h
index 02e024a..44550c5 100644
--- a/libarchive/archive_entry_locale.h
+++ b/libarchive/archive_entry_locale.h
@@ -63,9 +63,13 @@
     const char **, size_t *, struct archive_string_conv *);
 #define archive_entry_acl_text_l _archive_entry_acl_text_l
 int _archive_entry_acl_text_l(struct archive_entry *, int,
-    const char **, size_t *, struct archive_string_conv *);
-
-
+const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED;
+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l
+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,
+    struct archive_string_conv *);
+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l
+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,
+    int type, struct archive_string_conv *);
 #define archive_entry_copy_gname_l	_archive_entry_copy_gname_l
 int _archive_entry_copy_gname_l(struct archive_entry *,
     const char *, size_t, struct archive_string_conv *);
diff --git a/libarchive/archive_entry_paths.3 b/libarchive/archive_entry_paths.3
index fd22cf7..f647212 100644
--- a/libarchive/archive_entry_paths.3
+++ b/libarchive/archive_entry_paths.3
@@ -31,25 +31,25 @@
 .Nm archive_entry_set_hardlink ,
 .Nm archive_entry_copy_hardlink ,
 .Nm archive_entry_copy_hardlink_w ,
-.Nm archve_entry_update_hardlink_utf8 ,
+.Nm archive_entry_update_hardlink_utf8 ,
 .Nm archive_entry_set_link ,
 .Nm archive_entry_copy_link ,
 .Nm archive_entry_copy_link_w ,
-.Nm archve_entry_update_link_utf8 ,
+.Nm archive_entry_update_link_utf8 ,
 .Nm archive_entry_pathname ,
 .Nm archive_entry_pathname_w ,
 .Nm archive_entry_set_pathname ,
 .Nm archive_entry_copy_pathname ,
 .Nm archive_entry_copy_pathname_w ,
-.Nm archve_entry_update_pathname_utf8 ,
+.Nm archive_entry_update_pathname_utf8 ,
 .Nm archive_entry_sourcepath ,
 .Nm archive_entry_copy_sourcepath ,
-.Nm archive_entry_symlink,
-.Nm archive_entry_symlink_w,
+.Nm archive_entry_symlink ,
+.Nm archive_entry_symlink_w ,
 .Nm archive_entry_set_symlink ,
 .Nm archive_entry_copy_symlink ,
 .Nm archive_entry_copy_symlink_w ,
-.Nm archve_entry_update_symlink_utf8
+.Nm archive_entry_update_symlink_utf8
 .Nd functions for manipulating path names in archive entry descriptions
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
diff --git a/libarchive/archive_entry_perms.3 b/libarchive/archive_entry_perms.3
index 340c5ea..aae3648 100644
--- a/libarchive/archive_entry_perms.3
+++ b/libarchive/archive_entry_perms.3
@@ -34,8 +34,8 @@
 .Nm archive_entry_perm ,
 .Nm archive_entry_set_perm ,
 .Nm archive_entry_strmode ,
-.Nm archive_entry_uname
-.Nm archive_entry_uname_w
+.Nm archive_entry_uname ,
+.Nm archive_entry_uname_w ,
 .Nm archive_entry_set_uname ,
 .Nm archive_entry_copy_uname ,
 .Nm archive_entry_copy_uname_w ,
diff --git a/libarchive/archive_entry_strmode.c b/libarchive/archive_entry_strmode.c
index 16cb3f7..af2517a 100644
--- a/libarchive/archive_entry_strmode.c
+++ b/libarchive/archive_entry_strmode.c
@@ -80,7 +80,7 @@
 		if (mode & 0001) bp[9] = 't';
 		else bp[9] = 'T';
 	}
-	if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
+	if (archive_entry_acl_types(entry) != 0)
 		bp[10] = '+';
 
 	return (bp);
diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c
index 1e0ae28..f299655 100644
--- a/libarchive/archive_hmac.c
+++ b/libarchive/archive_hmac.c
@@ -76,6 +76,10 @@
 
 #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
 
+#ifndef BCRYPT_HASH_REUSABLE_FLAG
+# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020
+#endif
+
 static int
 __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
 {
diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c
index 0719cbd..be72066 100644
--- a/libarchive/archive_match.c
+++ b/libarchive/archive_match.c
@@ -471,7 +471,7 @@
 }
 
 /*
- * Utilty functions to get statistic information for inclusion patterns.
+ * Utility functions to get statistic information for inclusion patterns.
  */
 int
 archive_match_path_unmatched_inclusions(struct archive *_a)
@@ -1270,7 +1270,7 @@
 #endif /* _WIN32 && !__CYGWIN__ */
 
 /*
- * Call back funtions for archive_rb.
+ * Call back functions for archive_rb.
  */
 static int
 cmp_node_mbs(const struct archive_rb_node *n1,
@@ -1405,7 +1405,7 @@
 			&(a->exclusion_tree), pathname);
 
 		/*
-		 * We always overwrite comparison condision.
+		 * We always overwrite comparison condition.
 		 * If you do not want to overwrite it, you should not
 		 * call archive_match_exclude_entry(). We cannot know
 		 * what behavior you really expect since overwriting
@@ -1481,7 +1481,7 @@
 			if (nsec == a->older_ctime_nsec &&
 			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
 			      == 0)
-				return (1); /* Eeual, skip it. */
+				return (1); /* Equal, skip it. */
 		}
 	}
 	if (a->newer_mtime_filter) {
@@ -1513,7 +1513,7 @@
 		}
 	}
 
-	/* If there is no excluson list, include the file. */
+	/* If there is no exclusion list, include the file. */
 	if (a->exclusion_entry_list.count == 0)
 		return (0);
 
@@ -1700,7 +1700,7 @@
 			break;
 	}
 
-	/* Add oowner id. */
+	/* Add owner id. */
 	if (i == ids->count)
 		ids->ids[ids->count++] = id;
 	else if (ids->ids[i] != id) {
diff --git a/libarchive/archive_platform.h b/libarchive/archive_platform.h
index e44c932..01d6a70 100644
--- a/libarchive/archive_platform.h
+++ b/libarchive/archive_platform.h
@@ -147,8 +147,33 @@
  * acl_set_file(), and ACL_USER, we assume it has the rest of the
  * POSIX.1e draft functions used in archive_read_extract.c.
  */
-#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER
+#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE
+#if HAVE_DECL_ACL_USER
 #define	HAVE_POSIX_ACL	1
+#elif HAVE_DECL_ACL_TYPE_EXTENDED && HAVE_MEMBERSHIP_H
+#define HAVE_DARWIN_ACL 1
+#endif
+#if HAVE_DECL_ACL_TYPE_NFS4
+#define	HAVE_FREEBSD_NFS4_ACL 1
+#endif
+#endif
+
+/*
+ * If this platform has <sys/acl.h>, acl(), facl() and ACLENT_T
+ * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions
+ */
+#if HAVE_SYS_ACL_H && HAVE_ACL && HAVE_FACL && HAVE_ACLENT_T && \
+    HAVE_DECL_GETACL && HAVE_DECL_GETACLCNT && HAVE_DECL_SETACL
+#define	HAVE_SUN_ACL	1
+#if HAVE_ACE_T && HAVE_DECL_ACE_GETACL && HAVE_DECL_ACE_GETACLCNT && \
+    HAVE_DECL_ACE_SETACL
+#define HAVE_SUN_NFS4_ACL	1
+#endif
+#endif
+
+/* Define if platform supports NFSv4 ACLs */
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_NFS4_ACL || HAVE_DARWIN_ACL
+#define HAVE_NFS4_ACL	1
 #endif
 
 /*
diff --git a/libarchive/archive_random.c b/libarchive/archive_random.c
index a20b9b1..357f973 100644
--- a/libarchive/archive_random.c
+++ b/libarchive/archive_random.c
@@ -80,7 +80,7 @@
 
 	success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
 	    CRYPT_VERIFYCONTEXT);
-	if (!success && GetLastError() == NTE_BAD_KEYSET) {
+	if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) {
 		success = CryptAcquireContext(&hProv, NULL, NULL,
 		    PROV_RSA_FULL, CRYPT_NEWKEYSET);
 	}
diff --git a/libarchive/archive_rb.c b/libarchive/archive_rb.c
index 5b5da20..cf58ac3 100644
--- a/libarchive/archive_rb.c
+++ b/libarchive/archive_rb.c
@@ -312,7 +312,7 @@
 		father = RB_FATHER(self);
 		if (RB_BLACK_P(father)) {
 			/*
-			 * If our greatgrandpa is black, we're done.
+			 * If our great-grandpa is black, we're done.
 			 */
 			return;
 		}
diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c
index d490d7b..d1feceb 100644
--- a/libarchive/archive_read.c
+++ b/libarchive/archive_read.c
@@ -764,7 +764,7 @@
  * we cannot say whether there are encrypted entries, then
  * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
  * In general, this function will return values below zero when the
- * reader is uncertain or totally uncapable of encryption support.
+ * reader is uncertain or totally incapable of encryption support.
  * When this function returns 0 you can be sure that the reader
  * supports encryption detection but no encrypted entries have
  * been found yet.
diff --git a/libarchive/archive_read_disk.3 b/libarchive/archive_read_disk.3
index 525dc59..55e4bbb 100644
--- a/libarchive/archive_read_disk.3
+++ b/libarchive/archive_read_disk.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd December 30, 2016
 .Dt ARCHIVE_READ_DISK 3
 .Os
 .Sh NAME
@@ -37,10 +37,7 @@
 .Nm archive_read_disk_uname ,
 .Nm archive_read_disk_set_uname_lookup ,
 .Nm archive_read_disk_set_gname_lookup ,
-.Nm archive_read_disk_set_standard_lookup ,
-.Nm archive_read_close ,
-.Nm archive_read_finish ,
-.Nm archive_read_free
+.Nm archive_read_disk_set_standard_lookup
 .Nd functions for reading objects from disk
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -54,9 +51,9 @@
 .Fn archive_read_disk_set_symlink_physical "struct archive *"
 .Ft int
 .Fn archive_read_disk_set_symlink_hybrid "struct archive *"
-.Ft int
+.Ft const char *
 .Fn archive_read_disk_gname "struct archive *" "gid_t"
-.Ft int
+.Ft const char *
 .Fn archive_read_disk_uname "struct archive *" "uid_t"
 .Ft int
 .Fo archive_read_disk_set_gname_lookup
@@ -81,12 +78,6 @@
 .Fa "int fd"
 .Fa "const struct stat *"
 .Fc
-.Ft int
-.Fn archive_read_close "struct archive *"
-.Ft int
-.Fn archive_read_finish "struct archive *"
-.Ft int
-.Fn archive_read_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide an API for reading information about
 objects on disk.
@@ -181,17 +172,6 @@
 This affects the file ownership fields and ACL values in the
 .Tn struct archive_entry
 object.
-.It Fn archive_read_close
-Does nothing for
-.Tn archive_read_disk
-handles.
-.It Fn archive_read_finish
-This is a deprecated synonym for
-.Fn archive_read_free .
-.It Fn archive_read_free
-Invokes
-.Fn archive_read_close
-if it was not invoked manually, then releases all resources.
 .El
 More information about the
 .Va struct archive
diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index f37bb16..9dec2e9 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +38,11 @@
 #ifdef HAVE_SYS_ACL_H
 #include <sys/acl.h>
 #endif
+#ifdef HAVE_DARWIN_ACL
+#include <membership.h>
+#include <grp.h>
+#include <pwd.h>
+#endif
 #ifdef HAVE_SYS_EXTATTR_H
 #include <sys/extattr.h>
 #endif
@@ -117,6 +123,13 @@
 #define	ACL_GET_PERM acl_get_perm_np
 #endif
 
+/* NFSv4 platform ACL type */
+#if HAVE_DARWIN_ACL
+#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_EXTENDED
+#elif HAVE_FREEBSD_NFS4_ACL
+#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_NFS4
+#endif
+
 static int setup_acls(struct archive_read_disk *,
     struct archive_entry *, int *fd);
 static int setup_mac_metadata(struct archive_read_disk *,
@@ -188,15 +201,17 @@
 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
 	/* On FreeBSD, we get flags for free with the stat. */
 	/* TODO: Does this belong in copy_stat()? */
-	if (st->st_flags != 0)
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
 		archive_entry_set_fflags(entry, st->st_flags, 0);
 #endif
 
-#if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+    (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
 	/* Linux requires an extra ioctl to pull the flags.  Although
 	 * this is an extra step, it has a nice side-effect: We get an
 	 * open file descriptor which we can use in the subsequent lookups. */
-	if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
+	    (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
 		if (fd < 0) {
 			if (a->tree != NULL)
 				fd = a->open_on_current_dir(a->tree, path,
@@ -208,7 +223,13 @@
 		}
 		if (fd >= 0) {
 			int stflags;
-			r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
+			r = ioctl(fd,
+#if defined(FS_IOC_GETFLAGS)
+			    FS_IOC_GETFLAGS,
+#else
+			    EXT2_IOC_GETFLAGS,
+#endif
+			    &stflags);
 			if (r == 0 && stflags != 0)
 				archive_entry_set_fflags(entry, stflags, 0);
 		}
@@ -254,13 +275,15 @@
 	}
 #endif /* HAVE_READLINK || HAVE_READLINKAT */
 
-	r = setup_acls(a, entry, &fd);
-	if (!a->suppress_xattr) {
+	r = 0;
+	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
+		r = setup_acls(a, entry, &fd);
+	if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
 		r1 = setup_xattrs(a, entry, &fd);
 		if (r1 < r)
 			r = r1;
 	}
-	if (a->enable_copyfile) {
+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
 		r1 = setup_mac_metadata(a, entry, &fd);
 		if (r1 < r)
 			r = r1;
@@ -306,20 +329,17 @@
 	name = archive_entry_sourcepath(entry);
 	if (name == NULL)
 		name = archive_entry_pathname(entry);
+	else if (a->tree != NULL && a->tree_enter_working_dir(a->tree) != 0) {
+		archive_set_error(&a->archive, errno,
+			    "Can't change dir to read extended attributes");
+			return (ARCHIVE_FAILED);
+	}
 	if (name == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Can't open file to read extended attributes: No name");
 		return (ARCHIVE_WARN);
 	}
 
-	if (a->tree != NULL) {
-		if (a->tree_enter_working_dir(a->tree) != 0) {
-			archive_set_error(&a->archive, errno,
-				    "Couldn't change dir");
-				return (ARCHIVE_FAILED);
-		}
-	}
-
 	/* Short-circuit if there's nothing to do. */
 	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
 	if (have_attrs == -1) {
@@ -404,90 +424,235 @@
 }
 #endif
 
+#if HAVE_DARWIN_ACL
+static int translate_guid(struct archive *, acl_entry_t,
+    int *, int *, const char **);
 
-#ifdef HAVE_POSIX_ACL
+static void add_trivial_nfs4_acl(struct archive_entry *);
+#endif
+
+#if HAVE_SUN_ACL
+static int
+sun_acl_is_trivial(void *, int, mode_t, int, int, int *);
+
+static void *
+sunacl_get(int cmd, int *aclcnt, int fd, const char *path)
+{
+	int cnt, cntcmd;
+	size_t size;
+	void *aclp;
+
+	if (cmd == GETACL) {
+		cntcmd = GETACLCNT;
+		size = sizeof(aclent_t);
+	}
+#if HAVE_SUN_NFS4_ACL
+	else if (cmd == ACE_GETACL) {
+		cntcmd = ACE_GETACLCNT;
+		size = sizeof(ace_t);
+	}
+#endif
+	else {
+		errno = EINVAL;
+		*aclcnt = -1;
+		return (NULL);
+	}
+
+	aclp = NULL;
+	cnt = -2;
+
+	while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) {
+		if (path != NULL)
+			cnt = acl(path, cntcmd, 0, NULL);
+		else
+			cnt = facl(fd, cntcmd, 0, NULL);
+
+		if (cnt > 0) {
+			if (aclp == NULL)
+				aclp = malloc(cnt * size);
+			else
+				aclp = realloc(NULL, cnt * size);
+			if (aclp != NULL) {
+				if (path != NULL)
+					cnt = acl(path, cmd, cnt, aclp);
+				else
+					cnt = facl(fd, cmd, cnt, aclp);
+			}
+		} else {
+			if (aclp != NULL) {
+				free(aclp);
+				aclp = NULL;
+			}
+			break;
+		}
+	}
+
+	*aclcnt = cnt;
+	return (aclp);
+}
+#endif	/* HAVE_SUN_ACL */
+
+#if HAVE_POSIX_ACL || HAVE_NFS4_ACL
 static int translate_acl(struct archive_read_disk *a,
-    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
+    struct archive_entry *entry,
+#if HAVE_SUN_ACL
+    void *aclp,
+    int aclcnt,
+#else
+    acl_t acl,
+#endif
+    int archive_entry_acl_type);
 
 static int
 setup_acls(struct archive_read_disk *a,
     struct archive_entry *entry, int *fd)
 {
 	const char	*accpath;
-	acl_t		 acl;
+#if HAVE_SUN_ACL
+	void		*aclp;
+	int		aclcnt;
+#else
+	acl_t		acl;
+#endif
 	int		r;
 
-	accpath = archive_entry_sourcepath(entry);
-	if (accpath == NULL)
-		accpath = archive_entry_pathname(entry);
+	accpath = NULL;
 
-	if (*fd < 0 && a->tree != NULL) {
-		if (a->follow_symlinks ||
-		    archive_entry_filetype(entry) != AE_IFLNK)
+#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_ACL_GET_FD_NP
+	if (*fd < 0)
+#else
+	/* For default ACLs on Linux we need reachable accpath */
+	if (*fd < 0 || S_ISDIR(archive_entry_mode(entry)))
+#endif
+	{
+		accpath = archive_entry_sourcepath(entry);
+		if (accpath == NULL || (a->tree != NULL &&
+		    a->tree_enter_working_dir(a->tree) != 0))
+			accpath = archive_entry_pathname(entry);
+		if (accpath == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Couldn't determine file path to read ACLs");
+			return (ARCHIVE_WARN);
+		}
+		if (a->tree != NULL &&
+#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_ACL_GET_FD_NP
+		    *fd < 0 &&
+#endif
+		    (a->follow_symlinks ||
+		    archive_entry_filetype(entry) != AE_IFLNK)) {
 			*fd = a->open_on_current_dir(a->tree,
 			    accpath, O_RDONLY | O_NONBLOCK);
-		if (*fd < 0) {
-			if (a->tree_enter_working_dir(a->tree) != 0) {
-				archive_set_error(&a->archive, errno,
-				    "Couldn't access %s", accpath);
-				return (ARCHIVE_FAILED);
-			}
 		}
 	}
 
 	archive_entry_acl_clear(entry);
 
+#if HAVE_SUN_ACL
+	aclp = NULL;
+#else
 	acl = NULL;
+#endif
 
-#ifdef ACL_TYPE_NFS4
-	/* Try NFS4 ACL first. */
+#if HAVE_NFS4_ACL
+	/* Try NFSv4 ACL first. */
 	if (*fd >= 0)
-#if HAVE_ACL_GET_FD_NP
-		acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
+#if HAVE_SUN_ACL
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL);
+#elif HAVE_ACL_GET_FD_NP
+		acl = acl_get_fd_np(*fd, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
 #else
 		acl = acl_get_fd(*fd);
 #endif
 #if HAVE_ACL_GET_LINK_NP
 	else if (!a->follow_symlinks)
-		acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
+		acl = acl_get_link_np(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
 #else
 	else if ((!a->follow_symlinks)
 	    && (archive_entry_filetype(entry) == AE_IFLNK))
 		/* We can't get the ACL of a symlink, so we assume it can't
 		   have one. */
+#if HAVE_SUN_ACL
+		aclp = NULL;
+#else
 		acl = NULL;
 #endif
+#endif /* !HAVE_ACL_GET_LINK_NP */
 	else
-		acl = acl_get_file(accpath, ACL_TYPE_NFS4);
+#if HAVE_SUN_ACL
+		/* Solaris reads both POSIX.1e and NFSv4 ACLs here */
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath);
+#else
+		acl = acl_get_file(accpath, ARCHIVE_PLATFORM_ACL_TYPE_NFS4);
+#endif
 
-#if HAVE_ACL_IS_TRIVIAL_NP
-	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) {
-		/* Ignore "trivial" ACLs that just mirror the file mode. */
-		if (r) {
-			acl_free(acl);
-			acl = NULL;
-			/*
-			 * Simultaneous NFSv4 and POSIX.1e ACLs for the same
-			 * entry are not allowed, so we should return here
-			 */
-			return (ARCHIVE_OK);
-		}
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+#if HAVE_SUN_ACL
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+		return (ARCHIVE_OK);
+	}
+#elif HAVE_ACL_IS_TRIVIAL_NP
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
+		return (ARCHIVE_OK);
 	}
 #endif
-	if (acl != NULL) {
-		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+
+#if HAVE_SUN_ACL
+	if (aclp != NULL)
+#else
+	if (acl != NULL)
+#endif
+	{
+		r = translate_acl(a, entry,
+#if HAVE_SUN_ACL
+		    aclp, aclcnt,
+#else
+		    acl,
+#endif
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+#if HAVE_SUN_ACL
+		free(aclp);
+		aclp = NULL;
+#else
 		acl_free(acl);
+		acl = NULL;
+#endif
+
 		if (r != ARCHIVE_OK) {
 			archive_set_error(&a->archive, errno,
-			    "Couldn't translate NFSv4 ACLs: %s", accpath);
+			    "Couldn't translate NFSv4 ACLs");
 		}
+#if HAVE_DARWIN_ACL
+		/*
+		 * Because Mac OS doesn't support owner@, group@ and everyone@
+		 * ACLs we need to add NFSv4 ACLs mirroring the file mode to
+		 * the archive entry. Otherwise extraction on non-Mac platforms
+		 * would lead to an invalid file mode.
+		 */
+		if ((archive_entry_acl_types(entry) &
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+			add_trivial_nfs4_acl(entry);
+#endif
 		return (r);
 	}
-#endif	/* ACL_TYPE_NFS4 */
+#endif	/* HAVE_NFS4_ACL */
+
+#if HAVE_POSIX_ACL || HAVE_SUN_ACL
+	/* This code path is skipped on MacOS */
 
 	/* Retrieve access ACL from file. */
 	if (*fd >= 0)
+#if HAVE_SUN_ACL
+		aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL);
+#else
 		acl = acl_get_fd(*fd);
+#endif
 #if HAVE_ACL_GET_LINK_NP
 	else if (!a->follow_symlinks)
 		acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
@@ -496,35 +661,71 @@
 	    && (archive_entry_filetype(entry) == AE_IFLNK))
 		/* We can't get the ACL of a symlink, so we assume it can't
 		   have one. */
+#if HAVE_SUN_ACL
+		aclp = NULL;
+#else
 		acl = NULL;
 #endif
+#endif /* !HAVE_ACL_GET_LINK_NP */
 	else
+#if HAVE_SUN_ACL
+		aclp = sunacl_get(GETACL, &aclcnt, 0, accpath);
+#else
 		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+#endif
 
-#if HAVE_ACL_IS_TRIVIAL_NP
+
 	/* Ignore "trivial" ACLs that just mirror the file mode. */
-	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) {
-		if (r) {
-			acl_free(acl);
-			acl = NULL;
-		}
+#if HAVE_SUN_ACL
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+	}
+#elif HAVE_ACL_IS_TRIVIAL_NP
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
 	}
 #endif
 
-	if (acl != NULL) {
-		r = translate_acl(a, entry, acl,
+#if HAVE_SUN_ACL
+	if (aclp != NULL)
+#else
+	if (acl != NULL)
+#endif
+	{
+		r = translate_acl(a, entry,
+#if HAVE_SUN_ACL
+		    aclp, aclcnt,
+#else
+		    acl,
+#endif
 		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+#if HAVE_SUN_ACL
+		free(aclp);
+		aclp = NULL;
+#else
 		acl_free(acl);
 		acl = NULL;
+#endif
+
 		if (r != ARCHIVE_OK) {
 			archive_set_error(&a->archive, errno,
-			    "Couldn't translate access ACLs: %s", accpath);
+			    "Couldn't translate access ACLs");
 			return (r);
 		}
 	}
 
+#if !HAVE_SUN_ACL
 	/* Only directories can have default ACLs. */
 	if (S_ISDIR(archive_entry_mode(entry))) {
+#if HAVE_ACL_GET_FD_NP
+		if (*fd >= 0)
+			acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
+		else
+#endif
 		acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
 		if (acl != NULL) {
 			r = translate_acl(a, entry, acl,
@@ -532,75 +733,574 @@
 			acl_free(acl);
 			if (r != ARCHIVE_OK) {
 				archive_set_error(&a->archive, errno,
-				    "Couldn't translate default ACLs: %s",
-				    accpath);
+				    "Couldn't translate default ACLs");
 				return (r);
 			}
 		}
 	}
+#endif	/* !HAVE_SUN_ACL */
+#endif	/* HAVE_POSIX_ACL || HAVE_SUN_ACL */
 	return (ARCHIVE_OK);
 }
 
 /*
- * Translate system ACL into libarchive internal structure.
+ * Translate system ACL permissions into libarchive internal structure
  */
-
-static struct {
-        int archive_perm;
-        int platform_perm;
+static const struct {
+	const int archive_perm;
+	const int platform_perm;
 } acl_perm_map[] = {
-        {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
-        {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
-        {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
-#ifdef ACL_TYPE_NFS4
-        {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
-        {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
-        {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
-        {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
-        {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
-        {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
-        {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
-        {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
-        {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
-        {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
-        {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
-        {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
-        {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
-        {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
-        {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
-        {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#if HAVE_SUN_ACL	/* Solaris NFSv4 ACL permissions */
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}
+#elif HAVE_DARWIN_ACL	/* MacOS ACL permissions */
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
 #endif
+#else	/* POSIX.1e ACL permissions */
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+#if HAVE_FREEBSD_NFS4_ACL	/* FreeBSD NFSv4 ACL permissions */
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#endif
+#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
 };
 
-#ifdef ACL_TYPE_NFS4
-static struct {
-        int archive_inherit;
-        int platform_inherit;
+#if HAVE_NFS4_ACL
+/*
+ * Translate system NFSv4 inheritance flags into libarchive internal structure
+ */
+static const struct {
+	const int archive_inherit;
+	const int platform_inherit;
 } acl_inherit_map[] = {
-        {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+#if HAVE_SUN_NFS4_ACL	/* Solaris ACL inheritance flags */
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},
+#ifdef ACE_INHERITED_ACE
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+#endif
+#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 inheritance flags */
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
+#else	/* FreeBSD NFSv4 ACL inheritance flags */
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
 	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
 	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
+#endif	/* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */
 };
+#endif	/* HAVE_NFS4_ACL */
+
+#if HAVE_DARWIN_ACL
+static int translate_guid(struct archive *a, acl_entry_t acl_entry,
+    int *ae_id, int *ae_tag, const char **ae_name)
+{
+	void *q;
+	uid_t ugid;
+	int r, idtype;
+	struct passwd *pwd;
+	struct group *grp;
+
+	q = acl_get_qualifier(acl_entry);
+	if (q == NULL)
+		return (1);
+	r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);
+	if (r != 0) {
+		acl_free(q);
+		return (1);
+	}
+	if (idtype == ID_TYPE_UID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_USER;
+		pwd = getpwuuid(q);
+		if (pwd == NULL) {
+			*ae_id = ugid;
+			*ae_name = NULL;
+		} else {
+			*ae_id = pwd->pw_uid;
+			*ae_name = archive_read_disk_uname(a, *ae_id);
+		}
+	} else if (idtype == ID_TYPE_GID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+		grp = getgruuid(q);
+		if (grp == NULL) {
+			*ae_id = ugid;
+			*ae_name = NULL;
+		} else {
+			*ae_id = grp->gr_gid;
+			*ae_name = archive_read_disk_gname(a, *ae_id);
+		}
+	} else
+		r = 1;
+
+	acl_free(q);
+	return (r);
+}
+
+/*
+ * Add trivial NFSv4 ACL entries from mode
+ */
+static void
+add_trivial_nfs4_acl(struct archive_entry *entry)
+{
+	mode_t mode;
+	int i;
+	const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;
+	const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA;
+	const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;
+	const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+	const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+
+	struct {
+	    const int type;
+	    const int tag;
+	    int permset;
+	} tacl_entry[] = {
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}
+	};
+
+	mode = archive_entry_mode(entry);
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tacl_entry[5].permset |= rperm;
+	if (mode & 0002)
+		tacl_entry[5].permset |= wperm;
+	if (mode & 0001)
+		tacl_entry[5].permset |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tacl_entry[4].permset |= rperm;
+	else if (mode & 0004)
+		tacl_entry[2].permset |= rperm;
+	if (mode & 0020)
+		tacl_entry[4].permset |= wperm;
+	else if (mode & 0002)
+		tacl_entry[2].permset |= wperm;
+	if (mode & 0010)
+		tacl_entry[4].permset |= eperm;
+	else if (mode & 0001)
+		tacl_entry[2].permset |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tacl_entry[3].permset |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tacl_entry[0].permset |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tacl_entry[1].permset |= rperm;
+	if (mode & 0200) {
+		tacl_entry[3].permset |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tacl_entry[0].permset |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tacl_entry[1].permset |= wperm;
+	if (mode & 0100) {
+		tacl_entry[3].permset |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tacl_entry[0].permset |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tacl_entry[1].permset |= eperm;
+
+	for (i = 0; i < 6; i++) {
+		if (tacl_entry[i].permset != 0) {
+			archive_entry_acl_add_entry(entry,
+			    tacl_entry[i].type, tacl_entry[i].permset,
+			    tacl_entry[i].tag, -1, NULL);
+		}
+	}
+
+	return;
+}
+#elif HAVE_SUN_ACL
+/*
+ * Check if acl is trivial
+ * This is a FreeBSD acl_is_trivial_np() implementation for Solaris
+ */
+static int
+sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4,
+    int is_dir, int *trivialp)
+{
+	int i, p;
+#if HAVE_SUN_NFS4_ACL
+	const uint32_t rperm = ACE_READ_DATA;
+	const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA;
+	const uint32_t eperm = ACE_EXECUTE;
+	const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+	    ACE_READ_ACL | ACE_SYNCHRONIZE;
+	const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES |
+	    ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER;
+
+	ace_t *ace;
+	ace_t tace[6];
 #endif
+
+	if (aclp == NULL || trivialp == NULL)
+		return (-1);
+
+	*trivialp = 0;
+
+	/*
+	 * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with
+	 * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries,
+	 * including mask.
+	 */
+	if (!is_nfs4) {
+		if (aclcnt == 4)
+			*trivialp = 1;
+		return (0);
+	}
+
+#if HAVE_SUN_NFS4_ACL
+	/*
+	 * Continue with checking NFSv4 ACLs
+	 *
+	 * Create list of trivial ace's to be compared
+	 */
+
+	/* owner@ allow pre */
+	tace[0].a_flags = ACE_OWNER;
+	tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[0].a_access_mask = 0;
+
+	/* owner@ deny */
+	tace[1].a_flags = ACE_OWNER;
+	tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[1].a_access_mask = 0;
+
+	/* group@ deny */
+	tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[2].a_access_mask = 0;
+
+	/* owner@ allow */
+	tace[3].a_flags = ACE_OWNER;
+	tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[3].a_access_mask = ownset;
+
+	/* group@ allow */
+	tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[4].a_access_mask = pubset;
+
+	/* everyone@ allow */
+	tace[5].a_flags = ACE_EVERYONE;
+	tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[5].a_access_mask = pubset;
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tace[5].a_access_mask |= rperm;
+	if (mode & 0002)
+		tace[5].a_access_mask |= wperm;
+	if (mode & 0001)
+		tace[5].a_access_mask |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tace[4].a_access_mask |= rperm;
+	else if (mode & 0004)
+		tace[2].a_access_mask |= rperm;
+	if (mode & 0020)
+		tace[4].a_access_mask |= wperm;
+	else if (mode & 0002)
+		tace[2].a_access_mask |= wperm;
+	if (mode & 0010)
+		tace[4].a_access_mask |= eperm;
+	else if (mode & 0001)
+		tace[2].a_access_mask |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tace[3].a_access_mask |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tace[0].a_access_mask |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tace[1].a_access_mask |= rperm;
+	if (mode & 0200) {
+		tace[3].a_access_mask |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tace[0].a_access_mask |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tace[1].a_access_mask |= wperm;
+	if (mode & 0100) {
+		tace[3].a_access_mask |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tace[0].a_access_mask |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tace[1].a_access_mask |= eperm;
+
+	/* Check if the acl count matches */
+	p = 3;
+	for (i = 0; i < 3; i++) {
+		if (tace[i].a_access_mask != 0)
+			p++;
+	}
+	if (aclcnt != p)
+		return (0);
+
+	p = 0;
+	for (i = 0; i < 6; i++) {
+		if (tace[i].a_access_mask != 0) {
+			ace = &((ace_t *)aclp)[p];
+			/*
+			 * Illumos added ACE_DELETE_CHILD to write perms for
+			 * directories. We have to check against that, too.
+			 */
+			if (ace->a_flags != tace[i].a_flags ||
+			    ace->a_type != tace[i].a_type ||
+			    (ace->a_access_mask != tace[i].a_access_mask &&
+			    (!is_dir || (tace[i].a_access_mask & wperm) == 0 ||
+			    ace->a_access_mask !=
+			    (tace[i].a_access_mask | ACE_DELETE_CHILD))))
+				return (0);
+			p++;
+		}
+	}
+
+	*trivialp = 1;
+#else	/* !HAVE_SUN_NFS4_ACL */
+	(void)aclp;	/* UNUSED */
+#endif	/* !HAVE_SUN_NFS4_ACL */
+	return (0);
+}
+#endif	/* HAVE_SUN_ACL */
+
+#if HAVE_SUN_ACL
+/*
+ * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, void *aclp, int aclcnt,
+    int default_entry_acl_type)
+{
+	int e, i;
+	int ae_id, ae_tag, ae_perm;
+	int entry_acl_type;
+	const char *ae_name;
+	aclent_t *aclent;
+#if HAVE_SUN_NFS4_ACL
+	ace_t *ace;
+#endif
+
+	if (aclcnt <= 0)
+		return (ARCHIVE_OK);
+
+	for (e = 0; e < aclcnt; e++) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+
+#if HAVE_SUN_NFS4_ACL
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			ace = &((ace_t *)aclp)[e];
+			ae_id = ace->a_who;
+
+			switch(ace->a_type) {
+			case ACE_ACCESS_ALLOWED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				break;
+			case ACE_ACCESS_DENIED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+				break;
+			case ACE_SYSTEM_AUDIT_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+				break;
+			case ACE_SYSTEM_ALARM_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+				break;
+			default:
+				/* Unknown entry type, skip */
+				continue;
+			}
+
+			if ((ace->a_flags & ACE_OWNER) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			else if ((ace->a_flags & ACE_GROUP) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			else if ((ace->a_flags & ACE_EVERYONE) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) {
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			} else {
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+
+			for (i = 0; i < (int)(sizeof(acl_inherit_map) /
+			    sizeof(acl_inherit_map[0])); ++i) {
+				if ((ace->a_flags &
+				    acl_inherit_map[i].platform_inherit) != 0)
+					ae_perm |=
+					    acl_inherit_map[i].archive_inherit;
+			}
+
+			for (i = 0; i < (int)(sizeof(acl_perm_map) /
+			    sizeof(acl_perm_map[0])); ++i) {
+				if ((ace->a_access_mask &
+				    acl_perm_map[i].platform_perm) != 0)
+					ae_perm |=
+					    acl_perm_map[i].archive_perm;
+			}
+		} else
+#endif	/* HAVE_SUN_NFS4_ACL */
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+			aclent = &((aclent_t *)aclp)[e];
+			if ((aclent->a_type & ACL_DEFAULT) != 0)
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+			else
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			ae_id = aclent->a_id;
+
+			switch(aclent->a_type) {
+			case DEF_USER:
+			case USER:
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case DEF_GROUP:
+			case GROUP:
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case DEF_CLASS_OBJ:
+			case CLASS_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			case DEF_USER_OBJ:
+			case USER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case DEF_GROUP_OBJ:
+			case GROUP_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case DEF_OTHER_OBJ:
+			case OTHER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			default:
+				/* Unknown tag type, skip */
+				continue;
+			}
+
+			if ((aclent->a_perm & 1) != 0)
+				ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			if ((aclent->a_perm & 2) != 0)
+				ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
+			if ((aclent->a_perm & 4) != 0)
+				ae_perm |= ARCHIVE_ENTRY_ACL_READ;
+		} else
+			return (ARCHIVE_WARN);
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+#else	/* !HAVE_SUN_ACL */
+/*
+ * Translate POSIX.1e (Linux), FreeBSD (both POSIX.1e and NFSv4) and
+ * MacOS (NFSv4 only) ACLs into libarchive internal structure
+ */
 static int
 translate_acl(struct archive_read_disk *a,
     struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
 {
 	acl_tag_t	 acl_tag;
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL
 	acl_entry_type_t acl_type;
-	acl_flagset_t	 acl_flagset;
 	int brand;
 #endif
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL
+	acl_flagset_t	 acl_flagset;
+#endif
 	acl_entry_t	 acl_entry;
 	acl_permset_t	 acl_permset;
 	int		 i, entry_acl_type;
 	int		 r, s, ae_id, ae_tag, ae_perm;
+#if !HAVE_DARWIN_ACL
+	void		*q;
+#endif
 	const char	*ae_name;
 
-
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL
 	// FreeBSD "brands" ACLs as POSIX.1e or NFSv4
 	// Make sure the "brand" on this ACL is consistent
 	// with the default_entry_acl_type bits provided.
@@ -635,14 +1335,19 @@
 	}
 #endif
 
-
 	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
 	if (s == -1) {
 		archive_set_error(&a->archive, errno,
 		    "Failed to get first ACL entry");
 		return (ARCHIVE_WARN);
 	}
-	while (s == 1) {
+
+#if HAVE_DARWIN_ACL
+	while (s == 0)
+#else	/* FreeBSD, Linux */
+	while (s == 1)
+#endif
+	{
 		ae_id = -1;
 		ae_name = NULL;
 		ae_perm = 0;
@@ -653,14 +1358,25 @@
 			return (ARCHIVE_WARN);
 		}
 		switch (acl_tag) {
+#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
 		case ACL_USER:
-			ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
-			ae_name = archive_read_disk_uname(&a->archive, ae_id);
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(uid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
 			ae_tag = ARCHIVE_ENTRY_ACL_USER;
 			break;
 		case ACL_GROUP:
-			ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
-			ae_name = archive_read_disk_gname(&a->archive, ae_id);
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(gid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			}
 			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
 			break;
 		case ACL_MASK:
@@ -675,21 +1391,44 @@
 		case ACL_OTHER:
 			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
 			break;
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL
 		case ACL_EVERYONE:
 			ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
 			break;
 #endif
+#else	/* HAVE_DARWIN_ACL */
+		case ACL_EXTENDED_ALLOW:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+			r = translate_guid(&a->archive, acl_entry, &ae_id,
+			    &ae_tag, &ae_name);
+			break;
+		case ACL_EXTENDED_DENY:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			r = translate_guid(&a->archive, acl_entry, &ae_id,
+			    &ae_tag, &ae_name);
+			break;
+#endif	/* HAVE_DARWIN_ACL */
 		default:
 			/* Skip types that libarchive can't support. */
 			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
 			continue;
 		}
 
+#if HAVE_DARWIN_ACL
+		/* Skip if translate_guid() above failed */
+		if (r != 0) {
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+#endif
+
+#if !HAVE_DARWIN_ACL
 		// XXX acl_type maps to allow/deny/audit/YYYY bits
 		entry_acl_type = default_entry_acl_type;
-#ifdef ACL_TYPE_NFS4
+#endif
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL
 		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+#if HAVE_FREEBSD_NFS4_ACL
 			/*
 			 * acl_get_entry_type_np() fails with non-NFSv4 ACLs
 			 */
@@ -716,6 +1455,7 @@
 				    "Invalid NFSv4 ACL entry type");
 				return (ARCHIVE_WARN);
 			}
+#endif	/* HAVE_FREEBSD_NFS4_ACL */
 
 			/*
 			 * Libarchive stores "flag" (NFSv4 inheritance bits)
@@ -728,7 +1468,7 @@
 				    "Failed to get flagset from a NFSv4 ACL entry");
 				return (ARCHIVE_WARN);
 			}
-	                for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+			for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
 				r = acl_get_flag_np(acl_flagset,
 				    acl_inherit_map[i].platform_inherit);
 				if (r == -1) {
@@ -738,9 +1478,9 @@
 					return (ARCHIVE_WARN);
 				} else if (r)
 					ae_perm |= acl_inherit_map[i].archive_inherit;
-                	}
+			}
 		}
-#endif
+#endif	/* HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL */
 
 		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
 			archive_set_error(&a->archive, errno,
@@ -761,20 +1501,28 @@
 				ae_perm |= acl_perm_map[i].archive_perm;
 		}
 
+#if HAVE_DARWIN_ACL && !HAVE_DECL_ACL_SYNCHRONIZE
+		/* On Mac OS X without ACL_SYNCHRONIZE assume it is set */
+		ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+#endif
+
 		archive_entry_acl_add_entry(entry, entry_acl_type,
 					    ae_perm, ae_tag,
 					    ae_id, ae_name);
 
 		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+#if !HAVE_DARWIN_ACL
 		if (s == -1) {
 			archive_set_error(&a->archive, errno,
 			    "Failed to get next ACL entry");
 			return (ARCHIVE_WARN);
 		}
+#endif
 	}
 	return (ARCHIVE_OK);
 }
-#else
+#endif	/* !HAVE_SUN_ACL */
+#else	/* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */
 static int
 setup_acls(struct archive_read_disk *a,
     struct archive_entry *entry, int *fd)
@@ -784,7 +1532,7 @@
 	(void)fd;     /* UNUSED */
 	return (ARCHIVE_OK);
 }
-#endif
+#endif	/* !HAVE_POSIX_ACL && !HAVE_NFS4_ACL */
 
 #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \
     HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \
@@ -804,15 +1552,10 @@
 
 static int
 setup_xattr(struct archive_read_disk *a,
-    struct archive_entry *entry, const char *name, int fd)
+    struct archive_entry *entry, const char *name, int fd, const char *accpath)
 {
 	ssize_t size;
 	void *value = NULL;
-	const char *accpath;
-
-	accpath = archive_entry_sourcepath(entry);
-	if (accpath == NULL)
-		accpath = archive_entry_pathname(entry);
 
 #if HAVE_FGETXATTR
 	if (fd >= 0)
@@ -877,21 +1620,23 @@
 	const char *path;
 	ssize_t list_size;
 
-	path = archive_entry_sourcepath(entry);
-	if (path == NULL)
-		path = archive_entry_pathname(entry);
+	path = NULL;
 
-	if (*fd < 0 && a->tree != NULL) {
-		if (a->follow_symlinks ||
-		    archive_entry_filetype(entry) != AE_IFLNK)
-			*fd = a->open_on_current_dir(a->tree, path,
-				O_RDONLY | O_NONBLOCK);
-		if (*fd < 0) {
-			if (a->tree_enter_working_dir(a->tree) != 0) {
-				archive_set_error(&a->archive, errno,
-				    "Couldn't access %s", path);
-				return (ARCHIVE_FAILED);
-			}
+	if (*fd < 0) {
+		path = archive_entry_sourcepath(entry);
+		if (path == NULL || (a->tree != NULL &&
+		    a->tree_enter_working_dir(a->tree) != 0))
+			path = archive_entry_pathname(entry);
+		if (path == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Couldn't determine file path to read "
+			    "extended attributes");
+			return (ARCHIVE_WARN);
+		}
+		if (a->tree != NULL && (a->follow_symlinks ||
+		    archive_entry_filetype(entry) != AE_IFLNK)) {
+			*fd = a->open_on_current_dir(a->tree,
+			    path, O_RDONLY | O_NONBLOCK);
 		}
 	}
 
@@ -954,7 +1699,7 @@
 		if (strncmp(p, "system.", 7) == 0 ||
 				strncmp(p, "xfsroot.", 8) == 0)
 			continue;
-		setup_xattr(a, entry, p, *fd);
+		setup_xattr(a, entry, p, *fd, path);
 	}
 
 	free(list);
@@ -975,19 +1720,16 @@
  */
 static int
 setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
-    int namespace, const char *name, const char *fullname, int fd);
+    int namespace, const char *name, const char *fullname, int fd,
+    const char *path);
 
 static int
 setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
-    int namespace, const char *name, const char *fullname, int fd)
+    int namespace, const char *name, const char *fullname, int fd,
+    const char *accpath)
 {
 	ssize_t size;
 	void *value = NULL;
-	const char *accpath;
-
-	accpath = archive_entry_sourcepath(entry);
-	if (accpath == NULL)
-		accpath = archive_entry_pathname(entry);
 
 	if (fd >= 0)
 		size = extattr_get_fd(fd, namespace, name, NULL, 0);
@@ -1037,21 +1779,23 @@
 	const char *path;
 	int namespace = EXTATTR_NAMESPACE_USER;
 
-	path = archive_entry_sourcepath(entry);
-	if (path == NULL)
-		path = archive_entry_pathname(entry);
+	path = NULL;
 
-	if (*fd < 0 && a->tree != NULL) {
-		if (a->follow_symlinks ||
-		    archive_entry_filetype(entry) != AE_IFLNK)
-			*fd = a->open_on_current_dir(a->tree, path,
-				O_RDONLY | O_NONBLOCK);
-		if (*fd < 0) {
-			if (a->tree_enter_working_dir(a->tree) != 0) {
-				archive_set_error(&a->archive, errno,
-				    "Couldn't access %s", path);
-				return (ARCHIVE_FAILED);
-			}
+	if (*fd < 0) {
+		path = archive_entry_sourcepath(entry);
+		if (path == NULL || (a->tree != NULL &&
+		    a->tree_enter_working_dir(a->tree) != 0))
+			path = archive_entry_pathname(entry);
+		if (path == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Couldn't determine file path to read "
+			    "extended attributes");
+			return (ARCHIVE_WARN);
+		}
+		if (a->tree != NULL && (a->follow_symlinks ||
+		    archive_entry_filetype(entry) != AE_IFLNK)) {
+			*fd = a->open_on_current_dir(a->tree,
+			    path, O_RDONLY | O_NONBLOCK);
 		}
 	}
 
@@ -1101,7 +1845,7 @@
 		name = buff + strlen(buff);
 		memcpy(name, p + 1, len);
 		name[len] = '\0';
-		setup_xattr(a, entry, namespace, name, buff, *fd);
+		setup_xattr(a, entry, namespace, name, buff, *fd, path);
 		p += 1 + len;
 	}
 
diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c
index 3cc5168..6961ae6 100644
--- a/libarchive/archive_read_disk_posix.c
+++ b/libarchive/archive_read_disk_posix.c
@@ -465,8 +465,7 @@
 	a->entry = archive_entry_new2(&a->archive);
 	a->lookup_uname = trivial_lookup_uname;
 	a->lookup_gname = trivial_lookup_gname;
-	a->enable_copyfile = 1;
-	a->traverse_mount_points = 1;
+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
 	a->open_on_current_dir = open_on_current_dir;
 	a->tree_current_dir_fd = tree_current_dir_fd;
 	a->tree_enter_working_dir = tree_enter_working_dir;
@@ -563,25 +562,19 @@
 int
 archive_read_disk_set_atime_restored(struct archive *_a)
 {
-#ifndef HAVE_UTIMES
-	static int warning_done = 0;
-#endif
 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
 	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
 #ifdef HAVE_UTIMES
-	a->restore_time = 1;
+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
 	if (a->tree != NULL)
 		a->tree->flags |= needsRestoreTimes;
 	return (ARCHIVE_OK);
 #else
-	if (warning_done)
-		/* Warning was already emitted; suppress further warnings. */
-		return (ARCHIVE_OK);
-
+	/* Display warning and unset flag */
 	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 	    "Cannot restore access time on this system");
-	warning_done = 1;
+	a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME;
 	return (ARCHIVE_WARN);
 #endif
 }
@@ -595,29 +588,14 @@
 	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
 
+	a->flags = flags;
+
 	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
 		r = archive_read_disk_set_atime_restored(_a);
 	else {
-		a->restore_time = 0;
 		if (a->tree != NULL)
 			a->tree->flags &= ~needsRestoreTimes;
 	}
-	if (flags & ARCHIVE_READDISK_HONOR_NODUMP)
-		a->honor_nodump = 1;
-	else
-		a->honor_nodump = 0;
-	if (flags & ARCHIVE_READDISK_MAC_COPYFILE)
-		a->enable_copyfile = 1;
-	else
-		a->enable_copyfile = 0;
-	if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
-		a->traverse_mount_points = 0;
-	else
-		a->traverse_mount_points = 1;
-	if (flags & ARCHIVE_READDISK_NO_XATTR)
-		a->suppress_xattr = 1;
-	else
-		a->suppress_xattr = 0;
 	return (r);
 }
 
@@ -675,7 +653,7 @@
 				asize = cf->min_xfer_size;
 
 			/* Increase a buffer size up to 64K bytes in
-			 * a proper incremant size. */
+			 * a proper increment size. */
 			while (asize < 1024*64)
 				asize += incr;
 			/* Take a margin to adjust to the filesystem
@@ -918,7 +896,7 @@
 	} while (lst == NULL);
 
 #ifdef __APPLE__
-	if (a->enable_copyfile) {
+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
 		/* If we're using copyfile(), ignore "._XXX" files. */
 		const char *bname = strrchr(tree_current_path(t), '/');
 		if (bname == NULL)
@@ -989,7 +967,7 @@
 	}
 	if (t->initial_filesystem_id == -1)
 		t->initial_filesystem_id = t->current_filesystem_id;
-	if (!a->traverse_mount_points) {
+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
 		if (t->initial_filesystem_id != t->current_filesystem_id)
 			descend = 0;
 	}
@@ -999,12 +977,14 @@
 	 * Honor nodump flag.
 	 * If the file is marked with nodump flag, do not return this entry.
 	 */
-	if (a->honor_nodump) {
+	if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) {
 #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
 		if (st->st_flags & UF_NODUMP)
 			return (ARCHIVE_RETRY);
-#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\
-      defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \
+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \
+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
 		if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
 			int stflags;
 
@@ -1013,9 +993,18 @@
 			    O_RDONLY | O_NONBLOCK | O_CLOEXEC);
 			__archive_ensure_cloexec_flag(t->entry_fd);
 			if (t->entry_fd >= 0) {
-				r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS,
+				r = ioctl(t->entry_fd,
+#ifdef FS_IOC_GETFLAGS
+				FS_IOC_GETFLAGS,
+#else
+				EXT2_IOC_GETFLAGS,
+#endif
 					&stflags);
+#ifdef FS_NODUMP_FL
+				if (r == 0 && (stflags & FS_NODUMP_FL) != 0)
+#else
 				if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)
+#endif
 					return (ARCHIVE_RETRY);
 			}
 		}
@@ -1026,7 +1015,7 @@
 
 	/* Save the times to be restored. This must be in before
 	 * calling archive_read_disk_descend() or any chance of it,
-	 * especially, invokng a callback. */
+	 * especially, invoking a callback. */
 	t->restore_time.mtime = archive_entry_mtime(entry);
 	t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);
 	t->restore_time.atime = archive_entry_atime(entry);
@@ -1340,10 +1329,11 @@
 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
 
 	if (a->tree != NULL)
-		a->tree = tree_reopen(a->tree, pathname, a->restore_time);
+		a->tree = tree_reopen(a->tree, pathname,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
 	else
 		a->tree = tree_open(pathname, a->symlink_mode,
-		    a->restore_time);
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
 	if (a->tree == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate tar data");
@@ -1656,7 +1646,7 @@
 		archive_set_error(&a->archive, errno, "statvfs failed");
 		return (ARCHIVE_FAILED);
 	} else if (xr == 1) {
-		/* Usuall come here unless NetBSD supports _PC_REC_XFER_ALIGN
+		/* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN
 		 * for pathconf() function. */
 		t->current_filesystem->xfer_align = sfs.f_frsize;
 		t->current_filesystem->max_xfer_size = -1;
@@ -1944,7 +1934,7 @@
 	if (nm == -1)
 #  endif /* _PC_NAME_MAX */
 		/*
-		 * Some sysmtes (HP-UX or others?) incorrectly defined
+		 * Some systems (HP-UX or others?) incorrectly defined
 		 * NAME_MAX macro to be a smaller value.
 		 */
 #  if defined(NAME_MAX) && NAME_MAX >= 255
@@ -2132,7 +2122,7 @@
 static struct tree *
 tree_reopen(struct tree *t, const char *path, int restore_time)
 {
-	t->flags = (restore_time)?needsRestoreTimes:0;
+	t->flags = (restore_time != 0)?needsRestoreTimes:0;
 	t->flags |= onInitialDir;
 	t->visit_type = 0;
 	t->tree_errno = 0;
diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h
index 2569321..b5a8328 100644
--- a/libarchive/archive_read_disk_private.h
+++ b/libarchive/archive_read_disk_private.h
@@ -63,16 +63,8 @@
 	int	(*tree_current_dir_fd)(struct tree*);
 	int	(*tree_enter_working_dir)(struct tree*);
 
-	/* Set 1 if users request to restore atime . */
-	int		 restore_time;
-	/* Set 1 if users request to honor nodump flag . */
-	int		 honor_nodump;
-	/* Set 1 if users request to enable mac copyfile. */
-	int		 enable_copyfile;
-	/* Set 1 if users request to traverse mount points. */
-	int		 traverse_mount_points;
-	/* Set 1 if users want to suppress xattr information. */
-	int		 suppress_xattr;
+	/* Bitfield with ARCHIVE_READDISK_* tunables */
+	int	flags;
 
 	const char * (*lookup_gname)(void *private, int64_t gid);
 	void	(*cleanup_gname)(void *private);
diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c
index 993a1dc..3b90330 100644
--- a/libarchive/archive_read_disk_windows.c
+++ b/libarchive/archive_read_disk_windows.c
@@ -398,8 +398,7 @@
 	a->entry = archive_entry_new2(&a->archive);
 	a->lookup_uname = trivial_lookup_uname;
 	a->lookup_gname = trivial_lookup_gname;
-	a->enable_copyfile = 1;
-	a->traverse_mount_points = 1;
+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
 	return (&a->archive);
 }
 
@@ -495,7 +494,7 @@
 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
 	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
-	a->restore_time = 1;
+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
 	if (a->tree != NULL)
 		a->tree->flags |= needsRestoreTimes;
 	return (ARCHIVE_OK);
@@ -510,25 +509,14 @@
 	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
 
+	a->flags = flags;
+
 	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
 		r = archive_read_disk_set_atime_restored(_a);
 	else {
-		a->restore_time = 0;
 		if (a->tree != NULL)
 			a->tree->flags &= ~needsRestoreTimes;
 	}
-	if (flags & ARCHIVE_READDISK_HONOR_NODUMP)
-		a->honor_nodump = 1;
-	else
-		a->honor_nodump = 0;
-	if (flags & ARCHIVE_READDISK_MAC_COPYFILE)
-		a->enable_copyfile = 1;
-	else
-		a->enable_copyfile = 0;
-	if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
-		a->traverse_mount_points = 0;
-	else
-		a->traverse_mount_points = 1;
 	return (r);
 }
 
@@ -852,7 +840,7 @@
 	}
 	if (t->initial_filesystem_id == -1)
 		t->initial_filesystem_id = t->current_filesystem_id;
-	if (!a->traverse_mount_points) {
+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
 		if (t->initial_filesystem_id != t->current_filesystem_id)
 			return (ARCHIVE_RETRY);
 	}
@@ -862,7 +850,7 @@
 
 	/* Save the times to be restored. This must be in before
 	 * calling archive_read_disk_descend() or any chance of it,
-	 * especially, invokng a callback. */
+	 * especially, invoking a callback. */
 	t->restore_time.lastWriteTime = st->ftLastWriteTime;
 	t->restore_time.lastAccessTime = st->ftLastAccessTime;
 	t->restore_time.filetype = archive_entry_filetype(entry);
@@ -1219,9 +1207,11 @@
 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
 
 	if (a->tree != NULL)
-		a->tree = tree_reopen(a->tree, pathname, a->restore_time);
+		a->tree = tree_reopen(a->tree, pathname,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
 	else
-		a->tree = tree_open(pathname, a->symlink_mode, a->restore_time);
+		a->tree = tree_open(pathname, a->symlink_mode,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
 	if (a->tree == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate directory traversal data");
@@ -1401,7 +1391,7 @@
 	if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
 		return (0);
 
-	/* Close a file descritor.
+	/* Close a file descriptor.
 	 * It will not be used for SetFileTime() because it has been opened
 	 * by a read only mode.
 	 */
@@ -1519,7 +1509,7 @@
 	struct archive_wstring ws;
 	wchar_t *pathname, *p, *base;
 
-	t->flags = (restore_time)?needsRestoreTimes:0;
+	t->flags = (restore_time != 0)?needsRestoreTimes:0;
 	t->visit_type = 0;
 	t->tree_errno = 0;
 	t->full_path_dir_length = 0;
diff --git a/libarchive/archive_read_format.3 b/libarchive/archive_read_format.3
index 53b9a7e..91c5d2c 100644
--- a/libarchive/archive_read_format.3
+++ b/libarchive/archive_read_format.3
@@ -37,9 +37,9 @@
 .Nm archive_read_support_format_empty ,
 .Nm archive_read_support_format_iso9660 ,
 .Nm archive_read_support_format_lha ,
-.Nm archive_read_support_format_mtree,
-.Nm archive_read_support_format_rar,
-.Nm archive_read_support_format_raw,
+.Nm archive_read_support_format_mtree ,
+.Nm archive_read_support_format_rar ,
+.Nm archive_read_support_format_raw ,
 .Nm archive_read_support_format_tar ,
 .Nm archive_read_support_format_xar ,
 .Nm archive_read_support_format_zip
diff --git a/libarchive/archive_read_open.3 b/libarchive/archive_read_open.3
index 4d8272c..0249456 100644
--- a/libarchive/archive_read_open.3
+++ b/libarchive/archive_read_open.3
@@ -33,7 +33,7 @@
 .Nm archive_read_open_fd ,
 .Nm archive_read_open_FILE ,
 .Nm archive_read_open_filename ,
-.Nm archive_read_open_memory ,
+.Nm archive_read_open_memory
 .Nd functions for reading streaming archives
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c
index 5611aa8..86635e2 100644
--- a/libarchive/archive_read_open_filename.c
+++ b/libarchive/archive_read_open_filename.c
@@ -222,7 +222,7 @@
 	void *buffer;
 	const char *filename = NULL;
 	const wchar_t *wfilename = NULL;
-	int fd;
+	int fd = -1;
 	int is_disk_like = 0;
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 	off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
@@ -277,7 +277,7 @@
 #else
 		archive_set_error(a, ARCHIVE_ERRNO_MISC,
 		    "Unexpedted operation in archive_read_open_filename");
-		return (ARCHIVE_FATAL);
+		goto fail;
 #endif
 	}
 	if (fstat(fd, &st) != 0) {
@@ -287,7 +287,7 @@
 		else
 			archive_set_error(a, errno, "Can't stat '%s'",
 			    filename);
-		return (ARCHIVE_FATAL);
+		goto fail;
 	}
 
 	/*
@@ -356,11 +356,9 @@
 		mine->block_size = new_block_size;
 	}
 	buffer = malloc(mine->block_size);
-	if (mine == NULL || buffer == NULL) {
+	if (buffer == NULL) {
 		archive_set_error(a, ENOMEM, "No memory");
-		free(mine);
-		free(buffer);
-		return (ARCHIVE_FATAL);
+		goto fail;
 	}
 	mine->buffer = buffer;
 	mine->fd = fd;
@@ -372,6 +370,14 @@
 		mine->use_lseek = 1;
 
 	return (ARCHIVE_OK);
+fail:
+	/*
+	 * Don't close file descriptors not opened or ones pointing referring
+	 * to `FNT_STDIN`.
+	 */
+	if (fd != -1 && fd != 0)
+		close(fd);
+	return (ARCHIVE_FATAL);
 }
 
 static ssize_t
diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c
index 37b2f59..663e2d3 100644
--- a/libarchive/archive_read_support_filter_lz4.c
+++ b/libarchive/archive_read_support_filter_lz4.c
@@ -180,7 +180,7 @@
 			return (0);
 		bits_checked += 8;
 		BD = buffer[5];
-		/* A block maximum size shuld be more than 3. */
+		/* A block maximum size should be more than 3. */
 		if (((BD & 0x70) >> 4) < 4)
 			return (0);
 		/* Reserved bits must be "0". */
@@ -417,7 +417,7 @@
 	/* Reserved bits must be zero. */
 	if (bd & 0x8f)
 		goto malformed_error;
-	/* Get a maxinum block size. */
+	/* Get a maximum block size. */
 	switch (read_buf[1] >> 4) {
 	case 4: /* 64 KB */
 		state->flags.block_maximum_size = 64 * 1024;
@@ -627,7 +627,7 @@
 
 	if (state->stage == SELECT_STREAM) {
 		state->stage = READ_DEFAULT_STREAM;
-		/* First, read a desciprtor. */
+		/* First, read a descriptor. */
 		if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
 			return (ret);
 		state->stage = READ_DEFAULT_BLOCK;
@@ -706,6 +706,11 @@
 	/* Make sure we have a whole block. */
 	read_buf = __archive_read_filter_ahead(self->upstream,
 	    4 + compressed, NULL);
+	if (read_buf == NULL) {
+		archive_set_error(&(self->archive->archive),
+		    ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+		return (ARCHIVE_FATAL);
+	}
 	ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
 	    compressed, (int)state->out_block_size);
 	if (ret < 0) {
diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c
index 44ac996..a1c392f 100644
--- a/libarchive/archive_read_support_filter_lzop.c
+++ b/libarchive/archive_read_support_filter_lzop.c
@@ -436,7 +436,7 @@
 	}
 
 	/*
-	 * Drive lzo uncompresison.
+	 * Drive lzo uncompression.
 	 */
 	out_size = (lzo_uint)state->uncompressed_size;
 	r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c
index 66dc2f4..b8bf128 100644
--- a/libarchive/archive_read_support_filter_program.c
+++ b/libarchive/archive_read_support_filter_program.c
@@ -430,6 +430,7 @@
 	    &state->child_stdout);
 	if (child == -1) {
 		free(state->out_buf);
+		archive_string_free(&state->description);
 		free(state);
 		archive_set_error(&self->archive->archive, EINVAL,
 		    "Can't initialize filter; unable to run program \"%s\"",
@@ -441,6 +442,7 @@
 	if (state->child == NULL) {
 		child_stop(self, state);
 		free(state->out_buf);
+		archive_string_free(&state->description);
 		free(state);
 		archive_set_error(&self->archive->archive, EINVAL,
 		    "Can't initialize filter; unable to run program \"%s\"",
diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c
index 023c349..11807cf 100644
--- a/libarchive/archive_read_support_filter_xz.c
+++ b/libarchive/archive_read_support_filter_xz.c
@@ -293,7 +293,7 @@
 	/* Second through fifth bytes are dictionary size, stored in
 	 * little-endian order. The minimum dictionary size is
 	 * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option
-	 * -d12 and the maxinam dictionary size is 1 << 27(128MiB)
+	 * -d12 and the maximum dictionary size is 1 << 27(128MiB)
 	 * which the one uses with option -d27.
 	 * NOTE: A comment of LZMA SDK source code says this dictionary
 	 * range is from 1 << 12 to 1 << 30. */
@@ -584,9 +584,7 @@
 		return (ARCHIVE_FATAL);
 	}
 	ret = lzma_raw_decoder(&(state->stream), filters);
-#if LZMA_VERSION < 50010000
 	free(filters[0].options);
-#endif
 	if (ret != LZMA_OK) {
 		set_error(self, ret);
 		return (ARCHIVE_FATAL);
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
index 268daa2..3387eaf 100644
--- a/libarchive/archive_read_support_format_7zip.c
+++ b/libarchive/archive_read_support_format_7zip.c
@@ -263,22 +263,22 @@
 	/*
 	 * Decompressor controllers.
 	 */
-	/* Decording LZMA1 and LZMA2 data. */
+	/* Decoding LZMA1 and LZMA2 data. */
 #ifdef HAVE_LZMA_H
 	lzma_stream		 lzstream;
 	int			 lzstream_valid;
 #endif
-	/* Decording bzip2 data. */
+	/* Decoding bzip2 data. */
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 	bz_stream		 bzstream;
 	int			 bzstream_valid;
 #endif
-	/* Decording deflate data. */
+	/* Decoding deflate data. */
 #ifdef HAVE_ZLIB_H
 	z_stream		 stream;
 	int			 stream_valid;
 #endif
-	/* Decording PPMd data. */
+	/* Decoding PPMd data. */
 	int			 ppmd7_stat;
 	CPpmd7			 ppmd7_context;
 	CPpmd7z_RangeDec	 range_dec;
@@ -552,7 +552,7 @@
 	/*
 	 * If bytes_avail > SFX_MIN_ADDR we do not have to call
 	 * __archive_read_seek() at this time since we have
-	 * alredy had enough data.
+	 * already had enough data.
 	 */
 	if (bytes_avail > SFX_MIN_ADDR)
 		__archive_read_consume(a, SFX_MIN_ADDR);
@@ -760,7 +760,7 @@
 			symsize += size;
 		}
 		if (symsize == 0) {
-			/* If there is no synname, handle it as a regular
+			/* If there is no symname, handle it as a regular
 			 * file. */
 			zip_entry->mode &= ~AE_IFMT;
 			zip_entry->mode |= AE_IFREG;
@@ -1056,10 +1056,7 @@
 #endif
 	{
 		lzma_options_delta delta_opt;
-		lzma_filter filters[LZMA_FILTERS_MAX];
-#if LZMA_VERSION < 50010000
-		lzma_filter *ff;
-#endif
+		lzma_filter filters[LZMA_FILTERS_MAX], *ff;
 		int fi = 0;
 
 		if (zip->lzstream_valid) {
@@ -1144,9 +1141,7 @@
 		else
 			filters[fi].id = LZMA_FILTER_LZMA1;
 		filters[fi].options = NULL;
-#if LZMA_VERSION < 50010000
 		ff = &filters[fi];
-#endif
 		r = lzma_properties_decode(&filters[fi], NULL,
 		    coder1->properties, (size_t)coder1->propertiesSize);
 		if (r != LZMA_OK) {
@@ -1158,9 +1153,7 @@
 		filters[fi].id = LZMA_VLI_UNKNOWN;
 		filters[fi].options = NULL;
 		r = lzma_raw_decoder(&(zip->lzstream), filters);
-#if LZMA_VERSION < 50010000
 		free(ff->options);
-#endif
 		if (r != LZMA_OK) {
 			set_error(a, r);
 			return (ARCHIVE_FAILED);
@@ -3295,7 +3288,7 @@
 		return (r);
 
 	/*
-	 * Skip the bytes we alrady has skipped in skip_stream().
+	 * Skip the bytes we already has skipped in skip_stream().
 	 */
 	while (skip_bytes) {
 		ssize_t skipped;
@@ -3513,7 +3506,7 @@
 				return (ARCHIVE_FATAL);
 			}
 
-			/* Allocate memory for the decorded data of a sub
+			/* Allocate memory for the decoded data of a sub
 			 * stream. */
 			b[i] = malloc((size_t)zip->folder_outbytes_remaining);
 			if (b[i] == NULL) {
@@ -3598,7 +3591,7 @@
 	if (zip->folder_index == 0) {
 		/*
 		 * Optimization for a list mode.
-		 * Avoid unncecessary decoding operations.
+		 * Avoid unnecessary decoding operations.
 		 */
 		zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
 		    += skip_bytes;
diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c
index 2bdc1e2..e2f8c6b 100644
--- a/libarchive/archive_read_support_format_cab.c
+++ b/libarchive/archive_read_support_format_cab.c
@@ -1495,6 +1495,8 @@
 
 		/* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */
 		if (mszip > 0) {
+			if (bytes_avail <= 0)
+				goto nomszip;
 			if (bytes_avail <= mszip) {
 				if (mszip == 2) {
 					if (cab->stream.next_in[0] != 0x43)
diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c
index 7438bab..ffd4a85 100644
--- a/libarchive/archive_read_support_format_cpio.c
+++ b/libarchive/archive_read_support_format_cpio.c
@@ -326,7 +326,7 @@
 
 	cpio = (struct cpio *)(a->format->data);
 	if (strcmp(key, "compat-2x")  == 0) {
-		/* Handle filnames as libarchive 2.x */
+		/* Handle filenames as libarchive 2.x */
 		cpio->init_default_conversion = (val != NULL)?1:0;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "hdrcharset")  == 0) {
@@ -356,7 +356,7 @@
     struct archive_entry *entry)
 {
 	struct cpio *cpio;
-	const void *h;
+	const void *h, *hl;
 	struct archive_string_conv *sconv;
 	size_t namelength;
 	size_t name_pad;
@@ -406,11 +406,11 @@
 			    "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");
 			return (ARCHIVE_FATAL);
 		}
-		h = __archive_read_ahead(a,
+		hl = __archive_read_ahead(a,
 			(size_t)cpio->entry_bytes_remaining, NULL);
-		if (h == NULL)
+		if (hl == NULL)
 			return (ARCHIVE_FATAL);
-		if (archive_entry_copy_symlink_l(entry, (const char *)h,
+		if (archive_entry_copy_symlink_l(entry, (const char *)hl,
 		    (size_t)cpio->entry_bytes_remaining, sconv) != 0) {
 			if (errno == ENOMEM) {
 				archive_set_error(&a->archive, ENOMEM,
@@ -434,7 +434,8 @@
 	 * header.  XXX */
 
 	/* Compare name to "TRAILER!!!" to test for end-of-archive. */
-	if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
+	if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
+	    11) == 0) {
 		/* TODO: Store file location of start of block. */
 		archive_clear_error(&a->archive);
 		return (ARCHIVE_EOF);
diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c
index f41ba38..76da406 100644
--- a/libarchive/archive_read_support_format_iso9660.c
+++ b/libarchive/archive_read_support_format_iso9660.c
@@ -322,7 +322,7 @@
 
 	struct archive_string pathname;
 	char	seenRockridge;	/* Set true if RR extensions are used. */
-	char	seenSUSP;	/* Set true if SUSP is beging used. */
+	char	seenSUSP;	/* Set true if SUSP is being used. */
 	char	seenJoliet;
 
 	unsigned char	suspOffset;
@@ -374,7 +374,7 @@
 	size_t		 utf16be_path_len;
 	unsigned char *utf16be_previous_path;
 	size_t		 utf16be_previous_path_len;
-	/* Null buufer used in bidder to improve its performance. */
+	/* Null buffer used in bidder to improve its performance. */
 	unsigned char	 null[2048];
 };
 
@@ -1199,7 +1199,7 @@
 			    archive_string_conversion_from_charset(
 				&(a->archive), "UTF-16BE", 1);
 			if (iso9660->sconv_utf16be == NULL)
-				/* Coundn't allocate memory */
+				/* Couldn't allocate memory */
 				return (ARCHIVE_FATAL);
 		}
 		if (iso9660->utf16be_path == NULL) {
@@ -1864,7 +1864,7 @@
 		if ((file->utf16be_name = malloc(name_len)) == NULL) {
 			archive_set_error(&a->archive, ENOMEM,
 			    "No memory for file name");
-			return (NULL);
+			goto fail;
 		}
 		memcpy(file->utf16be_name, p, name_len);
 		file->utf16be_bytes = name_len;
@@ -1943,10 +1943,8 @@
 			file->symlink_continues = 0;
 			rr_start += iso9660->suspOffset;
 			r = parse_rockridge(a, file, rr_start, rr_end);
-			if (r != ARCHIVE_OK) {
-				free(file);
-				return (NULL);
-			}
+			if (r != ARCHIVE_OK)
+				goto fail;
 			/*
 			 * A file size of symbolic link files in ISO images
 			 * made by makefs is not zero and its location is
@@ -1990,7 +1988,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge RE");
-				return (NULL);
+				goto fail;
 			}
 			/*
 			 * Sanity check: file does not have "CL" extension.
@@ -1999,7 +1997,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge RE and CL");
-				return (NULL);
+				goto fail;
 			}
 			/*
 			 * Sanity check: The file type must be a directory.
@@ -2008,7 +2006,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge RE");
-				return (NULL);
+				goto fail;
 			}
 		} else if (parent != NULL && parent->rr_moved)
 			file->rr_moved_has_re_only = 0;
@@ -2022,7 +2020,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge CL");
-				return (NULL);
+				goto fail;
 			}
 			/*
 			 * Sanity check: The file type must be a regular file.
@@ -2031,7 +2029,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge CL");
-				return (NULL);
+				goto fail;
 			}
 			parent->subdirs++;
 			/* Overwrite an offset and a number of this "CL" entry
@@ -2049,7 +2047,7 @@
 					archive_set_error(&a->archive,
 					    ARCHIVE_ERRNO_MISC,
 					    "Invalid Rockridge CL");
-					return (NULL);
+					goto fail;
 				}
 			}
 			if (file->cl_offset == file->offset ||
@@ -2057,7 +2055,7 @@
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Invalid Rockridge CL");
-				return (NULL);
+				goto fail;
 			}
 		}
 	}
@@ -2088,6 +2086,10 @@
 #endif
 	register_file(iso9660, file);
 	return (file);
+fail:
+	archive_string_free(&file->name);
+	free(file);
+	return (NULL);
 }
 
 static int
@@ -2407,7 +2409,7 @@
 				return (ARCHIVE_FATAL);
 		} while (heap->cnt &&
 		    heap->reqs[0].offset == iso9660->current_position);
-		/* NOTE: Do not move this consume's code to fron of
+		/* NOTE: Do not move this consume's code to front of
 		 * do-while loop. Registration of nested CE extension
 		 * might cause error because of current position. */
 		__archive_read_consume(a, step);
@@ -2729,7 +2731,7 @@
 		if (file == NULL) {
 			/*
 			 * If directory entries all which are descendant of
-			 * rr_moved are stil remaning, expose their. 
+			 * rr_moved are still remaining, expose their.
 			 */
 			if (iso9660->re_files.first != NULL && 
 			    iso9660->rr_moved != NULL &&
@@ -2852,7 +2854,7 @@
 	empty_files.last = &empty_files.first;
 	/* Collect files which has the same file serial number.
 	 * Peek pending_files so that file which number is different
-	 * is not put bak. */
+	 * is not put back. */
 	while (iso9660->pending_files.used > 0 &&
 	    (iso9660->pending_files.files[0]->number == -1 ||
 	     iso9660->pending_files.files[0]->number == number)) {
@@ -2860,7 +2862,7 @@
 			/* This file has the same offset
 			 * but it's wrong offset which empty files
 			 * and symlink files have.
-			 * NOTE: This wrong offse was recorded by
+			 * NOTE: This wrong offset was recorded by
 			 * old mkisofs utility. If ISO images is
 			 * created by latest mkisofs, this does not
 			 * happen.
diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c
index a7f1d8d..d77a7c2 100644
--- a/libarchive/archive_read_support_format_lha.c
+++ b/libarchive/archive_read_support_format_lha.c
@@ -924,6 +924,9 @@
 	/* Get a real compressed file size. */
 	lha->compsize -= extdsize - 2;
 
+	if (lha->compsize < 0)
+		goto invalid;	/* Invalid compressed file size */
+
 	if (sum_calculated != headersum) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "LHa header sum error");
@@ -1711,7 +1714,7 @@
 	 */
 	for (;len >= 8; len -= 8) {
 		/* This if statement expects compiler optimization will
-		 * remove the stament which will not be executed. */
+		 * remove the statement which will not be executed. */
 #undef bswap16
 #if defined(_MSC_VER) && _MSC_VER >= 1400  /* Visual Studio */
 #  define bswap16(x) _byteswap_ushort(x)
diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c
index d0aa84c..4231ff5 100644
--- a/libarchive/archive_read_support_format_mtree.c
+++ b/libarchive/archive_read_support_format_mtree.c
@@ -715,13 +715,13 @@
 				}
 			} else
 				break;
-		} else if (strncmp(p, "/set", 4) == 0) {
+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {
 			if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
 				break;
 			/* This line continues. */
 			if (p[len-nl-1] == '\\')
 				multiline = 2;
-		} else if (strncmp(p, "/unset", 6) == 0) {
+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
 			if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
 				break;
 			/* This line continues. */
@@ -1019,11 +1019,11 @@
 		if (*p != '/') {
 			r = process_add_entry(a, mtree, &global, p, len,
 			    &last_entry, is_form_d);
-		} else if (strncmp(p, "/set", 4) == 0) {
+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {
 			if (p[4] != ' ' && p[4] != '\t')
 				break;
 			r = process_global_set(a, &global, p);
-		} else if (strncmp(p, "/unset", 6) == 0) {
+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
 			if (p[6] != ' ' && p[6] != '\t')
 				break;
 			r = process_global_unset(a, &global, p);
@@ -1608,8 +1608,11 @@
 			if (*val == '.') {
 				++val;
 				ns = (long)mtree_atol10(&val);
-			} else
-				ns = 0;
+				if (ns < 0)
+					ns = 0;
+				else if (ns > 999999999)
+					ns = 999999999;
+			}
 			if (m > my_time_t_max)
 				m = my_time_t_max;
 			else if (m < my_time_t_min)
diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c
index 9c9f6f1..1e9849f 100644
--- a/libarchive/archive_read_support_format_rar.c
+++ b/libarchive/archive_read_support_format_rar.c
@@ -906,7 +906,7 @@
                             sizeof(rar->reserved2));
       }
 
-      /* Main header is password encrytped, so we cannot read any
+      /* Main header is password encrypted, so we cannot read any
          file names or any other info about files from the header. */
       if (rar->main_flags & MHD_PASSWORD)
       {
diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c
index 948929a..bd7f13d 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -203,9 +204,14 @@
 		    struct archive_entry *);
 static int	checksum(struct archive_read *, const void *);
 static int 	pax_attribute(struct archive_read *, struct tar *,
-		    struct archive_entry *, const char *key, const char *value);
+		    struct archive_entry *, const char *key, const char *value,
+		    size_t value_length);
+static int	pax_attribute_acl(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *, int);
+static int	pax_attribute_xattr(struct archive_entry *, const char *,
+		    const char *);
 static int 	pax_header(struct archive_read *, struct tar *,
-		    struct archive_entry *, char *attr);
+		    struct archive_entry *, struct archive_string *);
 static void	pax_time(const char *, int64_t *sec, long *nanos);
 static ssize_t	readline(struct archive_read *, struct tar *, const char **,
 		    ssize_t limit, size_t *);
@@ -841,9 +847,9 @@
 				tar->sparse_gnu_pending = 0;
 				/* Read initial sparse map. */
 				bytes_read = gnu_sparse_10_read(a, tar, unconsumed);
-				tar->entry_bytes_remaining -= bytes_read;
 				if (bytes_read < 0)
 					return ((int)bytes_read);
+				tar->entry_bytes_remaining -= bytes_read;
 			} else {
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
@@ -938,7 +944,7 @@
 {
 	const struct archive_entry_header_ustar *header;
 	size_t size;
-	int err;
+	int err, acl_type;
 	int64_t type;
 	char *acl, *p;
 
@@ -983,11 +989,12 @@
 	switch ((int)type & ~0777777) {
 	case 01000000:
 		/* POSIX.1e ACL */
+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 		break;
 	case 03000000:
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Solaris NFSv4 ACLs not supported");
-		return (ARCHIVE_WARN);
+		/* NFSv4 ACL */
+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4;
+		break;
 	default:
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Malformed Solaris ACL attribute (unsupported type %o)",
@@ -1016,8 +1023,8 @@
 			return (ARCHIVE_FATAL);
 	}
 	archive_strncpy(&(tar->localname), acl, p - acl);
-	err = archive_acl_parse_l(archive_entry_acl(entry),
-	    tar->localname.s, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, tar->sconv_acl);
+	err = archive_acl_from_text_l(archive_entry_acl(entry),
+	    tar->localname.s, acl_type, tar->sconv_acl);
 	if (err != ARCHIVE_OK) {
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
@@ -1478,7 +1485,7 @@
 	 * and then skip any fields in the standard header that were
 	 * defined in the pax header.
 	 */
-	err2 = pax_header(a, tar, entry, tar->pax_header.s);
+	err2 = pax_header(a, tar, entry, &tar->pax_header);
 	err =  err_combine(err, err2);
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 	return (err);
@@ -1559,16 +1566,17 @@
  */
 static int
 pax_header(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, char *attr)
+    struct archive_entry *entry, struct archive_string *in_as)
 {
-	size_t attr_length, l, line_length;
+	size_t attr_length, l, line_length, value_length;
 	char *p;
 	char *key, *value;
 	struct archive_string *as;
 	struct archive_string_conv *sconv;
 	int err, err2;
+	char *attr = in_as->s;
 
-	attr_length = strlen(attr);
+	attr_length = in_as->length;
 	tar->pax_hdrcharset_binary = 0;
 	archive_string_empty(&(tar->entry_gname));
 	archive_string_empty(&(tar->entry_linkpath));
@@ -1633,11 +1641,13 @@
 		}
 		*p = '\0';
 
-		/* Identify null-terminated 'value' portion. */
 		value = p + 1;
 
+		/* Some values may be binary data */
+		value_length = attr + line_length - 1 - value;
+
 		/* Identify this attribute and set it in the entry. */
-		err2 = pax_attribute(a, tar, entry, key, value);
+		err2 = pax_attribute(a, tar, entry, key, value, value_length);
 		if (err2 == ARCHIVE_FATAL)
 			return (err2);
 		err = err_combine(err, err2);
@@ -1758,6 +1768,66 @@
 	return 0;
 }
 
+static int
+pax_attribute_schily_xattr(struct archive_entry *entry,
+	const char *name, const char *value, size_t value_length)
+{
+	if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0)
+		return 1;
+
+	name += 13;
+
+	archive_entry_xattr_add_entry(entry, name, value, value_length);
+
+	return 0;
+}
+
+static int
+pax_attribute_acl(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *value, int type)
+{
+	int r;
+	const char* errstr;
+
+	switch (type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		errstr = "SCHILY.acl.access";
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		errstr = "SCHILY.acl.default";
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		errstr = "SCHILY.acl.ace";
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unknown ACL type: %d", type);
+		return(ARCHIVE_FATAL);
+	}
+
+	if (tar->sconv_acl == NULL) {
+		tar->sconv_acl =
+		    archive_string_conversion_from_charset(
+			&(a->archive), "UTF-8", 1);
+		if (tar->sconv_acl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+
+	r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,
+	    tar->sconv_acl);
+	if (r != ARCHIVE_OK) {
+		if (r == ARCHIVE_FATAL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "%s %s", "Can't allocate memory for ",
+			    errstr);
+			return (r);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+	}
+	return (r);
+}
+
 /*
  * Parse a single key=value attribute.  key/value pointers are
  * assumed to point into reasonably long-lived storage.
@@ -1773,7 +1843,7 @@
  */
 static int
 pax_attribute(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const char *key, const char *value)
+    struct archive_entry *entry, const char *key, const char *value, size_t value_length)
 {
 	int64_t s;
 	long n;
@@ -1874,53 +1944,20 @@
 	case 'S':
 		/* We support some keys used by the "star" archiver */
 		if (strcmp(key, "SCHILY.acl.access") == 0) {
-			if (tar->sconv_acl == NULL) {
-				tar->sconv_acl =
-				    archive_string_conversion_from_charset(
-					&(a->archive), "UTF-8", 1);
-				if (tar->sconv_acl == NULL)
-					return (ARCHIVE_FATAL);
-			}
-
-			r = archive_acl_parse_l(archive_entry_acl(entry),
-			    value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-			    tar->sconv_acl);
-			if (r != ARCHIVE_OK) {
-				err = r;
-				if (err == ARCHIVE_FATAL) {
-					archive_set_error(&a->archive, ENOMEM,
-					    "Can't allocate memory for "
-					    "SCHILY.acl.access");
-					return (err);
-				}
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_MISC,
-				    "Parse error: SCHILY.acl.access");
-			}
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+			if (r == ARCHIVE_FATAL)
+				return (r);
 		} else if (strcmp(key, "SCHILY.acl.default") == 0) {
-			if (tar->sconv_acl == NULL) {
-				tar->sconv_acl =
-				    archive_string_conversion_from_charset(
-					&(a->archive), "UTF-8", 1);
-				if (tar->sconv_acl == NULL)
-					return (ARCHIVE_FATAL);
-			}
-
-			r = archive_acl_parse_l(archive_entry_acl(entry),
-			    value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
-			    tar->sconv_acl);
-			if (r != ARCHIVE_OK) {
-				err = r;
-				if (err == ARCHIVE_FATAL) {
-					archive_set_error(&a->archive, ENOMEM,
-					    "Can't allocate memory for "
-					    "SCHILY.acl.default");
-					return (err);
-				}
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_MISC,
-				    "Parse error: SCHILY.acl.default");
-			}
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+		} else if (strcmp(key, "SCHILY.acl.ace") == 0) {
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+			if (r == ARCHIVE_FATAL)
+				return (r);
 		} else if (strcmp(key, "SCHILY.devmajor") == 0) {
 			archive_entry_set_rdevmajor(entry,
 			    (dev_t)tar_atol10(value, strlen(value)));
@@ -1941,6 +1978,9 @@
 		} else if (strcmp(key, "SCHILY.realsize") == 0) {
 			tar->realsize = tar_atol10(value, strlen(value));
 			archive_entry_set_size(entry, tar->realsize);
+		} else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {
+			pax_attribute_schily_xattr(entry, key, value,
+			    value_length);
 		} else if (strcmp(key, "SUN.holesdata") == 0) {
 			/* A Solaris extension for sparse. */
 			r = solaris_sparse_parse(a, tar, entry, value);
@@ -2447,6 +2487,9 @@
 	tar_flush_unconsumed(a, unconsumed);
 	bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining);
 	to_skip = 0x1ff & -bytes_read;
+	/* Fail if tar->entry_bytes_remaing would get negative */
+	if (to_skip > remaining)
+		return (ARCHIVE_FATAL);
 	if (to_skip != __archive_read_consume(a, to_skip))
 		return (ARCHIVE_FATAL);
 	return ((ssize_t)(bytes_read + to_skip));
diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c
index a287fc2..e875385 100644
--- a/libarchive/archive_read_support_format_warc.c
+++ b/libarchive/archive_read_support_format_warc.c
@@ -88,7 +88,7 @@
 	WT_RVIS,
 	/* conversion, unsupported */
 	WT_CONV,
-	/* continutation, unsupported at the moment */
+	/* continuation, unsupported at the moment */
 	WT_CONT,
 	/* invalid type */
 	LAST_WT
@@ -134,8 +134,8 @@
 static time_t _warc_rdrtm(const char *buf, size_t bsz);
 static time_t _warc_rdmtm(const char *buf, size_t bsz);
 static const char *_warc_find_eoh(const char *buf, size_t bsz);
+static const char *_warc_find_eol(const char *buf, size_t bsz);
 
-
 int
 archive_read_support_format_warc(struct archive *_a)
 {
@@ -198,8 +198,8 @@
 
 	/* otherwise snarf the record's version number */
 	ver = _warc_rdver(hdr, nrd);
-	if (ver == 0U || ver > 10000U) {
-		/* oh oh oh, best not to wager ... */
+	if (ver < 1200U || ver > 10000U) {
+		/* we only support WARC 0.12 to 1.0 */
 		return -1;
 	}
 
@@ -254,23 +254,32 @@
 			&a->archive, ARCHIVE_ERRNO_MISC,
 			"Bad record header");
 		return (ARCHIVE_FATAL);
-	} else if ((ver = _warc_rdver(buf, eoh - buf)) > 10000U) {
-		/* nawww, I wish they promised backward compatibility
-		 * anyhoo, in their infinite wisdom the 28500 guys might
-		 * come up with something we can't possibly handle so
-		 * best end things here */
+	}
+	ver = _warc_rdver(buf, eoh - buf);
+	/* we currently support WARC 0.12 to 1.0 */
+	if (ver == 0U) {
 		archive_set_error(
 			&a->archive, ARCHIVE_ERRNO_MISC,
-			"Unsupported record version");
+			"Invalid record version");
 		return (ARCHIVE_FATAL);
-	} else if ((cntlen = _warc_rdlen(buf, eoh - buf)) < 0) {
+	} else if (ver < 1200U || ver > 10000U) {
+		archive_set_error(
+			&a->archive, ARCHIVE_ERRNO_MISC,
+			"Unsupported record version: %u.%u",
+			ver / 10000, (ver % 10000) / 100);
+		return (ARCHIVE_FATAL);
+	}
+	cntlen = _warc_rdlen(buf, eoh - buf);
+	if (cntlen < 0) {
 		/* nightmare!  the specs say content-length is mandatory
 		 * so I don't feel overly bad stopping the reader here */
 		archive_set_error(
 			&a->archive, EINVAL,
 			"Bad content length");
 		return (ARCHIVE_FATAL);
-	} else if ((rtime = _warc_rdrtm(buf, eoh - buf)) == (time_t)-1) {
+	}
+	rtime = _warc_rdrtm(buf, eoh - buf);
+	if (rtime == (time_t)-1) {
 		/* record time is mandatory as per WARC/1.0,
 		 * so just barf here, fast and loud */
 		archive_set_error(
@@ -284,7 +293,7 @@
 	if (ver != w->pver) {
 		/* stringify this entry's version */
 		archive_string_sprintf(&w->sver,
-			"WARC/%u.%u", ver / 10000, ver % 10000);
+			"WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
 		/* remember the version */
 		w->pver = ver;
 	}
@@ -534,7 +543,7 @@
 
 	/* as a courtesy to our callers, and since this is a non-standard
 	 * routine, we skip leading whitespace */
-	while (isspace((unsigned char)*s))
+	while (*s == ' ' || *s == '\t')
 		++s;
 
 	/* read year */
@@ -562,7 +571,7 @@
 		goto out;
 	}
 
-	/* massage TM to fulfill some of POSIX' contraints */
+	/* massage TM to fulfill some of POSIX' constraints */
 	tm.tm_year -= 1900;
 	tm.tm_mon--;
 
@@ -577,51 +586,44 @@
 }
 
 static unsigned int
-_warc_rdver(const char buf[10], size_t bsz)
+_warc_rdver(const char *buf, size_t bsz)
 {
 	static const char magic[] = "WARC/";
-	unsigned int ver;
+	const char *c;
+	unsigned int ver = 0U;
+	unsigned int end = 0U;
 
-	(void)bsz; /* UNUSED */
-
-	if (memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
-		/* nope */
-		return 99999U;
+	if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
+		/* buffer too small or invalid magic */
+		return ver;
 	}
 	/* looks good so far, read the version number for a laugh */
 	buf += sizeof(magic) - 1U;
-	/* most common case gets a quick-check here */
-	if (memcmp(buf, "1.0\r\n", 5U) == 0) {
-		ver = 10000U;
-	} else {
-		switch (*buf) {
-		case '0':
-		case '1':
-		case '2':
-		case '3':
-		case '4':
-		case '5':
-		case '6':
-		case '7':
-		case '8':
-			if (buf[1U] == '.') {
-				char *on;
 
-				/* set up major version */
-				ver = (buf[0U] - '0') * 10000U;
-				/* minor version, anyone? */
-				ver += (strtol(buf + 2U, &on, 10)) * 100U;
-				/* don't parse anything else */
-				if (on > buf + 2U) {
-					break;
-				}
-			}
-			/* FALLTHROUGH */
-		case '9':
-		default:
-			/* just make the version ridiculously high */
-			ver = 999999U;
-			break;
+	if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
+	    isdigit((unsigned char)buf[2U])) {
+		/* we support a maximum of 2 digits in the minor version */
+		if (isdigit((unsigned char)buf[3U]))
+			end = 1U;
+		/* set up major version */
+		ver = (buf[0U] - '0') * 10000U;
+		/* set up minor version */
+		if (end == 1U) {
+			ver += (buf[2U] - '0') * 1000U;
+			ver += (buf[3U] - '0') * 100U;
+		} else
+			ver += (buf[2U] - '0') * 100U;
+		/*
+		 * WARC below version 0.12 has a space-separated header
+		 * WARC 0.12 and above terminates the version with a CRLF
+		 */
+		c = buf + 3U + end;
+		if (ver >= 1200U) {
+			if (memcmp(c, "\r\n", 2U) != 0)
+				ver = 0U;
+		} else if (ver < 1200U) {
+			if (*c != ' ' && *c != '\t')
+				ver = 0U;
 		}
 	}
 	return ver;
@@ -631,34 +633,27 @@
 _warc_rdtyp(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Type:";
-	const char *const eob = buf + bsz;
-	const char *val;
+	const char *val, *eol;
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
 		/* no bother */
 		return WT_NONE;
 	}
-	/* overread whitespace */
 	val += sizeof(_key) - 1U;
-	while (val < eob && isspace((unsigned char)*val))
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
+		return WT_NONE;
+	}
+
+	/* overread whitespace */
+	while (val < eol && (*val == ' ' || *val == '\t'))
 		++val;
 
-	if (val + 8U > eob) {
-		;
-	} else if (memcmp(val, "resource", 8U) == 0) {
-		return WT_RSRC;
-	} else if (memcmp(val, "warcinfo", 8U) == 0) {
-		return WT_INFO;
-	} else if (memcmp(val, "metadata", 8U) == 0) {
-		return WT_META;
-	} else if (memcmp(val, "request", 7U) == 0) {
-		return WT_REQ;
-	} else if (memcmp(val, "response", 8U) == 0) {
-		return WT_RSP;
-	} else if (memcmp(val, "conversi", 8U) == 0) {
-		return WT_CONV;
-	} else if (memcmp(val, "continua", 8U) == 0) {
-		return WT_CONT;
+	if (val + 8U == eol) {
+		if (memcmp(val, "resource", 8U) == 0)
+			return WT_RSRC;
+		else if (memcmp(val, "response", 8U) == 0)
+			return WT_RSP;
 	}
 	return WT_NONE;
 }
@@ -667,10 +662,7 @@
 _warc_rduri(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Target-URI:";
-	const char *const eob = buf + bsz;
-	const char *val;
-	const char *uri;
-	const char *eol;
+	const char *val, *uri, *eol, *p;
 	warc_string_t res = {0U, NULL};
 
 	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
@@ -679,25 +671,32 @@
 	}
 	/* overread whitespace */
 	val += sizeof(_key) - 1U;
-	while (val < eob && isspace((unsigned char)*val))
-		++val;
-
-	/* overread URL designators */
-	if ((uri = xmemmem(val, eob - val, "://", 3U)) == NULL) {
-		/* not touching that! */
-		return res;
-	} else if ((eol = memchr(uri, '\n', eob - uri)) == NULL) {
-		/* no end of line? :O */
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
 		return res;
 	}
 
-	/* massage uri to point to after :// */
+	while (val < eol && (*val == ' ' || *val == '\t'))
+		++val;
+
+	/* overread URL designators */
+	if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
+		/* not touching that! */
+		return res;
+	}
+
+	/* spaces inside uri are not allowed, CRLF should follow */
+	for (p = val; p < eol; p++) {
+		if (isspace((unsigned char)*p))
+			return res;
+	}
+
+	/* there must be at least space for ftp */
+	if (uri < (val + 3U))
+		return res;
+
+	/* move uri to point to after :// */
 	uri += 3U;
-	/* also massage eol to point to the first whitespace
-	 * after the last non-whitespace character before
-	 * the end of the line */
-	while (eol > uri && isspace((unsigned char)eol[-1]))
-		--eol;
 
 	/* now then, inspect the URI */
 	if (memcmp(val, "file", 4U) == 0) {
@@ -720,7 +719,7 @@
 _warc_rdlen(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nContent-Length:";
-	const char *val;
+	const char *val, *eol;
 	char *on = NULL;
 	long int len;
 
@@ -728,14 +727,24 @@
 		/* no bother */
 		return -1;
 	}
-
-	/* strtol kindly overreads whitespace for us, so use that */
 	val += sizeof(_key) - 1U;
-	len = strtol(val, &on, 10);
-	if (on == NULL || !isspace((unsigned char)*on)) {
-		/* hm, can we trust that number?  Best not. */
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
 		return -1;
 	}
+
+	/* skip leading whitespace */
+	while (val < eol && (*val == ' ' || *val == '\t'))
+		val++;
+	/* there must be at least one digit */
+	if (!isdigit((unsigned char)*val))
+		return -1;
+	len = strtol(val, &on, 10);
+	if (on != eol) {
+		/* line must end here */
+		return -1;
+	}
+
 	return (size_t)len;
 }
 
@@ -743,7 +752,7 @@
 _warc_rdrtm(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nWARC-Date:";
-	const char *val;
+	const char *val, *eol;
 	char *on = NULL;
 	time_t res;
 
@@ -751,13 +760,17 @@
 		/* no bother */
 		return (time_t)-1;
 	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* no end of line */
+		return -1;
+	}
 
 	/* xstrpisotime() kindly overreads whitespace for us, so use that */
-	val += sizeof(_key) - 1U;
 	res = xstrpisotime(val, &on);
-	if (on == NULL || !isspace((unsigned char)*on)) {
-		/* hm, can we trust that number?  Best not. */
-		return (time_t)-1;
+	if (on != eol) {
+		/* line must end here */
+		return -1;
 	}
 	return res;
 }
@@ -766,7 +779,7 @@
 _warc_rdmtm(const char *buf, size_t bsz)
 {
 	static const char _key[] = "\r\nLast-Modified:";
-	const char *val;
+	const char *val, *eol;
 	char *on = NULL;
 	time_t res;
 
@@ -774,13 +787,17 @@
 		/* no bother */
 		return (time_t)-1;
 	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* no end of line */
+		return -1;
+	}
 
 	/* xstrpisotime() kindly overreads whitespace for us, so use that */
-	val += sizeof(_key) - 1U;
 	res = xstrpisotime(val, &on);
-	if (on == NULL || !isspace((unsigned char)*on)) {
-		/* hm, can we trust that number?  Best not. */
-		return (time_t)-1;
+	if (on != eol) {
+		/* line must end here */
+		return -1;
 	}
 	return res;
 }
@@ -797,4 +814,12 @@
 	return hit;
 }
 
+static const char*
+_warc_find_eol(const char *buf, size_t bsz)
+{
+	static const char _marker[] = "\r\n";
+	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+	return hit;
+}
 /* archive_read_support_format_warc.c ends here */
diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c
index 47ed064..7a22beb 100644
--- a/libarchive/archive_read_support_format_xar.c
+++ b/libarchive/archive_read_support_format_xar.c
@@ -394,6 +394,7 @@
 		    size_t, const void *, size_t);
 static int	checksum_final(struct archive_read *, const void *,
 		    size_t, const void *, size_t);
+static void	checksum_cleanup(struct archive_read *);
 static int	decompression_init(struct archive_read *, enum enctype);
 static int	decompress(struct archive_read *, const void **,
 		    size_t *, const void *, size_t *);
@@ -923,6 +924,7 @@
 	int r;
 
 	xar = (struct xar *)(a->format->data);
+	checksum_cleanup(a);
 	r = decompression_cleanup(a);
 	hdlink = xar->hdlink_list;
 	while (hdlink != NULL) {
@@ -933,6 +935,7 @@
 	}
 	for (i = 0; i < xar->file_queue.used; i++)
 		file_free(xar->file_queue.files[i]);
+	free(xar->file_queue.files);
 	while (xar->unknowntags != NULL) {
 		struct unknown_tag *tag;
 
@@ -1719,6 +1722,16 @@
 }
 
 static void
+checksum_cleanup(struct archive_read *a) {
+	struct xar *xar;
+
+	xar = (struct xar *)(a->format->data);
+
+	_checksum_final(&(xar->a_sumwrk), NULL, 0);
+	_checksum_final(&(xar->e_sumwrk), NULL, 0);
+}
+
+static void
 xmlattr_cleanup(struct xmlattr_list *list)
 {
 	struct xmlattr *attr, *next;
@@ -3047,7 +3060,7 @@
 	struct xar *xar;
 	const void *d;
 	size_t outbytes;
-	size_t used;
+	size_t used = 0;
 	int r;
 
 	a = (struct archive_read *)context;
@@ -3171,6 +3184,9 @@
 		value = strdup(atts[1]);
 		if (attr == NULL || name == NULL || value == NULL) {
 			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			free(attr);
+			free(name);
+			free(value);
 			return (ARCHIVE_FATAL);
 		}
 		attr->name = name;
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index 9796fca..08bcf1f 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -199,7 +199,7 @@
 	struct trad_enc_ctx	tctx;
 	char			tctx_valid;
 
-	/* WinZip AES decyption. */
+	/* WinZip AES decryption. */
 	/* Contexts used for AES decryption. */
 	archive_crypto_ctx	cctx;
 	char			cctx_valid;
@@ -242,7 +242,7 @@
 }
 
 static uint8_t
-trad_enc_decypt_byte(struct trad_enc_ctx *ctx)
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
 {
 	unsigned temp = ctx->keys[2] | 2;
 	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
@@ -257,7 +257,7 @@
 	max = (unsigned)((in_len < out_len)? in_len: out_len);
 
 	for (i = 0; i < max; i++) {
-		uint8_t t = in[i] ^ trad_enc_decypt_byte(ctx);
+		uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx);
 		out[i] = t;
 		trad_enc_update_keys(ctx, t);
 	}
@@ -452,26 +452,38 @@
 			/* Zip64 extended information extra field. */
 			zip_entry->flags |= LA_USED_ZIP64;
 			if (zip_entry->uncompressed_size == 0xffffffff) {
-				if (datasize < 8)
-					break;
-				zip_entry->uncompressed_size =
-				    archive_le64dec(p + offset);
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) > INT64_MAX) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit uncompressed size");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->uncompressed_size = t;
 				offset += 8;
 				datasize -= 8;
 			}
 			if (zip_entry->compressed_size == 0xffffffff) {
-				if (datasize < 8)
-					break;
-				zip_entry->compressed_size =
-				    archive_le64dec(p + offset);
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) > INT64_MAX) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit compressed size");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->compressed_size = t;
 				offset += 8;
 				datasize -= 8;
 			}
 			if (zip_entry->local_header_offset == 0xffffffff) {
-				if (datasize < 8)
-					break;
-				zip_entry->local_header_offset =
-				    archive_le64dec(p + offset);
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) > INT64_MAX) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit local header offset");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->local_header_offset = t;
 				offset += 8;
 				datasize -= 8;
 			}
@@ -710,7 +722,7 @@
 			break;
 		}
 		case 0x9901:
-			/* WinZIp AES extra data field. */
+			/* WinZip AES extra data field. */
 			if (p[offset + 2] == 'A' && p[offset + 3] == 'E') {
 				/* Vendor version. */
 				zip_entry->aes_extra.vendor =
@@ -864,29 +876,33 @@
 		zip_entry->mode |= AE_IFREG;
 	}
 
-	if ((zip_entry->mode & AE_IFMT) == 0) {
-		/* Especially in streaming mode, we can end up
-		   here without having seen proper mode information.
-		   Guess from the filename. */
+	/* If the mode is totally empty, set some sane default. */
+	if (zip_entry->mode == 0) {
+		zip_entry->mode |= 0664;
+	}
+
+	/* Make sure that entries with a trailing '/' are marked as directories
+	 * even if the External File Attributes contains bogus values.  If this
+	 * is not a directory and there is no type, assume regularfile. */
+	if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {
+		int has_slash;
+
 		wp = archive_entry_pathname_w(entry);
 		if (wp != NULL) {
 			len = wcslen(wp);
-			if (len > 0 && wp[len - 1] == L'/')
-				zip_entry->mode |= AE_IFDIR;
-			else
-				zip_entry->mode |= AE_IFREG;
+			has_slash = len > 0 && wp[len - 1] == L'/';
 		} else {
 			cp = archive_entry_pathname(entry);
 			len = (cp != NULL)?strlen(cp):0;
-			if (len > 0 && cp[len - 1] == '/')
-				zip_entry->mode |= AE_IFDIR;
-			else
-				zip_entry->mode |= AE_IFREG;
+			has_slash = len > 0 && cp[len - 1] == '/';
 		}
-		if (zip_entry->mode == AE_IFDIR) {
-			zip_entry->mode |= 0775;
-		} else if (zip_entry->mode == AE_IFREG) {
-			zip_entry->mode |= 0664;
+		/* Correct file type as needed. */
+		if (has_slash) {
+			zip_entry->mode &= ~AE_IFMT;
+			zip_entry->mode |= AE_IFDIR;
+			zip_entry->mode |= 0111;
+		} else if ((zip_entry->mode & AE_IFMT) == 0) {
+			zip_entry->mode |= AE_IFREG;
 		}
 	}
 
@@ -901,6 +917,7 @@
 				archive_wstrcat(&s, wp);
 				archive_wstrappend_wchar(&s, L'/');
 				archive_entry_copy_pathname_w(entry, s.s);
+				archive_wstring_free(&s);
 			}
 		} else {
 			cp = archive_entry_pathname(entry);
@@ -911,6 +928,7 @@
 				archive_strcat(&s, cp);
 				archive_strappend_char(&s, '/');
 				archive_entry_set_pathname(entry, s.s);
+				archive_string_free(&s);
 			}
 		}
 	}
@@ -1150,11 +1168,18 @@
 			|| (zip->hctx_valid
 			 && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
 			if (zip->entry->flags & LA_USED_ZIP64) {
+				uint64_t compressed, uncompressed;
 				zip->entry->crc32 = archive_le32dec(p + 4);
-				zip->entry->compressed_size =
-					archive_le64dec(p + 8);
-				zip->entry->uncompressed_size =
-					archive_le64dec(p + 16);
+				compressed = archive_le64dec(p + 8);
+				uncompressed = archive_le64dec(p + 16);
+				if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Overflow of 64-bit file sizes");
+					return ARCHIVE_FAILED;
+				}
+				zip->entry->compressed_size = compressed;
+				zip->entry->uncompressed_size = uncompressed;
 				zip->unconsumed = 24;
 			} else {
 				zip->entry->crc32 = archive_le32dec(p + 4);
@@ -1431,9 +1456,18 @@
 			zip->unconsumed = 4;
 		}
 		if (zip->entry->flags & LA_USED_ZIP64) {
+			uint64_t compressed, uncompressed;
 			zip->entry->crc32 = archive_le32dec(p);
-			zip->entry->compressed_size = archive_le64dec(p + 4);
-			zip->entry->uncompressed_size = archive_le64dec(p + 12);
+			compressed = archive_le64dec(p + 4);
+			uncompressed = archive_le64dec(p + 12);
+			if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Overflow of 64-bit file sizes");
+				return ARCHIVE_FAILED;
+			}
+			zip->entry->compressed_size = compressed;
+			zip->entry->uncompressed_size = uncompressed;
 			zip->unconsumed += 20;
 		} else {
 			zip->entry->crc32 = archive_le32dec(p);
@@ -1514,7 +1548,7 @@
 	case 0x6720:/* Blowfish */
 	case 0x6721:/* Twofish */
 	case 0x6801:/* RC4 */
-		/* Suuported encryption algorithm. */
+		/* Supported encryption algorithm. */
 		break;
 	default:
 		archive_set_error(&a->archive,
@@ -1623,7 +1657,7 @@
 	__archive_read_consume(a, 4);
 
 	/*return (ARCHIVE_OK);
-	 * This is not fully implemnted yet.*/
+	 * This is not fully implemented yet.*/
 	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 	    "Encrypted file is unsupported");
 	return (ARCHIVE_FAILED);
@@ -1705,7 +1739,7 @@
 		}
 
 		/*
-		 * Initialize ctx for Traditional PKWARE Decyption.
+		 * Initialize ctx for Traditional PKWARE Decryption.
 		 */
 		r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),
 			p, ENC_HEADER_SIZE, &crcchk);
diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c
index aa57f01..592ead2 100644
--- a/libarchive/archive_string.c
+++ b/libarchive/archive_string.c
@@ -219,6 +219,12 @@
 	return (as);
 }
 
+struct archive_string *
+archive_array_append(struct archive_string *as, const char *p, size_t s)
+{
+	return archive_string_append(as, p, s);
+}
+
 void
 archive_string_concat(struct archive_string *dest, struct archive_string *src)
 {
@@ -597,7 +603,7 @@
 	wcs = dest->s + dest->length;
 	/*
 	 * We cannot use mbsrtowcs/mbstowcs here because those may convert
-	 * extra MBS when strlen(p) > len and one wide character consis of
+	 * extra MBS when strlen(p) > len and one wide character consists of
 	 * multi bytes.
 	 */
 	while (*mbs && mbs_length > 0) {
@@ -1248,7 +1254,7 @@
 		sc->cd = iconv_open(tc, fc);
 		if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
 			/*
-			 * Unfortunaly, all of iconv implements do support 
+			 * Unfortunately, all of iconv implements do support
 			 * "CP932" character-set, so we should use "SJIS"
 			 * instead if iconv_open failed.
 			 */
@@ -1261,7 +1267,7 @@
 		/*
 		 * archive_mstring on Windows directly convert multi-bytes
 		 * into archive_wstring in order not to depend on locale
-		 * so that you can do a I18N programing. This will be
+		 * so that you can do a I18N programming. This will be
 		 * used only in archive_mstring_copy_mbs_len_l so far.
 		 */
 		if (flag & SCONV_FROM_CHARSET) {
@@ -1726,7 +1732,7 @@
  * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP
  * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).
  * So we should make a string conversion between CP_ACP and CP_OEMCP
- * for compatibillty.
+ * for compatibility.
  */
 #if defined(_WIN32) && !defined(__CYGWIN__)
 struct archive_string_conv *
@@ -1827,7 +1833,7 @@
 	 * A filename in UTF-8 was made with libarchive 2.x in a wrong
 	 * assumption that wchar_t was Unicode.
 	 * This option enables simulating the assumption in order to read
-	 * that filname correctly.
+	 * that filename correctly.
 	 */
 	case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
 #if (defined(_WIN32) && !defined(__CYGWIN__)) \
@@ -1939,12 +1945,19 @@
     struct archive_string_conv *sc)
 {
 	const void *s;
-	size_t length;
+	size_t length = 0;
 	int i, r = 0, r2;
 
+	if (_p != NULL && n > 0) {
+		if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))
+			length = utf16nbytes(_p, n);
+		else
+			length = mbsnbytes(_p, n);
+	}
+
 	/* We must allocate memory even if there is no data for conversion
 	 * or copy. This simulates archive_string_append behavior. */
-	if (_p == NULL || n == 0) {
+	if (length == 0) {
 		int tn = 1;
 		if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
 			tn = 2;
@@ -1960,16 +1973,11 @@
 	 * If sc is NULL, we just make a copy.
 	 */
 	if (sc == NULL) {
-		length = mbsnbytes(_p, n);
 		if (archive_string_append(as, _p, length) == NULL)
 			return (-1);/* No memory */
 		return (0);
 	}
 
-	if (sc->flag & SCONV_FROM_UTF16)
-		length = utf16nbytes(_p, n);
-	else
-		length = mbsnbytes(_p, n);
 	s = _p;
 	i = 0;
 	if (sc->nconverter > 1) {
@@ -2218,7 +2226,7 @@
 
 	/*
 	 * If a character is ASCII, this just copies it. If not, this
-	 * assigns '?' charater instead but in UTF-8 locale this assigns
+	 * assigns '?' character instead but in UTF-8 locale this assigns
 	 * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
 	 * a Replacement Character in Unicode.
 	 */
@@ -2552,7 +2560,7 @@
 
 	/*
 	 * Surrogate pair values(0xd800 through 0xdfff) are only
-	 * used by UTF-16, so, after above culculation, the code
+	 * used by UTF-16, so, after above calculation, the code
 	 * must not be surrogate values, and Unicode has no codes
 	 * larger than 0x10ffff. Thus, those are not legal Unicode
 	 * values.
@@ -2901,7 +2909,7 @@
 /*
  * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.
  *
- * TODO: Convert composition exclusions,which are never converted
+ * TODO: Convert composition exclusions, which are never converted
  * from NFC,NFD,NFKC and NFKD, to Form C.
  */
 static int
@@ -3435,7 +3443,7 @@
 		}
 
 		/*
-		 * As libarchie 2.x, translates the UTF-8 characters into
+		 * As libarchive 2.x, translates the UTF-8 characters into
 		 * wide-characters in the assumption that WCS is Unicode.
 		 */
 		if (n < 0) {
@@ -3945,7 +3953,7 @@
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	/*
-	 * Internationalization programing on Windows must use Wide
+	 * Internationalization programming on Windows must use Wide
 	 * characters because Windows platform cannot make locale UTF-8.
 	 */
 	if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {
@@ -4077,7 +4085,7 @@
 	archive_string_empty(&(aes->aes_utf8));
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	/*
-	 * Internationalization programing on Windows must use Wide
+	 * Internationalization programming on Windows must use Wide
 	 * characters because Windows platform cannot make locale UTF-8.
 	 */
 	if (sc == NULL) {
diff --git a/libarchive/archive_string.h b/libarchive/archive_string.h
index bac22e6..56dfbb2 100644
--- a/libarchive/archive_string.h
+++ b/libarchive/archive_string.h
@@ -81,6 +81,10 @@
 struct archive_wstring *
 archive_wstrappend_wchar(struct archive_wstring *, wchar_t);
 
+/* Append a raw array to an archive_string, resizing as necessary */
+struct archive_string *
+archive_array_append(struct archive_string *, const char *, size_t);
+
 /* Convert a Unicode string to current locale and append the result. */
 /* Returns -1 if conversion fails. */
 int
diff --git a/libarchive/archive_string_composition.h b/libarchive/archive_string_composition.h
index be41e33..8902ac1 100644
--- a/libarchive/archive_string_composition.h
+++ b/libarchive/archive_string_composition.h
@@ -1009,7 +1009,7 @@
 	(((uc) > 0x1D244)?0:\
 	ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F])
 
-/* The table of the value of Canonical Cimbining Class */
+/* The table of the value of Canonical Combining Class */
 static const unsigned char ccc_val[][16] = {
  /* idx=0: XXXX0 - XXXXF */
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c
index 8663bef..6ff8749 100644
--- a/libarchive/archive_windows.c
+++ b/libarchive/archive_windows.c
@@ -891,7 +891,7 @@
 		return;
 	}
 
-	for (i = 0; i < (int)sizeof(doserrors)/sizeof(doserrors[0]); i++)
+	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
 	{
 		if (doserrors[i].winerr == e)
 		{
diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h
index 8cf1c56..e77cd08 100644
--- a/libarchive/archive_windows.h
+++ b/libarchive/archive_windows.h
@@ -218,16 +218,20 @@
 #define	S_IWUSR	     _S_IWUSR
 #define	S_IRUSR	     _S_IRUSR
 #endif
+#ifndef S_IRWXG
 #define	S_IRWXG        _S_IRWXG
 #define	S_IXGRP        _S_IXGRP
 #define	S_IWGRP        _S_IWGRP
+#endif
 #ifndef S_IRGRP
 #define	S_IRGRP        _S_IRGRP
 #endif
+#ifndef S_IRWXO
 #define	S_IRWXO        _S_IRWXO
 #define	S_IXOTH        _S_IXOTH
 #define	S_IWOTH        _S_IWOTH
 #define	S_IROTH        _S_IROTH
+#endif
 
 #endif
 
diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c
index 0697db1..0634a22 100644
--- a/libarchive/archive_write.c
+++ b/libarchive/archive_write.c
@@ -231,7 +231,7 @@
 	if (length == 0)
 		return(ARCHIVE_OK);
 	if (f->write == NULL)
-		/* If unset, a fatal error has already ocuured, so this filter
+		/* If unset, a fatal error has already occurred, so this filter
 		 * didn't open. We cannot write anything. */
 		return(ARCHIVE_FATAL);
 	r = (f->write)(f, buff, length);
diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c
index 31a1b6f..55b5e8e 100644
--- a/libarchive/archive_write_add_filter_program.c
+++ b/libarchive/archive_write_add_filter_program.c
@@ -200,6 +200,7 @@
 		if (data->child)
 			CloseHandle(data->child);
 #endif
+		free(data->program_name);
 		free(data->child_buf);
 		free(data);
 	}
diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c
index 46a6c38..b0f25a6 100644
--- a/libarchive/archive_write_add_filter_xz.c
+++ b/libarchive/archive_write_add_filter_xz.c
@@ -233,7 +233,7 @@
 	if (f->code == ARCHIVE_FILTER_XZ) {
 #ifdef HAVE_LZMA_STREAM_ENCODER_MT
 		if (data->threads != 1) {
-			bzero(&mt_options, sizeof(mt_options));
+			memset(&mt_options, 0, sizeof(mt_options));
 			mt_options.threads = data->threads;
 			mt_options.timeout = 300;
 			mt_options.filters = data->lzmafilters;
diff --git a/libarchive/archive_write_data.3 b/libarchive/archive_write_data.3
index 0cdd25f..9c16cd9 100644
--- a/libarchive/archive_write_data.3
+++ b/libarchive/archive_write_data.3
@@ -24,11 +24,12 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd February 28, 2017
 .Dt ARCHIVE_WRITE_DATA 3
 .Os
 .Sh NAME
-.Nm archive_write_data
+.Nm archive_write_data ,
+.Nm archive_write_data_block
 .Nd functions for creating archives
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -36,8 +37,27 @@
 .In archive.h
 .Ft la_ssize_t
 .Fn archive_write_data "struct archive *" "const void *" "size_t"
+.Ft la_ssize_t
+.Fn archive_write_data_block "struct archive *" "const void *" "size_t size" "int64_t offset"
 .Sh DESCRIPTION
+.Bl -tag -width indent
+.It Fn archive_write_data
 Write data corresponding to the header just written.
+.It Fn archive_write_data_block
+Write data corresponding to the header just written.
+This is like
+.Fn archive_write_data
+except that it performs a seek on the file being
+written to the specified offset before writing the data.
+This is useful when restoring sparse files from archive
+formats that support sparse files.
+Returns number of bytes written or -1 on error.
+(Note: This is currently not supported for
+.Tn archive_write
+handles, only for
+.Tn archive_write_disk
+handles.
+.El
 .\" .Sh EXAMPLE
 .\"
 .Sh RETURN VALUES
diff --git a/libarchive/archive_write_disk.3 b/libarchive/archive_write_disk.3
index ba6c970..4891b85 100644
--- a/libarchive/archive_write_disk.3
+++ b/libarchive/archive_write_disk.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd February 28, 2017
 .Dt ARCHIVE_WRITE_DISK 3
 .Os
 .Sh NAME
@@ -33,14 +33,7 @@
 .Nm archive_write_disk_set_skip_file ,
 .Nm archive_write_disk_set_group_lookup ,
 .Nm archive_write_disk_set_standard_lookup ,
-.Nm archive_write_disk_set_user_lookup ,
-.Nm archive_write_header ,
-.Nm archive_write_data ,
-.Nm archive_write_data_block ,
-.Nm archive_write_finish_entry ,
-.Nm archive_write_close ,
-.Nm archive_write_finish
-.Nm archive_write_free
+.Nm archive_write_disk_set_user_lookup
 .Nd functions for creating objects on disk
 .Sh LIBRARY
 Streaming Archive Library (libarchive, -larchive)
@@ -68,20 +61,6 @@
 .Fa "uid_t (*)(void *, const char *uname, uid_t uid)"
 .Fa "void (*cleanup)(void *)"
 .Fc
-.Ft int
-.Fn archive_write_header "struct archive *" "struct archive_entry *"
-.Ft la_ssize_t
-.Fn archive_write_data "struct archive *" "const void *" "size_t"
-.Ft la_ssize_t
-.Fn archive_write_data_block "struct archive *" "const void *" "size_t size" "int64_t offset"
-.Ft int
-.Fn archive_write_finish_entry "struct archive *"
-.Ft int
-.Fn archive_write_close "struct archive *"
-.Ft int
-.Fn archive_write_finish "struct archive *"
-.Ft int
-.Fn archive_write_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for creating objects on
 disk from
@@ -223,60 +202,6 @@
 .Xr getpwnam 3
 and
 .Xr getgrnam 3 .
-.It Fn archive_write_header
-Build and write a header using the data in the provided
-.Tn struct archive_entry
-structure.
-See
-.Xr archive_entry 3
-for information on creating and populating
-.Tn struct archive_entry
-objects.
-.It Fn archive_write_data
-Write data corresponding to the header just written.
-Returns number of bytes written or -1 on error.
-.It Fn archive_write_data_block
-Write data corresponding to the header just written.
-This is like
-.Fn archive_write_data
-except that it performs a seek on the file being
-written to the specified offset before writing the data.
-This is useful when restoring sparse files from archive
-formats that support sparse files.
-Returns number of bytes written or -1 on error.
-(Note: This is currently not supported for
-.Tn archive_write
-handles, only for
-.Tn archive_write_disk
-handles.)
-.It Fn archive_write_finish_entry
-Close out the entry just written.
-Ordinarily, clients never need to call this, as it
-is called automatically by
-.Fn archive_write_next_header
-and
-.Fn archive_write_close
-as needed.
-However, some file attributes are written to disk only
-after the file is closed, so this can be necessary
-if you need to work with the file on disk right away.
-.It Fn archive_write_close
-Set any attributes that could not be set during the initial restore.
-For example, directory timestamps are not restored initially because
-restoring a subsequent file would alter that timestamp.
-Similarly, non-writable directories are initially created with
-write permissions (so that their contents can be restored).
-The
-.Nm
-library maintains a list of all such deferred attributes and
-sets them when this function is invoked.
-.It Fn archive_write_finish
-This is a deprecated synonym for
-.Fn archive_write_free .
-.It Fn archive_write_free
-Invokes
-.Fn archive_write_close
-if it was not invoked manually, then releases all resources.
 .El
 More information about the
 .Va struct archive
diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c
index e47384a..643f3c3 100644
--- a/libarchive/archive_write_disk_acl.c
+++ b/libarchive/archive_write_disk_acl.c
@@ -34,6 +34,9 @@
 #define _ACL_PRIVATE /* For debugging */
 #include <sys/acl.h>
 #endif
+#if HAVE_DARWIN_ACL
+#include <membership.h>
+#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -43,7 +46,7 @@
 #include "archive_acl_private.h"
 #include "archive_write_disk_private.h"
 
-#ifndef HAVE_POSIX_ACL
+#if !HAVE_POSIX_ACL && !HAVE_NFS4_ACL
 /* Default empty function body to satisfy mainline code. */
 int
 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
@@ -56,47 +59,117 @@
 	return (ARCHIVE_OK);
 }
 
-#else
+#else /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
+
+#if HAVE_DARWIN_ACL
+#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_EXTENDED
+#elif HAVE_FREEBSD_NFS4_ACL
+#define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_NFS4
+#endif
 
 static int	set_acl(struct archive *, int fd, const char *,
 			struct archive_acl *,
-			acl_type_t, int archive_entry_acl_type, const char *tn);
+#if !HAVE_SUN_ACL
+			acl_type_t,
+#endif
+			int archive_entry_acl_type, const char *tn);
 
-/*
- * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
- */
 int
 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
 	 struct archive_acl *abstract_acl)
 {
-	int		 ret;
+	int		ret = ARCHIVE_OK;
 
-	if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) > 0) {
-		ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS,
-		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
-		if (ret != ARCHIVE_OK)
-			return (ret);
-		ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT,
-		    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+#if !HAVE_DARWIN_ACL
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+#if HAVE_SUN_ACL
+		/* Solaris writes POSIX.1e access and default ACLs together */
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
+#else	/* HAVE_POSIX_ACL */
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			    "access");
+			if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+			    "default");
+#endif	/* !HAVE_SUN_ACL */
+		/* Simultaneous POSIX.1e and NFSv4 is not supported */
 		return (ret);
-#ifdef ACL_TYPE_NFS4
-	} else if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) {
-		ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_NFS4,
-		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
-		return (ret);
+	}
+#endif	/* !HAVE_DARWIN_ACL */
+#if HAVE_NFS4_ACL
+	if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+#if !HAVE_SUN_ACL
+		    ARCHIVE_PLATFORM_ACL_TYPE_NFS4,
 #endif
-	} else
-		return ARCHIVE_OK;
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#endif	/* HAVE_NFS4_ACL */
+	return (ret);
 }
 
-static struct {
-	int archive_perm;
-	int platform_perm;
+#if !HAVE_SUN_ACL || HAVE_SUN_NFS4_ACL
+/*
+ * Translate system ACL permissions into libarchive internal structure
+ */
+static const struct {
+	const int archive_perm;
+	const int platform_perm;
 } acl_perm_map[] = {
+#if HAVE_SUN_NFS4_ACL	/* Solaris NFSv4 ACL permissions */
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}
+#elif HAVE_DARWIN_ACL	/* MacOS ACL permissions */
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#endif
+#else	/* POSIX.1e ACL permissions */
 	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
 	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
 	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL	/* FreeBSD NFSv4 ACL permissions */
 	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
 	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
 	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
@@ -114,34 +187,77 @@
 	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
 	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
 #endif
+#endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
 };
+#endif	/* !HAVE_SUN_ACL || HAVE_SUN_NFS4_ACL */
 
-#ifdef ACL_TYPE_NFS4
-static struct {
-	int archive_inherit;
-	int platform_inherit;
+#if HAVE_NFS4_ACL
+/*
+ * Translate system NFSv4 inheritance flags into libarchive internal structure
+ */
+static const struct {
+	const int archive_inherit;
+	const int platform_inherit;
 } acl_inherit_map[] = {
+#if HAVE_SUN_NFS4_ACL	/* Solaris NFSv4 inheritance flags */
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},
+#ifdef ACE_INHERITED_ACE
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+#endif
+#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 inheritance flags */
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
+#else	/* FreeBSD NFSv4 ACL inheritance flags */
 	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
 	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
 	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
-	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
+#endif	/* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */
 };
-#endif
+#endif	/* HAVE_NFS4_ACL */
 
 static int
 set_acl(struct archive *a, int fd, const char *name,
     struct archive_acl *abstract_acl,
-    acl_type_t acl_type, int ae_requested_type, const char *tname)
+#if !HAVE_SUN_ACL
+    acl_type_t acl_type,
+#endif
+    int ae_requested_type, const char *tname)
 {
+#if HAVE_SUN_ACL
+	aclent_t	 *aclent;
+#if HAVE_SUN_NFS4_ACL
+	ace_t		 *ace;
+#endif
+	int		 cmd, e, r;
+	void		 *aclp;
+#else
 	acl_t		 acl;
 	acl_entry_t	 acl_entry;
 	acl_permset_t	 acl_permset;
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL
 	acl_flagset_t	 acl_flagset;
-	int		 r;
+#endif
+#endif	/* HAVE_SUN_ACL */
+#if HAVE_FREEBSD_NFS4_ACL
+	int		r;
 #endif
 	int		 ret;
 	int		 ae_type, ae_permset, ae_tag, ae_id;
+#if HAVE_DARWIN_ACL
+	uuid_t		ae_uuid;
+#endif
 	uid_t		 ae_uid;
 	gid_t		 ae_gid;
 	const char	*ae_name;
@@ -152,33 +268,184 @@
 	entries = archive_acl_reset(abstract_acl, ae_requested_type);
 	if (entries == 0)
 		return (ARCHIVE_OK);
+
+#if HAVE_SUN_ACL
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		cmd = SETACL;
+		aclp = malloc(entries * sizeof(aclent_t));
+		break;
+#if HAVE_SUN_NFS4_ACL
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		cmd = ACE_SETACL;
+		aclp = malloc(entries * sizeof(ace_t));
+		break;
+#endif
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Invalid ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (aclp == NULL) {
+		archive_set_error(a, errno,
+		    "Can't allocate memory for acl buffer");
+		return (ARCHIVE_FAILED);
+	}
+#else	/* !HAVE_SUN_ACL */
 	acl = acl_init(entries);
 	if (acl == (acl_t)NULL) {
 		archive_set_error(a, errno,
 		    "Failed to initialize ACL working storage");
 		return (ARCHIVE_FAILED);
 	}
+#endif	/* !HAVE_SUN_ACL */
+#if HAVE_SUN_ACL
+	e = 0;
+#endif
 	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
 		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+#if HAVE_SUN_ACL
+		aclent = NULL;
+#if HAVE_SUN_NFS4_ACL
+		ace = NULL;
+#endif
+		if (cmd == SETACL) {
+			aclent = &((aclent_t *)aclp)[e];
+			aclent->a_id = -1;
+			aclent->a_type = 0;
+			aclent->a_perm = 0;
+		}
+#if HAVE_SUN_NFS4_ACL
+		else {	/* cmd == ACE_SETACL */
+			ace = &((ace_t *)aclp)[e];
+			ace->a_who = -1;
+			ace->a_access_mask = 0;
+			ace->a_flags = 0;
+		}
+#endif	/* HAVE_SUN_NFS4_ACL */
+#else	/* !HAVE_SUN_ACL  */
+#if HAVE_DARWIN_ACL
+		/*
+		 * Mac OS doesn't support NFSv4 ACLs for
+		 * owner@, group@ and everyone@.
+		 * We skip any of these ACLs found.
+		 */
+		if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)
+			continue;
+#endif
 		if (acl_create_entry(&acl, &acl_entry) != 0) {
 			archive_set_error(a, errno,
 			    "Failed to create a new ACL entry");
 			ret = ARCHIVE_FAILED;
 			goto exit_free;
 		}
-
+#endif	/* !HAVE_SUN_ACL */
+#if HAVE_DARWIN_ACL
+		switch (ae_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);
+			break;
+		default:
+			/* We don't support any other types on MacOS */
+			continue;
+		}
+#endif
 		switch (ae_tag) {
+#if HAVE_SUN_ACL
 		case ARCHIVE_ENTRY_ACL_USER:
-			acl_set_tag_type(acl_entry, ACL_USER);
 			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
-			acl_set_qualifier(acl_entry, &ae_uid);
+			if (aclent != NULL) {
+				aclent->a_id = ae_uid;
+				aclent->a_type |= USER;
+			}
+#if HAVE_SUN_NFS4_ACL
+			else {
+				ace->a_who = ae_uid;
+			}
+#endif
 			break;
 		case ARCHIVE_ENTRY_ACL_GROUP:
-			acl_set_tag_type(acl_entry, ACL_GROUP);
 			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
-			acl_set_qualifier(acl_entry, &ae_gid);
+			if (aclent != NULL) {
+				aclent->a_id = ae_gid;
+				aclent->a_type |= GROUP;
+			}
+#if HAVE_SUN_NFS4_ACL
+			else {
+				ace->a_who = ae_gid;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+#endif
 			break;
 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= USER_OBJ;
+#if HAVE_SUN_NFS4_ACL
+			else {
+				ace->a_flags |= ACE_OWNER;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= GROUP_OBJ;
+#if HAVE_SUN_NFS4_ACL
+			else {
+				ace->a_flags |= ACE_GROUP;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			if (aclent != NULL)
+				aclent->a_type |= CLASS_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			if (aclent != NULL)
+				aclent->a_type |= OTHER_OBJ;
+			break;
+#if HAVE_SUN_NFS4_ACL
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			if (ace != NULL)
+				ace->a_flags |= ACE_EVERYONE;
+			break;
+#endif
+#else	/* !HAVE_SUN_ACL */
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
+			acl_set_tag_type(acl_entry, ACL_USER);
+			acl_set_qualifier(acl_entry, &ae_uid);
+#else	/* MacOS */
+			if (mbr_identifier_to_uuid(ID_TYPE_UID, &ae_uid,
+			    sizeof(uid_t), ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+#endif	/* HAVE_DARWIN_ACL */
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			acl_set_qualifier(acl_entry, &ae_gid);
+#else	/* MacOS */
+			if (mbr_identifier_to_uuid(ID_TYPE_GID, &ae_gid,
+			    sizeof(gid_t), ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+#endif	/* HAVE_DARWIN_ACL */
+			break;
+#if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
 			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
 			break;
 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
@@ -190,11 +457,13 @@
 		case ARCHIVE_ENTRY_ACL_OTHER:
 			acl_set_tag_type(acl_entry, ACL_OTHER);
 			break;
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL	/* FreeBSD only */
 		case ARCHIVE_ENTRY_ACL_EVERYONE:
 			acl_set_tag_type(acl_entry, ACL_EVERYONE);
 			break;
 #endif
+#endif	/* !HAVE_DARWIN_ACL */
+#endif	/* !HAVE_SUN_ACL */
 		default:
 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
 			    "Unknown ACL tag");
@@ -202,9 +471,47 @@
 			goto exit_free;
 		}
 
-#ifdef ACL_TYPE_NFS4
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_ACL
 		r = 0;
 		switch (ae_type) {
+#if HAVE_SUN_ACL
+#if HAVE_SUN_NFS4_ACL
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;
+			else
+				r = -1;
+			break;
+#endif
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+			if (aclent == NULL)
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			if (aclent != NULL)
+				aclent->a_type |= ACL_DEFAULT;
+			else
+				r = -1;
+			break;
+#else	/* !HAVE_SUN_ACL */
 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
 			r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW);
 			break;
@@ -221,20 +528,38 @@
 		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
 			// These don't translate directly into the system ACL.
 			break;
+#endif	/* !HAVE_SUN_ACL */
 		default:
 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			    "Unknown ACL entry type");
+			    "Unsupported ACL entry type");
 			ret = ARCHIVE_FAILED;
 			goto exit_free;
 		}
+
 		if (r != 0) {
+#if HAVE_SUN_ACL
+			errno = EINVAL;
+#endif
 			archive_set_error(a, errno,
 			    "Failed to set ACL entry type");
 			ret = ARCHIVE_FAILED;
 			goto exit_free;
 		}
-#endif
+#endif	/* HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_ACL */
 
+#if HAVE_SUN_ACL
+		if (aclent != NULL) {
+			if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
+				aclent->a_perm |= 1;
+			if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
+				aclent->a_perm |= 2;
+			if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
+				aclent->a_perm |= 4;
+		}
+#if HAVE_SUN_NFS4_ACL
+		else /* falls through to for statement below, ace != NULL */
+#endif
+#else
 		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
 			archive_set_error(a, errno,
 			    "Failed to get ACL permission set");
@@ -247,9 +572,14 @@
 			ret = ARCHIVE_FAILED;
 			goto exit_free;
 		}
-
+#endif	/* !HAVE_SUN_ACL */
+#if HAVE_POSIX_ACL || HAVE_NFS4_ACL
 		for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
-			if (ae_permset & acl_perm_map[i].archive_perm)
+			if (ae_permset & acl_perm_map[i].archive_perm) {
+#if HAVE_SUN_ACL
+				ace->a_access_mask |=
+				    acl_perm_map[i].platform_perm;
+#else
 				if (acl_add_perm(acl_permset,
 				    acl_perm_map[i].platform_perm) != 0) {
 					archive_set_error(a, errno,
@@ -257,10 +587,21 @@
 					ret = ARCHIVE_FAILED;
 					goto exit_free;
 				}
+#endif
+			}
 		}
+#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
 
-#ifdef ACL_TYPE_NFS4
-		if (acl_type == ACL_TYPE_NFS4) {
+#if HAVE_NFS4_ACL
+#if HAVE_SUN_NFS4_ACL
+		if (ace != NULL)
+#elif HAVE_DARWIN_ACL
+		if (acl_type == ACL_TYPE_EXTENDED)
+#else	/* FreeBSD */
+		if (acl_type == ACL_TYPE_NFS4)
+#endif
+		{
+#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL
 			/*
 			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
 			 */
@@ -276,8 +617,13 @@
 				ret = ARCHIVE_FAILED;
 				goto exit_free;
 			}
-			for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+#endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */
+			for (i = 0; i < (int)(sizeof(acl_inherit_map) /sizeof(acl_inherit_map[0])); ++i) {
 				if (ae_permset & acl_inherit_map[i].archive_inherit) {
+#if HAVE_SUN_ACL
+					ace->a_flags |=
+					    acl_inherit_map[i].platform_inherit;
+#else	/* !HAVE_SUN_ACL */
 					if (acl_add_flag_np(acl_flagset,
 							acl_inherit_map[i].platform_inherit) != 0) {
 						archive_set_error(a, errno,
@@ -285,38 +631,67 @@
 						ret = ARCHIVE_FAILED;
 						goto exit_free;
 					}
+#endif	/* HAVE_SUN_ACL */
 				}
 			}
 		}
+#endif	/* HAVE_NFS4_ACL */
+#if HAVE_SUN_ACL
+	e++;
 #endif
 	}
 
+#if HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL
 	/* Try restoring the ACL through 'fd' if we can. */
-#if HAVE_ACL_SET_FD
-	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
-		ret = ARCHIVE_OK;
-	else
-#else
-#if HAVE_ACL_SET_FD_NP
-	if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
-		ret = ARCHIVE_OK;
-	else
+#if HAVE_SUN_ACL || HAVE_ACL_SET_FD_NP
+	if (fd >= 0)
+#else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
+	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS)
 #endif
+	{
+#if HAVE_SUN_ACL
+		if (facl(fd, cmd, entries, aclp) == 0)
+#elif HAVE_ACL_SET_FD_NP
+		if (acl_set_fd_np(fd, acl, acl_type) == 0)
+#else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
+		if (acl_set_fd(fd, acl) == 0)
 #endif
-#if HAVE_ACL_SET_LINK_NP
-	  if (acl_set_link_np(name, acl_type, acl) != 0) {
-		archive_set_error(a, errno, "Failed to set %s acl", tname);
-		ret = ARCHIVE_WARN;
-	  }
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set %s acl on fd", tname);
+			}
+		}
+	} else
+#endif	/* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */
+#if HAVE_SUN_ACL
+	if (acl(name, cmd, entries, aclp) != 0)
+#elif HAVE_ACL_SET_LINK_NP
+	if (acl_set_link_np(name, acl_type, acl) != 0)
 #else
 	/* TODO: Skip this if 'name' is a symlink. */
-	if (acl_set_file(name, acl_type, acl) != 0) {
-		archive_set_error(a, errno, "Failed to set %s acl", tname);
-		ret = ARCHIVE_WARN;
-	}
+	if (acl_set_file(name, acl_type, acl) != 0)
 #endif
+	{
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set %s acl",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
 exit_free:
+#if HAVE_SUN_ACL
+	free(aclp);
+#else
 	acl_free(acl);
+#endif
 	return (ret);
 }
-#endif
+#endif	/* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index 5b41288..bf58b6d 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -110,6 +110,18 @@
 #include <sys/fcntl1.h>
 #endif
 
+/*
+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.
+ *
+ * It assumes that the input is an integer type of no more than 64 bits.
+ * If the number is less than zero, t must be a signed type, so it fits in
+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t
+ * without loss. But it could be a large unsigned value, so we have to clip it
+ * to INT64_MAX.*
+ */
+#define to_int64_time(t) \
+   ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))
+
 #if __APPLE__
 #include <TargetConditionals.h>
 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H
@@ -308,7 +320,7 @@
 #define	MAXIMUM_DIR_MODE 0775
 
 /*
- * Maxinum uncompressed size of a decmpfs block.
+ * Maximum uncompressed size of a decmpfs block.
  */
 #define MAX_DECMPFS_BLOCK_SIZE	(64 * 1024)
 /*
@@ -323,7 +335,7 @@
 #define RSRC_F_SIZE	50	/* Size of Resource fork footer. */
 /* Size to write compressed data to resource fork. */
 #define COMPRESSED_W_SIZE	(64 * 1024)
-/* decmpfs difinitions. */
+/* decmpfs definitions. */
 #define MAX_DECMPFS_XATTR_SIZE		3802
 #ifndef DECMPFS_XATTR_NAME
 #define DECMPFS_XATTR_NAME		"com.apple.decmpfs"
@@ -632,9 +644,9 @@
 		/*
 		 * NOTE: UF_COMPRESSED is ignored even if the filesystem
 		 * supports HFS+ Compression because the file should
-		 * have at least an extended attriute "com.apple.decmpfs"
+		 * have at least an extended attribute "com.apple.decmpfs"
 		 * before the flag is set to indicate that the file have
-		 * been compressed. If hte filesystem does not support
+		 * been compressed. If the filesystem does not support
 		 * HFS+ Compression the system call will fail.
 		 */
 		if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
@@ -1247,7 +1259,7 @@
 		ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
 		a->compressed_buffer_remaining = a->compressed_buffer_size;
 
-		/* If the compressed size is not enouph smaller than
+		/* If the compressed size is not enough smaller than
 		 * the uncompressed size. cancel HFS+ compression.
 		 * TODO: study a behavior of ditto utility and improve
 		 * the condition to fall back into no HFS+ compression. */
@@ -1352,7 +1364,7 @@
 		    (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
 		/* Set the block count to the resource fork. */
 		archive_le32enc(a->decmpfs_block_info++, block_count);
-		/* Get the position where we are goint to set compressed
+		/* Get the position where we are going to set compressed
 		 * data. */
 		a->compressed_rsrc_position =
 		    RSRC_H_SIZE + 4 + (block_count * 8);
@@ -1425,7 +1437,7 @@
 		bytes_to_write = size;
 		/* Seek if necessary to the specified offset. */
 		if (a->offset < a->fd_offset) {
-			/* Can't support backword move. */
+			/* Can't support backward move. */
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Seek failed");
 			return (ARCHIVE_FATAL);
@@ -1690,10 +1702,26 @@
 	 * ACLs that prevent attribute changes (including time).
 	 */
 	if (a->todo & TODO_ACLS) {
-		int r2 = archive_write_disk_set_acls(&a->archive, a->fd,
-				  archive_entry_pathname(a->entry),
-				  archive_entry_acl(a->entry));
+		int r2;
+#ifdef HAVE_DARWIN_ACL
+		/*
+		 * On Mac OS, platform ACLs are stored also in mac_metadata by
+		 * the operating system. If mac_metadata is present it takes
+		 * precedence and we skip extracting libarchive NFSv4 ACLs
+		 */
+		const void *metadata;
+		size_t metadata_size;
+		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+		if ((a->todo & TODO_MAC_METADATA) == 0 ||
+		    metadata == NULL || metadata_size == 0) {
+#endif
+		r2 = archive_write_disk_set_acls(&a->archive, a->fd,
+		    archive_entry_pathname(a->entry),
+		    archive_entry_acl(a->entry));
 		if (r2 < ret) ret = r2;
+#ifdef HAVE_DARWIN_ACL
+		}
+#endif
 	}
 
 finish_metadata:
@@ -2040,6 +2068,7 @@
 	int r;
 	/* these for check_symlinks_fsobj */
 	char *linkname_copy;	/* non-const copy of linkname */
+	struct stat st;
 	struct archive_string error_string;
 	int error_number;
 
@@ -2065,6 +2094,7 @@
 			archive_set_error(&a->archive, error_number, "%s",
 			    error_string.s);
 			free(linkname_copy);
+			archive_string_free(&error_string);
 			/*
 			 * EPERM is more appropriate than error_number for our
 			 * callers
@@ -2077,6 +2107,7 @@
 			archive_set_error(&a->archive, error_number, "%s",
 			    error_string.s);
 			free(linkname_copy);
+			archive_string_free(&error_string);
 			/*
 			 * EPERM is more appropriate than error_number for our
 			 * callers
@@ -2084,6 +2115,7 @@
 			return (EPERM);
 		}
 		free(linkname_copy);
+		archive_string_free(&error_string);
 		r = link(linkname, a->name) ? errno : 0;
 		/*
 		 * New cpio and pax formats allow hardlink entries
@@ -2101,11 +2133,20 @@
 			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);
-			__archive_ensure_cloexec_flag(a->fd);
-			if (a->fd < 0)
+#ifdef HAVE_LSTAT
+			r = lstat(a->name, &st);
+#else
+			r = stat(a->name, &st);
+#endif
+			if (r != 0)
 				r = errno;
+			else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+				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;
+			}
 		}
 		return (r);
 #endif
@@ -2252,8 +2293,13 @@
 		if (p->fixup & TODO_MODE_BASE)
 			chmod(p->name, p->mode);
 		if (p->fixup & TODO_ACLS)
-			archive_write_disk_set_acls(&a->archive,
-						    -1, p->name, &p->acl);
+#ifdef HAVE_DARWIN_ACL
+			if ((p->fixup & TODO_MAC_METADATA) == 0 ||
+			    p->mac_metadata == NULL ||
+			    p->mac_metadata_size == 0)
+#endif
+				archive_write_disk_set_acls(&a->archive,
+				    -1, p->name, &p->acl);
 		if (p->fixup & TODO_FFLAGS)
 			set_fflags_platform(a, -1, p->name,
 			    p->mode, p->fflags_set, 0);
@@ -2421,7 +2467,7 @@
 	if (a_eno)
 		*a_eno = err;
 	if (a_estr)
-		archive_string_sprintf(a_estr, errstr, path);
+		archive_string_sprintf(a_estr, "%s%s", errstr, path);
 }
 
 /*
@@ -2527,7 +2573,7 @@
 				 * with the deep-directory editing.
 				 */
 				fsobj_error(a_eno, a_estr, errno,
-				    "Could not stat %s", path);
+				    "Could not stat ", path);
 				res = ARCHIVE_FAILED;
 				break;
 			}
@@ -2536,7 +2582,7 @@
 				if (chdir(head) != 0) {
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, errno,
-					    "Could not chdir %s", path);
+					    "Could not chdir ", path);
 					res = (ARCHIVE_FATAL);
 					break;
 				}
@@ -2553,7 +2599,7 @@
 				if (unlink(head)) {
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, errno,
-					    "Could not remove symlink %s",
+					    "Could not remove symlink ",
 					    path);
 					res = ARCHIVE_FAILED;
 					break;
@@ -2572,7 +2618,7 @@
 				/*
 				if (!S_ISLNK(path)) {
 					fsobj_error(a_eno, a_estr, 0,
-					    "Removing symlink %s", path);
+					    "Removing symlink ", path);
 				}
 				*/
 				/* Symlink gone.  No more problem! */
@@ -2584,7 +2630,7 @@
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, 0,
 					    "Cannot remove intervening "
-					    "symlink %s", path);
+					    "symlink ", path);
 					res = ARCHIVE_FAILED;
 					break;
 				}
@@ -2606,7 +2652,7 @@
 					} else {
 						fsobj_error(a_eno, a_estr,
 						    errno,
-						    "Could not stat %s", path);
+						    "Could not stat ", path);
 						res = (ARCHIVE_FAILED);
 						break;
 					}
@@ -2615,7 +2661,7 @@
 						tail[0] = c;
 						fsobj_error(a_eno, a_estr,
 						    errno,
-						    "Could not chdir %s", path);
+						    "Could not chdir ", path);
 						res = (ARCHIVE_FATAL);
 						break;
 					}
@@ -2628,14 +2674,14 @@
 					tail[0] = c;
 					fsobj_error(a_eno, a_estr, 0,
 					    "Cannot extract through "
-					    "symlink %s", path);
+					    "symlink ", path);
 					res = ARCHIVE_FAILED;
 					break;
 				}
 			} else {
 				tail[0] = c;
 				fsobj_error(a_eno, a_estr, 0,
-				    "Cannot extract through symlink %s", path);
+				    "Cannot extract through symlink ", path);
 				res = ARCHIVE_FAILED;
 				break;
 			}
@@ -3421,12 +3467,19 @@
 #ifdef UF_APPEND
 	critical_flags |= UF_APPEND;
 #endif
-#ifdef EXT2_APPEND_FL
+#if defined(FS_APPEND_FL)
+	critical_flags |= FS_APPEND_FL;
+#elif defined(EXT2_APPEND_FL)
 	critical_flags |= EXT2_APPEND_FL;
 #endif
-#ifdef EXT2_IMMUTABLE_FL
+#if defined(FS_IMMUTABLE_FL)
+	critical_flags |= FS_IMMUTABLE_FL;
+#elif defined(EXT2_IMMUTABLE_FL)
 	critical_flags |= EXT2_IMMUTABLE_FL;
 #endif
+#ifdef FS_JOURNAL_DATA_FL
+	critical_flags |= FS_JOURNAL_DATA_FL;
+#endif
 
 	if (a->todo & TODO_FFLAGS) {
 		archive_entry_fflags(a->entry, &set, &clear);
@@ -3538,7 +3591,10 @@
 	return (ARCHIVE_WARN);
 }
 
-#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \
+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \
+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
 /*
  * Linux uses ioctl() to read and write file flags.
  */
@@ -3551,7 +3607,7 @@
 	int newflags, oldflags;
 	int sf_mask = 0;
 
-	if (set == 0  && clear == 0)
+	if (set == 0 && clear == 0)
 		return (ARCHIVE_OK);
 	/* Only regular files and dirs can have flags. */
 	if (!S_ISREG(mode) && !S_ISDIR(mode))
@@ -3572,12 +3628,19 @@
 	 * defines. (?)  The code below degrades reasonably gracefully
 	 * if sf_mask is incomplete.
 	 */
-#ifdef EXT2_IMMUTABLE_FL
+#if defined(FS_IMMUTABLE_FL)
+	sf_mask |= FS_IMMUTABLE_FL;
+#elif defined(EXT2_IMMUTABLE_FL)
 	sf_mask |= EXT2_IMMUTABLE_FL;
 #endif
-#ifdef EXT2_APPEND_FL
+#if defined(FS_APPEND_FL)
+	sf_mask |= FS_APPEND_FL;
+#elif defined(EXT2_APPEND_FL)
 	sf_mask |= EXT2_APPEND_FL;
 #endif
+#if defined(FS_JOURNAL_DATA_FL)
+	sf_mask |= FS_JOURNAL_DATA_FL;
+#endif
 	/*
 	 * XXX As above, this would be way simpler if we didn't have
 	 * to read the current flags from disk. XXX
@@ -3585,12 +3648,24 @@
 	ret = ARCHIVE_OK;
 
 	/* Read the current file flags. */
-	if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) < 0)
+	if (ioctl(myfd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &oldflags) < 0)
 		goto fail;
 
 	/* Try setting the flags as given. */
 	newflags = (oldflags & ~clear) | set;
-	if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+	if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	    &newflags) >= 0)
 		goto cleanup;
 	if (errno != EPERM)
 		goto fail;
@@ -3599,7 +3674,13 @@
 	newflags &= ~sf_mask;
 	oldflags &= sf_mask;
 	newflags |= oldflags;
-	if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+	if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	    &newflags) >= 0)
 		goto cleanup;
 
 	/* We couldn't set the flags, so report the failure. */
@@ -4125,10 +4206,10 @@
 {
 	/* First, test the seconds and return if we have a definite answer. */
 	/* Definitely older. */
-	if (st->st_mtime < archive_entry_mtime(entry))
+	if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
 		return (1);
 	/* Definitely younger. */
-	if (st->st_mtime > archive_entry_mtime(entry))
+	if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
 		return (0);
 	/* If this platform supports fractional seconds, try those. */
 #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
diff --git a/libarchive/archive_write_finish_entry.3 b/libarchive/archive_write_finish_entry.3
index c5ef69e..dc1b94b 100644
--- a/libarchive/archive_write_finish_entry.3
+++ b/libarchive/archive_write_finish_entry.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd February 28, 2017
 .Dt ARCHIVE_WRITE_FINISH_ENTRY 3
 .Os
 .Sh NAME
@@ -45,6 +45,9 @@
 and
 .Fn archive_write_close
 as needed.
+For
+.Tn archive_write_disk
+handles, this flushes pending file attribute changes like modification time.
 .\" .Sh EXAMPLE
 .Sh RETURN VALUES
 This function returns
diff --git a/libarchive/archive_write_format.3 b/libarchive/archive_write_format.3
index d4ba6ab..aaafb0a 100644
--- a/libarchive/archive_write_format.3
+++ b/libarchive/archive_write_format.3
@@ -108,7 +108,6 @@
 These functions set the format that will be used for the archive.
 .Pp
 The library can write a variety of common archive formats.
-
 .Bl -tag -width indent
 .It Fn archive_write_set_format
 Sets the format based on the format code (see
diff --git a/libarchive/archive_write_open.3 b/libarchive/archive_write_open.3
index a52959b..457873e 100644
--- a/libarchive/archive_write_open.3
+++ b/libarchive/archive_write_open.3
@@ -66,6 +66,7 @@
 This is the most generic form of this function, which accepts
 pointers to three callback functions which will be invoked by
 the compression layer to write the constructed archive.
+This does not alter the default archive padding.
 .It Fn archive_write_open_fd
 A convenience form of
 .Fn archive_write_open
@@ -123,12 +124,21 @@
 You should be careful to ensure that this variable
 remains allocated until after the archive is
 closed.
+This function will disable padding unless you
+have specifically set the block size.
 .El
 More information about the
 .Va struct archive
 object and the overall design of the library can be found in the
 .Xr libarchive 3
 overview.
+.Pp
+Note that the convenience forms above vary in how
+they block the output.
+See
+.Xr archive_write_blocksize 3
+if you need to control the block size used for writes
+or the end-of-file padding behavior.
 .\"
 .Sh CLIENT CALLBACKS
 To use this library, you will need to define and register
@@ -226,6 +236,7 @@
 .Xr tar 1 ,
 .Xr libarchive 3 ,
 .Xr archive_write 3 ,
+.Xr archive_write_blocksize 3 ,
 .Xr archive_write_filter 3 ,
 .Xr archive_write_format 3 ,
 .Xr archive_write_new 3 ,
diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c
index abd521a..41ed74d 100644
--- a/libarchive/archive_write_set_format_7zip.c
+++ b/libarchive/archive_write_set_format_7zip.c
@@ -1358,7 +1358,7 @@
 	if (r < 0)
 		return (r);
 
-	/* Write Nume size. */
+	/* Write Name size. */
 	r = enc_uint64(a, zip->total_bytes_entry_name+1);
 	if (r < 0)
 		return (r);
diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c
index a9c4123..2d858c9 100644
--- a/libarchive/archive_write_set_format_gnutar.c
+++ b/libarchive/archive_write_set_format_gnutar.c
@@ -478,15 +478,15 @@
 		archive_entry_set_pathname(temp, "././@LongLink");
 		archive_entry_set_size(temp, length);
 		ret = archive_format_gnutar_header(a, buff, temp, 'K');
+		archive_entry_free(temp);
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
 		ret = __archive_write_output(a, buff, 512);
-		if(ret < ARCHIVE_WARN)
+		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
-		archive_entry_free(temp);
 		/* Write name and trailing null byte. */
 		ret = __archive_write_output(a, gnutar->linkname, length);
-		if(ret < ARCHIVE_WARN)
+		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
 		/* Pad to 512 bytes */
 		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
@@ -508,12 +508,12 @@
 		archive_entry_set_pathname(temp, "././@LongLink");
 		archive_entry_set_size(temp, length);
 		ret = archive_format_gnutar_header(a, buff, temp, 'L');
+		archive_entry_free(temp);
 		if (ret < ARCHIVE_WARN)
 			goto exit_write_header;
 		ret = __archive_write_output(a, buff, 512);
 		if(ret < ARCHIVE_WARN)
 			goto exit_write_header;
-		archive_entry_free(temp);
 		/* Write pathname + trailing null byte. */
 		ret = __archive_write_output(a, pathname, length);
 		if(ret < ARCHIVE_WARN)
diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c
index 591105e..c0ca435 100644
--- a/libarchive/archive_write_set_format_iso9660.c
+++ b/libarchive/archive_write_set_format_iso9660.c
@@ -161,7 +161,7 @@
 	/* Used for managing struct isofile list. */
 	struct isofile		*allnext;
 	struct isofile		*datanext;
-	/* Used for managing a hardlined struct isofile list. */
+	/* Used for managing a hardlinked struct isofile list. */
 	struct isofile		*hlnext;
 	struct isofile		*hardlink_target;
 
@@ -528,7 +528,7 @@
 	 *   - allow more then 8 depths of directory trees;
 	 *   - disable a version number to a File Name;
 	 *   - disable a forced period to the tail of a File Name;
-	 *   - the maxinum length of files and directories is raised to 193.
+	 *   - the maximum length of files and directories is raised to 193.
 	 *     if rockridge option is disabled, raised to 207.
 	 */
 	unsigned int	 iso_level:3;
@@ -626,7 +626,7 @@
 	 *        :    NOTE  Our rockridge=useful option does not set a zero
 	 *        :          to uid and gid, you should use application
 	 *        :          option such as --gid,--gname,--uid and --uname
-	 *        :          badtar options instead.
+	 *        :          bsdtar options instead.
 	 * Type   : boolean/string
 	 * Default: Enabled as rockridge=useful
 	 * COMPAT : mkisofs -r / -R
@@ -660,7 +660,7 @@
 	 *        :    for making zisofs.
 	 *        :    When the file size is less than one Logical Block
 	 *        :    size, that file will not zisofs'ed since it does
-	 *        :    reduece an ISO-image size.
+	 *        :    reduce an ISO-image size.
 	 *        :
 	 *        :    When you specify option 'boot=<boot-image>', that
 	 *        :    'boot-image' file won't be converted to zisofs file.
@@ -680,7 +680,7 @@
 	/* The creation time of ISO image. */
 	time_t			 birth_time;
 	/* A file stream of a temporary file, which file contents
-	 * save to until ISO iamge can be created. */
+	 * save to until ISO image can be created. */
 	int			 temp_fd;
 
 	struct isofile		*cur_file;
@@ -703,7 +703,7 @@
 	}			 all_file_list;
 
 	/* A list of struct isofile entries which have its
-	 * contents and are not a directory, a hardlined file
+	 * contents and are not a directory, a hardlinked file
 	 * and a symlink file. */
 	struct {
 		struct isofile	*first;
@@ -1907,9 +1907,9 @@
 		    iso9660->primary.rootent);
 		if (ret < 0)
 			return (ret);
-		/* Make sure we have UTF-16BE convertors.
-		 * if there is no file entry, convertors are still
-		 * uninitilized. */
+		/* Make sure we have UTF-16BE converters.
+		 * if there is no file entry, converters are still
+		 * uninitialized. */
 		if (iso9660->sconv_to_utf16be == NULL) {
 			iso9660->sconv_to_utf16be =
 			    archive_string_conversion_to_charset(
@@ -1995,7 +1995,7 @@
 	 * Write an ISO 9660 image.
 	 */
 
-	/* Switc to start using wbuff as file buffer. */
+	/* Switch to start using wbuff as file buffer. */
 	iso9660->wbuff_remaining = wb_buffmax();
 	iso9660->wbuff_type = WB_TO_STREAM;
 	iso9660->wbuff_offset = 0;
@@ -2524,7 +2524,8 @@
 	tzset();
 	localtime_r(t, tm);
 #elif HAVE__LOCALTIME64_S
-	_localtime64_s(tm, t);
+	__time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits
+	_localtime64_s(tm, &tmp_t);
 #else
 	memcpy(tm, localtime(t), sizeof(*tm));
 #endif
@@ -2553,7 +2554,7 @@
 static void
 set_date_time_null(unsigned char *p)
 {
-	memset(p, '0', 16);
+	memset(p, (int)'0', 16);
 	p[16] = 0;
 }
 
@@ -2959,7 +2960,7 @@
 			gid = archive_entry_gid(file->entry);
 			if (iso9660->opt.rr == OPT_RR_USEFUL) {
 				/*
-				 * This action is simular mkisofs -r option
+				 * This action is similar to mkisofs -r option
 				 * but our rockridge=useful option does not
 				 * set a zero to uid and gid.
 				 */
@@ -3108,7 +3109,7 @@
 					/*
 					 *     flg  len
 					 *    +----+----+
-					 *    | 02 | 00 | CURREENT component.
+					 *    | 02 | 00 | CURRENT component.
 					 *    +----+----+ (".")
 					 */
 					if (nc != NULL) {
@@ -3947,7 +3948,7 @@
 	    "Abstract File", 0, D_CHAR);
 	if (r != ARCHIVE_OK)
 		return (r);
-	/* Bibliongraphic File Identifier */
+	/* Bibliographic File Identifier */
 	r = set_file_identifier(bp, 777, 813, vdc, a, vdd,
 	    &(iso9660->bibliographic_file_identifier),
 	    "Bibliongraphic File", 0, D_CHAR);
@@ -4073,7 +4074,10 @@
 	memset(info.s, 0, info_size);
 	opt = 0;
 #if defined(HAVE__CTIME64_S)
-	_ctime64_s(buf, sizeof(buf), &(iso9660->birth_time));
+	{
+		__time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits
+		_ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp));
+	}
 #elif defined(HAVE_CTIME_R)
 	ctime_r(&(iso9660->birth_time), buf);
 #else
@@ -4558,7 +4562,7 @@
 		file->cur_content = &(file->content);
 		do {
 			blocks += file->cur_content->blocks;
-			/* Next fragument */
+			/* Next fragment */
 			file->cur_content = file->cur_content->next;
 		} while (file->cur_content != NULL);
 	}
@@ -4748,7 +4752,7 @@
 		}
 
 		/*
-		 * Converte a filename to UTF-16BE.
+		 * Convert a filename to UTF-16BE.
 		 */
 		if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,
 		    iso9660->sconv_to_utf16be)) {
@@ -5512,7 +5516,7 @@
 			file->cur_content->location = location;
 			location += file->cur_content->blocks;
 			total_block += file->cur_content->blocks;
-			/* Next fragument */
+			/* Next fragment */
 			file->cur_content = file->cur_content->next;
 		} while (file->cur_content != NULL);
 	}
@@ -6135,7 +6139,7 @@
 					off = ffmax - extlen;
 					if (off == 0) {
 						/* A dot('.')  character
-						 * does't place to the first
+						 * doesn't place to the first
 						 * byte of identifier. */
 						off ++;
 						extlen --;
@@ -6164,7 +6168,7 @@
 		np->id_len = l = ext_off + np->ext_len;
 
 		/* Make an offset of the number which is used to be set
-		 * hexadecimal number to avoid duplicate identififier. */
+		 * hexadecimal number to avoid duplicate identifier. */
 		if (iso9660->opt.iso_level == 1) {
 			if (ext_off >= 5)
 				noff = 5;
@@ -6742,7 +6746,7 @@
 	int r;
 
 	pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);
-	/* Theare aren't level 8 directories reaching a deepr level. */
+	/* There aren't level 8 directories reaching a deeper level. */
 	if (pt->cnt == 0)
 		return (ARCHIVE_OK);
 
@@ -6813,7 +6817,7 @@
 	if (cmp != 0)
 		return (cmp);
 
-	/* Compare indetifier */
+	/* Compare identifier */
 	s1 = p1->identifier;
 	s2 = p2->identifier;
 	l = p1->ext_off;
@@ -6855,7 +6859,7 @@
 	if (cmp != 0)
 		return (cmp);
 
-	/* Compare indetifier */
+	/* Compare identifier */
 	s1 = (const unsigned char *)p1->identifier;
 	s2 = (const unsigned char *)p2->identifier;
 	l = p1->ext_off;
@@ -7149,7 +7153,7 @@
 
 	iso9660->el_torito.catalog = isoent;
 	/*
-	 * Get a boot medai type.
+	 * Get a boot media type.
 	 */
 	switch (iso9660->opt.boot_type) {
 	default:
diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c
index 386d745..6a301ac 100644
--- a/libarchive/archive_write_set_format_pax.c
+++ b/libarchive/archive_write_set_format_pax.c
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -61,15 +62,24 @@
 	struct sparse_block	*sparse_tail;
 	struct archive_string_conv *sconv_utf8;
 	int			 opt_binary;
+
+	unsigned flags;
+#define WRITE_SCHILY_XATTR       (1 << 0)
+#define WRITE_LIBARCHIVE_XATTR   (1 << 1)
 };
 
 static void		 add_pax_attr(struct archive_string *, const char *key,
 			     const char *value);
+static void		 add_pax_attr_binary(struct archive_string *,
+			     const char *key,
+			     const char *value, size_t value_len);
 static void		 add_pax_attr_int(struct archive_string *,
 			     const char *key, int64_t value);
 static void		 add_pax_attr_time(struct archive_string *,
 			     const char *key, int64_t sec,
 			     unsigned long nanos);
+static int		 add_pax_acl(struct archive_write *,
+			    struct archive_entry *, struct pax *, int);
 static ssize_t		 archive_write_pax_data(struct archive_write *,
 			     const void *, size_t);
 static int		 archive_write_pax_close(struct archive_write *);
@@ -133,6 +143,8 @@
 		    "Can't allocate pax data");
 		return (ARCHIVE_FATAL);
 	}
+	pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+
 	a->format_data = pax;
 	a->format_name = "pax";
 	a->format_options = archive_write_pax_options;
@@ -272,6 +284,17 @@
 static void
 add_pax_attr(struct archive_string *as, const char *key, const char *value)
 {
+	add_pax_attr_binary(as, key, value, strlen(value));
+}
+
+/*
+ * Add a key/value attribute to the pax header.  This function handles
+ * binary values.
+ */
+static void
+add_pax_attr_binary(struct archive_string *as, const char *key,
+		    const char *value, size_t value_len)
+{
 	int digits, i, len, next_ten;
 	char tmp[1 + 3 * sizeof(int)];	/* < 3 base-10 digits per byte */
 
@@ -279,7 +302,7 @@
 	 * PAX attributes have the following layout:
 	 *     <len> <space> <key> <=> <value> <nl>
 	 */
-	len = 1 + (int)strlen(key) + 1 + (int)strlen(value) + 1;
+	len = 1 + (int)strlen(key) + 1 + (int)value_len + 1;
 
 	/*
 	 * The <len> field includes the length of the <len> field, so
@@ -310,21 +333,47 @@
 	archive_strappend_char(as, ' ');
 	archive_strcat(as, key);
 	archive_strappend_char(as, '=');
-	archive_strcat(as, value);
+	archive_array_append(as, value, value_len);
 	archive_strappend_char(as, '\n');
 }
 
+static void
+archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
+    const void *value, size_t value_len)
+{
+	struct archive_string s;
+	char *encoded_value;
+
+	if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+		encoded_value = base64_encode((const char *)value, value_len);
+
+		if (encoded_name != NULL && encoded_value != NULL) {
+			archive_string_init(&s);
+			archive_strcpy(&s, "LIBARCHIVE.xattr.");
+			archive_strcat(&s, encoded_name);
+			add_pax_attr(&(pax->pax_header), s.s, encoded_value);
+			archive_string_free(&s);
+		}
+		free(encoded_value);
+	}
+	if (pax->flags & WRITE_SCHILY_XATTR) {
+		archive_string_init(&s);
+		archive_strcpy(&s, "SCHILY.xattr.");
+		archive_strcat(&s, encoded_name);
+		add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len);
+		archive_string_free(&s);
+	}
+}
+
 static int
 archive_write_pax_header_xattrs(struct archive_write *a,
     struct pax *pax, struct archive_entry *entry)
 {
-	struct archive_string s;
 	int i = archive_entry_xattr_reset(entry);
 
 	while (i--) {
 		const char *name;
 		const void *value;
-		char *encoded_value;
 		char *url_encoded_name = NULL, *encoded_name = NULL;
 		size_t size;
 		int r;
@@ -345,16 +394,9 @@
 			}
 		}
 
-		encoded_value = base64_encode((const char *)value, size);
+		archive_write_pax_header_xattr(pax, encoded_name,
+		    value, size);
 
-		if (encoded_name != NULL && encoded_value != NULL) {
-			archive_string_init(&s);
-			archive_strcpy(&s, "LIBARCHIVE.xattr.");
-			archive_strcat(&s, encoded_name);
-			add_pax_attr(&(pax->pax_header), s.s, encoded_value);
-			archive_string_free(&s);
-		}
-		free(encoded_value);
 	}
 	return (ARCHIVE_OK);
 }
@@ -449,6 +491,45 @@
 	return (ARCHIVE_OK);
 }
 
+/* Add ACL to pax header */
+static int
+add_pax_acl(struct archive_write *a,
+    struct archive_entry *entry, struct pax *pax, int flags)
+{
+	char *p;
+	const char *attr;
+	int acl_types;
+
+	acl_types = archive_entry_acl_types(entry);
+
+	if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+		attr = "SCHILY.acl.ace";
+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+		attr = "SCHILY.acl.access";
+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+		attr = "SCHILY.acl.default";
+	else
+		return (ARCHIVE_FATAL);
+
+	p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);
+	if (p == NULL) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM, "%s %s",
+			    "Can't allocate memory for ", attr);
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",
+		    "Can't translate ", attr, " to UTF-8");
+		return(ARCHIVE_WARN);
+	} else if (*p != '\0') {
+		add_pax_attr(&(pax->pax_header),
+		    attr, p);
+		free(p);
+	}
+	return(ARCHIVE_OK);
+}
+
 /*
  * TODO: Consider adding 'comment' and 'charset' fields to
  * archive_entry so that clients can specify them.  Also, consider
@@ -465,6 +546,7 @@
 	const char *p;
 	const char *suffix;
 	int need_extension, r, ret;
+	int acl_types;
 	int sparse_count;
 	uint64_t sparse_total, real_size;
 	struct pax *pax;
@@ -1016,16 +1098,6 @@
 	if (!need_extension && p != NULL  &&  *p != '\0')
 		need_extension = 1;
 
-	/* If there are non-trivial ACL entries, we need an extension. */
-	if (!need_extension && archive_entry_acl_count(entry_original,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0)
-		need_extension = 1;
-
-	/* If there are non-trivial ACL entries, we need an extension. */
-	if (!need_extension && archive_entry_acl_count(entry_original,
-		ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0)
-		need_extension = 1;
-
 	/* If there are extended attributes, we need an extension */
 	if (!need_extension && archive_entry_xattr_count(entry_original) > 0)
 		need_extension = 1;
@@ -1034,6 +1106,12 @@
 	if (!need_extension && sparse_count > 0)
 		need_extension = 1;
 
+	acl_types = archive_entry_acl_types(entry_original);
+
+	/* If there are any ACL entries, we need an extension */
+	if (!need_extension && acl_types != 0)
+		need_extension = 1;
+
 	/*
 	 * Libarchive used to include these in extended headers for
 	 * restricted pax format, but that confused people who
@@ -1085,43 +1163,29 @@
 			add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
 
 		/* I use star-compatible ACL attributes. */
-		r = archive_entry_acl_text_l(entry_original,
-		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
-		    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID,
-		    &p, NULL, pax->sconv_utf8);
-		if (r != 0) {
-			if (errno == ENOMEM) {
-				archive_set_error(&a->archive, ENOMEM,
-				    "Can't allocate memory for "
-				    "ACL.access");
+		if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA |
+			    ARCHIVE_ENTRY_ACL_STYLE_COMPACT);
+			if (ret == ARCHIVE_FATAL)
 				return (ARCHIVE_FATAL);
-			}
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Can't translate ACL.access to UTF-8");
-			ret = ARCHIVE_WARN;
-		} else if (p != NULL && *p != '\0') {
-			add_pax_attr(&(pax->pax_header),
-			    "SCHILY.acl.access", p);
 		}
-		r = archive_entry_acl_text_l(entry_original,
-		    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
-		    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID,
-		    &p, NULL, pax->sconv_utf8);
-		if (r != 0) {
-			if (errno == ENOMEM) {
-				archive_set_error(&a->archive, ENOMEM,
-				    "Can't allocate memory for "
-				    "ACL.default");
+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+			if (ret == ARCHIVE_FATAL)
 				return (ARCHIVE_FATAL);
-			}
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Can't translate ACL.default to UTF-8");
-			ret = ARCHIVE_WARN;
-		} else if (p != NULL && *p != '\0') {
-			add_pax_attr(&(pax->pax_header),
-			    "SCHILY.acl.default", p);
+		}
+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+			if (ret == ARCHIVE_FATAL)
+				return (ARCHIVE_FATAL);
 		}
 
 		/* We use GNU-tar-compatible sparse attributes. */
diff --git a/libarchive/archive_write_set_format_warc.c b/libarchive/archive_write_set_format_warc.c
index ea66929..8b6daf9 100644
--- a/libarchive/archive_write_set_format_warc.c
+++ b/libarchive/archive_write_set_format_warc.c
@@ -79,7 +79,7 @@
 	WT_RVIS,
 	/* conversion, unsupported */
 	WT_CONV,
-	/* continutation, unsupported at the moment */
+	/* continuation, unsupported at the moment */
 	WT_CONT,
 	/* invalid type */
 	LAST_WT
diff --git a/libarchive/archive_write_set_format_xar.c b/libarchive/archive_write_set_format_xar.c
index da6daac..495f0d4 100644
--- a/libarchive/archive_write_set_format_xar.c
+++ b/libarchive/archive_write_set_format_xar.c
@@ -63,7 +63,7 @@
  * - When writing an XML element <link type="<file-type>">, <file-type>
  *   which is a file type a symbolic link is referencing is always marked
  *   as "broken". Xar utility uses stat(2) to get the file type, but, in
- *   libarcive format writer, we should not use it; if it is needed, we
+ *   libarchive format writer, we should not use it; if it is needed, we
  *   should get about it at archive_read_disk.c.
  * - It is possible to appear both <flags> and <ext2> elements.
  *   Xar utility generates <flags> on BSD platform and <ext2> on Linux
@@ -1227,7 +1227,7 @@
 	case AE_IFLNK:
 		/*
 		 * xar utility has checked a file type, which
-		 * a symblic-link file has referenced.
+		 * a symbolic-link file has referenced.
 		 * For example:
 		 *   <link type="directory">../ref/</link>
 		 *   The symlink target file is "../ref/" and its
@@ -1237,8 +1237,8 @@
 		 *   The symlink target file is "../f" and its
 		 *   file type is a regular file.
 		 *
-		 * But our implemention cannot do it, and then we
-		 * always record that a attribute "type" is "borken",
+		 * But our implementation cannot do it, and then we
+		 * always record that a attribute "type" is "broken",
 		 * for example:
 		 *   <link type="broken">foo/bar</link>
 		 *   It means "foo/bar" is not reachable.
@@ -1544,7 +1544,7 @@
 	}
 
 	/*
-	 * Start recoding TOC
+	 * Start recording TOC
 	 */
 	r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
 	if (r < 0) {
@@ -1961,6 +1961,7 @@
 	archive_string_free(&(file->basename));
 	archive_string_free(&(file->symlink));
 	archive_string_free(&(file->script));
+	archive_entry_free(file->entry);
 	free(file);
 }
 
@@ -2484,7 +2485,7 @@
 		archive_entry_set_nlink(target->entry, hl->nlink);
 		if (hl->nlink > 1)
 			/* It means this file is a hardlink
-			 * targe itself. */
+			 * target itself. */
 			target->hardlink_target = target;
 		for (nf = target->hlnext;
 		    nf != NULL; nf = nf->hlnext) {
@@ -2913,7 +2914,7 @@
 	*strm = lzma_init_data;
 #ifdef HAVE_LZMA_STREAM_ENCODER_MT
 	if (threads > 1) {
-		bzero(&mt_options, sizeof(mt_options));
+		memset(&mt_options, 0, sizeof(mt_options));
 		mt_options.threads = threads;
 		mt_options.timeout = 300;
 		mt_options.filters = lzmafilters;
diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c
index 810b8d7..f69b846 100644
--- a/libarchive/archive_write_set_format_zip.c
+++ b/libarchive/archive_write_set_format_zip.c
@@ -878,7 +878,7 @@
 	        || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) {
 
 		memcpy(e, "\001\231\007\000\001\000AE", 8);
-		/* AES vendoer version AE-2 does not store a CRC.
+		/* AES vendor version AE-2 does not store a CRC.
 		 * WinZip 11 uses AE-1, which does store the CRC,
 		 * but it does not store the CRC when the file size
 		 * is less than 20 bytes. So we simulate what
@@ -1013,7 +1013,7 @@
 	if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
 		switch (zip->entry_encryption) {
 		case ENCRYPTION_TRADITIONAL:
-			/* Initialize traditoinal PKWARE encryption context. */
+			/* Initialize traditional PKWARE encryption context. */
 			if (!zip->tctx_valid) {
 				ret = init_traditional_pkware_encryption(a);
 				if (ret != ARCHIVE_OK)
@@ -1499,7 +1499,7 @@
 }
 
 static uint8_t
-trad_enc_decypt_byte(struct trad_enc_ctx *ctx)
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
 {
 	unsigned temp = ctx->keys[2] | 2;
 	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
@@ -1515,7 +1515,7 @@
 
 	for (i = 0; i < max; i++) {
 		uint8_t t = in[i];
-		out[i] = t ^ trad_enc_decypt_byte(ctx);
+		out[i] = t ^ trad_enc_decrypt_byte(ctx);
 		trad_enc_update_keys(ctx, t);
 	}
 	return i;
@@ -1626,7 +1626,7 @@
 		return (ARCHIVE_FAILED);
         }
 
-	/* Set a passowrd verification value after the 'salt'. */
+	/* Set a password verification value after the 'salt'. */
 	salt[salt_len] = derived_key[key_len * 2];
 	salt[salt_len + 1] = derived_key[key_len * 2 + 1];
 
diff --git a/libarchive/libarchive-formats.5 b/libarchive/libarchive-formats.5
index 9cec760..62359dd 100644
--- a/libarchive/libarchive-formats.5
+++ b/libarchive/libarchive-formats.5
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd March 18, 2012
+.Dd December 27, 2016
 .Dt LIBARCHIVE-FORMATS 5
 .Os
 .Sh NAME
@@ -191,8 +191,6 @@
 .It Solaris extensions
 Libarchive recognizes ACL and extended attribute records written
 by Solaris tar.
-Currently, libarchive only has support for old-style ACLs; the
-newer NFSv4 ACLs are recognized but discarded.
 .El
 .Pp
 The first tar program appeared in Seventh Edition Unix in 1979.
diff --git a/libarchive/mtree.5 b/libarchive/mtree.5
index 16c8abe..e607e4a 100644
--- a/libarchive/mtree.5
+++ b/libarchive/mtree.5
@@ -48,7 +48,7 @@
 .Pp
 When encoding file or pathnames, any backslash character or
 character outside of the 95 printable ASCII characters must be
-encoded as a a backslash followed by three
+encoded as a backslash followed by three
 octal digits.
 When reading mtree files, any appearance of a backslash
 followed by three octal digits should be converted into the
diff --git a/libarchive/tar.5 b/libarchive/tar.5
index 6e6f0c0..30b837d 100644
--- a/libarchive/tar.5
+++ b/libarchive/tar.5
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2003-2009 Tim Kientzle
+.\" Copyright (c) 2016 Martin Matuska
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd December 23, 2011
+.Dd December 27, 2016
 .Dt TAR 5
 .Os
 .Sh NAME
@@ -440,11 +441,11 @@
 Vendor-specific attributes used by Joerg Schilling's
 .Nm star
 implementation.
-.It Cm SCHILY.acl.access , Cm SCHILY.acl.default
-Stores the access and default ACLs as textual strings in a format
+.It Cm SCHILY.acl.access , Cm SCHILY.acl.default, Cm SCHILY.acl.ace
+Stores the access, default and NFSv4 ACLs as textual strings in a format
 that is an extension of the format specified by POSIX.1e draft 17.
-In particular, each user or group access specification can include a fourth
-colon-separated field with the numeric UID or GID.
+In particular, each user or group access specification can include
+an additional colon-separated field with the numeric UID or GID.
 This allows ACLs to be restored on systems that may not have complete
 user or group information available (such as when NIS/YP or LDAP services
 are temporarily unavailable).
diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
index ab9a8a4..5016eed 100644
--- a/libarchive/test/CMakeLists.txt
+++ b/libarchive/test/CMakeLists.txt
@@ -6,14 +6,15 @@
 IF(ENABLE_TEST)
   SET(libarchive_test_SOURCES
     ../../test_utils/test_utils.c
-    main.c
+    ../../test_utils/test_main.c
     read_open_memory.c
     test.h
-    test_acl_freebsd_nfs4.c
-    test_acl_freebsd_posix1e.c
     test_acl_nfs4.c
     test_acl_pax.c
+    test_acl_platform_nfs4.c
+    test_acl_platform_posix1e.c
     test_acl_posix1e.c
+    test_acl_text.c
     test_archive_api_feature.c
     test_archive_clear_error.c
     test_archive_cmdline.c
@@ -62,7 +63,7 @@
     test_compat_plexus_archiver_tar.c
     test_compat_solaris_pax_sparse.c
     test_compat_solaris_tar_acl.c
-    test_compat_star_acl_posix1e.c
+    test_compat_star_acl.c
     test_compat_tar_hardlink.c
     test_compat_uudecode.c
     test_compat_uudecode_large.c
@@ -169,6 +170,7 @@
     test_read_format_zip_encryption_partially.c
     test_read_format_zip_filename.c
     test_read_format_zip_high_compression.c
+    test_read_format_zip_jar.c
     test_read_format_zip_mac_metadata.c
     test_read_format_zip_malformed.c
     test_read_format_zip_msdos.c
@@ -181,6 +183,7 @@
     test_read_format_zip_winzip_aes_large.c
     test_read_format_zip_zip64.c
     test_read_large.c
+    test_read_pax_schily_xattr.c
     test_read_pax_truncated.c
     test_read_position.c
     test_read_set_format.c
@@ -300,6 +303,7 @@
   INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h)
   INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils)
+  INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/libarchive/test)
 
   # Experimental new test handling
   ADD_CUSTOM_TARGET(run_libarchive_test
diff --git a/libarchive/test/main.c b/libarchive/test/main.c
deleted file mode 100644
index 3553be6..0000000
--- a/libarchive/test/main.c
+++ /dev/null
@@ -1,3071 +0,0 @@
-/*
- * Copyright (c) 2003-2009 Tim Kientzle
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "test.h"
-#include "test_utils.h"
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#include <errno.h>
-#ifdef HAVE_ICONV_H
-#include <iconv.h>
-#endif
-/*
- * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
- * As the include guards don't agree, the order of include is important.
- */
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h>      /* for Linux file flags */
-#endif
-#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
-#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
-#endif
-#include <limits.h>
-#include <locale.h>
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include <stdarg.h>
-#include <time.h>
-
-/*
- * This same file is used pretty much verbatim for all test harnesses.
- *
- * The next few lines are the only differences.
- * TODO: Move this into a separate configuration header, have all test
- * suites share one copy of this file.
- */
-__FBSDID("$FreeBSD: head/lib/libarchive/test/main.c 201247 2009-12-30 05:59:21Z kientzle $");
-#define KNOWNREF	"test_compat_gtar_1.tar.uu"
-#define	ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */
-#undef	PROGRAM              /* Testing a library, not a program. */
-#define	LIBRARY	"libarchive"
-#define	EXTRA_DUMP(x)	archive_error_string((struct archive *)(x))
-#define	EXTRA_ERRNO(x)	archive_errno((struct archive *)(x))
-#define	EXTRA_VERSION	archive_version_details()
-
-/*
- *
- * Windows support routines
- *
- * Note: Configuration is a tricky issue.  Using HAVE_* feature macros
- * in the test harness is dangerous because they cover up
- * configuration errors.  The classic example of this is omitting a
- * configure check.  If libarchive and libarchive_test both look for
- * the same feature macro, such errors are hard to detect.  Platform
- * macros (e.g., _WIN32 or __GNUC__) are a little better, but can
- * easily lead to very messy code.  It's best to limit yourself
- * to only the most generic programming techniques in the test harness
- * and thus avoid conditionals altogether.  Where that's not possible,
- * try to minimize conditionals by grouping platform-specific tests in
- * one place (e.g., test_acl_freebsd) or by adding new assert()
- * functions (e.g., assertMakeHardlink()) to cover up platform
- * differences.  Platform-specific coding in libarchive_test is often
- * a symptom that some capability is missing from libarchive itself.
- */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#include <io.h>
-#include <direct.h>
-#include <windows.h>
-#ifndef F_OK
-#define F_OK (0)
-#endif
-#ifndef S_ISDIR
-#define S_ISDIR(m)  ((m) & _S_IFDIR)
-#endif
-#ifndef S_ISREG
-#define S_ISREG(m)  ((m) & _S_IFREG)
-#endif
-#if !defined(__BORLANDC__)
-#define access _access
-#undef chdir
-#define chdir _chdir
-#endif
-#ifndef fileno
-#define fileno _fileno
-#endif
-/*#define fstat _fstat64*/
-#if !defined(__BORLANDC__)
-#define getcwd _getcwd
-#endif
-#define lstat stat
-/*#define lstat _stat64*/
-/*#define stat _stat64*/
-#define rmdir _rmdir
-#if !defined(__BORLANDC__)
-#define strdup _strdup
-#define umask _umask
-#endif
-#define int64_t __int64
-#endif
-
-#if defined(HAVE__CrtSetReportMode)
-# include <crtdbg.h>
-#endif
-
-mode_t umasked(mode_t expected_mode)
-{
-	mode_t mode = umask(0);
-	umask(mode);
-	return expected_mode & ~mode;
-}
-
-/* Path to working directory for current test */
-const char *testworkdir;
-#ifdef PROGRAM
-/* Pathname of exe to be tested. */
-const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-const char *testprog;
-#endif
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static void	*GetFunctionKernel32(const char *);
-static int	 my_CreateSymbolicLinkA(const char *, const char *, int);
-static int	 my_CreateHardLinkA(const char *, const char *);
-static int	 my_GetFileInformationByName(const char *,
-		     BY_HANDLE_FILE_INFORMATION *);
-
-static void *
-GetFunctionKernel32(const char *name)
-{
-	static HINSTANCE lib;
-	static int set;
-	if (!set) {
-		set = 1;
-		lib = LoadLibrary("kernel32.dll");
-	}
-	if (lib == NULL) {
-		fprintf(stderr, "Can't load kernel32.dll?!\n");
-		exit(1);
-	}
-	return (void *)GetProcAddress(lib, name);
-}
-
-static int
-my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateSymbolicLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, flags);
-}
-
-static int
-my_CreateHardLinkA(const char *linkname, const char *target)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateHardLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, NULL);
-}
-
-static int
-my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi)
-{
-	HANDLE h;
-	int r;
-
-	memset(bhfi, 0, sizeof(*bhfi));
-	h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL,
-		OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE)
-		return (0);
-	r = GetFileInformationByHandle(h, bhfi);
-	CloseHandle(h);
-	return (r);
-}
-#endif
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-static void
-invalid_parameter_handler(const wchar_t * expression,
-    const wchar_t * function, const wchar_t * file,
-    unsigned int line, uintptr_t pReserved)
-{
-	/* nop */
-}
-#endif
-
-/*
- *
- * OPTIONS FLAGS
- *
- */
-
-/* Enable core dump on failure. */
-static int dump_on_failure = 0;
-/* Default is to remove temp dirs and log data for successful tests. */
-static int keep_temp_files = 0;
-/* Default is to run the specified tests once and report errors. */
-static int until_failure = 0;
-/* Default is to just report pass/fail for each test. */
-static int verbosity = 0;
-#define	VERBOSITY_SUMMARY_ONLY -1 /* -q */
-#define VERBOSITY_PASSFAIL 0   /* Default */
-#define VERBOSITY_LIGHT_REPORT 1 /* -v */
-#define VERBOSITY_FULL 2 /* -vv */
-/* A few places generate even more output for verbosity > VERBOSITY_FULL,
- * mostly for debugging the test harness itself. */
-/* Cumulative count of assertion failures. */
-static int failures = 0;
-/* Cumulative count of reported skips. */
-static int skips = 0;
-/* Cumulative count of assertions checked. */
-static int assertions = 0;
-
-/* Directory where uuencoded reference files can be found. */
-static const char *refdir;
-
-/*
- * Report log information selectively to console and/or disk log.
- */
-static int log_console = 0;
-static FILE *logfile;
-static void
-vlogprintf(const char *fmt, va_list ap)
-{
-#ifdef va_copy
-	va_list lfap;
-	va_copy(lfap, ap);
-#endif
-	if (log_console)
-		vfprintf(stdout, fmt, ap);
-	if (logfile != NULL)
-#ifdef va_copy
-		vfprintf(logfile, fmt, lfap);
-	va_end(lfap);
-#else
-		vfprintf(logfile, fmt, ap);
-#endif
-}
-
-static void
-logprintf(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	vlogprintf(fmt, ap);
-	va_end(ap);
-}
-
-/* Set up a message to display only if next assertion fails. */
-static char msgbuff[4096];
-static const char *msg, *nextmsg;
-void
-failure(const char *fmt, ...)
-{
-	va_list ap;
-	if (fmt == NULL) {
-		nextmsg = NULL;
-	} else {
-		va_start(ap, fmt);
-		vsprintf(msgbuff, fmt, ap);
-		va_end(ap);
-		nextmsg = msgbuff;
-	}
-}
-
-/*
- * Copy arguments into file-local variables.
- * This was added to permit vararg assert() functions without needing
- * variadic wrapper macros.  Turns out that the vararg capability is almost
- * never used, so almost all of the vararg assertions can be simplified
- * by removing the vararg capability and reworking the wrapper macro to
- * pass __FILE__, __LINE__ directly into the function instead of using
- * this hook.  I suspect this machinery is used so rarely that we
- * would be better off just removing it entirely.  That would simplify
- * the code here noticeably.
- */
-static const char *skipping_filename;
-static int skipping_line;
-void skipping_setup(const char *filename, int line)
-{
-	skipping_filename = filename;
-	skipping_line = line;
-}
-
-/* Called at the beginning of each assert() function. */
-static void
-assertion_count(const char *file, int line)
-{
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	++assertions;
-	/* Proper handling of "failure()" message. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* Uncomment to print file:line after every assertion.
-	 * Verbose, but occasionally useful in tracking down crashes. */
-	/* printf("Checked %s:%d\n", file, line); */
-}
-
-/*
- * For each test source file, we remember how many times each
- * assertion was reported.  Cleared before each new test,
- * used by test_summarize().
- */
-static struct line {
-	int count;
-	int skip;
-}  failed_lines[10000];
-const char *failed_filename;
-
-/* Count this failure, setup up log destination and handle initial report. */
-static void
-failure_start(const char *filename, int line, const char *fmt, ...)
-{
-	va_list ap;
-
-	/* Record another failure for this line. */
-	++failures;
-	failed_filename = filename;
-	failed_lines[line].count++;
-
-	/* Determine whether to log header to console. */
-	switch (verbosity) {
-	case VERBOSITY_LIGHT_REPORT:
-		log_console = (failed_lines[line].count < 2);
-		break;
-	default:
-		log_console = (verbosity >= VERBOSITY_FULL);
-	}
-
-	/* Log file:line header for this failure */
-	va_start(ap, fmt);
-#if _MSC_VER
-	logprintf("%s(%d): ", filename, line);
-#else
-	logprintf("%s:%d: ", filename, line);
-#endif
-	vlogprintf(fmt, ap);
-	va_end(ap);
-	logprintf("\n");
-
-	if (msg != NULL && msg[0] != '\0') {
-		logprintf("   Description: %s\n", msg);
-		msg = NULL;
-	}
-
-	/* Determine whether to log details to console. */
-	if (verbosity == VERBOSITY_LIGHT_REPORT)
-		log_console = 0;
-}
-
-/* Complete reporting of failed tests. */
-/*
- * The 'extra' hook here is used by libarchive to include libarchive
- * error messages with assertion failures.  It could also be used
- * to add strerror() output, for example.  Just define the EXTRA_DUMP()
- * macro appropriately.
- */
-static void
-failure_finish(void *extra)
-{
-	(void)extra; /* UNUSED (maybe) */
-#ifdef EXTRA_DUMP
-	if (extra != NULL) {
-		logprintf("    errno: %d\n", EXTRA_ERRNO(extra));
-		logprintf("   detail: %s\n", EXTRA_DUMP(extra));
-	}
-#endif
-
-	if (dump_on_failure) {
-		fprintf(stderr,
-		    " *** forcing core dump so failure can be debugged ***\n");
-		abort();
-	}
-}
-
-/* Inform user that we're skipping some checks. */
-void
-test_skipping(const char *fmt, ...)
-{
-	char buff[1024];
-	va_list ap;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	va_end(ap);
-	/* Use failure() message if set. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* failure_start() isn't quite right, but is awfully convenient. */
-	failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff);
-	--failures; /* Undo failures++ in failure_start() */
-	/* Don't failure_finish() here. */
-	/* Mark as skip, so doesn't count as failed test. */
-	failed_lines[skipping_line].skip = 1;
-	++skips;
-}
-
-/*
- *
- * ASSERTIONS
- *
- */
-
-/* Generic assert() just displays the failed condition. */
-int
-assertion_assert(const char *file, int line, int value,
-    const char *condition, void *extra)
-{
-	assertion_count(file, line);
-	if (!value) {
-		failure_start(file, line, "Assertion failed: %s", condition);
-		failure_finish(extra);
-	}
-	return (value);
-}
-
-/* chdir() and report any errors */
-int
-assertion_chdir(const char *file, int line, const char *pathname)
-{
-	assertion_count(file, line);
-	if (chdir(pathname) == 0)
-		return (1);
-	failure_start(file, line, "chdir(\"%s\")", pathname);
-	failure_finish(NULL);
-	return (0);
-
-}
-
-/* Verify two integers are equal. */
-int
-assertion_equal_int(const char *file, int line,
-    long long v1, const char *e1, long long v2, const char *e2, void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Utility to convert a single UTF-8 sequence.
- */
-static int
-_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
-{
-	static const char utf8_count[256] = {
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
-		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
-		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
-		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
-		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
-	};
-	int ch;
-	int cnt;
-	uint32_t wc;
-
-	*pwc = 0;
-
-	/* Sanity check. */
-	if (n == 0)
-		return (0);
-	/*
-	 * Decode 1-4 bytes depending on the value of the first byte.
-	 */
-	ch = (unsigned char)*s;
-	if (ch == 0)
-		return (0); /* Standard:  return 0 for end-of-string. */
-	cnt = utf8_count[ch];
-
-	/* Invalid sequence or there are not plenty bytes. */
-	if (n < (size_t)cnt)
-		return (-1);
-
-	/* Make a Unicode code point from a single UTF-8 sequence. */
-	switch (cnt) {
-	case 1:	/* 1 byte sequence. */
-		*pwc = ch & 0x7f;
-		return (cnt);
-	case 2:	/* 2 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
-		return (cnt);
-	case 3:	/* 3 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x0f) << 12)
-		    | ((s[1] & 0x3f) << 6)
-		    | (s[2] & 0x3f);
-		if (wc < 0x800)
-			return (-1);/* Overlong sequence. */
-		break;
-	case 4:	/* 4 bytes sequence. */
-		if (n < 4)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		if ((s[3] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x07) << 18)
-		    | ((s[1] & 0x3f) << 12)
-		    | ((s[2] & 0x3f) << 6)
-		    | (s[3] & 0x3f);
-		if (wc < 0x10000)
-			return (-1);/* Overlong sequence. */
-		break;
-	default:
-		return (-1);
-	}
-
-	/* The code point larger than 0x10FFFF is not legal
-	 * Unicode values. */
-	if (wc > 0x10FFFF)
-		return (-1);
-	/* Correctly gets a Unicode, returns used bytes. */
-	*pwc = wc;
-	return (cnt);
-}
-
-static void strdump(const char *e, const char *p, int ewidth, int utf8)
-{
-	const char *q = p;
-
-	logprintf("      %*s = ", ewidth, e);
-	if (p == NULL) {
-		logprintf("NULL\n");
-		return;
-	}
-	logprintf("\"");
-	while (*p != '\0') {
-		unsigned int c = 0xff & *p++;
-		switch (c) {
-		case '\a': logprintf("\\a"); break;
-		case '\b': logprintf("\\b"); break;
-		case '\n': logprintf("\\n"); break;
-		case '\r': logprintf("\\r"); break;
-		default:
-			if (c >= 32 && c < 127)
-				logprintf("%c", c);
-			else
-				logprintf("\\x%02X", c);
-		}
-	}
-	logprintf("\"");
-	logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q));
-
-	/*
-	 * If the current string is UTF-8, dump its code points.
-	 */
-	if (utf8) {
-		size_t len;
-		uint32_t uc;
-		int n;
-		int cnt = 0;
-
-		p = q;
-		len = strlen(p);
-		logprintf(" [");
-		while ((n = _utf8_to_unicode(&uc, p, len)) > 0) {
-			if (p != q)
-				logprintf(" ");
-			logprintf("%04X", uc);
-			p += n;
-			len -= n;
-			cnt++;
-		}
-		logprintf("]");
-		logprintf(" (count %d", cnt);
-		if (n < 0) {
-			logprintf(",unknown %d bytes", len);
-		}
-		logprintf(")");
-
-	}
-	logprintf("\n");
-}
-
-/* Verify two strings are equal, dump them if not. */
-int
-assertion_equal_string(const char *file, int line,
-    const char *v1, const char *e1,
-    const char *v2, const char *e2,
-    void *extra, int utf8)
-{
-	int l1, l2;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0))
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	l1 = (int)strlen(e1);
-	l2 = (int)strlen(e2);
-	if (l1 < l2)
-		l1 = l2;
-	strdump(e1, v1, l1, utf8);
-	strdump(e2, v2, l1, utf8);
-	failure_finish(extra);
-	return (0);
-}
-
-static void
-wcsdump(const char *e, const wchar_t *w)
-{
-	logprintf("      %s = ", e);
-	if (w == NULL) {
-		logprintf("(null)");
-		return;
-	}
-	logprintf("\"");
-	while (*w != L'\0') {
-		unsigned int c = *w++;
-		if (c >= 32 && c < 127)
-			logprintf("%c", c);
-		else if (c < 256)
-			logprintf("\\x%02X", c);
-		else if (c < 0x10000)
-			logprintf("\\u%04X", c);
-		else
-			logprintf("\\U%08X", c);
-	}
-	logprintf("\"\n");
-}
-
-#ifndef HAVE_WCSCMP
-static int
-wcscmp(const wchar_t *s1, const wchar_t *s2)
-{
-
-	while (*s1 == *s2++) {
-		if (*s1++ == L'\0')
-			return 0;
-	}
-	if (*s1 > *--s2)
-		return 1;
-	else
-		return -1;
-}
-#endif
-
-/* Verify that two wide strings are equal, dump them if not. */
-int
-assertion_equal_wstring(const char *file, int line,
-    const wchar_t *v1, const char *e1,
-    const wchar_t *v2, const char *e2,
-    void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	wcsdump(e1, v1);
-	wcsdump(e2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
- * any bytes in p that differ from ref will be highlighted with '_'
- * before and after the hex value.
- */
-static void
-hexdump(const char *p, const char *ref, size_t l, size_t offset)
-{
-	size_t i, j;
-	char sep;
-
-	if (p == NULL) {
-		logprintf("(null)\n");
-		return;
-	}
-	for(i=0; i < l; i+=16) {
-		logprintf("%04x", (unsigned)(i + offset));
-		sep = ' ';
-		for (j = 0; j < 16 && i + j < l; j++) {
-			if (ref != NULL && p[i + j] != ref[i + j])
-				sep = '_';
-			logprintf("%c%02x", sep, 0xff & (int)p[i+j]);
-			if (ref != NULL && p[i + j] == ref[i + j])
-				sep = ' ';
-		}
-		for (; j < 16; j++) {
-			logprintf("%c  ", sep);
-			sep = ' ';
-		}
-		logprintf("%c", sep);
-		for (j=0; j < 16 && i + j < l; j++) {
-			int c = p[i + j];
-			if (c >= ' ' && c <= 126)
-				logprintf("%c", c);
-			else
-				logprintf(".");
-		}
-		logprintf("\n");
-	}
-}
-
-/* Verify that two blocks of memory are the same, display the first
- * block of differences if they're not. */
-int
-assertion_equal_mem(const char *file, int line,
-    const void *_v1, const char *e1,
-    const void *_v2, const char *e2,
-    size_t l, const char *ld, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	const char *v2 = (const char *)_v2;
-	size_t offset;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0))
-		return (1);
-	if (v1 == NULL || v2 == NULL)
-		return (0);
-
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      size %s = %d\n", ld, (int)l);
-	/* Dump 48 bytes (3 lines) so that the first difference is
-	 * in the second line. */
-	offset = 0;
-	while (l > 64 && memcmp(v1, v2, 32) == 0) {
-		/* Two lines agree, so step forward one line. */
-		v1 += 16;
-		v2 += 16;
-		l -= 16;
-		offset += 16;
-	}
-	logprintf("      Dump of %s\n", e1);
-	hexdump(v1, v2, l < 128 ? l : 128, offset);
-	logprintf("      Dump of %s\n", e2);
-	hexdump(v2, v1, l < 128 ? l : 128, offset);
-	logprintf("\n");
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that a block of memory is filled with the specified byte. */
-int
-assertion_memory_filled_with(const char *file, int line,
-    const void *_v1, const char *vd,
-    size_t l, const char *ld,
-    char b, const char *bd, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	size_t c = 0;
-	size_t i;
-	(void)ld; /* UNUSED */
-
-	assertion_count(file, line);
-
-	for (i = 0; i < l; ++i) {
-		if (v1[i] == b) {
-			++c;
-		}
-	}
-	if (c == l)
-		return (1);
-
-	failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd);
-	logprintf("   Only %d bytes were correct\n", (int)c);
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that the named file exists and is empty. */
-int
-assertion_empty_file(const char *filename, int line, const char *f1)
-{
-	char buff[1024];
-	struct stat st;
-	ssize_t s;
-	FILE *f;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0)
-		return (1);
-
-	failure_start(filename, line, "File should be empty: %s", f1);
-	logprintf("    File size: %d\n", (int)st.st_size);
-	logprintf("    Contents:\n");
-	f = fopen(f1, "rb");
-	if (f == NULL) {
-		logprintf("    Unable to open %s\n", f1);
-	} else {
-		s = ((off_t)sizeof(buff) < st.st_size) ?
-		    (ssize_t)sizeof(buff) : (ssize_t)st.st_size;
-		s = fread(buff, 1, s, f);
-		hexdump(buff, NULL, s, 0);
-		fclose(f);
-	}
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file exists and is not empty. */
-int
-assertion_non_empty_file(const char *filename, int line, const char *f1)
-{
-	struct stat st;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0) {
-		failure_start(filename, line, "File empty: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify that two files have the same contents. */
-/* TODO: hexdump the first bytes that actually differ. */
-int
-assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2)
-{
-	char buff1[1024];
-	char buff2[1024];
-	FILE *f1, *f2;
-	int n1, n2;
-
-	assertion_count(filename, line);
-
-	f1 = fopen(fn1, "rb");
-	f2 = fopen(fn2, "rb");
-	if (f1 == NULL || f2 == NULL) {
-		if (f1) fclose(f1);
-		if (f2) fclose(f2);
-		return (0);
-	}
-	for (;;) {
-		n1 = (int)fread(buff1, 1, sizeof(buff1), f1);
-		n2 = (int)fread(buff2, 1, sizeof(buff2), f2);
-		if (n1 != n2)
-			break;
-		if (n1 == 0 && n2 == 0) {
-			fclose(f1);
-			fclose(f2);
-			return (1);
-		}
-		if (memcmp(buff1, buff2, n1) != 0)
-			break;
-	}
-	fclose(f1);
-	fclose(f2);
-	failure_start(filename, line, "Files not identical");
-	logprintf("  file1=\"%s\"\n", fn1);
-	logprintf("  file2=\"%s\"\n", fn2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file does exist. */
-int
-assertion_file_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (!_access(f, 0))
-		return (1);
-#else
-	if (!access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file doesn't exist. */
-int
-assertion_file_not_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (_access(f, 0))
-		return (1);
-#else
-	if (access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should not exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Compare the contents of a file to a block of memory. */
-int
-assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn)
-{
-	char *contents;
-	FILE *f;
-	int n;
-
-	assertion_count(filename, line);
-
-	f = fopen(fn, "rb");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File should exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	contents = malloc(s * 2);
-	n = (int)fread(contents, 1, s * 2, f);
-	fclose(f);
-	if (n == s && memcmp(buff, contents, s) == 0) {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "File contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0)
-		hexdump(contents, buff, n > 512 ? 512 : n, 0);
-	else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s > 512 ? 512 : s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Check the contents of a text file, being tolerant of line endings. */
-int
-assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn)
-{
-	char *contents;
-	const char *btxt, *ftxt;
-	FILE *f;
-	int n, s;
-
-	assertion_count(filename, line);
-	f = fopen(fn, "r");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File doesn't exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	s = (int)strlen(buff);
-	contents = malloc(s * 2 + 128);
-	n = (int)fread(contents, 1, s * 2 + 128 - 1, f);
-	if (n >= 0)
-		contents[n] = '\0';
-	fclose(f);
-	/* Compare texts. */
-	btxt = buff;
-	ftxt = (const char *)contents;
-	while (*btxt != '\0' && *ftxt != '\0') {
-		if (*btxt == *ftxt) {
-			++btxt;
-			++ftxt;
-			continue;
-		}
-		if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') {
-			/* Pass over different new line characters. */
-			++btxt;
-			ftxt += 2;
-			continue;
-		}
-		break;
-	}
-	if (*btxt == '\0' && *ftxt == '\0') {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "Contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0) {
-		hexdump(contents, buff, n, 0);
-		logprintf("  expected\n", fn);
-		hexdump(buff, contents, s, 0);
-	} else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Verify that a text file contains the specified lines, regardless of order */
-/* This could be more efficient if we sorted both sets of lines, etc, but
- * since this is used only for testing and only ever deals with a dozen or so
- * lines at a time, this relatively crude approach is just fine. */
-int
-assertion_file_contains_lines_any_order(const char *file, int line,
-    const char *pathname, const char *lines[])
-{
-	char *buff;
-	size_t buff_size;
-	size_t expected_count, actual_count, i, j;
-	char **expected = NULL;
-	char *p, **actual = NULL;
-	char c;
-	int expected_failure = 0, actual_failure = 0;
-
-	assertion_count(file, line);
-
-	buff = slurpfile(&buff_size, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(pathname, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	/* Make a copy of the provided lines and count up the expected
-	 * file size. */
-	for (i = 0; lines[i] != NULL; ++i) {
-	}
-	expected_count = i;
-	if (expected_count) {
-		expected = malloc(sizeof(char *) * expected_count);
-		if (expected == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			free(expected);
-			return (0);
-		}
-		for (i = 0; lines[i] != NULL; ++i) {
-			expected[i] = strdup(lines[i]);
-		}
-	}
-
-	/* Break the file into lines */
-	actual_count = 0;
-	for (c = '\0', p = buff; p < buff + buff_size; ++p) {
-		if (*p == '\x0d' || *p == '\x0a')
-			*p = '\0';
-		if (c == '\0' && *p != '\0')
-			++actual_count;
-		c = *p;
-	}
-	if (actual_count) {
-		actual = calloc(sizeof(char *), actual_count);
-		if (actual == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			free(expected);
-			return (0);
-		}
-		for (j = 0, p = buff; p < buff + buff_size;
-		    p += 1 + strlen(p)) {
-			if (*p != '\0') {
-				actual[j] = p;
-				++j;
-			}
-		}
-	}
-
-	/* Erase matching lines from both lists */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] == NULL)
-			continue;
-		for (j = 0; j < actual_count; ++j) {
-			if (actual[j] == NULL)
-				continue;
-			if (strcmp(expected[i], actual[j]) == 0) {
-				free(expected[i]);
-				expected[i] = NULL;
-				actual[j] = NULL;
-				break;
-			}
-		}
-	}
-
-	/* If there's anything left, it's a failure */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL)
-			++expected_failure;
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			++actual_failure;
-	}
-	if (expected_failure == 0 && actual_failure == 0) {
-		free(buff);
-		free(expected);
-		free(actual);
-		return (1);
-	}
-	failure_start(file, line, "File doesn't match: %s", pathname);
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL) {
-			logprintf("  Expected but not present: %s\n", expected[i]);
-			free(expected[i]);
-		}
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			logprintf("  Present but not expected: %s\n", actual[j]);
-	}
-	failure_finish(NULL);
-	free(buff);
-	free(expected);
-	free(actual);
-	return (0);
-}
-
-/* Verify that a text file does not contains the specified strings */
-int
-assertion_file_contains_no_invalid_strings(const char *file, int line,
-    const char *pathname, const char *strings[])
-{
-	char *buff;
-	int i;
-
-	buff = slurpfile(NULL, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(file, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	for (i = 0; strings[i] != NULL; ++i) {
-		if (strstr(buff, strings[i]) != NULL) {
-			failure_start(file, line, "Invalid string in %s: %s", pathname,
-			    strings[i]);
-			failure_finish(NULL);
-			free(buff);
-			return(0);
-		}
-	}
-
-	free(buff);
-	return (0);
-}
-
-/* Test that two paths point to the same file. */
-/* As a side-effect, asserts that both files exist. */
-static int
-is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(path1, &bhfi1);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = my_GetFileInformationByName(path2, &bhfi2);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
-		&& bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
-		&& bhfi1.nFileIndexLow == bhfi2.nFileIndexLow);
-#else
-	struct stat st1, st2;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(path1, &st1);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = lstat(path2, &st2);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev);
-#endif
-}
-
-int
-assertion_is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s are not hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-int
-assertion_is_not_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (!is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s should not be hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify a/b/mtime of 'pathname'. */
-/* If 'recent', verify that it's within last 10 seconds. */
-static int
-assertion_file_time(const char *file, int line,
-    const char *pathname, long t, long nsec, char type, int recent)
-{
-	long long filet, filet_nsec;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define EPOC_TIME	(116444736000000000ULL)
-	FILETIME fxtime, fbirthtime, fatime, fmtime;
-	ULARGE_INTEGER wintm;
-	HANDLE h;
-	fxtime.dwLowDateTime = 0;
-	fxtime.dwHighDateTime = 0;
-
-	assertion_count(file, line);
-	/* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open
-	 * a directory file. If not, CreateFile() will fail when
-	 * the pathname is a directory. */
-	h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
-	    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = GetFileTime(h, &fbirthtime, &fatime, &fmtime);
-	switch (type) {
-	case 'a': fxtime = fatime; break;
-	case 'b': fxtime = fbirthtime; break;
-	case 'm': fxtime = fmtime; break;
-	}
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't GetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	wintm.LowPart = fxtime.dwLowDateTime;
-	wintm.HighPart = fxtime.dwHighDateTime;
-	filet = (wintm.QuadPart - EPOC_TIME) / 10000000;
-	filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100;
-	nsec = (nsec / 100) * 100; /* Round the request */
-#else
-	struct stat st;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	switch (type) {
-	case 'a': filet = st.st_atime; break;
-	case 'm': filet = st.st_mtime; break;
-	case 'b': filet = 0; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-#if defined(__FreeBSD__)
-	switch (type) {
-	case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
-	case 'b': filet = st.st_birthtime;
-		/* FreeBSD filesystems that don't support birthtime
-		 * (e.g., UFS1) always return -1 here. */
-		if (filet == -1) {
-			return (1);
-		}
-		filet_nsec = st.st_birthtimespec.tv_nsec; break;
-	case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-	/* FreeBSD generally only stores to microsecond res, so round. */
-	filet_nsec = (filet_nsec / 1000) * 1000;
-	nsec = (nsec / 1000) * 1000;
-#else
-	filet_nsec = nsec = 0;	/* Generic POSIX only has whole seconds. */
-	if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */
-#if defined(__HAIKU__)
-	if (type == 'a') return (1); /* Haiku doesn't have atime. */
-#endif
-#endif
-#endif
-	if (recent) {
-		/* Check that requested time is up-to-date. */
-		time_t now = time(NULL);
-		if (filet < now - 10 || filet > now + 1) {
-			failure_start(file, line,
-			    "File %s has %ctime %lld, %lld seconds ago\n",
-			    pathname, type, filet, now - filet);
-			failure_finish(NULL);
-			return (0);
-		}
-	} else if (filet != t || filet_nsec != nsec) {
-		failure_start(file, line,
-		    "File %s has %ctime %lld.%09lld, expected %lld.%09lld",
-		    pathname, type, filet, filet_nsec, t, nsec);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify atime of 'pathname'. */
-int
-assertion_file_atime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'a', 0);
-}
-
-/* Verify atime of 'pathname' is up-to-date. */
-int
-assertion_file_atime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'a', 1);
-}
-
-/* Verify birthtime of 'pathname'. */
-int
-assertion_file_birthtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'b', 0);
-}
-
-/* Verify birthtime of 'pathname' is up-to-date. */
-int
-assertion_file_birthtime_recent(const char *file, int line,
-    const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
-}
-
-/* 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");
-	(void)mode; /* UNUSED */
-	(void)r; /* UNUSED */
-#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);
-}
-
-/* Verify mtime of 'pathname'. */
-int
-assertion_file_mtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'm', 0);
-}
-
-/* Verify mtime of 'pathname' is up-to-date. */
-int
-assertion_file_mtime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'm', 1);
-}
-
-/* Verify number of links to 'pathname'. */
-int
-assertion_file_nlinks(const char *file, int line,
-    const char *pathname, int nlinks)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(pathname, &bhfi);
-	if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, bhfi.nNumberOfLinks, nlinks);
-	failure_finish(NULL);
-	return (0);
-#else
-	struct stat st;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r == 0 && (int)st.st_nlink == nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, st.st_nlink, nlinks);
-	failure_finish(NULL);
-	return (0);
-#endif
-}
-
-/* Verify size of 'pathname'. */
-int
-assertion_file_size(const char *file, int line, const char *pathname, long size)
-{
-	int64_t filesize;
-	int r;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	{
-		BY_HANDLE_FILE_INFORMATION bhfi;
-		r = !my_GetFileInformationByName(pathname, &bhfi);
-		filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow;
-	}
-#else
-	{
-		struct stat st;
-		r = lstat(pathname, &st);
-		filesize = st.st_size;
-	}
-#endif
-	if (r == 0 && filesize == size)
-			return (1);
-	failure_start(file, line, "File %s has size %ld, expected %ld",
-	    pathname, (long)filesize, (long)size);
-	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)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Dir should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISDIR(st.st_mode)) {
-		failure_start(file, line, "%s is not a dir", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "Dir %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Verify that 'pathname' is a regular file.  If 'mode' is >= 0,
- * verify that too. */
-int
-assertion_is_reg(const char *file, int line, const char *pathname, int mode)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0 || !S_ISREG(st.st_mode)) {
-		failure_start(file, line, "File should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "File %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Check whether 'pathname' is a symbolic link.  If 'contents' is
- * non-NULL, verify that the symlink has those contents. */
-static int
-is_symlink(const char *file, int line,
-    const char *pathname, const char *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)pathname; /* UNUSED */
-	(void)contents; /* UNUSED */
-	assertion_count(file, line);
-	/* Windows sort-of has real symlinks, but they're only usable
-	 * by privileged users and are crippled even then, so there's
-	 * really not much point in bothering with this. */
-	return (0);
-#else
-	char buff[300];
-	struct stat st;
-	ssize_t linklen;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line,
-		    "Symlink should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISLNK(st.st_mode))
-		return (0);
-	if (contents == NULL)
-		return (1);
-	linklen = readlink(pathname, buff, sizeof(buff));
-	if (linklen < 0) {
-		failure_start(file, line, "Can't read symlink %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	buff[linklen] = '\0';
-	if (strcmp(buff, contents) != 0)
-		return (0);
-	return (1);
-#endif
-}
-
-/* Assert that path is a symlink that (optionally) contains contents. */
-int
-assertion_is_symlink(const char *file, int line,
-    const char *path, const char *contents)
-{
-	if (is_symlink(file, line, path, contents))
-		return (1);
-	if (contents)
-		failure_start(file, line, "File %s is not a symlink to %s",
-		    path, contents);
-	else
-		failure_start(file, line, "File %s is not a symlink", path);
-	failure_finish(NULL);
-	return (0);
-}
-
-
-/* Create a directory and report any errors. */
-int
-assertion_make_dir(const char *file, int line, const char *dirname, int mode)
-{
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-	if (0 == _mkdir(dirname))
-		return (1);
-#else
-	if (0 == mkdir(dirname, mode)) {
-		if (0 == chmod(dirname, mode)) {
-			assertion_file_mode(file, line, dirname, mode);
-			return (1);
-		}
-	}
-#endif
-	failure_start(file, line, "Could not create directory %s", dirname);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a file with the specified contents and report any failures. */
-int
-assertion_make_file(const char *file, int line,
-    const char *path, int mode, int csize, const void *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	/* TODO: Rework this to set file mode as well. */
-	FILE *f;
-	(void)mode; /* UNUSED */
-	assertion_count(file, line);
-	f = fopen(path, "wb");
-	if (f == NULL) {
-		failure_start(file, line, "Could not create file %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (contents != NULL) {
-		size_t wsize;
-
-		if (csize < 0)
-			wsize = strlen(contents);
-		else
-			wsize = (size_t)csize;
-		if (wsize != fwrite(contents, 1, wsize, f)) {
-			fclose(f);
-			failure_start(file, line,
-			    "Could not write file %s", path);
-			failure_finish(NULL);
-			return (0);
-		}
-	}
-	fclose(f);
-	return (1);
-#else
-	int fd;
-	assertion_count(file, line);
-	fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644);
-	if (fd < 0) {
-		failure_start(file, line, "Could not create %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (0 != chmod(path, mode)) {
-		failure_start(file, line, "Could not chmod %s", path);
-		failure_finish(NULL);
-		close(fd);
-		return (0);
-	}
-	if (contents != NULL) {
-		ssize_t wsize;
-
-		if (csize < 0)
-			wsize = (ssize_t)strlen(contents);
-		else
-			wsize = (ssize_t)csize;
-		if (wsize != write(fd, contents, wsize)) {
-			close(fd);
-			failure_start(file, line,
-			    "Could not write to %s", path);
-			failure_finish(NULL);
-			close(fd);
-			return (0);
-		}
-	}
-	close(fd);
-	assertion_file_mode(file, line, path, mode);
-	return (1);
-#endif
-}
-
-/* Create a hardlink and report any failures. */
-int
-assertion_make_hardlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-	int succeeded;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	succeeded = my_CreateHardLinkA(newpath, linkto);
-#elif HAVE_LINK
-	succeeded = !link(linkto, newpath);
-#else
-	succeeded = 0;
-#endif
-	if (succeeded)
-		return (1);
-	failure_start(file, line, "Could not create hardlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a symlink and report any failures. */
-int
-assertion_make_symlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	int targetIsDir = 0;  /* TODO: Fix this */
-	assertion_count(file, line);
-	if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
-		return (1);
-#elif HAVE_SYMLINK
-	assertion_count(file, line);
-	if (0 == symlink(linkto, newpath))
-		return (1);
-#endif
-	failure_start(file, line, "Could not create symlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Set umask, report failures. */
-int
-assertion_umask(const char *file, int line, int mask)
-{
-	assertion_count(file, line);
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	umask(mask);
-	return (1);
-}
-
-/* Set times, report failures. */
-int
-assertion_utimes(const char *file, int line,
-    const char *pathname, long at, long at_nsec, long mt, long mt_nsec)
-{
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
-	 + (((nsec)/1000)*10))
-	HANDLE h;
-	ULARGE_INTEGER wintm;
-	FILETIME fatime, fmtime;
-	FILETIME *pat, *pmt;
-
-	assertion_count(file, line);
-	h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE,
-		    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-		    FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (at > 0 || at_nsec > 0) {
-		wintm.QuadPart = WINTIME(at, at_nsec);
-		fatime.dwLowDateTime = wintm.LowPart;
-		fatime.dwHighDateTime = wintm.HighPart;
-		pat = &fatime;
-	} else
-		pat = NULL;
-	if (mt > 0 || mt_nsec > 0) {
-		wintm.QuadPart = WINTIME(mt, mt_nsec);
-		fmtime.dwLowDateTime = wintm.LowPart;
-		fmtime.dwHighDateTime = wintm.HighPart;
-		pmt = &fmtime;
-	} else
-		pmt = NULL;
-	if (pat != NULL || pmt != NULL)
-		r = SetFileTime(h, NULL, pat, pmt);
-	else
-		r = 1;
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't SetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#else /* defined(_WIN32) && !defined(__CYGWIN__) */
-	struct stat st;
-	struct timeval times[2];
-
-#if !defined(__FreeBSD__)
-	mt_nsec = at_nsec = 0;	/* Generic POSIX only has whole seconds. */
-#endif
-	if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0)
-		return (1);
-
-	r = lstat(pathname, &st);
-	if (r < 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (mt == 0 && mt_nsec == 0) {
-		mt = st.st_mtime;
-#if defined(__FreeBSD__)
-		mt_nsec = st.st_mtimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		mt_nsec = (mt_nsec / 1000) * 1000;
-#endif
-	}
-	if (at == 0 && at_nsec == 0) {
-		at = st.st_atime;
-#if defined(__FreeBSD__)
-		at_nsec = st.st_atimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		at_nsec = (at_nsec / 1000) * 1000;
-#endif
-	}
-
-	times[1].tv_sec = mt;
-	times[1].tv_usec = mt_nsec / 1000;
-
-	times[0].tv_sec = at;
-	times[0].tv_usec = at_nsec / 1000;
-
-#ifdef HAVE_LUTIMES
-	r = lutimes(pathname, times);
-#else
-	r = utimes(pathname, times);
-#endif
-	if (r < 0) {
-		failure_start(file, line, "Can't utimes %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
-}
-
-/* Set nodump, report failures. */
-int
-assertion_nodump(const char *file, int line, const char *pathname)
-{
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-	int r;
-
-	assertion_count(file, line);
-	r = chflags(pathname, UF_NODUMP);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-	int fd, r, flags;
-
-	assertion_count(file, line);
-	fd = open(pathname, O_RDONLY | O_NONBLOCK);
-	if (fd < 0) {
-		failure_start(file, line, "Can't open %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't get flags %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	close(fd);
-#else
-	(void)pathname; /* UNUSED */
-	assertion_count(file, line);
-#endif
-	return (1);
-}
-
-/*
- *
- *  UTILITIES for use by tests.
- *
- */
-
-/*
- * Check whether platform supports symlinks.  This is intended
- * for tests to use in deciding whether to bother testing symlink
- * support; if the platform doesn't support symlinks, there's no point
- * in checking whether the program being tested can create them.
- *
- * Note that the first time this test is called, we actually go out to
- * disk to create and verify a symlink.  This is necessary because
- * symlink support is actually a property of a particular filesystem
- * and can thus vary between directories on a single system.  After
- * the first call, this returns the cached result from memory, so it's
- * safe to call it as often as you wish.
- */
-int
-canSymlink(void)
-{
-	/* Remember the test result */
-	static int value = 0, tested = 0;
-	if (tested)
-		return (value);
-
-	++tested;
-	assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a");
-	/* Note: Cygwin has its own symlink() emulation that does not
-	 * use the Win32 CreateSymbolicLink() function. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0)
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0");
-#elif HAVE_SYMLINK
-	value = (0 == symlink("canSymlink.0", "canSymlink.1"))
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0");
-#endif
-	return (value);
-}
-
-/* Platform-dependent options for hiding the output of a subcommand. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */
-#else
-static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */
-#endif
-/*
- * Can this platform run the bzip2 program?
- */
-int
-canBzip2(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("bzip2 -d -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the grzip program?
- */
-int
-canGrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("grzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the gzip program?
- */
-int
-canGzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("gzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lrzip program?
- */
-int
-canRunCommand(const char *cmd)
-{
-  static int tested = 0, value = 0;
-  if (!tested) {
-    tested = 1;
-    if (systemf("%s %s", cmd, redirectArgs) == 0)
-      value = 1;
-  }
-  return (value);
-}
-
-int
-canLrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lrzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lz4 program?
- */
-int
-canLz4(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lz4 -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzip program?
- */
-int
-canLzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzma program?
- */
-int
-canLzma(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzma -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzop program?
- */
-int
-canLzop(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzop -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the xz program?
- */
-int
-canXz(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("xz -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this filesystem handle nodump flags.
- */
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	struct stat sb;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	if (chflags(path, UF_NODUMP) < 0)
-		return (0);
-	if (stat(path, &sb) < 0)
-		return (0);
-	if (sb.st_flags & UF_NODUMP)
-		return (1);
-	return (0);
-}
-
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	int fd, r, flags;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	if (flags & EXT2_NODUMP_FL)
-		return (1);
-	return (0);
-}
-
-#else
-
-int
-canNodump()
-{
-	return (0);
-}
-
-#endif
-
-/*
- * Sleep as needed; useful for verifying disk timestamp changes by
- * ensuring that the wall-clock time has actually changed before we
- * go back to re-read something from disk.
- */
-void
-sleepUntilAfter(time_t t)
-{
-	while (t >= time(NULL))
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		Sleep(500);
-#else
-		sleep(1);
-#endif
-}
-
-/*
- * Call standard system() call, but build up the command line using
- * sprintf() conventions.
- */
-int
-systemf(const char *fmt, ...)
-{
-	char buff[8192];
-	va_list ap;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	if (verbosity > VERBOSITY_FULL)
-		logprintf("Cmd: %s\n", buff);
-	r = system(buff);
-	va_end(ap);
-	return (r);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-char *
-slurpfile(size_t * sizep, const char *fmt, ...)
-{
-	char filename[8192];
-	struct stat st;
-	va_list ap;
-	char *p;
-	ssize_t bytes_read;
-	FILE *f;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(filename, fmt, ap);
-	va_end(ap);
-
-	f = fopen(filename, "rb");
-	if (f == NULL) {
-		/* Note: No error; non-existent file is okay here. */
-		return (NULL);
-	}
-	r = fstat(fileno(f), &st);
-	if (r != 0) {
-		logprintf("Can't stat file %s\n", filename);
-		fclose(f);
-		return (NULL);
-	}
-	p = malloc((size_t)st.st_size + 1);
-	if (p == NULL) {
-		logprintf("Can't allocate %ld bytes of memory to read file %s\n",
-		    (long int)st.st_size, filename);
-		fclose(f);
-		return (NULL);
-	}
-	bytes_read = fread(p, 1, (size_t)st.st_size, f);
-	if (bytes_read < st.st_size) {
-		logprintf("Can't read file %s\n", filename);
-		fclose(f);
-		free(p);
-		return (NULL);
-	}
-	p[st.st_size] = '\0';
-	if (sizep != NULL)
-		*sizep = (size_t)st.st_size;
-	fclose(f);
-	return (p);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-void
-dumpfile(const char *filename, void *data, size_t len)
-{
-	ssize_t bytes_written;
-	FILE *f;
-
-	f = fopen(filename, "wb");
-	if (f == NULL) {
-		logprintf("Can't open file %s for writing\n", filename);
-		return;
-	}
-	bytes_written = fwrite(data, 1, len, f);
-	if (bytes_written < (ssize_t)len)
-		logprintf("Can't write file %s\n", filename);
-	fclose(f);
-}
-
-/* Read a uuencoded file from the reference directory, decode, and
- * write the result into the current directory. */
-#define VALID_UUDECODE(c) (c >= 32 && c <= 96)
-#define	UUDECODE(c) (((c) - 0x20) & 0x3f)
-void
-extract_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-
-	sprintf(buff, "%s/%s.uu", refdir, name);
-	in = fopen(buff, "r");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Read up to and including the 'begin' line. */
-	for (;;) {
-		if (fgets(buff, sizeof(buff), in) == NULL) {
-			/* TODO: This is a failure. */
-			return;
-		}
-		if (memcmp(buff, "begin ", 6) == 0)
-			break;
-	}
-	/* Now, decode the rest and write it. */
-	out = fopen(name, "wb");
-	while (fgets(buff, sizeof(buff), in) != NULL) {
-		char *p = buff;
-		int bytes;
-
-		if (memcmp(buff, "end", 3) == 0)
-			break;
-
-		bytes = UUDECODE(*p++);
-		while (bytes > 0) {
-			int n = 0;
-			/* Write out 1-3 bytes from that. */
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				assert(VALID_UUDECODE(p[1]));
-				n = UUDECODE(*p++) << 18;
-				n |= UUDECODE(*p++) << 12;
-				fputc(n >> 16, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++) << 6;
-				fputc((n >> 8) & 0xFF, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++);
-				fputc(n & 0xFF, out);
-				--bytes;
-			}
-		}
-	}
-	fclose(out);
-	fclose(in);
-}
-
-void
-copy_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-	size_t rbytes;
-
-	sprintf(buff, "%s/%s", refdir, name);
-	in = fopen(buff, "rb");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Now, decode the rest and write it. */
-	/* Not a lot of error checking here; the input better be right. */
-	out = fopen(name, "wb");
-	while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) {
-		if (fwrite(buff, 1, rbytes, out) != rbytes) {
-			logprintf("Error: fwrite\n");
-			break;
-		}
-	}
-	fclose(out);
-	fclose(in);
-}
-
-int
-is_LargeInode(const char *file)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	r = my_GetFileInformationByName(file, &bhfi);
-	if (r != 0)
-		return (0);
-	return (bhfi.nFileIndexHigh & 0x0000FFFFUL);
-#else
-	struct stat st;
-	int64_t ino;
-
-	if (stat(file, &st) < 0)
-		return (0);
-	ino = (int64_t)st.st_ino;
-	return (ino > 0xffffffff);
-#endif
-}
-
-void
-extract_reference_files(const char **names)
-{
-	while (names && *names)
-		extract_reference_file(*names++);
-}
-
-/*
- *
- * TEST management
- *
- */
-
-/*
- * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has
- * a line like
- *      DEFINE_TEST(test_function)
- * for each test.
- */
-
-/* Use "list.h" to declare all of the test functions. */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(name) void name(void);
-#include "list.h"
-
-/* Use "list.h" to create a list of all tests (functions and names). */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(n) { n, #n, 0 },
-struct test_list_t tests[] = {
-	#include "list.h"
-};
-
-/*
- * Summarize repeated failures in the just-completed test.
- */
-static void
-test_summarize(int failed, int skips_num)
-{
-	unsigned int i;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY:
-		printf(failed ? "E" : ".");
-		fflush(stdout);
-		break;
-	case VERBOSITY_PASSFAIL:
-		printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n");
-		break;
-	}
-
-	log_console = (verbosity == VERBOSITY_LIGHT_REPORT);
-
-	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
-		if (failed_lines[i].count > 1 && !failed_lines[i].skip)
-			logprintf("%s:%d: Summary: Failed %d times\n",
-			    failed_filename, i, failed_lines[i].count);
-	}
-	/* Clear the failure history for the next file. */
-	failed_filename = NULL;
-	memset(failed_lines, 0, sizeof(failed_lines));
-}
-
-/*
- * Actually run a single test, with appropriate setup and cleanup.
- */
-static int
-test_run(int i, const char *tmpdir)
-{
-	char workdir[1024];
-	char logfilename[64];
-	int failures_before = failures;
-	int skips_before = skips;
-	int oldumask;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */
-		break;
-	case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */
-		printf("%3d: %-64s", i, tests[i].name);
-		fflush(stdout);
-		break;
-	default: /* Title of test, details will follow */
-		printf("%3d: %s\n", i, tests[i].name);
-	}
-
-	/* Chdir to the top-level work directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to top work dir %s\n", tmpdir);
-		exit(1);
-	}
-	/* Create a log file for this test. */
-	sprintf(logfilename, "%s.log", tests[i].name);
-	logfile = fopen(logfilename, "w");
-	fprintf(logfile, "%s\n\n", tests[i].name);
-	/* Chdir() to a work dir for this specific test. */
-	snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name);
-	testworkdir = workdir;
-	if (!assertMakeDir(testworkdir, 0755)
-	    || !assertChdir(testworkdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to work dir %s\n", testworkdir);
-		exit(1);
-	}
-	/* Explicitly reset the locale before each test. */
-	setlocale(LC_ALL, "C");
-	/* Record the umask before we run the test. */
-	umask(oldumask = umask(0));
-	/*
-	 * Run the actual test.
-	 */
-	(*tests[i].func)();
-	/*
-	 * Clean up and report afterwards.
-	 */
-	testworkdir = NULL;
-	/* Restore umask */
-	umask(oldumask);
-	/* Reset locale. */
-	setlocale(LC_ALL, "C");
-	/* Reset directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n",
-		    tmpdir);
-		exit(1);
-	}
-	/* Report per-test summaries. */
-	tests[i].failures = failures - failures_before;
-	test_summarize(tests[i].failures, skips - skips_before);
-	/* Close the per-test log file. */
-	fclose(logfile);
-	logfile = NULL;
-	/* If there were no failures, we can remove the work dir and logfile. */
-	if (tests[i].failures == 0) {
-		if (!keep_temp_files && assertChdir(tmpdir)) {
-#if defined(_WIN32) && !defined(__CYGWIN__)
-			/* Make sure not to leave empty directories.
-			 * Sometimes a processing of closing files used by tests
-			 * is not done, then rmdir will be failed and it will
-			 * leave a empty test directory. So we should wait a few
-			 * seconds and retry rmdir. */
-			int r, t;
-			for (t = 0; t < 10; t++) {
-				if (t > 0)
-					Sleep(1000);
-				r = systemf("rmdir /S /Q %s", tests[i].name);
-				if (r == 0)
-					break;
-			}
-			systemf("del %s", logfilename);
-#else
-			systemf("rm -rf %s", tests[i].name);
-			systemf("rm %s", logfilename);
-#endif
-		}
-	}
-	/* Return appropriate status. */
-	return (tests[i].failures);
-}
-
-/*
- *
- *
- * MAIN and support routines.
- *
- *
- */
-
-static void
-usage(const char *program)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int i;
-
-	printf("Usage: %s [options] <test> <test> ...\n", program);
-	printf("Default is to run all tests.\n");
-	printf("Otherwise, specify the numbers of the tests you wish to run.\n");
-	printf("Options:\n");
-	printf("  -d  Dump core after any failure, for debugging.\n");
-	printf("  -k  Keep all temp files.\n");
-	printf("      Default: temp files for successful tests deleted.\n");
-#ifdef PROGRAM
-	printf("  -p <path>  Path to executable to be tested.\n");
-	printf("      Default: path taken from " ENVBASE " environment variable.\n");
-#endif
-	printf("  -q  Quiet.\n");
-	printf("  -r <dir>   Path to dir containing reference files.\n");
-	printf("      Default: Current directory.\n");
-	printf("  -u  Keep running specifies tests until one fails.\n");
-	printf("  -v  Verbose.\n");
-	printf("Available tests:\n");
-	for (i = 0; i < limit; i++)
-		printf("  %d: %s\n", i, tests[i].name);
-	exit(1);
-}
-
-static char *
-get_refdir(const char *d)
-{
-	size_t tried_size, buff_size;
-	char *buff, *tried, *pwd = NULL, *p = NULL;
-
-#ifdef PATH_MAX
-	buff_size = PATH_MAX;
-#else
-	buff_size = 8192;
-#endif
-	buff = calloc(buff_size, 1);
-	if (buff == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* Allocate a buffer to hold the various directories we checked. */
-	tried_size = buff_size * 2;
-	tried = calloc(tried_size, 1);
-	if (tried == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* If a dir was specified, try that */
-	if (d != NULL) {
-		pwd = NULL;
-		snprintf(buff, buff_size, "%s", d);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-		goto failure;
-	}
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-	/* Look for a known file. */
-	snprintf(buff, buff_size, "%s", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-	snprintf(buff, buff_size, "%s/test", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(LIBRARY)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY);
-#else
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM);
-#endif
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(PROGRAM_ALIAS)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-#endif
-
-	if (memcmp(pwd, "/usr/obj", 8) == 0) {
-		snprintf(buff, buff_size, "%s", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-		snprintf(buff, buff_size, "%s/test", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-	}
-
-failure:
-	printf("Unable to locate known reference file %s\n", KNOWNREF);
-	printf("  Checked following directories:\n%s\n", tried);
-	printf("Use -r option to specify full path to reference directory\n");
-#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
-	DebugBreak();
-#endif
-	exit(1);
-
-success:
-	free(p);
-	free(pwd);
-	free(tried);
-
-	/* Copy result into a fresh buffer to reduce memory usage. */
-	p = strdup(buff);
-	free(buff);
-	return p;
-}
-
-int
-main(int argc, char **argv)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int test_set[sizeof(tests) / sizeof(tests[0])];
-	int i = 0, j = 0, tests_run = 0, tests_failed = 0, option;
-	time_t now;
-	char *refdir_alloc = NULL;
-	const char *progname;
-	char **saved_argv;
-	const char *tmp, *option_arg, *p;
-	char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL;
-	char tmpdir_timestamp[256];
-
-	(void)argc; /* UNUSED */
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-	/* To stop to run the default invalid parameter handler. */
-	_set_invalid_parameter_handler(invalid_parameter_handler);
-	/* Disable annoying assertion message box. */
-	_CrtSetReportMode(_CRT_ASSERT, 0);
-#endif
-
-	/*
-	 * Name of this program, used to build root of our temp directory
-	 * tree.
-	 */
-	progname = p = argv[0];
-	if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL)
-	{
-		fprintf(stderr, "ERROR: Out of memory.");
-		exit(1);
-	}
-	strcpy(testprogdir, progname);
-	while (*p != '\0') {
-		/* Support \ or / dir separators for Windows compat. */
-		if (*p == '/' || *p == '\\')
-		{
-			progname = p + 1;
-			i = j;
-		}
-		++p;
-		j++;
-	}
-	testprogdir[i] = '\0';
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (testprogdir[0] != '/' && testprogdir[0] != '\\' &&
-	    !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') ||
-	       (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) &&
-		testprogdir[1] == ':' &&
-		(testprogdir[2] == '/' || testprogdir[2] == '\\')))
-#else
-	if (testprogdir[0] != '/')
-#endif
-	{
-		/* Fixup path for relative directories. */
-		if ((testprogdir = (char *)realloc(testprogdir,
-			strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		memmove(testprogdir + strlen(pwd) + 1, testprogdir,
-		    strlen(testprogdir) + 1);
-		memcpy(testprogdir, pwd, strlen(pwd));
-		testprogdir[strlen(pwd)] = '/';
-	}
-
-#ifdef PROGRAM
-	/* Get the target program from environment, if available. */
-	testprogfile = getenv(ENVBASE);
-#endif
-
-	if (getenv("TMPDIR") != NULL)
-		tmp = getenv("TMPDIR");
-	else if (getenv("TMP") != NULL)
-		tmp = getenv("TMP");
-	else if (getenv("TEMP") != NULL)
-		tmp = getenv("TEMP");
-	else if (getenv("TEMPDIR") != NULL)
-		tmp = getenv("TEMPDIR");
-	else
-		tmp = "/tmp";
-
-	/* Allow -d to be controlled through the environment. */
-	if (getenv(ENVBASE "_DEBUG") != NULL)
-		dump_on_failure = 1;
-
-	/* Allow -v to be controlled through the environment. */
-	if (getenv("_VERBOSITY_LEVEL") != NULL)
-	{
-		vlevel = getenv("_VERBOSITY_LEVEL");
-		verbosity = atoi(vlevel);
-		if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL)
-		{
-			/* Unsupported verbosity levels are silently ignored */
-			vlevel = NULL;
-			verbosity = VERBOSITY_PASSFAIL;
-		}
-	}
-
-	/* Get the directory holding test files from environment. */
-	refdir = getenv(ENVBASE "_TEST_FILES");
-
-	/*
-	 * Parse options, without using getopt(), which isn't available
-	 * on all platforms.
-	 */
-	++argv; /* Skip program name */
-	while (*argv != NULL) {
-		if (**argv != '-')
-			break;
-		p = *argv++;
-		++p; /* Skip '-' */
-		while (*p != '\0') {
-			option = *p++;
-			option_arg = NULL;
-			/* If 'opt' takes an argument, parse that. */
-			if (option == 'p' || option == 'r') {
-				if (*p != '\0')
-					option_arg = p;
-				else if (*argv == NULL) {
-					fprintf(stderr,
-					    "Option -%c requires argument.\n",
-					    option);
-					usage(progname);
-				} else
-					option_arg = *argv++;
-				p = ""; /* End of this option word. */
-			}
-
-			/* Now, handle the option. */
-			switch (option) {
-			case 'd':
-				dump_on_failure = 1;
-				break;
-			case 'k':
-				keep_temp_files = 1;
-				break;
-			case 'p':
-#ifdef PROGRAM
-				testprogfile = option_arg;
-#else
-				fprintf(stderr, "-p option not permitted\n");
-				usage(progname);
-#endif
-				break;
-			case 'q':
-				if (!vlevel)
-					verbosity--;
-				break;
-			case 'r':
-				refdir = option_arg;
-				break;
-			case 'u':
-				until_failure++;
-				break;
-			case 'v':
-				if (!vlevel)
-					verbosity++;
-				break;
-			default:
-				fprintf(stderr, "Unrecognized option '%c'\n",
-				    option);
-				usage(progname);
-			}
-		}
-	}
-
-	/*
-	 * Sanity-check that our options make sense.
-	 */
-#ifdef PROGRAM
-	if (testprogfile == NULL)
-	{
-		if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 +
-			strlen(PROGRAM) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		strcpy(tmp2, testprogdir);
-		strcat(tmp2, "/");
-		strcat(tmp2, PROGRAM);
-		testprogfile = tmp2;
-	}
-
-	{
-		char *testprg;
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		/* Command.com sometimes rejects '/' separators. */
-		testprg = strdup(testprogfile);
-		for (i = 0; testprg[i] != '\0'; i++) {
-			if (testprg[i] == '/')
-				testprg[i] = '\\';
-		}
-		testprogfile = testprg;
-#endif
-		/* Quote the name that gets put into shell command lines. */
-		testprg = malloc(strlen(testprogfile) + 3);
-		strcpy(testprg, "\"");
-		strcat(testprg, testprogfile);
-		strcat(testprg, "\"");
-		testprog = testprg;
-	}
-#endif
-
-#if !defined(_WIN32) && defined(SIGPIPE)
-	{   /* Ignore SIGPIPE signals */
-		struct sigaction sa;
-		sa.sa_handler = SIG_IGN;
-		sigemptyset(&sa.sa_mask);
-		sa.sa_flags = 0;
-		sigaction(SIGPIPE, &sa, NULL);
-	}
-#endif
-
-	/*
-	 * Create a temp directory for the following tests.
-	 * Include the time the tests started as part of the name,
-	 * to make it easier to track the results of multiple tests.
-	 */
-	now = time(NULL);
-	for (i = 0; ; i++) {
-		strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
-		    "%Y-%m-%dT%H.%M.%S",
-		    localtime(&now));
-		sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname,
-		    tmpdir_timestamp, i);
-		if (assertMakeDir(tmpdir,0755))
-			break;
-		if (i >= 999) {
-			fprintf(stderr,
-			    "ERROR: Unable to create temp directory %s\n",
-			    tmpdir);
-			exit(1);
-		}
-	}
-
-	/*
-	 * If the user didn't specify a directory for locating
-	 * reference files, try to find the reference files in
-	 * the "usual places."
-	 */
-	refdir = refdir_alloc = get_refdir(refdir);
-
-	/*
-	 * Banner with basic information.
-	 */
-	printf("\n");
-	printf("If tests fail or crash, details will be in:\n");
-	printf("   %s\n", tmpdir);
-	printf("\n");
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("Reference files will be read from: %s\n", refdir);
-#ifdef PROGRAM
-		printf("Running tests on: %s\n", testprog);
-#endif
-		printf("Exercising: ");
-		fflush(stdout);
-		printf("%s\n", EXTRA_VERSION);
-	} else {
-		printf("Running ");
-		fflush(stdout);
-	}
-
-	/*
-	 * Run some or all of the individual tests.
-	 */
-	saved_argv = argv;
-	do {
-		argv = saved_argv;
-		do {
-			int test_num;
-
-			test_num = get_test_set(test_set, limit, *argv, tests);
-			if (test_num < 0) {
-				printf("*** INVALID Test %s\n", *argv);
-				free(refdir_alloc);
-				free(testprogdir);
-				usage(progname);
-				return (1);
-			}
-			for (i = 0; i < test_num; i++) {
-				tests_run++;
-				if (test_run(test_set[i], tmpdir)) {
-					tests_failed++;
-					if (until_failure)
-						goto finish;
-				}
-			}
-			if (*argv != NULL)
-				argv++;
-		} while (*argv != NULL);
-	} while (until_failure);
-
-finish:
-	/* Must be freed after all tests run */
-	free(tmp2);
-	free(testprogdir);
-	free(pwd);
-
-	/*
-	 * Report summary statistics.
-	 */
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("\n");
-		printf("Totals:\n");
-		printf("  Tests run:         %8d\n", tests_run);
-		printf("  Tests failed:      %8d\n", tests_failed);
-		printf("  Assertions checked:%8d\n", assertions);
-		printf("  Assertions failed: %8d\n", failures);
-		printf("  Skips reported:    %8d\n", skips);
-	}
-	if (failures) {
-		printf("\n");
-		printf("Failing tests:\n");
-		for (i = 0; i < limit; ++i) {
-			if (tests[i].failures)
-				printf("  %d: %s (%d failures)\n", i,
-				    tests[i].name, tests[i].failures);
-		}
-		printf("\n");
-		printf("Details for failing tests: %s\n", tmpdir);
-		printf("\n");
-	} else {
-		if (verbosity == VERBOSITY_SUMMARY_ONLY)
-			printf("\n");
-		printf("%d tests passed, no failures\n", tests_run);
-	}
-
-	free(refdir_alloc);
-
-	/* If the final tmpdir is empty, we can remove it. */
-	/* This should be the usual case when all tests succeed. */
-	assertChdir("..");
-	rmdir(tmpdir);
-
-	return (tests_failed ? 1 : 0);
-}
diff --git a/libarchive/test/test.h b/libarchive/test/test.h
index a48c426..fd679f5 100644
--- a/libarchive/test/test.h
+++ b/libarchive/test/test.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2006 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -21,345 +21,16 @@
  * 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.
- *
- * $FreeBSD: head/lib/libarchive/test/test.h 201247 2009-12-30 05:59:21Z kientzle $
  */
 
 /* Every test program should #include "test.h" as the first thing. */
 
-/*
- * The goal of this file (and the matching test.c) is to
- * simplify the very repetitive test-*.c test programs.
- */
-#if defined(HAVE_CONFIG_H)
-/* Most POSIX platforms use the 'configure' script to build config.h */
-#include "config.h"
-#elif defined(__FreeBSD__)
-/* Building as part of FreeBSD system requires a pre-built config.h. */
-#include "config_freebsd.h"
-#elif defined(_WIN32) && !defined(__CYGWIN__)
-/* Win32 can't run the 'configure' script. */
-#include "config_windows.h"
-#else
-/* Warn if the library hasn't been (automatically or manually) configured. */
-#error Oops: No config.h and no pre-built configuration in test.h.
-#endif
+#define KNOWNREF	"test_compat_gtar_1.tar.uu"
+#define	ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */
+#undef	PROGRAM              /* Testing a library, not a program. */
+#define	LIBRARY	"libarchive"
+#define	EXTRA_DUMP(x)	archive_error_string((struct archive *)(x))
+#define	EXTRA_ERRNO(x)	archive_errno((struct archive *)(x))
+#define	EXTRA_VERSION	archive_version_details()
 
-#include <sys/types.h>  /* Windows requires this before sys/stat.h */
-#include <sys/stat.h>
-
-#if HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-#ifdef HAVE_DIRECT_H
-#include <direct.h>
-#define dirent direct
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_IO_H
-#include <io.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <wchar.h>
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-/*
- * System-specific tweaks.  We really want to minimize these
- * as much as possible, since they make it harder to understand
- * the mainline code.
- */
-
-/* Windows (including Visual Studio and MinGW but not Cygwin) */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#if !defined(__BORLANDC__)
-#undef chdir
-#define chdir _chdir
-#define strdup _strdup
-#endif
-#endif
-
-/* Visual Studio */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#define snprintf	sprintf_s
-#endif
-
-#if defined(__BORLANDC__)
-#pragma warn -8068	/* Constant out of range in comparison. */
-#endif
-
-/* Haiku OS and QNX */
-#if defined(__HAIKU__) || defined(__QNXNTO__)
-/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
-#include <stdint.h>
-#endif
-
-/* Get a real definition for __FBSDID if we can */
-#if HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-
-/* If not, define it so as to avoid dangling semicolons. */
-#ifndef __FBSDID
-#define	__FBSDID(a)     struct _undefined_hack
-#endif
-
-#ifndef O_BINARY
-#define	O_BINARY 0
-#endif
-
-/*
- * Redefine DEFINE_TEST for use in defining the test functions.
- */
-#undef DEFINE_TEST
-#define DEFINE_TEST(name) void name(void); void name(void)
-
-/* An implementation of the standard assert() macro */
-#define assert(e)   assertion_assert(__FILE__, __LINE__, (e), #e, NULL)
-/* chdir() and error if it fails */
-#define assertChdir(path)  \
-  assertion_chdir(__FILE__, __LINE__, path)
-/* Assert two integers are the same.  Reports value of each one if not. */
-#define assertEqualInt(v1,v2) \
-  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* Assert two strings are the same.  Reports value of each one if not. */
-#define assertEqualString(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
-#define assertEqualUTF8String(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
-/* As above, but v1 and v2 are wchar_t * */
-#define assertEqualWString(v1,v2)   \
-  assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* As above, but raw blocks of bytes. */
-#define assertEqualMem(v1, v2, l)	\
-  assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
-/* Assert that memory is full of a specified byte */
-#define assertMemoryFilledWith(v1, l, b)					\
-  assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL)
-/* Assert two files are the same. */
-#define assertEqualFile(f1, f2)	\
-  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
-/* Assert that a file is empty. */
-#define assertEmptyFile(pathname)	\
-  assertion_empty_file(__FILE__, __LINE__, (pathname))
-/* Assert that a file is not empty. */
-#define assertNonEmptyFile(pathname)		\
-  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
-#define assertFileAtime(pathname, sec, nsec)	\
-  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileAtimeRecent(pathname)	\
-  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
-#define assertFileBirthtime(pathname, sec, nsec)	\
-  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileBirthtimeRecent(pathname) \
-  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
-/* Assert that a file exists; supports printf-style arguments. */
-#define assertFileExists(pathname) \
-  assertion_file_exists(__FILE__, __LINE__, pathname)
-/* Assert that a file exists. */
-#define assertFileNotExists(pathname) \
-  assertion_file_not_exists(__FILE__, __LINE__, pathname)
-/* Assert that file contents match a string. */
-#define assertFileContents(data, data_size, pathname) \
-  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
-/* Verify that a file does not contain invalid strings */
-#define assertFileContainsNoInvalidStrings(pathname, strings) \
-  assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
-#define assertFileMtime(pathname, sec, nsec)	\
-  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileMtimeRecent(pathname) \
-  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
-#define assertFileNLinks(pathname, nlinks)  \
-  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)	\
-  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
-#define assertIsDir(pathname, mode)		\
-  assertion_is_dir(__FILE__, __LINE__, pathname, mode)
-#define assertIsHardlink(path1, path2)	\
-  assertion_is_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsNotHardlink(path1, path2)	\
-  assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsReg(pathname, mode)		\
-  assertion_is_reg(__FILE__, __LINE__, pathname, mode)
-#define assertIsSymlink(pathname, contents)	\
-  assertion_is_symlink(__FILE__, __LINE__, pathname, contents)
-/* Create a directory, report error if it fails. */
-#define assertMakeDir(dirname, mode)	\
-  assertion_make_dir(__FILE__, __LINE__, dirname, mode)
-#define assertMakeFile(path, mode, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents)
-#define assertMakeBinFile(path, mode, csize, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
-#define assertMakeHardlink(newfile, oldfile)	\
-  assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto)	\
-  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
-#define assertNodump(path)      \
-  assertion_nodump(__FILE__, __LINE__, path)
-#define assertUmask(mask)	\
-  assertion_umask(__FILE__, __LINE__, mask)
-#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
-  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
-
-/*
- * This would be simple with C99 variadic macros, but I don't want to
- * require that.  Instead, I insert a function call before each
- * skipping() call to pass the file and line information down.  Crude,
- * but effective.
- */
-#define skipping	\
-  skipping_setup(__FILE__, __LINE__);test_skipping
-
-/* Function declarations.  These are defined in test_utility.c. */
-void failure(const char *fmt, ...);
-int assertion_assert(const char *, int, int, const char *, void *);
-int assertion_chdir(const char *, int, const char *);
-int assertion_empty_file(const char *, int, const char *);
-int assertion_equal_file(const char *, int, const char *, const char *);
-int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
-int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
-int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *);
-int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
-int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
-int assertion_file_atime(const char *, int, const char *, long, long);
-int assertion_file_atime_recent(const char *, int, const char *);
-int assertion_file_birthtime(const char *, int, const char *, long, long);
-int assertion_file_birthtime_recent(const char *, int, const char *);
-int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
-int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
-int assertion_file_contents(const char *, int, const void *, int, const char *);
-int assertion_file_exists(const char *, int, const char *);
-int assertion_file_mode(const char *, int, const char *, int);
-int assertion_file_mtime(const char *, int, const char *, long, long);
-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_is_dir(const char *, int, const char *, int);
-int assertion_is_hardlink(const char *, int, const char *, const char *);
-int assertion_is_not_hardlink(const char *, int, const char *, const char *);
-int assertion_is_reg(const char *, int, const char *, int);
-int assertion_is_symlink(const char *, int, const char *, const char *);
-int assertion_make_dir(const char *, int, const char *, int);
-int assertion_make_file(const char *, int, const char *, int, int, const void *);
-int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
-int assertion_nodump(const char *, int, const char *);
-int assertion_non_empty_file(const char *, int, const char *);
-int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
-int assertion_umask(const char *, int, int);
-int assertion_utimes(const char *, int, const char *, long, long, long, long );
-
-void skipping_setup(const char *, int);
-void test_skipping(const char *fmt, ...);
-
-/* Like sprintf, then system() */
-int systemf(const char * fmt, ...);
-
-/* Delay until time() returns a value after this. */
-void sleepUntilAfter(time_t);
-
-/* Return true if this platform can create symlinks. */
-int canSymlink(void);
-
-/* Return true if this platform can run the "bzip2" program. */
-int canBzip2(void);
-
-/* Return true if this platform can run the "grzip" program. */
-int canGrzip(void);
-
-/* Return true if this platform can run the "gzip" program. */
-int canGzip(void);
-
-/* Return true if this platform can run the specified command. */
-int canRunCommand(const char *);
-
-/* Return true if this platform can run the "lrzip" program. */
-int canLrzip(void);
-
-/* Return true if this platform can run the "lz4" program. */
-int canLz4(void);
-
-/* Return true if this platform can run the "lzip" program. */
-int canLzip(void);
-
-/* Return true if this platform can run the "lzma" program. */
-int canLzma(void);
-
-/* Return true if this platform can run the "lzop" program. */
-int canLzop(void);
-
-/* Return true if this platform can run the "xz" program. */
-int canXz(void);
-
-/* Return true if this filesystem can handle nodump flags. */
-int canNodump(void);
-
-/* Return true if the file has large i-node number(>0xffffffff). */
-int is_LargeInode(const char *);
-
-/* Suck file into string allocated via malloc(). Call free() when done. */
-/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
-char *slurpfile(size_t *, const char *fmt, ...);
-
-/* Dump block of bytes to a file. */
-void dumpfile(const char *filename, void *, size_t);
-
-/* Extracts named reference file to the current directory. */
-void extract_reference_file(const char *);
-/* Copies named reference file to the current directory. */
-void copy_reference_file(const char *);
-
-/* Extracts a list of files to the current directory.
- * List must be NULL terminated.
- */
-void extract_reference_files(const char **);
-
-/* Subtract umask from mode */
-mode_t umasked(mode_t expected_mode);
-
-/* Path to working directory for current test */
-extern const char *testworkdir;
-
-/*
- * Special interfaces for libarchive test harness.
- */
-
-#include "archive.h"
-#include "archive_entry.h"
-
-/* Special customized read-from-memory interface. */
-int read_open_memory(struct archive *, const void *, size_t, size_t);
-/* _minimal version exercises a slightly different set of libarchive APIs. */
-int read_open_memory_minimal(struct archive *, const void *, size_t, size_t);
-/* _seek version produces a seekable file. */
-int read_open_memory_seek(struct archive *, const void *, size_t, size_t);
-
-/* Versions of above that accept an archive argument for additional info. */
-#define assertA(e)   assertion_assert(__FILE__, __LINE__, (e), #e, (a))
-#define assertEqualIntA(a,v1,v2)   \
-  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
-#define assertEqualStringA(a,v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0)
-
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
+#include "test_common.h"
diff --git a/libarchive/test/test_acl_freebsd_nfs4.c b/libarchive/test/test_acl_freebsd_nfs4.c
deleted file mode 100644
index 89861d6..0000000
--- a/libarchive/test/test_acl_freebsd_nfs4.c
+++ /dev/null
@@ -1,547 +0,0 @@
-/*-
- * Copyright (c) 2003-2010 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$");
-
-#if defined(__FreeBSD__) && __FreeBSD__ >= 8
-#define _ACL_PRIVATE
-#include <sys/acl.h>
-
-struct myacl_t {
-	int type;
-	int permset;
-	int tag;
-	int qual; /* GID or UID of user/group, depending on tag. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct myacl_t acls_reg[] = {
-	/* For this test, we need the file owner to be able to read and write the ACL. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
-
-	/* An entry for each type. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER, 109, "user109" },
-
-	/* An entry for each permission. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER, 112, "user112" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA,
-	  ARCHIVE_ENTRY_ACL_USER, 113, "user113" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA,
-	  ARCHIVE_ENTRY_ACL_USER, 115, "user115" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA,
-	  ARCHIVE_ENTRY_ACL_USER, 117, "user117" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
-	  ARCHIVE_ENTRY_ACL_USER, 119, "user119" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
-	  ARCHIVE_ENTRY_ACL_USER, 120, "user120" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
-	  ARCHIVE_ENTRY_ACL_USER, 122, "user122" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
-	  ARCHIVE_ENTRY_ACL_USER, 123, "user123" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
-	  ARCHIVE_ENTRY_ACL_USER, 124, "user124" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
-	  ARCHIVE_ENTRY_ACL_USER, 125, "user125" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
-	  ARCHIVE_ENTRY_ACL_USER, 126, "user126" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
-	  ARCHIVE_ENTRY_ACL_USER, 127, "user127" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
-	  ARCHIVE_ENTRY_ACL_USER, 128, "user128" },
-
-	/* One entry for each qualifier. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER, 135, "user135" },
-//	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-//	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
-};
-
-
-static struct myacl_t acls_dir[] = {
-	/* For this test, we need to be able to read and write the ACL. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
-
-	/* An entry for each type. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_USER, 102, "user102" },
-
-	/* An entry for each permission. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_USER, 201, "user201" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE,
-	  ARCHIVE_ENTRY_ACL_USER, 202, "user202" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
-	  ARCHIVE_ENTRY_ACL_USER, 203, "user203" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
-	  ARCHIVE_ENTRY_ACL_USER, 204, "user204" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
-	  ARCHIVE_ENTRY_ACL_USER, 205, "user205" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD,
-	  ARCHIVE_ENTRY_ACL_USER, 206, "user206" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
-	  ARCHIVE_ENTRY_ACL_USER, 207, "user207" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
-	  ARCHIVE_ENTRY_ACL_USER, 208, "user208" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
-	  ARCHIVE_ENTRY_ACL_USER, 209, "user209" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
-	  ARCHIVE_ENTRY_ACL_USER, 210, "user210" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
-	  ARCHIVE_ENTRY_ACL_USER, 211, "user211" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
-	  ARCHIVE_ENTRY_ACL_USER, 212, "user212" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
-	  ARCHIVE_ENTRY_ACL_USER, 213, "user213" },
-
-	/* One entry with each inheritance value. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
-	  ARCHIVE_ENTRY_ACL_USER, 301, "user301" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
-	  ARCHIVE_ENTRY_ACL_USER, 302, "user302" },
-#if 0
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
-	  ARCHIVE_ENTRY_ACL_USER, 303, "user303" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
-	  ARCHIVE_ENTRY_ACL_USER, 304, "user304" },
-#endif
-
-#if 0
-	/* FreeBSD does not support audit entries. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
-	  ARCHIVE_ENTRY_ACL_USER, 401, "user401" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
-	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
-	  ARCHIVE_ENTRY_ACL_USER, 402, "user402" },
-#endif
-
-	/* One entry for each qualifier. */
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_USER, 501, "user501" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
-	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
-};
-
-static void
-set_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end)
-{
-	int i;
-
-	archive_entry_acl_clear(ae);
-	if (start > 0) {
-		assertEqualInt(ARCHIVE_OK,
-			archive_entry_acl_add_entry(ae,
-			    acls[0].type, acls[0].permset, acls[0].tag,
-			    acls[0].qual, acls[0].name));
-	}
-	for (i = start; i < end; i++) {
-		assertEqualInt(ARCHIVE_OK,
-		    archive_entry_acl_add_entry(ae,
-			acls[i].type, acls[i].permset, acls[i].tag,
-			acls[i].qual, acls[i].name));
-	}
-}
-
-static int
-acl_permset_to_bitmap(acl_permset_t opaque_ps)
-{
-	static struct { int machine; int portable; } perms[] = {
-		{ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
-		{ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE},
-		{ACL_READ, ARCHIVE_ENTRY_ACL_READ},
-		{ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
-		{ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
-		{ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
-		{ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
-		{ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
-		{ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
-		{ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
-		{ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
-		{ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
-		{ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
-		{ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
-		{ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
-		{ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
-		{ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
-		{ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
-		{ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
-	};
-	int i, permset = 0;
-
-	for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
-		if (acl_get_perm_np(opaque_ps, perms[i].machine))
-			permset |= perms[i].portable;
-	return permset;
-}
-
-static int
-acl_flagset_to_bitmap(acl_flagset_t opaque_fs)
-{
-	static struct { int machine; int portable; } flags[] = {
-		{ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
-		{ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
-		{ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
-		{ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
-	};
-	int i, flagset = 0;
-
-	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
-		if (acl_get_flag_np(opaque_fs, flags[i].machine))
-			flagset |= flags[i].portable;
-	return flagset;
-}
-
-static int
-acl_match(acl_entry_t aclent, struct myacl_t *myacl)
-{
-	gid_t g, *gp;
-	uid_t u, *up;
-	acl_tag_t tag_type;
-	acl_permset_t opaque_ps;
-	acl_flagset_t opaque_fs;
-	int perms;
-
-	acl_get_tag_type(aclent, &tag_type);
-
-	/* translate the silly opaque permset to a bitmap */
-	acl_get_permset(aclent, &opaque_ps);
-	acl_get_flagset_np(aclent, &opaque_fs);
-	perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs);
-	if (perms != myacl->permset)
-		return (0);
-
-	switch (tag_type) {
-	case ACL_USER_OBJ:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
-		break;
-	case ACL_USER:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
-			return (0);
-		up = acl_get_qualifier(aclent);
-		u = *up;
-		acl_free(up);
-		if ((uid_t)myacl->qual != u)
-			return (0);
-		break;
-	case ACL_GROUP_OBJ:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
-		break;
-	case ACL_GROUP:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
-			return (0);
-		gp = acl_get_qualifier(aclent);
-		g = *gp;
-		acl_free(gp);
-		if ((gid_t)myacl->qual != g)
-			return (0);
-		break;
-	case ACL_MASK:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
-		break;
-	case ACL_EVERYONE:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0);
-		break;
-	}
-	return (1);
-}
-
-static void
-compare_acls(acl_t acl, struct myacl_t *myacls, const char *filename, int start, int end)
-{
-	int *marker;
-	int entry_id = ACL_FIRST_ENTRY;
-	int matched;
-	int i, n;
-	acl_entry_t acl_entry;
-
-	n = end - start;
-	marker = malloc(sizeof(marker[0]) * (n + 1));
-	for (i = 0; i < n; i++)
-		marker[i] = i + start;
-	/* Always include the first ACE. */
-	if (start > 0) {
-	  marker[n] = 0;
-	  ++n;
-	}
-
-	/*
-	 * Iterate over acls in system acl object, try to match each
-	 * one with an item in the myacls array.
-	 */
-	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
-		/* After the first time... */
-		entry_id = ACL_NEXT_ENTRY;
-
-		/* Search for a matching entry (tag and qualifier) */
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (acl_match(acl_entry, &myacls[marker[i]])) {
-				/* We found a match; remove it. */
-				marker[i] = marker[n - 1];
-				n--;
-				matched = 1;
-			}
-		}
-
-		failure("ACL entry on file %s that shouldn't be there", filename);
-		assert(matched == 1);
-	}
-
-	/* Dump entries in the myacls array that weren't in the system acl. */
-	for (i = 0; i < n; ++i) {
-		failure(" ACL entry %d missing from %s: "
-		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
-		    marker[i], filename,
-		    myacls[marker[i]].type, myacls[marker[i]].permset,
-		    myacls[marker[i]].tag, myacls[marker[i]].qual,
-		    myacls[marker[i]].name);
-		assert(0); /* Record this as a failure. */
-	}
-	free(marker);
-}
-
-static void
-compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end)
-{
-	int *marker;
-	int matched;
-	int i, n;
-	int type, permset, tag, qual;
-	const char *name;
-
-	/* Count ACL entries in myacls array and allocate an indirect array. */
-	n = end - start;
-	marker = malloc(sizeof(marker[0]) * (n + 1));
-	for (i = 0; i < n; i++)
-		marker[i] = i + start;
-	/* Always include the first ACE. */
-	if (start > 0) {
-	  marker[n] = 0;
-	  ++n;
-	}
-
-	/*
-	 * Iterate over acls in entry, try to match each
-	 * one with an item in the myacls array.
-	 */
-	assertEqualInt(n, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
-	while (ARCHIVE_OK == archive_entry_acl_next(ae,
-	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) {
-
-		/* Search for a matching entry (tag and qualifier) */
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (tag == myacls[marker[i]].tag
-			    && qual == myacls[marker[i]].qual
-			    && permset == myacls[marker[i]].permset
-			    && type == myacls[marker[i]].type) {
-				/* We found a match; remove it. */
-				marker[i] = marker[n - 1];
-				n--;
-				matched = 1;
-			}
-		}
-
-		failure("ACL entry on file that shouldn't be there: "
-			"type=%d,permset=%x,tag=%d,qual=%d",
-			type,permset,tag,qual);
-		assert(matched == 1);
-	}
-
-	/* Dump entries in the myacls array that weren't in the system acl. */
-	for (i = 0; i < n; ++i) {
-		failure(" ACL entry %d missing from %s: "
-		    "type=%d,permset=%x,tag=%d,qual=%d,name=``%s''\n",
-		    marker[i], filename,
-		    myacls[marker[i]].type, myacls[marker[i]].permset,
-		    myacls[marker[i]].tag, myacls[marker[i]].qual,
-		    myacls[marker[i]].name);
-		assert(0); /* Record this as a failure. */
-	}
-	free(marker);
-}
-#endif
-
-/*
- * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
- */
-
-DEFINE_TEST(test_acl_freebsd_nfs4)
-{
-#if !defined(__FreeBSD__)
-	skipping("FreeBSD-specific NFS4 ACL restore test");
-#elif __FreeBSD__ < 8
-	skipping("NFS4 ACLs supported only on FreeBSD 8.0 and later");
-#else
-	char buff[64];
-	struct stat st;
-	struct archive *a;
-	struct archive_entry *ae;
-	int i, n;
-	acl_t acl;
-
-	/*
-	 * First, do a quick manual set/read of ACL data to
-	 * verify that the local filesystem does support ACLs.
-	 * If it doesn't, we'll simply skip the remaining tests.
-	 */
-	acl = acl_from_text("owner@:rwxp::allow,group@:rwp:f:allow");
-	assert((void *)acl != NULL);
-	/* Create a test dir and try to set an ACL on it. */
-	if (!assertMakeDir("pretest", 0755)) {
-		acl_free(acl);
-		return;
-	}
-
-	n = acl_set_file("pretest", ACL_TYPE_NFS4, acl);
-	acl_free(acl);
-	if (n != 0 && errno == EOPNOTSUPP) {
-		skipping("NFS4 ACL tests require that NFS4 ACLs"
-		    " be enabled on the filesystem");
-		return;
-	}
-	if (n != 0 && errno == EINVAL) {
-		skipping("This filesystem does not support NFS4 ACLs");
-		return;
-	}
-	failure("acl_set_file(): errno = %d (%s)",
-	    errno, strerror(errno));
-	assertEqualInt(0, n);
-
-	/* Create a write-to-disk object. */
-	assert(NULL != (a = archive_write_disk_new()));
-	archive_write_disk_set_options(a,
-	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
-
-	/* Populate an archive entry with some metadata, including ACL info */
-	ae = archive_entry_new();
-	assert(ae != NULL);
-	archive_entry_set_pathname(ae, "testall");
-	archive_entry_set_filetype(ae, AE_IFREG);
-	archive_entry_set_perm(ae, 0654);
-	archive_entry_set_mtime(ae, 123456, 7890);
-	archive_entry_set_size(ae, 0);
-	set_acls(ae, acls_reg, 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
-
-	/* Write the entry to disk, including ACLs. */
-	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
-
-	/* Likewise for a dir. */
-	archive_entry_set_pathname(ae, "dirall");
-	archive_entry_set_filetype(ae, AE_IFDIR);
-	archive_entry_set_perm(ae, 0654);
-	archive_entry_set_mtime(ae, 123456, 7890);
-	set_acls(ae, acls_dir, 0, (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
-	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
-
-	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
-	  sprintf(buff, "dir%d", i);
-	  archive_entry_set_pathname(ae, buff);
-	  archive_entry_set_filetype(ae, AE_IFDIR);
-	  archive_entry_set_perm(ae, 0654);
-	  archive_entry_set_mtime(ae, 123456 + i, 7891 + i);
-	  set_acls(ae, acls_dir, i, i + 1);
-	  assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
-	}
-
-	archive_entry_free(ae);
-
-	/* Close the archive. */
-	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
-	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
-
-	/* Verify the data on disk. */
-	assertEqualInt(0, stat("testall", &st));
-	assertEqualInt(st.st_mtime, 123456);
-	acl = acl_get_file("testall", ACL_TYPE_NFS4);
-	assert(acl != (acl_t)NULL);
-	compare_acls(acl, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
-	acl_free(acl);
-
-	/* Verify single-permission dirs on disk. */
-	for (i = 0; i < (int)(sizeof(acls_dir)/sizeof(acls_dir[0])); ++i) {
-	  sprintf(buff, "dir%d", i);
-	  assertEqualInt(0, stat(buff, &st));
-	  assertEqualInt(st.st_mtime, 123456 + i);
-	  acl = acl_get_file(buff, ACL_TYPE_NFS4);
-	  assert(acl != (acl_t)NULL);
-	  compare_acls(acl, acls_dir, buff, i, i + 1);
-	  acl_free(acl);
-	}
-
-	/* Verify "dirall" on disk. */
-	assertEqualInt(0, stat("dirall", &st));
-	assertEqualInt(st.st_mtime, 123456);
-	acl = acl_get_file("dirall", ACL_TYPE_NFS4);
-	assert(acl != (acl_t)NULL);
-	compare_acls(acl, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
-	acl_free(acl);
-
-	/* Read and compare ACL via archive_read_disk */
-	a = archive_read_disk_new();
-	assert(a != NULL);
-	ae = archive_entry_new();
-	assert(ae != NULL);
-	archive_entry_set_pathname(ae, "testall");
-	assertEqualInt(ARCHIVE_OK,
-		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
-	compare_entry_acls(ae, acls_reg, "testall", 0, (int)(sizeof(acls_reg)/sizeof(acls_reg[0])));
-	archive_entry_free(ae);
-	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
-
-	/* Read and compare ACL via archive_read_disk */
-	a = archive_read_disk_new();
-	assert(a != NULL);
-	ae = archive_entry_new();
-	assert(ae != NULL);
-	archive_entry_set_pathname(ae, "dirall");
-	assertEqualInt(ARCHIVE_OK,
-		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
-	compare_entry_acls(ae, acls_dir, "dirall", 0,  (int)(sizeof(acls_dir)/sizeof(acls_dir[0])));
-	archive_entry_free(ae);
-	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
-#endif
-}
diff --git a/libarchive/test/test_acl_freebsd_posix1e.c b/libarchive/test/test_acl_freebsd_posix1e.c
deleted file mode 100644
index 2eb0a00..0000000
--- a/libarchive/test/test_acl_freebsd_posix1e.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*-
- * Copyright (c) 2003-2008 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: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $");
-
-#if defined(__FreeBSD__) && __FreeBSD__ > 4
-#include <sys/acl.h>
-
-struct myacl_t {
-	int type;  /* Type of ACL: "access" or "default" */
-	int permset; /* Permissions for this class of users. */
-	int tag; /* Owner, User, Owning group, group, other, etc. */
-	int qual; /* GID or UID of user/group, depending on tag. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct myacl_t acls2[] = {
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
-	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
-	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_MASK, -1, "" },
-	{ 0, 0, 0, 0, NULL }
-};
-
-static void
-set_acls(struct archive_entry *ae, struct myacl_t *acls)
-{
-	int i;
-
-	archive_entry_acl_clear(ae);
-	for (i = 0; acls[i].name != NULL; i++) {
-		archive_entry_acl_add_entry(ae,
-		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-		    acls[i].name);
-	}
-}
-
-static int
-acl_entry_get_perm(acl_entry_t aclent) {
-	int permset = 0;
-	acl_permset_t opaque_ps;
-
-	/* translate the silly opaque permset to a bitmap */
-	acl_get_permset(aclent, &opaque_ps);
-	if (acl_get_perm_np(opaque_ps, ACL_EXECUTE))
-		permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
-	if (acl_get_perm_np(opaque_ps, ACL_WRITE))
-		permset |= ARCHIVE_ENTRY_ACL_WRITE;
-	if (acl_get_perm_np(opaque_ps, ACL_READ))
-		permset |= ARCHIVE_ENTRY_ACL_READ;
-	return permset;
-}
-
-#if 0
-static int
-acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) {
-	int entry_id = ACL_FIRST_ENTRY;
-	acl_entry_t acl_entry;
-	acl_tag_t acl_tag_type;
-	
-	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
-		/* After the first time... */
-		entry_id = ACL_NEXT_ENTRY;
-
-		/* If this matches, return perm mask */
-		acl_get_tag_type(acl_entry, &acl_tag_type);
-		if (acl_tag_type == requested_tag_type) {
-			switch (acl_tag_type) {
-			case ACL_USER_OBJ:
-				if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) {
-					return acl_entry_get_perm(acl_entry);
-				}
-				break;
-			case ACL_GROUP_OBJ:
-				if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) {
-					return acl_entry_get_perm(acl_entry);
-				}
-				break;
-			case ACL_USER:
-			case ACL_GROUP:
-			case ACL_OTHER:
-				return acl_entry_get_perm(acl_entry);
-			default:
-				failure("Unexpected ACL tag type");
-				assert(0);
-			}
-		}
-
-
-	}
-	return -1;
-}
-#endif
-
-static int
-acl_match(acl_entry_t aclent, struct myacl_t *myacl)
-{
-	gid_t g, *gp;
-	uid_t u, *up;
-	acl_tag_t tag_type;
-
-	if (myacl->permset != acl_entry_get_perm(aclent))
-		return (0);
-
-	acl_get_tag_type(aclent, &tag_type);
-	switch (tag_type) {
-	case ACL_USER_OBJ:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
-		break;
-	case ACL_USER:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
-			return (0);
-		up = acl_get_qualifier(aclent);
-		u = *up;
-		acl_free(up);
-		if ((uid_t)myacl->qual != u)
-			return (0);
-		break;
-	case ACL_GROUP_OBJ:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
-		break;
-	case ACL_GROUP:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
-			return (0);
-		gp = acl_get_qualifier(aclent);
-		g = *gp;
-		acl_free(gp);
-		if ((gid_t)myacl->qual != g)
-			return (0);
-		break;
-	case ACL_MASK:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
-		break;
-	case ACL_OTHER:
-		if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0);
-		break;
-	}
-	return (1);
-}
-
-static void
-compare_acls(acl_t acl, struct myacl_t *myacls)
-{
-	int *marker;
-	int entry_id = ACL_FIRST_ENTRY;
-	int matched;
-	int i, n;
-	acl_entry_t acl_entry;
-
-	/* Count ACL entries in myacls array and allocate an indirect array. */
-	for (n = 0; myacls[n].name != NULL; ++n)
-		continue;
-	if (n) {
-		marker = malloc(sizeof(marker[0]) * n);
-		if (marker == NULL)
-			return;
-		for (i = 0; i < n; i++)
-			marker[i] = i;
-	} else
-		marker = NULL;
-
-	/*
-	 * Iterate over acls in system acl object, try to match each
-	 * one with an item in the myacls array.
-	 */
-	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
-		/* After the first time... */
-		entry_id = ACL_NEXT_ENTRY;
-
-		/* Search for a matching entry (tag and qualifier) */
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (acl_match(acl_entry, &myacls[marker[i]])) {
-				/* We found a match; remove it. */
-				marker[i] = marker[n - 1];
-				n--;
-				matched = 1;
-			}
-		}
-
-		/* TODO: Print out more details in this case. */
-		failure("ACL entry on file that shouldn't be there");
-		assert(matched == 1);
-	}
-
-	/* Dump entries in the myacls array that weren't in the system acl. */
-	for (i = 0; i < n; ++i) {
-		failure(" ACL entry missing from file: "
-		    "type=%d,permset=%d,tag=%d,qual=%d,name=``%s''\n",
-		    myacls[marker[i]].type, myacls[marker[i]].permset,
-		    myacls[marker[i]].tag, myacls[marker[i]].qual,
-		    myacls[marker[i]].name);
-		assert(0); /* Record this as a failure. */
-	}
-	free(marker);
-}
-
-#endif
-
-
-/*
- * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
- */
-
-DEFINE_TEST(test_acl_freebsd_posix1e_restore)
-{
-#if !defined(__FreeBSD__)
-	skipping("FreeBSD-specific ACL restore test");
-#elif __FreeBSD__ < 5
-	skipping("ACL restore supported only on FreeBSD 5.0 and later");
-#else
-	struct stat st;
-	struct archive *a;
-	struct archive_entry *ae;
-	int n, fd;
-	acl_t acl;
-
-	/*
-	 * First, do a quick manual set/read of ACL data to
-	 * verify that the local filesystem does support ACLs.
-	 * If it doesn't, we'll simply skip the remaining tests.
-	 */
-	acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx");
-	assert((void *)acl != NULL);
-	/* Create a test file and try to set an ACL on it. */
-	fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777);
-	failure("Could not create test file?!");
-	if (!assert(fd >= 0)) {
-		acl_free(acl);
-		return;
-	}
-
-	n = acl_set_fd(fd, acl);
-	acl_free(acl);
-	if (n != 0 && errno == EOPNOTSUPP) {
-		close(fd);
-		skipping("ACL tests require that ACL support be enabled on the filesystem");
-		return;
-	}
-	if (n != 0 && errno == EINVAL) {
-		close(fd);
-		skipping("This filesystem does not support POSIX.1e ACLs");
-		return;
-	}
-	failure("acl_set_fd(): errno = %d (%s)",
-	    errno, strerror(errno));
-	assertEqualInt(0, n);
-	close(fd);
-
-	/* Create a write-to-disk object. */
-	assert(NULL != (a = archive_write_disk_new()));
-	archive_write_disk_set_options(a,
-	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
-
-	/* Populate an archive entry with some metadata, including ACL info */
-	ae = archive_entry_new();
-	assert(ae != NULL);
-	archive_entry_set_pathname(ae, "test0");
-	archive_entry_set_mtime(ae, 123456, 7890);
-	archive_entry_set_size(ae, 0);
-	set_acls(ae, acls2);
-	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
-	archive_entry_free(ae);
-
-	/* Close the archive. */
-	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
-	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
-
-	/* Verify the data on disk. */
-	assertEqualInt(0, stat("test0", &st));
-	assertEqualInt(st.st_mtime, 123456);
-	acl = acl_get_file("test0", ACL_TYPE_ACCESS);
-	assert(acl != (acl_t)NULL);
-	compare_acls(acl, acls2);
-	acl_free(acl);
-#endif
-}
-
-/*
- * Verify ACL reaed-from-disk.  This test is FreeBSD-specific.
- */
-DEFINE_TEST(test_acl_freebsd_posix1e_read)
-{
-#if !defined(__FreeBSD__)
-	skipping("FreeBSD-specific ACL read test");
-#elif __FreeBSD__ < 5
-	skipping("ACL read supported only on FreeBSD 5.0 and later");
-#else
-	struct archive *a;
-	struct archive_entry *ae;
-	int n, fd;
-	const char *acl1_text, *acl2_text;
-	acl_t acl1, acl2;
-
-	/*
-	 * Manually construct a directory and two files with
-	 * different ACLs.  This also serves to verify that ACLs
-	 * are supported on the local filesystem.
-	 */
-
-	/* Create a test file f1 with acl1 */
-	acl1_text = "user::rwx,group::rwx,other::rwx,user:1:rw-,group:15:r-x,mask::rwx";
-	acl1 = acl_from_text(acl1_text);
-	assert((void *)acl1 != NULL);
-	fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
-	failure("Could not create test file?!");
-	if (!assert(fd >= 0)) {
-		acl_free(acl1);
-		return;
-	}
-	n = acl_set_fd(fd, acl1);
-	acl_free(acl1);
-	if (n != 0 && errno == EOPNOTSUPP) {
-		close(fd);
-		skipping("ACL tests require that ACL support be enabled on the filesystem");
-		return;
-	}
-	if (n != 0 && errno == EINVAL) {
-		close(fd);
-		skipping("This filesystem does not support POSIX.1e ACLs");
-		return;
-	}
-	failure("acl_set_fd(): errno = %d (%s)",
-	    errno, strerror(errno));
-	assertEqualInt(0, n);
-	close(fd);
-
-	assertMakeDir("d", 0700);
-
-	/*
-	 * Create file d/f1 with acl2
-	 *
-	 * This differs from acl1 in the u:1: and g:15: permissions.
-	 *
-	 * This file deliberately has the same name but a different ACL.
-	 * Github Issue #777 explains how libarchive's directory traversal
-	 * did not always correctly enter directories before attempting
-	 * to read ACLs, resulting in reading the ACL from a like-named
-	 * file in the wrong directory.
-	 */
-	acl2_text = "user::rwx,group::rwx,other::---,user:1:r--,group:15:r--,mask::rwx";
-	acl2 = acl_from_text(acl2_text);
-	assert((void *)acl2 != NULL);
-	fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
-	failure("Could not create test file?!");
-	if (!assert(fd >= 0)) {
-		acl_free(acl2);
-		return;
-	}
-	n = acl_set_fd(fd, acl2);
-	acl_free(acl2);
-	if (n != 0 && errno == EOPNOTSUPP) {
-		close(fd);
-		skipping("ACL tests require that ACL support be enabled on the filesystem");
-		return;
-	}
-	if (n != 0 && errno == EINVAL) {
-		close(fd);
-		skipping("This filesystem does not support POSIX.1e ACLs");
-		return;
-	}
-	failure("acl_set_fd(): errno = %d (%s)",
-	    errno, strerror(errno));
-	assertEqualInt(0, n);
-	close(fd);
-
-	/* Create a read-from-disk object. */
-	assert(NULL != (a = archive_read_disk_new()));
-	assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "."));
-	assert(NULL != (ae = archive_entry_new()));
-
-	/* Walk the dir until we see both of the files */
-	while (ARCHIVE_OK == archive_read_next_header2(a, ae)) {
-		archive_read_disk_descend(a);
-		if (strcmp(archive_entry_pathname(ae), "./f1") == 0) {
-			assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl1_text);
-			    
-		} else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) {
-			assertEqualString(archive_entry_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), acl2_text);
-		}
-	}
-
-	archive_free(a);
-#endif
-}
diff --git a/libarchive/test/test_acl_nfs4.c b/libarchive/test/test_acl_nfs4.c
index c8f5937..fdc0191 100644
--- a/libarchive/test/test_acl_nfs4.c
+++ b/libarchive/test/test_acl_nfs4.c
@@ -33,15 +33,7 @@
  * filesystems support ACLs or not.
  */
 
-struct acl_t {
-	int type;  /* Type of entry: "allow" or "deny" */
-	int permset; /* Permissions for this class of users. */
-	int tag; /* Owner, User, Owning group, group, everyone, etc. */
-	int qual; /* GID or UID of user/group, depending on tag. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_READ_DATA,
@@ -52,7 +44,7 @@
 	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
 	/* An entry for each type. */
 	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0,
 	  ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
@@ -136,7 +128,7 @@
  * Entries that should be rejected when we attempt to set them
  * on an ACL that already has NFS4 entries.
  */
-static struct acl_t acls_bad[] = {
+static struct archive_test_acl_t acls_bad[] = {
 	/* POSIX.1e ACL types */
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER, 78, "" },
@@ -156,95 +148,6 @@
 	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-	int i;
-
-	archive_entry_acl_clear(ae);
-	for (i = 0; i < n; i++) {
-		failure("type=%d, permset=%d, tag=%d, qual=%d name=%s",
-		    acls[i].type, acls[i].permset, acls[i].tag,
-		    acls[i].qual, acls[i].name);
-		assertEqualInt(ARCHIVE_OK,
-		    archive_entry_acl_add_entry(ae,
-			acls[i].type, acls[i].permset, acls[i].tag,
-			acls[i].qual, acls[i].name));
-	}
-}
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual,
-    const char *name)
-{
-	if (acl == NULL)
-		return (0);
-	if (type != acl->type)
-		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_EVERYONE)
-		return (1);
-	if (qual != acl->qual)
-		return (0);
-	if (name == NULL) {
-		if (acl->name == NULL || acl->name[0] == '\0')
-			return (1);
-		return (0);
-	}
-	if (acl->name == NULL) {
-		if (name[0] == '\0')
-			return (1);
-		return (0);
-	}
-	return (0 == strcmp(name, acl->name));
-}
-
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-	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,
-			 ARCHIVE_ENTRY_ACL_TYPE_NFS4,
-			 &type, &permset, &tag, &qual, &name))) {
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (acl_match(&acls[marker[i]], type, permset,
-				tag, qual, name)) {
-				/* We found a match; remove it. */
-				marker[i] = marker[n - 1];
-				n--;
-				matched = 1;
-			}
-		}
-		failure("Could not find match for ACL "
-		    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-		    type, permset, tag, qual, name);
-		assertEqualInt(1, matched);
-	}
-	assertEqualInt(ARCHIVE_EOF, r);
-	failure("Could not find match for ACL "
-	    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-	    acls[marker[0]].type, acls[marker[0]].permset,
-	    acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-	assertEqualInt(0, n); /* Number of ACLs not matched should == 0 */
-	free(marker);
-}
-
 DEFINE_TEST(test_acl_nfs4)
 {
 	struct archive_entry *ae;
@@ -256,22 +159,31 @@
         archive_entry_set_mode(ae, S_IFREG | 0777);
 
 	/* Store and read back some basic ACL entries. */
-	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+
+	/* Check that entry contains only NFSv4 types */
+	assert((archive_entry_acl_types(ae) &
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0);
+	assert((archive_entry_acl_types(ae) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0);
+
 	assertEqualInt(4,
 	    archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
-	compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
 	/* A more extensive set of ACLs. */
-	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
 	assertEqualInt(32,
 	    archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
-	compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
 	/*
 	 * Check that clearing ACLs gets rid of them all by repeating
 	 * the first test.
 	 */
-	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
 	failure("Basic ACLs shouldn't be stored as extended ACLs");
 	assertEqualInt(4,
 	    archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_NFS4));
@@ -280,9 +192,9 @@
 	 * Different types of malformed ACL entries that should
 	 * fail when added to existing NFS4 ACLs.
 	 */
-	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
 	for (i = 0; i < (int)(sizeof(acls_bad)/sizeof(acls_bad[0])); ++i) {
-		struct acl_t *p = &acls_bad[i];
+		struct archive_test_acl_t *p = &acls_bad[i];
 		failure("Malformed ACL test #%d", i);
 		assertEqualInt(ARCHIVE_FAILED,
 		    archive_entry_acl_add_entry(ae,
diff --git a/libarchive/test/test_acl_pax.c b/libarchive/test/test_acl_pax.c
index 5150b11..8566f55 100644
--- a/libarchive/test/test_acl_pax.c
+++ b/libarchive/test/test_acl_pax.c
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,11 +24,11 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_pax.c 201247 2009-12-30 05:59:21Z kientzle $");
+__FBSDID("$FreeBSD$");
 
 /*
  * Exercise the system-independent portion of the ACL support.
- * Check that pax archive can save and restore ACL data.
+ * Check that pax archive can save and restore POSIX.1e ACL data.
  *
  * This should work on all systems, regardless of whether local
  * filesystems support ACLs or not.
@@ -35,15 +36,7 @@
 
 static unsigned char buff[16384];
 
-struct acl_t {
-	int type;  /* Type of ACL: "access" or "default" */
-	int permset; /* Permissions for this class of users. */
-	int tag; /* Owner, User, Owning group, group, other, etc. */
-	int qual; /* GID or UID of user/group, depending on tag. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
+static struct archive_test_acl_t acls0[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -52,7 +45,7 @@
 	  ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
 };
 
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -63,7 +56,7 @@
 	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -78,101 +71,149 @@
 	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-	int i;
+static struct archive_test_acl_t acls3[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-	archive_entry_acl_clear(ae);
-	for (i = 0; i < n; i++) {
-		archive_entry_acl_add_entry(ae,
-		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-		    acls[i].name);
-	}
-}
+static struct archive_test_acl_t acls4[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
-{
-	if (type != acl->type)
-		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 (qual != acl->qual)
-		return (0);
-	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 struct archive_test_acl_t acls5[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALARM,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
 
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
-{
-	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,
-			 ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-			 &type, &permset, &tag, &qual, &name))) {
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (acl_match(&acls[marker[i]], type, permset,
-				tag, qual, 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");
-			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");
-			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");
-			failure("OTHER permset (%02o) != other mode (%02o)",
-			    permset, mode & 07);
-			assert((permset << 0) == (mode & 0007));
-		} else {
-			failure("Could not find match for ACL "
-			    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-			    type, permset, tag, qual, 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,qual=%d,name=``%s'')",
-	    acls[marker[0]].type, acls[marker[0]].permset,
-	    acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-	assert(n == 0); /* Number of ACLs not matched should == 0 */
-	free(marker);
-}
-
-DEFINE_TEST(test_acl_pax)
+DEFINE_TEST(test_acl_pax_posix1e)
 {
 	struct archive *a;
 	struct archive_entry *ae;
@@ -197,23 +238,22 @@
         archive_entry_set_mode(ae, S_IFREG | 0777);
 
 	/* Basic owner/owning group should just update mode bits. */
-	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
 	assertA(0 == archive_write_header(a, ae));
 
 	/* With any extended ACL entry, we should read back a full set. */
-	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
 	assertA(0 == archive_write_header(a, ae));
 
-
 	/* A more extensive set of ACLs. */
-	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
 	assertA(0 == archive_write_header(a, ae));
 
 	/*
 	 * Check that clearing ACLs gets rid of them all by repeating
 	 * the first test.
 	 */
-	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
 	assertA(0 == archive_write_header(a, ae));
 	archive_entry_free(ae);
 
@@ -227,13 +267,13 @@
 	fclose(f);
 
 	/* Write out the reference data to a file for manual inspection. */
-	extract_reference_file("test_acl_pax.tar");
-	reference = slurpfile(&reference_size, "test_acl_pax.tar");
+	extract_reference_file("test_acl_pax_posix1e.tar");
+	reference = slurpfile(&reference_size, "test_acl_pax_posix1e.tar");
 
 	/* Assert that the generated data matches the built-in reference data.*/
-	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax.tar' reference file.");
+	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file.");
 	assertEqualMem(buff, reference, reference_size);
-	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax.tar' reference file.");
+	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_posix1e.tar' reference file.");
 	assertEqualInt((int)used, reference_size);
 	free(reference);
 
@@ -255,15 +295,18 @@
 	assertA(0 == archive_read_next_header(a, &ae));
 	failure("One extended ACL should flag all ACLs to be returned.");
 	assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-	compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
 	failure("Basic ACLs should set mode to 0142, not %04o",
 	    archive_entry_mode(ae)&0777);
 	assert((archive_entry_mode(ae) & 0777) == 0142);
 
 	/* Third item has pretty extensive ACLs */
 	assertA(0 == archive_read_next_header(a, &ae));
-	assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-	compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
 	failure("Basic ACLs should set mode to 0543, not %04o",
 	    archive_entry_mode(ae)&0777);
 	assert((archive_entry_mode(ae) & 0777) == 0543);
@@ -280,3 +323,93 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
+
+DEFINE_TEST(test_acl_pax_nfs4)
+{
+	struct archive *a;
+	struct archive_entry *ae;
+	size_t used;
+	FILE *f;
+	void *reference;
+	size_t reference_size;
+
+	/* Write an archive to memory. */
+	assert(NULL != (a = archive_write_new()));
+	assertA(0 == archive_write_set_format_pax(a));
+	assertA(0 == archive_write_add_filter_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a, 1));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Write a series of files to the archive with different ACL info. */
+
+	/* Create a simple archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_pathname(ae, "file");
+        archive_entry_set_mode(ae, S_IFREG | 0777);
+
+	/* NFS4 ACLs mirroring 0754 file mode */
+	assertEntrySetAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+	/* A more extensive set of NFS4 ACLs. */
+	assertEntrySetAcls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+	/* Set with special (audit, alarm) NFS4 ACLs. */
+	assertEntrySetAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+	archive_entry_free(ae);
+
+	/* Close out the archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+	/* Write out the data we generated to a file for manual inspection. */
+	assert(NULL != (f = fopen("testout", "wb")));
+	assertEqualInt(used, (size_t)fwrite(buff, 1, (unsigned int)used, f));
+	fclose(f);
+
+	/* Write out the reference data to a file for manual inspection. */
+	extract_reference_file("test_acl_pax_nfs4.tar");
+	reference = slurpfile(&reference_size, "test_acl_pax_nfs4.tar");
+
+	/* Assert that the generated data matches the built-in reference data.*/
+	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file.");
+	assertEqualMem(buff, reference, reference_size);
+	failure("Generated pax archive does not match reference; compare 'testout' to 'test_acl_pax_nfs4.tar' reference file.");
+	assertEqualInt((int)used, reference_size);
+	free(reference);
+
+	/* Read back each entry and check that the ACL data is right. */
+	assert(NULL != (a = archive_read_new()));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_filter_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	/* First item has NFS4 ACLs mirroring file mode */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(3, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ALLOW));
+	assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0);
+
+	/* Second item has has more fine-grained NFS4 ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls4[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+	/* Third item has has audit and alarm NFS4 ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+	/* Close the archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_acl_pax.tar.uu b/libarchive/test/test_acl_pax_nfs4.tar.uu
similarity index 62%
copy from libarchive/test/test_acl_pax.tar.uu
copy to libarchive/test/test_acl_pax_nfs4.tar.uu
index 58d7b62..ffc5cc2 100644
--- a/libarchive/test/test_acl_pax.tar.uu
+++ b/libarchive/test/test_acl_pax_nfs4.tar.uu
@@ -1,8 +1,8 @@
-begin 644 test_acl_pax.tar
-M9FEL90``````````````````````````````````````````````````````
+begin 644 test_acl_pax_nfs4.tar
+M4&%X2&5A9&5R+V9I;&4`````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M`````````````#`P,#$T,B``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`P
-M(#`P,#`P,#`P,#`P(#`Q,#`P-@`@,```````````````````````````````
+M`````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,3,R
+M(#`P,#`P,#`P,#`P(#`Q,C`P,P`@>```````````````````````````````
 M````````````````````````````````````````````````````````````
 M``````````````````````````````````````````!U<W1A<@`P,```````
 M````````````````````````````````````````````````````````````
@@ -10,20 +10,9 @@
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M``````````````````````!087A(96%D97(O9FEL90``````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````,#`P,30R(``P,#`P,#`@`#`P
-M,#`P,"``,#`P,#`P,#`Q,3`@,#`P,#`P,#`P,#`@,#$Q-S8Q`"!X````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M`````'5S=&%R`#`P````````````````````````````````````````````
-M`````````````````````````````````````````#`P,#`P,"``,#`P,#`P
-M(```````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M`````````````````````````````````````````````#<R(%-#2$E,62YA
-M8VPN86-C97-S/75S97(Z.BTM>"QG<F]U<#HZ<BTM+&]T:&5R.CHM=RTL=7-E
-M<CIU<V5R-S<Z<BTM.C<W"@``````````````````````````````````````
+M```````````````````````Y,"!30TA)3%DN86-L+F%C93UO=VYE<D`Z<G=X
+M<&%!4E=C0V]S.CIA;&QO=RQG<F]U<$`Z<G=P85)C<SHZ86QL;W<L979E<GEO
+M;F5`.G)A4F-S.CIA;&QO=PH`````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
@@ -32,11 +21,22 @@
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&9I;&4`````````
 M````````````````````````````````````````````````````````````
-M````````9FEL90``````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`P,#`P,#`P,#`P
+M,"`P,3`P,C0`(#``````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M`````````````````````#`P,#$T,B``,#`P,#`P(``P,#`P,#`@`#`P,#`P
-M,#`P,#`P(#`P,#`P,#`P,#`P(#`Q,#`P-@`@,```````````````````````
+M````````````````````````````=7-T87(`,#``````````````````````
+M````````````````````````````````````````````````````````````
+M````,#`P,#`P(``P,#`P,#`@````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````4&%X2&5A9&5R+V9I;&4`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@`#`P,#`P
+M,#`P,C4V(#`P,#`P,#`P,#`P(#`Q,C`Q,@`@>```````````````````````
 M````````````````````````````````````````````````````````````
 M``````````````````````````````````````````````````!U<W1A<@`P
 M,```````````````````````````````````````````````````````````
@@ -44,33 +44,33 @@
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M``````````````````````````````!087A(96%D97(O9FEL90``````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````,#`P-30S(``P,#`P
-M,#`@`#`P,#`P,"``,#`P,#`P,#`Q-C$@,#`P,#`P,#`P,#`@,#$Q-S<T`"!X
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M`````````````'5S=&%R`#`P````````````````````````````````````
-M`````````````````````````````````````````````````#`P,#`P,"``
-M,#`P,#`P(```````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M`````````````````````````````````````````````````````#$Q,R!3
-M0TA)3%DN86-L+F%C8V5S<SUU<V5R.CIR+7@L9W)O=7`Z.G(M+2QO=&AE<CHZ
-M+7=X+'5S97(Z=7-E<C<W.G(M+3HW-RQU<V5R.G5S97(W.#HM+2TZ-S@L9W)O
-M=7`Z9W)O=7`W.#IR=W@Z-S@*````````````````````````````````````
+M```````````````````````````````Q-S0@4T-(24Q9+F%C;"YA8V4];W=N
+M97)`.G)W<&%!4E=C0V]S.CIA;&QO=RQU<V5R.G5S97(W-SIR85)C<SI).F%L
+M;&]W.C<W+'5S97(Z=7-E<C<X.G)W>#HZ9&5N>3HW."QG<F]U<$`Z<G=P85)C
+M<SHZ86QL;W<L9W)O=7`Z9W)O=7`W.#IW<$%70V\Z.F1E;GDZ-S@L979E<GEO
+M;F5`.G)A4F-S.CIA;&QO=PH`````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````&9I;&4`
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M````````````````9FEL90``````````````````````````````````````
+M```````P,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`P,#`P
+M,#`P,#`P,"`P,3`P,C0`(#``````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M`````````````````````````````#`P,#4T,R``,#`P,#`P(``P,#`P,#`@
-M`#`P,#`P,#`P,#`P(#`P,#`P,#`P,#`P(#`Q,#`Q,P`@,```````````````
+M````````````````````````````````````=7-T87(`,#``````````````
+M````````````````````````````````````````````````````````````
+M````````````,#`P,#`P(``P,#`P,#`@````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````4&%X2&5A9&5R+V9I;&4`````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````#`P,#<W-R``,#`P,#`P(``P,#`P,#`@
+M`#`P,#`P,#`P,C8R(#`P,#`P,#`P,#`P(#`Q,C`P-P`@>```````````````
 M````````````````````````````````````````````````````````````
 M``````````````````````````````````````````````````````````!U
 M<W1A<@`P,```````````````````````````````````````````````````
@@ -78,15 +78,26 @@
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M``````````````````````````````````````!F:6QE````````````````
+M```````````````````````````````````````Q-S@@4T-(24Q9+F%C;"YA
+M8V4];W=N97)`.G)W>'!A05)78T-O<SHZ86QL;W<L=7-E<CIU<V5R-S<Z<G=P
+M85)C;W,Z.F%L;&]W.C<W+'5S97(Z=7-E<C<W.G=P.E,Z875D:70Z-S<L9W)O
+M=7!`.G)W<&%28W,Z.F%L;&]W+&=R;W5P.F=R;W5P-S@Z<F%28SI&.F%L87)M
+M.C<X+&5V97)Y;VYE0#IR85)C<SHZ86QL;W<*````````````````````````
 M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````,#`P,30R
-M(``P,#`P,#`@`#`P,#`P,"``,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@,#$P
-M,#`V`"`P````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-M`````````````````````'5S=&%R`#`P````````````````````````````
-M`````````````````````````````````````````````````````````#`P
-M,#`P,"``,#`P,#`P(```````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`&9I;&4`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`W-S<@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P
+M,"`P,#`P,#`P,#`P,"`P,3`P,C0`(#``````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#``````
+M````````````````````````````````````````````````````````````
+M````````````````````,#`P,#`P(``P,#`P,#`@````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
@@ -112,6 +123,7 @@
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
-C````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+'````````````
 `
 end
diff --git a/libarchive/test/test_acl_pax.tar.uu b/libarchive/test/test_acl_pax_posix1e.tar.uu
similarity index 99%
rename from libarchive/test/test_acl_pax.tar.uu
rename to libarchive/test/test_acl_pax_posix1e.tar.uu
index 58d7b62..46cf975 100644
--- a/libarchive/test/test_acl_pax.tar.uu
+++ b/libarchive/test/test_acl_pax_posix1e.tar.uu
@@ -1,4 +1,4 @@
-begin 644 test_acl_pax.tar
+begin 644 test_acl_pax_posix1e.tar
 M9FEL90``````````````````````````````````````````````````````
 M````````````````````````````````````````````````````````````
 M`````````````#`P,#$T,B``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`P
diff --git a/libarchive/test/test_acl_platform_nfs4.c b/libarchive/test/test_acl_platform_nfs4.c
new file mode 100644
index 0000000..c885408
--- /dev/null
+++ b/libarchive/test/test_acl_platform_nfs4.c
@@ -0,0 +1,894 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+#if HAVE_POSIX_ACL || HAVE_NFS4_ACL
+#define _ACL_PRIVATE
+#include <sys/acl.h>
+#if HAVE_DARWIN_ACL
+#include <membership.h>
+#endif
+#endif
+
+#if HAVE_NFS4_ACL
+struct myacl_t {
+	int type;
+	int permset;
+	int tag;
+	int qual; /* GID or UID of user/group, depending on tag. */
+	const char *name; /* Name of user/group, depending on tag. */
+};
+
+static struct myacl_t acls_reg[] = {
+#if !HAVE_DARWIN_ACL
+	/* For this test, we need the file owner to be able to read and write the ACL. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL | ARCHIVE_ENTRY_ACL_WRITE_ACL | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
+#endif
+	/* An entry for each type. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 108, "user108" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 109, "user109" },
+
+	/* An entry for each permission. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 112, "user112" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA,
+	  ARCHIVE_ENTRY_ACL_USER, 113, "user113" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_DATA,
+	  ARCHIVE_ENTRY_ACL_USER, 115, "user115" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_APPEND_DATA,
+	  ARCHIVE_ENTRY_ACL_USER, 117, "user117" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
+	  ARCHIVE_ENTRY_ACL_USER, 119, "user119" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
+	  ARCHIVE_ENTRY_ACL_USER, 120, "user120" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+	  ARCHIVE_ENTRY_ACL_USER, 122, "user122" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
+	  ARCHIVE_ENTRY_ACL_USER, 123, "user123" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
+	  ARCHIVE_ENTRY_ACL_USER, 124, "user124" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
+	  ARCHIVE_ENTRY_ACL_USER, 125, "user125" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
+	  ARCHIVE_ENTRY_ACL_USER, 126, "user126" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_USER, 127, "user127" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER, 128, "user128" },
+
+	/* One entry for each qualifier. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 135, "user135" },
+//	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+//	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_GROUP, 136, "group136" },
+#if !HAVE_DARWIN_ACL
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+#else	/* MacOS - mode 0654 */
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+#endif
+};
+
+static const int acls_reg_cnt = (int)(sizeof(acls_reg)/sizeof(acls_reg[0]));
+
+static struct myacl_t acls_dir[] = {
+	/* For this test, we need to be able to read and write the ACL. */
+#if !HAVE_DARWIN_ACL
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_READ_ACL,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, ""},
+#endif
+
+	/* An entry for each type. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_USER, 102, "user102" },
+
+	/* An entry for each permission. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_USER, 201, "user201" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_FILE,
+	  ARCHIVE_ENTRY_ACL_USER, 202, "user202" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+	  ARCHIVE_ENTRY_ACL_USER, 203, "user203" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
+	  ARCHIVE_ENTRY_ACL_USER, 204, "user204" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
+	  ARCHIVE_ENTRY_ACL_USER, 205, "user205" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE_CHILD,
+	  ARCHIVE_ENTRY_ACL_USER, 206, "user206" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+	  ARCHIVE_ENTRY_ACL_USER, 207, "user207" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
+	  ARCHIVE_ENTRY_ACL_USER, 208, "user208" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_DELETE,
+	  ARCHIVE_ENTRY_ACL_USER, 209, "user209" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ_ACL,
+	  ARCHIVE_ENTRY_ACL_USER, 210, "user210" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_ACL,
+	  ARCHIVE_ENTRY_ACL_USER, 211, "user211" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_USER, 212, "user212" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER, 213, "user213" },
+
+	/* One entry with each inheritance value. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
+	  ARCHIVE_ENTRY_ACL_USER, 301, "user301" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+	  ARCHIVE_ENTRY_ACL_USER, 302, "user302" },
+#if 0
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
+	  ARCHIVE_ENTRY_ACL_USER, 303, "user303" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
+	  ARCHIVE_ENTRY_ACL_USER, 304, "user304" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+	  ARCHIVE_ENTRY_ACL_USER, 305, "user305" },
+#endif
+
+#if 0
+	/* FreeBSD does not support audit entries. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
+	  ARCHIVE_ENTRY_ACL_USER, 401, "user401" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+	  ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
+	  ARCHIVE_ENTRY_ACL_USER, 402, "user402" },
+#endif
+
+	/* One entry for each qualifier. */
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_USER, 501, "user501" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_GROUP, 502, "group502" },
+#if !HAVE_DARWIN_ACL
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+#else	/* MacOS - mode 0654 */
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, -1, "" }
+#endif
+};
+
+static const int acls_dir_cnt = (int)(sizeof(acls_dir)/sizeof(acls_dir[0]));
+
+static void
+set_acls(struct archive_entry *ae, struct myacl_t *acls, int start, int end)
+{
+	int i;
+
+	archive_entry_acl_clear(ae);
+#if !HAVE_DARWIN_ACL
+	if (start > 0) {
+		assertEqualInt(ARCHIVE_OK,
+			archive_entry_acl_add_entry(ae,
+			    acls[0].type, acls[0].permset, acls[0].tag,
+			    acls[0].qual, acls[0].name));
+	}
+#endif
+	for (i = start; i < end; i++) {
+		assertEqualInt(ARCHIVE_OK,
+		    archive_entry_acl_add_entry(ae,
+			acls[i].type, acls[i].permset, acls[i].tag,
+			acls[i].qual, acls[i].name));
+	}
+}
+
+static int
+#ifdef HAVE_SUN_NFS4_ACL
+acl_permset_to_bitmap(uint32_t a_access_mask)
+#else
+acl_permset_to_bitmap(acl_permset_t opaque_ps)
+#endif
+{
+	static struct { int machine; int portable; } perms[] = {
+#ifdef HAVE_SUN_NFS4_ACL	/* Solaris NFSv4 ACL permissions */
+		{ACE_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
+		{ACE_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
+		{ACE_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
+		{ACE_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
+		{ACE_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
+		{ACE_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
+		{ACE_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
+		{ACE_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
+		{ACE_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
+		{ACE_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
+		{ACE_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
+		{ACE_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
+		{ACE_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
+		{ACE_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
+		{ACE_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
+		{ACE_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
+		{ACE_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
+#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 ACL permissions */
+		{ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
+		{ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
+		{ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
+		{ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
+		{ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
+		{ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
+		{ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
+		{ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
+		{ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
+		{ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
+		{ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
+		{ACL_READ_EXTATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
+		{ACL_WRITE_EXTATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
+		{ACL_READ_SECURITY, ARCHIVE_ENTRY_ACL_READ_ACL},
+		{ACL_WRITE_SECURITY, ARCHIVE_ENTRY_ACL_WRITE_ACL},
+		{ACL_CHANGE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+		{ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE},
+#endif
+#else	/* FreeBSD NFSv4 ACL permissions */
+		{ACL_EXECUTE, ARCHIVE_ENTRY_ACL_EXECUTE},
+		{ACL_WRITE, ARCHIVE_ENTRY_ACL_WRITE},
+		{ACL_READ, ARCHIVE_ENTRY_ACL_READ},
+		{ACL_READ_DATA, ARCHIVE_ENTRY_ACL_READ_DATA},
+		{ACL_LIST_DIRECTORY, ARCHIVE_ENTRY_ACL_LIST_DIRECTORY},
+		{ACL_WRITE_DATA, ARCHIVE_ENTRY_ACL_WRITE_DATA},
+		{ACL_ADD_FILE, ARCHIVE_ENTRY_ACL_ADD_FILE},
+		{ACL_APPEND_DATA, ARCHIVE_ENTRY_ACL_APPEND_DATA},
+		{ACL_ADD_SUBDIRECTORY, ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY},
+		{ACL_READ_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS},
+		{ACL_WRITE_NAMED_ATTRS, ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS},
+		{ACL_DELETE_CHILD, ARCHIVE_ENTRY_ACL_DELETE_CHILD},
+		{ACL_READ_ATTRIBUTES, ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES},
+		{ACL_WRITE_ATTRIBUTES, ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES},
+		{ACL_DELETE, ARCHIVE_ENTRY_ACL_DELETE},
+		{ACL_READ_ACL, ARCHIVE_ENTRY_ACL_READ_ACL},
+		{ACL_WRITE_ACL, ARCHIVE_ENTRY_ACL_WRITE_ACL},
+		{ACL_WRITE_OWNER, ARCHIVE_ENTRY_ACL_WRITE_OWNER},
+		{ACL_SYNCHRONIZE, ARCHIVE_ENTRY_ACL_SYNCHRONIZE}
+#endif
+	};
+	int i, permset = 0;
+
+	for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
+#if HAVE_SUN_NFS4_ACL
+		if (a_access_mask & perms[i].machine)
+#else
+		if (acl_get_perm_np(opaque_ps, perms[i].machine))
+#endif
+			permset |= perms[i].portable;
+	return permset;
+}
+
+static int
+#if HAVE_SUN_NFS4_ACL
+acl_flagset_to_bitmap(uint16_t a_flags)
+#else
+acl_flagset_to_bitmap(acl_flagset_t opaque_fs)
+#endif
+{
+	static struct { int machine; int portable; } flags[] = {
+#if HAVE_SUN_NFS4_ACL	/* Solaris NFSv4 ACL inheritance flags */
+		{ACE_FILE_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
+		{ACE_DIRECTORY_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
+		{ACE_NO_PROPAGATE_INHERIT_ACE, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
+		{ACE_INHERIT_ONLY_ACE, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
+		{ACE_SUCCESSFUL_ACCESS_ACE_FLAG, ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS},
+		{ACE_FAILED_ACCESS_ACE_FLAG, ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS},
+#ifdef ACE_INHERITED_ACE
+		{ACE_INHERITED_ACE, ARCHIVE_ENTRY_ACL_ENTRY_INHERITED}
+#endif
+#elif HAVE_DARWIN_ACL	/* MacOS NFSv4 ACL inheritance flags */
+		{ACL_ENTRY_INHERITED, ARCHIVE_ENTRY_ACL_ENTRY_INHERITED},
+		{ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
+		{ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
+		{ACL_ENTRY_LIMIT_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
+		{ACL_ENTRY_ONLY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY}
+#else	/* FreeBSD NFSv4 ACL inheritance flags */
+		{ACL_ENTRY_FILE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT},
+		{ACL_ENTRY_DIRECTORY_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT},
+		{ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT},
+		{ACL_ENTRY_SUCCESSFUL_ACCESS, ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS},
+		{ACL_ENTRY_NO_PROPAGATE_INHERIT, ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS},
+		{ACL_ENTRY_INHERIT_ONLY, ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY},
+#endif
+	};
+	int i, flagset = 0;
+
+	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
+#if HAVE_SUN_NFS4_ACL
+		if (a_flags & flags[i].machine)
+#else
+		if (acl_get_flag_np(opaque_fs, flags[i].machine))
+#endif
+			flagset |= flags[i].portable;
+	return flagset;
+}
+
+static int
+#if HAVE_SUN_NFS4_ACL
+acl_match(ace_t *ace, struct myacl_t *myacl)
+#else
+acl_match(acl_entry_t aclent, struct myacl_t *myacl)
+#endif
+{
+#if !HAVE_SUN_NFS4_ACL
+#if HAVE_DARWIN_ACL
+	void *q;
+	uid_t ugid;
+	int r, idtype;
+#else
+	gid_t g, *gp;
+	uid_t u, *up;
+	acl_entry_type_t entry_type;
+#endif	/* !HAVE_DARWIN_ACL */
+	acl_tag_t tag_type;
+	acl_permset_t opaque_ps;
+	acl_flagset_t opaque_fs;
+#endif	/* !HAVE_SUN_NFS4_ACL */
+	int perms;
+
+#if HAVE_SUN_NFS4_ACL
+	perms = acl_permset_to_bitmap(ace->a_access_mask) | acl_flagset_to_bitmap(ace->a_flags);
+#else
+	acl_get_tag_type(aclent, &tag_type);
+#if !HAVE_DARWIN_ACL
+	acl_get_entry_type_np(aclent, &entry_type);
+#endif
+
+	/* translate the silly opaque permset to a bitmap */
+	acl_get_permset(aclent, &opaque_ps);
+	acl_get_flagset_np(aclent, &opaque_fs);
+	perms = acl_permset_to_bitmap(opaque_ps) | acl_flagset_to_bitmap(opaque_fs);
+#endif
+	if (perms != myacl->permset)
+		return (0);
+
+#if HAVE_SUN_NFS4_ACL
+	switch (ace->a_type) {
+	case ACE_ACCESS_ALLOWED_ACE_TYPE:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW)
+			return (0);
+		break;
+	case ACE_ACCESS_DENIED_ACE_TYPE:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY)
+			return (0);
+		break;
+	case ACE_SYSTEM_AUDIT_ACE_TYPE:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_AUDIT)
+			return (0);
+		break;
+	case ACE_SYSTEM_ALARM_ACE_TYPE:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALARM)
+			return (0);
+		break;
+	default:
+		return (0);
+	}
+
+	if (ace->a_flags & ACE_OWNER) {
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ)
+			return (0);
+	} else if (ace->a_flags & ACE_GROUP) {
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ)
+			return (0);
+	} else if (ace->a_flags & ACE_EVERYONE) {
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE)
+			return (0);
+	} else if (ace->a_flags & ACE_IDENTIFIER_GROUP) {
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+			return (0);
+		if ((gid_t)myacl->qual != ace->a_who)
+			return (0);
+	} else {
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+			return (0);
+		if ((uid_t)myacl->qual != ace->a_who)
+			return (0);
+	}
+#elif HAVE_DARWIN_ACL
+	r = 0;
+	switch (tag_type) {
+	case ACL_EXTENDED_ALLOW:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW)
+			return (0);
+		break;
+	case ACL_EXTENDED_DENY:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY)
+			return (0);
+		break;
+	default:
+		return (0);
+	}
+	q = acl_get_qualifier(aclent);
+	if (q == NULL)
+		return (0);
+	r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);
+	acl_free(q);
+	if (r != 0)
+		return (0);
+	switch (idtype) {
+		case ID_TYPE_UID:
+			if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+				return (0);
+			if ((uid_t)myacl->qual != ugid)
+				return (0);
+			break;
+		case ID_TYPE_GID:
+			if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+				return (0);
+			if ((gid_t)myacl->qual != ugid)
+				return (0);
+			break;
+		default:
+			return (0);
+	}
+#else	/* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */
+	switch (entry_type) {
+	case ACL_ENTRY_TYPE_ALLOW:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALLOW)
+			return (0);
+		break;
+	case ACL_ENTRY_TYPE_DENY:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_DENY)
+			return (0);
+		break;
+	case ACL_ENTRY_TYPE_AUDIT:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_AUDIT)
+			return (0);
+	case ACL_ENTRY_TYPE_ALARM:
+		if (myacl->type != ARCHIVE_ENTRY_ACL_TYPE_ALARM)
+			return (0);
+	default:
+		return (0);
+	}
+
+	switch (tag_type) {
+	case ACL_USER_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
+		break;
+	case ACL_USER:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+			return (0);
+		up = acl_get_qualifier(aclent);
+		u = *up;
+		acl_free(up);
+		if ((uid_t)myacl->qual != u)
+			return (0);
+		break;
+	case ACL_GROUP_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
+		break;
+	case ACL_GROUP:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+			return (0);
+		gp = acl_get_qualifier(aclent);
+		g = *gp;
+		acl_free(gp);
+		if ((gid_t)myacl->qual != g)
+			return (0);
+		break;
+	case ACL_MASK:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
+		break;
+	case ACL_EVERYONE:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_EVERYONE) return (0);
+		break;
+	}
+#endif	/* !HAVE_SUN_NFS4_ACL && !HAVE_DARWIN_ACL */
+	return (1);
+}
+
+static void
+compare_acls(
+#if HAVE_SUN_NFS4_ACL
+    void *aclp,
+    int aclcnt,
+#else
+    acl_t acl,
+#endif
+    struct myacl_t *myacls, const char *filename, int start, int end)
+{
+	int *marker;
+	int matched;
+	int i, n;
+#if HAVE_SUN_NFS4_ACL
+	int e;
+	ace_t *acl_entry;
+#else
+	int entry_id = ACL_FIRST_ENTRY;
+	acl_entry_t acl_entry;
+#endif
+
+	n = end - start;
+	marker = malloc(sizeof(marker[0]) * (n + 1));
+	for (i = 0; i < n; i++)
+		marker[i] = i + start;
+#if !HAVE_DARWIN_ACL
+	/* Always include the first ACE. */
+	if (start > 0) {
+	  marker[n] = 0;
+	  ++n;
+	}
+#endif
+
+	/*
+	 * Iterate over acls in system acl object, try to match each
+	 * one with an item in the myacls array.
+	 */
+#if HAVE_SUN_NFS4_ACL
+	for (e = 0; e < aclcnt; e++)
+#elif HAVE_DARWIN_ACL
+	while (0 == acl_get_entry(acl, entry_id, &acl_entry))
+#else
+	while (1 == acl_get_entry(acl, entry_id, &acl_entry))
+#endif
+	{
+#if HAVE_SUN_NFS4_ACL
+		acl_entry = &((ace_t *)aclp)[e];
+#else
+		/* After the first time... */
+		entry_id = ACL_NEXT_ENTRY;
+#endif
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(acl_entry, &myacls[marker[i]])) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+
+		failure("ACL entry on file %s that shouldn't be there",
+		    filename);
+		assert(matched == 1);
+	}
+
+	/* Dump entries in the myacls array that weren't in the system acl. */
+	for (i = 0; i < n; ++i) {
+		failure(" ACL entry %d missing from %s: "
+		    "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
+		    marker[i], filename,
+		    myacls[marker[i]].type, myacls[marker[i]].permset,
+		    myacls[marker[i]].tag, myacls[marker[i]].qual,
+		    myacls[marker[i]].name);
+		assert(0); /* Record this as a failure. */
+	}
+	free(marker);
+}
+
+static void
+compare_entry_acls(struct archive_entry *ae, struct myacl_t *myacls, const char *filename, int start, int end)
+{
+	int *marker;
+	int matched;
+	int i, n;
+	int type, permset, tag, qual;
+	const char *name;
+
+	/* Count ACL entries in myacls array and allocate an indirect array. */
+	n = end - start;
+	marker = malloc(sizeof(marker[0]) * (n + 1));
+	for (i = 0; i < n; i++)
+		marker[i] = i + start;
+	/* Always include the first ACE. */
+	if (start > 0) {
+	  marker[n] = 0;
+	  ++n;
+	}
+
+	/*
+	 * Iterate over acls in entry, try to match each
+	 * one with an item in the myacls array.
+	 */
+	assertEqualInt(n, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	while (ARCHIVE_OK == archive_entry_acl_next(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, &type, &permset, &tag, &qual, &name)) {
+
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (tag == myacls[marker[i]].tag
+			    && qual == myacls[marker[i]].qual
+			    && permset == myacls[marker[i]].permset
+			    && type == myacls[marker[i]].type) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+
+		failure("ACL entry on file that shouldn't be there: "
+			"type=%#010x,permset=%#010x,tag=%d,qual=%d",
+			type,permset,tag,qual);
+		assert(matched == 1);
+	}
+
+	/* Dump entries in the myacls array that weren't in the system acl. */
+	for (i = 0; i < n; ++i) {
+		failure(" ACL entry %d missing from %s: "
+		    "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
+		    marker[i], filename,
+		    myacls[marker[i]].type, myacls[marker[i]].permset,
+		    myacls[marker[i]].tag, myacls[marker[i]].qual,
+		    myacls[marker[i]].name);
+		assert(0); /* Record this as a failure. */
+	}
+	free(marker);
+}
+#endif	/* HAVE_NFS4_ACL */
+
+/*
+ * Verify ACL restore-to-disk.  This test is Platform-specific.
+ */
+
+DEFINE_TEST(test_acl_platform_nfs4)
+{
+#if !HAVE_NFS4_ACL
+	skipping("NFS4 ACLs are not supported on this platform");
+#else
+	char buff[64];
+	int i;
+	struct stat st;
+	struct archive *a;
+	struct archive_entry *ae;
+#if HAVE_DARWIN_ACL /* On MacOS we skip trivial ACLs in some tests */
+	const int regcnt = acls_reg_cnt - 4;
+	const int dircnt = acls_dir_cnt - 4;
+#else
+	const int regcnt = acls_reg_cnt;
+	const int dircnt = acls_dir_cnt;
+#endif
+#if HAVE_SUN_NFS4_ACL
+	void *aclp;
+	int aclcnt;
+#else	/* !HAVE_SUN_NFS4_ACL */
+	acl_t acl;
+#endif
+
+	assertMakeFile("pretest", 0644, "a");
+
+	if (setTestAcl("pretest") != ARCHIVE_TEST_ACL_TYPE_NFS4) {
+		skipping("NFS4 ACLs are not writable on this filesystem");
+		return;
+	}
+
+	/* Create a write-to-disk object. */
+	assert(NULL != (a = archive_write_disk_new()));
+	archive_write_disk_set_options(a,
+	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
+
+	/* Populate an archive entry with some metadata, including ACL info */
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_pathname(ae, "testall");
+	archive_entry_set_filetype(ae, AE_IFREG);
+	archive_entry_set_perm(ae, 0654);
+	archive_entry_set_mtime(ae, 123456, 7890);
+	archive_entry_set_size(ae, 0);
+	set_acls(ae, acls_reg, 0, acls_reg_cnt);
+
+	/* Write the entry to disk, including ACLs. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+
+	/* Likewise for a dir. */
+	archive_entry_set_pathname(ae, "dirall");
+	archive_entry_set_filetype(ae, AE_IFDIR);
+	archive_entry_set_perm(ae, 0654);
+	archive_entry_set_mtime(ae, 123456, 7890);
+	set_acls(ae, acls_dir, 0, acls_dir_cnt);
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+
+	for (i = 0; i < acls_dir_cnt; ++i) {
+	  sprintf(buff, "dir%d", i);
+	  archive_entry_set_pathname(ae, buff);
+	  archive_entry_set_filetype(ae, AE_IFDIR);
+	  archive_entry_set_perm(ae, 0654);
+	  archive_entry_set_mtime(ae, 123456 + i, 7891 + i);
+	  set_acls(ae, acls_dir, i, i + 1);
+	  assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+	}
+
+	archive_entry_free(ae);
+
+	/* Close the archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+	/* Verify the data on disk. */
+	assertEqualInt(0, stat("testall", &st));
+	assertEqualInt(st.st_mtime, 123456);
+#if HAVE_SUN_NFS4_ACL
+	aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, "testall");
+	failure("acl(): errno = %d (%s)", errno, strerror(errno));
+	assert(aclp != NULL);
+#else
+#if HAVE_DARWIN_ACL
+	acl = acl_get_file("testall", ACL_TYPE_EXTENDED);
+#else
+	acl = acl_get_file("testall", ACL_TYPE_NFS4);
+#endif
+	failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno));
+	assert(acl != (acl_t)NULL);
+#endif
+#if HAVE_SUN_NFS4_ACL
+	compare_acls(aclp, aclcnt, acls_reg, "testall", 0, regcnt);
+	free(aclp);
+	aclp = NULL;
+#else
+	compare_acls(acl, acls_reg, "testall", 0, regcnt);
+	acl_free(acl);
+#endif
+
+
+	/* Verify single-permission dirs on disk. */
+	for (i = 0; i < dircnt; ++i) {
+		sprintf(buff, "dir%d", i);
+		assertEqualInt(0, stat(buff, &st));
+		assertEqualInt(st.st_mtime, 123456 + i);
+#if HAVE_SUN_NFS4_ACL
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, buff);
+		failure("acl(): errno = %d (%s)", errno, strerror(errno));
+		assert(aclp != NULL);
+#else
+#if HAVE_DARWIN_ACL
+		acl = acl_get_file(buff, ACL_TYPE_EXTENDED);
+#else
+		acl = acl_get_file(buff, ACL_TYPE_NFS4);
+#endif
+		failure("acl_get_file(): errno = %d (%s)", errno,
+		    strerror(errno));
+		assert(acl != (acl_t)NULL);
+#endif
+#if HAVE_SUN_NFS4_ACL
+		compare_acls(aclp, aclcnt, acls_dir, buff, i, i + 1);
+		free(aclp);
+		aclp = NULL;
+#else
+		compare_acls(acl, acls_dir, buff, i, i + 1);
+		acl_free(acl);
+#endif
+	}
+
+	/* Verify "dirall" on disk. */
+	assertEqualInt(0, stat("dirall", &st));
+	assertEqualInt(st.st_mtime, 123456);
+#if HAVE_SUN_NFS4_ACL
+	aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, "dirall");
+	failure("acl(): errno = %d (%s)", errno, strerror(errno));
+	assert(aclp != NULL);
+#else
+#if HAVE_DARWIN_ACL
+	acl = acl_get_file("dirall", ACL_TYPE_EXTENDED);
+#else
+	acl = acl_get_file("dirall", ACL_TYPE_NFS4);
+#endif
+	failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno));
+	assert(acl != (acl_t)NULL);
+#endif
+#if HAVE_SUN_NFS4_ACL
+	compare_acls(aclp, aclcnt, acls_dir, "dirall", 0, dircnt);
+	free(aclp);
+	aclp = NULL;
+#else
+	compare_acls(acl, acls_dir, "dirall", 0, dircnt);
+	acl_free(acl);
+#endif
+
+	/* Read and compare ACL via archive_read_disk */
+	a = archive_read_disk_new();
+	assert(a != NULL);
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_pathname(ae, "testall");
+	assertEqualInt(ARCHIVE_OK,
+		       archive_read_disk_entry_from_file(a, ae, -1, NULL));
+	compare_entry_acls(ae, acls_reg, "testall", 0, acls_reg_cnt);
+	archive_entry_free(ae);
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+	/* Read and compare ACL via archive_read_disk */
+	a = archive_read_disk_new();
+	assert(a != NULL);
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_pathname(ae, "dirall");
+	assertEqualInt(ARCHIVE_OK,
+	archive_read_disk_entry_from_file(a, ae, -1, NULL));
+	compare_entry_acls(ae, acls_dir, "dirall", 0, acls_dir_cnt);
+	archive_entry_free(ae);
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+#endif /* HAVE_NFS4_ACL */
+}
diff --git a/libarchive/test/test_acl_platform_posix1e.c b/libarchive/test/test_acl_platform_posix1e.c
new file mode 100644
index 0000000..0224a57
--- /dev/null
+++ b/libarchive/test/test_acl_platform_posix1e.c
@@ -0,0 +1,614 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $");
+
+#if HAVE_POSIX_ACL || HAVE_SUN_ACL
+#include <sys/acl.h>
+#if HAVE_ACL_GET_PERM
+#include <acl/libacl.h>
+#define ACL_GET_PERM acl_get_perm
+#elif HAVE_ACL_GET_PERM_NP
+#define ACL_GET_PERM acl_get_perm_np
+#endif
+
+static struct archive_test_acl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	  ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, "" },
+};
+
+static int
+#if HAVE_SUN_ACL
+acl_entry_get_perm(aclent_t *aclent)
+#else
+acl_entry_get_perm(acl_entry_t aclent)
+#endif
+{
+	int permset = 0;
+#if HAVE_POSIX_ACL
+	acl_permset_t opaque_ps;
+#endif
+
+#if HAVE_SUN_ACL
+	if (aclent->a_perm & 1)
+		permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+	if (aclent->a_perm & 2)
+		permset |= ARCHIVE_ENTRY_ACL_WRITE;
+	if (aclent->a_perm & 4)
+		permset |= ARCHIVE_ENTRY_ACL_READ;
+#else
+	/* translate the silly opaque permset to a bitmap */
+	acl_get_permset(aclent, &opaque_ps);
+	if (ACL_GET_PERM(opaque_ps, ACL_EXECUTE))
+		permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+	if (ACL_GET_PERM(opaque_ps, ACL_WRITE))
+		permset |= ARCHIVE_ENTRY_ACL_WRITE;
+	if (ACL_GET_PERM(opaque_ps, ACL_READ))
+		permset |= ARCHIVE_ENTRY_ACL_READ;
+#endif
+	return permset;
+}
+
+#if 0
+static int
+acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) {
+	int entry_id = ACL_FIRST_ENTRY;
+	acl_entry_t acl_entry;
+	acl_tag_t acl_tag_type;
+	
+	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
+		/* After the first time... */
+		entry_id = ACL_NEXT_ENTRY;
+
+		/* If this matches, return perm mask */
+		acl_get_tag_type(acl_entry, &acl_tag_type);
+		if (acl_tag_type == requested_tag_type) {
+			switch (acl_tag_type) {
+			case ACL_USER_OBJ:
+				if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) {
+					return acl_entry_get_perm(acl_entry);
+				}
+				break;
+			case ACL_GROUP_OBJ:
+				if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) {
+					return acl_entry_get_perm(acl_entry);
+				}
+				break;
+			case ACL_USER:
+			case ACL_GROUP:
+			case ACL_OTHER:
+				return acl_entry_get_perm(acl_entry);
+			default:
+				failure("Unexpected ACL tag type");
+				assert(0);
+			}
+		}
+
+
+	}
+	return -1;
+}
+#endif
+
+static int
+#if HAVE_SUN_ACL
+acl_match(aclent_t *aclent, struct archive_test_acl_t *myacl)
+#else
+acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl)
+#endif
+{
+#if HAVE_POSIX_ACL
+	gid_t g, *gp;
+	uid_t u, *up;
+	acl_tag_t tag_type;
+#endif
+
+	if (myacl->permset != acl_entry_get_perm(aclent))
+		return (0);
+
+#if HAVE_SUN_ACL
+	switch (aclent->a_type)
+#else
+	acl_get_tag_type(aclent, &tag_type);
+	switch (tag_type)
+#endif
+	{
+#if HAVE_SUN_ACL
+	case DEF_USER_OBJ:
+	case USER_OBJ:
+#else
+	case ACL_USER_OBJ:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
+		break;
+#if HAVE_SUN_ACL
+	case DEF_USER:
+	case USER:
+#else
+	case ACL_USER:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+			return (0);
+#if HAVE_SUN_ACL
+		if ((uid_t)myacl->qual != aclent->a_id)
+			return (0);
+#else
+		up = acl_get_qualifier(aclent);
+		u = *up;
+		acl_free(up);
+		if ((uid_t)myacl->qual != u)
+			return (0);
+#endif
+		break;
+#if HAVE_SUN_ACL
+	case DEF_GROUP_OBJ:
+	case GROUP_OBJ:
+#else
+	case ACL_GROUP_OBJ:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
+		break;
+#if HAVE_SUN_ACL
+	case DEF_GROUP:
+	case GROUP:
+#else
+	case ACL_GROUP:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+			return (0);
+#if HAVE_SUN_ACL
+		if ((gid_t)myacl->qual != aclent->a_id)
+			return (0);
+#else
+		gp = acl_get_qualifier(aclent);
+		g = *gp;
+		acl_free(gp);
+		if ((gid_t)myacl->qual != g)
+			return (0);
+#endif
+		break;
+#if HAVE_SUN_ACL
+	case DEF_CLASS_OBJ:
+	case CLASS_OBJ:
+#else
+	case ACL_MASK:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
+		break;
+#if HAVE_SUN_ACL
+	case DEF_OTHER_OBJ:
+	case OTHER_OBJ:
+#else
+	case ACL_OTHER:
+#endif
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0);
+		break;
+	}
+	return (1);
+}
+
+static void
+#if HAVE_SUN_ACL
+compare_acls(void *aclp, int aclcnt, struct archive_test_acl_t *myacls, int n)
+#else
+compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n)
+#endif
+{
+	int *marker;
+	int matched;
+	int i;
+#if HAVE_SUN_ACL
+	int e;
+	aclent_t *acl_entry;
+#else
+	int entry_id = ACL_FIRST_ENTRY;
+	acl_entry_t acl_entry;
+#endif
+
+	/* Count ACL entries in myacls array and allocate an indirect array. */
+	marker = malloc(sizeof(marker[0]) * n);
+	if (marker == NULL)
+		return;
+	for (i = 0; i < n; i++)
+		marker[i] = i;
+
+	/*
+	 * Iterate over acls in system acl object, try to match each
+	 * one with an item in the myacls array.
+	 */
+#if HAVE_SUN_ACL
+	for(e = 0; e < aclcnt; e++) {
+		acl_entry = &((aclent_t *)aclp)[e];
+#else
+	while (1 == acl_get_entry(acl, entry_id, &acl_entry)) {
+		/* After the first time... */
+		entry_id = ACL_NEXT_ENTRY;
+#endif
+
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(acl_entry, &myacls[marker[i]])) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+
+		/* TODO: Print out more details in this case. */
+		failure("ACL entry on file that shouldn't be there");
+		assert(matched == 1);
+	}
+
+	/* Dump entries in the myacls array that weren't in the system acl. */
+	for (i = 0; i < n; ++i) {
+		failure(" ACL entry missing from file: "
+		    "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n",
+		    myacls[marker[i]].type, myacls[marker[i]].permset,
+		    myacls[marker[i]].tag, myacls[marker[i]].qual,
+		    myacls[marker[i]].name);
+		assert(0); /* Record this as a failure. */
+	}
+	free(marker);
+}
+
+#endif
+
+
+/*
+ * Verify ACL restore-to-disk.  This test is Platform-specific.
+ */
+
+DEFINE_TEST(test_acl_platform_posix1e_restore)
+{
+#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL
+	skipping("POSIX.1e ACLs are not supported on this platform");
+#else	/* HAVE_SUN_ACL || HAVE_POSIX_ACL */
+	struct stat st;
+	struct archive *a;
+	struct archive_entry *ae;
+#if HAVE_SUN_ACL
+	void *aclp;
+	int aclcnt;
+#else
+	acl_t acl;
+#endif
+
+	assertMakeFile("pretest", 0644, "a");
+
+	if (setTestAcl("pretest") != ARCHIVE_TEST_ACL_TYPE_POSIX1E) {
+		skipping("POSIX.1e ACLs are not writable on this filesystem");
+		return;
+	}
+
+	/* Create a write-to-disk object. */
+	assert(NULL != (a = archive_write_disk_new()));
+	archive_write_disk_set_options(a,
+	    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL);
+
+	/* Populate an archive entry with some metadata, including ACL info */
+	ae = archive_entry_new();
+	assert(ae != NULL);
+	archive_entry_set_pathname(ae, "test0");
+	archive_entry_set_mtime(ae, 123456, 7890);
+	archive_entry_set_size(ae, 0);
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Close the archive. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+	/* Verify the data on disk. */
+	assertEqualInt(0, stat("test0", &st));
+	assertEqualInt(st.st_mtime, 123456);
+#if HAVE_SUN_ACL
+	aclp = sunacl_get(GETACL, &aclcnt, 0, "test0");
+	failure("acl(): errno = %d (%s)", errno, strerror(errno));
+	assert(aclp != NULL);
+#else
+	acl = acl_get_file("test0", ACL_TYPE_ACCESS);
+	failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno));
+	assert(acl != (acl_t)NULL);
+#endif
+#if HAVE_SUN_ACL
+	compare_acls(aclp, aclcnt, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	free(aclp);
+	aclp = NULL;
+#else
+	compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	acl_free(acl);
+#endif
+
+#endif	/* HAVE_SUN_ACL || HAVE_POSIX_ACL */
+}
+
+/*
+ * Verify ACL read-from-disk.  This test is Platform-specific.
+ */
+DEFINE_TEST(test_acl_platform_posix1e_read)
+{
+#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL
+	skipping("POSIX.1e ACLs are not supported on this platform");
+#else
+	struct archive *a;
+	struct archive_entry *ae;
+	int n, fd, flags, dflags;
+	char *func, *acl_text;
+	const char *acl1_text, *acl2_text, *acl3_text;
+#if HAVE_SUN_ACL
+	void *aclp;
+	int aclcnt;
+#else
+	acl_t acl1, acl2, acl3;
+#endif
+
+	/*
+	 * Manually construct a directory and two files with
+	 * different ACLs.  This also serves to verify that ACLs
+	 * are supported on the local filesystem.
+	 */
+
+	/* Create a test file f1 with acl1 */
+#if HAVE_SUN_ACL
+	acl1_text = "user::rwx,"
+	    "group::rwx,"
+	    "other:rwx,"
+	    "user:1:rw-,"
+	    "group:15:r-x,"
+	    "mask:rwx";
+	aclent_t aclp1[] = {
+	    { USER_OBJ, -1, 4 | 2 | 1 },
+	    { USER, 1, 4 | 2 },
+	    { GROUP_OBJ, -1, 4 | 2 | 1 },
+	    { GROUP, 15, 4 | 1 },
+	    { CLASS_OBJ, -1, 4 | 2 | 1 },
+	    { OTHER_OBJ, -1, 4 | 2 | 1 }
+	};
+#else
+	acl1_text = "user::rwx\n"
+	    "group::rwx\n"
+	    "other::rwx\n"
+	    "user:1:rw-\n"
+	    "group:15:r-x\n"
+	    "mask::rwx";
+	acl1 = acl_from_text(acl1_text);
+	failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno));
+	assert((void *)acl1 != NULL);
+#endif
+	fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
+	failure("Could not create test file?!");
+	if (!assert(fd >= 0)) {
+#if !HAVE_SUN_ACL
+		acl_free(acl1);
+#endif
+		return;
+	}
+#if HAVE_SUN_ACL
+	/* Check if Solaris filesystem supports POSIX.1e ACLs */
+	aclp = sunacl_get(GETACL, &aclcnt, fd, NULL);
+	if (aclp == 0)
+		close(fd);
+	if (errno == ENOSYS || errno == ENOTSUP) {
+		skipping("POSIX.1e ACLs are not supported on this filesystem");
+		return;
+	}
+	failure("facl(): errno = %d (%s)", errno, strerror(errno));
+	assert(aclp != NULL);
+
+	func = "facl()";
+	n = facl(fd, SETACL, (int)(sizeof(aclp1)/sizeof(aclp1[0])), aclp1);
+#else
+	func = "acl_set_fd()";
+	n = acl_set_fd(fd, acl1);
+#endif
+#if !HAVE_SUN_ACL
+	acl_free(acl1);
+#endif
+
+	if (n != 0) {
+#if HAVE_SUN_ACL
+		if (errno == ENOSYS || errno == ENOTSUP)
+#else
+		if (errno == EOPNOTSUPP || errno == EINVAL)
+#endif
+		{
+			close(fd);
+			skipping("POSIX.1e ACLs are not supported on this filesystem");
+			return;
+		}
+	}
+	failure("%s: errno = %d (%s)", func, errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	close(fd);
+
+	assertMakeDir("d", 0700);
+
+	/*
+	 * Create file d/f1 with acl2
+	 *
+	 * This differs from acl1 in the u:1: and g:15: permissions.
+	 *
+	 * This file deliberately has the same name but a different ACL.
+	 * Github Issue #777 explains how libarchive's directory traversal
+	 * did not always correctly enter directories before attempting
+	 * to read ACLs, resulting in reading the ACL from a like-named
+	 * file in the wrong directory.
+	 */
+#if HAVE_SUN_ACL
+	acl2_text = "user::rwx,"
+	    "group::rwx,"
+	    "other:---,"
+	    "user:1:r--,"
+	    "group:15:r--,"
+	    "mask:rwx";
+	aclent_t aclp2[] = {
+	    { USER_OBJ, -1, 4 | 2 | 1 },
+	    { USER, 1, 4 },
+	    { GROUP_OBJ, -1, 4 | 2 | 1},
+	    { GROUP, 15, 4 },
+	    { CLASS_OBJ, -1, 4 | 2 | 1},
+	    { OTHER_OBJ, -1, 0 }
+	};
+#else
+	acl2_text = "user::rwx\n"
+	    "group::rwx\n"
+	    "other::---\n"
+	    "user:1:r--\n"
+	    "group:15:r--\n"
+	    "mask::rwx";
+	acl2 = acl_from_text(acl2_text);
+	failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno));
+	assert((void *)acl2 != NULL);
+#endif
+	fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
+	failure("Could not create test file?!");
+	if (!assert(fd >= 0)) {
+#if !HAVE_SUN_ACL
+		acl_free(acl2);
+#endif
+		return;
+	}
+#if HAVE_SUN_ACL
+	func = "facl()";
+	n = facl(fd, SETACL, (int)(sizeof(aclp2) / sizeof(aclp2[0])), aclp2);
+#else
+	func = "acl_set_fd()";
+	n = acl_set_fd(fd, acl2);
+	acl_free(acl2);
+#endif
+	if (n != 0)
+		close(fd);
+	failure("%s: errno = %d (%s)", func, errno, strerror(errno));
+	assertEqualInt(0, n);
+	close(fd);
+
+	/* Create nested directory d2 with default ACLs */
+	assertMakeDir("d/d2", 0755);
+
+#if HAVE_SUN_ACL
+	acl3_text = "user::rwx,"
+	    "group::r-x,"
+	    "other:r-x,"
+	    "user:2:r--,"
+	    "group:16:-w-,"
+	    "mask:rwx,"
+	    "default:user::rwx,"
+	    "default:user:1:r--,"
+	    "default:group::r-x,"
+	    "default:group:15:r--,"
+	    "default:mask:rwx,"
+	    "default:other:r-x";
+	aclent_t aclp3[] = {
+	    { USER_OBJ, -1, 4 | 2 | 1 },
+	    { USER, 2, 4 },
+	    { GROUP_OBJ, -1, 4 | 1 },
+	    { GROUP, 16, 2 },
+	    { CLASS_OBJ, -1, 4 | 2 | 1 },
+	    { OTHER_OBJ, -1, 4 | 1 },
+	    { USER_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1 },
+	    { USER | ACL_DEFAULT, 1, 4 },
+	    { GROUP_OBJ | ACL_DEFAULT, -1, 4 | 1 },
+	    { GROUP | ACL_DEFAULT, 15, 4 },
+	    { CLASS_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1},
+	    { OTHER_OBJ | ACL_DEFAULT, -1, 4 | 1 }
+	};
+#else
+	acl3_text = "user::rwx\n"
+	    "user:1:r--\n"
+	    "group::r-x\n"
+	    "group:15:r--\n"
+	    "mask::rwx\n"
+	    "other::r-x";
+	acl3 = acl_from_text(acl3_text);
+	failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno));
+	assert((void *)acl3 != NULL);
+#endif
+
+#if HAVE_SUN_ACL
+	func = "acl()";
+	n = acl("d/d2", SETACL, (int)(sizeof(aclp3) / sizeof(aclp3[0])), aclp3);
+#else
+	func = "acl_set_file()";
+	n = acl_set_file("d/d2", ACL_TYPE_DEFAULT, acl3);
+	acl_free(acl3);
+#endif
+	failure("%s: errno = %d (%s)", func, errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	/* Create a read-from-disk object. */
+	assert(NULL != (a = archive_read_disk_new()));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "."));
+	assert(NULL != (ae = archive_entry_new()));
+
+#if HAVE_SUN_ACL
+	flags = ARCHIVE_ENTRY_ACL_TYPE_POSIX1E
+	    | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA
+	    | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS;
+	dflags = flags;
+#else
+	flags = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+	dflags = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+#endif
+
+	/* Walk the dir until we see both of the files */
+	while (ARCHIVE_OK == archive_read_next_header2(a, ae)) {
+		archive_read_disk_descend(a);
+		if (strcmp(archive_entry_pathname(ae), "./f1") == 0) {
+			acl_text = archive_entry_acl_to_text(ae, NULL, flags);
+			assertEqualString(acl_text, acl1_text);
+			free(acl_text);
+		} else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) {
+			acl_text = archive_entry_acl_to_text(ae, NULL, flags);
+			assertEqualString(acl_text, acl2_text);
+			free(acl_text);
+		} else if (strcmp(archive_entry_pathname(ae), "./d/d2") == 0) {
+			acl_text = archive_entry_acl_to_text(ae, NULL, dflags);
+			assertEqualString(acl_text, acl3_text);
+			free(acl_text);
+		}
+	}
+
+	archive_entry_free(ae);
+	assertEqualInt(ARCHIVE_OK, archive_free(a));
+#endif
+}
diff --git a/libarchive/test/test_acl_posix1e.c b/libarchive/test/test_acl_posix1e.c
index 9984d44..fa2628d 100644
--- a/libarchive/test/test_acl_posix1e.c
+++ b/libarchive/test/test_acl_posix1e.c
@@ -27,21 +27,14 @@
 
 /*
  * Exercise the system-independent portion of the ACL support.
- * Check that archive_entry objects can save and restore POSIX.1e-style ACL data.
+ * Check that archive_entry objects can save and restore POSIX.1e-style
+ * ACL data.
  *
  * This should work on all systems, regardless of whether local
  * filesystems support ACLs or not.
  */
 
-struct acl_t {
-	int type;  /* Type of ACL: "access" or "default" */
-	int permset; /* Permissions for this class of users. */
-	int tag; /* Owner, User, Owning group, group, other, etc. */
-	int qual; /* GID or UID of user/group, depending on tag. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
+static struct archive_test_acl_t acls0[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -50,7 +43,7 @@
 	  ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
 };
 
-static struct acl_t acls1[] = {
+static struct archive_test_acl_t acls1[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -61,7 +54,7 @@
 	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
 };
 
-static struct acl_t acls2[] = {
+static struct archive_test_acl_t acls2[] = {
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
@@ -80,7 +73,7 @@
  * NFS4 entry types; attempts to set these on top of POSIX.1e
  * attributes should fail.
  */
-static struct acl_t acls_nfs4[] = {
+static struct archive_test_acl_t acls_nfs4[] = {
 	/* NFS4 types */
 	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_READ,
 	  ARCHIVE_ENTRY_ACL_USER, 78, "" },
@@ -104,106 +97,6 @@
 	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
 };
 
-static void
-set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
-{
-	int i;
-
-	archive_entry_acl_clear(ae);
-	for (i = 0; i < n; i++) {
-		archive_entry_acl_add_entry(ae,
-		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
-		    acls[i].name);
-	}
-}
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
-{
-	if (type != acl->type)
-		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 (qual != acl->qual)
-		return (0);
-	if (name == NULL) {
-		if (acl->name == NULL || acl->name[0] == '\0')
-			return (1);
-		return (0);
-	}
-	if (acl->name == NULL) {
-		if (name[0] == '\0')
-			return (1);
-		return (0);
-	}
-	return (0 == strcmp(name, acl->name));
-}
-
-static void
-compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
-{
-	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,
-			 ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-			 &type, &permset, &tag, &qual, &name))) {
-		for (i = 0, matched = 0; i < n && !matched; i++) {
-			if (acl_match(&acls[marker[i]], type, permset,
-				tag, qual, 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");
-			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");
-			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");
-			failure("OTHER permset (%02o) != other mode (%02o)",
-			    permset, mode & 07);
-			assert((permset << 0) == (mode & 0007));
-		} else {
-			failure("Could not find match for ACL "
-			    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
-			    type, permset, tag, qual, 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,qual=%d,name=``%s'')",
-	    acls[marker[0]].type, acls[marker[0]].permset,
-	    acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
-	assert(n == 0); /* Number of ACLs not matched should == 0 */
-	free(marker);
-}
-
 DEFINE_TEST(test_acl_posix1e)
 {
 	struct archive_entry *ae;
@@ -223,28 +116,36 @@
 	 * triggering unnecessary extensions.  It's better to identify
 	 * trivial ACLs at the point they are being read from disk.
 	 */
-	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
 	failure("Basic ACLs shouldn't be stored as extended ACLs");
 	assert(0 == archive_entry_acl_reset(ae, 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);
 
-
 	/* With any extended ACL entry, we should read back a full set. */
-	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
 	failure("One extended ACL should flag all ACLs to be returned.");
+
+	/* Check that entry contains only POSIX.1e types */
+	assert((archive_entry_acl_types(ae) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0);
+	assert((archive_entry_acl_types(ae) &
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0);
+
 	assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-	compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
 	failure("Basic ACLs should set mode to 0142, not %04o",
 	    archive_entry_mode(ae)&0777);
 	assert((archive_entry_mode(ae) & 0777) == 0142);
 
 
 	/* A more extensive set of ACLs. */
-	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
 	assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-	compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+	assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
 	failure("Basic ACLs should set mode to 0543, not %04o",
 	    archive_entry_mode(ae)&0777);
 	assert((archive_entry_mode(ae) & 0777) == 0543);
@@ -253,7 +154,7 @@
 	 * Check that clearing ACLs gets rid of them all by repeating
 	 * the first test.
 	 */
-	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
 	failure("Basic ACLs shouldn't be stored as extended ACLs");
 	assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
 	failure("Basic ACLs should set mode to 0142, not %04o",
@@ -264,9 +165,9 @@
 	 * Different types of malformed ACL entries that should
 	 * fail when added to existing POSIX.1e ACLs.
 	 */
-	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
 	for (i = 0; i < (int)(sizeof(acls_nfs4)/sizeof(acls_nfs4[0])); ++i) {
-		struct acl_t *p = &acls_nfs4[i];
+		struct archive_test_acl_t *p = &acls_nfs4[i];
 		failure("Malformed ACL test #%d", i);
 		assertEqualInt(ARCHIVE_FAILED,
 		    archive_entry_acl_add_entry(ae,
diff --git a/libarchive/test/test_acl_text.c b/libarchive/test/test_acl_text.c
new file mode 100644
index 0000000..8072893
--- /dev/null
+++ b/libarchive/test/test_acl_text.c
@@ -0,0 +1,473 @@
+/*-
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+/*
+ * Test converting ACLs to text, both wide and non-wide
+ *
+ * This should work on all systems, regardless of whether local
+ * filesystems support ACLs or not.
+ */
+
+static struct archive_test_acl_t acls0[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ |
+	    ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 100, "user100" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, 1000, "user1000" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ |
+	    ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_READ |
+	    ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 101, "user101"},
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+	    ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_GROUP, 79, "group79" },
+};
+
+static struct archive_test_acl_t acls1[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_DELETE_CHILD |
+	    ARCHIVE_ENTRY_ACL_DELETE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY |
+	    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
+	  ARCHIVE_ENTRY_ACL_USER, 101, "user101" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+const char* acltext[] = {
+	"user::rwx\n"
+	"group::r-x\n"
+	"other::r-x\n"
+	"user:user100:r-x\n"
+	"user:user1000:---\n"
+	"group:group78:rwx\n"
+	"default:user::r-x\n"
+	"default:group::r-x\n"
+	"default:other::---\n"
+	"default:user:user101:r-x\n"
+	"default:group:group79:--x",
+
+	"user::rwx\n"
+	"group::r-x\n"
+	"other::r-x\n"
+	"user:user100:r-x:100\n"
+	"user:user1000:---:1000\n"
+	"group:group78:rwx:78\n"
+	"default:user::r-x\n"
+	"default:group::r-x\n"
+	"default:other::---\n"
+	"default:user:user101:r-x:101\n"
+	"default:group:group79:--x:79",
+
+	"u::rwx\n"
+	"g::r-x\n"
+	"o::r-x\n"
+	"u:user100:r-x:100\n"
+	"u:user1000:---:1000\n"
+	"g:group78:rwx:78\n"
+	"d:user::r-x\n"
+	"d:group::r-x\n"
+	"d:other::---\n"
+	"d:user:user101:r-x:101\n"
+	"d:group:group79:--x:79",
+
+	"user::rwx\n"
+	"group::r-x\n"
+	"other::r-x\n"
+	"user:user100:r-x\n"
+	"user:user1000:---\n"
+	"group:group78:rwx",
+
+	"user::rwx,"
+	"group::r-x,"
+	"other::r-x,"
+	"user:user100:r-x,"
+	"user:user1000:---,"
+	"group:group78:rwx",
+
+	"user::rwx\n"
+	"group::r-x\n"
+	"other::r-x\n"
+	"user:user100:r-x:100\n"
+	"user:user1000:---:1000\n"
+	"group:group78:rwx:78",
+
+	"user::r-x\n"
+	"group::r-x\n"
+	"other::---\n"
+	"user:user101:r-x\n"
+	"group:group79:--x",
+
+	"user::r-x\n"
+	"group::r-x\n"
+	"other::---\n"
+	"user:user101:r-x:101\n"
+	"group:group79:--x:79",
+
+	"default:user::r-x\n"
+	"default:group::r-x\n"
+	"default:other::---\n"
+	"default:user:user101:r-x\n"
+	"default:group:group79:--x",
+
+	"user:user77:rw-p--a-R-c-o-:-------:allow\n"
+	"user:user101:-w-pdD--------:fdin---:deny\n"
+	"group:group78:r-----a-R-c---:------I:allow\n"
+	"owner@:rwxp--aARWcCo-:-------:allow\n"
+	"group@:rw-p--a-R-c---:-------:allow\n"
+	"everyone@:r-----a-R-c--s:-------:allow",
+
+	"user:user77:rw-p--a-R-c-o-:-------:allow:77\n"
+	"user:user101:-w-pdD--------:fdin---:deny:101\n"
+	"group:group78:r-----a-R-c---:------I:allow:78\n"
+	"owner@:rwxp--aARWcCo-:-------:allow\n"
+	"group@:rw-p--a-R-c---:-------:allow\n"
+	"everyone@:r-----a-R-c--s:-------:allow",
+
+	"user:user77:rwpaRco::allow:77\n"
+	"user:user101:wpdD:fdin:deny:101\n"
+	"group:group78:raRc:I:allow:78\n"
+	"owner@:rwxpaARWcCo::allow\n"
+	"group@:rwpaRc::allow\n"
+	"everyone@:raRcs::allow"
+};
+
+static wchar_t *
+convert_s_to_ws(const char *s)
+{
+	size_t len;
+	wchar_t *ws = NULL;
+
+	if (s != NULL) {
+		len = strlen(s) + 1;
+		ws = malloc(len * sizeof(wchar_t));
+		assert(mbstowcs(ws, s, len) != (size_t)-1);
+	}
+
+	return (ws);
+}
+
+static void
+compare_acl_text(struct archive_entry *ae, int flags, const char *s)
+{
+	char *text;
+	wchar_t *wtext;
+	wchar_t *ws;
+	ssize_t slen;
+
+	ws = convert_s_to_ws(s);
+
+	text = archive_entry_acl_to_text(ae, &slen, flags);
+	assertEqualString(text, s);
+	if (text != NULL)
+		assertEqualInt(strlen(text), slen);
+	wtext = archive_entry_acl_to_text_w(ae, &slen, flags);
+	assertEqualWString(wtext, ws);
+	if (wtext != NULL) {
+		assertEqualInt(wcslen(wtext), slen);
+	}
+	free(text);
+	free(wtext);
+	free(ws);
+}
+
+DEFINE_TEST(test_acl_from_text)
+{
+	struct archive_entry *ae;
+	wchar_t *ws = NULL;
+
+	/* Create an empty archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+
+	/* 1a. Read POSIX.1e access ACLs from text */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[5],
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+
+	/* 1b. Now read POSIX.1e default ACLs and append them */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[7],
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	/* 1a and 1b with wide strings */
+	ws = convert_s_to_ws(acltext[5]);
+
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0755);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+
+	free(ws);
+	ws = convert_s_to_ws(acltext[7]);
+
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	/* 2. Read POSIX.1e default ACLs from text */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[7],
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0);
+	assertEqualInt(5, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	archive_entry_acl_clear(ae);
+
+	/* ws is still acltext[7] */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0);
+	assertEqualInt(5, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	archive_entry_acl_clear(ae);
+
+	/* 3. Read POSIX.1e access and default ACLs from text */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[1],
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	free(ws);
+	ws = convert_s_to_ws(acltext[1]);
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	/* 4. Read POSIX.1e access and default ACLs from text (short form) */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[2],
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	free(ws);
+	ws = convert_s_to_ws(acltext[2]);
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, 0755);
+	assertEqualInt(11, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E));
+	archive_entry_acl_clear(ae);
+
+	/* 5. Read NFSv4 ACLs from text */
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text(ae, acltext[10],
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	archive_entry_acl_clear(ae);
+
+	free(ws);
+	ws = convert_s_to_ws(acltext[10]);
+
+	assertEqualInt(ARCHIVE_OK,
+	    archive_entry_acl_from_text_w(ae, ws,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	archive_entry_acl_clear(ae);
+
+	free(ws);
+	archive_entry_free(ae);
+}
+
+DEFINE_TEST(test_acl_to_text)
+{
+	struct archive_entry *ae;
+
+	/* Create an empty archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+
+	/* Write POSIX.1e ACLs  */
+	assertEntrySetAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+
+	/* No flags should give output like getfacl(1) on linux */
+	compare_acl_text(ae, 0, acltext[0]);
+
+	/* This should give the same output as previous test */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[0]);
+
+	/* This should give the same output as previous two tests */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+	    ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[0]);
+
+	/* POSIX.1e access and default ACLs with appended ID */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[1]);
+
+	/* POSIX.1e access acls only, like getfacl(1) on FreeBSD */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, acltext[3]);
+
+	/* POSIX.1e access acls separated with comma */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+	    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA,
+	    acltext[4]);
+
+	/* POSIX.1e access acls with appended user or group ID */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+	    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[5]);
+
+	/* POSIX.1e default acls */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, acltext[6]);
+
+	/* POSIX.1e default acls with appended user or group ID */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+	    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[7]);
+
+	/* POSIX.1e default acls prefixed with default: */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+	    ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT, acltext[8]);
+
+	/* Write NFSv4 ACLs */
+	assertEntrySetAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+
+	/* NFSv4 ACLs like getfacl(1) on FreeBSD */
+	compare_acl_text(ae, 0, acltext[9]);
+
+	/* NFSv4 ACLs like "getfacl -i" on FreeBSD */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID, acltext[10]);
+
+	/* NFSv4 ACLs like "getfacl -i" on FreeBSD with stripped minus chars */
+	compare_acl_text(ae, ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+	    ARCHIVE_ENTRY_ACL_STYLE_COMPACT, acltext[11]);
+
+	archive_entry_free(ae);
+}
diff --git a/libarchive/test/test_archive_api_feature.c b/libarchive/test/test_archive_api_feature.c
index d551e6a..60773ad 100644
--- a/libarchive/test/test_archive_api_feature.c
+++ b/libarchive/test/test_archive_api_feature.c
@@ -42,8 +42,12 @@
 	if (strlen(buff) < strlen(archive_version_string())) {
 		p = archive_version_string() + strlen(buff);
 		failure("Version string is: %s", archive_version_string());
-		assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd');
-		++p;
+		if (p[0] == 'd'&& p[1] == 'e' && p[2] == 'v')
+			p += 3;
+		else {
+			assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd');
+			++p;
+		}
 		failure("Version string is: %s", archive_version_string());
 		assert(*p == '\0');
 	}
diff --git a/libarchive/test/test_archive_read_add_passphrase.c b/libarchive/test/test_archive_read_add_passphrase.c
index 68dec10..0ce5a76 100644
--- a/libarchive/test/test_archive_read_add_passphrase.c
+++ b/libarchive/test/test_archive_read_add_passphrase.c
@@ -191,7 +191,7 @@
 	/* Fist call, we should get "passCallBack" as a passphrase. */
 	assertEqualString("passCallBack", __archive_read_next_passphrase(ar));
 	__archive_read_reset_passphrase(ar);
-	/* After reset passphrase, we should get "passCallBack"passphrase. */
+	/* After reset passphrase, we should get "passCallBack" passphrase. */
 	assertEqualString("passCallBack", __archive_read_next_passphrase(ar));
 	/* Second call, we should get NULL which means all the passphrases
 	 * are passed already. */
diff --git a/libarchive/test/test_archive_string.c b/libarchive/test/test_archive_string.c
index 9e3f907..7fa743b 100644
--- a/libarchive/test/test_archive_string.c
+++ b/libarchive/test/test_archive_string.c
@@ -67,6 +67,8 @@
 
 	assert(&s == archive_string_ensure(&s, EXTENT + 1));
 	assertNonNULLString(0, 2 * EXTENT, s);
+
+	archive_string_free(&s);
 }
 
 static void
@@ -92,6 +94,8 @@
 	/* non-empty target, non-empty source */
 	assert(&s == archive_strcat(&s, "baz"));
 	assertExactString(8, EXTENT, "fubarbaz", s);
+
+	archive_string_free(&s);
 }
 
 static void
@@ -109,6 +113,8 @@
 	/* non-empty target */
 	archive_strappend_char(&s, 'Y');
 	assertExactString(2, EXTENT, "XY", s);
+
+	archive_string_free(&s);
 }
 
 /* archive_strnXXX() tests focus on length handling.
@@ -134,6 +140,8 @@
 	/* long read is ok too! */
 	assert(&s == archive_strncat(&s, "snafu", 8));
 	assertExactString(13, EXTENT, "snafubarsnafu", s);
+
+	archive_string_free(&s);
 }
 
 static void
@@ -155,6 +163,8 @@
 	/* long read is ok too! */
 	assert(&s == archive_strncpy(&s, "snafu", 8));
 	assertExactString(5, EXTENT, "snafu", s);
+
+	archive_string_free(&s);
 }
 
 static void
@@ -176,6 +186,8 @@
 	/* dirty target, empty source */
 	assert(&s == archive_strcpy(&s, ""));
 	assertExactString(0, EXTENT, "", s);
+
+	archive_string_free(&s);
 }
 
 static void
@@ -222,6 +234,11 @@
 	archive_string_concat(&t, &s);
 	assertExactString(5, EXTENT, "snafu", s);
 	assertExactString(5, EXTENT, "snafu", t);
+
+	archive_string_free(&v);
+	archive_string_free(&u);
+	archive_string_free(&t);
+	archive_string_free(&s);
 }
 
 static void
@@ -274,6 +291,11 @@
 	archive_string_copy(&t, &s);
 	assertExactString(5, EXTENT, "fubar", s);
 	assertExactString(5, EXTENT, "fubar", t);
+
+	archive_string_free(&v);
+	archive_string_free(&u);
+	archive_string_free(&t);
+	archive_string_free(&s);
 }
 
 static void
@@ -328,6 +350,8 @@
 	archive_string_empty(&s);
 	archive_string_sprintf(&s, "%d", 1234567890);
 	assertExactString(10, 8 * EXTENT, "1234567890", s);
+
+	archive_string_free(&s);
 }
 
 DEFINE_TEST(test_archive_string)
diff --git a/libarchive/test/test_compat_gtar.c b/libarchive/test/test_compat_gtar.c
index def24aa..7066924 100644
--- a/libarchive/test/test_compat_gtar.c
+++ b/libarchive/test/test_compat_gtar.c
@@ -142,6 +142,8 @@
 	assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_NONE);
 	assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR);
 
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
 
 DEFINE_TEST(test_compat_gtar)
diff --git a/libarchive/test/test_compat_solaris_tar_acl.c b/libarchive/test/test_compat_solaris_tar_acl.c
index a0cf9a8..8159772 100644
--- a/libarchive/test/test_compat_solaris_tar_acl.c
+++ b/libarchive/test/test_compat_solaris_tar_acl.c
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,104 +24,242 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/lib/libarchive/test/test_compat_solaris_tar_acl.c 201247 2009-12-30 05:59:21Z kientzle $");
+__FBSDID("$FreeBSD$");
 
 /*
- * Exercise support for reading Solaris-style ACL data
- * from tar archives.
+ * Verify reading entries with POSIX.1e and NFSv4 ACLs from archives created
+ * with Solaris tar.
  *
- * This should work on all systems, regardless of whether local
- * filesystems support ACLs or not.
+ * This should work on all systems, regardless of whether local filesystems
+ * support ACLs or not.
  */
 
+static struct archive_test_acl_t acls0[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 71, "lp" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 666, "666" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 1000, "1000" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls1[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 2, "bin" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP, 3, "sys" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1 ,"" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 2, "bin" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP, 3, "sys" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls3[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP, 12, "daemon" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP, 2, "bin" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER, 4, "adm" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+static struct archive_test_acl_t acls4[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
+	  ARCHIVE_ENTRY_ACL_USER, 1100, "1100" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+	  ARCHIVE_ENTRY_ACL_GROUP, 4, "adm" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_DELETE_CHILD |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
 DEFINE_TEST(test_compat_solaris_tar_acl)
 {
+	char name[] = "test_compat_solaris_tar_acl.tar";
 	struct archive *a;
 	struct archive_entry *ae;
-	const char *reference1 = "test_compat_solaris_tar_acl.tar";
-	int type, permset, tag, qual;
-	const char *name;
 
-	/* Sample file generated on Solaris 10 */
-	extract_reference_file(reference1);
+	/* Read archive file */
 	assert(NULL != (a = archive_read_new()));
-	assertA(0 == archive_read_support_format_all(a));
-	assertA(0 == archive_read_support_filter_all(a));
-	assertA(0 == archive_read_open_filename(a, reference1, 512));
+        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));
 
-	/* Archive has 1 entry with some ACLs set on it. */
+	/* First item has access ACLs */
 	assertA(0 == archive_read_next_header(a, &ae));
+	failure("One extended ACL should flag all ACLs to be returned.");
+	assertEqualInt(7, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0644);
 	failure("Basic ACLs should set mode to 0644, not %04o",
 	    archive_entry_mode(ae)&0777);
-	assertEqualInt((archive_entry_mode(ae) & 0777), 0644);
-	assertEqualInt(7, archive_entry_acl_reset(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(006, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_USER_OBJ, tag);
-	assertEqualInt(-1, qual);
-	assert(name == NULL);
+	assert((archive_entry_mode(ae) & 0777) == 0644);
 
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(004, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_GROUP_OBJ, tag);
-	assertEqualInt(-1, qual);
-	assert(name == NULL);
+	/* Second item has default and access ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0750);
+	failure("Basic ACLs should set mode to 0750, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0750);
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+	assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0750);
 
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(004, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_OTHER, tag);
-	assertEqualInt(-1, qual);
-	assert(name == NULL);
+	/* Third item has NFS4 ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(001, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
-	assertEqualInt(71, qual);
-	assertEqualString(name, "lp");
-
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(004, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
-	assertEqualInt(666, qual);
-	assertEqualString(name, "666");
-
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(007, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_USER, tag);
-	assertEqualInt(1000, qual);
-	assertEqualString(name, "trasz");
-
-	assertEqualInt(ARCHIVE_OK, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
-	assertEqualInt(ARCHIVE_ENTRY_ACL_TYPE_ACCESS, type);
-	assertEqualInt(004, permset);
-	assertEqualInt(ARCHIVE_ENTRY_ACL_MASK, tag);
-	assertEqualInt(-1, qual);
-	assertEqualString(name, NULL);
-
-	assertEqualInt(ARCHIVE_EOF, archive_entry_acl_next(ae,
-		ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
-		&type, &permset, &tag, &qual, &name));
+	/* Fourth item has NFS4 ACLs and inheritance flags */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(5, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
 
 	/* Close the archive. */
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
diff --git a/libarchive/test/test_compat_solaris_tar_acl.tar.uu b/libarchive/test/test_compat_solaris_tar_acl.tar.uu
index 54aabc2..028dd61 100644
--- a/libarchive/test/test_compat_solaris_tar_acl.tar.uu
+++ b/libarchive/test/test_compat_solaris_tar_acl.tar.uu
@@ -1,61 +1,163 @@
-$FreeBSD: head/lib/libarchive/test/test_compat_solaris_tar_acl.tar.uu 191576 2009-04-27 18:27:54Z kientzle $
-begin 644 test_acl_solaris.tar
-M9FEL92UW:71H+7!O<VEX+6%C;',`````````````````````````````````
-M````````````````````````````````````````````````````````````
-M`````````````#`P,#`V-#0`,#`P,3<U,``P,#`P,#`P`#`P,#`P,#`P,30T
-M`#$Q,3<T-C`T,34W`#`P,34Q-S8`00``````````````````````````````
-M````````````````````````````````````````````````````````````
-M``````````````````````````````````````````!U<W1A<@`P,'1R87-Z
-M````````````````````````````````````<F]O=```````````````````
-M```````````````````P,#`P,C$P`#`P,#`P,3``````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M```````````````````````Q,#`P,#`W`'5S97(Z.G)W+2QU<V5R.FQP.BTM
-M>#HW,2QU<V5R.C8V-CIR+2TZ-C8V+'5S97(Z=')A<WHZ<G=X.C$P,#`L9W)O
-M=7`Z.G(M+2QM87-K.G(M+2QO=&AE<CIR+2T``````````3````````/-@```
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````!%&8`````````&L`````,3`P,#`P-P!U
+begin 644 test_compat_solaris_tar_acl.tar
+M9FEL92UW:71H+7!O<VEX+6%C;',                                 
+M                                                            
+M             # P,# V-#0 ,# P,3<U,  P,# P,# P # P,# P,# P,30S
+M #$Q,3<T-C T,34W # P,30Q,C$ 00                              
+M                                                            
+M                                          !U<W1A<@ P,       
+M                                    <F]O=                   
+M                   P,# P-#$T # P,# P,#,                     
+M                                                            
+M                                                            
+M                                                            
+M                       Q,# P,# W '5S97(Z.G)W+2QU<V5R.FQP.BTM
+M>#HW,2QU<V5R.C8V-CIR+2TZ-C8V+'5S97(Z,3 P,#IR=W@Z,3 P,"QG<F]U
+M<#HZ<BTM+&UA<VLZ<BTM+&]T:&5R.G(M+0       # !                
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                           (Q@@(     &L         ,3 P,# P-P!U
 M<V5R.CIR=RTL=7-E<CIL<#HM+7@Z-S$L=7-E<CHV-C8Z<BTM.C8V-BQU<V5R
-M.G1R87-Z.G)W>#HQ,#`P+&=R;W5P.CIR+2TL;6%S:SIR+69I;&4M=VET:"UP
-M;W-I>"UA8VQS````````````````````````````````````````````````
-M```````````````````````````````````````````````````````````P
-M,#`P-C0T`#`P,#$W-3``,#`P,#`P,``P,#`P,#`P,#`P,``Q,3$W-#8P-#$U
-M-P`P,#$U,30T`#``````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````=7-T87(`,#!T<F%S>@``````````````
-M`````````````````````')O;W0`````````````````````````````````
-M````,#`P,#(Q,``P,#`P,#$P````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-M````````````````````````````````````````````````````````````
-H````````````````````````````````````````````````````````
-`
+M.C$P,# Z<G=X.C$P,# L9W)O=7 Z.G(M+2QM87-K.G(M+69I;&4M=VET:"UP
+M;W-I>"UA8VQS                                                
+M                                                           P
+M,# P-C0T # P,#$W-3  ,# P,# P,  P,# P,# P,# P,  Q,3$W-#8P-#$U
+M-P P,#$T,#<P #                                              
+M                                                            
+M                            =7-T87( ,#                      
+M                     ')O;W0                                 
+M    ,# P,#0Q-  P,# P,# S                                    
+M                                                            
+M                                                            
+M                                                            
+M        9&ER+7=I=&@M<&]S:7@M86-L<R\                         
+M                                                            
+M                     # P,# W-3  ,# P,3<U,  P,# P,# P # P,# P
+M,# P,S P #$S,#,V-3$R,C4T # P,30P,C, 00                      
+M                                                            
+M                                                  !U<W1A<@ P
+M,                                           <F]O=           
+M                           P,# P-#$T # P,# P,#,             
+M                                                            
+M                                                            
+M                                                            
+M                               Q,# P,#$T '5S97(Z.G)W>"QU<V5R
+M.F)I;CIR=W@Z,BQG<F]U<#HZ<BUX+&=R;W5P.G-Y<SIR+7@Z,RQM87-K.G(M
+M>"QO=&AE<CHM+2TL9&5F875L='5S97(Z.G)W>"QD969A=6QT=7-E<CIB:6XZ
+M<G=X.C(L9&5F875L=&=R;W5P.CIR+7@L9&5F875L=&=R;W5P.G-Y<SIR+7@Z
+M,RQD969A=6QT;6%S:SIR=W@L9&5F875L=&]T:&5R.BTM+0   @       #C%
+M" @                                                         
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                     &1I<BUW
+M:71H+7!O<VEX+6%C;',O                                        
+M                                                            
+M       P,# P-S4P # P,#$W-3  ,# P,# P,  P,# P,# P,# P,  Q,S S
+M-C4Q,C(U-  P,#$T,# T #4                                     
+M                                                            
+M                                    =7-T87( ,#              
+M                             ')O;W0                         
+M            ,# P,#0Q-  P,# P,# S                            
+M                                                            
+M                                                            
+M                                                            
+M                9FEL92UW:71H+6YF<W8T+6%C;',                 
+M                                                            
+M                             # P,# V-#  ,# P,3<U,  P,# P,# P
+M # P,# P,# P,S8T #$S,#,V-3$S-C0Q # P,30P,34 00              
+M                                                            
+M                                                          !U
+M<W1A<@ P,                                           <F]O=   
+M                                   P,# P-#$T # R,# P,#(     
+M                                                            
+M                                                            
+M                                                            
+M                                       S,# P,# V &=R;W5P.F1A
+M96UO;CIR=WAP+2UA05)78T-O<SHM+2TM+2TM.F1E;GDZ,3(L9W)O=7 Z8FEN
+M.G)W>' M+2TM+2TM+2US.BTM+2TM+2TZ86QL;W<Z,BQU<V5R.F%D;3IR+2TM
+M+2UA+5(M8RTM<SHM+2TM+2TM.F%L;&]W.C0L;W=N97) .G)W+7 M+6%!4E=C
+M0V]S.BTM+2TM+2TZ86QL;W<L9W)O=7! .G(M+2TM+6$M4BUC+2US.BTM+2TM
+M+2TZ86QL;W<L979E<GEO;F5 .BTM+2TM+6$M4BUC+2US.BTM+2TM+2TZ86QL
+M;W<      &@                                                 
+M                                                            
+M                            F-8("     #[         #,P,# P,#8 
+M9W)O=7 Z9&%E;6]N.G)W>' M+6%!4E=C0V]S.BTM+2TM+2TZ9&5N>3HQ,BQG
+M<F]U<#IB:6XZ<G=X<"TM+2TM+2TM+7,Z+2TM+2TM+3IA;&QO=SHR+'5S97(Z
+M861M.G(M+2TM+6$M4BUC+2US.BTM+2TM+2TZ86QL;W<Z-"QO=VYE<D Z<G<M
+M<&9I;&4M=VET:"UN9G-V-"UA8VQS                                
+M                                                            
+M               P,# P-C0P # P,#$W-3  ,# P,# P,  P,# P,# P,# P
+M,  Q,S S-C4Q,S8T,0 P,#$S-S4W #                              
+M                                                            
+M                                            =7-T87( ,#      
+M                                     ')O;W0                 
+M                    ,# P,#0Q-  P,C P,# R                    
+M                                                            
+M                                                            
+M                                                            
+M                        9&ER+7=I=&@M;F9S=C0M86-L<R\         
+M                                                            
+M                                     # P,# W-3  ,# P,# P,  P
+M,# P,# P # P,# P,# P,S$T #$S,#,V-3$S-S,U # P,30V,C, 00      
+M                                                            
+M                                                            
+M      !U<W1A<@ P,')O;W0                                     
+M<F]O=                                      P,# P-#$T # R,# P
+M,#(                                                         
+M                                                            
+M                                                            
+M                                               S,# P,# U '5S
+M97(Z,3$P,#IR=WAP+2UA05)78T-O<SIF9&DM+2TM.F%L;&]W.C$Q,# L9W)O
+M=7 Z861M.G(M+2TM+6$M4BUC+2US.F9D+2TM+2TZ86QL;W<Z-"QO=VYE<D Z
+M<G=X<"U$84%25V-#;W,Z+2TM+2TM+3IA;&QO=RQG<F]U<$ Z<BUX+2TM82U2
+M+6,M+7,Z+2TM+2TM+3IA;&QO=RQE=F5R>6]N94 Z+2TM+2TM82U2+6,M+7,Z
+M+2TM+2TM+3IA;&QO=P      4                                   
+M                                                            
+M          "HUP@(     -,         ,S P,# P-0!U<V5R.C$Q,# Z<G=X
+M<"TM84%25V-#;W,Z9F1I+2TM+3IA;&QO=SHQ,3 P+&=R;W5P.F%D;3IR+2TM
+M+2UA+5(M8RTM<SIF9"TM+2TM.F%L;&]W.C0L;W=N97) .G)W>' M1&%!4E=C
+M0V]S.BTM+2TM+2TZ86QL;W<L9W)O=7! .G(M>"TM+6$M4BUC+2US.BTM+2TM
+M+2TZ86QL;W<L979E<GEO;F5 .BTM+2TM+6$M4BUC+2US.BTM+2TM+2TZ86QL
+M;W<      &1I<BUW:71H+6YF<W8T+6%C;',O                        
+M                                                            
+M                       P,# P-S4P # P,# P,#  ,# P,# P,  P,# P
+M,# P,# P,  Q,S S-C4Q,S<S-0 P,#$T-3<W #4                     
+M                                                            
+M                                                    =7-T87( 
+M,#!R;V]T                                     ')O;W0         
+M                            ,# P,#0Q-  P,C P,# R            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+M                                                            
+-                    
+ 
 end
diff --git a/libarchive/test/test_compat_star_acl.c b/libarchive/test/test_compat_star_acl.c
new file mode 100644
index 0000000..8247101
--- /dev/null
+++ b/libarchive/test/test_compat_star_acl.c
@@ -0,0 +1,321 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+/*
+ * Verify reading entries with POSIX.1e and NFSv4 ACLs from archives created
+ * by star.
+ *
+ * This should work on all systems, regardless of whether local filesystems
+ * support ACLs or not.
+ */
+
+static struct archive_test_acl_t acls0[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls1[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, -1, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1 ,"" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, -1, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_GROUP, -1, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_MASK, -1, ""},
+	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct archive_test_acl_t acls3[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+static struct archive_test_acl_t acls4[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+static struct archive_test_acl_t acls5[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_DENY,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_DELETE |
+	    ARCHIVE_ENTRY_ACL_DELETE_CHILD |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE |
+	    ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT |
+	    ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+	    ARCHIVE_ENTRY_ACL_READ_DATA |
+	    ARCHIVE_ENTRY_ACL_EXECUTE |
+	    ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+	  ARCHIVE_ENTRY_ACL_EVERYONE, 0, "" },
+};
+
+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));
+	assertEntryCompareAcls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0142);
+	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));
+	assertEntryCompareAcls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0543);
+	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));
+	assertEntryCompareAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, 0142);
+	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));
+}
+
+DEFINE_TEST(test_compat_star_acl_nfs4)
+{
+	char name[] = "test_compat_star_acl_nfs4.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 NFS4 ACLs mirroring file mode */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(3, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_ALLOW));
+	assertEntryCompareAcls(ae, acls3, sizeof(acls3)/sizeof(acls3[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_ALLOW, 0);
+
+	/* Second item has has fine-grained NFS4 ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls4, sizeof(acls4)/sizeof(acls0[4]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+	/* Third item has file and directory inheritance NFS4 ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(5, archive_entry_acl_reset(ae,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4));
+	assertEntryCompareAcls(ae, acls5, sizeof(acls5)/sizeof(acls5[0]),
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4, 0);
+
+	/* 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_nfs4.tar.uu b/libarchive/test/test_compat_star_acl_nfs4.tar.uu
new file mode 100644
index 0000000..79b228e
--- /dev/null
+++ b/libarchive/test/test_compat_star_acl_nfs4.tar.uu
@@ -0,0 +1,231 @@
+begin 644 test_compat_star_acl_nfs4.tar
+M+B\N+T!087A(96%D97(`````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V,#`@,#`P,#`P,"`P,#`P,#`P(#`P,#`P,#`P,C<R
+M(#`P,#`P,#`P,#`P(#`P,38P-S0@9P``````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,')O;W0`
+M````````````````````````````````````=VAE96P`````````````````
+M```````````````````P,#`P,#`P(#`P,#`P,#`@````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````P,#`P,#`P,#`P,"`P,#`P,#`P
+M,#`P,"`````````````````U-R!30TA)3%DN<F5L96%S93US=&%R(#$N-2XS
+M("AA;60V-"UU;FMN;W=N+69R965B<V0Q,2XP*0HR-R!30TA)3%DN87)C:'1Y
+M<&4]97AU<W1A<@HT-R!30TA)3%DN=F]L:&1R+F1U;7!D871E/3$T-SDQ-C<W
+M,C<N,38W,C(U,C@Q"C(U(%-#2$E,62YV;VQH9'(N=F]L;F\],0HS,"!30TA)
+M3%DN=F]L:&1R+F)L;V-K<VEZ93TR,`H`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"XO+B]`4&%X2&5A
+M9&5R````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#,S-"`P,#`P,#`P,#`P
+M,"`P,#$V,3$T('@`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````'=H965L````````````````````````````````
+M````,#`P,#`P,"`P,#`P,#`P(```````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@````````
+M````````,S`@871I;64],30W.3$Q.34U-"XP-#,U-#DP,#`*,S`@8W1I;64]
+M,30W.3$Q.3DQ,BXY,SDQ-C@P,#`*,S`@;71I;64],30W.3$Q.34U-"XP-#,U
+M-#DP,#`*,3,P(%-#2$E,62YA8VPN86-E/6]W;F5R0#IR=WAP+2UA05)78T-O
+M<SHM+2TM+2TM.F%L;&]W+&=R;W5P0#IR=RUP+2UA+5(M8RTM<SHM+2TM+2TM
+M.F%L;&]W+&5V97)Y;VYE0#IR+2TM+2UA+5(M8RTM<SHM+2TM+2TM.F%L;&]W
+M"@``````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````!F:6QE,0``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````,#`P,#<V-"`P,#`P
+M,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3$S,#(@,#`Q-#8S-B`P
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````'5S=&%R`#`P<F]O=```````````````````````````````
+M``````!W:&5E;````````````````````````````````````#`P,#`P,#`@
+M,#`P,#`P,"``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````#$S,#$R,S$Q,S`R(#$S,#$R,S$R,#4P(````````````````"XO+B]`
+M4&%X2&5A9&5R````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#4T,"`P,#`P
+M,#`P,#`P,"`P,#$V,3$S('@`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````=7-T87(`,#!R;V]T````````
+M`````````````````````````````'=H965L````````````````````````
+M````````````,#`P,#`P,"`P,#`P,#`P(```````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P,#`P,#`@
+M````````````````,S`@871I;64],30W.3$Q.34U-2XR-C<P,3@P,#`*,S`@
+M8W1I;64],30W.3$V,34Y."XY,SDV-#8P,#`*,S`@;71I;64],30W.3$Q.34U
+M-2XR-C<P,3@P,#`*,C8R(%-#2$E,62YA8VPN86-E/75S97(Z=7-E<C<X.G)W
+M>"TM+2TM+2TM+2TM.BTM+2TM+2TZ9&5N>3HW."QG<F]U<#IG<F]U<#<X.BUW
+M+7`M+2U!+5<M0V\M.BTM+2TM+2TZ9&5N>3HW."QU<V5R.G5S97(W-SIR+2TM
+M+2UA+5(M8RTM<SHM+2TM+2U).F%L;&]W.C<W+&]W;F5R0#IR=RUP+2UA05)7
+M8T-O<SHM+2TM+2TM.F%L;&]W+&=R;W5P0#IR=RUP+2UA+5(M8RTM<SHM+2TM
+M+2TM.F%L;&]W+&5V97)Y;VYE0#IR+2TM+2UA+5(M8RTM<SHM+2TM+2TM.F%L
+M;&]W"@``````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````!F:6QE,@``````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````,#`P,#8V
+M-"`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3$S,#,@,#`Q
+M-#8U-R`P````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````'5S=&%R`#`P<F]O=```````````````````````
+M``````````````!W:&5E;````````````````````````````````````#`P
+M,#`P,#`@,#`P,#`P,"``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#$S,#$R,S$Q,S`S(#$S,#$R-#,S,S<V(```````````````
+M`"XO+B]`4&%X2&5A9&5R````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````P,#`P-C`P(#`P,#`P,#`@,#`P,#`P,"`P,#`P,#`P,#0V
+M-2`P,#`P,#`P,#`P,"`P,#$V,3(Q('@`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````=7-T87(`,#!R;V]T
+M`````````````````````````````````````'=H965L````````````````
+M````````````````````,#`P,#`P,"`P,#`P,#`P(```````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````,#`P,#`P,#`P,#`@,#`P,#`P
+M,#`P,#`@````````````````,S`@871I;64],30W.3$V-S0Y-2XV.#<X-C8P
+M,#`*,S`@8W1I;64],30W.3$V-S<R,RXT-#`X-C<P,#`*,S`@;71I;64],30W
+M.3$R,#8W."XT-#$U.#`P,#`*,C$Y(%-#2$E,62YA8VPN86-E/6=R;W5P.F=R
+M;W5P-S@Z<G=X<$1D84%25V-#;W,Z9F0M+2TM+3ID96YY.C<X+'5S97(Z=7-E
+M<C<W.G(M+2TM+6$M4BUC+2US.F9D+2TM+2TZ86QL;W<Z-S<L;W=N97)`.G)W
+M>'`M+6%!4E=C0V]S.BTM+2TM+2TZ86QL;W<L9W)O=7!`.G)W>'`M+6%!4E=C
+M+2US.BTM+2TM+2TZ86QL;W<L979E<GEO;F5`.G(M>"TM+6$M4BUC+2US.BTM
+M+2TM+2TZ86QL;W<*````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````!D:7(Q+P``````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M,#`P,#<W-2`P,#`P,#`P(#`P,#`P,#`@,#`P,#`P,#`P,#`@,3,P,3(S,3,T
+M-#8@,#`Q-#8S,2`U````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````'5S=&%R`#`P<F]O=```````````````
+M``````````````````````!W:&5E;```````````````````````````````
+M`````#`P,#`P,#`@,#`P,#`P,"``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````#$S,#$R-#0W,#`W(#$S,#$R-#0W,S4S(```````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+9````````````````````````````````````
+`
+end
diff --git a/libarchive/test/test_compat_star_acl_posix1e.c b/libarchive/test/test_compat_star_acl_posix1e.c
deleted file mode 100644
index 10bffd9..0000000
--- a/libarchive/test/test_compat_star_acl_posix1e.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*-
- * Copyright (c) 2003-2007 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$");
-
-/*
- * Verify reading entries with POSIX.1e ACLs from archives created by star
- *
- * This should work on all systems, regardless of whether local filesystems
- * support ACLs or not.
- */
-
-struct acl_t {
-	int type;  /* Type of ACL: "access" or "default" */
-	int permset; /* Permissions for this class of users. */
-	int tag; /* Owner, User, Owning group, group, other, etc. */
-	const char *name; /* Name of user/group, depending on tag. */
-};
-
-static struct acl_t acls0[] = {
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER, "user77" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_MASK, ""},
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
-	  ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static struct acl_t acls1[] = {
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER, "user77" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
-	  ARCHIVE_ENTRY_ACL_USER, "user78" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
-	  ARCHIVE_ENTRY_ACL_GROUP, "group78" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
-	  ARCHIVE_ENTRY_ACL_MASK, ""}, 
-	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static struct acl_t acls2[] = {
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_USER_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_USER, "user77" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ,
-	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, "" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_GROUP, "group78" },
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE,
-	  ARCHIVE_ENTRY_ACL_MASK, ""},
-	{ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_WRITE,
-	  ARCHIVE_ENTRY_ACL_OTHER, "" },
-};
-
-static int
-acl_match(struct acl_t *acl, int type, int permset, int tag, const char *name)
-{
-	if (type != acl->type)
-		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_uudecode.c b/libarchive/test/test_compat_uudecode.c
index 95b1c9a..cfb17c8 100644
--- a/libarchive/test/test_compat_uudecode.c
+++ b/libarchive/test/test_compat_uudecode.c
@@ -40,7 +40,7 @@
 };
 
 /*
- * Compatibility: uudecode command ignores junk data placed ater the "end"
+ * Compatibility: uudecode command ignores junk data placed after the "end"
  * marker.
  */
 DEFINE_TEST(test_compat_uudecode)
diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c
index ff064c0..ce7b866 100644
--- a/libarchive/test/test_fuzz.c
+++ b/libarchive/test/test_fuzz.c
@@ -110,8 +110,9 @@
 		} else {
 			for (i = 0; filesets[n].names[i] != NULL; ++i)
 			{
+				char *newraw;
 				tmp = slurpfile(&size, filesets[n].names[i]);
-				char *newraw = realloc(rawimage, oldsize + size);
+				newraw = realloc(rawimage, oldsize + size);
 				if (!assert(newraw != NULL))
 				{
 					free(rawimage);
@@ -406,10 +407,12 @@
 		"test_read_format_tar_empty_filename.tar",
 		NULL
 	};
+#if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H
 	static const char *fileset9[] = {
 		"test_compat_lzop_1.tar.lzo",
 		NULL
 	};
+#endif
 	static const struct files filesets[] = {
 		{0, fileset1}, /* Exercise bzip2 decompressor. */
 		{1, fileset1},
@@ -420,7 +423,9 @@
 		{0, fileset6}, /* Exercise xz decompressor. */
 		{0, fileset7},
 		{0, fileset8},
+#if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H
 		{0, fileset9}, /* Exercise lzo decompressor. */
+#endif
 		{1, NULL}
 	};
 	test_fuzz(filesets);
diff --git a/libarchive/test/test_read_disk.c b/libarchive/test/test_read_disk.c
index bd36c39..43d7d86 100644
--- a/libarchive/test/test_read_disk.c
+++ b/libarchive/test/test_read_disk.c
@@ -61,6 +61,7 @@
 	return ("NOTFOO");
 }
 
+#if !defined(__CYGWIN__) && !defined(__HAIKU__)
 /* We test GID lookup by looking up the name of group number zero and
  * checking it against the following list.  If your system uses a
  * different conventional name for group number zero, please extend
@@ -71,13 +72,16 @@
 	"root",   /* Linux */
 	"wheel"  /* BSD */
 };
+#endif
 
 DEFINE_TEST(test_read_disk)
 {
 	struct archive *a;
 	int gmagic = 0x13579, umagic = 0x1234;
+#if !defined(__CYGWIN__) && !defined(__HAIKU__)
 	const char *p;
 	size_t i;
+#endif
 
 	assert((a = archive_read_disk_new()) != NULL);
 
@@ -115,8 +119,6 @@
 		/* Some platforms don't have predictable names for
 		 * uid=0, so we skip this part of the test. */
 		skipping("standard uname/gname lookup");
-		i = 0;
-		p = zero_groups[0]; /* avoid unused warnings */
 #else
 		/* XXX Someday, we may need to generalize this the
 		 * same way we generalized the group name check below.
diff --git a/libarchive/test/test_read_disk_directory_traversals.c b/libarchive/test/test_read_disk_directory_traversals.c
index 0a81e1e..705b3d9 100644
--- a/libarchive/test/test_read_disk_directory_traversals.c
+++ b/libarchive/test/test_read_disk_directory_traversals.c
@@ -500,8 +500,8 @@
 
 	/*
 	 * We should be on the initial directory where we performed
-	 * archive_read_disk_new() after we perfome archive_read_free()
-	 *  even if we broke off the directory traversals.
+	 * archive_read_disk_new() after we perform archive_read_free()
+	 * even if we broke off the directory traversals.
 	 */
 
 	/* Save current working directory. */
@@ -1228,11 +1228,11 @@
 	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
 
 	/*
-	 * Test4: Traversals with archive_read_disk_set_atime_restored() and
-	 * archive_read_disk_honor_nodump().
+	 * Test4: Traversals with ARCHIVE_READDISK_RESTORE_ATIME and
+	 * ARCHIVE_READDISK_HONOR_NODUMP
 	 */
-	assertNodump("at/f1");
-	assertNodump("at/f2");
+	assertSetNodump("at/f1");
+	assertSetNodump("at/f2");
 	assertUtimes("at/f1", 886600, 0, 886600, 0);
 	assertUtimes("at/f2", 886611, 0, 886611, 0);
 	assertUtimes("at/fe", 886611, 0, 886611, 0);
@@ -1320,11 +1320,13 @@
 	assertUtimes("cb", 886622, 0, 886622, 0);
 
 	assert((ae = archive_entry_new()) != NULL);
-	if (assert((a = archive_read_disk_new()) != NULL)) {
+	assert((a = archive_read_disk_new()) != NULL);
+	if (a == NULL) {
 		archive_entry_free(ae);
 		return;
 	}
-	if (assert((m = archive_match_new()) != NULL)) {
+	assert((m = archive_match_new()) != NULL);
+	if (m == NULL) {
 		archive_entry_free(ae);
 		archive_read_free(a);
 		archive_match_free(m);
@@ -1377,6 +1379,10 @@
 	/* Close the disk object. */
 	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
 
+	/* Reset name filter */
+	assertEqualIntA(a, ARCHIVE_OK,
+	    archive_read_disk_set_matching(a, NULL, NULL, NULL));
+
 	/*
 	 * Test2: Traversals with a metadata filter.
 	 */
@@ -1394,7 +1400,7 @@
 	while (file_count--) {
 		archive_entry_clear(ae);
 		assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae));
-		failure("File 'cb/f1' should be exclueded");
+		failure("File 'cb/f1' should be excluded");
 		assert(strcmp(archive_entry_pathname(ae), "cb/f1") != 0);
 		if (strcmp(archive_entry_pathname(ae), "cb") == 0) {
 			assertEqualInt(archive_entry_filetype(ae), AE_IFDIR);
@@ -1444,7 +1450,7 @@
 	assertMakeFile("nd/f1", 0644, "0123456789");
 	assertMakeFile("nd/f2", 0644, "hello world");
 	assertMakeFile("nd/fe", 0644, NULL);
-	assertNodump("nd/f2");
+	assertSetNodump("nd/f2");
 	assertUtimes("nd/f1", 886600, 0, 886600, 0);
 	assertUtimes("nd/f2", 886611, 0, 886611, 0);
 	assertUtimes("nd/fe", 886611, 0, 886611, 0);
@@ -1454,7 +1460,7 @@
 	assert((a = archive_read_disk_new()) != NULL);
 
 	/*
-	 * Test1: Traversals without archive_read_disk_honor_nodump().
+	 * Test1: Traversals without ARCHIVE_READDISK_HONOR_NODUMP
 	 */
 	failure("Directory traversals should work as well");
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "nd"));
@@ -1507,7 +1513,7 @@
 	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
 
 	/*
-	 * Test2: Traversals with archive_read_disk_honor_nodump().
+	 * Test2: Traversals with ARCHIVE_READDISK_HONOR_NODUMP
 	 */
 	assertUtimes("nd/f1", 886600, 0, 886600, 0);
 	assertUtimes("nd/f2", 886611, 0, 886611, 0);
@@ -1565,11 +1571,11 @@
 {
 	/* Basic test. */
 	test_basic();
-	/* Test hybird mode; follow symlink initially, then not. */
+	/* Test hybrid mode; follow symlink initially, then not. */
 	test_symlink_hybrid();
-	/* Test logcal mode; follow all symlinks. */
+	/* Test logical mode; follow all symlinks. */
 	test_symlink_logical();
-	/* Test logcal mode; prevent loop in symlinks. */ 
+	/* Test logical mode; prevent loop in symlinks. */
 	test_symlink_logical_loop();
 	/* Test to restore atime. */
 	test_restore_atime();
diff --git a/libarchive/test/test_read_filter_lzop.c b/libarchive/test/test_read_filter_lzop.c
index 86a5e6e..acce6a4 100644
--- a/libarchive/test/test_read_filter_lzop.c
+++ b/libarchive/test/test_read_filter_lzop.c
@@ -39,13 +39,16 @@
 	assert((a = archive_read_new()) != NULL);
 	r = archive_read_support_filter_lzop(a);
 	if (r != ARCHIVE_OK) {
-		if (r == ARCHIVE_WARN && !canLzop()) {
+		if (!canLzop()) {
 			assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 			skipping("lzop compression is not supported "
 			    "on this platform");
-		} else
+			return;
+		} else if (r != ARCHIVE_WARN) {
 			assertEqualIntA(a, ARCHIVE_OK, r);
-		return;
+			assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+			return;
+		}
 	}
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	assertEqualIntA(a, ARCHIVE_OK,
diff --git a/libarchive/test/test_read_filter_lzop_multiple_parts.c b/libarchive/test/test_read_filter_lzop_multiple_parts.c
index 3b0febb..82eaf35 100644
--- a/libarchive/test/test_read_filter_lzop_multiple_parts.c
+++ b/libarchive/test/test_read_filter_lzop_multiple_parts.c
@@ -36,12 +36,16 @@
 	assert((a = archive_read_new()) != NULL);
 	r = archive_read_support_filter_lzop(a);
 	if (r != ARCHIVE_OK) {
-		if (r == ARCHIVE_WARN && !canLzop()) {
-			assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+		if (!canLzop()) {
 			skipping("lzop compression is not supported "
 			    "on this platform");
+		} else if (r == ARCHIVE_WARN) {
+			skipping("lzop multiple parts decoding is not "
+			    "supported via external program");
+
 		} else
 			assertEqualIntA(a, ARCHIVE_OK, r);
+		assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 		return;
 	}
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
diff --git a/libarchive/test/test_read_format_7zip.c b/libarchive/test/test_read_format_7zip.c
index 14447de..1d1e4c7 100644
--- a/libarchive/test/test_read_format_7zip.c
+++ b/libarchive/test/test_read_format_7zip.c
@@ -688,7 +688,7 @@
 	assertEqualInt(32, archive_read_data(a, buff, sizeof(buff)));
 	assertEqualMem(buff, "hellohellohello\nhellohellohello\n", 32);
 
-	/* Verify symbolic-linke symlinkfile. */
+	/* Verify symbolic-link symlinkfile. */
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualInt((AE_IFLNK | 0755), archive_entry_mode(ae));
 	assertEqualString("symlinkfile", archive_entry_pathname(ae));
diff --git a/libarchive/test/test_read_format_cpio_afio.c b/libarchive/test/test_read_format_cpio_afio.c
index 16065eb..95d3171 100644
--- a/libarchive/test/test_read_format_cpio_afio.c
+++ b/libarchive/test/test_read_format_cpio_afio.c
@@ -27,7 +27,7 @@
 __FBSDID("$FreeBSD$");
 
 /*
-ecute the following to rebuild the data for this program:
+execute the following to rebuild the data for this program:
    tail -n +33 test_read_format_cpio_afio.c | /bin/sh
 
 # How to make a sample data.
diff --git a/libarchive/test/test_read_format_isorr_bz2.c b/libarchive/test/test_read_format_isorr_bz2.c
index d2dfa2e..fb4f4a8 100644
--- a/libarchive/test/test_read_format_isorr_bz2.c
+++ b/libarchive/test/test_read_format_isorr_bz2.c
@@ -26,7 +26,7 @@
 __FBSDID("$FreeBSD: head/lib/libarchive/test/test_read_format_isorr_bz2.c 201247 2009-12-30 05:59:21Z kientzle $");
 
 /*
-PLEASE use old cdrtools; mkisofs verion is 2.01.
+PLEASE use old cdrtools; mkisofs version is 2.01.
 This version mkisofs made wrong "SL" System Use Entry of RRIP.
 
 Execute the following command to rebuild the data for this program:
diff --git a/libarchive/test/test_read_format_zip.c b/libarchive/test/test_read_format_zip.c
index da5b137..cea2676 100644
--- a/libarchive/test/test_read_format_zip.c
+++ b/libarchive/test/test_read_format_zip.c
@@ -126,6 +126,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31));
 	verify_basic(a, 0);
+	free(p);
 }
 
 /*
@@ -195,6 +196,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108));
 	verify_info_zip_ux(a, 0);
+	free(p);
 }
 
 /*
@@ -258,6 +260,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108));
 	verify_extract_length_at_end(a, 0);
+	free(p);
 }
 
 static void
@@ -294,6 +297,8 @@
 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+	free(p);
 }
 
 DEFINE_TEST(test_read_format_zip)
diff --git a/libarchive/test/test_read_format_zip_comment_stored.c b/libarchive/test/test_read_format_zip_comment_stored.c
index d2b935d..b92b288 100644
--- a/libarchive/test/test_read_format_zip_comment_stored.c
+++ b/libarchive/test/test_read_format_zip_comment_stored.c
@@ -63,6 +63,8 @@
 	assertEqualInt(archive_entry_is_encrypted(ae), 0);
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+	free(p);
 }
 
 DEFINE_TEST(test_read_format_zip_comment_stored)
diff --git a/libarchive/test/test_read_format_zip_filename.c b/libarchive/test/test_read_format_zip_filename.c
index 93ba09b..4dd2e8a 100644
--- a/libarchive/test/test_read_format_zip_filename.c
+++ b/libarchive/test/test_read_format_zip_filename.c
@@ -1116,7 +1116,7 @@
  * - the filename of second file is stored in UTF-8.
  *
  * Whenever hdrcharset option is specified, we will correctly read the
- * filename of sencod file, which is stored in UTF-8.
+ * filename of second file, which is stored in UTF-8.
  */
 
 DEFINE_TEST(test_read_format_zip_filename_KOI8R_UTF8_2)
diff --git a/libarchive/test/test_read_format_zip_jar.c b/libarchive/test/test_read_format_zip_jar.c
new file mode 100644
index 0000000..ffb520e
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_jar.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2016 Peter Wu
+ * 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$");
+
+/*
+ * Issue 822: jar files have an empty External File Attributes field which
+ * is misinterpreted as regular file type due to OS MS-DOS.
+ */
+
+DEFINE_TEST(test_read_format_zip_jar)
+{
+	const char *refname = "test_read_format_zip_jar.jar";
+	char *p;
+	size_t s;
+	struct archive *a;
+	struct archive_entry *ae;
+	char data[16];
+
+	extract_reference_file(refname);
+	p = slurpfile(&s, refname);
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a));
+	assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1));
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("somedir/", archive_entry_pathname(ae));
+	assertEqualInt(AE_IFDIR | 0775, archive_entry_mode(ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	assertEqualIntA(a, 0, archive_read_data(a, data, 16));
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+	free(p);
+}
diff --git a/libarchive/test/test_read_format_zip_jar.jar.uu b/libarchive/test/test_read_format_zip_jar.jar.uu
new file mode 100644
index 0000000..0778c93
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_jar.jar.uu
@@ -0,0 +1,6 @@
+begin 640 test_read_format_zip_jar.jar
+M4$L#! H   @  $AQETD                (  0 <V]M961I<B_^R@  4$L!
+M @H "@  "   2'&720                @ !                    '-O
+@;65D:7(O_LH  %!+!08      0 ! #H    J        
+ 
+end
diff --git a/libarchive/test/test_read_format_zip_mac_metadata.c b/libarchive/test/test_read_format_zip_mac_metadata.c
index 97aa427..99b7012 100644
--- a/libarchive/test/test_read_format_zip_mac_metadata.c
+++ b/libarchive/test/test_read_format_zip_mac_metadata.c
@@ -112,4 +112,6 @@
 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+	free(p);
 }
diff --git a/libarchive/test/test_read_format_zip_malformed.c b/libarchive/test/test_read_format_zip_malformed.c
index 2327d91..e14a3f5 100644
--- a/libarchive/test/test_read_format_zip_malformed.c
+++ b/libarchive/test/test_read_format_zip_malformed.c
@@ -53,6 +53,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+	free(p);
 }
 
 DEFINE_TEST(test_read_format_zip_malformed)
diff --git a/libarchive/test/test_read_format_zip_nested.c b/libarchive/test/test_read_format_zip_nested.c
index 6830afb..5f6edf2 100644
--- a/libarchive/test/test_read_format_zip_nested.c
+++ b/libarchive/test/test_read_format_zip_nested.c
@@ -65,6 +65,8 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
 
+	free(p);
+
 	/* Inspect inner Zip. */
 	assert((a = archive_read_new()) != NULL);
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
diff --git a/libarchive/test/test_read_format_zip_padded.c b/libarchive/test/test_read_format_zip_padded.c
index dae88ab..2094eca 100644
--- a/libarchive/test/test_read_format_zip_padded.c
+++ b/libarchive/test/test_read_format_zip_padded.c
@@ -53,6 +53,8 @@
 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+	free(p);
 }
 
 /*
diff --git a/libarchive/test/test_read_format_zip_sfx.c b/libarchive/test/test_read_format_zip_sfx.c
index d5992d3..dc76ef9 100644
--- a/libarchive/test/test_read_format_zip_sfx.c
+++ b/libarchive/test/test_read_format_zip_sfx.c
@@ -60,4 +60,6 @@
 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+	free(p);
 }
diff --git a/libarchive/test/test_read_format_zip_traditional_encryption_data.c b/libarchive/test/test_read_format_zip_traditional_encryption_data.c
index 2700be1..3052615 100644
--- a/libarchive/test/test_read_format_zip_traditional_encryption_data.c
+++ b/libarchive/test/test_read_format_zip_traditional_encryption_data.c
@@ -28,7 +28,7 @@
 
 DEFINE_TEST(test_read_format_zip_traditional_encryption_data)
 {
-	/* This file is password protected (Traditional PKWARE Enctypted).
+	/* This file is password protected (Traditional PKWARE Encrypted).
 	   The headers are NOT encrypted. Password is "12345678". */
 	const char *refname =
 		"test_read_format_zip_traditional_encryption_data.zip";
@@ -36,7 +36,7 @@
 	struct archive *a;
 	char buff[512];
 
-	/* Check if running system has cryptographic functionarity. */
+	/* Check if running system has cryptographic functionality. */
 	assert((a = archive_write_new()) != NULL);
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
diff --git a/libarchive/test/test_read_format_zip_winzip_aes.c b/libarchive/test/test_read_format_zip_winzip_aes.c
index 082337d..cc1e311 100644
--- a/libarchive/test/test_read_format_zip_winzip_aes.c
+++ b/libarchive/test/test_read_format_zip_winzip_aes.c
@@ -33,7 +33,7 @@
 	struct archive *a;
 	char buff[512];
 
-	/* Check if running system has cryptographic functionarity. */
+	/* Check if running system has cryptographic functionality. */
 	assert((a = archive_write_new()) != NULL);
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
diff --git a/libarchive/test/test_read_format_zip_winzip_aes_large.c b/libarchive/test/test_read_format_zip_winzip_aes_large.c
index a40d5cf..6c40ae7 100644
--- a/libarchive/test/test_read_format_zip_winzip_aes_large.c
+++ b/libarchive/test/test_read_format_zip_winzip_aes_large.c
@@ -34,7 +34,7 @@
 	char buff[512];
 
 
-	/* Check if running system has cryptographic functionarity. */
+	/* Check if running system has cryptographic functionality. */
 	assert((a = archive_write_new()) != NULL);
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
diff --git a/libarchive/test/test_read_pax_schily_xattr.c b/libarchive/test/test_read_pax_schily_xattr.c
new file mode 100644
index 0000000..7554f6d
--- /dev/null
+++ b/libarchive/test/test_read_pax_schily_xattr.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2016 IBM Corporation
+ * Copyright (c) 2003-2007 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.
+ *
+ * This test case's code has been derived from test_entry.c
+ */
+#include "test.h"
+
+DEFINE_TEST(test_schily_xattr_pax)
+{
+	struct archive *a;
+	struct archive_entry *ae;
+	const char *refname = "test_read_pax_schily_xattr.tar";
+	const char *xname; /* For xattr tests. */
+	const void *xval; /* For xattr tests. */
+	size_t xsize; /* For xattr tests. */
+	const char *string, *array;
+
+	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(refname);
+	assertEqualIntA(a, ARCHIVE_OK,
+	    archive_read_open_filename(a, refname, 10240));
+
+	assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualInt(2, archive_entry_xattr_count(ae));
+	assertEqualInt(2, archive_entry_xattr_reset(ae));
+
+	assertEqualInt(0, archive_entry_xattr_next(ae, &xname, &xval, &xsize));
+	assertEqualString(xname, "security.selinux");
+	string = "system_u:object_r:unlabeled_t:s0";
+	assertEqualString(xval, string);
+	/* the xattr's value also contains the terminating \0 */
+	assertEqualInt((int)xsize, strlen(string) + 1);
+
+	assertEqualInt(0, archive_entry_xattr_next(ae, &xname, &xval, &xsize));
+	assertEqualString(xname, "security.ima");
+	assertEqualInt((int)xsize, 265);
+	/* we only compare the first 12 bytes */
+	array = "\x03\x02\x04\xb0\xe9\xd6\x79\x01\x00\x2b\xad\x1e";
+	assertEqualMem(xval, array, 12);
+
+	/* Close the archive. */
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_pax_schily_xattr.tar.uu b/libarchive/test/test_read_pax_schily_xattr.tar.uu
new file mode 100644
index 0000000..52f7a8f
--- /dev/null
+++ b/libarchive/test/test_read_pax_schily_xattr.tar.uu
@@ -0,0 +1,231 @@
+begin 644 test_schily_xattr_pax.tar
+M+B]087A(96%D97)S+C$U,C4O8V]N9F9I;&5S````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V-#0`,#`P,#`P,``P,#`P,#`P`#`P,#`P,#`P-C0W
+M`#$R-S$R,C$P-3`V`#`Q,C4V-@`@>```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,```````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````S,"!A=&EM93TQ-#8R,S`Y,S8Y+C<Q-#DW-3DP
+M.0HS,"!C=&EM93TQ-#8R,S`Y,S8Y+C<W.#DW-C(Q,PHR.38@4T-(24Q9+GAA
+M='1R+G-E8W5R:71Y+FEM83T#`@2PZ=9Y`0`KK1Z%635)4!FT,I"<49LTXR`'
+M@42;2R:POQ9`JQA=+16.AY^<_[XK597$><QT!GLCT8_IF@@:/Z<?]<HQ0T7I
+MO&87D9-4,L'!GUG'K%=]KEPC<<)&@`J)'T89MBPB7],R(#&7"#>W6X5O?Y6:
+M9^':P2MZR[4)$@W<NV(VUT&(R0R-_S>?)B6GX0U@<,0M%6YNMO%OG+IS%/.<
+M,"A(N&S.F9]=!*5=\).X."2$GUGJ,0C:@+G#$M_E8UQP,LU-G(8IKW^K^<8*
+M*3_.N0'%8.^$8S$`D9XOF+DK<<)U34U'_"O5/2<N#T*!'!4Z73X-X4W!/LCF
+M/,721A`CQF:PGDD/"C8W(%-#2$E,62YX871T<BYS96-U<FET>2YS96QI;G5X
+M/7-Y<W1E;5]U.F]B:F5C=%]R.G5N;&%B96QE9%]T.G,P``H`````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"XO8V]N9F9I;&5S
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C0T`#`P,#`P,#``,#`P,#`P,``P,#`P,#`P,#`T,0`Q,C<Q,C(Q,#4P
+M-@`P,3,P-S,`(#``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````')O;W0`````````````````````````````````
+M````,#`P,#`P,``P,#`P,#`P````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````+V5T8R]D969A=6QT+VEM82UP;VQI8WDN:6YA8W1I=F4*````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+9````````````````````````````````````
+`
+end
diff --git a/libarchive/test/test_sparse_basic.c b/libarchive/test/test_sparse_basic.c
index 06f2c9d..9d45f0f 100644
--- a/libarchive/test/test_sparse_basic.c
+++ b/libarchive/test/test_sparse_basic.c
@@ -57,7 +57,7 @@
 
 /*
  * NOTE: On FreeBSD and Solaris, this test needs ZFS.
- * You may should perfom this test as
+ * You may perform this test as
  * 'TMPDIR=<a directory on the ZFS> libarchive_test'.
  */
 
diff --git a/libarchive/test/test_write_disk_appledouble.c b/libarchive/test/test_write_disk_appledouble.c
index 81032fc..706794a 100644
--- a/libarchive/test/test_write_disk_appledouble.c
+++ b/libarchive/test/test_write_disk_appledouble.c
@@ -144,7 +144,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualString("./file3", archive_entry_pathname(ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
-	/* Extract ._file3 which will be merged into file3 as medtadata. */
+	/* Extract ._file3 which will be merged into file3 as metadata. */
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualString("./._file3", archive_entry_pathname(ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
@@ -203,7 +203,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualString("./file3", archive_entry_pathname(ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
-	/* Extract ._file3 which will be merged into file3 as medtadata. */
+	/* Extract ._file3 which will be merged into file3 as metadata. */
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
 	assertEqualString("./._file3", archive_entry_pathname(ae));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
diff --git a/libarchive/test/test_write_disk_secure746.c b/libarchive/test/test_write_disk_secure746.c
index 460aafe..5ce1fd9 100644
--- a/libarchive/test/test_write_disk_secure746.c
+++ b/libarchive/test/test_write_disk_secure746.c
@@ -72,6 +72,9 @@
 
 	/* Verify that target file contents are unchanged. */
 	assertTextFileContents("unmodified", "../target/foo");
+
+	assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a));
+	archive_write_free(a);
 #endif
 }
 
diff --git a/libarchive/test/test_write_filter_lz4.c b/libarchive/test/test_write_filter_lz4.c
index a043698..4f2135a 100644
--- a/libarchive/test/test_write_filter_lz4.c
+++ b/libarchive/test/test_write_filter_lz4.c
@@ -56,6 +56,7 @@
 	} else {
 		assertEqualInt(ARCHIVE_OK, r);
 	}
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
 	buffsize = 2000000;
 	assert(NULL != (buff = (char *)malloc(buffsize)));
@@ -299,6 +300,7 @@
 	} else {
 		assertEqualInt(ARCHIVE_OK, r);
 	}
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
 	buffsize = 2000000;
 	assert(NULL != (buff = (char *)malloc(buffsize)));
diff --git a/libarchive/test/test_write_filter_lzop.c b/libarchive/test/test_write_filter_lzop.c
index a32932c..92db7bf 100644
--- a/libarchive/test/test_write_filter_lzop.c
+++ b/libarchive/test/test_write_filter_lzop.c
@@ -43,12 +43,12 @@
 
 	assert((a = archive_write_new()) != NULL);
 	r = archive_write_add_filter_lzop(a);
+	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 	if (r != ARCHIVE_OK) {
 		if (canLzop() && r == ARCHIVE_WARN)
 			use_prog = 1;
 		else {
 			skipping("lzop writing not supported on this platform");
-			assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 			return;
 		}
 	}
@@ -92,7 +92,7 @@
 	assert((a = archive_read_new()) != NULL);
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	r = archive_read_support_filter_lzop(a);
-	if (r == ARCHIVE_WARN) {
+	if (r == ARCHIVE_WARN && !use_prog) {
 		skipping("Can't verify lzop writing by reading back;"
 		    " lzop reading not fully supported on this platform");
 	} else {
@@ -212,7 +212,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
 	r = archive_read_support_filter_lzop(a);
-	if (r == ARCHIVE_WARN) {
+	if (r == ARCHIVE_WARN && !use_prog) {
 		skipping("lzop reading not fully supported on this platform");
 	} else {
 		assertEqualIntA(a, ARCHIVE_OK,
diff --git a/libarchive/test/test_write_format_iso9660.c b/libarchive/test/test_write_format_iso9660.c
index ee6db6f..e4e98bb 100644
--- a/libarchive/test/test_write_format_iso9660.c
+++ b/libarchive/test/test_write_format_iso9660.c
@@ -719,7 +719,7 @@
 	assertEqualInt(5, archive_entry_ctime(ae));
 	assert(archive_entry_mtime_is_set(ae));
 	assertEqualInt(5, archive_entry_mtime(ae));
-	/* Trim lngname to 64 characters. */
+	/* Trim longname to 64 characters. */
 	longname[64] = '\0';
 	assertEqualString(longname, archive_entry_pathname(ae));
 	assert((AE_IFREG | 0400) == archive_entry_mode(ae));
diff --git a/libarchive/test/test_write_format_iso9660_zisofs.c b/libarchive/test/test_write_format_iso9660_zisofs.c
index 136255b..2140ed8 100644
--- a/libarchive/test/test_write_format_iso9660_zisofs.c
+++ b/libarchive/test/test_write_format_iso9660_zisofs.c
@@ -25,7 +25,7 @@
 #include "test.h"
 
 /*
- * Check that a "zisofs" ISO 9660 imaeg is correctly created.
+ * Check that a "zisofs" ISO 9660 image is correctly created.
  */
 
 static const unsigned char primary_id[] = {
diff --git a/libarchive/test/test_write_format_xar_empty.c b/libarchive/test/test_write_format_xar_empty.c
index aa26d5d..97c0808 100644
--- a/libarchive/test/test_write_format_xar_empty.c
+++ b/libarchive/test/test_write_format_xar_empty.c
@@ -115,6 +115,6 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
-	/* Verify the correct format for an empy Xar archive. */
+	/* Verify the correct format for an empty Xar archive. */
 	assertEqualInt(used, 0);
 }
diff --git a/libarchive/test/test_write_format_zip_empty.c b/libarchive/test/test_write_format_zip_empty.c
index 2e3afec..73a5573 100644
--- a/libarchive/test/test_write_format_zip_empty.c
+++ b/libarchive/test/test_write_format_zip_empty.c
@@ -49,7 +49,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
-	/* Verify the correct format for an empy Zip archive. */
+	/* Verify the correct format for an empty Zip archive. */
 	assertEqualInt(used, 22);
 	assertEqualMem(buff,
 	    "PK\005\006\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
diff --git a/libarchive/test/test_write_format_zip_empty_zip64.c b/libarchive/test/test_write_format_zip_empty_zip64.c
index 3efdf62..8f9975b 100644
--- a/libarchive/test/test_write_format_zip_empty_zip64.c
+++ b/libarchive/test/test_write_format_zip_empty_zip64.c
@@ -51,7 +51,7 @@
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
-	/* Verify the correct format for an empy Zip archive with Zip64 extensions forced. */
+	/* Verify the correct format for an empty Zip archive with Zip64 extensions forced. */
 	assertEqualInt(used, 98);
 	assertEqualMem(buff,
 	    "PK\006\006" /* Zip64 end-of-central-directory record */
diff --git a/libarchive/test/test_write_format_zip_large.c b/libarchive/test/test_write_format_zip_large.c
index d73dd62..88788b5 100644
--- a/libarchive/test/test_write_format_zip_large.c
+++ b/libarchive/test/test_write_format_zip_large.c
@@ -470,5 +470,6 @@
 	assertEqualMem(cd_start, "PK\001\002", 4);
 
 	fileblocks_free(fileblocks);
+	free(buff);
 	free(nulldata);
 }
diff --git a/libarchive/test/test_write_format_zip_zip64.c b/libarchive/test/test_write_format_zip_zip64.c
index b83aeab..c5f00a2 100644
--- a/libarchive/test/test_write_format_zip_zip64.c
+++ b/libarchive/test/test_write_format_zip_zip64.c
@@ -49,6 +49,8 @@
 	archive_entry_set_size(ae, size);
 	assertEqualInt(expected, archive_write_header(a, ae));
 
+	archive_entry_free(ae);
+
 	/* Don't actually write 4GB! ;-) */
 	assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a));
 }
diff --git a/libarchive/xxhash.c b/libarchive/xxhash.c
index d7f8e96..6f5ba52 100644
--- a/libarchive/xxhash.c
+++ b/libarchive/xxhash.c
@@ -29,10 +29,11 @@
 You can contact the author at :
 - xxHash source repository : http://code.google.com/p/xxhash/
 */
+#include "archive_platform.h"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include "archive_platform.h"
 #include "archive_xxhash.h"
 
 #ifdef HAVE_LIBLZ4
@@ -60,7 +61,7 @@
 ** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
 ** Results are therefore identical for little-endian and big-endian CPU.
 ** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
-** Should endian-independance be of no importance for your application, you may set the #define below to 1.
+** Should endian-independence be of no importance for your application, you may set the #define below to 1.
 ** It will improve speed for Big-endian CPU.
 ** This option has no impact on Little_Endian CPU.
 */
diff --git a/tar/bsdtar.1 b/tar/bsdtar.1
index 9eadaaf..0537bf3 100644
--- a/tar/bsdtar.1
+++ b/tar/bsdtar.1
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2003-2007 Tim Kientzle
+.\" Copyright (c) 2017 Martin Matuska
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd September 16, 2014
+.Dd February 25, 2017
 .Dt TAR 1
 .Os
 .Sh NAME
@@ -124,7 +125,7 @@
 all operating modes.
 .Bl -tag -width indent
 .It Cm @ Ns Pa archive
-(c and r mode only)
+(c and r modes only)
 The specified archive is opened and the entries
 in it will be appended to the current archive.
 As a simple example,
@@ -164,6 +165,16 @@
 .Dl Nm Fl a Fl jcf Pa archive.xxx source.c source.h
 if it is unknown suffix or no suffix, creates a new archive with
 restricted pax format and bzip2 compression.
+.It Fl Fl acls
+(c, r, u, x modes only)
+Archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of
+.Fl Fl no-acls
+and the default behavior in c, r, and u modes (except Mac OS X) or if
+.Nm
+is run in x mode as root. On Mac OS X this option translates extended ACLs
+to NFSv4 ACLs. To store extended ACLs the
+.Fl Fl mac-metadata
+option is preferred.
 .It Fl B , Fl Fl read-full-blocks
 Ignored for compatibility with other
 .Xr tar 1
@@ -188,15 +199,18 @@
 (x mode only)
 Before removing file system objects to replace them, clear platform-specific
 file flags that might prevent removal.
-.It Fl Fl disable-copyfile
-Mac OS X specific.
-Disable the use of
-.Xr copyfile 3 .
 .It Fl Fl exclude Ar pattern
 Do not process files or directories that match the
 specified pattern.
 Note that exclusions take precedence over patterns or filenames
 specified on the command line.
+.It Fl Fl fflags
+(c, r, u, x modes only)
+Archive or extract file flags. This is the reverse of
+.Fl Fl no-fflags
+and the default behavior in c, r, and u modes or if
+.Nm
+is run in x mode as root.
 .It Fl Fl format Ar format
 (c, r, u mode only)
 Use the specified format for the created archive.
@@ -245,11 +259,11 @@
 in the archive;
 the name will not be verified against the system group database.
 .It Fl H
-(c and r mode only)
+(c and r modes only)
 Symbolic links named on the command line will be followed; the
 target of the link will be archived, not the link itself.
 .It Fl h
-(c and r mode only)
+(c and r modes only)
 Synonym for
 .Fl L .
 .It Fl I
@@ -259,7 +273,8 @@
 Show usage.
 .It Fl Fl hfsCompression
 (x mode only)
-Mac OS X specific(v10.6 or later). Compress extracted regular files with HFS+ compression.
+Mac OS X specific (v10.6 or later). Compress extracted regular files with HFS+
+compression.
 .It Fl Fl ignore-zeros
 An alias of
 .Fl Fl options Cm read_concatenated_archives
@@ -310,7 +325,7 @@
 Do not overwrite existing files that are newer than the
 versions appearing in the archive being extracted.
 .It Fl L , Fl Fl dereference
-(c and r mode only)
+(c and r modes only)
 All symbolic links will be followed.
 Normally, symbolic links are archived as such.
 With this option, the target of the link will be archived instead.
@@ -345,6 +360,16 @@
 (x mode only)
 Do not extract modification time.
 By default, the modification time is set to the time stored in the archive.
+.It Fl Fl mac-metadata
+(c, r, u and x mode only)
+Mac OS X specific. Archive or extract extended ACLs and extended attributes
+using
+.Xr copyfile 3
+in AppleDouble format. This is the reverse of
+.Fl Fl no-mac-metadata .
+and the default behavior in c, r, and u modes or if
+.Nm
+is run in x mode as root.
 .It Fl n , Fl Fl norecurse , Fl Fl no-recursion
 (c, r, u modes only)
 Do not recursively archive the contents of directories.
@@ -385,6 +410,30 @@
 .Fl print0
 option to
 .Xr find 1 .
+.It Fl Fl no-acls
+(c, r, u, x modes only)
+Do not archive or extract POSIX.1e or NFSv4 ACLs. This is the reverse of
+.Fl Fl acls
+and the default behavior if
+.Nm
+is run as non-root in x mode (on Mac OS X also in c, r and u modes).
+.It Fl Fl no-fflags
+(c, r, u, x modes only)
+Do not archive or extract file flags. This is the reverse of
+.Fl Fl fflags
+and the default behavior if
+.Nm
+is run as non-root in x mode.
+.It Fl Fl no-mac-metadata
+(x mode only)
+Mac OS X specific. Do not archive or extract ACLs and extended attributes using
+.Xr copyfile 3
+in AppleDouble format. This is the reverse of
+.Fl Fl mac-metadata .
+and the default behavior if
+.Nm
+is run as non-root in x mode.
+.It Fl n , Fl Fl norecurse , Fl Fl no-recursion
 .It Fl Fl no-same-owner
 (x mode only)
 Do not extract owner and group IDs.
@@ -402,6 +451,13 @@
 and the default behavior if
 .Nm
 is run as non-root.
+.It Fl Fl no-xattrs
+(c, r, u, x modes only)
+Do not archive or extract extended attributes. This is the reverse of
+.Fl Fl xattrs
+and the default behavior if
+.Nm
+is run as non-root in x mode.
 .It Fl Fl numeric-owner
 This is equivalent to
 .Fl Fl uname
@@ -583,14 +639,18 @@
 .It Fl p , Fl Fl insecure , Fl Fl preserve-permissions
 (x mode only)
 Preserve file permissions.
-Attempt to restore the full permissions, including owner, file modes, file
-flags and ACLs, if available, for each item extracted from the archive.
-This is the default, if
+Attempt to restore the full permissions, including owner, file modes, ACLs,
+extended attributes and extended file flags, if available, for each item
+extracted from the archive. This is te reverse of
+.Fl Fl no-same-permissions
+and the default if
 .Nm
-is being run by root and can be overridden by also specifying
-.Fl Fl no-same-owner
-and
-.Fl Fl no-same-permissions .
+is being run by root and can be partially overridden by also specifying
+.Fl Fl no-acls ,
+.Fl Fl no-fflags ,
+.Fl Fl no-mac-metadata
+or
+.Fl Fl no-xattrs .
 .It Fl Fl passphrase Ar passphrase
 The
 .Pa passphrase
@@ -692,7 +752,7 @@
 .Fl n
 as well.
 .It Fl Fl totals
-(c, r, u mode only)
+(c, r, u modes only)
 After archiving all files, print a summary to stderr.
 .It Fl U , Fl Fl unlink , Fl Fl unlink-first
 (x mode only)
@@ -754,6 +814,13 @@
 See
 .Fl Fl exclude
 for more information about the handling of exclusions.
+.It Fl Fl xattrs
+(c, r, u, x modes only)
+Archive or extract extended attributes. This is the reverse of
+.Fl Fl no-xattrs
+and the default behavior in c, r, and u modes or if
+.Nm
+is run in x mode as root.
 .It Fl y
 (c mode only)
 Compress the resulting archive with
diff --git a/tar/bsdtar.c b/tar/bsdtar.c
index 93bf60a..9fc6833 100644
--- a/tar/bsdtar.c
+++ b/tar/bsdtar.c
@@ -118,11 +118,11 @@
 }
 #endif
 
-static void		 long_help(void);
+static void		 long_help(void) __LA_DEAD;
 static void		 only_mode(struct bsdtar *, const char *opt,
 			     const char *valid);
 static void		 set_mode(struct bsdtar *, char opt);
-static void		 version(void);
+static void		 version(void) __LA_DEAD;
 
 /* A basic set of security flags to request from libarchive. */
 #define	SECURITY					\
@@ -137,7 +137,6 @@
 	char			 compression, compression2;
 	const char		*compression_name, *compression2_name;
 	const char		*compress_program;
-	char			 option_a, option_o;
 	char			 possible_help_request;
 	char			 buff[16];
 
@@ -150,7 +149,7 @@
 	bsdtar->fd = -1; /* Mark as "unused" */
 	bsdtar->gid = -1;
 	bsdtar->uid = -1;
-	option_a = option_o = 0;
+	bsdtar->flags = 0;
 	compression = compression2 = '\0';
 	compression_name = compression2_name = NULL;
 	compress_program = NULL;
@@ -233,6 +232,14 @@
 	if (getenv(COPYFILE_DISABLE_VAR))
 		bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
 #endif
+#if defined(__APPLE__)
+	/*
+	 * On Mac OS ACLs are archived with copyfile() (--mac-metadata)
+	 * Translation to NFSv4 ACLs has to be requested explicitly with --acls
+	 */
+	bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
+#endif
+
 	bsdtar->matching = archive_match_new();
 	if (bsdtar->matching == NULL)
 		lafe_errc(1, errno, "Out of memory");
@@ -252,7 +259,12 @@
 	while ((opt = bsdtar_getopt(bsdtar)) != -1) {
 		switch (opt) {
 		case 'a': /* GNU tar */
-			option_a = 1; /* Record it and resolve it later. */
+			bsdtar->flags |= OPTFLAG_AUTO_COMPRESS;
+			break;
+		case OPTION_ACLS: /* GNU tar */
+			bsdtar->extract_flags |= ARCHIVE_EXTRACT_ACL;
+			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_ACL;
+			bsdtar->flags |= OPTFLAG_ACLS;
 			break;
 		case 'B': /* GNU tar */
 			/* libarchive doesn't need this; just ignore it. */
@@ -285,24 +297,26 @@
 			set_mode(bsdtar, opt);
 			break;
 		case OPTION_CHECK_LINKS: /* GNU tar */
-			bsdtar->option_warn_links = 1;
+			bsdtar->flags |= OPTFLAG_WARN_LINKS;
 			break;
 		case OPTION_CHROOT: /* NetBSD */
-			bsdtar->option_chroot = 1;
+			bsdtar->flags |= OPTFLAG_CHROOT;
 			break;
 		case OPTION_CLEAR_NOCHANGE_FFLAGS:
 			bsdtar->extract_flags |=
 			    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS;
 			break;
-		case OPTION_DISABLE_COPYFILE: /* Mac OS X */
-			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
-			break;
 		case OPTION_EXCLUDE: /* GNU tar */
 			if (archive_match_exclude_pattern(
 			    bsdtar->matching, bsdtar->argument) != ARCHIVE_OK)
 				lafe_errc(1, 0,
 				    "Couldn't exclude %s\n", bsdtar->argument);
 			break;
+		case OPTION_FFLAGS:
+			bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
+			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS;
+			bsdtar->flags |= OPTFLAG_FFLAGS;
+			break;
 		case OPTION_FORMAT: /* GNU tar, others */
 			cset_set_format(bsdtar->cset, bsdtar->argument);
 			break;
@@ -344,7 +358,7 @@
 			    ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED;
 			break;
 		case OPTION_IGNORE_ZEROS:
-			bsdtar->option_ignore_zeros = 1;
+			bsdtar->flags |= OPTFLAG_IGNORE_ZEROS;
 			break;
 		case 'I': /* GNU tar */
 			/*
@@ -398,7 +412,7 @@
 			break;
 	        case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
 			/* GNU tar 1.13  used -l for --one-file-system */
-			bsdtar->option_warn_links = 1;
+			bsdtar->flags |= OPTFLAG_WARN_LINKS;
 			break;
 		case OPTION_LRZIP:
 		case OPTION_LZ4:
@@ -421,8 +435,13 @@
 		case 'm': /* SUSv2 */
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
 			break;
+		case OPTION_MAC_METADATA: /* Mac OS X */
+			bsdtar->readdisk_flags |= ARCHIVE_READDISK_MAC_COPYFILE;
+			bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA;
+			bsdtar->flags |= OPTFLAG_MAC_METADATA;
+			break;
 		case 'n': /* GNU tar */
-			bsdtar->option_no_subdirs = 1;
+			bsdtar->flags |= OPTFLAG_NO_SUBDIRS;
 			break;
 	        /*
 		 * Selecting files by time:
@@ -466,6 +485,21 @@
 			bsdtar->extract_flags |=
 			    ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
 			break;
+		case OPTION_NO_ACLS: /* GNU tar */
+			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_ACL;
+			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_ACL;
+			bsdtar->flags |= OPTFLAG_NO_ACLS;
+			break;
+		case OPTION_NO_FFLAGS:
+			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
+			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_FFLAGS;
+			bsdtar->flags |= OPTFLAG_NO_FFLAGS;
+			break;
+		case OPTION_NO_MAC_METADATA: /* Mac OS X */
+			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_MAC_COPYFILE;
+			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
+			bsdtar->flags |= OPTFLAG_NO_MAC_METADATA;
+			break;
 		case OPTION_NO_SAME_OWNER: /* GNU tar */
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
 			break;
@@ -476,23 +510,24 @@
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_FFLAGS;
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_MAC_METADATA;
 			break;
-		case OPTION_NO_XATTR: /* Issue #131 */
+		case OPTION_NO_XATTRS: /* GNU tar */
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_XATTR;
 			bsdtar->readdisk_flags |= ARCHIVE_READDISK_NO_XATTR;
+			bsdtar->flags |= OPTFLAG_NO_XATTRS;
 			break;
 		case OPTION_NULL: /* GNU tar */
-			bsdtar->option_null++;
+			bsdtar->flags |= OPTFLAG_NULL;
 			break;
 		case OPTION_NUMERIC_OWNER: /* GNU tar */
 			bsdtar->uname = "";
 			bsdtar->gname = "";
-			bsdtar->option_numeric_owner++;
+			bsdtar->flags |= OPTFLAG_NUMERIC_OWNER;
 			break;
 		case 'O': /* GNU tar */
-			bsdtar->option_stdout = 1;
+			bsdtar->flags |= OPTFLAG_STDOUT;
 			break;
 		case 'o': /* SUSv2 and GNU conflict here, but not fatally */
-			option_o = 1; /* Record it and resolve it later. */
+			bsdtar->flags |= OPTFLAG_O;
 			break;
 	        /*
 		 * Selecting files by time:
@@ -548,7 +583,7 @@
 #endif
 		case 'P': /* GNU tar */
 			bsdtar->extract_flags &= ~SECURITY;
-			bsdtar->option_absolute_paths = 1;
+			bsdtar->flags |= OPTFLAG_ABSOLUTE_PATHS;
 			break;
 		case 'p': /* GNU tar, star */
 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_PERM;
@@ -564,7 +599,7 @@
 			cset_set_format(bsdtar->cset, "pax");
 			break;
 		case 'q': /* FreeBSD GNU tar --fast-read, NetBSD -q */
-			bsdtar->option_fast_read = 1;
+			bsdtar->flags |= OPTFLAG_FAST_READ;
 			break;
 		case 'r': /* SUSv2 */
 			set_mode(bsdtar, opt);
@@ -601,11 +636,11 @@
 			bsdtar->verbose++;
 			break;
 		case OPTION_TOTALS: /* GNU tar */
-			bsdtar->option_totals++;
+			bsdtar->flags |= OPTFLAG_TOTALS;
 			break;
 		case 'U': /* GNU tar */
 			bsdtar->extract_flags |= ARCHIVE_EXTRACT_UNLINK;
-			bsdtar->option_unlink_first = 1;
+			bsdtar->flags |= OPTFLAG_UNLINK_FIRST;
 			break;
 		case 'u': /* SUSv2 */
 			set_mode(bsdtar, opt);
@@ -643,7 +678,7 @@
 			break;
 #endif
 		case 'w': /* SUSv2 */
-			bsdtar->option_interactive = 1;
+			bsdtar->flags |= OPTFLAG_INTERACTIVE;
 			break;
 		case 'X': /* GNU tar */
 			if (archive_match_exclude_pattern_from_file(
@@ -655,6 +690,11 @@
 		case 'x': /* SUSv2 */
 			set_mode(bsdtar, opt);
 			break;
+		case OPTION_XATTRS: /* GNU tar */
+			bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
+			bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_XATTR;
+			bsdtar->flags |= OPTFLAG_XATTRS;
+			break;
 		case 'y': /* FreeBSD version of GNU tar */
 			if (compression != '\0')
 				lafe_errc(1, 0,
@@ -703,11 +743,11 @@
 		    "Must specify one of -c, -r, -t, -u, -x");
 
 	/* Check boolean options only permitted in certain modes. */
-	if (option_a)
+	if (bsdtar->flags & OPTFLAG_AUTO_COMPRESS)
 		only_mode(bsdtar, "-a", "c");
 	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
 		only_mode(bsdtar, "--one-file-system", "cru");
-	if (bsdtar->option_fast_read)
+	if (bsdtar->flags & OPTFLAG_FAST_READ)
 		only_mode(bsdtar, "--fast-read", "xt");
 	if (bsdtar->extract_flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED)
 		only_mode(bsdtar, "--hfsCompression", "x");
@@ -715,9 +755,23 @@
 		only_mode(bsdtar, "--nopreserveHFSCompression", "x");
 	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_HONOR_NODUMP)
 		only_mode(bsdtar, "--nodump", "cru");
-	if (bsdtar->readdisk_flags & ARCHIVE_READDISK_NO_XATTR)
-		only_mode(bsdtar, "--no-xattr", "crux");
-	if (option_o > 0) {
+	if (bsdtar->flags & OPTFLAG_ACLS)
+		only_mode(bsdtar, "--acls", "crux");
+	if (bsdtar->flags & OPTFLAG_NO_ACLS)
+		only_mode(bsdtar, "--no-acls", "crux");
+	if (bsdtar->flags & OPTFLAG_XATTRS)
+		only_mode(bsdtar, "--xattrs", "crux");
+	if (bsdtar->flags & OPTFLAG_NO_XATTRS)
+		only_mode(bsdtar, "--no-xattrs", "crux");
+	if (bsdtar->flags & OPTFLAG_FFLAGS)
+		only_mode(bsdtar, "--fflags", "crux");
+	if (bsdtar->flags & OPTFLAG_NO_FFLAGS)
+		only_mode(bsdtar, "--no-fflags", "crux");
+	if (bsdtar->flags & OPTFLAG_MAC_METADATA)
+		only_mode(bsdtar, "--mac-metadata", "crux");
+	if (bsdtar->flags & OPTFLAG_NO_MAC_METADATA)
+		only_mode(bsdtar, "--no-mac-metadata", "crux");
+	if (bsdtar->flags & OPTFLAG_O) {
 		switch (bsdtar->mode) {
 		case 'c':
 			/*
@@ -730,7 +784,7 @@
 			break;
 		case 'x':
 			/* POSIX-compatible behavior. */
-			bsdtar->option_no_owner = 1;
+			bsdtar->flags |= OPTFLAG_NO_OWNER;
 			bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
 			break;
 		default:
@@ -738,16 +792,17 @@
 			break;
 		}
 	}
-	if (bsdtar->option_no_subdirs)
+	if (bsdtar->flags & OPTFLAG_NO_SUBDIRS)
 		only_mode(bsdtar, "-n", "cru");
-	if (bsdtar->option_stdout)
+	if (bsdtar->flags & OPTFLAG_STDOUT)
 		only_mode(bsdtar, "-O", "xt");
-	if (bsdtar->option_unlink_first)
+	if (bsdtar->flags & OPTFLAG_UNLINK_FIRST)
 		only_mode(bsdtar, "-U", "x");
-	if (bsdtar->option_warn_links)
+	if (bsdtar->flags & OPTFLAG_WARN_LINKS)
 		only_mode(bsdtar, "--check-links", "cr");
 
-	if (option_a && cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
+	if ((bsdtar->flags & OPTFLAG_AUTO_COMPRESS) &&
+	    cset_auto_compress(bsdtar->cset, bsdtar->filename)) {
 		/* Ignore specified compressions if auto-compress works. */
 		compression = '\0';
 		compression2 = '\0';
diff --git a/tar/bsdtar.h b/tar/bsdtar.h
index 4b84ba1..10a2cf2 100644
--- a/tar/bsdtar.h
+++ b/tar/bsdtar.h
@@ -50,6 +50,7 @@
 	int		  bytes_per_block; /* -b block_size */
 	int		  bytes_in_last_block; /* See -b handling. */
 	int		  verbose;   /* -v */
+	unsigned int	  flags; /* Bitfield of boolean options */
 	int		  extract_flags; /* Flags for extract operation */
 	int		  readdisk_flags; /* Flags for read disk operation */
 	int		  strip_components; /* Remove this many leading dirs */
@@ -60,20 +61,7 @@
 	const char	 *passphrase; /* --passphrase */
 	char		  mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */
 	char		  symlink_mode; /* H or L, per BSD conventions */
-	char		  option_absolute_paths; /* -P */
-	char		  option_chroot; /* --chroot */
-	char		  option_fast_read; /* --fast-read */
 	const char	 *option_options; /* --options */
-	char		  option_ignore_zeros; /* --ignore-zeros */
-	char		  option_interactive; /* -w */
-	char		  option_no_owner; /* -o */
-	char		  option_no_subdirs; /* -n */
-	char		  option_numeric_owner; /* --numeric-owner */
-	char		  option_null; /* --null */
-	char		  option_stdout; /* -O */
-	char		  option_totals; /* --totals */
-	char		  option_unlink_first; /* -U */
-	char		  option_warn_links; /* --check-links */
 	char		  day_first; /* show day before month in -tv output */
 	struct creation_set *cset;
 
@@ -114,14 +102,40 @@
 	char			*ppbuff;	/* for util.c */
 };
 
+/* Options for flags bitfield */
+#define	OPTFLAG_AUTO_COMPRESS	(0x00000001)	/* -a */
+#define	OPTFLAG_ABSOLUTE_PATHS	(0x00000002)	/* -P */
+#define	OPTFLAG_CHROOT		(0x00000004)	/* --chroot */
+#define	OPTFLAG_FAST_READ	(0x00000008)	/* --fast-read */
+#define	OPTFLAG_IGNORE_ZEROS	(0x00000010)	/* --ignore-zeros */
+#define	OPTFLAG_INTERACTIVE	(0x00000020)	/* -w */
+#define	OPTFLAG_NO_OWNER	(0x00000040)	/* -o */
+#define	OPTFLAG_NO_SUBDIRS	(0x00000080)	/* -n */
+#define	OPTFLAG_NULL		(0x00000100)	/* --null */
+#define	OPTFLAG_NUMERIC_OWNER	(0x00000200)	/* --numeric-owner */
+#define	OPTFLAG_O		(0x00000400)	/* -o */
+#define	OPTFLAG_STDOUT		(0x00000800)	/* -O */
+#define	OPTFLAG_TOTALS		(0x00001000)	/* --totals */
+#define	OPTFLAG_UNLINK_FIRST	(0x00002000)	/* -U */
+#define	OPTFLAG_WARN_LINKS	(0x00004000)	/* --check-links */
+#define	OPTFLAG_NO_XATTRS	(0x00008000)	/* --no-xattrs */
+#define	OPTFLAG_XATTRS		(0x00010000)	/* --xattrs */
+#define	OPTFLAG_NO_ACLS		(0x00020000)	/* --no-acls */
+#define	OPTFLAG_ACLS		(0x00040000)	/* --acls */
+#define	OPTFLAG_NO_FFLAGS	(0x00080000)	/* --no-fflags */
+#define	OPTFLAG_FFLAGS		(0x00100000)	/* --fflags */
+#define	OPTFLAG_NO_MAC_METADATA	(0x00200000)	/* --no-mac-metadata */
+#define	OPTFLAG_MAC_METADATA	(0x00400000)	/* --mac-metadata */
+
 /* Fake short equivalents for long options that otherwise lack them. */
 enum {
-	OPTION_B64ENCODE = 1,
+	OPTION_ACLS = 1,
+	OPTION_B64ENCODE,
 	OPTION_CHECK_LINKS,
 	OPTION_CHROOT,
 	OPTION_CLEAR_NOCHANGE_FFLAGS,
-	OPTION_DISABLE_COPYFILE,
 	OPTION_EXCLUDE,
+	OPTION_FFLAGS,
 	OPTION_FORMAT,
 	OPTION_GID,
 	OPTION_GNAME,
@@ -136,15 +150,19 @@
 	OPTION_LZIP,
 	OPTION_LZMA,
 	OPTION_LZOP,
+	OPTION_MAC_METADATA,
 	OPTION_NEWER_CTIME,
 	OPTION_NEWER_CTIME_THAN,
 	OPTION_NEWER_MTIME,
 	OPTION_NEWER_MTIME_THAN,
 	OPTION_NODUMP,
 	OPTION_NOPRESERVE_HFS_COMPRESSION,
+	OPTION_NO_ACLS,
+	OPTION_NO_FFLAGS,
+	OPTION_NO_MAC_METADATA,
 	OPTION_NO_SAME_OWNER,
 	OPTION_NO_SAME_PERMISSIONS,
-	OPTION_NO_XATTR,
+	OPTION_NO_XATTRS,
 	OPTION_NULL,
 	OPTION_NUMERIC_OWNER,
 	OPTION_OLDER_CTIME,
@@ -162,7 +180,8 @@
 	OPTION_UNAME,
 	OPTION_USE_COMPRESS_PROGRAM,
 	OPTION_UUENCODE,
-	OPTION_VERSION
+	OPTION_VERSION,
+	OPTION_XATTRS
 };
 
 int	bsdtar_getopt(struct bsdtar *);
@@ -170,7 +189,7 @@
 int	edit_pathname(struct bsdtar *, struct archive_entry *);
 int	need_report(void);
 int	pathcmp(const char *a, const char *b);
-void	safe_fprintf(FILE *, const char *fmt, ...);
+void	safe_fprintf(FILE *, const char *fmt, ...) __LA_PRINTF(2, 3);
 void	set_chdir(struct bsdtar *, const char *newdir);
 const char *tar_i64toa(int64_t);
 void	tar_mode_c(struct bsdtar *bsdtar);
@@ -178,8 +197,8 @@
 void	tar_mode_t(struct bsdtar *bsdtar);
 void	tar_mode_u(struct bsdtar *bsdtar);
 void	tar_mode_x(struct bsdtar *bsdtar);
-void	usage(void);
-int	yes(const char *fmt, ...);
+void	usage(void) __LA_DEAD;
+int	yes(const char *fmt, ...) __LA_PRINTF(1, 2);
 
 #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
 void	add_substitution(struct bsdtar *, const char *);
diff --git a/tar/cmdline.c b/tar/cmdline.c
index c87741c..e36c545 100644
--- a/tar/cmdline.c
+++ b/tar/cmdline.c
@@ -65,6 +65,7 @@
 } tar_longopts[] = {
 	{ "absolute-paths",       0, 'P' },
 	{ "append",               0, 'r' },
+	{ "acls",                 0, OPTION_ACLS },
 	{ "auto-compress",        0, 'a' },
 	{ "b64encode",            0, OPTION_B64ENCODE },
 	{ "block-size",           1, 'b' },
@@ -81,11 +82,12 @@
 	{ "create",               0, 'c' },
 	{ "dereference",	  0, 'L' },
 	{ "directory",            1, 'C' },
-	{ "disable-copyfile",	  0, OPTION_DISABLE_COPYFILE },
+	{ "disable-copyfile",	  0, OPTION_NO_MAC_METADATA },
 	{ "exclude",              1, OPTION_EXCLUDE },
 	{ "exclude-from",         1, 'X' },
 	{ "extract",              0, 'x' },
 	{ "fast-read",            0, 'q' },
+	{ "fflags",               0, OPTION_FFLAGS },
 	{ "file",                 1, 'f' },
 	{ "files-from",           1, 'T' },
 	{ "format",               1, OPTION_FORMAT },
@@ -108,6 +110,7 @@
 	{ "lzip",                 0, OPTION_LZIP },
 	{ "lzma",                 0, OPTION_LZMA },
 	{ "lzop",                 0, OPTION_LZOP },
+	{ "mac-metadata",         0, OPTION_MAC_METADATA },
 	{ "modification-time",    0, 'm' },
 	{ "newer",		  1, OPTION_NEWER_CTIME },
 	{ "newer-ctime",	  1, OPTION_NEWER_CTIME },
@@ -115,10 +118,14 @@
 	{ "newer-mtime",	  1, OPTION_NEWER_MTIME },
 	{ "newer-mtime-than",	  1, OPTION_NEWER_MTIME_THAN },
 	{ "newer-than",		  1, OPTION_NEWER_CTIME_THAN },
+	{ "no-acls",              0, OPTION_NO_ACLS },
+	{ "no-fflags",            0, OPTION_NO_FFLAGS },
+	{ "no-mac-metadata",      0, OPTION_NO_MAC_METADATA },
 	{ "no-recursion",         0, 'n' },
 	{ "no-same-owner",	  0, OPTION_NO_SAME_OWNER },
 	{ "no-same-permissions",  0, OPTION_NO_SAME_PERMISSIONS },
-	{ "no-xattr",             0, OPTION_NO_XATTR },
+	{ "no-xattr",             0, OPTION_NO_XATTRS },
+	{ "no-xattrs",            0, OPTION_NO_XATTRS },
 	{ "nodump",               0, OPTION_NODUMP },
 	{ "nopreserveHFSCompression",0, OPTION_NOPRESERVE_HFS_COMPRESSION },
 	{ "norecurse",            0, 'n' },
@@ -151,6 +158,7 @@
 	{ "uuencode",             0, OPTION_UUENCODE },
 	{ "verbose",              0, 'v' },
 	{ "version",              0, OPTION_VERSION },
+	{ "xattrs",               0, OPTION_XATTRS },
 	{ "xz",                   0, 'J' },
 	{ NULL, 0, 0 }
 };
diff --git a/tar/read.c b/tar/read.c
index e94cb3d..658c810 100644
--- a/tar/read.c
+++ b/tar/read.c
@@ -105,7 +105,7 @@
 	writer = archive_write_disk_new();
 	if (writer == NULL)
 		lafe_errc(1, ENOMEM, "Cannot allocate disk writer object");
-	if (!bsdtar->option_numeric_owner)
+	if ((bsdtar->flags & OPTFLAG_NUMERIC_OWNER) == 0)
 		archive_write_disk_set_standard_lookup(writer);
 	archive_write_disk_set_options(writer, bsdtar->extract_flags);
 
@@ -177,7 +177,7 @@
 	if (bsdtar->names_from_file != NULL)
 		if (archive_match_include_pattern_from_file(
 		    bsdtar->matching, bsdtar->names_from_file,
-		    bsdtar->option_null) != ARCHIVE_OK)
+		    (bsdtar->flags & OPTFLAG_NULL)) != ARCHIVE_OK)
 			lafe_errc(1, 0, "Error inclusion pattern: %s",
 			    archive_error_string(bsdtar->matching));
 
@@ -188,18 +188,17 @@
 
 	reader_options = getenv(ENV_READER_OPTIONS);
 	if (reader_options != NULL) {
+		size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1;
+		size_t opt_len = strlen(reader_options) + 1;
 		char *p;
 		/* Set default read options. */
-		p = (char *)malloc(sizeof(IGNORE_WRONG_MODULE_NAME)
-		    + strlen(reader_options) + 1);
-		if (p == NULL)
+		if ((p = malloc(module_len + opt_len)) == NULL)
 			lafe_errc(1, errno, "Out of memory");
 		/* Prepend magic code to ignore options for
 		 * a format or  modules which are not added to
 		 *  the archive read object. */
-		strncpy(p, IGNORE_WRONG_MODULE_NAME,
-		    sizeof(IGNORE_WRONG_MODULE_NAME) -1);
-		strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options);
+		memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len);
+		memcpy(p + module_len, reader_options, opt_len);
 		r = archive_read_set_options(a, p);
 		free(p);
 		if (r == ARCHIVE_FATAL)
@@ -209,7 +208,7 @@
 	}
 	if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
 		lafe_errc(1, 0, "%s", archive_error_string(a));
-	if (bsdtar->option_ignore_zeros)
+	if (bsdtar->flags & OPTFLAG_IGNORE_ZEROS)
 		if (archive_read_set_options(a,
 		    "read_concatenated_archives") != ARCHIVE_OK)
 			lafe_errc(1, 0, "%s", archive_error_string(a));
@@ -235,7 +234,7 @@
 		    &progress_data);
 	}
 
-	if (mode == 'x' && bsdtar->option_chroot) {
+	if (mode == 'x' && (bsdtar->flags & OPTFLAG_CHROOT)) {
 #if HAVE_CHROOT
 		if (chroot(".") != 0)
 			lafe_errc(1, errno, "Can't chroot to \".\"");
@@ -246,7 +245,7 @@
 	}
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-	if (mode == 'x' && bsdtar->option_stdout) {
+	if (mode == 'x' && (bsdtar->flags & OPTFLAG_STDOUT)) {
 		_setmode(1, _O_BINARY);
 	}
 #endif
@@ -254,7 +253,7 @@
 	for (;;) {
 		/* Support --fast-read option */
 		const char *p;
-		if (bsdtar->option_fast_read &&
+		if ((bsdtar->flags & OPTFLAG_FAST_READ) &&
 		    archive_match_path_unmatched_inclusions(bsdtar->matching) == 0)
 			break;
 
@@ -308,7 +307,8 @@
 		if (mode == 't') {
 			/* Perversely, gtar uses -O to mean "send to stderr"
 			 * when used with -t. */
-			out = bsdtar->option_stdout ? stderr : stdout;
+			out = (bsdtar->flags & OPTFLAG_STDOUT) ?
+			    stderr : stdout;
 
 			/*
 			 * TODO: Provide some reasonable way to
@@ -346,7 +346,7 @@
 			if (edit_pathname(bsdtar, entry))
 				continue; /* Excluded by a rewrite failure. */
 
-			if (bsdtar->option_interactive &&
+			if ((bsdtar->flags & OPTFLAG_INTERACTIVE) &&
 			    !yes("extract '%s'", archive_entry_pathname(entry)))
 				continue;
 
@@ -365,7 +365,7 @@
 
 			/* TODO siginfo_printinfo(bsdtar, 0); */
 
-			if (bsdtar->option_stdout)
+			if (bsdtar->flags & OPTFLAG_STDOUT)
 				r = archive_read_data_into_fd(a, 1);
 			else
 				r = archive_read_extract2(a, entry, writer);
diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt
index 0d2da36..e6054ba 100644
--- a/tar/test/CMakeLists.txt
+++ b/tar/test/CMakeLists.txt
@@ -6,7 +6,7 @@
 IF(ENABLE_TAR AND ENABLE_TEST)
   SET(bsdtar_test_SOURCES
     ../../test_utils/test_utils.c
-    main.c
+    ../../test_utils/test_main.c
     test.h
     test_0.c
     test_basic.c
@@ -34,9 +34,11 @@
     test_option_U_upper.c
     test_option_X_upper.c
     test_option_a.c
+    test_option_acls.c
     test_option_b.c
     test_option_b64encode.c
     test_option_exclude.c
+    test_option_fflags.c
     test_option_gid_gname.c
     test_option_grzip.c
     test_option_j.c
@@ -71,6 +73,11 @@
   # Register target
   #
   ADD_EXECUTABLE(bsdtar_test ${bsdtar_test_SOURCES})
+  IF(ENABLE_ACL)
+    IF(HAVE_LIBACL)
+      TARGET_LINK_LIBRARIES(bsdtar_test ${ACL_LIBRARY})
+    ENDIF(HAVE_LIBACL)
+  ENDIF(ENABLE_ACL)
   SET_PROPERTY(TARGET bsdtar_test PROPERTY COMPILE_DEFINITIONS LIST_H)
 
   #
@@ -96,6 +103,7 @@
   INCLUDE(${CMAKE_CURRENT_BINARY_DIR}/list.h)
   INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/test_utils)
+  INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/tar/test)
 
   # Experimental new test handling
   ADD_CUSTOM_TARGET(run_bsdtar_test
diff --git a/tar/test/main.c b/tar/test/main.c
deleted file mode 100644
index 8d31706..0000000
--- a/tar/test/main.c
+++ /dev/null
@@ -1,3072 +0,0 @@
-/*
- * Copyright (c) 2003-2009 Tim Kientzle
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "test.h"
-#include "test_utils.h"
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#include <errno.h>
-#ifdef HAVE_ICONV_H
-#include <iconv.h>
-#endif
-/*
- * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
- * As the include guards don't agree, the order of include is important.
- */
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h>      /* for Linux file flags */
-#endif
-#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
-#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
-#endif
-#include <limits.h>
-#include <locale.h>
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include <stdarg.h>
-#include <time.h>
-
-/*
- * This same file is used pretty much verbatim for all test harnesses.
- *
- * The next few lines are the only differences.
- * TODO: Move this into a separate configuration header, have all test
- * suites share one copy of this file.
- */
-__FBSDID("$FreeBSD: src/usr.bin/tar/test/main.c,v 1.6 2008/11/05 06:40:53 kientzle Exp $");
-#define KNOWNREF	"test_patterns_2.tar.uu"
-#define ENVBASE "BSDTAR"  /* Prefix for environment variables. */
-#define	PROGRAM "bsdtar"  /* Name of program being tested. */
-#define PROGRAM_ALIAS "tar" /* Generic alias for program */
-#undef	LIBRARY		  /* Not testing a library. */
-#undef	EXTRA_DUMP	  /* How to dump extra data */
-#undef	EXTRA_ERRNO	  /* How to dump errno */
-/* How to generate extra version info. */
-#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
-
-/*
- *
- * Windows support routines
- *
- * Note: Configuration is a tricky issue.  Using HAVE_* feature macros
- * in the test harness is dangerous because they cover up
- * configuration errors.  The classic example of this is omitting a
- * configure check.  If libarchive and libarchive_test both look for
- * the same feature macro, such errors are hard to detect.  Platform
- * macros (e.g., _WIN32 or __GNUC__) are a little better, but can
- * easily lead to very messy code.  It's best to limit yourself
- * to only the most generic programming techniques in the test harness
- * and thus avoid conditionals altogether.  Where that's not possible,
- * try to minimize conditionals by grouping platform-specific tests in
- * one place (e.g., test_acl_freebsd) or by adding new assert()
- * functions (e.g., assertMakeHardlink()) to cover up platform
- * differences.  Platform-specific coding in libarchive_test is often
- * a symptom that some capability is missing from libarchive itself.
- */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#include <io.h>
-#include <direct.h>
-#include <windows.h>
-#ifndef F_OK
-#define F_OK (0)
-#endif
-#ifndef S_ISDIR
-#define S_ISDIR(m)  ((m) & _S_IFDIR)
-#endif
-#ifndef S_ISREG
-#define S_ISREG(m)  ((m) & _S_IFREG)
-#endif
-#if !defined(__BORLANDC__)
-#define access _access
-#undef chdir
-#define chdir _chdir
-#endif
-#ifndef fileno
-#define fileno _fileno
-#endif
-/*#define fstat _fstat64*/
-#if !defined(__BORLANDC__)
-#define getcwd _getcwd
-#endif
-#define lstat stat
-/*#define lstat _stat64*/
-/*#define stat _stat64*/
-#define rmdir _rmdir
-#if !defined(__BORLANDC__)
-#define strdup _strdup
-#define umask _umask
-#endif
-#define int64_t __int64
-#endif
-
-#if defined(HAVE__CrtSetReportMode)
-# include <crtdbg.h>
-#endif
-
-mode_t umasked(mode_t expected_mode)
-{
-	mode_t mode = umask(0);
-	umask(mode);
-	return expected_mode & ~mode;
-}
-
-/* Path to working directory for current test */
-const char *testworkdir;
-#ifdef PROGRAM
-/* Pathname of exe to be tested. */
-const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-const char *testprog;
-#endif
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static void	*GetFunctionKernel32(const char *);
-static int	 my_CreateSymbolicLinkA(const char *, const char *, int);
-static int	 my_CreateHardLinkA(const char *, const char *);
-static int	 my_GetFileInformationByName(const char *,
-		     BY_HANDLE_FILE_INFORMATION *);
-
-static void *
-GetFunctionKernel32(const char *name)
-{
-	static HINSTANCE lib;
-	static int set;
-	if (!set) {
-		set = 1;
-		lib = LoadLibrary("kernel32.dll");
-	}
-	if (lib == NULL) {
-		fprintf(stderr, "Can't load kernel32.dll?!\n");
-		exit(1);
-	}
-	return (void *)GetProcAddress(lib, name);
-}
-
-static int
-my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateSymbolicLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, flags);
-}
-
-static int
-my_CreateHardLinkA(const char *linkname, const char *target)
-{
-	static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES);
-	static int set;
-	if (!set) {
-		set = 1;
-		f = GetFunctionKernel32("CreateHardLinkA");
-	}
-	return f == NULL ? 0 : (*f)(linkname, target, NULL);
-}
-
-static int
-my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi)
-{
-	HANDLE h;
-	int r;
-
-	memset(bhfi, 0, sizeof(*bhfi));
-	h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL,
-		OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE)
-		return (0);
-	r = GetFileInformationByHandle(h, bhfi);
-	CloseHandle(h);
-	return (r);
-}
-#endif
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-static void
-invalid_parameter_handler(const wchar_t * expression,
-    const wchar_t * function, const wchar_t * file,
-    unsigned int line, uintptr_t pReserved)
-{
-	/* nop */
-}
-#endif
-
-/*
- *
- * OPTIONS FLAGS
- *
- */
-
-/* Enable core dump on failure. */
-static int dump_on_failure = 0;
-/* Default is to remove temp dirs and log data for successful tests. */
-static int keep_temp_files = 0;
-/* Default is to run the specified tests once and report errors. */
-static int until_failure = 0;
-/* Default is to just report pass/fail for each test. */
-static int verbosity = 0;
-#define	VERBOSITY_SUMMARY_ONLY -1 /* -q */
-#define VERBOSITY_PASSFAIL 0   /* Default */
-#define VERBOSITY_LIGHT_REPORT 1 /* -v */
-#define VERBOSITY_FULL 2 /* -vv */
-/* A few places generate even more output for verbosity > VERBOSITY_FULL,
- * mostly for debugging the test harness itself. */
-/* Cumulative count of assertion failures. */
-static int failures = 0;
-/* Cumulative count of reported skips. */
-static int skips = 0;
-/* Cumulative count of assertions checked. */
-static int assertions = 0;
-
-/* Directory where uuencoded reference files can be found. */
-static const char *refdir;
-
-/*
- * Report log information selectively to console and/or disk log.
- */
-static int log_console = 0;
-static FILE *logfile;
-static void
-vlogprintf(const char *fmt, va_list ap)
-{
-#ifdef va_copy
-	va_list lfap;
-	va_copy(lfap, ap);
-#endif
-	if (log_console)
-		vfprintf(stdout, fmt, ap);
-	if (logfile != NULL)
-#ifdef va_copy
-		vfprintf(logfile, fmt, lfap);
-	va_end(lfap);
-#else
-		vfprintf(logfile, fmt, ap);
-#endif
-}
-
-static void
-logprintf(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	vlogprintf(fmt, ap);
-	va_end(ap);
-}
-
-/* Set up a message to display only if next assertion fails. */
-static char msgbuff[4096];
-static const char *msg, *nextmsg;
-void
-failure(const char *fmt, ...)
-{
-	va_list ap;
-	if (fmt == NULL) {
-		nextmsg = NULL;
-	} else {
-		va_start(ap, fmt);
-		vsprintf(msgbuff, fmt, ap);
-		va_end(ap);
-		nextmsg = msgbuff;
-	}
-}
-
-/*
- * Copy arguments into file-local variables.
- * This was added to permit vararg assert() functions without needing
- * variadic wrapper macros.  Turns out that the vararg capability is almost
- * never used, so almost all of the vararg assertions can be simplified
- * by removing the vararg capability and reworking the wrapper macro to
- * pass __FILE__, __LINE__ directly into the function instead of using
- * this hook.  I suspect this machinery is used so rarely that we
- * would be better off just removing it entirely.  That would simplify
- * the code here noticeably.
- */
-static const char *skipping_filename;
-static int skipping_line;
-void skipping_setup(const char *filename, int line)
-{
-	skipping_filename = filename;
-	skipping_line = line;
-}
-
-/* Called at the beginning of each assert() function. */
-static void
-assertion_count(const char *file, int line)
-{
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	++assertions;
-	/* Proper handling of "failure()" message. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* Uncomment to print file:line after every assertion.
-	 * Verbose, but occasionally useful in tracking down crashes. */
-	/* printf("Checked %s:%d\n", file, line); */
-}
-
-/*
- * For each test source file, we remember how many times each
- * assertion was reported.  Cleared before each new test,
- * used by test_summarize().
- */
-static struct line {
-	int count;
-	int skip;
-}  failed_lines[10000];
-const char *failed_filename;
-
-/* Count this failure, setup up log destination and handle initial report. */
-static void
-failure_start(const char *filename, int line, const char *fmt, ...)
-{
-	va_list ap;
-
-	/* Record another failure for this line. */
-	++failures;
-	failed_filename = filename;
-	failed_lines[line].count++;
-
-	/* Determine whether to log header to console. */
-	switch (verbosity) {
-	case VERBOSITY_LIGHT_REPORT:
-		log_console = (failed_lines[line].count < 2);
-		break;
-	default:
-		log_console = (verbosity >= VERBOSITY_FULL);
-	}
-
-	/* Log file:line header for this failure */
-	va_start(ap, fmt);
-#if _MSC_VER
-	logprintf("%s(%d): ", filename, line);
-#else
-	logprintf("%s:%d: ", filename, line);
-#endif
-	vlogprintf(fmt, ap);
-	va_end(ap);
-	logprintf("\n");
-
-	if (msg != NULL && msg[0] != '\0') {
-		logprintf("   Description: %s\n", msg);
-		msg = NULL;
-	}
-
-	/* Determine whether to log details to console. */
-	if (verbosity == VERBOSITY_LIGHT_REPORT)
-		log_console = 0;
-}
-
-/* Complete reporting of failed tests. */
-/*
- * The 'extra' hook here is used by libarchive to include libarchive
- * error messages with assertion failures.  It could also be used
- * to add strerror() output, for example.  Just define the EXTRA_DUMP()
- * macro appropriately.
- */
-static void
-failure_finish(void *extra)
-{
-	(void)extra; /* UNUSED (maybe) */
-#ifdef EXTRA_DUMP
-	if (extra != NULL) {
-		logprintf("    errno: %d\n", EXTRA_ERRNO(extra));
-		logprintf("   detail: %s\n", EXTRA_DUMP(extra));
-	}
-#endif
-
-	if (dump_on_failure) {
-		fprintf(stderr,
-		    " *** forcing core dump so failure can be debugged ***\n");
-		abort();
-	}
-}
-
-/* Inform user that we're skipping some checks. */
-void
-test_skipping(const char *fmt, ...)
-{
-	char buff[1024];
-	va_list ap;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	va_end(ap);
-	/* Use failure() message if set. */
-	msg = nextmsg;
-	nextmsg = NULL;
-	/* failure_start() isn't quite right, but is awfully convenient. */
-	failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff);
-	--failures; /* Undo failures++ in failure_start() */
-	/* Don't failure_finish() here. */
-	/* Mark as skip, so doesn't count as failed test. */
-	failed_lines[skipping_line].skip = 1;
-	++skips;
-}
-
-/*
- *
- * ASSERTIONS
- *
- */
-
-/* Generic assert() just displays the failed condition. */
-int
-assertion_assert(const char *file, int line, int value,
-    const char *condition, void *extra)
-{
-	assertion_count(file, line);
-	if (!value) {
-		failure_start(file, line, "Assertion failed: %s", condition);
-		failure_finish(extra);
-	}
-	return (value);
-}
-
-/* chdir() and report any errors */
-int
-assertion_chdir(const char *file, int line, const char *pathname)
-{
-	assertion_count(file, line);
-	if (chdir(pathname) == 0)
-		return (1);
-	failure_start(file, line, "chdir(\"%s\")", pathname);
-	failure_finish(NULL);
-	return (0);
-
-}
-
-/* Verify two integers are equal. */
-int
-assertion_equal_int(const char *file, int line,
-    long long v1, const char *e1, long long v2, const char *e2, void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1);
-	logprintf("      %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Utility to convert a single UTF-8 sequence.
- */
-static int
-_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
-{
-	static const char utf8_count[256] = {
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
-		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
-		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
-		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
-		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
-		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
-		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
-	};
-	int ch;
-	int cnt;
-	uint32_t wc;
-
-	*pwc = 0;
-
-	/* Sanity check. */
-	if (n == 0)
-		return (0);
-	/*
-	 * Decode 1-4 bytes depending on the value of the first byte.
-	 */
-	ch = (unsigned char)*s;
-	if (ch == 0)
-		return (0); /* Standard:  return 0 for end-of-string. */
-	cnt = utf8_count[ch];
-
-	/* Invalid sequence or there are not plenty bytes. */
-	if (n < (size_t)cnt)
-		return (-1);
-
-	/* Make a Unicode code point from a single UTF-8 sequence. */
-	switch (cnt) {
-	case 1:	/* 1 byte sequence. */
-		*pwc = ch & 0x7f;
-		return (cnt);
-	case 2:	/* 2 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
-		return (cnt);
-	case 3:	/* 3 bytes sequence. */
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x0f) << 12)
-		    | ((s[1] & 0x3f) << 6)
-		    | (s[2] & 0x3f);
-		if (wc < 0x800)
-			return (-1);/* Overlong sequence. */
-		break;
-	case 4:	/* 4 bytes sequence. */
-		if (n < 4)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		if ((s[3] & 0xc0) != 0x80) return (-1);
-		wc = ((ch & 0x07) << 18)
-		    | ((s[1] & 0x3f) << 12)
-		    | ((s[2] & 0x3f) << 6)
-		    | (s[3] & 0x3f);
-		if (wc < 0x10000)
-			return (-1);/* Overlong sequence. */
-		break;
-	default:
-		return (-1);
-	}
-
-	/* The code point larger than 0x10FFFF is not legal
-	 * Unicode values. */
-	if (wc > 0x10FFFF)
-		return (-1);
-	/* Correctly gets a Unicode, returns used bytes. */
-	*pwc = wc;
-	return (cnt);
-}
-
-static void strdump(const char *e, const char *p, int ewidth, int utf8)
-{
-	const char *q = p;
-
-	logprintf("      %*s = ", ewidth, e);
-	if (p == NULL) {
-		logprintf("NULL\n");
-		return;
-	}
-	logprintf("\"");
-	while (*p != '\0') {
-		unsigned int c = 0xff & *p++;
-		switch (c) {
-		case '\a': logprintf("\\a"); break;
-		case '\b': logprintf("\\b"); break;
-		case '\n': logprintf("\\n"); break;
-		case '\r': logprintf("\\r"); break;
-		default:
-			if (c >= 32 && c < 127)
-				logprintf("%c", c);
-			else
-				logprintf("\\x%02X", c);
-		}
-	}
-	logprintf("\"");
-	logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q));
-
-	/*
-	 * If the current string is UTF-8, dump its code points.
-	 */
-	if (utf8) {
-		size_t len;
-		uint32_t uc;
-		int n;
-		int cnt = 0;
-
-		p = q;
-		len = strlen(p);
-		logprintf(" [");
-		while ((n = _utf8_to_unicode(&uc, p, len)) > 0) {
-			if (p != q)
-				logprintf(" ");
-			logprintf("%04X", uc);
-			p += n;
-			len -= n;
-			cnt++;
-		}
-		logprintf("]");
-		logprintf(" (count %d", cnt);
-		if (n < 0) {
-			logprintf(",unknown %d bytes", len);
-		}
-		logprintf(")");
-
-	}
-	logprintf("\n");
-}
-
-/* Verify two strings are equal, dump them if not. */
-int
-assertion_equal_string(const char *file, int line,
-    const char *v1, const char *e1,
-    const char *v2, const char *e2,
-    void *extra, int utf8)
-{
-	int l1, l2;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0))
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	l1 = (int)strlen(e1);
-	l2 = (int)strlen(e2);
-	if (l1 < l2)
-		l1 = l2;
-	strdump(e1, v1, l1, utf8);
-	strdump(e2, v2, l1, utf8);
-	failure_finish(extra);
-	return (0);
-}
-
-static void
-wcsdump(const char *e, const wchar_t *w)
-{
-	logprintf("      %s = ", e);
-	if (w == NULL) {
-		logprintf("(null)");
-		return;
-	}
-	logprintf("\"");
-	while (*w != L'\0') {
-		unsigned int c = *w++;
-		if (c >= 32 && c < 127)
-			logprintf("%c", c);
-		else if (c < 256)
-			logprintf("\\x%02X", c);
-		else if (c < 0x10000)
-			logprintf("\\u%04X", c);
-		else
-			logprintf("\\U%08X", c);
-	}
-	logprintf("\"\n");
-}
-
-#ifndef HAVE_WCSCMP
-static int
-wcscmp(const wchar_t *s1, const wchar_t *s2)
-{
-
-	while (*s1 == *s2++) {
-		if (*s1++ == L'\0')
-			return 0;
-	}
-	if (*s1 > *--s2)
-		return 1;
-	else
-		return -1;
-}
-#endif
-
-/* Verify that two wide strings are equal, dump them if not. */
-int
-assertion_equal_wstring(const char *file, int line,
-    const wchar_t *v1, const char *e1,
-    const wchar_t *v2, const char *e2,
-    void *extra)
-{
-	assertion_count(file, line);
-	if (v1 == v2)
-		return (1);
-	if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0)
-		return (1);
-	failure_start(file, line, "%s != %s", e1, e2);
-	wcsdump(e1, v1);
-	wcsdump(e2, v2);
-	failure_finish(extra);
-	return (0);
-}
-
-/*
- * Pretty standard hexdump routine.  As a bonus, if ref != NULL, then
- * any bytes in p that differ from ref will be highlighted with '_'
- * before and after the hex value.
- */
-static void
-hexdump(const char *p, const char *ref, size_t l, size_t offset)
-{
-	size_t i, j;
-	char sep;
-
-	if (p == NULL) {
-		logprintf("(null)\n");
-		return;
-	}
-	for(i=0; i < l; i+=16) {
-		logprintf("%04x", (unsigned)(i + offset));
-		sep = ' ';
-		for (j = 0; j < 16 && i + j < l; j++) {
-			if (ref != NULL && p[i + j] != ref[i + j])
-				sep = '_';
-			logprintf("%c%02x", sep, 0xff & (int)p[i+j]);
-			if (ref != NULL && p[i + j] == ref[i + j])
-				sep = ' ';
-		}
-		for (; j < 16; j++) {
-			logprintf("%c  ", sep);
-			sep = ' ';
-		}
-		logprintf("%c", sep);
-		for (j=0; j < 16 && i + j < l; j++) {
-			int c = p[i + j];
-			if (c >= ' ' && c <= 126)
-				logprintf("%c", c);
-			else
-				logprintf(".");
-		}
-		logprintf("\n");
-	}
-}
-
-/* Verify that two blocks of memory are the same, display the first
- * block of differences if they're not. */
-int
-assertion_equal_mem(const char *file, int line,
-    const void *_v1, const char *e1,
-    const void *_v2, const char *e2,
-    size_t l, const char *ld, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	const char *v2 = (const char *)_v2;
-	size_t offset;
-
-	assertion_count(file, line);
-	if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0))
-		return (1);
-	if (v1 == NULL || v2 == NULL)
-		return (0);
-
-	failure_start(file, line, "%s != %s", e1, e2);
-	logprintf("      size %s = %d\n", ld, (int)l);
-	/* Dump 48 bytes (3 lines) so that the first difference is
-	 * in the second line. */
-	offset = 0;
-	while (l > 64 && memcmp(v1, v2, 32) == 0) {
-		/* Two lines agree, so step forward one line. */
-		v1 += 16;
-		v2 += 16;
-		l -= 16;
-		offset += 16;
-	}
-	logprintf("      Dump of %s\n", e1);
-	hexdump(v1, v2, l < 128 ? l : 128, offset);
-	logprintf("      Dump of %s\n", e2);
-	hexdump(v2, v1, l < 128 ? l : 128, offset);
-	logprintf("\n");
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that a block of memory is filled with the specified byte. */
-int
-assertion_memory_filled_with(const char *file, int line,
-    const void *_v1, const char *vd,
-    size_t l, const char *ld,
-    char b, const char *bd, void *extra)
-{
-	const char *v1 = (const char *)_v1;
-	size_t c = 0;
-	size_t i;
-	(void)ld; /* UNUSED */
-
-	assertion_count(file, line);
-
-	for (i = 0; i < l; ++i) {
-		if (v1[i] == b) {
-			++c;
-		}
-	}
-	if (c == l)
-		return (1);
-
-	failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd);
-	logprintf("   Only %d bytes were correct\n", (int)c);
-	failure_finish(extra);
-	return (0);
-}
-
-/* Verify that the named file exists and is empty. */
-int
-assertion_empty_file(const char *filename, int line, const char *f1)
-{
-	char buff[1024];
-	struct stat st;
-	ssize_t s;
-	FILE *f;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0)
-		return (1);
-
-	failure_start(filename, line, "File should be empty: %s", f1);
-	logprintf("    File size: %d\n", (int)st.st_size);
-	logprintf("    Contents:\n");
-	f = fopen(f1, "rb");
-	if (f == NULL) {
-		logprintf("    Unable to open %s\n", f1);
-	} else {
-		s = ((off_t)sizeof(buff) < st.st_size) ?
-		    (ssize_t)sizeof(buff) : (ssize_t)st.st_size;
-		s = fread(buff, 1, s, f);
-		hexdump(buff, NULL, s, 0);
-		fclose(f);
-	}
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file exists and is not empty. */
-int
-assertion_non_empty_file(const char *filename, int line, const char *f1)
-{
-	struct stat st;
-
-	assertion_count(filename, line);
-
-	if (stat(f1, &st) != 0) {
-		failure_start(filename, line, "Stat failed: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (st.st_size == 0) {
-		failure_start(filename, line, "File empty: %s", f1);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify that two files have the same contents. */
-/* TODO: hexdump the first bytes that actually differ. */
-int
-assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2)
-{
-	char buff1[1024];
-	char buff2[1024];
-	FILE *f1, *f2;
-	int n1, n2;
-
-	assertion_count(filename, line);
-
-	f1 = fopen(fn1, "rb");
-	f2 = fopen(fn2, "rb");
-	if (f1 == NULL || f2 == NULL) {
-		if (f1) fclose(f1);
-		if (f2) fclose(f2);
-		return (0);
-	}
-	for (;;) {
-		n1 = (int)fread(buff1, 1, sizeof(buff1), f1);
-		n2 = (int)fread(buff2, 1, sizeof(buff2), f2);
-		if (n1 != n2)
-			break;
-		if (n1 == 0 && n2 == 0) {
-			fclose(f1);
-			fclose(f2);
-			return (1);
-		}
-		if (memcmp(buff1, buff2, n1) != 0)
-			break;
-	}
-	fclose(f1);
-	fclose(f2);
-	failure_start(filename, line, "Files not identical");
-	logprintf("  file1=\"%s\"\n", fn1);
-	logprintf("  file2=\"%s\"\n", fn2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file does exist. */
-int
-assertion_file_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (!_access(f, 0))
-		return (1);
-#else
-	if (!access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify that the named file doesn't exist. */
-int
-assertion_file_not_exists(const char *filename, int line, const char *f)
-{
-	assertion_count(filename, line);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (_access(f, 0))
-		return (1);
-#else
-	if (access(f, F_OK))
-		return (1);
-#endif
-	failure_start(filename, line, "File should not exist: %s", f);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Compare the contents of a file to a block of memory. */
-int
-assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn)
-{
-	char *contents;
-	FILE *f;
-	int n;
-
-	assertion_count(filename, line);
-
-	f = fopen(fn, "rb");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File should exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	contents = malloc(s * 2);
-	n = (int)fread(contents, 1, s * 2, f);
-	fclose(f);
-	if (n == s && memcmp(buff, contents, s) == 0) {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "File contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0)
-		hexdump(contents, buff, n > 512 ? 512 : n, 0);
-	else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s > 512 ? 512 : s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Check the contents of a text file, being tolerant of line endings. */
-int
-assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn)
-{
-	char *contents;
-	const char *btxt, *ftxt;
-	FILE *f;
-	int n, s;
-
-	assertion_count(filename, line);
-	f = fopen(fn, "r");
-	if (f == NULL) {
-		failure_start(filename, line,
-		    "File doesn't exist: %s", fn);
-		failure_finish(NULL);
-		return (0);
-	}
-	s = (int)strlen(buff);
-	contents = malloc(s * 2 + 128);
-	n = (int)fread(contents, 1, s * 2 + 128 - 1, f);
-	if (n >= 0)
-		contents[n] = '\0';
-	fclose(f);
-	/* Compare texts. */
-	btxt = buff;
-	ftxt = (const char *)contents;
-	while (*btxt != '\0' && *ftxt != '\0') {
-		if (*btxt == *ftxt) {
-			++btxt;
-			++ftxt;
-			continue;
-		}
-		if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') {
-			/* Pass over different new line characters. */
-			++btxt;
-			ftxt += 2;
-			continue;
-		}
-		break;
-	}
-	if (*btxt == '\0' && *ftxt == '\0') {
-		free(contents);
-		return (1);
-	}
-	failure_start(filename, line, "Contents don't match");
-	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0) {
-		hexdump(contents, buff, n, 0);
-		logprintf("  expected\n", fn);
-		hexdump(buff, contents, s, 0);
-	} else {
-		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s, 0);
-	}
-	failure_finish(NULL);
-	free(contents);
-	return (0);
-}
-
-/* Verify that a text file contains the specified lines, regardless of order */
-/* This could be more efficient if we sorted both sets of lines, etc, but
- * since this is used only for testing and only ever deals with a dozen or so
- * lines at a time, this relatively crude approach is just fine. */
-int
-assertion_file_contains_lines_any_order(const char *file, int line,
-    const char *pathname, const char *lines[])
-{
-	char *buff;
-	size_t buff_size;
-	size_t expected_count, actual_count, i, j;
-	char **expected = NULL;
-	char *p, **actual = NULL;
-	char c;
-	int expected_failure = 0, actual_failure = 0, retval = 0;
-
-	assertion_count(file, line);
-
-	buff = slurpfile(&buff_size, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(pathname, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	/* Make a copy of the provided lines and count up the expected
-	 * file size. */
-	for (i = 0; lines[i] != NULL; ++i) {
-	}
-	expected_count = i;
-	if (expected_count) {
-		expected = malloc(sizeof(char *) * expected_count);
-		if (expected == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			goto done;
-		}
-		for (i = 0; lines[i] != NULL; ++i) {
-			expected[i] = strdup(lines[i]);
-		}
-	}
-
-	/* Break the file into lines */
-	actual_count = 0;
-	for (c = '\0', p = buff; p < buff + buff_size; ++p) {
-		if (*p == '\x0d' || *p == '\x0a')
-			*p = '\0';
-		if (c == '\0' && *p != '\0')
-			++actual_count;
-		c = *p;
-	}
-	if (actual_count) {
-		actual = calloc(sizeof(char *), actual_count);
-		if (actual == NULL) {
-			failure_start(pathname, line, "Can't allocate memory");
-			failure_finish(NULL);
-			goto done;
-		}
-		for (j = 0, p = buff; p < buff + buff_size;
-		    p += 1 + strlen(p)) {
-			if (*p != '\0') {
-				actual[j] = p;
-				++j;
-			}
-		}
-	}
-
-	/* Erase matching lines from both lists */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] == NULL)
-			continue;
-		for (j = 0; j < actual_count; ++j) {
-			if (actual[j] == NULL)
-				continue;
-			if (strcmp(expected[i], actual[j]) == 0) {
-				free(expected[i]);
-				expected[i] = NULL;
-				actual[j] = NULL;
-				break;
-			}
-		}
-	}
-
-	/* If there's anything left, it's a failure */
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL)
-			++expected_failure;
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			++actual_failure;
-	}
-	if (expected_failure == 0 && actual_failure == 0) {
-		retval = 1;
-		goto done;
-	}
-	failure_start(file, line, "File doesn't match: %s", pathname);
-	for (i = 0; i < expected_count; ++i) {
-		if (expected[i] != NULL)
-			logprintf("  Expected but not present: %s\n", expected[i]);
-	}
-	for (j = 0; j < actual_count; ++j) {
-		if (actual[j] != NULL)
-			logprintf("  Present but not expected: %s\n", actual[j]);
-	}
-	failure_finish(NULL);
-done:
-	free(actual);
-	free(buff);
-	for (i = 0; i < expected_count; ++i)
-		free(expected[i]);
-	free(expected);
-
-	return (retval);
-}
-
-/* Verify that a text file does not contains the specified strings */
-int
-assertion_file_contains_no_invalid_strings(const char *file, int line,
-    const char *pathname, const char *strings[])
-{
-	char *buff;
-	int i;
-
-	buff = slurpfile(NULL, "%s", pathname);
-	if (buff == NULL) {
-		failure_start(file, line, "Can't read file: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	for (i = 0; strings[i] != NULL; ++i) {
-		if (strstr(buff, strings[i]) != NULL) {
-			failure_start(file, line, "Invalid string in %s: %s", pathname,
-			    strings[i]);
-			failure_finish(NULL);
-			free(buff);
-			return(0);
-		}
-	}
-
-	free(buff);
-	return (0);
-}
-
-/* Test that two paths point to the same file. */
-/* As a side-effect, asserts that both files exist. */
-static int
-is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(path1, &bhfi1);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = my_GetFileInformationByName(path2, &bhfi2);
-	if (r == 0) {
-		failure_start(file, line, "File %s can't be inspected?", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
-		&& bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
-		&& bhfi1.nFileIndexLow == bhfi2.nFileIndexLow);
-#else
-	struct stat st1, st2;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(path1, &st1);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path1);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = lstat(path2, &st2);
-	if (r != 0) {
-		failure_start(file, line, "File should exist: %s", path2);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev);
-#endif
-}
-
-int
-assertion_is_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s are not hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-int
-assertion_is_not_hardlink(const char *file, int line,
-    const char *path1, const char *path2)
-{
-	if (!is_hardlink(file, line, path1, path2))
-		return (1);
-	failure_start(file, line,
-	    "Files %s and %s should not be hardlinked", path1, path2);
-	failure_finish(NULL);
-	return (0);
-}
-
-/* Verify a/b/mtime of 'pathname'. */
-/* If 'recent', verify that it's within last 10 seconds. */
-static int
-assertion_file_time(const char *file, int line,
-    const char *pathname, long t, long nsec, char type, int recent)
-{
-	long long filet, filet_nsec;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define EPOC_TIME	(116444736000000000ULL)
-	FILETIME fxtime, fbirthtime, fatime, fmtime;
-	ULARGE_INTEGER wintm;
-	HANDLE h;
-	fxtime.dwLowDateTime = 0;
-	fxtime.dwHighDateTime = 0;
-
-	assertion_count(file, line);
-	/* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open
-	 * a directory file. If not, CreateFile() will fail when
-	 * the pathname is a directory. */
-	h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
-	    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = GetFileTime(h, &fbirthtime, &fatime, &fmtime);
-	switch (type) {
-	case 'a': fxtime = fatime; break;
-	case 'b': fxtime = fbirthtime; break;
-	case 'm': fxtime = fmtime; break;
-	}
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't GetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	wintm.LowPart = fxtime.dwLowDateTime;
-	wintm.HighPart = fxtime.dwHighDateTime;
-	filet = (wintm.QuadPart - EPOC_TIME) / 10000000;
-	filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100;
-	nsec = (nsec / 100) * 100; /* Round the request */
-#else
-	struct stat st;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	switch (type) {
-	case 'a': filet = st.st_atime; break;
-	case 'm': filet = st.st_mtime; break;
-	case 'b': filet = 0; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-#if defined(__FreeBSD__)
-	switch (type) {
-	case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
-	case 'b': filet = st.st_birthtime;
-		/* FreeBSD filesystems that don't support birthtime
-		 * (e.g., UFS1) always return -1 here. */
-		if (filet == -1) {
-			return (1);
-		}
-		filet_nsec = st.st_birthtimespec.tv_nsec; break;
-	case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
-	default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
-		exit(1);
-	}
-	/* FreeBSD generally only stores to microsecond res, so round. */
-	filet_nsec = (filet_nsec / 1000) * 1000;
-	nsec = (nsec / 1000) * 1000;
-#else
-	filet_nsec = nsec = 0;	/* Generic POSIX only has whole seconds. */
-	if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */
-#if defined(__HAIKU__)
-	if (type == 'a') return (1); /* Haiku doesn't have atime. */
-#endif
-#endif
-#endif
-	if (recent) {
-		/* Check that requested time is up-to-date. */
-		time_t now = time(NULL);
-		if (filet < now - 10 || filet > now + 1) {
-			failure_start(file, line,
-			    "File %s has %ctime %lld, %lld seconds ago\n",
-			    pathname, type, filet, now - filet);
-			failure_finish(NULL);
-			return (0);
-		}
-	} else if (filet != t || filet_nsec != nsec) {
-		failure_start(file, line,
-		    "File %s has %ctime %lld.%09lld, expected %lld.%09lld",
-		    pathname, type, filet, filet_nsec, t, nsec);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-}
-
-/* Verify atime of 'pathname'. */
-int
-assertion_file_atime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'a', 0);
-}
-
-/* Verify atime of 'pathname' is up-to-date. */
-int
-assertion_file_atime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'a', 1);
-}
-
-/* Verify birthtime of 'pathname'. */
-int
-assertion_file_birthtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'b', 0);
-}
-
-/* Verify birthtime of 'pathname' is up-to-date. */
-int
-assertion_file_birthtime_recent(const char *file, int line,
-    const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
-}
-
-/* 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");
-	(void)mode; /* UNUSED */
-	(void)r; /* UNUSED */
-#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);
-}
-
-/* Verify mtime of 'pathname'. */
-int
-assertion_file_mtime(const char *file, int line,
-    const char *pathname, long t, long nsec)
-{
-	return assertion_file_time(file, line, pathname, t, nsec, 'm', 0);
-}
-
-/* Verify mtime of 'pathname' is up-to-date. */
-int
-assertion_file_mtime_recent(const char *file, int line, const char *pathname)
-{
-	return assertion_file_time(file, line, pathname, 0, 0, 'm', 1);
-}
-
-/* Verify number of links to 'pathname'. */
-int
-assertion_file_nlinks(const char *file, int line,
-    const char *pathname, int nlinks)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	assertion_count(file, line);
-	r = my_GetFileInformationByName(pathname, &bhfi);
-	if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, bhfi.nNumberOfLinks, nlinks);
-	failure_finish(NULL);
-	return (0);
-#else
-	struct stat st;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r == 0 && (int)st.st_nlink == nlinks)
-		return (1);
-	failure_start(file, line, "File %s has %d links, expected %d",
-	    pathname, st.st_nlink, nlinks);
-	failure_finish(NULL);
-	return (0);
-#endif
-}
-
-/* Verify size of 'pathname'. */
-int
-assertion_file_size(const char *file, int line, const char *pathname, long size)
-{
-	int64_t filesize;
-	int r;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	{
-		BY_HANDLE_FILE_INFORMATION bhfi;
-		r = !my_GetFileInformationByName(pathname, &bhfi);
-		filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow;
-	}
-#else
-	{
-		struct stat st;
-		r = lstat(pathname, &st);
-		filesize = st.st_size;
-	}
-#endif
-	if (r == 0 && filesize == size)
-			return (1);
-	failure_start(file, line, "File %s has size %ld, expected %ld",
-	    pathname, (long)filesize, (long)size);
-	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)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line, "Dir should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISDIR(st.st_mode)) {
-		failure_start(file, line, "%s is not a dir", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "Dir %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Verify that 'pathname' is a regular file.  If 'mode' is >= 0,
- * verify that too. */
-int
-assertion_is_reg(const char *file, int line, const char *pathname, int mode)
-{
-	struct stat st;
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-#endif
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0 || !S_ISREG(st.st_mode)) {
-		failure_start(file, line, "File should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#if !defined(_WIN32) || defined(__CYGWIN__)
-	/* Windows doesn't handle permissions the same way as POSIX,
-	 * so just ignore the mode tests. */
-	/* TODO: Can we do better here? */
-	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
-		failure_start(file, line, "File %s has wrong mode", pathname);
-		logprintf("  Expected: 0%3o\n", mode);
-		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
-		failure_finish(NULL);
-		return (0);
-	}
-#endif
-	return (1);
-}
-
-/* Check whether 'pathname' is a symbolic link.  If 'contents' is
- * non-NULL, verify that the symlink has those contents. */
-static int
-is_symlink(const char *file, int line,
-    const char *pathname, const char *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)pathname; /* UNUSED */
-	(void)contents; /* UNUSED */
-	assertion_count(file, line);
-	/* Windows sort-of has real symlinks, but they're only usable
-	 * by privileged users and are crippled even then, so there's
-	 * really not much point in bothering with this. */
-	return (0);
-#else
-	char buff[301];
-	struct stat st;
-	ssize_t linklen;
-	int r;
-
-	assertion_count(file, line);
-	r = lstat(pathname, &st);
-	if (r != 0) {
-		failure_start(file, line,
-		    "Symlink should exist: %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (!S_ISLNK(st.st_mode))
-		return (0);
-	if (contents == NULL)
-		return (1);
-	linklen = readlink(pathname, buff, sizeof(buff) - 1);
-	if (linklen < 0) {
-		failure_start(file, line, "Can't read symlink %s", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	buff[linklen] = '\0';
-	if (strcmp(buff, contents) != 0)
-		return (0);
-	return (1);
-#endif
-}
-
-/* Assert that path is a symlink that (optionally) contains contents. */
-int
-assertion_is_symlink(const char *file, int line,
-    const char *path, const char *contents)
-{
-	if (is_symlink(file, line, path, contents))
-		return (1);
-	if (contents)
-		failure_start(file, line, "File %s is not a symlink to %s",
-		    path, contents);
-	else
-		failure_start(file, line, "File %s is not a symlink", path);
-	failure_finish(NULL);
-	return (0);
-}
-
-
-/* Create a directory and report any errors. */
-int
-assertion_make_dir(const char *file, int line, const char *dirname, int mode)
-{
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	(void)mode; /* UNUSED */
-	if (0 == _mkdir(dirname))
-		return (1);
-#else
-	if (0 == mkdir(dirname, mode)) {
-		if (0 == chmod(dirname, mode)) {
-			assertion_file_mode(file, line, dirname, mode);
-			return (1);
-		}
-	}
-#endif
-	failure_start(file, line, "Could not create directory %s", dirname);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a file with the specified contents and report any failures. */
-int
-assertion_make_file(const char *file, int line,
-    const char *path, int mode, int csize, const void *contents)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	/* TODO: Rework this to set file mode as well. */
-	FILE *f;
-	(void)mode; /* UNUSED */
-	assertion_count(file, line);
-	f = fopen(path, "wb");
-	if (f == NULL) {
-		failure_start(file, line, "Could not create file %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (contents != NULL) {
-		size_t wsize;
-
-		if (csize < 0)
-			wsize = strlen(contents);
-		else
-			wsize = (size_t)csize;
-		if (wsize != fwrite(contents, 1, wsize, f)) {
-			fclose(f);
-			failure_start(file, line,
-			    "Could not write file %s", path);
-			failure_finish(NULL);
-			return (0);
-		}
-	}
-	fclose(f);
-	return (1);
-#else
-	int fd;
-	assertion_count(file, line);
-	fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644);
-	if (fd < 0) {
-		failure_start(file, line, "Could not create %s", path);
-		failure_finish(NULL);
-		return (0);
-	}
-	if (0 != chmod(path, mode)) {
-		failure_start(file, line, "Could not chmod %s", path);
-		failure_finish(NULL);
-		close(fd);
-		return (0);
-	}
-	if (contents != NULL) {
-		ssize_t wsize;
-
-		if (csize < 0)
-			wsize = (ssize_t)strlen(contents);
-		else
-			wsize = (ssize_t)csize;
-		if (wsize != write(fd, contents, wsize)) {
-			close(fd);
-			failure_start(file, line,
-			    "Could not write to %s", path);
-			failure_finish(NULL);
-			close(fd);
-			return (0);
-		}
-	}
-	close(fd);
-	assertion_file_mode(file, line, path, mode);
-	return (1);
-#endif
-}
-
-/* Create a hardlink and report any failures. */
-int
-assertion_make_hardlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-	int succeeded;
-
-	assertion_count(file, line);
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	succeeded = my_CreateHardLinkA(newpath, linkto);
-#elif HAVE_LINK
-	succeeded = !link(linkto, newpath);
-#else
-	succeeded = 0;
-#endif
-	if (succeeded)
-		return (1);
-	failure_start(file, line, "Could not create hardlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Create a symlink and report any failures. */
-int
-assertion_make_symlink(const char *file, int line,
-    const char *newpath, const char *linkto)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	int targetIsDir = 0;  /* TODO: Fix this */
-	assertion_count(file, line);
-	if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
-		return (1);
-#elif HAVE_SYMLINK
-	assertion_count(file, line);
-	if (0 == symlink(linkto, newpath))
-		return (1);
-#endif
-	failure_start(file, line, "Could not create symlink");
-	logprintf("   New link: %s\n", newpath);
-	logprintf("   Old name: %s\n", linkto);
-	failure_finish(NULL);
-	return(0);
-}
-
-/* Set umask, report failures. */
-int
-assertion_umask(const char *file, int line, int mask)
-{
-	assertion_count(file, line);
-	(void)file; /* UNUSED */
-	(void)line; /* UNUSED */
-	umask(mask);
-	return (1);
-}
-
-/* Set times, report failures. */
-int
-assertion_utimes(const char *file, int line,
-    const char *pathname, long at, long at_nsec, long mt, long mt_nsec)
-{
-	int r;
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
-	 + (((nsec)/1000)*10))
-	HANDLE h;
-	ULARGE_INTEGER wintm;
-	FILETIME fatime, fmtime;
-	FILETIME *pat, *pmt;
-
-	assertion_count(file, line);
-	h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE,
-		    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-		    FILE_FLAG_BACKUP_SEMANTICS, NULL);
-	if (h == INVALID_HANDLE_VALUE) {
-		failure_start(file, line, "Can't access %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (at > 0 || at_nsec > 0) {
-		wintm.QuadPart = WINTIME(at, at_nsec);
-		fatime.dwLowDateTime = wintm.LowPart;
-		fatime.dwHighDateTime = wintm.HighPart;
-		pat = &fatime;
-	} else
-		pat = NULL;
-	if (mt > 0 || mt_nsec > 0) {
-		wintm.QuadPart = WINTIME(mt, mt_nsec);
-		fmtime.dwLowDateTime = wintm.LowPart;
-		fmtime.dwHighDateTime = wintm.HighPart;
-		pmt = &fmtime;
-	} else
-		pmt = NULL;
-	if (pat != NULL || pmt != NULL)
-		r = SetFileTime(h, NULL, pat, pmt);
-	else
-		r = 1;
-	CloseHandle(h);
-	if (r == 0) {
-		failure_start(file, line, "Can't SetFileTime %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#else /* defined(_WIN32) && !defined(__CYGWIN__) */
-	struct stat st;
-	struct timeval times[2];
-
-#if !defined(__FreeBSD__)
-	mt_nsec = at_nsec = 0;	/* Generic POSIX only has whole seconds. */
-#endif
-	if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0)
-		return (1);
-
-	r = lstat(pathname, &st);
-	if (r < 0) {
-		failure_start(file, line, "Can't stat %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-
-	if (mt == 0 && mt_nsec == 0) {
-		mt = st.st_mtime;
-#if defined(__FreeBSD__)
-		mt_nsec = st.st_mtimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		mt_nsec = (mt_nsec / 1000) * 1000;
-#endif
-	}
-	if (at == 0 && at_nsec == 0) {
-		at = st.st_atime;
-#if defined(__FreeBSD__)
-		at_nsec = st.st_atimespec.tv_nsec;
-		/* FreeBSD generally only stores to microsecond res, so round. */
-		at_nsec = (at_nsec / 1000) * 1000;
-#endif
-	}
-
-	times[1].tv_sec = mt;
-	times[1].tv_usec = mt_nsec / 1000;
-
-	times[0].tv_sec = at;
-	times[0].tv_usec = at_nsec / 1000;
-
-#ifdef HAVE_LUTIMES
-	r = lutimes(pathname, times);
-#else
-	r = utimes(pathname, times);
-#endif
-	if (r < 0) {
-		failure_start(file, line, "Can't utimes %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	return (1);
-#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
-}
-
-/* Set nodump, report failures. */
-int
-assertion_nodump(const char *file, int line, const char *pathname)
-{
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-	int r;
-
-	assertion_count(file, line);
-	r = chflags(pathname, UF_NODUMP);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-	int fd, r, flags;
-
-	assertion_count(file, line);
-	fd = open(pathname, O_RDONLY | O_NONBLOCK);
-	if (fd < 0) {
-		failure_start(file, line, "Can't open %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't get flags %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0) {
-		failure_start(file, line, "Can't set nodump %s\n", pathname);
-		failure_finish(NULL);
-		return (0);
-	}
-	close(fd);
-#else
-	(void)pathname; /* UNUSED */
-	assertion_count(file, line);
-#endif
-	return (1);
-}
-
-/*
- *
- *  UTILITIES for use by tests.
- *
- */
-
-/*
- * Check whether platform supports symlinks.  This is intended
- * for tests to use in deciding whether to bother testing symlink
- * support; if the platform doesn't support symlinks, there's no point
- * in checking whether the program being tested can create them.
- *
- * Note that the first time this test is called, we actually go out to
- * disk to create and verify a symlink.  This is necessary because
- * symlink support is actually a property of a particular filesystem
- * and can thus vary between directories on a single system.  After
- * the first call, this returns the cached result from memory, so it's
- * safe to call it as often as you wish.
- */
-int
-canSymlink(void)
-{
-	/* Remember the test result */
-	static int value = 0, tested = 0;
-	if (tested)
-		return (value);
-
-	++tested;
-	assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a");
-	/* Note: Cygwin has its own symlink() emulation that does not
-	 * use the Win32 CreateSymbolicLink() function. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0)
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0");
-#elif HAVE_SYMLINK
-	value = (0 == symlink("canSymlink.0", "canSymlink.1"))
-	    && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0");
-#endif
-	return (value);
-}
-
-/* Platform-dependent options for hiding the output of a subcommand. */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */
-#else
-static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */
-#endif
-/*
- * Can this platform run the bzip2 program?
- */
-int
-canBzip2(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("bzip2 -d -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the grzip program?
- */
-int
-canGrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("grzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the gzip program?
- */
-int
-canGzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("gzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lrzip program?
- */
-int
-canRunCommand(const char *cmd)
-{
-  static int tested = 0, value = 0;
-  if (!tested) {
-    tested = 1;
-    if (systemf("%s %s", cmd, redirectArgs) == 0)
-      value = 1;
-  }
-  return (value);
-}
-
-int
-canLrzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lrzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lz4 program?
- */
-int
-canLz4(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lz4 -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzip program?
- */
-int
-canLzip(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzip -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzma program?
- */
-int
-canLzma(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzma -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the lzop program?
- */
-int
-canLzop(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("lzop -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this platform run the xz program?
- */
-int
-canXz(void)
-{
-	static int tested = 0, value = 0;
-	if (!tested) {
-		tested = 1;
-		if (systemf("xz -V %s", redirectArgs) == 0)
-			value = 1;
-	}
-	return (value);
-}
-
-/*
- * Can this filesystem handle nodump flags.
- */
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	struct stat sb;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	if (chflags(path, UF_NODUMP) < 0)
-		return (0);
-	if (stat(path, &sb) < 0)
-		return (0);
-	if (sb.st_flags & UF_NODUMP)
-		return (1);
-	return (0);
-}
-
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-
-int
-canNodump(void)
-{
-	const char *path = "cannodumptest";
-	int fd, r, flags;
-
-	assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	fd = open(path, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
-	if (r < 0)
-		return (0);
-	close(fd);
-	if (flags & EXT2_NODUMP_FL)
-		return (1);
-	return (0);
-}
-
-#else
-
-int
-canNodump()
-{
-	return (0);
-}
-
-#endif
-
-/*
- * Sleep as needed; useful for verifying disk timestamp changes by
- * ensuring that the wall-clock time has actually changed before we
- * go back to re-read something from disk.
- */
-void
-sleepUntilAfter(time_t t)
-{
-	while (t >= time(NULL))
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		Sleep(500);
-#else
-		sleep(1);
-#endif
-}
-
-/*
- * Call standard system() call, but build up the command line using
- * sprintf() conventions.
- */
-int
-systemf(const char *fmt, ...)
-{
-	char buff[8192];
-	va_list ap;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(buff, fmt, ap);
-	if (verbosity > VERBOSITY_FULL)
-		logprintf("Cmd: %s\n", buff);
-	r = system(buff);
-	va_end(ap);
-	return (r);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-char *
-slurpfile(size_t * sizep, const char *fmt, ...)
-{
-	char filename[8192];
-	struct stat st;
-	va_list ap;
-	char *p;
-	ssize_t bytes_read;
-	FILE *f;
-	int r;
-
-	va_start(ap, fmt);
-	vsprintf(filename, fmt, ap);
-	va_end(ap);
-
-	f = fopen(filename, "rb");
-	if (f == NULL) {
-		/* Note: No error; non-existent file is okay here. */
-		return (NULL);
-	}
-	r = fstat(fileno(f), &st);
-	if (r != 0) {
-		logprintf("Can't stat file %s\n", filename);
-		fclose(f);
-		return (NULL);
-	}
-	p = malloc((size_t)st.st_size + 1);
-	if (p == NULL) {
-		logprintf("Can't allocate %ld bytes of memory to read file %s\n",
-		    (long int)st.st_size, filename);
-		fclose(f);
-		return (NULL);
-	}
-	bytes_read = fread(p, 1, (size_t)st.st_size, f);
-	if (bytes_read < st.st_size) {
-		logprintf("Can't read file %s\n", filename);
-		fclose(f);
-		free(p);
-		return (NULL);
-	}
-	p[st.st_size] = '\0';
-	if (sizep != NULL)
-		*sizep = (size_t)st.st_size;
-	fclose(f);
-	return (p);
-}
-
-/*
- * Slurp a file into memory for ease of comparison and testing.
- * Returns size of file in 'sizep' if non-NULL, null-terminates
- * data in memory for ease of use.
- */
-void
-dumpfile(const char *filename, void *data, size_t len)
-{
-	ssize_t bytes_written;
-	FILE *f;
-
-	f = fopen(filename, "wb");
-	if (f == NULL) {
-		logprintf("Can't open file %s for writing\n", filename);
-		return;
-	}
-	bytes_written = fwrite(data, 1, len, f);
-	if (bytes_written < (ssize_t)len)
-		logprintf("Can't write file %s\n", filename);
-	fclose(f);
-}
-
-/* Read a uuencoded file from the reference directory, decode, and
- * write the result into the current directory. */
-#define VALID_UUDECODE(c) (c >= 32 && c <= 96)
-#define	UUDECODE(c) (((c) - 0x20) & 0x3f)
-void
-extract_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-
-	sprintf(buff, "%s/%s.uu", refdir, name);
-	in = fopen(buff, "r");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Read up to and including the 'begin' line. */
-	for (;;) {
-		if (fgets(buff, sizeof(buff), in) == NULL) {
-			/* TODO: This is a failure. */
-			goto done;
-		}
-		if (memcmp(buff, "begin ", 6) == 0)
-			break;
-	}
-	/* Now, decode the rest and write it. */
-	out = fopen(name, "wb");
-	while (fgets(buff, sizeof(buff), in) != NULL) {
-		char *p = buff;
-		int bytes;
-
-		if (memcmp(buff, "end", 3) == 0)
-			break;
-
-		bytes = UUDECODE(*p++);
-		while (bytes > 0) {
-			int n = 0;
-			/* Write out 1-3 bytes from that. */
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				assert(VALID_UUDECODE(p[1]));
-				n = UUDECODE(*p++) << 18;
-				n |= UUDECODE(*p++) << 12;
-				fputc(n >> 16, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++) << 6;
-				fputc((n >> 8) & 0xFF, out);
-				--bytes;
-			}
-			if (bytes > 0) {
-				assert(VALID_UUDECODE(p[0]));
-				n |= UUDECODE(*p++);
-				fputc(n & 0xFF, out);
-				--bytes;
-			}
-		}
-	}
-	fclose(out);
-done:
-	fclose(in);
-}
-
-void
-copy_reference_file(const char *name)
-{
-	char buff[1024];
-	FILE *in, *out;
-	size_t rbytes;
-
-	sprintf(buff, "%s/%s", refdir, name);
-	in = fopen(buff, "rb");
-	failure("Couldn't open reference file %s", buff);
-	assert(in != NULL);
-	if (in == NULL)
-		return;
-	/* Now, decode the rest and write it. */
-	/* Not a lot of error checking here; the input better be right. */
-	out = fopen(name, "wb");
-	while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) {
-		if (fwrite(buff, 1, rbytes, out) != rbytes) {
-			logprintf("Error: fwrite\n");
-			break;
-		}
-	}
-	fclose(out);
-	fclose(in);
-}
-
-int
-is_LargeInode(const char *file)
-{
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	BY_HANDLE_FILE_INFORMATION bhfi;
-	int r;
-
-	r = my_GetFileInformationByName(file, &bhfi);
-	if (r != 0)
-		return (0);
-	return (bhfi.nFileIndexHigh & 0x0000FFFFUL);
-#else
-	struct stat st;
-	int64_t ino;
-
-	if (stat(file, &st) < 0)
-		return (0);
-	ino = (int64_t)st.st_ino;
-	return (ino > 0xffffffff);
-#endif
-}
-
-void
-extract_reference_files(const char **names)
-{
-	while (names && *names)
-		extract_reference_file(*names++);
-}
-
-/*
- *
- * TEST management
- *
- */
-
-/*
- * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has
- * a line like
- *      DEFINE_TEST(test_function)
- * for each test.
- */
-
-/* Use "list.h" to declare all of the test functions. */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(name) void name(void);
-#include "list.h"
-
-/* Use "list.h" to create a list of all tests (functions and names). */
-#undef DEFINE_TEST
-#define	DEFINE_TEST(n) { n, #n, 0 },
-struct test_list_t tests[] = {
-	#include "list.h"
-};
-
-/*
- * Summarize repeated failures in the just-completed test.
- */
-static void
-test_summarize(int failed, int skips_num)
-{
-	unsigned int i;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY:
-		printf(failed ? "E" : ".");
-		fflush(stdout);
-		break;
-	case VERBOSITY_PASSFAIL:
-		printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n");
-		break;
-	}
-
-	log_console = (verbosity == VERBOSITY_LIGHT_REPORT);
-
-	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
-		if (failed_lines[i].count > 1 && !failed_lines[i].skip)
-			logprintf("%s:%d: Summary: Failed %d times\n",
-			    failed_filename, i, failed_lines[i].count);
-	}
-	/* Clear the failure history for the next file. */
-	failed_filename = NULL;
-	memset(failed_lines, 0, sizeof(failed_lines));
-}
-
-/*
- * Actually run a single test, with appropriate setup and cleanup.
- */
-static int
-test_run(int i, const char *tmpdir)
-{
-	char workdir[1024];
-	char logfilename[64];
-	int failures_before = failures;
-	int skips_before = skips;
-	int oldumask;
-
-	switch (verbosity) {
-	case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */
-		break;
-	case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */
-		printf("%3d: %-64s", i, tests[i].name);
-		fflush(stdout);
-		break;
-	default: /* Title of test, details will follow */
-		printf("%3d: %s\n", i, tests[i].name);
-	}
-
-	/* Chdir to the top-level work directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to top work dir %s\n", tmpdir);
-		exit(1);
-	}
-	/* Create a log file for this test. */
-	sprintf(logfilename, "%s.log", tests[i].name);
-	logfile = fopen(logfilename, "w");
-	fprintf(logfile, "%s\n\n", tests[i].name);
-	/* Chdir() to a work dir for this specific test. */
-	snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name);
-	testworkdir = workdir;
-	if (!assertMakeDir(testworkdir, 0755)
-	    || !assertChdir(testworkdir)) {
-		fprintf(stderr,
-		    "ERROR: Can't chdir to work dir %s\n", testworkdir);
-		exit(1);
-	}
-	/* Explicitly reset the locale before each test. */
-	setlocale(LC_ALL, "C");
-	/* Record the umask before we run the test. */
-	umask(oldumask = umask(0));
-	/*
-	 * Run the actual test.
-	 */
-	(*tests[i].func)();
-	/*
-	 * Clean up and report afterwards.
-	 */
-	testworkdir = NULL;
-	/* Restore umask */
-	umask(oldumask);
-	/* Reset locale. */
-	setlocale(LC_ALL, "C");
-	/* Reset directory. */
-	if (!assertChdir(tmpdir)) {
-		fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n",
-		    tmpdir);
-		exit(1);
-	}
-	/* Report per-test summaries. */
-	tests[i].failures = failures - failures_before;
-	test_summarize(tests[i].failures, skips - skips_before);
-	/* Close the per-test log file. */
-	fclose(logfile);
-	logfile = NULL;
-	/* If there were no failures, we can remove the work dir and logfile. */
-	if (tests[i].failures == 0) {
-		if (!keep_temp_files && assertChdir(tmpdir)) {
-#if defined(_WIN32) && !defined(__CYGWIN__)
-			/* Make sure not to leave empty directories.
-			 * Sometimes a processing of closing files used by tests
-			 * is not done, then rmdir will be failed and it will
-			 * leave a empty test directory. So we should wait a few
-			 * seconds and retry rmdir. */
-			int r, t;
-			for (t = 0; t < 10; t++) {
-				if (t > 0)
-					Sleep(1000);
-				r = systemf("rmdir /S /Q %s", tests[i].name);
-				if (r == 0)
-					break;
-			}
-			systemf("del %s", logfilename);
-#else
-			systemf("rm -rf %s", tests[i].name);
-			systemf("rm %s", logfilename);
-#endif
-		}
-	}
-	/* Return appropriate status. */
-	return (tests[i].failures);
-}
-
-/*
- *
- *
- * MAIN and support routines.
- *
- *
- */
-
-static void
-usage(const char *program)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int i;
-
-	printf("Usage: %s [options] <test> <test> ...\n", program);
-	printf("Default is to run all tests.\n");
-	printf("Otherwise, specify the numbers of the tests you wish to run.\n");
-	printf("Options:\n");
-	printf("  -d  Dump core after any failure, for debugging.\n");
-	printf("  -k  Keep all temp files.\n");
-	printf("      Default: temp files for successful tests deleted.\n");
-#ifdef PROGRAM
-	printf("  -p <path>  Path to executable to be tested.\n");
-	printf("      Default: path taken from " ENVBASE " environment variable.\n");
-#endif
-	printf("  -q  Quiet.\n");
-	printf("  -r <dir>   Path to dir containing reference files.\n");
-	printf("      Default: Current directory.\n");
-	printf("  -u  Keep running specifies tests until one fails.\n");
-	printf("  -v  Verbose.\n");
-	printf("Available tests:\n");
-	for (i = 0; i < limit; i++)
-		printf("  %d: %s\n", i, tests[i].name);
-	exit(1);
-}
-
-static char *
-get_refdir(const char *d)
-{
-	size_t tried_size, buff_size;
-	char *buff, *tried, *pwd = NULL, *p = NULL;
-
-#ifdef PATH_MAX
-	buff_size = PATH_MAX;
-#else
-	buff_size = 8192;
-#endif
-	buff = calloc(buff_size, 1);
-	if (buff == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* Allocate a buffer to hold the various directories we checked. */
-	tried_size = buff_size * 2;
-	tried = calloc(tried_size, 1);
-	if (tried == NULL) {
-		fprintf(stderr, "Unable to allocate memory\n");
-		exit(1);
-	}
-
-	/* If a dir was specified, try that */
-	if (d != NULL) {
-		pwd = NULL;
-		snprintf(buff, buff_size, "%s", d);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-		goto failure;
-	}
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-	/* Look for a known file. */
-	snprintf(buff, buff_size, "%s", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-	snprintf(buff, buff_size, "%s/test", pwd);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(LIBRARY)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY);
-#else
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM);
-#endif
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-#if defined(PROGRAM_ALIAS)
-	snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS);
-	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-	if (p != NULL) goto success;
-	strncat(tried, buff, tried_size - strlen(tried) - 1);
-	strncat(tried, "\n", tried_size - strlen(tried) - 1);
-#endif
-
-	if (memcmp(pwd, "/usr/obj", 8) == 0) {
-		snprintf(buff, buff_size, "%s", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-
-		snprintf(buff, buff_size, "%s/test", pwd + 8);
-		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
-		if (p != NULL) goto success;
-		strncat(tried, buff, tried_size - strlen(tried) - 1);
-		strncat(tried, "\n", tried_size - strlen(tried) - 1);
-	}
-
-failure:
-	printf("Unable to locate known reference file %s\n", KNOWNREF);
-	printf("  Checked following directories:\n%s\n", tried);
-	printf("Use -r option to specify full path to reference directory\n");
-#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
-	DebugBreak();
-#endif
-	exit(1);
-
-success:
-	free(p);
-	free(pwd);
-	free(tried);
-
-	/* Copy result into a fresh buffer to reduce memory usage. */
-	p = strdup(buff);
-	free(buff);
-	return p;
-}
-
-int
-main(int argc, char **argv)
-{
-	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int test_set[sizeof(tests) / sizeof(tests[0])];
-	int i = 0, j = 0, tests_run = 0, tests_failed = 0, option;
-	time_t now;
-	char *refdir_alloc = NULL;
-	const char *progname;
-	char **saved_argv;
-	const char *tmp, *option_arg, *p;
-	char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL;
-	char tmpdir_timestamp[256];
-
-	(void)argc; /* UNUSED */
-
-	/* Get the current dir. */
-#ifdef PATH_MAX
-	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
-#else
-	pwd = getcwd(NULL, 0);
-#endif
-	while (pwd[strlen(pwd) - 1] == '\n')
-		pwd[strlen(pwd) - 1] = '\0';
-
-#if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__)
-	/* To stop to run the default invalid parameter handler. */
-	_set_invalid_parameter_handler(invalid_parameter_handler);
-	/* Disable annoying assertion message box. */
-	_CrtSetReportMode(_CRT_ASSERT, 0);
-#endif
-
-	/*
-	 * Name of this program, used to build root of our temp directory
-	 * tree.
-	 */
-	progname = p = argv[0];
-	if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL)
-	{
-		fprintf(stderr, "ERROR: Out of memory.");
-		exit(1);
-	}
-	strcpy(testprogdir, progname);
-	while (*p != '\0') {
-		/* Support \ or / dir separators for Windows compat. */
-		if (*p == '/' || *p == '\\')
-		{
-			progname = p + 1;
-			i = j;
-		}
-		++p;
-		j++;
-	}
-	testprogdir[i] = '\0';
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	if (testprogdir[0] != '/' && testprogdir[0] != '\\' &&
-	    !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') ||
-	       (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) &&
-		testprogdir[1] == ':' &&
-		(testprogdir[2] == '/' || testprogdir[2] == '\\')))
-#else
-	if (testprogdir[0] != '/')
-#endif
-	{
-		/* Fixup path for relative directories. */
-		if ((testprogdir = (char *)realloc(testprogdir,
-			strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		memmove(testprogdir + strlen(pwd) + 1, testprogdir,
-		    strlen(testprogdir) + 1);
-		memcpy(testprogdir, pwd, strlen(pwd));
-		testprogdir[strlen(pwd)] = '/';
-	}
-
-#ifdef PROGRAM
-	/* Get the target program from environment, if available. */
-	testprogfile = getenv(ENVBASE);
-#endif
-
-	if (getenv("TMPDIR") != NULL)
-		tmp = getenv("TMPDIR");
-	else if (getenv("TMP") != NULL)
-		tmp = getenv("TMP");
-	else if (getenv("TEMP") != NULL)
-		tmp = getenv("TEMP");
-	else if (getenv("TEMPDIR") != NULL)
-		tmp = getenv("TEMPDIR");
-	else
-		tmp = "/tmp";
-
-	/* Allow -d to be controlled through the environment. */
-	if (getenv(ENVBASE "_DEBUG") != NULL)
-		dump_on_failure = 1;
-
-	/* Allow -v to be controlled through the environment. */
-	if (getenv("_VERBOSITY_LEVEL") != NULL)
-	{
-		vlevel = getenv("_VERBOSITY_LEVEL");
-		verbosity = atoi(vlevel);
-		if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL)
-		{
-			/* Unsupported verbosity levels are silently ignored */
-			vlevel = NULL;
-			verbosity = VERBOSITY_PASSFAIL;
-		}
-	}
-
-	/* Get the directory holding test files from environment. */
-	refdir = getenv(ENVBASE "_TEST_FILES");
-
-	/*
-	 * Parse options, without using getopt(), which isn't available
-	 * on all platforms.
-	 */
-	++argv; /* Skip program name */
-	while (*argv != NULL) {
-		if (**argv != '-')
-			break;
-		p = *argv++;
-		++p; /* Skip '-' */
-		while (*p != '\0') {
-			option = *p++;
-			option_arg = NULL;
-			/* If 'opt' takes an argument, parse that. */
-			if (option == 'p' || option == 'r') {
-				if (*p != '\0')
-					option_arg = p;
-				else if (*argv == NULL) {
-					fprintf(stderr,
-					    "Option -%c requires argument.\n",
-					    option);
-					usage(progname);
-				} else
-					option_arg = *argv++;
-				p = ""; /* End of this option word. */
-			}
-
-			/* Now, handle the option. */
-			switch (option) {
-			case 'd':
-				dump_on_failure = 1;
-				break;
-			case 'k':
-				keep_temp_files = 1;
-				break;
-			case 'p':
-#ifdef PROGRAM
-				testprogfile = option_arg;
-#else
-				fprintf(stderr, "-p option not permitted\n");
-				usage(progname);
-#endif
-				break;
-			case 'q':
-				if (!vlevel)
-					verbosity--;
-				break;
-			case 'r':
-				refdir = option_arg;
-				break;
-			case 'u':
-				until_failure++;
-				break;
-			case 'v':
-				if (!vlevel)
-					verbosity++;
-				break;
-			default:
-				fprintf(stderr, "Unrecognized option '%c'\n",
-				    option);
-				usage(progname);
-			}
-		}
-	}
-
-	/*
-	 * Sanity-check that our options make sense.
-	 */
-#ifdef PROGRAM
-	if (testprogfile == NULL)
-	{
-		if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 +
-			strlen(PROGRAM) + 1)) == NULL)
-		{
-			fprintf(stderr, "ERROR: Out of memory.");
-			exit(1);
-		}
-		strcpy(tmp2, testprogdir);
-		strcat(tmp2, "/");
-		strcat(tmp2, PROGRAM);
-		testprogfile = tmp2;
-	}
-
-	{
-		char *testprg;
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		/* Command.com sometimes rejects '/' separators. */
-		testprg = strdup(testprogfile);
-		for (i = 0; testprg[i] != '\0'; i++) {
-			if (testprg[i] == '/')
-				testprg[i] = '\\';
-		}
-		testprogfile = testprg;
-#endif
-		/* Quote the name that gets put into shell command lines. */
-		testprg = malloc(strlen(testprogfile) + 3);
-		strcpy(testprg, "\"");
-		strcat(testprg, testprogfile);
-		strcat(testprg, "\"");
-		testprog = testprg;
-	}
-#endif
-
-#if !defined(_WIN32) && defined(SIGPIPE)
-	{   /* Ignore SIGPIPE signals */
-		struct sigaction sa;
-		sa.sa_handler = SIG_IGN;
-		sigemptyset(&sa.sa_mask);
-		sa.sa_flags = 0;
-		sigaction(SIGPIPE, &sa, NULL);
-	}
-#endif
-
-	/*
-	 * Create a temp directory for the following tests.
-	 * Include the time the tests started as part of the name,
-	 * to make it easier to track the results of multiple tests.
-	 */
-	now = time(NULL);
-	for (i = 0; ; i++) {
-		strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp),
-		    "%Y-%m-%dT%H.%M.%S",
-		    localtime(&now));
-		snprintf(tmpdir, sizeof(tmpdir), "%s/%s.%s-%03d", tmp,
-		    progname, tmpdir_timestamp, i);
-		if (assertMakeDir(tmpdir,0755))
-			break;
-		if (i >= 999) {
-			fprintf(stderr,
-			    "ERROR: Unable to create temp directory %s\n",
-			    tmpdir);
-			exit(1);
-		}
-	}
-
-	/*
-	 * If the user didn't specify a directory for locating
-	 * reference files, try to find the reference files in
-	 * the "usual places."
-	 */
-	refdir = refdir_alloc = get_refdir(refdir);
-
-	/*
-	 * Banner with basic information.
-	 */
-	printf("\n");
-	printf("If tests fail or crash, details will be in:\n");
-	printf("   %s\n", tmpdir);
-	printf("\n");
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("Reference files will be read from: %s\n", refdir);
-#ifdef PROGRAM
-		printf("Running tests on: %s\n", testprog);
-#endif
-		printf("Exercising: ");
-		fflush(stdout);
-		printf("%s\n", EXTRA_VERSION);
-	} else {
-		printf("Running ");
-		fflush(stdout);
-	}
-
-	/*
-	 * Run some or all of the individual tests.
-	 */
-	saved_argv = argv;
-	do {
-		argv = saved_argv;
-		do {
-			int test_num;
-
-			test_num = get_test_set(test_set, limit, *argv, tests);
-			if (test_num < 0) {
-				printf("*** INVALID Test %s\n", *argv);
-				free(refdir_alloc);
-				free(testprogdir);
-				usage(progname);
-				return (1);
-			}
-			for (i = 0; i < test_num; i++) {
-				tests_run++;
-				if (test_run(test_set[i], tmpdir)) {
-					tests_failed++;
-					if (until_failure)
-						goto finish;
-				}
-			}
-			if (*argv != NULL)
-				argv++;
-		} while (*argv != NULL);
-	} while (until_failure);
-
-finish:
-	/* Must be freed after all tests run */
-	free(tmp2);
-	free(testprogdir);
-	free(pwd);
-
-	/*
-	 * Report summary statistics.
-	 */
-	if (verbosity > VERBOSITY_SUMMARY_ONLY) {
-		printf("\n");
-		printf("Totals:\n");
-		printf("  Tests run:         %8d\n", tests_run);
-		printf("  Tests failed:      %8d\n", tests_failed);
-		printf("  Assertions checked:%8d\n", assertions);
-		printf("  Assertions failed: %8d\n", failures);
-		printf("  Skips reported:    %8d\n", skips);
-	}
-	if (failures) {
-		printf("\n");
-		printf("Failing tests:\n");
-		for (i = 0; i < limit; ++i) {
-			if (tests[i].failures)
-				printf("  %d: %s (%d failures)\n", i,
-				    tests[i].name, tests[i].failures);
-		}
-		printf("\n");
-		printf("Details for failing tests: %s\n", tmpdir);
-		printf("\n");
-	} else {
-		if (verbosity == VERBOSITY_SUMMARY_ONLY)
-			printf("\n");
-		printf("%d tests passed, no failures\n", tests_run);
-	}
-
-	free(refdir_alloc);
-
-	/* If the final tmpdir is empty, we can remove it. */
-	/* This should be the usual case when all tests succeed. */
-	assertChdir("..");
-	rmdir(tmpdir);
-
-	return (tests_failed ? 1 : 0);
-}
diff --git a/tar/test/test.h b/tar/test/test.h
index 3a23a05..1e1bee8 100644
--- a/tar/test/test.h
+++ b/tar/test/test.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2006 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,333 +22,19 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/tar/test/test.h,v 1.4 2008/08/21 07:04:57 kientzle Exp $
+ * $FreeBSD$
  */
 
 /* Every test program should #include "test.h" as the first thing. */
 
-/*
- * The goal of this file (and the matching test.c) is to
- * simplify the very repetitive test-*.c test programs.
- */
-#if defined(HAVE_CONFIG_H)
-/* Most POSIX platforms use the 'configure' script to build config.h */
-#include "config.h"
-#elif defined(__FreeBSD__)
-/* Building as part of FreeBSD system requires a pre-built config.h. */
-#include "config_freebsd.h"
-#elif defined(_WIN32) && !defined(__CYGWIN__)
-/* Win32 can't run the 'configure' script. */
-#include "config_windows.h"
-#else
-/* Warn if the library hasn't been (automatically or manually) configured. */
-#error Oops: No config.h and no pre-built configuration in test.h.
-#endif
+#define KNOWNREF	"test_patterns_2.tar.uu"
+#define ENVBASE "BSDTAR"  /* Prefix for environment variables. */
+#define	PROGRAM "bsdtar"  /* Name of program being tested. */
+#define PROGRAM_ALIAS "tar" /* Generic alias for program */
+#undef	LIBRARY		  /* Not testing a library. */
+#undef	EXTRA_DUMP	  /* How to dump extra data */
+#undef	EXTRA_ERRNO	  /* How to dump errno */
+/* How to generate extra version info. */
+#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
 
-#include <sys/types.h>  /* Windows requires this before sys/stat.h */
-#include <sys/stat.h>
-
-#if HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-#ifdef HAVE_DIRECT_H
-#include <direct.h>
-#define dirent direct
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_IO_H
-#include <io.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <wchar.h>
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-/*
- * System-specific tweaks.  We really want to minimize these
- * as much as possible, since they make it harder to understand
- * the mainline code.
- */
-
-/* Windows (including Visual Studio and MinGW but not Cygwin) */
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#if !defined(__BORLANDC__)
-#undef chdir
-#define chdir _chdir
-#define strdup _strdup
-#endif
-#endif
-
-/* Visual Studio */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#define snprintf	sprintf_s
-#endif
-
-#if defined(__BORLANDC__)
-#pragma warn -8068	/* Constant out of range in comparison. */
-#endif
-
-/* Haiku OS and QNX */
-#if defined(__HAIKU__) || defined(__QNXNTO__)
-/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
-#include <stdint.h>
-#endif
-
-/* Get a real definition for __FBSDID if we can */
-#if HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-
-/* If not, define it so as to avoid dangling semicolons. */
-#ifndef __FBSDID
-#define	__FBSDID(a)     struct _undefined_hack
-#endif
-
-#ifndef O_BINARY
-#define	O_BINARY 0
-#endif
-
-/*
- * Redefine DEFINE_TEST for use in defining the test functions.
- */
-#undef DEFINE_TEST
-#define DEFINE_TEST(name) void name(void); void name(void)
-
-/* An implementation of the standard assert() macro */
-#define assert(e)   assertion_assert(__FILE__, __LINE__, (e), #e, NULL)
-/* chdir() and error if it fails */
-#define assertChdir(path)  \
-  assertion_chdir(__FILE__, __LINE__, path)
-/* Assert two integers are the same.  Reports value of each one if not. */
-#define assertEqualInt(v1,v2) \
-  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* Assert two strings are the same.  Reports value of each one if not. */
-#define assertEqualString(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
-#define assertEqualUTF8String(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
-/* As above, but v1 and v2 are wchar_t * */
-#define assertEqualWString(v1,v2)   \
-  assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
-/* As above, but raw blocks of bytes. */
-#define assertEqualMem(v1, v2, l)	\
-  assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
-/* Assert that memory is full of a specified byte */
-#define assertMemoryFilledWith(v1, l, b)					\
-  assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL)
-/* Assert two files are the same. */
-#define assertEqualFile(f1, f2)	\
-  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
-/* Assert that a file is empty. */
-#define assertEmptyFile(pathname)	\
-  assertion_empty_file(__FILE__, __LINE__, (pathname))
-/* Assert that a file is not empty. */
-#define assertNonEmptyFile(pathname)		\
-  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
-#define assertFileAtime(pathname, sec, nsec)	\
-  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileAtimeRecent(pathname)	\
-  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
-#define assertFileBirthtime(pathname, sec, nsec)	\
-  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileBirthtimeRecent(pathname) \
-  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
-/* Assert that a file exists; supports printf-style arguments. */
-#define assertFileExists(pathname) \
-  assertion_file_exists(__FILE__, __LINE__, pathname)
-/* Assert that a file exists. */
-#define assertFileNotExists(pathname) \
-  assertion_file_not_exists(__FILE__, __LINE__, pathname)
-/* Assert that file contents match a string. */
-#define assertFileContents(data, data_size, pathname) \
-  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
-/* Verify that a file does not contain invalid strings */
-#define assertFileContainsNoInvalidStrings(pathname, strings) \
-  assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
-#define assertFileMtime(pathname, sec, nsec)	\
-  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
-#define assertFileMtimeRecent(pathname) \
-  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
-#define assertFileNLinks(pathname, nlinks)  \
-  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)	\
-  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
-#define assertIsDir(pathname, mode)		\
-  assertion_is_dir(__FILE__, __LINE__, pathname, mode)
-#define assertIsHardlink(path1, path2)	\
-  assertion_is_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsNotHardlink(path1, path2)	\
-  assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2)
-#define assertIsReg(pathname, mode)		\
-  assertion_is_reg(__FILE__, __LINE__, pathname, mode)
-#define assertIsSymlink(pathname, contents)	\
-  assertion_is_symlink(__FILE__, __LINE__, pathname, contents)
-/* Create a directory, report error if it fails. */
-#define assertMakeDir(dirname, mode)	\
-  assertion_make_dir(__FILE__, __LINE__, dirname, mode)
-#define assertMakeFile(path, mode, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents)
-#define assertMakeBinFile(path, mode, csize, contents) \
-  assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
-#define assertMakeHardlink(newfile, oldfile)	\
-  assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto)	\
-  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
-#define assertNodump(path)      \
-  assertion_nodump(__FILE__, __LINE__, path)
-#define assertUmask(mask)	\
-  assertion_umask(__FILE__, __LINE__, mask)
-#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
-  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
-
-/*
- * This would be simple with C99 variadic macros, but I don't want to
- * require that.  Instead, I insert a function call before each
- * skipping() call to pass the file and line information down.  Crude,
- * but effective.
- */
-#define skipping	\
-  skipping_setup(__FILE__, __LINE__);test_skipping
-
-/* Function declarations.  These are defined in test_utility.c. */
-void failure(const char *fmt, ...);
-int assertion_assert(const char *, int, int, const char *, void *);
-int assertion_chdir(const char *, int, const char *);
-int assertion_empty_file(const char *, int, const char *);
-int assertion_equal_file(const char *, int, const char *, const char *);
-int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
-int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
-int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *);
-int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
-int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
-int assertion_file_atime(const char *, int, const char *, long, long);
-int assertion_file_atime_recent(const char *, int, const char *);
-int assertion_file_birthtime(const char *, int, const char *, long, long);
-int assertion_file_birthtime_recent(const char *, int, const char *);
-int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
-int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
-int assertion_file_contents(const char *, int, const void *, int, const char *);
-int assertion_file_exists(const char *, int, const char *);
-int assertion_file_mode(const char *, int, const char *, int);
-int assertion_file_mtime(const char *, int, const char *, long, long);
-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_is_dir(const char *, int, const char *, int);
-int assertion_is_hardlink(const char *, int, const char *, const char *);
-int assertion_is_not_hardlink(const char *, int, const char *, const char *);
-int assertion_is_reg(const char *, int, const char *, int);
-int assertion_is_symlink(const char *, int, const char *, const char *);
-int assertion_make_dir(const char *, int, const char *, int);
-int assertion_make_file(const char *, int, const char *, int, int, const void *);
-int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
-int assertion_nodump(const char *, int, const char *);
-int assertion_non_empty_file(const char *, int, const char *);
-int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
-int assertion_umask(const char *, int, int);
-int assertion_utimes(const char *, int, const char *, long, long, long, long );
-
-void skipping_setup(const char *, int);
-void test_skipping(const char *fmt, ...);
-
-/* Like sprintf, then system() */
-int systemf(const char * fmt, ...);
-
-/* Delay until time() returns a value after this. */
-void sleepUntilAfter(time_t);
-
-/* Return true if this platform can create symlinks. */
-int canSymlink(void);
-
-/* Return true if this platform can run the "bzip2" program. */
-int canBzip2(void);
-
-/* Return true if this platform can run the "grzip" program. */
-int canGrzip(void);
-
-/* Return true if this platform can run the "gzip" program. */
-int canGzip(void);
-
-/* Return true if this platform can run the specified command. */
-int canRunCommand(const char *);
-
-/* Return true if this platform can run the "lrzip" program. */
-int canLrzip(void);
-
-/* Return true if this platform can run the "lz4" program. */
-int canLz4(void);
-
-/* Return true if this platform can run the "lzip" program. */
-int canLzip(void);
-
-/* Return true if this platform can run the "lzma" program. */
-int canLzma(void);
-
-/* Return true if this platform can run the "lzop" program. */
-int canLzop(void);
-
-/* Return true if this platform can run the "xz" program. */
-int canXz(void);
-
-/* Return true if this filesystem can handle nodump flags. */
-int canNodump(void);
-
-/* Return true if the file has large i-node number(>0xffffffff). */
-int is_LargeInode(const char *);
-
-/* Suck file into string allocated via malloc(). Call free() when done. */
-/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
-char *slurpfile(size_t *, const char *fmt, ...);
-
-/* Dump block of bytes to a file. */
-void dumpfile(const char *filename, void *, size_t);
-
-/* Extracts named reference file to the current directory. */
-void extract_reference_file(const char *);
-/* Copies named reference file to the current directory. */
-void copy_reference_file(const char *);
-
-/* Extracts a list of files to the current directory.
- * List must be NULL terminated.
- */
-void extract_reference_files(const char **);
-
-/* Subtract umask from mode */
-mode_t umasked(mode_t expected_mode);
-
-/* Path to working directory for current test */
-extern const char *testworkdir;
-
-/*
- * Special interfaces for program test harness.
- */
-
-/* Pathname of exe to be tested. */
-extern const char *testprogfile;
-/* Name of exe to use in printf-formatted command strings. */
-/* On Windows, this includes leading/trailing quotes. */
-extern const char *testprog;
-
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
+#include "test_common.h"
diff --git a/tar/test/test_option_acls.c b/tar/test/test_option_acls.c
new file mode 100644
index 0000000..5c3fbfd
--- /dev/null
+++ b/tar/test/test_option_acls.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+#if HAVE_POSIX_ACL || HAVE_DARWIN_ACL
+static const acl_perm_t acl_perms[] = {
+#if HAVE_DARWIN_ACL
+    ACL_READ_DATA,
+    ACL_LIST_DIRECTORY,
+    ACL_WRITE_DATA,
+    ACL_ADD_FILE,
+    ACL_EXECUTE,
+    ACL_SEARCH,
+    ACL_DELETE,
+    ACL_APPEND_DATA,
+    ACL_ADD_SUBDIRECTORY,
+    ACL_DELETE_CHILD,
+    ACL_READ_ATTRIBUTES,
+    ACL_WRITE_ATTRIBUTES,
+    ACL_READ_EXTATTRIBUTES,
+    ACL_WRITE_EXTATTRIBUTES,
+    ACL_READ_SECURITY,
+    ACL_WRITE_SECURITY,
+    ACL_CHANGE_OWNER,
+    ACL_SYNCHRONIZE
+#else /* !HAVE_DARWIN_ACL */
+    ACL_EXECUTE,
+    ACL_WRITE,
+    ACL_READ,
+#if HAVE_FREEBSD_NFS4_ACL
+    ACL_READ_DATA,
+    ACL_LIST_DIRECTORY,
+    ACL_WRITE_DATA,
+    ACL_ADD_FILE,
+    ACL_APPEND_DATA,
+    ACL_ADD_SUBDIRECTORY,
+    ACL_READ_NAMED_ATTRS,
+    ACL_WRITE_NAMED_ATTRS,
+    ACL_DELETE_CHILD,
+    ACL_READ_ATTRIBUTES,
+    ACL_WRITE_ATTRIBUTES,
+    ACL_DELETE,
+    ACL_READ_ACL,
+    ACL_WRITE_ACL,
+    ACL_WRITE_OWNER,
+    ACL_SYNCHRONIZE
+#endif	/* HAVE_FREEBSD_NFS4_ACL */
+#endif /* !HAVE_DARWIN_ACL */
+};
+#if HAVE_DARWIN_ACL || HAVE_FREEBSD_NFS4_ACL
+static const acl_flag_t acl_flags[] = {
+#if HAVE_DARWIN_ACL
+    ACL_FLAG_DEFER_INHERIT,
+    ACL_FLAG_NO_INHERIT,
+    ACL_ENTRY_INHERITED,
+    ACL_ENTRY_FILE_INHERIT,
+    ACL_ENTRY_DIRECTORY_INHERIT,
+    ACL_ENTRY_LIMIT_INHERIT,
+    ACL_ENTRY_ONLY_INHERIT
+#else	/* HAVE_FREEBSD_NFS4_ACL */
+    ACL_ENTRY_FILE_INHERIT,
+    ACL_ENTRY_DIRECTORY_INHERIT,
+    ACL_ENTRY_NO_PROPAGATE_INHERIT,
+    ACL_ENTRY_INHERIT_ONLY,
+    ACL_ENTRY_SUCCESSFUL_ACCESS,
+    ACL_ENTRY_FAILED_ACCESS,
+    ACL_ENTRY_INHERITED
+#endif	/* HAVE_FREEBSD_NFS4_ACL */
+};
+#endif /* HAVE_DARWIN_ACL || HAVE_FREEBSD_NFS4_ACL */
+
+/*
+ * Compare two ACL entries on FreeBSD or on Mac OS X
+ */
+static int
+compare_acl_entry(acl_entry_t ae_a, acl_entry_t ae_b, int is_nfs4)
+{
+	acl_tag_t tag_a, tag_b;
+	acl_permset_t permset_a, permset_b;
+	int perm_a, perm_b, perm_start, perm_end;
+	void *qual_a, *qual_b;
+#if HAVE_FREEBSD_NFS4_ACL
+	acl_entry_type_t type_a, type_b;
+#endif
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL
+	acl_flagset_t flagset_a, flagset_b;
+	int flag_a, flag_b;
+#endif
+	int i, r;
+
+
+	/* Compare ACL tag */
+	r = acl_get_tag_type(ae_a, &tag_a);
+	failure("acl_get_tag_type() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		return (-1);
+	r = acl_get_tag_type(ae_b, &tag_b);
+	failure("acl_get_tag_type() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		return (-1);
+	if (tag_a != tag_b)
+		return (0);
+
+	/* Compare ACL qualifier */
+#if HAVE_DARWIN_ACL
+	if (tag_a == ACL_EXTENDED_ALLOW || tag_b == ACL_EXTENDED_DENY)
+#else
+	if (tag_a == ACL_USER || tag_a == ACL_GROUP)
+#endif
+	{
+		qual_a = acl_get_qualifier(ae_a);
+		failure("acl_get_qualifier() error: %s", strerror(errno));
+		if (assert(qual_a != NULL) == 0)
+			return (-1);
+		qual_b = acl_get_qualifier(ae_b);
+		failure("acl_get_qualifier() error: %s", strerror(errno));
+		if (assert(qual_b != NULL) == 0) {
+			acl_free(qual_a);
+			return (-1);
+		}
+#if HAVE_DARWIN_ACL
+		if (memcmp(((guid_t *)qual_a)->g_guid,
+		    ((guid_t *)qual_b)->g_guid, KAUTH_GUID_SIZE) != 0)
+#else
+		if ((tag_a == ACL_USER &&
+		    (*(uid_t *)qual_a != *(uid_t *)qual_b)) ||
+		    (tag_a == ACL_GROUP &&
+		    (*(gid_t *)qual_a != *(gid_t *)qual_b)))
+#endif
+		{
+			acl_free(qual_a);
+			acl_free(qual_b);
+			return (0);
+		}
+		acl_free(qual_a);
+		acl_free(qual_b);
+	}
+
+#if HAVE_FREEBSD_NFS4_ACL
+	if (is_nfs4) {
+		/* Compare NFS4 ACL type */
+		r = acl_get_entry_type_np(ae_a, &type_a);
+		failure("acl_get_entry_type_np() error: %s", strerror(errno));
+		if (assertEqualInt(r, 0) == 0)
+			return (-1);
+		r = acl_get_entry_type_np(ae_b, &type_b);
+		failure("acl_get_entry_type_np() error: %s", strerror(errno));
+		if (assertEqualInt(r, 0) == 0)
+			return (-1);
+		if (type_a != type_b)
+			return (0);
+	}
+#endif
+
+	/* Compare ACL perms */
+	r = acl_get_permset(ae_a, &permset_a);
+	failure("acl_get_permset() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		return (-1);
+	r = acl_get_permset(ae_b, &permset_b);
+	failure("acl_get_permset() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		return (-1);
+
+	perm_start = 0;
+	perm_end = (int)(sizeof(acl_perms) / sizeof(acl_perms[0]));
+#if HAVE_FREEBSD_NFS4_ACL
+	if (is_nfs4)
+		perm_start = 3;
+	else
+		perm_end = 3;
+#endif
+	/* Cycle through all perms and compare their value */
+	for (i = perm_start; i < perm_end; i++) {
+#if HAVE_LIBACL
+		perm_a = acl_get_perm(permset_a, acl_perms[i]);
+		perm_b = acl_get_perm(permset_b, acl_perms[i]);
+#else
+		perm_a = acl_get_perm_np(permset_a, acl_perms[i]);
+		perm_b = acl_get_perm_np(permset_b, acl_perms[i]);
+#endif
+		if (perm_a == -1 || perm_b == -1)
+			return (-1);
+		if (perm_a != perm_b)
+			return (0);
+	}
+
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL
+	if (is_nfs4) {
+		r = acl_get_flagset_np(ae_a, &flagset_a);
+		failure("acl_get_flagset_np() error: %s", strerror(errno));
+		if (assertEqualInt(r, 0) == 0)
+			return (-1);
+		r = acl_get_flagset_np(ae_b, &flagset_b);
+		failure("acl_get_flagset_np() error: %s", strerror(errno));
+		if (assertEqualInt(r, 0) == 0)
+			return (-1);
+		/* Cycle through all flags and compare their status */
+		for (i = 0; i < (int)(sizeof(acl_flags) / sizeof(acl_flags[0]));
+		    i++) {
+			flag_a = acl_get_flag_np(flagset_a, acl_flags[i]);
+			flag_b = acl_get_flag_np(flagset_b, acl_flags[i]);
+			if (flag_a == -1 || flag_b == -1)
+				return (-1);
+			if (flag_a != flag_b)
+				return (0);
+		}
+	}
+#else	/* HAVE_FREEBSD_NFS4_ACL || HAVE_DARWIN_ACL*/
+	(void)is_nfs4;	/* UNUSED */
+#endif
+	return (1);
+}
+#endif	/* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */
+
+#if HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL
+/*
+ * Clear default ACLs or inheritance flags
+ */
+static void
+clear_inheritance_flags(const char *path, int type)
+{
+	switch (type) {
+	case ARCHIVE_TEST_ACL_TYPE_POSIX1E:
+#if HAVE_POSIX_ACL
+		acl_delete_def_file(path);
+#else
+		/* Solaris */
+		setTestAcl(path);
+#endif
+		break;
+	case ARCHIVE_TEST_ACL_TYPE_NFS4:
+#if HAVE_NFS4_ACL
+		setTestAcl(path);
+#endif
+		break;
+	default:
+		(void)path;	/* UNUSED */
+		break;
+	}
+}
+
+static int
+compare_acls(const char *path_a, const char *path_b)
+{
+	int ret = 1;
+	int is_nfs4 = 0;
+#if HAVE_SUN_ACL
+	void *acl_a, *acl_b;
+	int aclcnt_a, aclcnt_b;
+        aclent_t *aclent_a, *aclent_b;
+        ace_t *ace_a, *ace_b;
+	int e;
+#else
+	acl_t acl_a, acl_b;
+	acl_entry_t aclent_a, aclent_b;
+	int a, b, r;
+#endif
+
+	acl_a = NULL;
+	acl_b = NULL;
+#if HAVE_SUN_ACL
+	acl_a = sunacl_get(GETACL, &aclcnt_a, 0, path_a);
+	if (acl_a == NULL) {
+#if HAVE_SUN_NFS4_ACL
+		is_nfs4 = 1;
+		acl_a = sunacl_get(ACE_GETACL, &aclcnt_a, 0, path_a);
+#endif
+		failure("acl_get() error: %s", strerror(errno));
+		if (assert(acl_a != NULL) == 0)
+			return (-1);
+#if HAVE_SUN_NFS4_ACL
+		acl_b = sunacl_get(ACE_GETACL, &aclcnt_b, 0, path_b);
+#endif
+	} else
+		acl_b = sunacl_get(GETACL, &aclcnt_b, 0, path_b);
+	if (acl_b == NULL && (errno == ENOSYS || errno == ENOTSUP)) {
+		free(acl_a);
+		return (0);
+	}
+	failure("acl_get() error: %s", strerror(errno));
+	if (assert(acl_b != NULL) == 0) {
+		free(acl_a);
+		return (-1);
+	}
+
+	if (aclcnt_a != aclcnt_b) {
+		ret = 0;
+		goto exit_free;
+	}
+
+	for (e = 0; e < aclcnt_a; e++) {
+		if (!is_nfs4) {
+			aclent_a = &((aclent_t *)acl_a)[e];
+			aclent_b = &((aclent_t *)acl_b)[e];
+			if (aclent_a->a_type != aclent_b->a_type ||
+			    aclent_a->a_id != aclent_b->a_id ||
+			    aclent_a->a_perm != aclent_b->a_perm) {
+				ret = 0;
+				goto exit_free;
+			}
+		}
+#if HAVE_SUN_NFS4_ACL
+		else {
+			ace_a = &((ace_t *)acl_a)[e];
+			ace_b = &((ace_t *)acl_b)[e];
+			if (ace_a->a_who != ace_b->a_who ||
+			    ace_a->a_access_mask != ace_b->a_access_mask ||
+			    ace_a->a_flags != ace_b->a_flags ||
+			    ace_a->a_type != ace_b->a_type) {
+				ret = 0;
+				goto exit_free;
+			}
+		}
+#endif
+	}
+#else	/* !HAVE_SUN_ACL */
+#if HAVE_DARWIN_ACL
+	is_nfs4 = 1;
+	acl_a = acl_get_file(path_a, ACL_TYPE_EXTENDED);
+#elif HAVE_FREEBSD_NFS4_ACL
+	acl_a = acl_get_file(path_a, ACL_TYPE_NFS4);
+	if (acl_a != NULL)
+		is_nfs4 = 1;
+#endif
+#if !HAVE_DARWIN_ACL
+	if (acl_a == NULL)
+		acl_a = acl_get_file(path_a, ACL_TYPE_ACCESS);
+#endif
+	failure("acl_get_file() error: %s (%s)", path_a, strerror(errno));
+	if (assert(acl_a != NULL) == 0)
+		return (-1);
+#if HAVE_DARWIN_ACL
+	acl_b = acl_get_file(path_b, ACL_TYPE_EXTENDED);
+#elif HAVE_FREEBSD_NFS4_ACL
+	acl_b = acl_get_file(path_b, ACL_TYPE_NFS4);
+#endif
+#if !HAVE_DARWIN_ACL
+	if (acl_b == NULL) {
+#if HAVE_FREEBSD_NFS4_ACL
+		if (is_nfs4) {
+			acl_free(acl_a);
+			return (0);
+		}
+#endif
+		acl_b = acl_get_file(path_b, ACL_TYPE_ACCESS);
+	}
+	failure("acl_get_file() error: %s (%s)", path_b, strerror(errno));
+	if (assert(acl_b != NULL) == 0) {
+		acl_free(acl_a);
+		return (-1);
+	}
+#endif
+	a = acl_get_entry(acl_a, ACL_FIRST_ENTRY, &aclent_a);
+	if (a == -1) {
+		ret = 0;
+		goto exit_free;
+	}
+	b = acl_get_entry(acl_b, ACL_FIRST_ENTRY, &aclent_b);
+	if (b == -1) {
+		ret = 0;
+		goto exit_free;
+	}
+#if HAVE_DARWIN_ACL
+	while (a == 0 && b == 0)
+#else	/* FreeBSD, Linux */
+	while (a == 1 && b == 1)
+#endif
+	{
+		r = compare_acl_entry(aclent_a, aclent_b, is_nfs4);
+		if (r != 1) {
+			ret = r;
+			goto exit_free;
+		}
+		a = acl_get_entry(acl_a, ACL_NEXT_ENTRY, &aclent_a);
+		b = acl_get_entry(acl_b, ACL_NEXT_ENTRY, &aclent_b);
+	}
+	/* Entry count must match */
+	if (a != b)
+		ret = 0;
+#endif	/* !HAVE_SUN_ACL */
+exit_free:
+#if HAVE_SUN_ACL
+	free(acl_a);
+	free(acl_b);
+#else
+	acl_free(acl_a);
+	acl_free(acl_b);
+#endif
+	return (ret);
+}
+#endif	/* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */
+
+DEFINE_TEST(test_option_acls)
+{
+#if !HAVE_SUN_ACL && !HAVE_DARWIN_ACL && !HAVE_POSIX_ACL
+        skipping("ACLs are not supported on this platform");
+#else   /* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */
+	int acltype, r;
+
+	assertMakeFile("f", 0644, "a");
+	acltype = setTestAcl("f");
+	if (acltype == 0) {
+		skipping("Can't write ACLs on the filesystem");
+		return;
+	}
+
+	/* Archive it with acls */
+        r = systemf("%s -c --no-mac-metadata --acls -f acls.tar f >acls.out 2>acls.err", testprog);
+        assertEqualInt(r, 0);
+
+	/* Archive it without acls */
+	r = systemf("%s -c --no-mac-metadata --no-acls -f noacls.tar f >noacls.out 2>noacls.err", testprog);
+	assertEqualInt(r, 0);
+
+	/* Extract acls with acls */
+	assertMakeDir("acls_acls", 0755);
+	clear_inheritance_flags("acls_acls", acltype);
+	r = systemf("%s -x -C acls_acls --no-same-permissions --acls -f acls.tar >acls_acls.out 2>acls_acls.err", testprog);
+	assertEqualInt(r, 0);
+	r = compare_acls("f", "acls_acls/f");
+	assertEqualInt(r, 1);
+
+	/* Extractl acls without acls */
+	assertMakeDir("acls_noacls", 0755);
+	clear_inheritance_flags("acls_noacls", acltype);
+	r = systemf("%s -x -C acls_noacls -p --no-acls -f acls.tar >acls_noacls.out 2>acls_noacls.err", testprog);
+	assertEqualInt(r, 0);
+	r = compare_acls("f", "acls_noacls/f");
+	assertEqualInt(r, 0);
+
+	/* Extract noacls with acls flag */
+	assertMakeDir("noacls_acls", 0755);
+	clear_inheritance_flags("noacls_acls", acltype);
+	r = systemf("%s -x -C noacls_acls --no-same-permissions --acls -f noacls.tar >noacls_acls.out 2>noacls_acls.err", testprog);
+	assertEqualInt(r, 0);
+	r = compare_acls("f", "noacls_acls/f");
+	assertEqualInt(r, 0);
+
+	/* Extract noacls with noacls */
+	assertMakeDir("noacls_noacls", 0755);
+	clear_inheritance_flags("noacls_noacls", acltype);
+	r = systemf("%s -x -C noacls_noacls -p --no-acls -f noacls.tar >noacls_noacls.out 2>noacls_noacls.err", testprog);
+	assertEqualInt(r, 0);
+	r = compare_acls("f", "noacls_noacls/f");
+	assertEqualInt(r, 0);
+#endif	/* HAVE_SUN_ACL || HAVE_DARWIN_ACL || HAVE_POSIX_ACL */
+}
diff --git a/tar/test/test_option_fflags.c b/tar/test/test_option_fflags.c
new file mode 100644
index 0000000..77a4e3e
--- /dev/null
+++ b/tar/test/test_option_fflags.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+static void
+clear_fflags(const char *pathname)
+{
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS)
+	chflags(pathname, 0);
+#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+	int fd;
+
+	fd = open(pathname, O_RDONLY | O_NONBLOCK);
+	if (fd < 0)
+		return;
+	ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    0);
+#else
+	(void)pathname; /* UNUSED */
+#endif
+	return;
+}
+
+DEFINE_TEST(test_option_fflags)
+{
+	int r;
+
+	if (!canNodump()) {
+		skipping("Can't test nodump flag on this filesystem");
+		return;
+	}
+
+	/* Create a file. */
+	assertMakeFile("f", 0644, "a");
+
+	/* Set nodump flag on the file */
+	assertSetNodump("f");
+
+	/* FreeBSD ZFS workaround: ZFS sets uarch on all touched files and dirs */
+	chmod("f", 0644);
+
+	/* Archive it with fflags */
+	r = systemf("%s -c --fflags -f fflags.tar f >fflags.out 2>fflags.err", testprog);
+	assertEqualInt(r, 0);
+
+	/* Archive it without fflags */
+	r = systemf("%s -c --no-fflags -f nofflags.tar f >nofflags.out 2>nofflags.err", testprog);
+	assertEqualInt(r, 0);
+
+	/* Extract fflags with fflags */
+	assertMakeDir("fflags_fflags", 0755);
+	clear_fflags("fflags_fflags");
+	r = systemf("%s -x -C fflags_fflags --no-same-permissions --fflags -f fflags.tar >fflags_fflags.out 2>fflags_fflags.err", testprog);
+	assertEqualInt(r, 0);
+	assertEqualFflags("f", "fflags_fflags/f");
+
+	/* Extract fflags without fflags */
+	assertMakeDir("fflags_nofflags", 0755);
+	clear_fflags("fflags_nofflags");
+	r = systemf("%s -x -C fflags_nofflags -p --no-fflags -f fflags.tar >fflags_nofflags.out 2>fflags_nofflags.err", testprog);
+	assertEqualInt(r, 0);
+	assertUnequalFflags("f", "fflags_nofflags/f");
+
+	/* Extract nofflags with fflags */
+	assertMakeDir("nofflags_fflags", 0755);
+	clear_fflags("nofflags_fflags");
+	r = systemf("%s -x -C nofflags_fflags --no-same-permissions --fflags -f nofflags.tar >nofflags_fflags.out 2>nofflags_fflags.err", testprog);
+	assertEqualInt(r, 0);	
+	assertUnequalFflags("f", "nofflags_fflags/f");
+
+	/* Extract nofflags with nofflags */
+	assertMakeDir("nofflags_nofflags", 0755);
+	clear_fflags("nofflags_nofflags");
+	r = systemf("%s -x -C nofflags_nofflags -p --no-fflags -f nofflags.tar >nofflags_nofflags.out 2>nofflags_nofflags.err", testprog);
+	assertEqualInt(r, 0);
+	assertUnequalFflags("f", "nofflags_nofflags/f");
+}
diff --git a/tar/test/test_option_nodump.c b/tar/test/test_option_nodump.c
index 768f64a..815b08e 100644
--- a/tar/test/test_option_nodump.c
+++ b/tar/test/test_option_nodump.c
@@ -36,7 +36,7 @@
 	assertMakeFile("file1", 0644, "file1");
 	assertMakeFile("file2", 0644, "file2");
 	assertMakeFile("file3", 0644, "file3");
-	assertNodump("file2");
+	assertSetNodump("file2");
 
 	/* Test 1: Without --nodump */
 	assertEqualInt(0, systemf("%s -cf test1.tar file1 file2 file3",
diff --git a/tar/test/test_option_uid_uname.c b/tar/test/test_option_uid_uname.c
index 0a8a9bb..80c0619 100644
--- a/tar/test/test_option_uid_uname.c
+++ b/tar/test/test_option_uid_uname.c
@@ -45,25 +45,25 @@
 	/* Again with both --uid and --uname */
 	failure("Error invoking %s c", testprog);
 	assertEqualInt(0,
-	    systemf("%s cf archive2 --uid=17 --uname=foofoofoo --format=ustar file >stdout2.txt 2>stderr2.txt",
+	    systemf("%s cf archive2 --uid=65123 --uname=foofoofoo --format=ustar file >stdout2.txt 2>stderr2.txt",
 		testprog));
 	assertEmptyFile("stdout2.txt");
 	assertEmptyFile("stderr2.txt");
 	data = slurpfile(&s, "archive2");
 	/* Should force uid and uname fields in ustar header. */
-	assertEqualMem(data + 108, "000021 \0", 8);
+	assertEqualMem(data + 108, "177143 \0", 8);
 	assertEqualMem(data + 265, "foofoofoo\0", 10);
 	free(data);
 
 	/* Again with just --uid */
 	failure("Error invoking %s c", testprog);
 	assertEqualInt(0,
-	    systemf("%s cf archive3 --uid=17 --format=ustar file >stdout3.txt 2>stderr3.txt",
+	    systemf("%s cf archive3 --uid=65123 --format=ustar file >stdout3.txt 2>stderr3.txt",
 		testprog));
 	assertEmptyFile("stdout3.txt");
 	assertEmptyFile("stderr3.txt");
 	data = slurpfile(&s, "archive3");
-	assertEqualMem(data + 108, "000021 \0", 8);
+	assertEqualMem(data + 108, "177143 \0", 8);
 	/* Uname field in ustar header should be empty. */
 	assertEqualMem(data + 265, "\0", 1);
 	free(data);
diff --git a/tar/test/test_version.c b/tar/test/test_version.c
index 0c904d7..1b896c0 100644
--- a/tar/test/test_version.c
+++ b/tar/test/test_version.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2017 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,7 +23,6 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/test/test_version.c,v 1.2 2008/05/26 17:10:10 kientzle Exp $");
 
 /*
  * Test that --version option works and generates reasonable output.
@@ -31,73 +30,5 @@
 
 DEFINE_TEST(test_version)
 {
-	int r;
-	char *p, *q;
-	size_t s;
-
-
-	r = systemf("%s --version >version.stdout 2>version.stderr", testprog);
-	if (r != 0)
-		r = systemf("%s -W version >version.stdout 2>version.stderr",
-		    testprog);
-	failure("Unable to run either %s --version or %s -W version",
-	    testprog, testprog);
-	if (!assert(r == 0))
-		return;
-
-	/* --version should generate nothing to stdout. */
-	assertEmptyFile("version.stderr");
-	/* Verify format of version message. */
-	q = p = slurpfile(&s, "version.stdout");
-	/* Version message should start with name of program, then space. */
-	assert(s > 6);
-	failure("Version must start with 'bsdtar': ``%s''", p);
-	if (!assertEqualMem(q, "bsdtar ", 7))
-		goto done;
-	q += 7; s -= 7;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Version number terminated by space. */
-	failure("No space after bsdtar version: ``%s''", p);
-	assert(s > 1);
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	failure("No space after bsdtar version: ``%s''", p);
-	assert(*q == ' ');
-	++q; --s;
-	/* Separator. */
-	failure("No `-' between bsdtar and libarchive versions: ``%s''", p);
-	assertEqualMem(q, "- ", 2);
-	q += 2; s -= 2;
-	/* libarchive name and version number */
-	failure("Not long enough for libarchive version: ``%s''", p);
-	assert(s > 11);
-	failure("Libarchive version must start with `libarchive': ``%s''", p);
-	assertEqualMem(q, "libarchive ", 11);
-	q += 11; s -= 11;
-	/* Version number is a series of digits and periods. */
-	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
-		++q;
-		--s;
-	}
-	/* Skip a single trailing a,b,c, or d. */
-	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
-		++q;
-	/* Skip arbitrary third-party version numbers. */
-	while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum(*q))) {
-		++q;
-		--s;
-	}
-	/* All terminated by end-of-line. */
-	assert(s >= 1);
-	/* Skip an optional CR character (e.g., Windows) */
-	failure("Version output must end with \\n or \\r\\n");
-	if (*q == '\r') { ++q; --s; }
-	assertEqualMem(q, "\n", 1);
-done:
-	free(p);
+	assertVersion(testprog, "bsdtar");
 }
diff --git a/tar/util.c b/tar/util.c
index 2b4aebe..662db5b 100644
--- a/tar/util.c
+++ b/tar/util.c
@@ -140,6 +140,7 @@
 		} else {
 			/* Leave fmtbuff pointing to the truncated
 			 * string in fmtbuff_stack. */
+			fmtbuff = fmtbuff_stack;
 			length = sizeof(fmtbuff_stack) - 1;
 			break;
 		}
@@ -533,7 +534,7 @@
 		}
 	}
 
-	if (!bsdtar->option_absolute_paths) {
+	if ((bsdtar->flags & OPTFLAG_ABSOLUTE_PATHS) == 0) {
 		/* By default, don't write or restore absolute pathnames. */
 		name = strip_absolute_path(bsdtar, name);
 		if (*name == '\0')
diff --git a/tar/write.c b/tar/write.c
index 7960487..9c24566 100644
--- a/tar/write.c
+++ b/tar/write.c
@@ -145,18 +145,17 @@
 
 	writer_options = getenv(ENV_WRITER_OPTIONS);
 	if (writer_options != NULL) {
+		size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1;
+		size_t opt_len = strlen(writer_options) + 1;
 		char *p;
 		/* Set default write options. */
-		p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME)
-		    + strlen(writer_options) + 1);
-		if (p == NULL)
+		if ((p = malloc(module_len + opt_len)) == NULL)
 			lafe_errc(1, errno, "Out of memory");
 		/* Prepend magic code to ignore options for
 		 * a format or filters which are not added to
 		 * the archive write object. */
-		strncpy(p, IGNORE_WRONG_MODULE_NAME,
-		    sizeof(IGNORE_WRONG_MODULE_NAME) -1);
-		strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, writer_options);
+		memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len);
+		memcpy(p, writer_options, opt_len);
 		r = archive_write_set_options(a, p);
 		free(p);
 		if (r < ARCHIVE_WARN)
@@ -178,18 +177,18 @@
 
 	reader_options = getenv(ENV_READER_OPTIONS);
 	if (reader_options != NULL) {
+		size_t module_len = sizeof(IGNORE_WRONG_MODULE_NAME) - 1;
+		size_t opt_len = strlen(reader_options) + 1;
 		char *p;
 		/* Set default write options. */
-		p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME)
-		    + strlen(reader_options) + 1);
+		if ((p = malloc(module_len + opt_len)) == NULL)
 		if (p == NULL)
 			lafe_errc(1, errno, "Out of memory");
 		/* Prepend magic code to ignore options for
 		 * a format or filters which are not added to
 		 * the archive write object. */
-		strncpy(p, IGNORE_WRONG_MODULE_NAME,
-		    sizeof(IGNORE_WRONG_MODULE_NAME) -1);
-		strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options);
+		memcpy(p, IGNORE_WRONG_MODULE_NAME, module_len);
+		memcpy(p, reader_options, opt_len);
 		r = archive_read_set_options(a, p);
 		free(p);
 		if (r < ARCHIVE_WARN)
@@ -584,7 +583,7 @@
 	archive_read_free(bsdtar->diskreader);
 	bsdtar->diskreader = NULL;
 
-	if (bsdtar->option_totals) {
+	if (bsdtar->flags & OPTFLAG_TOTALS) {
 		fprintf(stderr, "Total bytes written: %s\n",
 		    tar_i64toa(archive_filter_bytes(a, -1)));
 	}
@@ -607,7 +606,8 @@
 
 	bsdtar->next_line_is_dir = 0;
 
-	lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null);
+	lr = lafe_line_reader(bsdtar->names_from_file,
+	    (bsdtar->flags & OPTFLAG_NULL));
 	while ((line = lafe_line_reader_next(lr)) != NULL) {
 		if (bsdtar->next_line_is_dir) {
 			if (*line != '\0')
@@ -618,7 +618,8 @@
 				bsdtar->return_value = 1;
 			}
 			bsdtar->next_line_is_dir = 0;
-		} else if (!bsdtar->option_null && strcmp(line, "-C") == 0)
+		} else if (((bsdtar->flags & OPTFLAG_NULL) == 0) &&
+		    strcmp(line, "-C") == 0)
 			bsdtar->next_line_is_dir = 1;
 		else {
 			if (*line != '/')
@@ -691,7 +692,7 @@
 	while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) {
 		if (archive_match_excluded(bsdtar->matching, in_entry))
 			continue;
-		if (bsdtar->option_interactive &&
+		if ((bsdtar->flags & OPTFLAG_INTERACTIVE) &&
 		    !yes("copy '%s'", archive_entry_pathname(in_entry)))
 			continue;
 		if (bsdtar->verbose > 1) {
@@ -810,11 +811,11 @@
 {
 	struct bsdtar *bsdtar = (struct bsdtar *)_data;
 
-	if (bsdtar->option_no_subdirs)
+	if (bsdtar->flags & OPTFLAG_NO_SUBDIRS)
 		return;
 	if (!archive_read_disk_can_descend(a))
 		return;
-	if (bsdtar->option_interactive &&
+	if ((bsdtar->flags & OPTFLAG_INTERACTIVE) &&
 	    !yes("add '%s'", archive_entry_pathname(entry)))
 		return;
 	archive_read_disk_descend(a);
@@ -845,12 +846,13 @@
 	 * check would veto this file, we shouldn't bother
 	 * the user with it.
 	 */
-	if (bsdtar->option_interactive &&
+	if ((bsdtar->flags & OPTFLAG_INTERACTIVE) &&
 	    !yes("add '%s'", archive_entry_pathname(entry)))
 		return (0);
 
 	/* Note: if user vetoes, we won't descend. */
-	if (!bsdtar->option_no_subdirs && archive_read_disk_can_descend(a))
+	if (((bsdtar->flags & OPTFLAG_NO_SUBDIRS) == 0) &&
+	    archive_read_disk_can_descend(a))
 		archive_read_disk_descend(a);
 
 	return (1);
@@ -1011,7 +1013,7 @@
 	uncomp = archive_filter_bytes(a, 0);
 	fprintf(stderr, "In: %d files, %s bytes;",
 	    archive_file_count(a), tar_i64toa(uncomp));
-	if (comp > uncomp)
+	if (comp >= uncomp)
 		compression = 0;
 	else
 		compression = (int)((uncomp - comp) * 100 / uncomp);
diff --git a/test_utils/test_common.h b/test_utils/test_common.h
new file mode 100644
index 0000000..0078054
--- /dev/null
+++ b/test_utils/test_common.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2003-2017 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	TEST_COMMON_H
+#define	TEST_COMMON_H
+
+/*
+ * The goal of this file (and the matching test.c) is to
+ * simplify the very repetitive test-*.c test programs.
+ */
+#if defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "config.h"
+#elif defined(__FreeBSD__)
+/* Building as part of FreeBSD system requires a pre-built config.h. */
+#include "config_freebsd.h"
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+/* Win32 can't run the 'configure' script. */
+#include "config_windows.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in test.h.
+#endif
+
+#include <sys/types.h>  /* Windows requires this before sys/stat.h */
+#include <sys/stat.h>
+
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#define dirent direct
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <wchar.h>
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+/*
+ * System-specific tweaks.  We really want to minimize these
+ * as much as possible, since they make it harder to understand
+ * the mainline code.
+ */
+
+/* Windows (including Visual Studio and MinGW but not Cygwin) */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(__BORLANDC__)
+#undef chdir
+#define chdir _chdir
+#define strdup _strdup
+#endif
+#endif
+
+/* Visual Studio */
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf	sprintf_s
+#endif
+
+#if defined(__BORLANDC__)
+#pragma warn -8068	/* Constant out of range in comparison. */
+#endif
+
+/* Haiku OS and QNX */
+#if defined(__HAIKU__) || defined(__QNXNTO__)
+/* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
+#include <stdint.h>
+#endif
+
+/* Get a real definition for __FBSDID if we can */
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+/* If not, define it so as to avoid dangling semicolons. */
+#ifndef __FBSDID
+#define	__FBSDID(a)     struct _undefined_hack
+#endif
+
+#ifndef O_BINARY
+#define	O_BINARY 0
+#endif
+
+/*
+ * If this platform has <sys/acl.h>, acl_create(), acl_init(),
+ * acl_set_file(), and ACL_USER, we assume it has the rest of the
+ * POSIX.1e draft functions used in archive_read_extract.c.
+ */
+#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE
+#if HAVE_DECL_ACL_USER
+#define	HAVE_POSIX_ACL	1
+#elif HAVE_DECL_ACL_TYPE_EXTENDED
+#define	HAVE_DARWIN_ACL	1
+#endif
+#if HAVE_DECL_ACL_TYPE_NFS4
+#define	HAVE_FREEBSD_NFS4_ACL 1
+#endif
+#endif
+
+/*
+ * If this platform has <sys/acl.h>, acl_get(), facl_get(), acl_set(),
+ * facl_set() and types aclent_t and ace_t it uses Solaris-style ACL functions
+ */
+#if HAVE_SYS_ACL_H && HAVE_ACL && HAVE_FACL && HAVE_ACLENT_T && \
+    HAVE_DECL_GETACL && HAVE_DECL_GETACLCNT && HAVE_DECL_SETACL
+#define HAVE_SUN_ACL    1
+#if HAVE_ACE_T && HAVE_DECL_ACE_GETACL && HAVE_DECL_ACE_GETACLCNT && \
+    HAVE_DECL_ACE_SETACL
+#define HAVE_SUN_NFS4_ACL       1
+#endif
+#endif
+
+/* Define if platform supports NFSv4 ACLs */
+#if HAVE_FREEBSD_NFS4_ACL || HAVE_SUN_NFS4_ACL || HAVE_DARWIN_ACL
+#define HAVE_NFS4_ACL   1
+#endif
+
+#define	ARCHIVE_TEST_ACL_TYPE_POSIX1E	1
+#define	ARCHIVE_TEST_ACL_TYPE_NFS4	2
+
+/*
+ * Redefine DEFINE_TEST for use in defining the test functions.
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(name) void name(void); void name(void)
+
+/* An implementation of the standard assert() macro */
+#define assert(e)   assertion_assert(__FILE__, __LINE__, (e), #e, NULL)
+/* chdir() and error if it fails */
+#define assertChdir(path)  \
+  assertion_chdir(__FILE__, __LINE__, path)
+/* Assert two files have the same file flags */
+#define assertEqualFflags(patha, pathb)	\
+  assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 0)
+/* Assert two integers are the same.  Reports value of each one if not. */
+#define assertEqualInt(v1,v2) \
+  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* Assert two strings are the same.  Reports value of each one if not. */
+#define assertEqualString(v1,v2)   \
+  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
+#define assertEqualUTF8String(v1,v2)   \
+  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
+/* As above, but v1 and v2 are wchar_t * */
+#define assertEqualWString(v1,v2)   \
+  assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* As above, but raw blocks of bytes. */
+#define assertEqualMem(v1, v2, l)	\
+  assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
+/* Assert that memory is full of a specified byte */
+#define assertMemoryFilledWith(v1, l, b)					\
+  assertion_memory_filled_with(__FILE__, __LINE__, (v1), #v1, (l), #l, (b), #b, NULL)
+/* Assert two files are the same. */
+#define assertEqualFile(f1, f2)	\
+  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
+/* Assert that a file is empty. */
+#define assertEmptyFile(pathname)	\
+  assertion_empty_file(__FILE__, __LINE__, (pathname))
+/* Assert that a file is not empty. */
+#define assertNonEmptyFile(pathname)		\
+  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
+#define assertFileAtime(pathname, sec, nsec)	\
+  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileAtimeRecent(pathname)	\
+  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
+#define assertFileBirthtime(pathname, sec, nsec)	\
+  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileBirthtimeRecent(pathname) \
+  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
+/* Assert that a file exists; supports printf-style arguments. */
+#define assertFileExists(pathname) \
+  assertion_file_exists(__FILE__, __LINE__, pathname)
+/* Assert that a file exists. */
+#define assertFileNotExists(pathname) \
+  assertion_file_not_exists(__FILE__, __LINE__, pathname)
+/* Assert that file contents match a string. */
+#define assertFileContents(data, data_size, pathname) \
+  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
+/* Verify that a file does not contain invalid strings */
+#define assertFileContainsNoInvalidStrings(pathname, strings) \
+  assertion_file_contains_no_invalid_strings(__FILE__, __LINE__, pathname, strings)
+#define assertFileMtime(pathname, sec, nsec)	\
+  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileMtimeRecent(pathname) \
+  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
+#define assertFileNLinks(pathname, nlinks)  \
+  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)	\
+  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
+#define assertIsDir(pathname, mode)		\
+  assertion_is_dir(__FILE__, __LINE__, pathname, mode)
+#define assertIsHardlink(path1, path2)	\
+  assertion_is_hardlink(__FILE__, __LINE__, path1, path2)
+#define assertIsNotHardlink(path1, path2)	\
+  assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2)
+#define assertIsReg(pathname, mode)		\
+  assertion_is_reg(__FILE__, __LINE__, pathname, mode)
+#define assertIsSymlink(pathname, contents)	\
+  assertion_is_symlink(__FILE__, __LINE__, pathname, contents)
+/* Create a directory, report error if it fails. */
+#define assertMakeDir(dirname, mode)	\
+  assertion_make_dir(__FILE__, __LINE__, dirname, mode)
+#define assertMakeFile(path, mode, contents) \
+  assertion_make_file(__FILE__, __LINE__, path, mode, -1, contents)
+#define assertMakeBinFile(path, mode, csize, contents) \
+  assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
+#define assertMakeHardlink(newfile, oldfile)	\
+  assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
+#define assertMakeSymlink(newfile, linkto)	\
+  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
+#define assertSetNodump(path)	\
+  assertion_set_nodump(__FILE__, __LINE__, path)
+#define assertUmask(mask)	\
+  assertion_umask(__FILE__, __LINE__, mask)
+/* Assert that two files have unequal file flags */
+#define assertUnequalFflags(patha, pathb)	\
+  assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 1)
+#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
+  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
+#ifndef PROGRAM
+#define assertEntrySetAcls(entry, acls, count) \
+  assertion_entry_set_acls(__FILE__, __LINE__, entry, acls, count)
+#define assertEntryCompareAcls(entry, acls, count, type, mode) \
+  assertion_entry_compare_acls(__FILE__, __LINE__, entry, acls, count, type, mode)
+#endif
+
+/*
+ * This would be simple with C99 variadic macros, but I don't want to
+ * require that.  Instead, I insert a function call before each
+ * skipping() call to pass the file and line information down.  Crude,
+ * but effective.
+ */
+#define skipping	\
+  skipping_setup(__FILE__, __LINE__);test_skipping
+
+/* Function declarations.  These are defined in test_utility.c. */
+void failure(const char *fmt, ...);
+int assertion_assert(const char *, int, int, const char *, void *);
+int assertion_chdir(const char *, int, const char *);
+int assertion_compare_fflags(const char *, int, const char *, const char *,
+    int);
+int assertion_empty_file(const char *, int, const char *);
+int assertion_equal_file(const char *, int, const char *, const char *);
+int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
+int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
+int assertion_memory_filled_with(const char *, int, const void *, const char *, size_t, const char *, char, const char *, void *);
+int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
+int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
+int assertion_file_atime(const char *, int, const char *, long, long);
+int assertion_file_atime_recent(const char *, int, const char *);
+int assertion_file_birthtime(const char *, int, const char *, long, long);
+int assertion_file_birthtime_recent(const char *, int, const char *);
+int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
+int assertion_file_contains_no_invalid_strings(const char *, int, const char *, const char **);
+int assertion_file_contents(const char *, int, const void *, int, const char *);
+int assertion_file_exists(const char *, int, const char *);
+int assertion_file_mode(const char *, int, const char *, int);
+int assertion_file_mtime(const char *, int, const char *, long, long);
+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_is_dir(const char *, int, const char *, int);
+int assertion_is_hardlink(const char *, int, const char *, const char *);
+int assertion_is_not_hardlink(const char *, int, const char *, const char *);
+int assertion_is_reg(const char *, int, const char *, int);
+int assertion_is_symlink(const char *, int, const char *, const char *);
+int assertion_make_dir(const char *, int, const char *, int);
+int assertion_make_file(const char *, int, const char *, int, int, const void *);
+int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
+int assertion_make_symlink(const char *, int, const char *newpath, const char *);
+int assertion_non_empty_file(const char *, int, const char *);
+int assertion_set_nodump(const char *, int, const char *);
+int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
+int assertion_umask(const char *, int, int);
+int assertion_utimes(const char *, int, const char *, long, long, long, long );
+int assertion_version(const char*, int, const char *, const char *);
+
+void skipping_setup(const char *, int);
+void test_skipping(const char *fmt, ...);
+
+/* Like sprintf, then system() */
+int systemf(const char * fmt, ...);
+
+/* Delay until time() returns a value after this. */
+void sleepUntilAfter(time_t);
+
+/* Return true if this platform can create symlinks. */
+int canSymlink(void);
+
+/* Return true if this platform can run the "bzip2" program. */
+int canBzip2(void);
+
+/* Return true if this platform can run the "grzip" program. */
+int canGrzip(void);
+
+/* Return true if this platform can run the "gzip" program. */
+int canGzip(void);
+
+/* Return true if this platform can run the specified command. */
+int canRunCommand(const char *);
+
+/* Return true if this platform can run the "lrzip" program. */
+int canLrzip(void);
+
+/* Return true if this platform can run the "lz4" program. */
+int canLz4(void);
+
+/* Return true if this platform can run the "lzip" program. */
+int canLzip(void);
+
+/* Return true if this platform can run the "lzma" program. */
+int canLzma(void);
+
+/* Return true if this platform can run the "lzop" program. */
+int canLzop(void);
+
+/* Return true if this platform can run the "xz" program. */
+int canXz(void);
+
+/* Return true if this filesystem can handle nodump flags. */
+int canNodump(void);
+
+/* Set test ACLs */
+int setTestAcl(const char *path);
+
+/* Return true if the file has large i-node number(>0xffffffff). */
+int is_LargeInode(const char *);
+
+#if HAVE_SUN_ACL
+/* Fetch ACLs on Solaris using acl() or facl() */
+void *sunacl_get(int cmd, int *aclcnt, int fd, const char *path);
+#endif
+
+/* Suck file into string allocated via malloc(). Call free() when done. */
+/* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
+char *slurpfile(size_t *, const char *fmt, ...);
+
+/* Dump block of bytes to a file. */
+void dumpfile(const char *filename, void *, size_t);
+
+/* Extracts named reference file to the current directory. */
+void extract_reference_file(const char *);
+/* Copies named reference file to the current directory. */
+void copy_reference_file(const char *);
+
+/* Extracts a list of files to the current directory.
+ * List must be NULL terminated.
+ */
+void extract_reference_files(const char **);
+
+/* Subtract umask from mode */
+mode_t umasked(mode_t expected_mode);
+
+/* Path to working directory for current test */
+extern const char *testworkdir;
+
+#ifndef PROGRAM
+/*
+ * Special interfaces for libarchive test harness.
+ */
+
+#include "archive.h"
+#include "archive_entry.h"
+
+/* ACL structure */
+struct archive_test_acl_t {
+	int type;  /* Type of ACL */
+	int permset; /* Permissions for this class of users. */
+	int tag; /* Owner, User, Owning group, group, other, etc. */
+	int qual; /* GID or UID of user/group, depending on tag. */
+	const char *name; /* Name of user/group, depending on tag. */
+};
+
+/* Set ACLs */
+int assertion_entry_set_acls(const char *, int, struct archive_entry *,
+    struct archive_test_acl_t *, int);
+
+/* Compare ACLs */
+int assertion_entry_compare_acls(const char *, int, struct archive_entry *,
+    struct archive_test_acl_t *, int, int, int);
+
+/* Special customized read-from-memory interface. */
+int read_open_memory(struct archive *, const void *, size_t, size_t);
+/* _minimal version exercises a slightly different set of libarchive APIs. */
+int read_open_memory_minimal(struct archive *, const void *, size_t, size_t);
+/* _seek version produces a seekable file. */
+int read_open_memory_seek(struct archive *, const void *, size_t, size_t);
+
+/* Versions of above that accept an archive argument for additional info. */
+#define assertA(e)   assertion_assert(__FILE__, __LINE__, (e), #e, (a))
+#define assertEqualIntA(a,v1,v2)   \
+  assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+#define assertEqualStringA(a,v1,v2)   \
+  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a), 0)
+
+#else	/* defined(PROGRAM) */
+/*
+ * Special interfaces for program test harness.
+ */
+
+/* Pathname of exe to be tested. */
+extern const char *testprogfile;
+/* Name of exe to use in printf-formatted command strings. */
+/* On Windows, this includes leading/trailing quotes. */
+extern const char *testprog;
+
+void assertVersion(const char *prog, const char *base);
+
+#endif	/* defined(PROGRAM) */
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+#endif	/* TEST_COMMON_H */
diff --git a/cat/test/main.c b/test_utils/test_main.c
similarity index 80%
rename from cat/test/main.c
rename to test_utils/test_main.c
index cdfdd9b..86ab5f1 100644
--- a/cat/test/main.c
+++ b/test_utils/test_main.c
@@ -45,6 +45,9 @@
 #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
 #include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
 #endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
 #include <limits.h>
 #include <locale.h>
 #ifdef HAVE_SIGNAL_H
@@ -53,22 +56,19 @@
 #include <stdarg.h>
 #include <time.h>
 
-/*
- * This same file is used pretty much verbatim for all test harnesses.
- *
- * The next few lines are the only differences.
- * TODO: Move this into a separate configuration header, have all test
- * suites share one copy of this file.
- */
-#define KNOWNREF       "test_expand.Z.uu"
-#define ENVBASE "BSDCAT"  /* Prefix for environment variables. */
-#define	PROGRAM "bsdcat"  /* Name of program being tested. */
-#define PROGRAM_ALIAS "cat" /* Generic alias for program */
-#undef	LIBRARY		  /* Not testing a library. */
-#undef	EXTRA_DUMP	  /* How to dump extra data */
-#undef	EXTRA_ERRNO	  /* How to dump errno */
-/* How to generate extra version info. */
-#define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
+/* ACL support */
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#if HAVE_DARWIN_ACL
+#include <membership.h>
+#endif
 
 /*
  *
@@ -217,6 +217,12 @@
     unsigned int line, uintptr_t pReserved)
 {
 	/* nop */
+	// Silence unused-parameter compiler warnings.
+	(void)expression;
+	(void)function;
+	(void)file;
+	(void)line;
+	(void)pReserved;
 }
 #endif
 
@@ -1413,6 +1419,8 @@
 	failure_start(file, line, "assertFileMode not yet implemented for Windows");
 	(void)mode; /* UNUSED */
 	(void)r; /* UNUSED */
+	(void)pathname; /* UNUSED */
+	(void)expected_mode; /* UNUSED */
 #else
 	{
 		struct stat st;
@@ -1889,9 +1897,103 @@
 #endif /* defined(_WIN32) && !defined(__CYGWIN__) */
 }
 
+/* Compare file flags */
+int
+assertion_compare_fflags(const char *file, int line, const char *patha,
+    const char *pathb, int nomatch)
+{
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+	struct stat sa, sb;
+
+	assertion_count(file, line);
+
+	if (stat(patha, &sa) < 0)
+		return (0);
+	if (stat(pathb, &sb) < 0)
+		return (0);
+	if (!nomatch && sa.st_flags != sb.st_flags) {
+		failure_start(file, line, "File flags should be identical: "
+		    "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb,
+		    sb.st_flags);
+		failure_finish(NULL);
+		return (0);
+	}
+	if (nomatch && sa.st_flags == sb.st_flags) {
+		failure_start(file, line, "File flags should be different: "
+		    "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb,
+		    sb.st_flags);
+		failure_finish(NULL);
+		return (0);
+	}
+#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \
+       defined(FS_NODUMP_FL)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \
+         && defined(EXT2_NODUMP_FL))
+	int fd, r, flagsa, flagsb;
+
+	assertion_count(file, line);
+	fd = open(patha, O_RDONLY | O_NONBLOCK);
+	if (fd < 0) {
+		failure_start(file, line, "Can't open %s\n", patha);
+		failure_finish(NULL);
+		return (0);
+	}
+	r = ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &flagsa);
+	close(fd);
+	if (r < 0) {
+		failure_start(file, line, "Can't get flags %s\n", patha);
+		failure_finish(NULL);
+		return (0);
+	}
+	fd = open(pathb, O_RDONLY | O_NONBLOCK);
+	if (fd < 0) {
+		failure_start(file, line, "Can't open %s\n", pathb);
+		failure_finish(NULL);
+		return (0);
+	}
+	r = ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &flagsb);
+	close(fd);
+	if (r < 0) {
+		failure_start(file, line, "Can't get flags %s\n", pathb);
+		failure_finish(NULL);
+		return (0);
+	}
+	if (!nomatch && flagsa != flagsb) {
+		failure_start(file, line, "File flags should be identical: "
+		    "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb);
+		failure_finish(NULL);
+		return (0);
+	}
+	if (nomatch && flagsa == flagsb) {
+		failure_start(file, line, "File flags should be different: "
+		    "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb);
+		failure_finish(NULL);
+		return (0);
+	}
+#else
+	(void)patha; /* UNUSED */
+	(void)pathb; /* UNUSED */
+	(void)nomatch; /* UNUSED */
+	assertion_count(file, line);
+#endif
+	return (1);
+}
+
 /* Set nodump, report failures. */
 int
-assertion_nodump(const char *file, int line, const char *pathname)
+assertion_set_nodump(const char *file, int line, const char *pathname)
 {
 #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
 	int r;
@@ -1903,8 +2005,10 @@
 		failure_finish(NULL);
 		return (0);
 	}
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
+#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \
+       defined(FS_NODUMP_FL)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \
+	 && defined(EXT2_NODUMP_FL))
 	int fd, r, flags;
 
 	assertion_count(file, line);
@@ -1914,14 +2018,31 @@
 		failure_finish(NULL);
 		return (0);
 	}
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
+	r = ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &flags);
 	if (r < 0) {
 		failure_start(file, line, "Can't get flags %s\n", pathname);
 		failure_finish(NULL);
 		return (0);
 	}
+#ifdef FS_NODUMP_FL
+	flags |= FS_NODUMP_FL;
+#else
 	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
+#endif
+
+	 r = ioctl(fd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	    &flags);
 	if (r < 0) {
 		failure_start(file, line, "Can't set nodump %s\n", pathname);
 		failure_finish(NULL);
@@ -1935,6 +2056,117 @@
 	return (1);
 }
 
+#ifdef PROGRAM
+static void assert_version_id(char **qq, size_t *ss)
+{
+	char *q = *qq;
+	size_t s = *ss;
+
+	/* Version number is a series of digits and periods. */
+	while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) {
+		++q;
+		--s;
+	}
+
+	if (q[0] == 'd' && q[1] == 'e' && q[2] == 'v') {
+		q += 3;
+		s -= 3;
+	}
+	
+	/* Skip a single trailing a,b,c, or d. */
+	if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd')
+		++q;
+
+	/* Version number terminated by space. */
+	failure("No space after version: ``%s''", q);
+	assert(s > 1);
+	failure("No space after version: ``%s''", q);
+	assert(*q == ' ');
+
+	++q; --s;
+
+	*qq = q;
+	*ss = s;
+}
+
+
+/*
+ * Check program version
+ */
+void assertVersion(const char *prog, const char *base)
+{
+	int r;
+	char *p, *q;
+	size_t s;
+	unsigned int prog_len = strlen(base);
+
+	r = systemf("%s --version >version.stdout 2>version.stderr", prog);
+	if (r != 0)
+		r = systemf("%s -W version >version.stdout 2>version.stderr",
+		    prog);
+
+	failure("Unable to run either %s --version or %s -W version",
+		prog, prog);
+	if (!assert(r == 0))
+		return;
+
+	/* --version should generate nothing to stdout. */
+	assertEmptyFile("version.stderr");
+
+	/* Verify format of version message. */
+	q = p = slurpfile(&s, "version.stdout");
+
+	/* Version message should start with name of program, then space. */
+	assert(s > prog_len + 1);
+	
+	failure("Version must start with '%s': ``%s''", base, p);
+	if (!assertEqualMem(q, base, prog_len)) {
+		free(p);
+		return;
+	}
+
+	q += prog_len; s -= prog_len;
+
+	assert(*q == ' ');
+	q++; s--;
+
+	assert_version_id(&q, &s);
+
+	/* Separator. */
+	failure("No `-' between program name and versions: ``%s''", p);
+	assertEqualMem(q, "- ", 2);
+	q += 2; s -= 2;
+
+	failure("Not long enough for libarchive version: ``%s''", p);
+	assert(s > 11);
+
+	failure("Libarchive version must start with `libarchive': ``%s''", p);
+	assertEqualMem(q, "libarchive ", 11);
+
+	q += 11; s -= 11;
+
+	assert_version_id(&q, &s);
+
+	/* Skip arbitrary third-party version numbers. */
+	while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' ||
+	    isalnum(*q))) {
+		++q;
+		--s;
+	}
+
+	/* All terminated by end-of-line. */
+	assert(s >= 1);
+
+	/* Skip an optional CR character (e.g., Windows) */
+	failure("Version output must end with \\n or \\r\\n");
+
+	if (*q == '\r') { ++q; --s; }
+	assertEqualMem(q, "\n", 1);
+
+	free(p);
+}
+#endif	/* PROGRAM */
+
 /*
  *
  *  UTILITIES for use by tests.
@@ -2132,11 +2364,10 @@
 /*
  * Can this filesystem handle nodump flags.
  */
-#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
-
 int
 canNodump(void)
 {
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
 	const char *path = "cannodumptest";
 	struct stat sb;
 
@@ -2147,15 +2378,10 @@
 		return (0);
 	if (sb.st_flags & UF_NODUMP)
 		return (1);
-	return (0);
-}
-
-#elif defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)\
-	 && defined(EXT2_NODUMP_FL)
-
-int
-canNodump(void)
-{
+#elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) \
+	 && defined(FS_NODUMP_FL)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \
+	 && defined(EXT2_NODUMP_FL))
 	const char *path = "cannodumptest";
 	int fd, r, flags;
 
@@ -2163,35 +2389,273 @@
 	fd = open(path, O_RDONLY | O_NONBLOCK);
 	if (fd < 0)
 		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
+	r = ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &flags);
 	if (r < 0)
 		return (0);
+#ifdef FS_NODUMP_FL
+	flags |= FS_NODUMP_FL;
+#else
 	flags |= EXT2_NODUMP_FL;
-	r = ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
+#endif
+	r = ioctl(fd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	   &flags);
 	if (r < 0)
 		return (0);
 	close(fd);
 	fd = open(path, O_RDONLY | O_NONBLOCK);
 	if (fd < 0)
 		return (0);
-	r = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
+	r = ioctl(fd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &flags);
 	if (r < 0)
 		return (0);
 	close(fd);
-	if (flags & EXT2_NODUMP_FL)
-		return (1);
-	return (0);
-}
-
+#ifdef FS_NODUMP_FL
+	if (flags & FS_NODUMP_FL)
 #else
-
-int
-canNodump()
-{
+	if (flags & EXT2_NODUMP_FL)
+#endif
+		return (1);
+#endif
 	return (0);
 }
 
+#if HAVE_SUN_ACL
+/* Fetch ACLs on Solaris using acl() or facl() */
+void *
+sunacl_get(int cmd, int *aclcnt, int fd, const char *path)
+{
+	int cnt, cntcmd;
+	size_t size;
+	void *aclp;
+
+	if (cmd == GETACL) {
+		cntcmd = GETACLCNT;
+		size = sizeof(aclent_t);
+	}
+#if HAVE_SUN_NFS4_ACL
+	else if (cmd == ACE_GETACL) {
+		cntcmd = ACE_GETACLCNT;
+		size = sizeof(ace_t);
+	}
 #endif
+	else {
+		errno = EINVAL;
+		*aclcnt = -1;
+		return (NULL);
+	}
+
+	aclp = NULL;
+	cnt = -2;
+	while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) {
+		if (path != NULL)
+			cnt = acl(path, cntcmd, 0, NULL);
+		else
+			cnt = facl(fd, cntcmd, 0, NULL);
+
+		if (cnt > 0) {
+			if (aclp == NULL)
+				aclp = malloc(cnt * size);
+			else
+				aclp = realloc(NULL, cnt * size);
+			if (aclp != NULL) {
+				if (path != NULL)
+					cnt = acl(path, cmd, cnt, aclp);
+				else
+					cnt = facl(fd, cmd, cnt, aclp);
+			}
+		} else {
+			if (aclp != NULL) {
+				free(aclp);
+				aclp = NULL;
+			}
+			break;
+		}
+	}
+
+	*aclcnt = cnt;
+	return (aclp);
+}
+#endif /* HAVE_SUN_ACL */
+
+/*
+ * Set test ACLs on a path
+ * Return values:
+ * 0: error setting ACLs
+ * ARCHIVE_TEST_ACL_TYPE_POSIX1E: POSIX.1E ACLs have been set
+ * ARCHIVE_TEST_ACL_TYPE_NFS4: NFSv4 or extended ACLs have been set
+ */
+int
+setTestAcl(const char *path)
+{
+#if HAVE_POSIX_ACL || HAVE_NFS4_ACL
+	int r = 1;
+#if !HAVE_SUN_ACL
+	acl_t acl;
+#endif
+#if HAVE_POSIX_ACL /* Linux, FreeBSD POSIX.1e */
+	const char *acltext_posix1e = "user:1:rw-,"
+	    "group:15:r-x,"
+	    "user::rwx,"
+	    "group::rwx,"
+	    "other::r-x,"
+	    "mask::rwx";
+#elif HAVE_SUN_ACL /* Solaris POSIX.1e */
+	aclent_t aclp_posix1e[] = {
+	    { USER_OBJ, -1, 4 | 2 | 1 },
+	    { USER, 1, 4 | 2 },
+	    { GROUP_OBJ, -1, 4 | 2 | 1 },
+	    { GROUP, 15, 4 | 1 },
+	    { CLASS_OBJ, -1, 4 | 2 | 1 },
+	    { OTHER_OBJ, -1, 4 | 2 | 1 }
+	};
+#endif
+#if HAVE_FREEBSD_NFS4_ACL /* FreeBSD NFS4 */
+	const char *acltext_nfs4 = "user:1:rwpaRcs::allow:1,"
+	    "group:15:rxaRcs::allow:15,"
+	    "owner@:rwpxaARWcCos::allow,"
+	    "group@:rwpxaRcs::allow,"
+	    "everyone@:rxaRcs::allow";
+#elif HAVE_SUN_NFS4_ACL /* Solaris NFS4 */
+	ace_t aclp_nfs4[] = {
+	    { 1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA |
+	      ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL |
+	      ACE_SYNCHRONIZE, 0, ACE_ACCESS_ALLOWED_ACE_TYPE },
+	    { 15, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES |
+	      ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE,
+	      ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE },
+	    { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA |
+	      ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_WRITE_ATTRIBUTES |
+	      ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS |
+	      ACE_READ_ACL | ACE_WRITE_ACL | ACE_WRITE_OWNER | ACE_SYNCHRONIZE,
+	      ACE_OWNER, ACE_ACCESS_ALLOWED_ACE_TYPE },
+	    { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA |
+	      ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+	      ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_GROUP | ACE_IDENTIFIER_GROUP,
+	      ACE_ACCESS_ALLOWED_ACE_TYPE },
+	    { -1, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES |
+	      ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE,
+	      ACE_EVERYONE, ACE_ACCESS_ALLOWED_ACE_TYPE }
+	};
+#elif HAVE_DARWIN_ACL /* Mac OS X */
+	acl_entry_t aclent;
+	acl_permset_t permset;
+	const uid_t uid = 1;
+	uuid_t uuid;
+	int i;
+	const acl_perm_t acl_perms[] = {
+		ACL_READ_DATA,
+		ACL_WRITE_DATA,
+		ACL_APPEND_DATA,
+		ACL_EXECUTE,
+		ACL_READ_ATTRIBUTES,
+		ACL_READ_EXTATTRIBUTES,
+		ACL_READ_SECURITY,
+#if HAVE_DECL_ACL_SYNCHRONIZE
+		ACL_SYNCHRONIZE
+#endif
+	};
+#endif /* HAVE_DARWIN_ACL */
+
+#if HAVE_FREEBSD_NFS4_ACL
+	acl = acl_from_text(acltext_nfs4);
+	failure("acl_from_text() error: %s", strerror(errno));
+	if (assert(acl != NULL) == 0)
+		return (0);
+#elif HAVE_DARWIN_ACL
+	acl = acl_init(1);
+	failure("acl_init() error: %s", strerror(errno));
+	if (assert(acl != NULL) == 0)
+		return (0);
+	r = acl_create_entry(&acl, &aclent);
+	failure("acl_create_entry() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+	r = acl_set_tag_type(aclent, ACL_EXTENDED_ALLOW);
+	failure("acl_set_tag_type() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+	r = acl_get_permset(aclent, &permset);
+	failure("acl_get_permset() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+	for (i = 0; i < (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); i++) {
+		r = acl_add_perm(permset, acl_perms[i]);
+		failure("acl_add_perm() error: %s", strerror(errno));
+		if (assertEqualInt(r, 0) == 0)
+			goto testacl_free;
+	}
+	r = acl_set_permset(aclent, permset);
+	failure("acl_set_permset() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+	r = mbr_identifier_to_uuid(ID_TYPE_UID, &uid, sizeof(uid_t), uuid);
+	failure("mbr_identifier_to_uuid() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+	r = acl_set_qualifier(aclent, uuid);
+	failure("acl_set_qualifier() error: %s", strerror(errno));
+	if (assertEqualInt(r, 0) == 0)
+		goto testacl_free;
+#endif /* HAVE_DARWIN_ACL */
+
+#if HAVE_NFS4_ACL
+#if HAVE_FREEBSD_NFS4_ACL
+	r = acl_set_file(path, ACL_TYPE_NFS4, acl);
+	acl_free(acl);
+#elif HAVE_SUN_NFS4_ACL
+	r = acl(path, ACE_SETACL,
+	    (int)(sizeof(aclp_nfs4)/sizeof(aclp_nfs4[0])), aclp_nfs4);
+#elif HAVE_DARWIN_ACL
+	r = acl_set_file(path, ACL_TYPE_EXTENDED, acl);
+	acl_free(acl);
+#endif
+	if (r == 0)
+		return (ARCHIVE_TEST_ACL_TYPE_NFS4);
+#endif	/* HAVE_NFS4_ACL */
+
+#if HAVE_POSIX_ACL || HAVE_SUN_ACL
+#if HAVE_POSIX_ACL
+	acl = acl_from_text(acltext_posix1e);
+	failure("acl_from_text() error: %s", strerror(errno));
+	if (assert(acl != NULL) == 0)
+		return (0);
+
+	r = acl_set_file(path, ACL_TYPE_ACCESS, acl);
+	acl_free(acl);
+#elif HAVE_SUN_ACL
+	r = acl(path, SETACL,
+	    (int)(sizeof(aclp_posix1e)/sizeof(aclp_posix1e[0])), aclp_posix1e);
+#endif
+	if (r == 0)
+		return (ARCHIVE_TEST_ACL_TYPE_POSIX1E);
+	else
+		return (0);
+#endif /* HAVE_POSIX_ACL || HAVE_SUN_ACL */
+#if HAVE_DARWIN_ACL
+testacl_free:
+	acl_free(acl);
+#endif
+#endif /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
+	(void)path;	/* UNUSED */
+	return (0);
+}
 
 /*
  * Sleep as needed; useful for verifying disk timestamp changes by
@@ -2422,6 +2886,190 @@
 		extract_reference_file(*names++);
 }
 
+#ifndef PROGRAM
+/* Set ACLs */
+int
+assertion_entry_set_acls(const char *file, int line, struct archive_entry *ae,
+    struct archive_test_acl_t *acls, int n)
+{
+	int i, r, ret;
+
+	assertion_count(file, line);
+
+	ret = 0;
+	archive_entry_acl_clear(ae);
+	for (i = 0; i < n; i++) {
+		r = archive_entry_acl_add_entry(ae,
+		    acls[i].type, acls[i].permset, acls[i].tag,
+		    acls[i].qual, acls[i].name);
+		if (r != 0) {
+			ret = 1;
+			failure_start(file, line, "type=%#010x, ",
+			    "permset=%#010x, tag=%d, qual=%d name=%s",
+			    acls[i].type, acls[i].permset, acls[i].tag,
+			    acls[i].qual, acls[i].name);
+			failure_finish(NULL);
+		}
+	}
+
+	return (ret);
+}
+
+static int
+archive_test_acl_match(struct archive_test_acl_t *acl, int type, int permset,
+    int tag, int qual, const char *name)
+{
+	if (type != acl->type)
+		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_EVERYONE)
+		return (1);
+	if (tag == ARCHIVE_ENTRY_ACL_OTHER)
+		return (1);
+	if (qual != acl->qual)
+		return (0);
+	if (name == NULL) {
+		if (acl->name == NULL || acl->name[0] == '\0')
+			return (1);
+		return (0);
+	}
+	if (acl->name == NULL) {
+		if (name[0] == '\0')
+			return (1);
+		return (0);
+	}
+	return (0 == strcmp(name, acl->name));
+}
+
+/* Compare ACLs */
+int
+assertion_entry_compare_acls(const char *file, int line,
+    struct archive_entry *ae, struct archive_test_acl_t *acls, int cnt,
+    int want_type, int mode)
+{
+	int *marker;
+	int i, r, n, ret;
+	int type, permset, tag, qual;
+	int matched;
+	const char *name;
+
+	assertion_count(file, line);
+
+	ret = 0;
+	n = 0;
+	marker = malloc(sizeof(marker[0]) * cnt);
+
+	for (i = 0; i < cnt; i++) {
+		if ((acls[i].type & want_type) != 0) {
+			marker[n] = i;
+			n++;
+		}
+	}
+
+	if (n == 0) {
+		failure_start(file, line, "No ACL's to compare, type mask: %d",
+		    want_type);
+		return (1);
+	}
+
+	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 (archive_test_acl_match(&acls[marker[i]], type,
+			    permset, tag, qual, name)) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+		if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+		    && tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
+			if (!matched) {
+				failure_start(file, line, "No match for "
+				    "user_obj perm");
+				failure_finish(NULL);
+				ret = 1;
+			}
+			if ((permset << 6) != (mode & 0700)) {
+				failure_start(file, line, "USER_OBJ permset "
+				    "(%02o) != user mode (%02o)", permset,
+				    07 & (mode >> 6));
+				failure_finish(NULL);
+				ret = 1;
+			}
+		} else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+		    && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
+			if (!matched) {
+				failure_start(file, line, "No match for "
+				    "group_obj perm");
+				failure_finish(NULL);
+				ret = 1;
+			}
+			if ((permset << 3) != (mode & 0070)) {
+				failure_start(file, line, "GROUP_OBJ permset "
+				    "(%02o) != group mode (%02o)", permset,
+				    07 & (mode >> 3));
+				failure_finish(NULL);
+				ret = 1;
+			}
+		} else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+		    && tag == ARCHIVE_ENTRY_ACL_OTHER) {
+			if (!matched) {
+				failure_start(file, line, "No match for "
+				    "other perm");
+				failure_finish(NULL);
+				ret = 1;
+			}
+			if ((permset << 0) != (mode & 0007)) {
+				failure_start(file, line, "OTHER permset "
+				    "(%02o) != other mode (%02o)", permset,
+				    mode & 07);
+				failure_finish(NULL);
+				ret = 1;
+			}
+		} else if (matched != 1) {
+			failure_start(file, line, "Could not find match for "
+			    "ACL (type=%#010x,permset=%#010x,tag=%d,qual=%d,"
+			    "name=``%s'')", type, permset, tag, qual, name);
+			failure_finish(NULL);
+			ret = 1;
+		}
+	}
+	if (r != ARCHIVE_EOF) {
+		failure_start(file, line, "Should not exit before EOF");
+		failure_finish(NULL);
+		ret = 1;
+	}
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
+	    (mode_t)(mode & 0777) != (archive_entry_mode(ae) & 0777)) {
+		failure_start(file, line, "Mode (%02o) and entry mode (%02o) "
+		    "mismatch", mode, archive_entry_mode(ae));
+		failure_finish(NULL);
+		ret = 1;
+	}
+	if (n != 0) {
+		failure_start(file, line, "Could not find match for ACL "
+		    "(type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s'')",
+		    acls[marker[0]].type, acls[marker[0]].permset,
+		    acls[marker[0]].tag, acls[marker[0]].qual,
+		    acls[marker[0]].name);
+		failure_finish(NULL);
+		ret = 1;
+		/* Number of ACLs not matched should == 0 */
+	}
+	free(marker);
+	return (ret);
+}
+#endif	/* !defined(PROGRAM) */
+
 /*
  *
  * TEST management