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) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008-2009 Edward Tomasz Napierała + * Copyright (c) 2008-2010 Edward Tomasz Napierała * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #else #include @@ -49,10 +50,18 @@ __FBSDID("$FreeBSD$"); #include #define KASSERT(a, b) assert(a) #define CTASSERT(a) -#endif /* _KERNEL */ +void acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode); + +#endif /* !_KERNEL */ + +static int acl_nfs4_old_semantics = 1; + #ifdef _KERNEL +SYSCTL_INT(_vfs, OID_AUTO, acl_nfs4_old_semantics, CTLFLAG_RW, + &acl_nfs4_old_semantics, 1, "Use pre-PSARC/2010/029 NFSv4 ACL semantics"); + static struct { accmode_t accmode; int mask; @@ -349,64 +358,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 +704,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 +837,12 @@ 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, +/* + * Calculate inherited ACL in a manner compatible with NFSv4 Minor Version 1, + * draft-ietf-nfsv4-minorversion1-03.txt. + */ +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 +1001,213 @@ 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 parent_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; + + 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")); + + 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; + + /* + * Don't inherit owner@, group@, or everyone@ entries. + */ + 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; + + /* + * Entry qualifies for being inherited. + */ + KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + entry = &(child_aclp->acl_entry[child_aclp->acl_cnt]); + *entry = *parent_entry; + child_aclp->acl_cnt++; + + /* + * If the type of the ACE is neither ALLOW nor DENY, + * then leave it as it is and proceed to the next one. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) { + /* + * Some permissions must never be inherited. + */ + entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER | + ACL_WRITE_NAMED_ATTRS | ACL_WRITE_ATTRIBUTES); + + /* + * Others must be masked according to the file mode. + */ + if ((mode & S_IRGRP) == 0) + entry->ae_perm &= ~ACL_READ_DATA; + if ((mode & S_IWGRP) == 0) + entry->ae_perm &= + ~(ACL_WRITE_DATA | ACL_APPEND_DATA); + if ((mode & S_IXGRP) == 0) + entry->ae_perm &= ~ACL_EXECUTE; + } + + /* + * 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); + } + + /* + * If the object is a directory and ACL_ENTRY_FILE_INHERIT + * is set, but ACL_ENTRY_DIRECTORY_INHERIT is not set, ensure + * that ACL_ENTRY_INHERIT_ONLY is set. + */ + 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; + } + } +} + +/* + * 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) +{ + + aclp->acl_cnt = 0; + 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 +1244,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,11 +1255,24 @@ 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_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); + 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_draft(tmpaclp, tmpmode, file_owner_id); + trivial = _acls_are_equal(aclp, tmpaclp); acl_free(tmpaclp); return (trivial);