From owner-svn-src-head@FreeBSD.ORG Mon Dec 13 18:56:04 2010 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id A4DDE1065694; Mon, 13 Dec 2010 18:56:04 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 92A738FC19; Mon, 13 Dec 2010 18:56:04 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id oBDIu4DQ090952; Mon, 13 Dec 2010 18:56:04 GMT (envelope-from trasz@svn.freebsd.org) Received: (from trasz@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id oBDIu451090950; Mon, 13 Dec 2010 18:56:04 GMT (envelope-from trasz@svn.freebsd.org) Message-Id: <201012131856.oBDIu451090950@svn.freebsd.org> From: Edward Tomasz Napierala Date: Mon, 13 Dec 2010 18:56:04 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r216413 - head/sys/kern X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 13 Dec 2010 18:56:04 -0000 Author: trasz Date: Mon Dec 13 18:56:04 2010 New Revision: 216413 URL: http://svn.freebsd.org/changeset/base/216413 Log: Adapt filesystem-independent NFSv4 ACL code (used by UFS, but not by ZFS) to PSARC/2010/029. In short, the semantics is simplified - "weird stuff" no longer happens after chmod, entries don't get duplicated during inheritance, and trivial ACLs no longer contain three "DENY" entries, which is also more friendly to MS Windows. By default, UFS keeps using old semantics. To change it, set sysctl vfs.acl_nfs4_old_semantics to 0. I'll flip the switch when ZFSv28 hits the tree, to keep these two in sync - ZFS v28 uses PSARC semantics, and ZFS v15 uses the old one. Modified: head/sys/kern/subr_acl_nfs4.c Modified: head/sys/kern/subr_acl_nfs4.c ============================================================================== --- head/sys/kern/subr_acl_nfs4.c Mon Dec 13 17:56:31 2010 (r216412) +++ head/sys/kern/subr_acl_nfs4.c Mon Dec 13 18:56:04 2010 (r216413) @@ -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,63 +358,9 @@ _acl_duplicate_entry(struct acl *aclp, 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) -{ - 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) +static void +acl_nfs4_sync_acl_from_mode_draft(struct acl *aclp, mode_t mode, + int file_owner_id) { int i, meets, must_append; struct acl_entry *entry, *copy, *previous, @@ -749,6 +704,17 @@ acl_nfs4_sync_acl_from_mode(struct acl * } 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 *_mod *_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,218 @@ acl_nfs4_compute_inherited_acl(const str 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++; + + entry->ae_flags &= ~ACL_ENTRY_INHERIT_ONLY; + + /* + * 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 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; + } + + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW && + (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) == 0) { + /* + * 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; + } + } +} + +/* + * 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 +1249,7 @@ acl_nfs4_is_trivial(const struct acl *ac mode_t tmpmode = 0; struct acl *tmpaclp; - if (aclp->acl_cnt != 6) + if (aclp->acl_cnt > 6) return (0); /* @@ -1078,10 +1260,23 @@ acl_nfs4_is_trivial(const struct acl *ac * 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);