svn commit: r216413 - head/sys/kern

Edward Tomasz Napierala trasz at FreeBSD.org
Mon Dec 13 18:56:04 UTC 2010


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 <trasz at FreeBSD.org>
+ * Copyright (c) 2008-2010 Edward Tomasz Napierała <trasz at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/vnode.h>
 #include <sys/errno.h>
 #include <sys/stat.h>
+#include <sys/sysctl.h>
 #include <sys/acl.h>
 #else
 #include <errno.h>
@@ -49,10 +50,18 @@ __FBSDID("$FreeBSD$");
 #include <sys/stat.h>
 #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);
 


More information about the svn-src-head mailing list