Index: lib/libc/posix1e/acl_is_trivial_np.3 =================================================================== --- lib/libc/posix1e/acl_is_trivial_np.3 (revision 214369) +++ lib/libc/posix1e/acl_is_trivial_np.3 (working copy) @@ -56,7 +56,9 @@ ACL is trivial if it can be fully expressed as a f any access rules. For POSIX.1e ACLs, ACL is trivial if it has the three required entries, one for owner, one for owning group, and one for other. -For NFSv4 ACLs, ACL is trivial if it has the "canonical six" entries. +For NFSv4 ACLs, ACL is trivial if is identical to the ACL generated by +.Fn acl_strip_np 3 +from the file mode. Files that have non-trivial ACL have a plus sign appended after mode bits in "ls -l" output. .Sh RETURN VALUES Index: lib/libc/posix1e/acl_strip.c =================================================================== --- lib/libc/posix1e/acl_strip.c (revision 214369) +++ lib/libc/posix1e/acl_strip.c (working copy) @@ -141,7 +141,7 @@ acl_strip_np(const acl_t aclp, int recalculate_mas { switch (_acl_brand(aclp)) { case ACL_BRAND_NFS4: - return (_nfs4_acl_strip_np(aclp, 1)); + return (_nfs4_acl_strip_np(aclp, 0)); case ACL_BRAND_POSIX: return (_posix1e_acl_strip_np(aclp, recalculate_mask)); Index: sys/kern/subr_acl_nfs4.c =================================================================== --- sys/kern/subr_acl_nfs4.c (revision 214369) +++ sys/kern/subr_acl_nfs4.c (working copy) @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #else #include @@ -53,6 +54,10 @@ __FBSDID("$FreeBSD$"); #ifdef _KERNEL +static int acl_nfs4_old_semantics = 1; +SYSCTL_INT(_vfs, OID_AUTO, acl_nfs4_old_semantics, CTLFLAG_RW, + &acl_nfs4_old_semantics, 0, "Use pre-PSARC/2010/029 NFSv4 ACL semantics"); + static struct { accmode_t accmode; int mask; @@ -349,64 +354,10 @@ _acl_duplicate_entry(struct acl *aclp, int entry_i return (&(aclp->acl_entry[entry_index + 1])); } -/* - * Calculate trivial ACL in a manner compatible with PSARC/2010/029. - * Note that this results in an ACL different from (but semantically - * equal to) the "canonical six" trivial ACL computed using algorithm - * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. - */ -void -acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) +static void +acl_nfs4_sync_acl_from_mode_draft(struct acl *aclp, mode_t mode, + int file_owner_id) { - acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; - acl_perm_t user_allow, group_allow, everyone_allow; - - KASSERT(aclp->acl_cnt == 0, ("aclp->acl_cnt == 0")); - - user_allow = group_allow = everyone_allow = ACL_READ_ACL | - ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; - user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | - ACL_WRITE_NAMED_ATTRS; - - if (mode & S_IRUSR) - user_allow |= ACL_READ_DATA; - if (mode & S_IWUSR) - user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); - if (mode & S_IXUSR) - user_allow |= ACL_EXECUTE; - - if (mode & S_IRGRP) - group_allow |= ACL_READ_DATA; - if (mode & S_IWGRP) - group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); - if (mode & S_IXGRP) - group_allow |= ACL_EXECUTE; - - if (mode & S_IROTH) - everyone_allow |= ACL_READ_DATA; - if (mode & S_IWOTH) - everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); - if (mode & S_IXOTH) - everyone_allow |= ACL_EXECUTE; - - user_deny = ((group_allow | everyone_allow) & ~user_allow); - group_deny = everyone_allow & ~group_allow; - user_allow_first = group_deny & ~user_deny; - - if (user_allow_first != 0) - _acl_append(aclp, ACL_USER_OBJ, user_allow_first, ACL_ENTRY_TYPE_ALLOW); - if (user_deny != 0) - _acl_append(aclp, ACL_USER_OBJ, user_deny, ACL_ENTRY_TYPE_DENY); - if (group_deny != 0) - _acl_append(aclp, ACL_GROUP_OBJ, group_deny, ACL_ENTRY_TYPE_DENY); - _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); - _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); - _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); -} - -void -acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id) -{ int i, meets, must_append; struct acl_entry *entry, *copy, *previous, *a1, *a2, *a3, *a4, *a5, *a6; @@ -749,6 +700,17 @@ _acl_duplicate_entry(struct acl *aclp, int entry_i } void +acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, + int file_owner_id) +{ + + if (acl_nfs4_old_semantics) + acl_nfs4_sync_acl_from_mode_draft(aclp, mode, file_owner_id); + else + acl_nfs4_trivial_from_mode(aclp, mode); +} + +void acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) { int i; @@ -871,8 +833,8 @@ acl_nfs4_sync_mode_from_acl(mode_t *_mode, const s *_mode = mode | (old_mode & ACL_PRESERVE_MASK); } -void -acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, +static void +acl_nfs4_compute_inherited_acl_draft(const struct acl *parent_aclp, struct acl *child_aclp, mode_t mode, int file_owner_id, int is_directory) { @@ -1031,6 +993,253 @@ acl_nfs4_sync_mode_from_acl(mode_t *_mode, const s acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); } +/* + * Populate the ACL with entries inherited from child_aclp. + */ +static void +acl_nfs4_inherit_entries(const struct acl *parent_aclp, + struct acl *child_aclp, mode_t mode, int file_owner_id, + int is_directory) +{ + int i, flags, tag; + const struct acl_entry *parent_entry; + struct acl_entry *entry, *copy; + + KASSERT(parent_aclp->acl_cnt > 0, ("parent_aclp->acl_cnt > 0")); + KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + /* + * Form an ACL that is the concatenation of all inheritable ACEs. + */ + for (i = 0; i < parent_aclp->acl_cnt; i++) { + parent_entry = &(parent_aclp->acl_entry[i]); + flags = parent_entry->ae_flags; + tag = parent_entry->ae_tag; + + /* + * Never inherit entries for owner@, group@, or everyone@. + */ + if (tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || + tag == ACL_EVERYONE) + continue; + + /* + * Entry is not inheritable at all. + */ + if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_FILE_INHERIT)) == 0) + continue; + + /* + * We're creating a file, but entry is not inheritable + * by files. + */ + if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) + continue; + + /* + * Entry is inheritable only by files, but has NO_PROPAGATE + * flag set, and we're creating a directory, so it wouldn't + * propagate to any file in that directory anyway. + */ + if (is_directory && + (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && + (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) + continue; + + KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; + child_aclp->acl_cnt++; + } + + /* + * For each entry in the new ACL, adjust its flags. + */ + for (i = 0; i < child_aclp->acl_cnt; i++) { + entry = &(child_aclp->acl_entry[i]); + + /* + * This is not in the specification, but SunOS + * apparently does that. + * XXX: check. + */ + if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || + !is_directory) && + entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object + * being created is not a directory, then clear the + * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, + * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_INHERIT_ONLY. + */ + if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || + !is_directory) { + entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * Continue on to the next ACE. + */ + continue; + } + + /* + * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT + * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure + * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the + * next ACE. Otherwise... + */ + /* + * XXX: Read it again and make sure what does the "otherwise" + * apply to. + */ + if (is_directory && + (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && + ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + continue; + } + + /* + * 2.C. If the type of the ACE is neither ALLOW nor deny, + * then continue. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + /* + * 2.D. Copy the original ACE into a second, adjacent ACE. + */ + copy = _acl_duplicate_entry(child_aclp, i); + + /* + * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY + * is set. + */ + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + + /* + * 2.F. On the second ACE, clear the following flags: + * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. + */ + copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * 2.G. On the second ACE, if the type is ALLOW, + * an implementation MAY clear the following + * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. + */ + if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * Increment the counter to skip the copied entry. + */ + i++; + } +} + +/* + * Calculate inherited ACL in a manner compatible with PSARC/2010/029. + * It's also being used to calculate a trivial ACL, by inheriting from + * a NULL ACL. + */ +static void +acl_nfs4_compute_inherited_acl_psarc(const struct acl *parent_aclp, + struct acl *aclp, mode_t mode, int file_owner_id, int is_directory) +{ + acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; + acl_perm_t user_allow, group_allow, everyone_allow; + + KASSERT(aclp->acl_cnt == 0, ("aclp->acl_cnt == 0")); + + user_allow = group_allow = everyone_allow = ACL_READ_ACL | + ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; + user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS; + + if (mode & S_IRUSR) + user_allow |= ACL_READ_DATA; + if (mode & S_IWUSR) + user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXUSR) + user_allow |= ACL_EXECUTE; + + if (mode & S_IRGRP) + group_allow |= ACL_READ_DATA; + if (mode & S_IWGRP) + group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXGRP) + group_allow |= ACL_EXECUTE; + + if (mode & S_IROTH) + everyone_allow |= ACL_READ_DATA; + if (mode & S_IWOTH) + everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXOTH) + everyone_allow |= ACL_EXECUTE; + + user_deny = ((group_allow | everyone_allow) & ~user_allow); + group_deny = everyone_allow & ~group_allow; + user_allow_first = group_deny & ~user_deny; + + if (user_allow_first != 0) + _acl_append(aclp, ACL_USER_OBJ, user_allow_first, + ACL_ENTRY_TYPE_ALLOW); + if (user_deny != 0) + _acl_append(aclp, ACL_USER_OBJ, user_deny, + ACL_ENTRY_TYPE_DENY); + if (group_deny != 0) + _acl_append(aclp, ACL_GROUP_OBJ, group_deny, + ACL_ENTRY_TYPE_DENY); + if (parent_aclp != NULL) + acl_nfs4_inherit_entries(parent_aclp, aclp, mode, + file_owner_id, is_directory); + _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); + _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); + _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); +} + +void +acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, + struct acl *child_aclp, mode_t mode, int file_owner_id, + int is_directory) +{ + + if (acl_nfs4_old_semantics) + acl_nfs4_compute_inherited_acl_draft(parent_aclp, child_aclp, + mode, file_owner_id, is_directory); + else + acl_nfs4_compute_inherited_acl_psarc(parent_aclp, child_aclp, + mode, file_owner_id, is_directory); +} + +/* + * Calculate trivial ACL in a manner compatible with PSARC/2010/029. + * Note that this results in an ACL different from (but semantically + * equal to) the "canonical six" trivial ACL computed using algorithm + * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. + * + * This routine is not static only because the code is being used in libc. + * Kernel code should call acl_nfs4_sync_acl_from_mode() instead. + */ +void +acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) +{ + + acl_nfs4_compute_inherited_acl_psarc(NULL, aclp, mode, -1, -1); +} + #ifdef _KERNEL static int _acls_are_equal(const struct acl *a, const struct acl *b) @@ -1067,7 +1276,7 @@ acl_nfs4_is_trivial(const struct acl *aclp, int fi mode_t tmpmode = 0; struct acl *tmpaclp; - if (aclp->acl_cnt != 6) + if (aclp->acl_cnt > 6) return (0); /* @@ -1078,9 +1287,22 @@ acl_nfs4_is_trivial(const struct acl *aclp, int fi * this slow implementation significantly speeds things up * for files that don't have non-trivial ACLs - it's critical * for performance to not use EA when they are not needed. + * + * First try the PSARC/2010/029 semantics. */ tmpaclp = acl_alloc(M_WAITOK | M_ZERO); acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); + acl_nfs4_trivial_from_mode(tmpaclp, tmpmode); + trivial = _acls_are_equal(aclp, tmpaclp); + if (trivial) { + acl_free(tmpaclp); + return (trivial); + } + + /* + * Check if it's a draft-ietf-nfsv4-minorversion1-03.txt trivial ACL. + */ + tmpaclp->acl_cnt = 0; acl_nfs4_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); trivial = _acls_are_equal(aclp, tmpaclp); acl_free(tmpaclp);