From 85fb5122964927c714b79746d2854b6766291287 Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Tue, 10 Jan 2017 17:03:17 +0100 Subject: [PATCH] Add POSIX.1e and NFSv4 ACL support for Solaris and derivates --- CMakeLists.txt | 20 + Makefile.am | 4 +- NEWS | 2 + configure.ac | 6 + libarchive/archive_platform.h | 8 + .../archive_read_disk_entry_from_file.c | 236 +++++++- libarchive/archive_write_disk_acl.c | 292 +++++++++- libarchive/test/CMakeLists.txt | 2 + libarchive/test/test_acl_solaris_nfs4.c | 515 ++++++++++++++++++ libarchive/test/test_acl_solaris_posix1e.c | 415 ++++++++++++++ 10 files changed, 1456 insertions(+), 44 deletions(-) create mode 100644 libarchive/test/test_acl_solaris_nfs4.c create mode 100644 libarchive/test/test_acl_solaris_posix1e.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fa651eb23..14649e997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1609,6 +1609,21 @@ IF(ENABLE_ACL) # 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 @@ ELSE(ENABLE_ACL) 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 f92d723a4..318c7ac5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -323,11 +323,13 @@ libarchive_test_SOURCES= \ libarchive/test/main.c \ libarchive/test/read_open_memory.c \ libarchive/test/test.h \ - libarchive/test/test_acl_freebsd_posix1e.c \ libarchive/test/test_acl_freebsd_nfs4.c \ + libarchive/test/test_acl_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 4d875b2a9..3dffdf9d8 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 ffefa6c51..f97ef854d 100644 --- a/configure.ac +++ b/configure.ac @@ -730,6 +730,12 @@ if test "x$enable_acl" != "xno"; then [AC_DEFINE(HAVE_ACL_USER, 1, [True for systems with POSIX ACL support])], [], [#include ]) + + # Solaris and derivates ACLs + AC_CHECK_LIB([sec], [acl_get]) + AC_CHECK_TYPES([aclent_t], [], [], [[#include ]]) + AC_CHECK_TYPES([ace_t], [], [], [[#include ]]) + 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 e44c93277..eb9d4bbe8 100644 --- a/libarchive/archive_platform.h +++ b/libarchive/archive_platform.h @@ -151,6 +151,14 @@ #define HAVE_POSIX_ACL 1 #endif +/* + * If this platform has , 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 ce576e0ac..e8bf07e96 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -405,17 +405,26 @@ setup_mac_metadata(struct archive_read_disk *a, } #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 @@ setup_acls(struct archive_read_disk *a, 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 @@ setup_acls(struct archive_read_disk *a, 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 @@ setup_acls(struct archive_read_disk *a, 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 @@ setup_acls(struct archive_read_disk *a, #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,13 +571,53 @@ setup_acls(struct archive_read_disk *a, } } } +#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; @@ -592,6 +659,139 @@ static struct { {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 @@ translate_acl(struct archive_read_disk *a, } #endif - s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); if (s == -1) { archive_set_error(&a->archive, errno, @@ -783,7 +982,8 @@ translate_acl(struct archive_read_disk *a, } 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 @@ setup_acls(struct archive_read_disk *a, (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 706ab62a0..4a0112e5d 100644 --- a/libarchive/archive_write_disk_acl.c +++ b/libarchive/archive_write_disk_acl.c @@ -43,7 +43,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 0 #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 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, 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 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, { 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 @@ static struct { {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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, 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 @@ set_acl(struct archive *a, int fd, const char *name, } } } 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 @@ exit_free: 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 06b27638e..9bf19b93a 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -14,6 +14,8 @@ IF(ENABLE_TEST) 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 000000000..6580de4f3 --- /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 + +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 000000000..4f41221d8 --- /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 + +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 +} -- 2.47.2