Add POSIX.1e and NFSv4 ACL support for Solaris and derivates
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fa651eb..14649e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1609,6 +1609,21 @@
   # 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)
+
+  # Solaris and derivates ACLs
+  CHECK_LIBRARY_EXISTS(sec "acl_get" "" HAVE_LIBSEC)
+  IF(HAVE_LIBSEC)
+    SET(CMAKE_REQUIRED_LIBRARIES "sec")
+    FIND_LIBRARY(SEC_LIBRARY NAMES sec)
+    LIST(APPEND ADDITIONAL_LIBS ${SEC_LIBRARY})
+  ENDIF(HAVE_LIBSEC)
+  #
+  CHECK_TYPE_EXISTS(aclent_t "${INCLUDES}" HAVE_ACLENT_T)
+  CHECK_TYPE_EXISTS(ace_t "${INCLUDES}" HAVE_ACE_T)
+  CHECK_FUNCTION_EXISTS(acl_get HAVE_FACL_GET)
+  CHECK_FUNCTION_EXISTS(facl_get HAVE_FACL_GET)
+  CHECK_FUNCTION_EXISTS(acl_set HAVE_FACL_SET)
+  CHECK_FUNCTION_EXISTS(facl_set HAVE_FACL_SET)
 ELSE(ENABLE_ACL)
   # If someone runs cmake, then disables ACL support, we need
   # to forcibly override the cached values for these.
@@ -1624,6 +1639,11 @@
   SET(HAVE_ACL_SET_FD_NP FALSE)
   SET(HAVE_ACL_SET_FILE FALSE)
   SET(HAVE_ACL_USER FALSE)
+  SET(HAVE_ACL_GET FALSE)
+  SET(HAVE_FACL_GET FALSE)
+  SET(HAVE_ACL_SET FALSE)
+  SET(HAVE_FACL_SET FALSE)
+  SET(HAVE_ACL_TRIVIAL FALSE)
 ENDIF(ENABLE_ACL)
 
 #
diff --git a/Makefile.am b/Makefile.am
index f92d723..318c7ac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -323,11 +323,13 @@
 	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_freebsd_posix1e.c \
 	libarchive/test/test_acl_nfs4.c \
 	libarchive/test/test_acl_pax.c \
 	libarchive/test/test_acl_posix1e.c \
+	libarchive/test/test_acl_solaris_nfs4.c \
+	libarchive/test/test_acl_solaris_posix1e.c \
 	libarchive/test/test_acl_text.c \
 	libarchive/test/test_archive_api_feature.c \
 	libarchive/test/test_archive_clear_error.c \
diff --git a/NEWS b/NEWS
index 4d875b2..3dffdf9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,5 @@
+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()
 
