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
+}