1775 lines
51 KiB
Diff
1775 lines
51 KiB
Diff
|
From: Andreas Gruenbacher <agruen@suse.de>
|
||
|
Subject: NFSv4 ACL in-memory representation and manipulation
|
||
|
Patch-mainline: not yet
|
||
|
|
||
|
* In-memory representation (struct nfs4acl).
|
||
|
* Functionality a filesystem needs such as permission checking,
|
||
|
apply mode to acl, compute mode from acl, inheritance upon file
|
||
|
create.
|
||
|
* Compute a mask-less acl from struct nfs4acl that grants the same
|
||
|
permissions. Protocols which don't understand the masks need
|
||
|
this.
|
||
|
* Convert to/from xattrs.
|
||
|
|
||
|
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
||
|
|
||
|
---
|
||
|
fs/Kconfig | 4
|
||
|
fs/Makefile | 4
|
||
|
fs/nfs4acl_base.c | 566 +++++++++++++++++++++++++++++++
|
||
|
fs/nfs4acl_compat.c | 757 ++++++++++++++++++++++++++++++++++++++++++
|
||
|
fs/nfs4acl_xattr.c | 146 ++++++++
|
||
|
include/linux/nfs4acl.h | 205 +++++++++++
|
||
|
include/linux/nfs4acl_xattr.h | 32 +
|
||
|
7 files changed, 1714 insertions(+)
|
||
|
|
||
|
--- a/fs/Kconfig
|
||
|
+++ b/fs/Kconfig
|
||
|
@@ -39,6 +39,10 @@ config FS_POSIX_ACL
|
||
|
bool
|
||
|
default n
|
||
|
|
||
|
+config FS_NFS4ACL
|
||
|
+ bool
|
||
|
+ default n
|
||
|
+
|
||
|
source "fs/xfs/Kconfig"
|
||
|
source "fs/gfs2/Kconfig"
|
||
|
source "fs/ocfs2/Kconfig"
|
||
|
--- a/fs/Makefile
|
||
|
+++ b/fs/Makefile
|
||
|
@@ -51,6 +51,10 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.
|
||
|
obj-$(CONFIG_NFS_COMMON) += nfs_common/
|
||
|
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
|
||
|
|
||
|
+obj-$(CONFIG_FS_NFS4ACL) += nfs4acl.o
|
||
|
+nfs4acl-y := nfs4acl_base.o nfs4acl_xattr.o \
|
||
|
+ nfs4acl_compat.o
|
||
|
+
|
||
|
obj-y += quota/
|
||
|
|
||
|
obj-$(CONFIG_DMAPI) += dmapi/
|
||
|
--- /dev/null
|
||
|
+++ b/fs/nfs4acl_base.c
|
||
|
@@ -0,0 +1,567 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@computer.org>
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||
|
+ * under the terms of the GNU General Public License as published by the
|
||
|
+ * Free Software Foundation; either version 2, or (at your option) any
|
||
|
+ * later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful, but
|
||
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
+ * General Public License for more details.
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/sched.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/fs.h>
|
||
|
+#include <linux/fs_struct.h>
|
||
|
+#include <linux/nfs4acl.h>
|
||
|
+
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
+
|
||
|
+/*
|
||
|
+ * ACL entries that have ACE4_SPECIAL_WHO set in ace->e_flags use the
|
||
|
+ * pointer values of these constants in ace->u.e_who to avoid massive
|
||
|
+ * amounts of string comparisons.
|
||
|
+ */
|
||
|
+
|
||
|
+const char nfs4ace_owner_who[] = "OWNER@";
|
||
|
+const char nfs4ace_group_who[] = "GROUP@";
|
||
|
+const char nfs4ace_everyone_who[] = "EVERYONE@";
|
||
|
+
|
||
|
+EXPORT_SYMBOL(nfs4ace_owner_who);
|
||
|
+EXPORT_SYMBOL(nfs4ace_group_who);
|
||
|
+EXPORT_SYMBOL(nfs4ace_everyone_who);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_alloc - allocate an acl
|
||
|
+ * @count: number of entries
|
||
|
+ */
|
||
|
+struct nfs4acl *
|
||
|
+nfs4acl_alloc(int count)
|
||
|
+{
|
||
|
+ size_t size = sizeof(struct nfs4acl) + count * sizeof(struct nfs4ace);
|
||
|
+ struct nfs4acl *acl = kmalloc(size, GFP_KERNEL);
|
||
|
+
|
||
|
+ if (acl) {
|
||
|
+ memset(acl, 0, size);
|
||
|
+ atomic_set(&acl->a_refcount, 1);
|
||
|
+ acl->a_count = count;
|
||
|
+ }
|
||
|
+ return acl;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_alloc);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_clone - create a copy of an acl
|
||
|
+ */
|
||
|
+struct nfs4acl *
|
||
|
+nfs4acl_clone(const struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ int count = acl->a_count;
|
||
|
+ size_t size = sizeof(struct nfs4acl) + count * sizeof(struct nfs4ace);
|
||
|
+ struct nfs4acl *dup = kmalloc(size, GFP_KERNEL);
|
||
|
+
|
||
|
+ if (dup) {
|
||
|
+ memcpy(dup, acl, size);
|
||
|
+ atomic_set(&dup->a_refcount, 1);
|
||
|
+ }
|
||
|
+ return dup;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * The POSIX permissions are supersets of the below mask flags.
|
||
|
+ *
|
||
|
+ * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted
|
||
|
+ * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. We
|
||
|
+ * make sure that we do not mask them if they are set, so that users who
|
||
|
+ * rely on these flags won't get confused.
|
||
|
+ */
|
||
|
+#define ACE4_POSIX_MODE_READ ( \
|
||
|
+ ACE4_READ_DATA | ACE4_LIST_DIRECTORY )
|
||
|
+#define ACE4_POSIX_MODE_WRITE ( \
|
||
|
+ ACE4_WRITE_DATA | ACE4_ADD_FILE | \
|
||
|
+ ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \
|
||
|
+ ACE4_DELETE_CHILD )
|
||
|
+#define ACE4_POSIX_MODE_EXEC ( \
|
||
|
+ ACE4_EXECUTE)
|
||
|
+
|
||
|
+static int
|
||
|
+nfs4acl_mask_to_mode(unsigned int mask)
|
||
|
+{
|
||
|
+ int mode = 0;
|
||
|
+
|
||
|
+ if (mask & ACE4_POSIX_MODE_READ)
|
||
|
+ mode |= MAY_READ;
|
||
|
+ if (mask & ACE4_POSIX_MODE_WRITE)
|
||
|
+ mode |= MAY_WRITE;
|
||
|
+ if (mask & ACE4_POSIX_MODE_EXEC)
|
||
|
+ mode |= MAY_EXEC;
|
||
|
+
|
||
|
+ return mode;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_masks_to_mode - compute file mode permission bits from file masks
|
||
|
+ *
|
||
|
+ * Compute the file mode permission bits from the file masks in the acl.
|
||
|
+ */
|
||
|
+int
|
||
|
+nfs4acl_masks_to_mode(const struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ return nfs4acl_mask_to_mode(acl->a_owner_mask) << 6 |
|
||
|
+ nfs4acl_mask_to_mode(acl->a_group_mask) << 3 |
|
||
|
+ nfs4acl_mask_to_mode(acl->a_other_mask);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_masks_to_mode);
|
||
|
+
|
||
|
+static unsigned int
|
||
|
+nfs4acl_mode_to_mask(mode_t mode)
|
||
|
+{
|
||
|
+ unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED;
|
||
|
+
|
||
|
+ if (mode & MAY_READ)
|
||
|
+ mask |= ACE4_POSIX_MODE_READ;
|
||
|
+ if (mode & MAY_WRITE)
|
||
|
+ mask |= ACE4_POSIX_MODE_WRITE;
|
||
|
+ if (mode & MAY_EXEC)
|
||
|
+ mask |= ACE4_POSIX_MODE_EXEC;
|
||
|
+
|
||
|
+ return mask;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_chmod - update the file masks to reflect the new mode
|
||
|
+ * @mode: file mode permission bits to apply to the @acl
|
||
|
+ *
|
||
|
+ * Converts the mask flags corresponding to the owner, group, and other file
|
||
|
+ * permissions and computes the file masks. Returns @acl if it already has the
|
||
|
+ * appropriate file masks, or updates the flags in a copy of @acl. Takes over
|
||
|
+ * @acl.
|
||
|
+ */
|
||
|
+struct nfs4acl *
|
||
|
+nfs4acl_chmod(struct nfs4acl *acl, mode_t mode)
|
||
|
+{
|
||
|
+ unsigned int owner_mask, group_mask, other_mask;
|
||
|
+ struct nfs4acl *clone;
|
||
|
+
|
||
|
+ owner_mask = nfs4acl_mode_to_mask(mode >> 6);
|
||
|
+ group_mask = nfs4acl_mode_to_mask(mode >> 3);
|
||
|
+ other_mask = nfs4acl_mode_to_mask(mode);
|
||
|
+
|
||
|
+ if (acl->a_owner_mask == owner_mask &&
|
||
|
+ acl->a_group_mask == group_mask &&
|
||
|
+ acl->a_other_mask == other_mask)
|
||
|
+ return acl;
|
||
|
+
|
||
|
+ clone = nfs4acl_clone(acl);
|
||
|
+ nfs4acl_put(acl);
|
||
|
+ if (!clone)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+
|
||
|
+ clone->a_owner_mask = owner_mask;
|
||
|
+ clone->a_group_mask = group_mask;
|
||
|
+ clone->a_other_mask = other_mask;
|
||
|
+
|
||
|
+ if (nfs4acl_write_through(&clone)) {
|
||
|
+ nfs4acl_put(clone);
|
||
|
+ clone = ERR_PTR(-ENOMEM);
|
||
|
+ }
|
||
|
+ return clone;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_chmod);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_want_to_mask - convert permission want argument to a mask
|
||
|
+ * @want: @want argument of the permission inode operation
|
||
|
+ *
|
||
|
+ * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
|
||
|
+ */
|
||
|
+unsigned int
|
||
|
+nfs4acl_want_to_mask(int want)
|
||
|
+{
|
||
|
+ unsigned int mask = 0;
|
||
|
+
|
||
|
+ if (want & MAY_READ)
|
||
|
+ mask |= ACE4_READ_DATA;
|
||
|
+ if (want & MAY_APPEND)
|
||
|
+ mask |= ACE4_APPEND_DATA;
|
||
|
+ else if (want & MAY_WRITE)
|
||
|
+ mask |= ACE4_WRITE_DATA;
|
||
|
+ if (want & MAY_EXEC)
|
||
|
+ mask |= ACE4_EXECUTE;
|
||
|
+
|
||
|
+ return mask;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_want_to_mask);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_capability_check - check for capabilities overriding read/write access
|
||
|
+ * @inode: inode to check
|
||
|
+ * @mask: requested access (ACE4_* bitmask)
|
||
|
+ *
|
||
|
+ * Capabilities other than CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH must be checked
|
||
|
+ * separately.
|
||
|
+ */
|
||
|
+static inline int nfs4acl_capability_check(struct inode *inode, unsigned int mask)
|
||
|
+{
|
||
|
+ /*
|
||
|
+ * Read/write DACs are always overridable.
|
||
|
+ * Executable DACs are overridable if at least one exec bit is set.
|
||
|
+ */
|
||
|
+ if (!(mask & (ACE4_WRITE_ACL | ACE4_WRITE_OWNER)) &&
|
||
|
+ (!(mask & ACE4_EXECUTE) ||
|
||
|
+ (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)))
|
||
|
+ if (capable(CAP_DAC_OVERRIDE))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Searching includes executable on directories, else just read.
|
||
|
+ */
|
||
|
+ if (!(mask & ~(ACE4_READ_DATA | ACE4_EXECUTE)) &&
|
||
|
+ (S_ISDIR(inode->i_mode) || !(mask & ACE4_EXECUTE)))
|
||
|
+ if (capable(CAP_DAC_READ_SEARCH))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ return -EACCES;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_permission - permission check algorithm with masking
|
||
|
+ * @inode: inode to check
|
||
|
+ * @acl: nfs4 acl of the inode
|
||
|
+ * @mask: requested access (ACE4_* bitmask)
|
||
|
+ *
|
||
|
+ * Checks if the current process is granted @mask flags in @acl. With
|
||
|
+ * write-through, the OWNER@ is always granted the owner file mask, the
|
||
|
+ * GROUP@ is always granted the group file mask, and EVERYONE@ is always
|
||
|
+ * granted the other file mask. Otherwise, processes are only granted
|
||
|
+ * @mask flags which they are granted in the @acl as well as in their
|
||
|
+ * file mask.
|
||
|
+ */
|
||
|
+int nfs4acl_permission(struct inode *inode, const struct nfs4acl *acl,
|
||
|
+ unsigned int mask)
|
||
|
+{
|
||
|
+ const struct nfs4ace *ace;
|
||
|
+ unsigned int file_mask, requested = mask, denied = 0;
|
||
|
+ int in_owning_group = in_group_p(inode->i_gid);
|
||
|
+ int owner_or_group_class = in_owning_group;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A process is in the
|
||
|
+ * - owner file class if it owns the file, in the
|
||
|
+ * - group file class if it is in the file's owning group or
|
||
|
+ * it matches any of the user or group entries, and in the
|
||
|
+ * - other file class otherwise.
|
||
|
+ */
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry(ace, acl) {
|
||
|
+ unsigned int ace_mask = ace->e_mask;
|
||
|
+
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_owner(ace)) {
|
||
|
+ if (current_fsuid() != inode->i_uid)
|
||
|
+ continue;
|
||
|
+ goto is_owner;
|
||
|
+ } else if (nfs4ace_is_group(ace)) {
|
||
|
+ if (!in_owning_group)
|
||
|
+ continue;
|
||
|
+ } else if (nfs4ace_is_unix_id(ace)) {
|
||
|
+ if (ace->e_flags & ACE4_IDENTIFIER_GROUP) {
|
||
|
+ if (!in_group_p(ace->u.e_id))
|
||
|
+ continue;
|
||
|
+ } else {
|
||
|
+ if (current_fsuid() != ace->u.e_id)
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ goto is_everyone;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Apply the group file mask to entries other than OWNER@ and
|
||
|
+ * EVERYONE@. This is not required for correct access checking
|
||
|
+ * but ensures that we grant the same permissions as the acl
|
||
|
+ * computed by nfs4acl_apply_masks().
|
||
|
+ *
|
||
|
+ * For example, without this restriction, 'group@:rw::allow'
|
||
|
+ * with mode 0600 would grant rw access to owner processes
|
||
|
+ * which are also in the owning group. This cannot be expressed
|
||
|
+ * in an acl.
|
||
|
+ */
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ ace_mask &= acl->a_group_mask;
|
||
|
+
|
||
|
+ is_owner:
|
||
|
+ /* The process is in the owner or group file class. */
|
||
|
+ owner_or_group_class = 1;
|
||
|
+
|
||
|
+ is_everyone:
|
||
|
+ /* Check which mask flags the ACE allows or denies. */
|
||
|
+ if (nfs4ace_is_deny(ace))
|
||
|
+ denied |= ace_mask & mask;
|
||
|
+ mask &= ~ace_mask;
|
||
|
+
|
||
|
+ /* Keep going until we know which file class the process is in. */
|
||
|
+ if (!mask && owner_or_group_class)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ denied |= mask;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Figure out which file mask applies.
|
||
|
+ * Clear write-through if the process is in the file group class but
|
||
|
+ * not in the owning group, and so the denied permissions apply.
|
||
|
+ */
|
||
|
+ if (current_fsuid() == inode->i_uid)
|
||
|
+ file_mask = acl->a_owner_mask;
|
||
|
+ else if (in_owning_group || owner_or_group_class)
|
||
|
+ file_mask = acl->a_group_mask;
|
||
|
+ else
|
||
|
+ file_mask = acl->a_other_mask;
|
||
|
+
|
||
|
+ denied |= requested & ~file_mask;
|
||
|
+ if (!denied)
|
||
|
+ return 0;
|
||
|
+ return nfs4acl_capability_check(inode, requested);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_permission);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_generic_permission - permission check algorithm without explicit acl
|
||
|
+ * @inode: inode to check permissions for
|
||
|
+ * @mask: requested access (ACE4_* bitmask)
|
||
|
+ *
|
||
|
+ * The file mode of a file without ACL corresponds to an ACL with a single
|
||
|
+ * "EVERYONE:~0::ALLOW" entry, with file masks that correspond to the file mode
|
||
|
+ * permissions. Instead of constructing a temporary ACL and applying
|
||
|
+ * nfs4acl_permission() to it, compute the identical result directly from the file
|
||
|
+ * mode.
|
||
|
+ */
|
||
|
+int nfs4acl_generic_permission(struct inode *inode, unsigned int mask)
|
||
|
+{
|
||
|
+ int mode = inode->i_mode;
|
||
|
+
|
||
|
+ if (current_fsuid() == inode->i_uid)
|
||
|
+ mode >>= 6;
|
||
|
+ else if (in_group_p(inode->i_gid))
|
||
|
+ mode >>= 3;
|
||
|
+ if (!(mask & ~nfs4acl_mode_to_mask(mode)))
|
||
|
+ return 0;
|
||
|
+ return nfs4acl_capability_check(inode, mask);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_generic_permission);
|
||
|
+
|
||
|
+/*
|
||
|
+ * nfs4ace_is_same_who - do both acl entries refer to the same identifier?
|
||
|
+ */
|
||
|
+int
|
||
|
+nfs4ace_is_same_who(const struct nfs4ace *a, const struct nfs4ace *b)
|
||
|
+{
|
||
|
+#define WHO_FLAGS (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP)
|
||
|
+ if ((a->e_flags & WHO_FLAGS) != (b->e_flags & WHO_FLAGS))
|
||
|
+ return 0;
|
||
|
+ if (a->e_flags & ACE4_SPECIAL_WHO)
|
||
|
+ return a->u.e_who == b->u.e_who;
|
||
|
+ else
|
||
|
+ return a->u.e_id == b->u.e_id;
|
||
|
+#undef WHO_FLAGS
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_set_who - set a special who value
|
||
|
+ * @ace: acl entry
|
||
|
+ * @who: who value to use
|
||
|
+ */
|
||
|
+int
|
||
|
+nfs4ace_set_who(struct nfs4ace *ace, const char *who)
|
||
|
+{
|
||
|
+ if (!strcmp(who, nfs4ace_owner_who))
|
||
|
+ who = nfs4ace_owner_who;
|
||
|
+ else if (!strcmp(who, nfs4ace_group_who))
|
||
|
+ who = nfs4ace_group_who;
|
||
|
+ else if (!strcmp(who, nfs4ace_everyone_who))
|
||
|
+ who = nfs4ace_everyone_who;
|
||
|
+ else
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ ace->u.e_who = who;
|
||
|
+ ace->e_flags |= ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_flags &= ~ACE4_IDENTIFIER_GROUP;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4ace_set_who);
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_allowed_to_who - mask flags allowed to a specific who value
|
||
|
+ *
|
||
|
+ * Computes the mask values allowed to a specific who value, taking
|
||
|
+ * EVERYONE@ entries into account.
|
||
|
+ */
|
||
|
+static unsigned int
|
||
|
+nfs4acl_allowed_to_who(struct nfs4acl *acl, struct nfs4ace *who)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed = 0;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry_reverse(ace, acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_same_who(ace, who) ||
|
||
|
+ nfs4ace_is_everyone(ace)) {
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ allowed |= ace->e_mask;
|
||
|
+ else if (nfs4ace_is_deny(ace))
|
||
|
+ allowed &= ~ace->e_mask;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return allowed;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_compute_max_masks - compute upper bound masks
|
||
|
+ *
|
||
|
+ * Computes upper bound owner, group, and other masks so that none of
|
||
|
+ * the mask flags allowed by the acl are disabled (for any choice of the
|
||
|
+ * file owner or group membership).
|
||
|
+ */
|
||
|
+static void
|
||
|
+nfs4acl_compute_max_masks(struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+
|
||
|
+ acl->a_owner_mask = 0;
|
||
|
+ acl->a_group_mask = 0;
|
||
|
+ acl->a_other_mask = 0;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry_reverse(ace, acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (nfs4ace_is_owner(ace)) {
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ acl->a_owner_mask |= ace->e_mask;
|
||
|
+ else if (nfs4ace_is_deny(ace))
|
||
|
+ acl->a_owner_mask &= ~ace->e_mask;
|
||
|
+ } else if (nfs4ace_is_everyone(ace)) {
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ struct nfs4ace who = {
|
||
|
+ .e_flags = ACE4_SPECIAL_WHO,
|
||
|
+ .u.e_who = nfs4ace_group_who,
|
||
|
+ };
|
||
|
+
|
||
|
+ acl->a_other_mask |= ace->e_mask;
|
||
|
+ acl->a_group_mask |=
|
||
|
+ nfs4acl_allowed_to_who(acl, &who);
|
||
|
+ acl->a_owner_mask |= ace->e_mask;
|
||
|
+ } else if (nfs4ace_is_deny(ace)) {
|
||
|
+ acl->a_other_mask &= ~ace->e_mask;
|
||
|
+ acl->a_group_mask &= ~ace->e_mask;
|
||
|
+ acl->a_owner_mask &= ~ace->e_mask;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ unsigned int mask =
|
||
|
+ nfs4acl_allowed_to_who(acl, ace);
|
||
|
+
|
||
|
+ acl->a_group_mask |= mask;
|
||
|
+ acl->a_owner_mask |= mask;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_inherit - compute the acl a new file will inherit
|
||
|
+ * @dir_acl: acl of the containing direcory
|
||
|
+ * @mode: file type and create mode of the new file
|
||
|
+ *
|
||
|
+ * Given the containing directory's acl, this function will compute the
|
||
|
+ * acl that new files in that directory will inherit, or %NULL if
|
||
|
+ * @dir_acl does not contain acl entries inheritable by this file.
|
||
|
+ *
|
||
|
+ * Without write-through, the file masks in the returned acl are set to
|
||
|
+ * the intersection of the create mode and the maximum permissions
|
||
|
+ * allowed to each file class. With write-through, the file masks are
|
||
|
+ * set to the create mode.
|
||
|
+ */
|
||
|
+struct nfs4acl *
|
||
|
+nfs4acl_inherit(const struct nfs4acl *dir_acl, mode_t mode)
|
||
|
+{
|
||
|
+ const struct nfs4ace *dir_ace;
|
||
|
+ struct nfs4acl *acl;
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ int count = 0;
|
||
|
+
|
||
|
+ if (S_ISDIR(mode)) {
|
||
|
+ nfs4acl_for_each_entry(dir_ace, dir_acl) {
|
||
|
+ if (!nfs4ace_is_inheritable(dir_ace))
|
||
|
+ continue;
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ if (!count)
|
||
|
+ return NULL;
|
||
|
+ acl = nfs4acl_alloc(count);
|
||
|
+ if (!acl)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+ ace = acl->a_entries;
|
||
|
+ nfs4acl_for_each_entry(dir_ace, dir_acl) {
|
||
|
+ if (!nfs4ace_is_inheritable(dir_ace))
|
||
|
+ continue;
|
||
|
+ memcpy(ace, dir_ace, sizeof(struct nfs4ace));
|
||
|
+ if (dir_ace->e_flags & ACE4_NO_PROPAGATE_INHERIT_ACE)
|
||
|
+ nfs4ace_clear_inheritance_flags(ace);
|
||
|
+ if ((dir_ace->e_flags & ACE4_FILE_INHERIT_ACE) &&
|
||
|
+ !(dir_ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE))
|
||
|
+ ace->e_flags |= ACE4_INHERIT_ONLY_ACE;
|
||
|
+ ace++;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ nfs4acl_for_each_entry(dir_ace, dir_acl) {
|
||
|
+ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
|
||
|
+ continue;
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ if (!count)
|
||
|
+ return NULL;
|
||
|
+ acl = nfs4acl_alloc(count);
|
||
|
+ if (!acl)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+ ace = acl->a_entries;
|
||
|
+ nfs4acl_for_each_entry(dir_ace, dir_acl) {
|
||
|
+ if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
|
||
|
+ continue;
|
||
|
+ memcpy(ace, dir_ace, sizeof(struct nfs4ace));
|
||
|
+ nfs4ace_clear_inheritance_flags(ace);
|
||
|
+ ace++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* The maximum max flags that the owner, group, and other classes
|
||
|
+ are allowed. */
|
||
|
+ if (dir_acl->a_flags & ACL4_WRITE_THROUGH) {
|
||
|
+ acl->a_owner_mask = ACE4_VALID_MASK;
|
||
|
+ acl->a_group_mask = ACE4_VALID_MASK;
|
||
|
+ acl->a_other_mask = ACE4_VALID_MASK;
|
||
|
+
|
||
|
+ mode &= ~current->fs->umask;
|
||
|
+ } else
|
||
|
+ nfs4acl_compute_max_masks(acl);
|
||
|
+
|
||
|
+ /* Apply the create mode. */
|
||
|
+ acl->a_owner_mask &= nfs4acl_mode_to_mask(mode >> 6);
|
||
|
+ acl->a_group_mask &= nfs4acl_mode_to_mask(mode >> 3);
|
||
|
+ acl->a_other_mask &= nfs4acl_mode_to_mask(mode);
|
||
|
+
|
||
|
+ if (nfs4acl_write_through(&acl)) {
|
||
|
+ nfs4acl_put(acl);
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+ }
|
||
|
+
|
||
|
+ acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH);
|
||
|
+
|
||
|
+ return acl;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_inherit);
|
||
|
--- /dev/null
|
||
|
+++ b/fs/nfs4acl_compat.c
|
||
|
@@ -0,0 +1,758 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@computer.org>
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||
|
+ * under the terms of the GNU General Public License as published by the
|
||
|
+ * Free Software Foundation; either version 2, or (at your option) any
|
||
|
+ * later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful, but
|
||
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
+ * General Public License for more details.
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/fs.h>
|
||
|
+#include <linux/nfs4acl.h>
|
||
|
+
|
||
|
+/**
|
||
|
+ * struct nfs4acl_alloc - remember how many entries are actually allocated
|
||
|
+ * @acl: acl with a_count <= @count
|
||
|
+ * @count: the actual number of entries allocated in @acl
|
||
|
+ *
|
||
|
+ * We pass around this structure while modifying an acl, so that we do
|
||
|
+ * not have to reallocate when we remove existing entries followed by
|
||
|
+ * adding new entries.
|
||
|
+ */
|
||
|
+struct nfs4acl_alloc {
|
||
|
+ struct nfs4acl *acl;
|
||
|
+ unsigned int count;
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_delete_entry - delete an entry in an acl
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ * @ace: an entry in @x->acl
|
||
|
+ *
|
||
|
+ * Updates @ace so that it points to the entry before the deleted entry
|
||
|
+ * on return. (When deleting the first entry, @ace will point to the
|
||
|
+ * (non-existant) entry before the first entry). This behavior is the
|
||
|
+ * expected behavior when deleting entries while forward iterating over
|
||
|
+ * an acl.
|
||
|
+ */
|
||
|
+static void
|
||
|
+nfs4acl_delete_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace)
|
||
|
+{
|
||
|
+ void *end = x->acl->a_entries + x->acl->a_count;
|
||
|
+
|
||
|
+ memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
|
||
|
+ (*ace)--;
|
||
|
+ x->acl->a_count--;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_insert_entry - insert an entry in an acl
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ * @ace: entry before which the new entry shall be inserted
|
||
|
+ *
|
||
|
+ * Insert a new entry in @x->acl at position @ace, and zero-initialize
|
||
|
+ * it. This may require reallocating @x->acl.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4acl_insert_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace)
|
||
|
+{
|
||
|
+ if (x->count == x->acl->a_count) {
|
||
|
+ int n = *ace - x->acl->a_entries;
|
||
|
+ struct nfs4acl *acl2;
|
||
|
+
|
||
|
+ acl2 = nfs4acl_alloc(x->acl->a_count + 1);
|
||
|
+ if (!acl2)
|
||
|
+ return -1;
|
||
|
+ acl2->a_flags = x->acl->a_flags;
|
||
|
+ acl2->a_owner_mask = x->acl->a_owner_mask;
|
||
|
+ acl2->a_group_mask = x->acl->a_group_mask;
|
||
|
+ acl2->a_other_mask = x->acl->a_other_mask;
|
||
|
+ memcpy(acl2->a_entries, x->acl->a_entries,
|
||
|
+ n * sizeof(struct nfs4ace));
|
||
|
+ memcpy(acl2->a_entries + n + 1, *ace,
|
||
|
+ (x->acl->a_count - n) * sizeof(struct nfs4ace));
|
||
|
+ kfree(x->acl);
|
||
|
+ x->acl = acl2;
|
||
|
+ x->count = acl2->a_count;
|
||
|
+ *ace = acl2->a_entries + n;
|
||
|
+ } else {
|
||
|
+ void *end = x->acl->a_entries + x->acl->a_count;
|
||
|
+
|
||
|
+ memmove(*ace + 1, *ace, end - (void *)*ace);
|
||
|
+ x->acl->a_count++;
|
||
|
+ }
|
||
|
+ memset(*ace, 0, sizeof(struct nfs4ace));
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4ace_change_mask - change the mask in @ace to @mask
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ * @ace: entry to modify
|
||
|
+ * @mask: new mask for @ace
|
||
|
+ *
|
||
|
+ * Set the effective mask of @ace to @mask. This will require splitting
|
||
|
+ * off a separate acl entry if @ace is inheritable. In that case, the
|
||
|
+ * effective- only acl entry is inserted after the inheritable acl
|
||
|
+ * entry, end the inheritable acl entry is set to inheritable-only. If
|
||
|
+ * @mode is 0, either set the original acl entry to inheritable-only if
|
||
|
+ * it was inheritable, or remove it otherwise. The returned @ace points
|
||
|
+ * to the modified or inserted effective-only acl entry if that entry
|
||
|
+ * exists, to the entry that has become inheritable-only, or else to the
|
||
|
+ * previous entry in the acl. This is the expected behavior when
|
||
|
+ * modifying masks while forward iterating over an acl.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4ace_change_mask(struct nfs4acl_alloc *x, struct nfs4ace **ace,
|
||
|
+ unsigned int mask)
|
||
|
+{
|
||
|
+ if (mask && (*ace)->e_mask == mask)
|
||
|
+ return 0;
|
||
|
+ if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) {
|
||
|
+ if (nfs4ace_is_inheritable(*ace)) {
|
||
|
+ if (nfs4acl_insert_entry(x, ace))
|
||
|
+ return -1;
|
||
|
+ memcpy(*ace, *ace + 1, sizeof(struct nfs4ace));
|
||
|
+ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
|
||
|
+ (*ace)++;
|
||
|
+ nfs4ace_clear_inheritance_flags(*ace);
|
||
|
+ }
|
||
|
+ (*ace)->e_mask = mask;
|
||
|
+ } else {
|
||
|
+ if (nfs4ace_is_inheritable(*ace))
|
||
|
+ (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
|
||
|
+ else
|
||
|
+ nfs4acl_delete_entry(x, ace);
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_move_everyone_aces_down - move everyone@ acl entries to the end
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ *
|
||
|
+ * Move all everyone acl entries to the bottom of the acl so that only a
|
||
|
+ * single everyone@ allow acl entry remains at the end, and update the
|
||
|
+ * mask fields of all acl entries on the way. If everyone@ is not
|
||
|
+ * granted any permissions, no empty everyone@ acl entry is inserted.
|
||
|
+ *
|
||
|
+ * This transformation does not modify the permissions that the acl
|
||
|
+ * grants, but we need it to simplify successive transformations.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4acl_move_everyone_aces_down(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed = 0, denied = 0;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_everyone(ace)) {
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ allowed |= (ace->e_mask & ~denied);
|
||
|
+ else if (nfs4ace_is_deny(ace))
|
||
|
+ denied |= (ace->e_mask & ~allowed);
|
||
|
+ else
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_change_mask(x, &ace, 0))
|
||
|
+ return -1;
|
||
|
+ } else {
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, allowed |
|
||
|
+ (ace->e_mask & ~denied)))
|
||
|
+ return -1;
|
||
|
+ } else if (nfs4ace_is_deny(ace)) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, denied |
|
||
|
+ (ace->e_mask & ~allowed)))
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
|
||
|
+ struct nfs4ace *last_ace = ace - 1;
|
||
|
+
|
||
|
+ if (nfs4ace_is_everyone(last_ace) &&
|
||
|
+ nfs4ace_is_allow(last_ace) &&
|
||
|
+ nfs4ace_is_inherit_only(last_ace) &&
|
||
|
+ last_ace->e_mask == allowed)
|
||
|
+ last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE;
|
||
|
+ else {
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = allowed;
|
||
|
+ ace->u.e_who = nfs4ace_everyone_who;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * __nfs4acl_propagate_everyone - propagate everyone@ mask flags up for @who
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ * @who: identifier to propagate mask flags for
|
||
|
+ * @allow: mask flags to propagate up
|
||
|
+ *
|
||
|
+ * Propagate mask flags from the trailing everyone@ allow acl entry up
|
||
|
+ * for the specified @who.
|
||
|
+ *
|
||
|
+ * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an
|
||
|
+ * additional @who ALLOW entry, but with the following optimizations:
|
||
|
+ * (1) we don't bother setting any flags in the new @who ALLOW entry
|
||
|
+ * that has already been allowed or denied by a previous @who entry, (2)
|
||
|
+ * we merge the new @who entry with a previous @who entry if there is
|
||
|
+ * such a previous @who entry and there are no intervening DENY entries
|
||
|
+ * with mask flags that overlap the flags we care about.
|
||
|
+ */
|
||
|
+static int
|
||
|
+__nfs4acl_propagate_everyone(struct nfs4acl_alloc *x, struct nfs4ace *who,
|
||
|
+ unsigned int allow)
|
||
|
+{
|
||
|
+ struct nfs4ace *allow_last = NULL, *ace;
|
||
|
+
|
||
|
+ /* Remove the mask flags from allow that are already determined for
|
||
|
+ this who value, and figure out if there is an ALLOW entry for
|
||
|
+ this who value that is "reachable" from the trailing EVERYONE@
|
||
|
+ ALLOW ACE. */
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ if (nfs4ace_is_same_who(ace, who)) {
|
||
|
+ allow &= ~ace->e_mask;
|
||
|
+ allow_last = ace;
|
||
|
+ }
|
||
|
+ } else if (nfs4ace_is_deny(ace)) {
|
||
|
+ if (nfs4ace_is_same_who(ace, who))
|
||
|
+ allow &= ~ace->e_mask;
|
||
|
+ if (allow & ace->e_mask)
|
||
|
+ allow_last = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (allow) {
|
||
|
+ if (allow_last)
|
||
|
+ return nfs4ace_change_mask(x, &allow_last,
|
||
|
+ allow_last->e_mask | allow);
|
||
|
+ else {
|
||
|
+ struct nfs4ace who_copy;
|
||
|
+
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count - 1;
|
||
|
+ memcpy(&who_copy, who, sizeof(struct nfs4ace));
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ memcpy(ace, &who_copy, sizeof(struct nfs4ace));
|
||
|
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+ nfs4ace_clear_inheritance_flags(ace);
|
||
|
+ ace->e_mask = allow;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_propagate_everyone - propagate everyone@ mask flags up the acl
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ *
|
||
|
+ * Make sure for owner@, group@, and all other users, groups, and
|
||
|
+ * special identifiers that they are allowed or denied all permissions
|
||
|
+ * that are granted be the trailing everyone@ acl entry. If they are
|
||
|
+ * not, try to add the missing permissions to existing allow acl entries
|
||
|
+ * for those users, or introduce additional acl entries if that is not
|
||
|
+ * possible.
|
||
|
+ *
|
||
|
+ * We do this so that no mask flags will get lost when finally applying
|
||
|
+ * the file masks to the acl entries: otherwise, with an other file mask
|
||
|
+ * that is more restrictive than the owner and/or group file mask, mask
|
||
|
+ * flags that were allowed to processes in the owner and group classes
|
||
|
+ * and that the other mask denies would be lost. For example, the
|
||
|
+ * following two acls show the problem when mode 0664 is applied to
|
||
|
+ * them:
|
||
|
+ *
|
||
|
+ * masking without propagation (wrong)
|
||
|
+ * ===========================================================
|
||
|
+ * joe:r::allow => joe:r::allow
|
||
|
+ * everyone@:rwx::allow => everyone@:r::allow
|
||
|
+ * -----------------------------------------------------------
|
||
|
+ * joe:w::deny => joe:w::deny
|
||
|
+ * everyone@:rwx::allow everyone@:r::allow
|
||
|
+ *
|
||
|
+ * Note that the permissions of joe end up being more restrictive than
|
||
|
+ * what the acl would allow when first computing the allowed flags and
|
||
|
+ * then applying the respective mask. With propagation of permissions,
|
||
|
+ * we get:
|
||
|
+ *
|
||
|
+ * masking after propagation (correct)
|
||
|
+ * ===========================================================
|
||
|
+ * joe:r::allow => joe:rw::allow
|
||
|
+ * owner@:rw::allow
|
||
|
+ * group@:rw::allow
|
||
|
+ * everyone@:rwx::allow everyone@:r::allow
|
||
|
+ * -----------------------------------------------------------
|
||
|
+ * joe:w::deny => owner@:x::deny
|
||
|
+ * joe:w::deny
|
||
|
+ * owner@:rw::allow
|
||
|
+ * owner@:rw::allow
|
||
|
+ * joe:r::allow
|
||
|
+ * everyone@:rwx::allow everyone@:r::allow
|
||
|
+ *
|
||
|
+ * The examples show the acls that would result from propagation with no
|
||
|
+ * masking performed. In fact, we do apply the respective mask to the
|
||
|
+ * acl entries before computing the propagation because this will save
|
||
|
+ * us from adding acl entries that would end up with empty mask fields
|
||
|
+ * after applying the masks.
|
||
|
+ *
|
||
|
+ * It is ensured that no more than one entry will be inserted for each
|
||
|
+ * who value, no matter how many entries each who value has already.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4acl_propagate_everyone(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH);
|
||
|
+ struct nfs4ace who = { .e_flags = ACE4_SPECIAL_WHO };
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int owner_allow, group_allow;
|
||
|
+ int retval;
|
||
|
+
|
||
|
+ if (!((x->acl->a_owner_mask | x->acl->a_group_mask) &
|
||
|
+ ~x->acl->a_other_mask))
|
||
|
+ return 0;
|
||
|
+ if (!x->acl->a_count)
|
||
|
+ return 0;
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count - 1;
|
||
|
+ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace))
|
||
|
+ return 0;
|
||
|
+ if (!(ace->e_mask & ~x->acl->a_other_mask)) {
|
||
|
+ /* None of the allowed permissions will get masked. */
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ owner_allow = ace->e_mask & x->acl->a_owner_mask;
|
||
|
+ group_allow = ace->e_mask & x->acl->a_group_mask;
|
||
|
+
|
||
|
+ /* Propagate everyone@ permissions through to owner@. */
|
||
|
+ if (owner_allow && !write_through &&
|
||
|
+ (x->acl->a_owner_mask & ~x->acl->a_other_mask)) {
|
||
|
+ who.u.e_who = nfs4ace_owner_who;
|
||
|
+ retval = __nfs4acl_propagate_everyone(x, &who, owner_allow);
|
||
|
+ if (retval)
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) {
|
||
|
+ int n;
|
||
|
+
|
||
|
+ if (!write_through) {
|
||
|
+ /* Propagate everyone@ permissions through to group@. */
|
||
|
+ who.u.e_who = nfs4ace_group_who;
|
||
|
+ retval = __nfs4acl_propagate_everyone(x, &who,
|
||
|
+ group_allow);
|
||
|
+ if (retval)
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Start from the entry before the trailing EVERYONE@ ALLOW
|
||
|
+ entry. We will not hit EVERYONE@ entries in the loop. */
|
||
|
+ for (n = x->acl->a_count - 2; n != -1; n--) {
|
||
|
+ ace = x->acl->a_entries + n;
|
||
|
+
|
||
|
+ if (nfs4ace_is_inherit_only(ace) ||
|
||
|
+ nfs4ace_is_owner(ace) ||
|
||
|
+ nfs4ace_is_group(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_allow(ace) || nfs4ace_is_deny(ace)) {
|
||
|
+ /* Any inserted entry will end up below the
|
||
|
+ current entry. */
|
||
|
+ retval = __nfs4acl_propagate_everyone(x, ace,
|
||
|
+ group_allow);
|
||
|
+ if (retval)
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * __nfs4acl_apply_masks - apply the masks to the acl entries
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ *
|
||
|
+ * Apply the owner file mask to owner@ entries, the intersection of the
|
||
|
+ * group and other file masks to everyone@ entries, and the group file
|
||
|
+ * mask to all other entries.
|
||
|
+ */
|
||
|
+static int
|
||
|
+__nfs4acl_apply_masks(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ unsigned int mask;
|
||
|
+
|
||
|
+ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_allow(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_owner(ace))
|
||
|
+ mask = x->acl->a_owner_mask;
|
||
|
+ else if (nfs4ace_is_everyone(ace))
|
||
|
+ mask = x->acl->a_other_mask;
|
||
|
+ else
|
||
|
+ mask = x->acl->a_group_mask;
|
||
|
+ if (nfs4ace_change_mask(x, &ace, ace->e_mask & mask))
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_max_allowed - maximum mask flags that anybody is allowed
|
||
|
+ */
|
||
|
+static unsigned int
|
||
|
+nfs4acl_max_allowed(struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed = 0;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry_reverse(ace, acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ allowed |= ace->e_mask;
|
||
|
+ else if (nfs4ace_is_deny(ace)) {
|
||
|
+ if (nfs4ace_is_everyone(ace))
|
||
|
+ allowed &= ~ace->e_mask;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return allowed;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_isolate_owner_class - limit the owner class to the owner file mask
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ *
|
||
|
+ * Make sure the owner class (owner@) is granted no more than the owner
|
||
|
+ * mask by first checking which permissions anyone is granted, and then
|
||
|
+ * denying owner@ all permissions beyond that.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4acl_isolate_owner_class(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed = 0;
|
||
|
+
|
||
|
+ allowed = nfs4acl_max_allowed(x->acl);
|
||
|
+ if (allowed & ~x->acl->a_owner_mask) {
|
||
|
+ /* Figure out if we can update an existig OWNER@ DENY entry. */
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_deny(ace)) {
|
||
|
+ if (nfs4ace_is_owner(ace))
|
||
|
+ break;
|
||
|
+ } else if (nfs4ace_is_allow(ace)) {
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (ace != x->acl->a_entries + x->acl->a_count) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, ace->e_mask |
|
||
|
+ (allowed & ~x->acl->a_owner_mask)))
|
||
|
+ return -1;
|
||
|
+ } else {
|
||
|
+ /* Insert an owner@ deny entry at the front. */
|
||
|
+ ace = x->acl->a_entries;
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = allowed & ~x->acl->a_owner_mask;
|
||
|
+ ace->u.e_who = nfs4ace_owner_who;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * __nfs4acl_isolate_who - isolate entry from EVERYONE@ ALLOW entry
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ * @who: identifier to isolate
|
||
|
+ * @deny: mask flags this identifier should not be allowed
|
||
|
+ *
|
||
|
+ * Make sure that @who is not allowed any mask flags in @deny by checking
|
||
|
+ * which mask flags this identifier is allowed, and adding excess allowed
|
||
|
+ * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW
|
||
|
+ * entry, or inserting such an entry.
|
||
|
+ */
|
||
|
+static int
|
||
|
+__nfs4acl_isolate_who(struct nfs4acl_alloc *x, struct nfs4ace *who,
|
||
|
+ unsigned int deny)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed = 0, n;
|
||
|
+
|
||
|
+ /* Compute the mask flags granted to this who value. */
|
||
|
+ nfs4acl_for_each_entry_reverse(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_same_who(ace, who)) {
|
||
|
+ if (nfs4ace_is_allow(ace))
|
||
|
+ allowed |= ace->e_mask;
|
||
|
+ else if (nfs4ace_is_deny(ace))
|
||
|
+ allowed &= ~ace->e_mask;
|
||
|
+ deny &= ~ace->e_mask;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (!deny)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /* Figure out if we can update an existig DENY entry. Start
|
||
|
+ from the entry before the trailing EVERYONE@ ALLOW entry. We
|
||
|
+ will not hit EVERYONE@ entries in the loop. */
|
||
|
+ for (n = x->acl->a_count - 2; n != -1; n--) {
|
||
|
+ ace = x->acl->a_entries + n;
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_deny(ace)) {
|
||
|
+ if (nfs4ace_is_same_who(ace, who))
|
||
|
+ break;
|
||
|
+ } else if (nfs4ace_is_allow(ace) &&
|
||
|
+ (ace->e_mask & deny)) {
|
||
|
+ n = -1;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (n != -1) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, ace->e_mask | deny))
|
||
|
+ return -1;
|
||
|
+ } else {
|
||
|
+ /* Insert a eny entry before the trailing EVERYONE@ DENY
|
||
|
+ entry. */
|
||
|
+ struct nfs4ace who_copy;
|
||
|
+
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count - 1;
|
||
|
+ memcpy(&who_copy, who, sizeof(struct nfs4ace));
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ memcpy(ace, &who_copy, sizeof(struct nfs4ace));
|
||
|
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
|
||
|
+ nfs4ace_clear_inheritance_flags(ace);
|
||
|
+ ace->e_mask = deny;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_isolate_group_class - limit the group class to the group file mask
|
||
|
+ * @x: acl and number of allocated entries
|
||
|
+ *
|
||
|
+ * Make sure the group class (all entries except owner@ and everyone@) is
|
||
|
+ * granted no more than the group mask by inserting DENY entries for group
|
||
|
+ * class entries where necessary.
|
||
|
+ */
|
||
|
+static int
|
||
|
+nfs4acl_isolate_group_class(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ struct nfs4ace who = {
|
||
|
+ .e_flags = ACE4_SPECIAL_WHO,
|
||
|
+ .u.e_who = nfs4ace_group_who,
|
||
|
+ };
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int deny;
|
||
|
+
|
||
|
+ if (!x->acl->a_count)
|
||
|
+ return 0;
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count - 1;
|
||
|
+ if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace))
|
||
|
+ return 0;
|
||
|
+ deny = ace->e_mask & ~x->acl->a_group_mask;
|
||
|
+
|
||
|
+ if (deny) {
|
||
|
+ unsigned int n;
|
||
|
+
|
||
|
+ if (__nfs4acl_isolate_who(x, &who, deny))
|
||
|
+ return -1;
|
||
|
+
|
||
|
+ /* Start from the entry before the trailing EVERYONE@ ALLOW
|
||
|
+ entry. We will not hit EVERYONE@ entries in the loop. */
|
||
|
+ for (n = x->acl->a_count - 2; n != -1; n--) {
|
||
|
+ ace = x->acl->a_entries + n;
|
||
|
+
|
||
|
+ if (nfs4ace_is_inherit_only(ace) ||
|
||
|
+ nfs4ace_is_owner(ace) ||
|
||
|
+ nfs4ace_is_group(ace))
|
||
|
+ continue;
|
||
|
+ if (__nfs4acl_isolate_who(x, ace, deny))
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * __nfs4acl_write_through - grant the full masks to owner@, group@, everyone@
|
||
|
+ *
|
||
|
+ * Make sure that owner, group@, and everyone@ are allowed the full mask
|
||
|
+ * permissions, and not only the permissions granted both by the acl and
|
||
|
+ * the masks.
|
||
|
+ */
|
||
|
+static int
|
||
|
+__nfs4acl_write_through(struct nfs4acl_alloc *x)
|
||
|
+{
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ unsigned int allowed;
|
||
|
+
|
||
|
+ /* Remove all owner@ and group@ ACEs: we re-insert them at the
|
||
|
+ top. */
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if ((nfs4ace_is_owner(ace) || nfs4ace_is_group(ace)) &&
|
||
|
+ nfs4ace_change_mask(x, &ace, 0))
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Insert the everyone@ allow entry at the end, or update the
|
||
|
+ existing entry. */
|
||
|
+ allowed = x->acl->a_other_mask;
|
||
|
+ if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count - 1;
|
||
|
+ if (x->acl->a_count && nfs4ace_is_everyone(ace) &&
|
||
|
+ !nfs4ace_is_inherit_only(ace)) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, allowed))
|
||
|
+ return -1;
|
||
|
+ } else {
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count;
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = allowed;
|
||
|
+ ace->u.e_who = nfs4ace_everyone_who;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Compute the permissions that owner@ and group@ are already granted
|
||
|
+ though the everyone@ allow entry at the end. Note that the acl
|
||
|
+ contains no owner@ or group@ entries at this point. */
|
||
|
+ allowed = 0;
|
||
|
+ nfs4acl_for_each_entry_reverse(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ if (nfs4ace_is_everyone(ace))
|
||
|
+ allowed |= ace->e_mask;
|
||
|
+ } else if (nfs4ace_is_deny(ace))
|
||
|
+ allowed &= ~ace->e_mask;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Insert the appropriate group@ allow entry at the front. */
|
||
|
+ if (x->acl->a_group_mask & ~allowed) {
|
||
|
+ ace = x->acl->a_entries;
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = x->acl->a_group_mask /*& ~allowed*/;
|
||
|
+ ace->u.e_who = nfs4ace_group_who;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Insert the appropriate owner@ allow entry at the front. */
|
||
|
+ if (x->acl->a_owner_mask & ~allowed) {
|
||
|
+ ace = x->acl->a_entries;
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = x->acl->a_owner_mask /*& ~allowed*/;
|
||
|
+ ace->u.e_who = nfs4ace_owner_who;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Insert the appropriate owner@ deny entry at the front. */
|
||
|
+ allowed = nfs4acl_max_allowed(x->acl);
|
||
|
+ if (allowed & ~x->acl->a_owner_mask) {
|
||
|
+ nfs4acl_for_each_entry(ace, x->acl) {
|
||
|
+ if (nfs4ace_is_inherit_only(ace))
|
||
|
+ continue;
|
||
|
+ if (nfs4ace_is_allow(ace)) {
|
||
|
+ ace = x->acl->a_entries + x->acl->a_count;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ if (nfs4ace_is_deny(ace) && nfs4ace_is_owner(ace))
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ if (ace != x->acl->a_entries + x->acl->a_count) {
|
||
|
+ if (nfs4ace_change_mask(x, &ace, ace->e_mask |
|
||
|
+ (allowed & ~x->acl->a_owner_mask)))
|
||
|
+ return -1;
|
||
|
+ } else {
|
||
|
+ ace = x->acl->a_entries;
|
||
|
+ if (nfs4acl_insert_entry(x, &ace))
|
||
|
+ return -1;
|
||
|
+ ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
|
||
|
+ ace->e_flags = ACE4_SPECIAL_WHO;
|
||
|
+ ace->e_mask = allowed & ~x->acl->a_owner_mask;
|
||
|
+ ace->u.e_who = nfs4ace_owner_who;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * nfs4acl_apply_masks - apply the masks to the acl
|
||
|
+ *
|
||
|
+ * Apply the masks so that the acl allows no more flags than the
|
||
|
+ * intersection between the flags that the original acl allows and the
|
||
|
+ * mask matching the process.
|
||
|
+ *
|
||
|
+ * Note: this algorithm may push the number of entries in the acl above
|
||
|
+ * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail.
|
||
|
+ */
|
||
|
+int
|
||
|
+nfs4acl_apply_masks(struct nfs4acl **acl)
|
||
|
+{
|
||
|
+ struct nfs4acl_alloc x = {
|
||
|
+ .acl = *acl,
|
||
|
+ .count = (*acl)->a_count,
|
||
|
+ };
|
||
|
+ int retval = 0;
|
||
|
+
|
||
|
+ if (nfs4acl_move_everyone_aces_down(&x) ||
|
||
|
+ nfs4acl_propagate_everyone(&x) ||
|
||
|
+ __nfs4acl_apply_masks(&x) ||
|
||
|
+ nfs4acl_isolate_owner_class(&x) ||
|
||
|
+ nfs4acl_isolate_group_class(&x))
|
||
|
+ retval = -ENOMEM;
|
||
|
+
|
||
|
+ *acl = x.acl;
|
||
|
+ return retval;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_apply_masks);
|
||
|
+
|
||
|
+int nfs4acl_write_through(struct nfs4acl **acl)
|
||
|
+{
|
||
|
+ struct nfs4acl_alloc x = {
|
||
|
+ .acl = *acl,
|
||
|
+ .count = (*acl)->a_count,
|
||
|
+ };
|
||
|
+ int retval = 0;
|
||
|
+
|
||
|
+ if (!((*acl)->a_flags & ACL4_WRITE_THROUGH))
|
||
|
+ goto out;
|
||
|
+
|
||
|
+ if (nfs4acl_move_everyone_aces_down(&x) ||
|
||
|
+ nfs4acl_propagate_everyone(&x) ||
|
||
|
+ __nfs4acl_write_through(&x))
|
||
|
+ retval = -ENOMEM;
|
||
|
+
|
||
|
+ *acl = x.acl;
|
||
|
+out:
|
||
|
+ return retval;
|
||
|
+}
|
||
|
--- /dev/null
|
||
|
+++ b/fs/nfs4acl_xattr.c
|
||
|
@@ -0,0 +1,146 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@computer.org>
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||
|
+ * under the terms of the GNU General Public License as published by the
|
||
|
+ * Free Software Foundation; either version 2, or (at your option) any
|
||
|
+ * later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful, but
|
||
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
+ * General Public License for more details.
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/fs.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/nfs4acl_xattr.h>
|
||
|
+
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
+
|
||
|
+struct nfs4acl *
|
||
|
+nfs4acl_from_xattr(const void *value, size_t size)
|
||
|
+{
|
||
|
+ const struct nfs4acl_xattr *xattr_acl = value;
|
||
|
+ const struct nfs4ace_xattr *xattr_ace = (void *)(xattr_acl + 1);
|
||
|
+ struct nfs4acl *acl;
|
||
|
+ struct nfs4ace *ace;
|
||
|
+ int count;
|
||
|
+
|
||
|
+ if (size < sizeof(struct nfs4acl_xattr) ||
|
||
|
+ xattr_acl->a_version != ACL4_XATTR_VERSION ||
|
||
|
+ (xattr_acl->a_flags & ~ACL4_VALID_FLAGS))
|
||
|
+ return ERR_PTR(-EINVAL);
|
||
|
+
|
||
|
+ count = be16_to_cpu(xattr_acl->a_count);
|
||
|
+ if (count > ACL4_XATTR_MAX_COUNT)
|
||
|
+ return ERR_PTR(-EINVAL);
|
||
|
+
|
||
|
+ acl = nfs4acl_alloc(count);
|
||
|
+ if (!acl)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+
|
||
|
+ acl->a_flags = xattr_acl->a_flags;
|
||
|
+ acl->a_owner_mask = be32_to_cpu(xattr_acl->a_owner_mask);
|
||
|
+ if (acl->a_owner_mask & ~ACE4_VALID_MASK)
|
||
|
+ goto fail_einval;
|
||
|
+ acl->a_group_mask = be32_to_cpu(xattr_acl->a_group_mask);
|
||
|
+ if (acl->a_group_mask & ~ACE4_VALID_MASK)
|
||
|
+ goto fail_einval;
|
||
|
+ acl->a_other_mask = be32_to_cpu(xattr_acl->a_other_mask);
|
||
|
+ if (acl->a_other_mask & ~ACE4_VALID_MASK)
|
||
|
+ goto fail_einval;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry(ace, acl) {
|
||
|
+ const char *who = (void *)(xattr_ace + 1), *end;
|
||
|
+ ssize_t used = (void *)who - value;
|
||
|
+
|
||
|
+ if (used > size)
|
||
|
+ goto fail_einval;
|
||
|
+ end = memchr(who, 0, size - used);
|
||
|
+ if (!end)
|
||
|
+ goto fail_einval;
|
||
|
+
|
||
|
+ ace->e_type = be16_to_cpu(xattr_ace->e_type);
|
||
|
+ ace->e_flags = be16_to_cpu(xattr_ace->e_flags);
|
||
|
+ ace->e_mask = be32_to_cpu(xattr_ace->e_mask);
|
||
|
+ ace->u.e_id = be32_to_cpu(xattr_ace->e_id);
|
||
|
+
|
||
|
+ if (ace->e_flags & ~ACE4_VALID_FLAGS) {
|
||
|
+ memset(ace, 0, sizeof(struct nfs4ace));
|
||
|
+ goto fail_einval;
|
||
|
+ }
|
||
|
+ if (ace->e_type > ACE4_ACCESS_DENIED_ACE_TYPE ||
|
||
|
+ (ace->e_mask & ~ACE4_VALID_MASK))
|
||
|
+ goto fail_einval;
|
||
|
+
|
||
|
+ if (who == end) {
|
||
|
+ if (ace->u.e_id == -1)
|
||
|
+ goto fail_einval; /* uid/gid needed */
|
||
|
+ } else if (nfs4ace_set_who(ace, who))
|
||
|
+ goto fail_einval;
|
||
|
+
|
||
|
+ xattr_ace = (void *)who + ALIGN(end - who + 1, 4);
|
||
|
+ }
|
||
|
+
|
||
|
+ return acl;
|
||
|
+
|
||
|
+fail_einval:
|
||
|
+ nfs4acl_put(acl);
|
||
|
+ return ERR_PTR(-EINVAL);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_from_xattr);
|
||
|
+
|
||
|
+size_t
|
||
|
+nfs4acl_xattr_size(const struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ size_t size = sizeof(struct nfs4acl_xattr);
|
||
|
+ const struct nfs4ace *ace;
|
||
|
+
|
||
|
+ nfs4acl_for_each_entry(ace, acl) {
|
||
|
+ size += sizeof(struct nfs4ace_xattr) +
|
||
|
+ (nfs4ace_is_unix_id(ace) ? 4 :
|
||
|
+ ALIGN(strlen(ace->u.e_who) + 1, 4));
|
||
|
+ }
|
||
|
+ return size;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_xattr_size);
|
||
|
+
|
||
|
+void
|
||
|
+nfs4acl_to_xattr(const struct nfs4acl *acl, void *buffer)
|
||
|
+{
|
||
|
+ struct nfs4acl_xattr *xattr_acl = buffer;
|
||
|
+ struct nfs4ace_xattr *xattr_ace;
|
||
|
+ const struct nfs4ace *ace;
|
||
|
+
|
||
|
+ xattr_acl->a_version = ACL4_XATTR_VERSION;
|
||
|
+ xattr_acl->a_flags = acl->a_flags;
|
||
|
+ xattr_acl->a_count = cpu_to_be16(acl->a_count);
|
||
|
+
|
||
|
+ xattr_acl->a_owner_mask = cpu_to_be32(acl->a_owner_mask);
|
||
|
+ xattr_acl->a_group_mask = cpu_to_be32(acl->a_group_mask);
|
||
|
+ xattr_acl->a_other_mask = cpu_to_be32(acl->a_other_mask);
|
||
|
+
|
||
|
+ xattr_ace = (void *)(xattr_acl + 1);
|
||
|
+ nfs4acl_for_each_entry(ace, acl) {
|
||
|
+ xattr_ace->e_type = cpu_to_be16(ace->e_type);
|
||
|
+ xattr_ace->e_flags = cpu_to_be16(ace->e_flags &
|
||
|
+ ACE4_VALID_FLAGS);
|
||
|
+ xattr_ace->e_mask = cpu_to_be32(ace->e_mask);
|
||
|
+ if (nfs4ace_is_unix_id(ace)) {
|
||
|
+ xattr_ace->e_id = cpu_to_be32(ace->u.e_id);
|
||
|
+ memset(xattr_ace->e_who, 0, 4);
|
||
|
+ xattr_ace = (void *)xattr_ace->e_who + 4;
|
||
|
+ } else {
|
||
|
+ int sz = ALIGN(strlen(ace->u.e_who) + 1, 4);
|
||
|
+
|
||
|
+ xattr_ace->e_id = cpu_to_be32(-1);
|
||
|
+ memset(xattr_ace->e_who + sz - 4, 0, 4);
|
||
|
+ strcpy(xattr_ace->e_who, ace->u.e_who);
|
||
|
+ xattr_ace = (void *)xattr_ace->e_who + sz;
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(nfs4acl_to_xattr);
|
||
|
--- /dev/null
|
||
|
+++ b/include/linux/nfs4acl.h
|
||
|
@@ -0,0 +1,205 @@
|
||
|
+#ifndef __NFS4ACL_H
|
||
|
+#define __NFS4ACL_H
|
||
|
+
|
||
|
+struct nfs4ace {
|
||
|
+ unsigned short e_type;
|
||
|
+ unsigned short e_flags;
|
||
|
+ unsigned int e_mask;
|
||
|
+ union {
|
||
|
+ unsigned int e_id;
|
||
|
+ const char *e_who;
|
||
|
+ } u;
|
||
|
+};
|
||
|
+
|
||
|
+struct nfs4acl {
|
||
|
+ atomic_t a_refcount;
|
||
|
+ unsigned int a_owner_mask;
|
||
|
+ unsigned int a_group_mask;
|
||
|
+ unsigned int a_other_mask;
|
||
|
+ unsigned short a_count;
|
||
|
+ unsigned short a_flags;
|
||
|
+ struct nfs4ace a_entries[0];
|
||
|
+};
|
||
|
+
|
||
|
+#define nfs4acl_for_each_entry(_ace, _acl) \
|
||
|
+ for (_ace = _acl->a_entries; \
|
||
|
+ _ace != _acl->a_entries + _acl->a_count; \
|
||
|
+ _ace++)
|
||
|
+
|
||
|
+#define nfs4acl_for_each_entry_reverse(_ace, _acl) \
|
||
|
+ for (_ace = _acl->a_entries + _acl->a_count - 1; \
|
||
|
+ _ace != _acl->a_entries - 1; \
|
||
|
+ _ace--)
|
||
|
+
|
||
|
+/* a_flags values */
|
||
|
+#define ACL4_WRITE_THROUGH 0x40
|
||
|
+
|
||
|
+#define ACL4_VALID_FLAGS \
|
||
|
+ ACL4_WRITE_THROUGH
|
||
|
+
|
||
|
+/* e_type values */
|
||
|
+#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000
|
||
|
+#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001
|
||
|
+/*#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x0002*/
|
||
|
+/*#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x0003*/
|
||
|
+
|
||
|
+/* e_flags bitflags */
|
||
|
+#define ACE4_FILE_INHERIT_ACE 0x0001
|
||
|
+#define ACE4_DIRECTORY_INHERIT_ACE 0x0002
|
||
|
+#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004
|
||
|
+#define ACE4_INHERIT_ONLY_ACE 0x0008
|
||
|
+/*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/
|
||
|
+/*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/
|
||
|
+#define ACE4_IDENTIFIER_GROUP 0x0040
|
||
|
+#define ACE4_SPECIAL_WHO 0x4000 /* in-memory representation only */
|
||
|
+
|
||
|
+#define ACE4_VALID_FLAGS ( \
|
||
|
+ ACE4_FILE_INHERIT_ACE | \
|
||
|
+ ACE4_DIRECTORY_INHERIT_ACE | \
|
||
|
+ ACE4_NO_PROPAGATE_INHERIT_ACE | \
|
||
|
+ ACE4_INHERIT_ONLY_ACE | \
|
||
|
+ ACE4_IDENTIFIER_GROUP )
|
||
|
+
|
||
|
+/* e_mask bitflags */
|
||
|
+#define ACE4_READ_DATA 0x00000001
|
||
|
+#define ACE4_LIST_DIRECTORY 0x00000001
|
||
|
+#define ACE4_WRITE_DATA 0x00000002
|
||
|
+#define ACE4_ADD_FILE 0x00000002
|
||
|
+#define ACE4_APPEND_DATA 0x00000004
|
||
|
+#define ACE4_ADD_SUBDIRECTORY 0x00000004
|
||
|
+#define ACE4_READ_NAMED_ATTRS 0x00000008
|
||
|
+#define ACE4_WRITE_NAMED_ATTRS 0x00000010
|
||
|
+#define ACE4_EXECUTE 0x00000020
|
||
|
+#define ACE4_DELETE_CHILD 0x00000040
|
||
|
+#define ACE4_READ_ATTRIBUTES 0x00000080
|
||
|
+#define ACE4_WRITE_ATTRIBUTES 0x00000100
|
||
|
+#define ACE4_DELETE 0x00010000
|
||
|
+#define ACE4_READ_ACL 0x00020000
|
||
|
+#define ACE4_WRITE_ACL 0x00040000
|
||
|
+#define ACE4_WRITE_OWNER 0x00080000
|
||
|
+#define ACE4_SYNCHRONIZE 0x00100000
|
||
|
+
|
||
|
+#define ACE4_VALID_MASK ( \
|
||
|
+ ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \
|
||
|
+ ACE4_WRITE_DATA | ACE4_ADD_FILE | \
|
||
|
+ ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \
|
||
|
+ ACE4_READ_NAMED_ATTRS | \
|
||
|
+ ACE4_WRITE_NAMED_ATTRS | \
|
||
|
+ ACE4_EXECUTE | \
|
||
|
+ ACE4_DELETE_CHILD | \
|
||
|
+ ACE4_READ_ATTRIBUTES | \
|
||
|
+ ACE4_WRITE_ATTRIBUTES | \
|
||
|
+ ACE4_DELETE | \
|
||
|
+ ACE4_READ_ACL | \
|
||
|
+ ACE4_WRITE_ACL | \
|
||
|
+ ACE4_WRITE_OWNER | \
|
||
|
+ ACE4_SYNCHRONIZE )
|
||
|
+
|
||
|
+#define ACE4_POSIX_ALWAYS_ALLOWED ( \
|
||
|
+ ACE4_SYNCHRONIZE | \
|
||
|
+ ACE4_READ_ATTRIBUTES | \
|
||
|
+ ACE4_READ_ACL )
|
||
|
+/*
|
||
|
+ * Duplicate an NFS4ACL handle.
|
||
|
+ */
|
||
|
+static inline struct nfs4acl *
|
||
|
+nfs4acl_get(struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ if (acl)
|
||
|
+ atomic_inc(&acl->a_refcount);
|
||
|
+ return acl;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Free an NFS4ACL handle
|
||
|
+ */
|
||
|
+static inline void
|
||
|
+nfs4acl_put(struct nfs4acl *acl)
|
||
|
+{
|
||
|
+ if (acl && atomic_dec_and_test(&acl->a_refcount))
|
||
|
+ kfree(acl);
|
||
|
+}
|
||
|
+
|
||
|
+/* Special e_who identifiers: we use these pointer values in comparisons
|
||
|
+ instead of strcmp for efficiency. */
|
||
|
+
|
||
|
+extern const char nfs4ace_owner_who[];
|
||
|
+extern const char nfs4ace_group_who[];
|
||
|
+extern const char nfs4ace_everyone_who[];
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_owner(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
|
||
|
+ ace->u.e_who == nfs4ace_owner_who;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_group(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
|
||
|
+ ace->u.e_who == nfs4ace_group_who;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_everyone(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return (ace->e_flags & ACE4_SPECIAL_WHO) &&
|
||
|
+ ace->u.e_who == nfs4ace_everyone_who;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_unix_id(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return !(ace->e_flags & ACE4_SPECIAL_WHO);
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_inherit_only(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return ace->e_flags & ACE4_INHERIT_ONLY_ACE;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_inheritable(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return ace->e_flags & (ACE4_FILE_INHERIT_ACE |
|
||
|
+ ACE4_DIRECTORY_INHERIT_ACE);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void
|
||
|
+nfs4ace_clear_inheritance_flags(struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE |
|
||
|
+ ACE4_DIRECTORY_INHERIT_ACE |
|
||
|
+ ACE4_NO_PROPAGATE_INHERIT_ACE |
|
||
|
+ ACE4_INHERIT_ONLY_ACE);
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_allow(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
+nfs4ace_is_deny(const struct nfs4ace *ace)
|
||
|
+{
|
||
|
+ return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE;
|
||
|
+}
|
||
|
+
|
||
|
+extern struct nfs4acl *nfs4acl_alloc(int count);
|
||
|
+extern struct nfs4acl *nfs4acl_clone(const struct nfs4acl *acl);
|
||
|
+
|
||
|
+extern unsigned int nfs4acl_want_to_mask(int want);
|
||
|
+extern int nfs4acl_permission(struct inode *, const struct nfs4acl *, unsigned int);
|
||
|
+extern int nfs4acl_generic_permission(struct inode *, unsigned int);
|
||
|
+extern int nfs4ace_is_same_who(const struct nfs4ace *, const struct nfs4ace *);
|
||
|
+extern int nfs4ace_set_who(struct nfs4ace *ace, const char *who);
|
||
|
+extern struct nfs4acl *nfs4acl_inherit(const struct nfs4acl *, mode_t);
|
||
|
+extern int nfs4acl_masks_to_mode(const struct nfs4acl *);
|
||
|
+extern struct nfs4acl *nfs4acl_chmod(struct nfs4acl *, mode_t);
|
||
|
+extern int nfs4acl_apply_masks(struct nfs4acl **acl);
|
||
|
+extern int nfs4acl_write_through(struct nfs4acl **acl);
|
||
|
+
|
||
|
+#endif /* __NFS4ACL_H */
|
||
|
--- /dev/null
|
||
|
+++ b/include/linux/nfs4acl_xattr.h
|
||
|
@@ -0,0 +1,32 @@
|
||
|
+#ifndef __NFS4ACL_XATTR_H
|
||
|
+#define __NFS4ACL_XATTR_H
|
||
|
+
|
||
|
+#include <linux/nfs4acl.h>
|
||
|
+
|
||
|
+#define NFS4ACL_XATTR "system.nfs4acl"
|
||
|
+
|
||
|
+struct nfs4ace_xattr {
|
||
|
+ __be16 e_type;
|
||
|
+ __be16 e_flags;
|
||
|
+ __be32 e_mask;
|
||
|
+ __be32 e_id;
|
||
|
+ char e_who[0];
|
||
|
+};
|
||
|
+
|
||
|
+struct nfs4acl_xattr {
|
||
|
+ unsigned char a_version;
|
||
|
+ unsigned char a_flags;
|
||
|
+ __be16 a_count;
|
||
|
+ __be32 a_owner_mask;
|
||
|
+ __be32 a_group_mask;
|
||
|
+ __be32 a_other_mask;
|
||
|
+};
|
||
|
+
|
||
|
+#define ACL4_XATTR_VERSION 0
|
||
|
+#define ACL4_XATTR_MAX_COUNT 1024
|
||
|
+
|
||
|
+extern struct nfs4acl *nfs4acl_from_xattr(const void *, size_t);
|
||
|
+extern size_t nfs4acl_xattr_size(const struct nfs4acl *acl);
|
||
|
+extern void nfs4acl_to_xattr(const struct nfs4acl *, void *);
|
||
|
+
|
||
|
+#endif /* __NFS4ACL_XATTR_H */
|