diff --git a/configure.ac b/configure.ac
index ffefa6c..f97ef85 100644
--- a/configure.ac
+++ b/configure.ac
@@ -730,6 +730,12 @@
 		[AC_DEFINE(HAVE_ACL_USER, 1, [True for systems with POSIX ACL support])],
 		[],
 		[#include <sys/acl.h>])
+
+    # Solaris and derivates ACLs
+    AC_CHECK_LIB([sec], [acl_get])
+    AC_CHECK_TYPES([aclent_t], [], [], [[#include <sys/acl.h>]])
+    AC_CHECK_TYPES([ace_t], [], [], [[#include <sys/acl.h>]])
+    AC_CHECK_FUNCS(acl_get facl_get acl_set facl_set)
 fi
 
 # Additional requirements
diff --git a/libarchive/archive_platform.h b/libarchive/archive_platform.h
index e44c932..eb9d4bb 100644
--- a/libarchive/archive_platform.h
+++ b/libarchive/archive_platform.h
@@ -152,6 +152,14 @@
 #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_GET && HAVE_FACL_GET && HAVE_ACL_SET && HAVE_FACL_SET && HAVE_ACLENT_T && HAVE_ACE_T
+#define	HAVE_SUN_ACL	1
+#endif
+
+/*
  * If we can't restore metadata using a file descriptor, then
  * for compatibility's sake, close files before trying to restore metadata.
  */
diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c
index ce576e0..e8bf07e 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -405,17 +405,26 @@
 }
 #endif
 
-
-#ifdef HAVE_POSIX_ACL
+#if HAVE_POSIX_ACL || HAVE_SUN_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
+    acl_t *acl,
+#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
+	acl_t		*acl;
+#else
+	acl_t		acl;
+#endif
 	int		r;
 
 	accpath = archive_entry_sourcepath(entry);
@@ -440,10 +449,12 @@
 
 	acl = NULL;
 
-#ifdef ACL_TYPE_NFS4
+#if defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL
 	/* Try NFSv4 ACL first. */
 	if (*fd >= 0)
-#if HAVE_ACL_GET_FD_NP
+#if HAVE_SUN_ACL
+		facl_get(*fd, 0, &acl);
+#elif HAVE_ACL_GET_FD_NP
 		acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
 #else
 		acl = acl_get_fd(*fd);
@@ -459,12 +470,24 @@
 		acl = NULL;
 #endif
 	else
+#if HAVE_SUN_ACL
+		acl_get(accpath, 0, &acl);
+#else
 		acl = acl_get_file(accpath, 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) {
+
+#if HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (acl != NULL) {
+#if HAVE_SUN_ACL
+		/* TODO: Check if ace_t is trivial like acl_is_trivial_np()  */
+		if (acl->acl_type == ACLENT_T &&
+			(acl->acl_flags & ACL_IS_TRIVIAL) != 0)
+#elif HAVE_ACL_IS_TRIVIAL_NP
+		if (acl_is_trivial_np(acl, &r) == 0 && r == 1)
+#endif
+		{
 			acl_free(acl);
 			acl = NULL;
 			/*
@@ -474,18 +497,23 @@
 			return (ARCHIVE_OK);
 		}
 	}
-#endif
+#endif	/* HAVE_ACL_IS_TRIVIAL_NP || HAVE_SUN_ACL */
 	if (acl != NULL) {
 		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
 		acl_free(acl);
 		if (r != ARCHIVE_OK) {
 			archive_set_error(&a->archive, errno,
+#if HAVE_SUN_ACL
+			    "Couldn't translate ACLs: %s", accpath);
+#else
 			    "Couldn't translate NFSv4 ACLs: %s", accpath);
+#endif
 		}
 		return (r);
 	}
-#endif	/* ACL_TYPE_NFS4 */
+#endif	/* defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL */
 
+#if !HAVE_SUN_ACL
 	/* Retrieve access ACL from file. */
 	if (*fd >= 0)
 		acl = acl_get_fd(*fd);
@@ -513,8 +541,7 @@
 #endif
 
 	if (acl != NULL) {
-		r = translate_acl(a, entry, acl,
-		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
 		acl_free(acl);
 		acl = NULL;
 		if (r != ARCHIVE_OK) {
@@ -544,14 +571,54 @@
 			}
 		}
 	}
+#endif	/* !HAVE_SUN_ACL */
 	return (ARCHIVE_OK);
 }
 
+#if HAVE_SUN_ACL
 /*
- * Translate system ACL into libarchive internal structure.
+ * Translate Solaris system NFSv4 ACL into libarchive internal structure.
  */
+static struct {
+        int archive_perm;
+        int platform_perm;
+} acl_perm_map[] = {
+        {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}
+};
 
 static struct {
+        int archive_inherit;
+        int platform_inherit;
+} acl_inherit_map[] = {
+        {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},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+};
+#else	/* !HAVE_SUN_ACL */
+/*
+ * Translate FreeBSD system ACL into libarchive internal structure.
+ */
+static struct {
         int archive_perm;
         int platform_perm;
 } acl_perm_map[] = {
@@ -592,6 +659,139 @@
 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
 };
 #endif
+#endif	/* !HAVE_SUN_ACL */
+
+#if HAVE_SUN_ACL
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t *acl, 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;
+	ace_t *ace;
+
+	(void)default_entry_acl_type;
+
+	if (acl->acl_cnt <= 0)
+		return (ARCHIVE_OK);
+
+	for (e = 0; e < acl->acl_cnt; e++) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+
+		if (acl->acl_type == ACE_T) {
+			ace = &((ace_t *)acl->acl_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 {
+			aclent = &((aclent_t *)acl->acl_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;
+		} /* default_entry_acl_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4 */
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+#else	/* !HAVE_SUN_ACL */
 static int
 translate_acl(struct archive_read_disk *a,
     struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
@@ -644,7 +844,6 @@
 	}
 #endif
 
-
 	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
 	if (s == -1) {
 		archive_set_error(&a->archive, errno,
@@ -783,7 +982,8 @@
 	}
 	return (ARCHIVE_OK);
 }
-#else
+#endif	/* !HAVE_SUN_ACL */
+#else	/* !HAVE_POSIX_ACL && !HAVE_SUN_ACL */
 static int
 setup_acls(struct archive_read_disk *a,
     struct archive_entry *entry, int *fd)
@@ -793,7 +993,7 @@
 	(void)fd;     /* UNUSED */
 	return (ARCHIVE_OK);
 }
-#endif
+#endif	/* !HAVE_POSIX_ACL && !HAVE_SUN_ACL */
 
 #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \
     HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \
diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c
index 706ab62..4a0112e 100644
--- a/libarchive/archive_write_disk_acl.c
+++ b/libarchive/archive_write_disk_acl.c
@@ -43,7 +43,7 @@
 #include "archive_acl_private.h"
 #include "archive_write_disk_private.h"
 
-#ifndef HAVE_POSIX_ACL
+#if !HAVE_POSIX_ACL && !HAVE_SUN_ACL
 /* Default empty function body to satisfy mainline code. */
 int
 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
@@ -56,7 +56,7 @@
 	return (ARCHIVE_OK);
 }
 
-#else
+#else /* HAVE_POSIX_ACL || HAVE_SUN_ACL */
 
 static int	set_acl(struct archive *, int fd, const char *,
 			struct archive_acl *,
@@ -71,24 +71,84 @@
 {
 	int		 ret;
 
-	if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) > 0) {
+	if (archive_acl_count(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, ACLENT_T,
+		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
+#else
 		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");
+#endif
 		return (ret);
-#ifdef ACL_TYPE_NFS4
-	} else if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) {
+	}
+#if defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL
+	else if (archive_acl_count(abstract_acl,
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) {
+#if HAVE_SUN_ACL
+		ret = set_acl(a, fd, name, abstract_acl, ACE_T,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+#else	/* !HAVE_SUN_ACL */
 		ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_NFS4,
 		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+#endif	/* !HAVE_SUN_ACL */
 		return (ret);
-#endif
-	} else
+	}
+#endif	/* defined(ACL_TYPE_NFS4) && HAVE_SUN_ACL */
+	else {
+		/* No ACLs found */
 		return ARCHIVE_OK;
+	}
 }
 
+#if HAVE_SUN_ACL
+/*
+ * Translate Solaris system NFSv4 ACL into libarchive internal structure.
+ */
+static struct {
+        int archive_perm;
+        int platform_perm;
+} acl_perm_map[] = {
+        {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}
+};
+
+static struct {
+        int archive_inherit;
+        int platform_inherit;
+} acl_inherit_map[] = {
+        {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_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},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+};
+#else	/* HAVE_SUN_ACL */
+/*
+ * Translate FreeBSD system ACL into libarchive internal structure.
+ */
 static struct {
 	int archive_perm;
 	int platform_perm;
@@ -130,15 +190,23 @@
 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
 };
 #endif
+#endif	/* HAVE_SUN_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
+	aclent_t	 *aclent;
+	ace_t		 *ace;
+	int		 e, r;
+	acl_t		 *acl;
+#else
 	acl_t		 acl;
 	acl_entry_t	 acl_entry;
 	acl_permset_t	 acl_permset;
+#endif
 #ifdef ACL_TYPE_NFS4
 	acl_flagset_t	 acl_flagset;
 	int		 r;
@@ -155,22 +223,114 @@
 	entries = archive_acl_reset(abstract_acl, ae_requested_type);
 	if (entries == 0)
 		return (ARCHIVE_OK);
+
+#if HAVE_SUN_ACL
+	acl = NULL;
+	acl = malloc(sizeof(acl_t));
+	if (acl == NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			"Invalid ACL type");
+		return (ARCHIVE_FAILED);
+	}
+	if (acl_type == ACE_T)
+		acl->acl_entry_size = sizeof(ace_t);
+	else if (acl_type == ACLENT_T)
+		acl->acl_entry_size = sizeof(aclent_t);
+	else {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			"Invalid ACL type");
+		acl_free(acl);
+		return (ARCHIVE_FAILED);
+	}
+	acl->acl_type = acl_type;
+	acl->acl_cnt = entries;
+
+	acl->acl_aclp = malloc(entries * acl->acl_entry_size);
+	if (acl->acl_aclp == NULL) {
+		archive_set_error(a, errno,
+		    "Can't allocate memory for acl buffer");
+		acl_free(acl);
+		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
+		ace = NULL;
+		aclent = NULL;
+		if (acl->acl_type == ACE_T)  {
+			ace = &((ace_t *)acl->acl_aclp)[e];
+			ace->a_who = -1;
+			ace->a_access_mask = 0;
+			ace->a_flags = 0;
+		} else {
+			aclent = &((aclent_t *)acl->acl_aclp)[e];
+			aclent->a_id = -1;
+			aclent->a_type = 0;
+			aclent->a_perm = 0;
+		}
+#else	/* !HAVE_SUN_ACL */
 		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 */
 		switch (ae_tag) {
+#if HAVE_SUN_ACL
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			if (acl->acl_type == ACE_T)
+				ace->a_who = ae_uid;
+			else {
+				aclent->a_id = ae_uid;
+				aclent->a_type |= USER;
+			}
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			if (acl->acl_type == ACE_T) {
+				ace->a_who = ae_gid;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			} else {
+				aclent->a_id = ae_gid;
+				aclent->a_type |= GROUP;
+			}
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (acl->acl_type == ACE_T)
+				ace->a_flags |= ACE_OWNER;
+			else
+				aclent->a_type |= USER_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (acl->acl_type == ACE_T) {
+				ace->a_flags |= ACE_GROUP;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			} else
+				aclent->a_type |= GROUP_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			aclent->a_type |= CLASS_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			aclent->a_type |= OTHER_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			ace->a_flags |= ACE_EVERYONE;
+			break;
+#else	/* !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);
@@ -198,6 +358,7 @@
 			acl_set_tag_type(acl_entry, ACL_EVERYONE);
 			break;
 #endif
+#endif	/* !HAVE_SUN_ACL */
 		default:
 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
 			    "Unknown ACL tag");
@@ -205,9 +366,45 @@
 			goto exit_free;
 		}
 
-#ifdef ACL_TYPE_NFS4
+#if defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL
 		r = 0;
 		switch (ae_type) {
+#if HAVE_SUN_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;
+		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;
@@ -224,20 +421,35 @@
 		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");
 			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	/* defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL */
 
+#if HAVE_SUN_ACL
+		if (acl->acl_type == ACLENT_T) {
+			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;
+		} else
+#else
 		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
 			archive_set_error(a, errno,
 			    "Failed to get ACL permission set");
@@ -250,9 +462,13 @@
 			ret = ARCHIVE_FAILED;
 			goto exit_free;
 		}
-
+#endif	/* !HAVE_SUN_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,
@@ -260,10 +476,18 @@
 					ret = ARCHIVE_FAILED;
 					goto exit_free;
 				}
+#endif
+			}
 		}
 
-#ifdef ACL_TYPE_NFS4
-		if (acl_type == ACL_TYPE_NFS4) {
+#if defined(ACL_TYPE_NFS4) || HAVE_SUN_ACL
+#if HAVE_SUN_ACL
+		if (acl_type == ACE_T)
+#else
+		if (acl_type == ACL_TYPE_NFS4)
+#endif
+		{
+#ifdef HAVE_POSIX_ACL
 			/*
 			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
 			 */
@@ -279,8 +503,13 @@
 				ret = ARCHIVE_FAILED;
 				goto exit_free;
 			}
-			for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) {
+#endif
+			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,
@@ -288,19 +517,29 @@
 						ret = ARCHIVE_FAILED;
 						goto exit_free;
 					}
+#endif	/* HAVE_SUN_ACLi */
 				}
 			}
 		}
 #endif
