Andreas Gruenbacher b8379c
From: Andreas Gruenbacher <agruen@suse.de>
Andreas Gruenbacher b8379c
Date: Fri, 11 Jun 2010 16:12:47 +0530
Andreas Gruenbacher b8379c
Subject: [PATCH 06/16] richacl: Compute maximum file masks from an acl
Andreas Gruenbacher b8379c
Patch-mainline: not yet
Andreas Gruenbacher b8379c
Andreas Gruenbacher b8379c
Compute upper bound owner, group, and other file masks with as few
Andreas Gruenbacher b8379c
permissions as possible without denying any permissions that the NFSv4
Andreas Gruenbacher b8379c
acl in a richacl grants.
Andreas Gruenbacher b8379c
Andreas Gruenbacher b8379c
This algorithm is used when a file inherits an acl at create time and
Andreas Gruenbacher b8379c
when an acl is set via a mechanism that does not specify file modes
Andreas Gruenbacher b8379c
(such as via nfsd).  When user-space sets an acl, the file masks are
Andreas Gruenbacher b8379c
passed in as part of the xattr.
Andreas Gruenbacher b8379c
Andreas Gruenbacher b8379c
When setting a richacl, the file masks determine what the file
Andreas Gruenbacher b8379c
permission bits will be set to; see richacl_masks_to_mode().
Andreas Gruenbacher b8379c
Andreas Gruenbacher b8379c
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Andreas Gruenbacher b8379c
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Andreas Gruenbacher b8379c
---
Jeff Mahoney a40327
 fs/richacl_base.c       |  125 ++++++++++++++++++++++++++++++++++++++++++++++++
Jeff Mahoney a40327
 include/linux/richacl.h |    1 
Jeff Mahoney a40327
 2 files changed, 126 insertions(+)
Andreas Gruenbacher b8379c
Andreas Gruenbacher b8379c
--- a/fs/richacl_base.c
Andreas Gruenbacher b8379c
+++ b/fs/richacl_base.c
Jeff Mahoney a40327
@@ -205,3 +205,128 @@ richace_set_who(struct richace *ace, con
Andreas Gruenbacher b8379c
 	return 0;
Andreas Gruenbacher b8379c
 }
