| /*- |
| * Copyright (c) 2017 Martin Matuska |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include "test.h" |
| __FBSDID("$FreeBSD$"); |
| |
| #if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL |
| static const acl_perm_t acl_perms[] = { |
| #if ARCHIVE_ACL_DARWIN |
| ACL_READ_DATA, |
| ACL_LIST_DIRECTORY, |
| ACL_WRITE_DATA, |
| ACL_ADD_FILE, |
| ACL_EXECUTE, |
| ACL_SEARCH, |
| ACL_DELETE, |
| ACL_APPEND_DATA, |
| ACL_ADD_SUBDIRECTORY, |
| ACL_DELETE_CHILD, |
| ACL_READ_ATTRIBUTES, |
| ACL_WRITE_ATTRIBUTES, |
| ACL_READ_EXTATTRIBUTES, |
| ACL_WRITE_EXTATTRIBUTES, |
| ACL_READ_SECURITY, |
| ACL_WRITE_SECURITY, |
| ACL_CHANGE_OWNER, |
| ACL_SYNCHRONIZE |
| #else /* !ARCHIVE_ACL_DARWIN */ |
| ACL_EXECUTE, |
| ACL_WRITE, |
| ACL_READ, |
| #if ARCHIVE_ACL_FREEBSD_NFS4 |
| ACL_READ_DATA, |
| ACL_LIST_DIRECTORY, |
| ACL_WRITE_DATA, |
| ACL_ADD_FILE, |
| ACL_APPEND_DATA, |
| ACL_ADD_SUBDIRECTORY, |
| ACL_READ_NAMED_ATTRS, |
| ACL_WRITE_NAMED_ATTRS, |
| ACL_DELETE_CHILD, |
| ACL_READ_ATTRIBUTES, |
| ACL_WRITE_ATTRIBUTES, |
| ACL_DELETE, |
| ACL_READ_ACL, |
| ACL_WRITE_ACL, |
| ACL_WRITE_OWNER, |
| ACL_SYNCHRONIZE |
| #endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ |
| #endif /* !ARCHIVE_ACL_DARWIN */ |
| }; |
| #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 |
| static const acl_flag_t acl_flags[] = { |
| #if ARCHIVE_ACL_DARWIN |
| ACL_ENTRY_INHERITED, |
| ACL_ENTRY_FILE_INHERIT, |
| ACL_ENTRY_DIRECTORY_INHERIT, |
| ACL_ENTRY_LIMIT_INHERIT, |
| ACL_ENTRY_ONLY_INHERIT |
| #else /* ARCHIVE_ACL_FREEBSD_NFS4 */ |
| ACL_ENTRY_FILE_INHERIT, |
| ACL_ENTRY_DIRECTORY_INHERIT, |
| ACL_ENTRY_NO_PROPAGATE_INHERIT, |
| ACL_ENTRY_INHERIT_ONLY, |
| ACL_ENTRY_SUCCESSFUL_ACCESS, |
| ACL_ENTRY_FAILED_ACCESS, |
| ACL_ENTRY_INHERITED |
| #endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ |
| }; |
| #endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 */ |
| |
| /* |
| * Compare two ACL entries on FreeBSD or on Mac OS X |
| */ |
| static int |
| compare_acl_entry(acl_entry_t ae_a, acl_entry_t ae_b, int is_nfs4) |
| { |
| acl_tag_t tag_a, tag_b; |
| acl_permset_t permset_a, permset_b; |
| int perm_a, perm_b, perm_start, perm_end; |
| void *qual_a, *qual_b; |
| #if ARCHIVE_ACL_FREEBSD_NFS4 |
| acl_entry_type_t type_a, type_b; |
| #endif |
| #if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN |
| acl_flagset_t flagset_a, flagset_b; |
| int flag_a, flag_b; |
| #endif |
| int i, r; |
| |
| |
| /* Compare ACL tag */ |
| r = acl_get_tag_type(ae_a, &tag_a); |
| failure("acl_get_tag_type() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| r = acl_get_tag_type(ae_b, &tag_b); |
| failure("acl_get_tag_type() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| if (tag_a != tag_b) |
| return (0); |
| |
| /* Compare ACL qualifier */ |
| #if ARCHIVE_ACL_DARWIN |
| if (tag_a == ACL_EXTENDED_ALLOW || tag_b == ACL_EXTENDED_DENY) |
| #else |
| if (tag_a == ACL_USER || tag_a == ACL_GROUP) |
| #endif |
| { |
| qual_a = acl_get_qualifier(ae_a); |
| failure("acl_get_qualifier() error: %s", strerror(errno)); |
| if (assert(qual_a != NULL) == 0) |
| return (-1); |
| qual_b = acl_get_qualifier(ae_b); |
| failure("acl_get_qualifier() error: %s", strerror(errno)); |
| if (assert(qual_b != NULL) == 0) { |
| acl_free(qual_a); |
| return (-1); |
| } |
| #if ARCHIVE_ACL_DARWIN |
| if (memcmp(((guid_t *)qual_a)->g_guid, |
| ((guid_t *)qual_b)->g_guid, KAUTH_GUID_SIZE) != 0) |
| #else |
| if ((tag_a == ACL_USER && |
| (*(uid_t *)qual_a != *(uid_t *)qual_b)) || |
| (tag_a == ACL_GROUP && |
| (*(gid_t *)qual_a != *(gid_t *)qual_b))) |
| #endif |
| { |
| acl_free(qual_a); |
| acl_free(qual_b); |
| return (0); |
| } |
| acl_free(qual_a); |
| acl_free(qual_b); |
| } |
| |
| #if ARCHIVE_ACL_FREEBSD_NFS4 |
| if (is_nfs4) { |
| /* Compare NFS4 ACL type */ |
| r = acl_get_entry_type_np(ae_a, &type_a); |
| failure("acl_get_entry_type_np() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| r = acl_get_entry_type_np(ae_b, &type_b); |
| failure("acl_get_entry_type_np() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| if (type_a != type_b) |
| return (0); |
| } |
| #endif |
| |
| /* Compare ACL perms */ |
| r = acl_get_permset(ae_a, &permset_a); |
| failure("acl_get_permset() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| r = acl_get_permset(ae_b, &permset_b); |
| failure("acl_get_permset() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| |
| perm_start = 0; |
| perm_end = (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); |
| #if ARCHIVE_ACL_FREEBSD_NFS4 |
| if (is_nfs4) |
| perm_start = 3; |
| else |
| perm_end = 3; |
| #endif |
| /* Cycle through all perms and compare their value */ |
| for (i = perm_start; i < perm_end; i++) { |
| #if ARCHIVE_ACL_LIBACL |
| perm_a = acl_get_perm(permset_a, acl_perms[i]); |
| perm_b = acl_get_perm(permset_b, acl_perms[i]); |
| #else |
| perm_a = acl_get_perm_np(permset_a, acl_perms[i]); |
| perm_b = acl_get_perm_np(permset_b, acl_perms[i]); |
| #endif |
| if (perm_a == -1 || perm_b == -1) |
| return (-1); |
| if (perm_a != perm_b) |
| return (0); |
| } |
| |
| #if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN |
| if (is_nfs4) { |
| r = acl_get_flagset_np(ae_a, &flagset_a); |
| failure("acl_get_flagset_np() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| r = acl_get_flagset_np(ae_b, &flagset_b); |
| failure("acl_get_flagset_np() error: %s", strerror(errno)); |
| if (assertEqualInt(r, 0) == 0) |
| return (-1); |
| /* Cycle through all flags and compare their status */ |
| for (i = 0; i < (int)(sizeof(acl_flags) / sizeof(acl_flags[0])); |
| i++) { |
| flag_a = acl_get_flag_np(flagset_a, acl_flags[i]); |
| flag_b = acl_get_flag_np(flagset_b, acl_flags[i]); |
| if (flag_a == -1 || flag_b == -1) |
| return (-1); |
| if (flag_a != flag_b) |
| return (0); |
| } |
| } |
| #else /* ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN */ |
| (void)is_nfs4; /* UNUSED */ |
| #endif |
| return (1); |
| } |
| #endif /* ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL */ |
| |
| #if ARCHIVE_ACL_SUPPORT |
| /* |
| * Clear default ACLs or inheritance flags |
| */ |
| static void |
| clear_inheritance_flags(const char *path, int type) |
| { |
| switch (type) { |
| case ARCHIVE_TEST_ACL_TYPE_POSIX1E: |
| #if ARCHIVE_ACL_POSIX1E |
| #if !ARCHIVE_ACL_SUNOS |
| acl_delete_def_file(path); |
| #else |
| /* Solaris */ |
| setTestAcl(path); |
| #endif |
| #endif /* ARCHIVE_ACL_POSIX1E */ |
| break; |
| case ARCHIVE_TEST_ACL_TYPE_NFS4: |
| #if ARCHIVE_ACL_NFS4 |
| setTestAcl(path); |
| #endif |
| break; |
| default: |
| (void)path; /* UNUSED */ |
| break; |
| } |
| } |
| |
| static int |
| compare_acls(const char *path_a, const char *path_b) |
| { |
| int ret = 1; |
| int is_nfs4 = 0; |
| #if ARCHIVE_ACL_SUNOS |
| void *acl_a, *acl_b; |
| int aclcnt_a, aclcnt_b; |
| aclent_t *aclent_a, *aclent_b; |
| ace_t *ace_a, *ace_b; |
| int e; |
| #elif ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL |
| acl_t acl_a, acl_b; |
| acl_entry_t aclent_a, aclent_b; |
| int a, b, r; |
| #endif |
| #if ARCHIVE_ACL_LIBRICHACL |
| struct richacl *richacl_a, *richacl_b; |
| |
| richacl_a = NULL; |
| richacl_b = NULL; |
| #endif |
| |
| #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL || \ |
| ARCHIVE_ACL_SUNOS |
| acl_a = NULL; |
| acl_b = NULL; |
| #endif |
| #if ARCHIVE_ACL_SUNOS |
| acl_a = sunacl_get(GETACL, &aclcnt_a, 0, path_a); |
| if (acl_a == NULL) { |
| #if ARCHIVE_ACL_SUNOS_NFS4 |
| is_nfs4 = 1; |
| acl_a = sunacl_get(ACE_GETACL, &aclcnt_a, 0, path_a); |
| #endif |
| failure("acl_get() error: %s", strerror(errno)); |
| if (assert(acl_a != NULL) == 0) |
| return (-1); |
| #if ARCHIVE_ACL_SUNOS_NFS4 |
| acl_b = sunacl_get(ACE_GETACL, &aclcnt_b, 0, path_b); |
| #endif |
| } else |
| acl_b = sunacl_get(GETACL, &aclcnt_b, 0, path_b); |
| if (acl_b == NULL && (errno == ENOSYS || errno == ENOTSUP)) { |
| free(acl_a); |
| return (0); |
| } |
| failure("acl_get() error: %s", strerror(errno)); |
| if (assert(acl_b != NULL) == 0) { |
| free(acl_a); |
| return (-1); |
| } |
| |
| if (aclcnt_a != aclcnt_b) { |
| ret = 0; |
| goto exit_free; |
| } |
| |
| for (e = 0; e < aclcnt_a; e++) { |
| if (!is_nfs4) { |
| aclent_a = &((aclent_t *)acl_a)[e]; |
| aclent_b = &((aclent_t *)acl_b)[e]; |
| if (aclent_a->a_type != aclent_b->a_type || |
| aclent_a->a_id != aclent_b->a_id || |
| aclent_a->a_perm != aclent_b->a_perm) { |
| ret = 0; |
| goto exit_free; |
| } |
| } |
| #if ARCHIVE_ACL_SUNOS_NFS4 |
| else { |
| ace_a = &((ace_t *)acl_a)[e]; |
| ace_b = &((ace_t *)acl_b)[e]; |
| if (ace_a->a_who != ace_b->a_who || |
| ace_a->a_access_mask != ace_b->a_access_mask || |
| ace_a->a_flags != ace_b->a_flags || |
| ace_a->a_type != ace_b->a_type) { |
| ret = 0; |
| goto exit_free; |
| } |
| } |
| #endif |
| } |
| #else /* !ARCHIVE_ACL_SUNOS */ |
| #if ARCHIVE_ACL_LIBRICHACL |
| richacl_a = richacl_get_file(path_a); |
| #if !ARCHIVE_ACL_LIBACL |
| if (richacl_a == NULL && |
| (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) |
| return (0); |
| failure("richacl_get_file() error: %s (%s)", path_a, strerror(errno)); |
| if (assert(richacl_a != NULL) == 0) |
| return (-1); |
| #endif |
| if (richacl_a != NULL) { |
| richacl_b = richacl_get_file(path_b); |
| if (richacl_b == NULL && |
| (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) { |
| richacl_free(richacl_a); |
| return (0); |
| } |
| failure("richacl_get_file() error: %s (%s)", path_b, |
| strerror(errno)); |
| if (assert(richacl_b != NULL) == 0) { |
| richacl_free(richacl_a); |
| return (-1); |
| } |
| if (richacl_compare(richacl_a, richacl_b) == 0) |
| ret = 0; |
| richacl_free(richacl_a); |
| richacl_free(richacl_b); |
| return (ret); |
| } |
| #endif /* ARCHIVE_ACL_LIBRICHACL */ |
| #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL |
| #if ARCHIVE_ACL_DARWIN |
| is_nfs4 = 1; |
| acl_a = acl_get_file(path_a, ACL_TYPE_EXTENDED); |
| #elif ARCHIVE_ACL_FREEBSD_NFS4 |
| acl_a = acl_get_file(path_a, ACL_TYPE_NFS4); |
| if (acl_a != NULL) |
| is_nfs4 = 1; |
| #endif |
| if (acl_a == NULL) |
| acl_a = acl_get_file(path_a, ACL_TYPE_ACCESS); |
| failure("acl_get_file() error: %s (%s)", path_a, strerror(errno)); |
| if (assert(acl_a != NULL) == 0) |
| return (-1); |
| #if ARCHIVE_ACL_DARWIN |
| acl_b = acl_get_file(path_b, ACL_TYPE_EXTENDED); |
| #elif ARCHIVE_ACL_FREEBSD_NFS4 |
| acl_b = acl_get_file(path_b, ACL_TYPE_NFS4); |
| #endif |
| #if !ARCHIVE_ACL_DARWIN |
| if (acl_b == NULL) { |
| #if ARCHIVE_ACL_FREEBSD_NFS4 |
| if (is_nfs4) { |
| acl_free(acl_a); |
| return (0); |
| } |
| #endif |
| acl_b = acl_get_file(path_b, ACL_TYPE_ACCESS); |
| } |
| failure("acl_get_file() error: %s (%s)", path_b, strerror(errno)); |
| if (assert(acl_b != NULL) == 0) { |
| acl_free(acl_a); |
| return (-1); |
| } |
| #endif |
| a = acl_get_entry(acl_a, ACL_FIRST_ENTRY, &aclent_a); |
| if (a == -1) { |
| ret = 0; |
| goto exit_free; |
| } |
| b = acl_get_entry(acl_b, ACL_FIRST_ENTRY, &aclent_b); |
| if (b == -1) { |
| ret = 0; |
| goto exit_free; |
| } |
| #if ARCHIVE_ACL_DARWIN |
| while (a == 0 && b == 0) |
| #else /* FreeBSD, Linux */ |
| while (a == 1 && b == 1) |
| #endif |
| { |
| r = compare_acl_entry(aclent_a, aclent_b, is_nfs4); |
| if (r != 1) { |
| ret = r; |
| goto exit_free; |
| } |
| a = acl_get_entry(acl_a, ACL_NEXT_ENTRY, &aclent_a); |
| b = acl_get_entry(acl_b, ACL_NEXT_ENTRY, &aclent_b); |
| } |
| /* Entry count must match */ |
| if (a != b) |
| ret = 0; |
| #endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL */ |
| #endif /* !ARCHIVE_ACL_SUNOS */ |
| exit_free: |
| #if ARCHIVE_ACL_SUNOS |
| free(acl_a); |
| free(acl_b); |
| #else |
| acl_free(acl_a); |
| acl_free(acl_b); |
| #endif |
| return (ret); |
| } |
| #endif /* ARCHIVE_ACL_SUPPORT */ |
| |
| DEFINE_TEST(test_option_acls) |
| { |
| #if !ARCHIVE_ACL_SUPPORT |
| skipping("ACLs are not supported on this platform"); |
| #else /* ARCHIVE_ACL_SUPPORT */ |
| int acltype, r; |
| |
| assertMakeFile("f", 0644, "a"); |
| acltype = setTestAcl("f"); |
| if (acltype == 0) { |
| skipping("Can't write ACLs on the filesystem"); |
| return; |
| } |
| |
| /* Archive it with acls */ |
| r = systemf("%s -c --no-mac-metadata --acls -f acls.tar f >acls.out 2>acls.err", testprog); |
| assertEqualInt(r, 0); |
| |
| /* Archive it without acls */ |
| r = systemf("%s -c --no-mac-metadata --no-acls -f noacls.tar f >noacls.out 2>noacls.err", testprog); |
| assertEqualInt(r, 0); |
| |
| /* Extract acls with acls */ |
| assertMakeDir("acls_acls", 0755); |
| clear_inheritance_flags("acls_acls", acltype); |
| r = systemf("%s -x -C acls_acls --no-same-permissions --acls -f acls.tar >acls_acls.out 2>acls_acls.err", testprog); |
| assertEqualInt(r, 0); |
| r = compare_acls("f", "acls_acls/f"); |
| assertEqualInt(r, 1); |
| |
| /* Extractl acls without acls */ |
| assertMakeDir("acls_noacls", 0755); |
| clear_inheritance_flags("acls_noacls", acltype); |
| r = systemf("%s -x -C acls_noacls -p --no-acls -f acls.tar >acls_noacls.out 2>acls_noacls.err", testprog); |
| assertEqualInt(r, 0); |
| r = compare_acls("f", "acls_noacls/f"); |
| assertEqualInt(r, 0); |
| |
| /* Extract noacls with acls flag */ |
| assertMakeDir("noacls_acls", 0755); |
| clear_inheritance_flags("noacls_acls", acltype); |
| r = systemf("%s -x -C noacls_acls --no-same-permissions --acls -f noacls.tar >noacls_acls.out 2>noacls_acls.err", testprog); |
| assertEqualInt(r, 0); |
| r = compare_acls("f", "noacls_acls/f"); |
| assertEqualInt(r, 0); |
| |
| /* Extract noacls with noacls */ |
| assertMakeDir("noacls_noacls", 0755); |
| clear_inheritance_flags("noacls_noacls", acltype); |
| r = systemf("%s -x -C noacls_noacls -p --no-acls -f noacls.tar >noacls_noacls.out 2>noacls_noacls.err", testprog); |
| assertEqualInt(r, 0); |
| r = compare_acls("f", "noacls_noacls/f"); |
| assertEqualInt(r, 0); |
| #endif /* ARCHIVE_ACL_SUPPORT */ |
| } |