+#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_NP || HAVE_ACL_SET_FD
-#if HAVE_ACL_SET_FD_NP
-	if (fd >= 0) {
+#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_set(fd, acl) == 0)
+#elif HAVE_ACL_SET_FD_NP
 		if (acl_set_fd_np(fd, acl, acl_type) == 0)
-#else /* HAVE_ACL_SET_FD */
-	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS) {
+#else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
 		if (acl_set_fd(fd, acl) == 0)
 #endif
 			ret = ARCHIVE_OK;
@@ -314,13 +553,16 @@
 			}
 		}
 	} else
-#endif	/* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD */
-#if HAVE_ACL_SET_LINK_NP
-	if (acl_set_link_np(name, acl_type, acl) != 0) {
+#endif	/* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */
+#if HAVE_SUN_ACL
+	if (acl_set(name, acl) != 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) {
+	if (acl_set_file(name, acl_type, acl) != 0)
 #endif
+	{
 		if (errno == EOPNOTSUPP) {
 			/* Filesystem doesn't support ACLs */
 			ret = ARCHIVE_OK;
@@ -334,4 +576,4 @@
 	acl_free(acl);
 	return (ret);
 }
-#endif
+#endif	/* HAVE_POSIX_ACL || HAVE_SUN_ACL */
diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
index 06b2763..9bf19b9 100644
--- a/libarchive/test/CMakeLists.txt
+++ b/libarchive/test/CMakeLists.txt
@@ -14,6 +14,8 @@
     test_acl_nfs4.c
     test_acl_pax.c
     test_acl_posix1e.c
+    test_acl_solaris_nfs4.c
+    test_acl_solaris_posix1e.c
     test_acl_text.c
     test_archive_api_feature.c
     test_archive_clear_error.c
diff --git a/libarchive/test/test_acl_solaris_nfs4.c b/libarchive/test/test_acl_solaris_nfs4.c
new file mode 100644
index 0000000..6580de4
--- /dev/null
+++ b/libarchive/test/test_acl_solaris_nfs4.c
@@ -0,0 +1,515 @@
+/*-
+ * 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"
+
+#if defined(__sun__)
+#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(uint32_t a_access_mask)
+{
+	static struct { int machine; int portable; } perms[] = {
+		{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}
+	};
+	int i, permset = 0;
+
+	for (i = 0; i < (int)(sizeof(perms)/sizeof(perms[0])); ++i)
+		if (a_access_mask & perms[i].machine)
+			permset |= perms[i].portable;
+	return permset;
+}
+
+static int
+acl_flagset_to_bitmap(uint16_t a_flags)
+{
+	static struct { int machine; int portable; } 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},
+		{ACE_INHERITED_ACE, ARCHIVE_ENTRY_ACL_ENTRY_INHERITED}
+	};
+	int i, flagset = 0;
+
+	for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); ++i)
+		if (a_flags & flags[i].machine)
+			flagset |= flags[i].portable;
+	return flagset;
+}
+
+static int
+acl_match(ace_t *ace, struct myacl_t *myacl)
+{
+	int perms;
+
+	/* translate the silly opaque permset to a bitmap */
+	perms = acl_permset_to_bitmap(ace->a_access_mask) | acl_flagset_to_bitmap(ace->a_flags);
+	if (perms != myacl->permset)
+		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);
+	}
+	return (1);
+}
+
+static void
+compare_acls(acl_t *acl, struct myacl_t *myacls, const char *filename, int start, int end)
+{
+	int *marker;
+	int matched;
+	int e, i, n;
+	ace_t *ace;
+
+	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.
+	 */
+	for (e = 0; e < acl->acl_cnt; e++) {
+		ace = &((ace_t *)acl->acl_aclp)[e];
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(ace, &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
+
+/*
+ * Verify ACL restore-to-disk.  This test is FreeBSD-specific.
+ */
+
+DEFINE_TEST(test_acl_solaris_nfs4)
+{
+#if !defined(__sun__)
+	skipping("Solaris-specific NFS4 ACL restore test");
+#else
+	char buff[64];
+	struct stat st;
+	struct archive *a;
+	struct archive_entry *ae;
+	int i, n;
+	acl_t *acl;
+
+	/* Create a test dir and try to get trivial ACL from it. */
+	if (!assertMakeDir("pretest", 0755))
+		return;
+
+	n = acl_get("pretest", 0, &acl);
+	if (n != 0 && errno == ENOSYS) {
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("acl_get(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	if (acl->acl_type != ACE_T) {
+		acl_free(acl);
+		skipping("Filesystem does not use NFSv4 ACLs");
+		return;
+	}
+
+	acl_free(acl);
+
+	/* 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);
+	n = acl_get("testall", 0, &acl);
+	failure("acl_get(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+	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);
+	  n = acl_get(buff, 0, &acl);
+	  failure("acl_get(): errno = %d (%s)", errno, strerror(errno));
+	  assertEqualInt(0, n);
+	  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);
+	n = acl_get("dirall", 0, &acl);
+	failure("acl_get(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+	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_solaris_posix1e.c b/libarchive/test/test_acl_solaris_posix1e.c
new file mode 100644
index 0000000..4f41221
--- /dev/null
+++ b/libarchive/test/test_acl_solaris_posix1e.c
@@ -0,0 +1,415 @@
+/*-
+ * 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"
+
+#if defined(__sun__)
+#include <sys/acl.h>
+
+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
+acl_entry_get_perm(aclent_t *aclent) {
+	int permset = 0;
+
+	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;
+
+	return permset;
+}
+
+static int
+acl_match(aclent_t *aclent, struct archive_test_acl_t *myacl)
+{
+	if (myacl->permset != acl_entry_get_perm(aclent))
+		return (0);
+
+	switch (aclent->a_type) {
+	case DEF_USER_OBJ:
+	case USER_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0);
+		break;
+	case DEF_USER:
+	case USER:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_USER)
+			return (0);
+		if ((uid_t)myacl->qual != aclent->a_id)
+			return (0);
+		break;
+	case DEF_GROUP_OBJ:
+	case GROUP_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0);
+		break;
+	case DEF_GROUP:
+	case GROUP:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP)
+			return (0);
+		if ((gid_t)myacl->qual != aclent->a_id)
+			return (0);
+		break;
+	case DEF_CLASS_OBJ:
+	case CLASS_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0);
+		break;
+	case DEF_OTHER_OBJ:
+	case OTHER_OBJ:
+		if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0);
+		break;
+	}
+	return (1);
+}
+
+static void
+compare_acls(acl_t *acl, struct archive_test_acl_t *myacls, int n)
+{
+	int *marker;
+	int matched;
+	int e, i;
+	aclent_t *aclent;
+
+	/* 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.
+	 */
+	for(e = 0; e < acl->acl_cnt; e++) {
+		aclent = &((aclent_t *)acl->acl_aclp)[e];
+
+		/* Search for a matching entry (tag and qualifier) */
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(aclent, &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 Solaris-specific.
+ */
+
+DEFINE_TEST(test_acl_solaris_posix1e_restore)
+{
+#if !defined(__sun__)
+	skipping("Solaris-specific ACL restore test");
+#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 = 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))
+		return;
+	n = facl_get(fd, 0, &acl);
+	if (n != 0)
+		close(fd);
+	if (n != 0 && errno == ENOSYS) {
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("facl_get(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	if (acl->acl_type != ACLENT_T) {
+		acl_free(acl);
+		close(fd);
+		skipping("Filesystem does not use POSIX.1e ACLs");
+		return;
+	}
+
+	acl_free(acl);
+
+	n = acl_fromtext("user::rwx,user:1:rw-,group::rwx,group:15:r-x,other:rwx,mask:rwx", &acl);
+	if (n != 0)
+		close(fd);
+	failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	n = facl_set(fd, acl);
+	acl_free(acl);
+	if (n != 0 && errno == ENOSYS) {
+		close(fd);
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("facl_set(): 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);
+	archive_test_set_acls(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);
+	n = acl_get("test0", 0, &acl);
+	failure("acl_get(): errno = %d (%s)",
+	    errno, strerror(errno));
+	assert(n == 0);
+	if (acl->acl_type != ACLENT_T) {
+		acl_free(acl);
+		skipping("Filesystem uses other than POSIX.1e ACLs");
+		return;
+	}
+	assertEqualInt(ACLENT_T, acl->acl_type);
+	compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	acl_free(acl);
+#endif
+}
+
+/*
+ * Verify ACL read-from-disk.  This test is FreeBSD-specific.
+ */
+DEFINE_TEST(test_acl_solaris_posix1e_read)
+{
+#if !defined(__sun__)
+	skipping("Solaris-specific ACL read test");
+#else
+	struct archive *a;
+	struct archive_entry *ae;
+	int n, fd;
+	const char *acl1_text, *acl2_text, *acl3_text;
+	acl_t *acl;
+
+	/*
+	 * 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_text */
+	fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
+	failure("Could not create test file?!");
+	if (!assert(fd >= 0))
+		return;
+	n = facl_get(fd, 0, &acl);
+	if (n != 0)
+		close(fd);
+	if (n != 0 && errno == ENOSYS) {
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("facl_get(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	if (acl->acl_type != ACLENT_T) {
+		acl_free(acl);
+		close(fd);
+		skipping("Filesystem does not use POSIX.1e ACLs");
+		return;
+	}
+
+	acl_free(acl);
+
+	acl1_text = "user::rwx,"
+	    "group::rwx,"
+	    "other:rwx,"
+	    "user:1:rw-,"
+	    "group:15:r-x,"
+	    "mask:rwx";
+	n = acl_fromtext(acl1_text, &acl);
+	failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	n = facl_set(fd, acl);
+	acl_free(acl);
+	if (n != 0 && errno == ENOSYS) {
+		close(fd);
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("facl_set(): errno = %d (%s)",
+	    errno, strerror(errno));
+	assertEqualInt(0, n);
+	close(fd);
+
+	assertMakeDir("d1", 0700);
+
+	/*
+	 * Create file d/f1 with acl2_text
+	 *
+	 * This differs from acl1_text 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";
+	n = acl_fromtext(acl2_text, &acl);
+	failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	fd = open("d1/f1", O_WRONLY | O_CREAT | O_EXCL, 0777);
+	failure("Could not create test file?!");
+	if (!assert(fd >= 0)) {
+		acl_free(acl);
+		return;
+	}
+	n = facl_set(fd, acl);
+	acl_free(acl);
+	if (n != 0 && errno == ENOSYS) {
+		close(fd);
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("facl_set(): errno = %d (%s)",
+	    errno, strerror(errno));
+	assertEqualInt(0, n);
+	close(fd);
+
+	/* Create directory d2 with access and default ACLs */
+	assertMakeDir("d2", 0755);
+
+	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";
+
+	n = acl_fromtext(acl3_text, &acl);
+	failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno));
+	assertEqualInt(0, n);
+
+	n = acl_set("d2", acl);
+	acl_free(acl);
+	if (n != 0 && errno == ENOSYS) {
+		close(fd);
+		skipping("ACL tests require ACL support on the filesystem");
+		return;
+	}
+	failure("acl_set(): errno = %d (%s)",
+	    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()));
+
+	/* 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_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS), acl1_text);
+		} else if (strcmp(archive_entry_pathname(ae), "./d1/f1") == 0) {
+			assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS), acl2_text);
+		} else if (strcmp(archive_entry_pathname(ae), "./d2") == 0) {
+			assertEqualString(archive_entry_acl_to_text(ae, NULL, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS), acl3_text);
+		}
+	}
+
+	archive_free(a);
+#endif
+}