Andreas Gruenbacher b8379c
 EXPORT_SYMBOL_GPL(richace_set_who);
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+/**
Andreas Gruenbacher b8379c
+ * richacl_allowed_to_who  -  mask flags allowed to a specific who value
Andreas Gruenbacher b8379c
+ *
Andreas Gruenbacher b8379c
+ * Computes the mask values allowed to a specific who value, taking
Andreas Gruenbacher b8379c
+ * EVERYONE@ entries into account.
Andreas Gruenbacher b8379c
+ */
Andreas Gruenbacher b8379c
+static unsigned int richacl_allowed_to_who(struct richacl *acl,
Andreas Gruenbacher b8379c
+					   struct richace *who)
Andreas Gruenbacher b8379c
+{
Andreas Gruenbacher b8379c
+	struct richace *ace;
Andreas Gruenbacher b8379c
+	unsigned int allowed = 0;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+	richacl_for_each_entry_reverse(ace, acl) {
Andreas Gruenbacher b8379c
+		if (richace_is_inherit_only(ace))
Andreas Gruenbacher b8379c
+			continue;
Andreas Gruenbacher b8379c
+		if (richace_is_same_identifier(ace, who) ||
Andreas Gruenbacher b8379c
+		    richace_is_everyone(ace)) {
Andreas Gruenbacher b8379c
+			if (richace_is_allow(ace))
Andreas Gruenbacher b8379c
+				allowed |= ace->e_mask;
Andreas Gruenbacher b8379c
+			else if (richace_is_deny(ace))
Andreas Gruenbacher b8379c
+				allowed &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+		}
Andreas Gruenbacher b8379c
+	}
Andreas Gruenbacher b8379c
+	return allowed;
Andreas Gruenbacher b8379c
+}
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+/**
Andreas Gruenbacher b8379c
+ * richacl_group_class_allowed  -  maximum permissions the group class is allowed
Andreas Gruenbacher b8379c
+ *
Andreas Gruenbacher b8379c
+ * See richacl_compute_max_masks().
Andreas Gruenbacher b8379c
+ */
Andreas Gruenbacher b8379c
+static unsigned int richacl_group_class_allowed(struct richacl *acl)
Andreas Gruenbacher b8379c
+{
Andreas Gruenbacher b8379c
+	struct richace *ace;
Andreas Gruenbacher b8379c
+	unsigned int everyone_allowed = 0, group_class_allowed = 0;
Andreas Gruenbacher b8379c
+	int had_group_ace = 0;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+	richacl_for_each_entry_reverse(ace, acl) {
Andreas Gruenbacher b8379c
+		if (richace_is_inherit_only(ace) ||
Andreas Gruenbacher b8379c
+		    richace_is_owner(ace))
Andreas Gruenbacher b8379c
+			continue;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+		if (richace_is_everyone(ace)) {
Andreas Gruenbacher b8379c
+			if (richace_is_allow(ace))
Andreas Gruenbacher b8379c
+				everyone_allowed |= ace->e_mask;
Andreas Gruenbacher b8379c
+			else if (richace_is_deny(ace))
Andreas Gruenbacher b8379c
+				everyone_allowed &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+		} else {
Andreas Gruenbacher b8379c
+			group_class_allowed |=
Andreas Gruenbacher b8379c
+				richacl_allowed_to_who(acl, ace);
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+			if (richace_is_group(ace))
Andreas Gruenbacher b8379c
+				had_group_ace = 1;
Andreas Gruenbacher b8379c
+		}
Andreas Gruenbacher b8379c
+	}
Andreas Gruenbacher b8379c
+	if (!had_group_ace)
Andreas Gruenbacher b8379c
+		group_class_allowed |= everyone_allowed;
Andreas Gruenbacher b8379c
+	return group_class_allowed;
Andreas Gruenbacher b8379c
+}
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+/**
Andreas Gruenbacher b8379c
+ * richacl_compute_max_masks  -  compute upper bound masks
Andreas Gruenbacher b8379c
+ *
Andreas Gruenbacher b8379c
+ * Computes upper bound owner, group, and other masks so that none of
Andreas Gruenbacher b8379c
+ * the mask flags allowed by the acl are disabled (for any choice of the
Andreas Gruenbacher b8379c
+ * file owner or group membership).
Andreas Gruenbacher b8379c
+ */
Andreas Gruenbacher b8379c
+void richacl_compute_max_masks(struct richacl *acl)
Andreas Gruenbacher b8379c
+{
Andreas Gruenbacher b8379c
+	unsigned int gmask = ~0;
Andreas Gruenbacher b8379c
+	struct richace *ace;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+	/*
Andreas Gruenbacher b8379c
+	 * @gmask contains all permissions which the group class is ever
Andreas Gruenbacher b8379c
+	 * allowed.  We use it to avoid adding permissions to the group mask
Andreas Gruenbacher b8379c
+	 * from everyone@ allow aces which the group class is always denied
Andreas Gruenbacher b8379c
+	 * through other aces.  For example, the following acl would otherwise
Andreas Gruenbacher b8379c
+	 * result in a group mask or rw:
Andreas Gruenbacher b8379c
+	 *
Andreas Gruenbacher b8379c
+	 *	group@:w::deny
Andreas Gruenbacher b8379c
+	 *	everyone@:rw::allow
Andreas Gruenbacher b8379c
+	 *
Andreas Gruenbacher b8379c
+	 * Avoid computing @gmask for acls which do not include any group class
Andreas Gruenbacher b8379c
+	 * deny aces: in such acls, the group class is never denied any
Andreas Gruenbacher b8379c
+	 * permissions from everyone@ allow aces.
Andreas Gruenbacher b8379c
+	 */
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+restart:
Andreas Gruenbacher b8379c
+	acl->a_owner_mask = 0;
Andreas Gruenbacher b8379c
+	acl->a_group_mask = 0;
Andreas Gruenbacher b8379c
+	acl->a_other_mask = 0;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+	richacl_for_each_entry_reverse(ace, acl) {
Andreas Gruenbacher b8379c
+		if (richace_is_inherit_only(ace))
Andreas Gruenbacher b8379c
+			continue;
Andreas Gruenbacher b8379c
+
Andreas Gruenbacher b8379c
+		if (richace_is_owner(ace)) {
Andreas Gruenbacher b8379c
+			if (richace_is_allow(ace))
Andreas Gruenbacher b8379c
+				acl->a_owner_mask |= ace->e_mask;
Andreas Gruenbacher b8379c
+			else if (richace_is_deny(ace))
Andreas Gruenbacher b8379c
+				acl->a_owner_mask &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+		} else if (richace_is_everyone(ace)) {
Andreas Gruenbacher b8379c
+			if (richace_is_allow(ace)) {
Andreas Gruenbacher b8379c
+				acl->a_owner_mask |= ace->e_mask;
Andreas Gruenbacher b8379c
+				acl->a_group_mask |= ace->e_mask & gmask;
Andreas Gruenbacher b8379c
+				acl->a_other_mask |= ace->e_mask;
Andreas Gruenbacher b8379c
+			} else if (richace_is_deny(ace)) {
Andreas Gruenbacher b8379c
+				acl->a_owner_mask &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+				acl->a_group_mask &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+				acl->a_other_mask &= ~ace->e_mask;
Andreas Gruenbacher b8379c
+			}
Andreas Gruenbacher b8379c
+		} else {
Andreas Gruenbacher b8379c
+			if (richace_is_allow(ace)) {
Andreas Gruenbacher b8379c
+				acl->a_owner_mask |= ace->e_mask & gmask;
Andreas Gruenbacher b8379c
+				acl->a_group_mask |= ace->e_mask & gmask;
Andreas Gruenbacher b8379c
+			} else if (richace_is_deny(ace) && gmask == ~0) {
Andreas Gruenbacher b8379c
+				gmask = richacl_group_class_allowed(acl);
Andreas Gruenbacher b8379c
+				if (likely(gmask != ~0))  /* should always be true */
Andreas Gruenbacher b8379c
+					goto restart;
Andreas Gruenbacher b8379c
+			}
Andreas Gruenbacher b8379c
+		}
Andreas Gruenbacher b8379c
+	}
Andreas Gruenbacher b8379c
+}
Andreas Gruenbacher b8379c
+EXPORT_SYMBOL_GPL(richacl_compute_max_masks);
Andreas Gruenbacher b8379c
--- a/include/linux/richacl.h
Andreas Gruenbacher b8379c
+++ b/include/linux/richacl.h
Jeff Mahoney a40327
@@ -268,5 +268,6 @@ extern int richace_set_who(struct richac
Andreas Gruenbacher b8379c
 extern int richacl_masks_to_mode(const struct richacl *);
Andreas Gruenbacher b8379c
 extern unsigned int richacl_mode_to_mask(mode_t);
Andreas Gruenbacher b8379c
 extern unsigned int richacl_want_to_mask(int);
Andreas Gruenbacher b8379c
+extern void richacl_compute_max_masks(struct richacl *);
Andreas Gruenbacher b8379c
 
Andreas Gruenbacher b8379c
 #endif /* __RICHACL_H */