[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20100201232141.GS19418@fieldses.org>
Date: Mon, 1 Feb 2010 18:21:41 -0500
From: "J. Bruce Fields" <bfields@...i.umich.edu>
To: "Aneesh Kumar K.V" <aneesh.kumar@...ux.vnet.ibm.com>
Cc: sfrench@...ibm.com, ffilz@...ibm.com, agruen@...e.de,
adilger@....com, sandeen@...hat.com, tytso@....edu,
staubach@...hat.com, jlayton@...hat.com,
linux-fsdevel@...r.kernel.org, nfsv4@...ux-nfs.org,
linux-ext4@...r.kernel.org
Subject: Re: [PATCH 03/23] vfs: rich ACL in-memory representation and
manipulation
On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:
> From: Andreas Gruenbacher <agruen@...e.de>
>
> * In-memory representation (struct richacl).
> * 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 richacl that grants the same
> permissions. Protocols which don't understand the masks need
> this.
The mask calculations are a little complicated. I'd like to review
them, but I think I'd need to sit down and think hard about it for a day
or so, and I'm not sure when.
--b.
> * Convert to/from xattrs.
>
> Signed-off-by: Andreas Gruenbacher <agruen@...e.de>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@...ux.vnet.ibm.com>
> ---
> fs/Kconfig | 4 +
> fs/Makefile | 4 +
> fs/richacl_base.c | 568 +++++++++++++++++++++++++++++++
> fs/richacl_compat.c | 757 +++++++++++++++++++++++++++++++++++++++++
> fs/richacl_xattr.c | 146 ++++++++
> include/linux/richacl.h | 208 +++++++++++
> include/linux/richacl_xattr.h | 32 ++
> 7 files changed, 1719 insertions(+), 0 deletions(-)
> create mode 100644 fs/richacl_base.c
> create mode 100644 fs/richacl_compat.c
> create mode 100644 fs/richacl_xattr.c
> create mode 100644 include/linux/richacl.h
> create mode 100644 include/linux/richacl_xattr.h
>
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 64d44ef..adbda7c 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -39,6 +39,10 @@ config FS_POSIX_ACL
> bool
> default n
>
> +config FS_RICHACL
> + bool
> + default n
> +
> source "fs/xfs/Kconfig"
> source "fs/gfs2/Kconfig"
> source "fs/ocfs2/Kconfig"
> diff --git a/fs/Makefile b/fs/Makefile
> index af6d047..0a046a1 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -51,6 +51,10 @@ obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
> obj-$(CONFIG_NFS_COMMON) += nfs_common/
> obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
>
> +obj-$(CONFIG_FS_RICHACL) += richacl.o
> +richacl-y := richacl_base.o richacl_xattr.o \
> + richacl_compat.o
> +
> obj-y += quota/
>
> obj-$(CONFIG_PROC_FS) += proc/
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> new file mode 100644
> index 0000000..de99340
> --- /dev/null
> +++ b/fs/richacl_base.c
> @@ -0,0 +1,568 @@
> +/*
> + * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@...puter.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/sched.h>
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/richacl.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 richace_owner_who[] = "OWNER@";
> +EXPORT_SYMBOL_GPL(richace_owner_who);
> +const char richace_group_who[] = "GROUP@";
> +EXPORT_SYMBOL_GPL(richace_group_who);
> +const char richace_everyone_who[] = "EVERYONE@";
> +EXPORT_SYMBOL_GPL(richace_everyone_who);
> +
> +/**
> + * richacl_alloc - allocate an acl
> + * @count: number of entries
> + */
> +struct richacl *
> +richacl_alloc(int count)
> +{
> + size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
> + struct richacl *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_GPL(richacl_alloc);
> +
> +/**
> + * richacl_clone - create a copy of an acl
> + */
> +struct richacl *
> +richacl_clone(const struct richacl *acl)
> +{
> + int count = acl->a_count;
> + size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
> + struct richacl *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
> +richacl_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;
> +}
> +
> +/**
> + * richacl_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
> +richacl_masks_to_mode(const struct richacl *acl)
> +{
> + return richacl_mask_to_mode(acl->a_owner_mask) << 6 |
> + richacl_mask_to_mode(acl->a_group_mask) << 3 |
> + richacl_mask_to_mode(acl->a_other_mask);
> +}
> +EXPORT_SYMBOL_GPL(richacl_masks_to_mode);
> +
> +static unsigned int
> +richacl_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;
> +}
> +
> +/**
> + * richacl_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 richacl *
> +richacl_chmod(struct richacl *acl, mode_t mode)
> +{
> + unsigned int owner_mask, group_mask, other_mask;
> + struct richacl *clone;
> +
> + owner_mask = richacl_mode_to_mask(mode >> 6);
> + group_mask = richacl_mode_to_mask(mode >> 3);
> + other_mask = richacl_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 = richacl_clone(acl);
> + richacl_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 (richacl_write_through(&clone)) {
> + richacl_put(clone);
> + clone = ERR_PTR(-ENOMEM);
> + }
> + return clone;
> +}
> +EXPORT_SYMBOL_GPL(richacl_chmod);
> +
> +/**
> + * richacl_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
> +richacl_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_GPL(richacl_want_to_mask);
> +
> +/**
> + * richacl_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 richacl_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;
> +}
> +
> +/**
> + * richacl_permission - permission check algorithm with masking
> + * @inode: inode to check
> + * @acl: rich 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 richacl_permission(struct inode *inode, const struct richacl *acl,
> + unsigned int mask)
> +{
> + const struct richace *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.
> + */
> +
> + richacl_for_each_entry(ace, acl) {
> + unsigned int ace_mask = ace->e_mask;
> +
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_owner(ace)) {
> + if (current_fsuid() != inode->i_uid)
> + continue;
> + goto is_owner;
> + } else if (richace_is_group(ace)) {
> + if (!in_owning_group)
> + continue;
> + } else if (richace_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 richacl_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 (richace_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 (richace_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 richacl_capability_check(inode, requested);
> +}
> +EXPORT_SYMBOL_GPL(richacl_permission);
> +
> +/**
> + * richacl_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
> + * richacl_permission() to it, compute the identical result directly from
> + * the file mode.
> + */
> +int richacl_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 & ~richacl_mode_to_mask(mode)))
> + return 0;
> + return richacl_capability_check(inode, mask);
> +}
> +EXPORT_SYMBOL_GPL(richacl_generic_permission);
> +
> +/*
> + * richace_is_same_who - do both acl entries refer to the same identifier?
> + */
> +int
> +richace_is_same_who(const struct richace *a, const struct richace *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
> +}
> +
> +/**
> + * richacl_set_who - set a special who value
> + * @ace: acl entry
> + * @who: who value to use
> + */
> +int
> +richace_set_who(struct richace *ace, const char *who)
> +{
> + if (!strcmp(who, richace_owner_who))
> + who = richace_owner_who;
> + else if (!strcmp(who, richace_group_who))
> + who = richace_group_who;
> + else if (!strcmp(who, richace_everyone_who))
> + who = richace_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_GPL(richace_set_who);
> +
> +/**
> + * richacl_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
> +richacl_allowed_to_who(struct richacl *acl, struct richace *who)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_same_who(ace, who) ||
> + richace_is_everyone(ace)) {
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + allowed &= ~ace->e_mask;
> + }
> + }
> + return allowed;
> +}
> +
> +/**
> + * richacl_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
> +richacl_compute_max_masks(struct richacl *acl)
> +{
> + struct richace *ace;
> +
> + acl->a_owner_mask = 0;
> + acl->a_group_mask = 0;
> + acl->a_other_mask = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> +
> + if (richace_is_owner(ace)) {
> + if (richace_is_allow(ace))
> + acl->a_owner_mask |= ace->e_mask;
> + else if (richace_is_deny(ace))
> + acl->a_owner_mask &= ~ace->e_mask;
> + } else if (richace_is_everyone(ace)) {
> + if (richace_is_allow(ace)) {
> + struct richace who = {
> + .e_flags = ACE4_SPECIAL_WHO,
> + .u.e_who = richace_group_who,
> + };
> +
> + acl->a_other_mask |= ace->e_mask;
> + acl->a_group_mask |=
> + richacl_allowed_to_who(acl, &who);
> + acl->a_owner_mask |= ace->e_mask;
> + } else if (richace_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 (richace_is_allow(ace)) {
> + unsigned int mask =
> + richacl_allowed_to_who(acl, ace);
> +
> + acl->a_group_mask |= mask;
> + acl->a_owner_mask |= mask;
> + }
> + }
> + }
> +}
> +
> +/**
> + * richacl_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 richacl *
> +richacl_inherit(const struct richacl *dir_acl, mode_t mode)
> +{
> + const struct richace *dir_ace;
> + struct richacl *acl;
> + struct richace *ace;
> + int count = 0;
> +
> + if (S_ISDIR(mode)) {
> + richacl_for_each_entry(dir_ace, dir_acl) {
> + if (!richace_is_inheritable(dir_ace))
> + continue;
> + count++;
> + }
> + if (!count)
> + return NULL;
> + acl = richacl_alloc(count);
> + if (!acl)
> + return ERR_PTR(-ENOMEM);
> + ace = acl->a_entries;
> + richacl_for_each_entry(dir_ace, dir_acl) {
> + if (!richace_is_inheritable(dir_ace))
> + continue;
> + memcpy(ace, dir_ace, sizeof(struct richace));
> + if (dir_ace->e_flags & ACE4_NO_PROPAGATE_INHERIT_ACE)
> + richace_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 {
> + richacl_for_each_entry(dir_ace, dir_acl) {
> + if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
> + continue;
> + count++;
> + }
> + if (!count)
> + return NULL;
> + acl = richacl_alloc(count);
> + if (!acl)
> + return ERR_PTR(-ENOMEM);
> + ace = acl->a_entries;
> + richacl_for_each_entry(dir_ace, dir_acl) {
> + if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE))
> + continue;
> + memcpy(ace, dir_ace, sizeof(struct richace));
> + richace_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_umask();
> + } else
> + richacl_compute_max_masks(acl);
> +
> + /* Apply the create mode. */
> + acl->a_owner_mask &= richacl_mode_to_mask(mode >> 6);
> + acl->a_group_mask &= richacl_mode_to_mask(mode >> 3);
> + acl->a_other_mask &= richacl_mode_to_mask(mode);
> +
> + if (richacl_write_through(&acl)) {
> + richacl_put(acl);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + acl->a_flags = (dir_acl->a_flags & ACL4_WRITE_THROUGH);
> +
> + return acl;
> +}
> +EXPORT_SYMBOL_GPL(richacl_inherit);
> diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> new file mode 100644
> index 0000000..a985bbf
> --- /dev/null
> +++ b/fs/richacl_compat.c
> @@ -0,0 +1,757 @@
> +/*
> + * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@...puter.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/module.h>
> +#include <linux/fs.h>
> +#include <linux/richacl.h>
> +
> +/**
> + * struct richacl_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 richacl_alloc {
> + struct richacl *acl;
> + unsigned int count;
> +};
> +
> +/**
> + * richacl_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
> +richacl_delete_entry(struct richacl_alloc *x, struct richace **ace)
> +{
> + void *end = x->acl->a_entries + x->acl->a_count;
> +
> + memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
> + (*ace)--;
> + x->acl->a_count--;
> +}
> +
> +/**
> + * richacl_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
> +richacl_insert_entry(struct richacl_alloc *x, struct richace **ace)
> +{
> + if (x->count == x->acl->a_count) {
> + int n = *ace - x->acl->a_entries;
> + struct richacl *acl2;
> +
> + acl2 = richacl_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 richace));
> + memcpy(acl2->a_entries + n + 1, *ace,
> + (x->acl->a_count - n) * sizeof(struct richace));
> + 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 richace));
> + return 0;
> +}
> +
> +/**
> + * richace_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
> +richace_change_mask(struct richacl_alloc *x, struct richace **ace,
> + unsigned int mask)
> +{
> + if (mask && (*ace)->e_mask == mask)
> + return 0;
> + if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) {
> + if (richace_is_inheritable(*ace)) {
> + if (richacl_insert_entry(x, ace))
> + return -1;
> + memcpy(*ace, *ace + 1, sizeof(struct richace));
> + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
> + (*ace)++;
> + richace_clear_inheritance_flags(*ace);
> + }
> + (*ace)->e_mask = mask;
> + } else {
> + if (richace_is_inheritable(*ace))
> + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE;
> + else
> + richacl_delete_entry(x, ace);
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_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
> +richacl_move_everyone_aces_down(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0, denied = 0;
> +
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_everyone(ace)) {
> + if (richace_is_allow(ace))
> + allowed |= (ace->e_mask & ~denied);
> + else if (richace_is_deny(ace))
> + denied |= (ace->e_mask & ~allowed);
> + else
> + continue;
> + if (richace_change_mask(x, &ace, 0))
> + return -1;
> + } else {
> + if (richace_is_allow(ace)) {
> + if (richace_change_mask(x, &ace, allowed |
> + (ace->e_mask & ~denied)))
> + return -1;
> + } else if (richace_is_deny(ace)) {
> + if (richace_change_mask(x, &ace, denied |
> + (ace->e_mask & ~allowed)))
> + return -1;
> + }
> + }
> + }
> + if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) {
> + struct richace *last_ace = ace - 1;
> +
> + if (richace_is_everyone(last_ace) &&
> + richace_is_allow(last_ace) &&
> + richace_is_inherit_only(last_ace) &&
> + last_ace->e_mask == allowed)
> + last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE;
> + else {
> + if (richacl_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 = richace_everyone_who;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_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
> +__richacl_propagate_everyone(struct richacl_alloc *x, struct richace *who,
> + unsigned int allow)
> +{
> + struct richace *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. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + if (richace_is_same_who(ace, who)) {
> + allow &= ~ace->e_mask;
> + allow_last = ace;
> + }
> + } else if (richace_is_deny(ace)) {
> + if (richace_is_same_who(ace, who))
> + allow &= ~ace->e_mask;
> + if (allow & ace->e_mask)
> + allow_last = NULL;
> + }
> + }
> +
> + if (allow) {
> + if (allow_last)
> + return richace_change_mask(x, &allow_last,
> + allow_last->e_mask | allow);
> + else {
> + struct richace who_copy;
> +
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + memcpy(&who_copy, who, sizeof(struct richace));
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + memcpy(ace, &who_copy, sizeof(struct richace));
> + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
> + richace_clear_inheritance_flags(ace);
> + ace->e_mask = allow;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_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
> +richacl_propagate_everyone(struct richacl_alloc *x)
> +{
> + int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH);
> + struct richace who = { .e_flags = ACE4_SPECIAL_WHO };
> + struct richace *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 (richace_is_inherit_only(ace) || !richace_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 = richace_owner_who;
> + retval = __richacl_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 = richace_group_who;
> + retval = __richacl_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 (richace_is_inherit_only(ace) ||
> + richace_is_owner(ace) ||
> + richace_is_group(ace))
> + continue;
> + if (richace_is_allow(ace) || richace_is_deny(ace)) {
> + /* Any inserted entry will end up below the
> + current entry. */
> + retval = __richacl_propagate_everyone(x, ace,
> + group_allow);
> + if (retval)
> + return -1;
> + }
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_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
> +__richacl_apply_masks(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> +
> + richacl_for_each_entry(ace, x->acl) {
> + unsigned int mask;
> +
> + if (richace_is_inherit_only(ace) || !richace_is_allow(ace))
> + continue;
> + if (richace_is_owner(ace))
> + mask = x->acl->a_owner_mask;
> + else if (richace_is_everyone(ace))
> + mask = x->acl->a_other_mask;
> + else
> + mask = x->acl->a_group_mask;
> + if (richace_change_mask(x, &ace, ace->e_mask & mask))
> + return -1;
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_max_allowed - maximum mask flags that anybody is allowed
> + */
> +static unsigned int
> +richacl_max_allowed(struct richacl *acl)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + richacl_for_each_entry_reverse(ace, acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_is_deny(ace)) {
> + if (richace_is_everyone(ace))
> + allowed &= ~ace->e_mask;
> + }
> + }
> + return allowed;
> +}
> +
> +/**
> + * richacl_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
> +richacl_isolate_owner_class(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0;
> +
> + allowed = richacl_max_allowed(x->acl);
> + if (allowed & ~x->acl->a_owner_mask) {
> + /* Figure out if we can update an existig OWNER@ DENY entry. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_deny(ace)) {
> + if (richace_is_owner(ace))
> + break;
> + } else if (richace_is_allow(ace)) {
> + ace = x->acl->a_entries + x->acl->a_count;
> + break;
> + }
> + }
> + if (ace != x->acl->a_entries + x->acl->a_count) {
> + if (richace_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 (richacl_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 = richace_owner_who;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_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
> +__richacl_isolate_who(struct richacl_alloc *x, struct richace *who,
> + unsigned int deny)
> +{
> + struct richace *ace;
> + unsigned int allowed = 0, n;
> +
> + /* Compute the mask flags granted to this who value. */
> + richacl_for_each_entry_reverse(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_same_who(ace, who)) {
> + if (richace_is_allow(ace))
> + allowed |= ace->e_mask;
> + else if (richace_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 (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_deny(ace)) {
> + if (richace_is_same_who(ace, who))
> + break;
> + } else if (richace_is_allow(ace) &&
> + (ace->e_mask & deny)) {
> + n = -1;
> + break;
> + }
> + }
> + if (n != -1) {
> + if (richace_change_mask(x, &ace, ace->e_mask | deny))
> + return -1;
> + } else {
> + /* Insert a eny entry before the trailing EVERYONE@ DENY
> + entry. */
> + struct richace who_copy;
> +
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + memcpy(&who_copy, who, sizeof(struct richace));
> + if (richacl_insert_entry(x, &ace))
> + return -1;
> + memcpy(ace, &who_copy, sizeof(struct richace));
> + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE;
> + richace_clear_inheritance_flags(ace);
> + ace->e_mask = deny;
> + }
> + return 0;
> +}
> +
> +/**
> + * richacl_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
> +richacl_isolate_group_class(struct richacl_alloc *x)
> +{
> + struct richace who = {
> + .e_flags = ACE4_SPECIAL_WHO,
> + .u.e_who = richace_group_who,
> + };
> + struct richace *ace;
> + unsigned int deny;
> +
> + if (!x->acl->a_count)
> + return 0;
> + ace = x->acl->a_entries + x->acl->a_count - 1;
> + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
> + return 0;
> + deny = ace->e_mask & ~x->acl->a_group_mask;
> +
> + if (deny) {
> + unsigned int n;
> +
> + if (__richacl_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 (richace_is_inherit_only(ace) ||
> + richace_is_owner(ace) ||
> + richace_is_group(ace))
> + continue;
> + if (__richacl_isolate_who(x, ace, deny))
> + return -1;
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * __richacl_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
> +__richacl_write_through(struct richacl_alloc *x)
> +{
> + struct richace *ace;
> + unsigned int allowed;
> +
> + /* Remove all owner@ and group@ ACEs: we re-insert them at the
> + top. */
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if ((richace_is_owner(ace) || richace_is_group(ace)) &&
> + richace_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 && richace_is_everyone(ace) &&
> + !richace_is_inherit_only(ace)) {
> + if (richace_change_mask(x, &ace, allowed))
> + return -1;
> + } else {
> + ace = x->acl->a_entries + x->acl->a_count;
> + if (richacl_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 = richace_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;
> + richacl_for_each_entry_reverse(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + if (richace_is_everyone(ace))
> + allowed |= ace->e_mask;
> + } else if (richace_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 (richacl_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 = richace_group_who;
> + }
> +
> + /* Insert the appropriate owner@ allow entry at the front. */
> + if (x->acl->a_owner_mask & ~allowed) {
> + ace = x->acl->a_entries;
> + if (richacl_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 = richace_owner_who;
> + }
> +
> + /* Insert the appropriate owner@ deny entry at the front. */
> + allowed = richacl_max_allowed(x->acl);
> + if (allowed & ~x->acl->a_owner_mask) {
> + richacl_for_each_entry(ace, x->acl) {
> + if (richace_is_inherit_only(ace))
> + continue;
> + if (richace_is_allow(ace)) {
> + ace = x->acl->a_entries + x->acl->a_count;
> + break;
> + }
> + if (richace_is_deny(ace) && richace_is_owner(ace))
> + break;
> + }
> + if (ace != x->acl->a_entries + x->acl->a_count) {
> + if (richace_change_mask(x, &ace, ace->e_mask |
> + (allowed & ~x->acl->a_owner_mask)))
> + return -1;
> + } else {
> + ace = x->acl->a_entries;
> + if (richacl_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 = richace_owner_who;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * richacl_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
> +richacl_apply_masks(struct richacl **acl)
> +{
> + struct richacl_alloc x = {
> + .acl = *acl,
> + .count = (*acl)->a_count,
> + };
> + int retval = 0;
> +
> + if (richacl_move_everyone_aces_down(&x) ||
> + richacl_propagate_everyone(&x) ||
> + __richacl_apply_masks(&x) ||
> + richacl_isolate_owner_class(&x) ||
> + richacl_isolate_group_class(&x))
> + retval = -ENOMEM;
> +
> + *acl = x.acl;
> + return retval;
> +}
> +EXPORT_SYMBOL_GPL(richacl_apply_masks);
> +
> +int richacl_write_through(struct richacl **acl)
> +{
> + struct richacl_alloc x = {
> + .acl = *acl,
> + .count = (*acl)->a_count,
> + };
> + int retval = 0;
> +
> + if (!((*acl)->a_flags & ACL4_WRITE_THROUGH))
> + goto out;
> +
> + if (richacl_move_everyone_aces_down(&x) ||
> + richacl_propagate_everyone(&x) ||
> + __richacl_write_through(&x))
> + retval = -ENOMEM;
> +
> + *acl = x.acl;
> +out:
> + return retval;
> +}
> diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
> new file mode 100644
> index 0000000..4c04417
> --- /dev/null
> +++ b/fs/richacl_xattr.c
> @@ -0,0 +1,146 @@
> +/*
> + * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@...puter.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/richacl_xattr.h>
> +
> +MODULE_LICENSE("GPL");
> +
> +struct richacl *
> +richacl_from_xattr(const void *value, size_t size)
> +{
> + const struct richacl_xattr *xattr_acl = value;
> + const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
> + struct richacl *acl;
> + struct richace *ace;
> + int count;
> +
> + if (size < sizeof(struct richacl_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 = richacl_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;
> +
> + richacl_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 richace));
> + 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 (richace_set_who(ace, who))
> + goto fail_einval;
> +
> + xattr_ace = (void *)who + ALIGN(end - who + 1, 4);
> + }
> +
> + return acl;
> +
> +fail_einval:
> + richacl_put(acl);
> + return ERR_PTR(-EINVAL);
> +}
> +EXPORT_SYMBOL_GPL(richacl_from_xattr);
> +
> +size_t
> +richacl_xattr_size(const struct richacl *acl)
> +{
> + size_t size = sizeof(struct richacl_xattr);
> + const struct richace *ace;
> +
> + richacl_for_each_entry(ace, acl) {
> + size += sizeof(struct richace_xattr) +
> + (richace_is_unix_id(ace) ? 4 :
> + ALIGN(strlen(ace->u.e_who) + 1, 4));
> + }
> + return size;
> +}
> +EXPORT_SYMBOL_GPL(richacl_xattr_size);
> +
> +void
> +richacl_to_xattr(const struct richacl *acl, void *buffer)
> +{
> + struct richacl_xattr *xattr_acl = buffer;
> + struct richace_xattr *xattr_ace;
> + const struct richace *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);
> + richacl_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 (richace_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_GPL(richacl_to_xattr);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> new file mode 100644
> index 0000000..a2b4bd0
> --- /dev/null
> +++ b/include/linux/richacl.h
> @@ -0,0 +1,208 @@
> +#ifndef __RICHACL_H
> +#define __RICHACL_H
> +#include <linux/slab.h>
> +
> +struct richace {
> + unsigned short e_type;
> + unsigned short e_flags;
> + unsigned int e_mask;
> + union {
> + unsigned int e_id;
> + const char *e_who;
> + } u;
> +};
> +
> +struct richacl {
> + 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 richace a_entries[0];
> +};
> +
> +#define richacl_for_each_entry(_ace, _acl) \
> + for (_ace = _acl->a_entries; \
> + _ace != _acl->a_entries + _acl->a_count; \
> + _ace++)
> +
> +#define richacl_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
> +/* in-memory representation only */
> +#define ACE4_SPECIAL_WHO 0x4000
> +
> +#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 RICHACL handle.
> + */
> +static inline struct richacl *
> +richacl_get(struct richacl *acl)
> +{
> + if (acl)
> + atomic_inc(&acl->a_refcount);
> + return acl;
> +}
> +
> +/*
> + * Free an RICHACL handle
> + */
> +static inline void
> +richacl_put(struct richacl *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 richace_owner_who[];
> +extern const char richace_group_who[];
> +extern const char richace_everyone_who[];
> +
> +static inline int
> +richace_is_owner(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_owner_who;
> +}
> +
> +static inline int
> +richace_is_group(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_group_who;
> +}
> +
> +static inline int
> +richace_is_everyone(const struct richace *ace)
> +{
> + return (ace->e_flags & ACE4_SPECIAL_WHO) &&
> + ace->u.e_who == richace_everyone_who;
> +}
> +
> +static inline int
> +richace_is_unix_id(const struct richace *ace)
> +{
> + return !(ace->e_flags & ACE4_SPECIAL_WHO);
> +}
> +
> +static inline int
> +richace_is_inherit_only(const struct richace *ace)
> +{
> + return ace->e_flags & ACE4_INHERIT_ONLY_ACE;
> +}
> +
> +static inline int
> +richace_is_inheritable(const struct richace *ace)
> +{
> + return ace->e_flags & (ACE4_FILE_INHERIT_ACE |
> + ACE4_DIRECTORY_INHERIT_ACE);
> +}
> +
> +static inline void
> +richace_clear_inheritance_flags(struct richace *ace)
> +{
> + ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE |
> + ACE4_DIRECTORY_INHERIT_ACE |
> + ACE4_NO_PROPAGATE_INHERIT_ACE |
> + ACE4_INHERIT_ONLY_ACE);
> +}
> +
> +static inline int
> +richace_is_allow(const struct richace *ace)
> +{
> + return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE;
> +}
> +
> +static inline int
> +richace_is_deny(const struct richace *ace)
> +{
> + return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE;
> +}
> +
> +extern struct richacl *richacl_alloc(int count);
> +extern struct richacl *richacl_clone(const struct richacl *acl);
> +
> +extern unsigned int richacl_want_to_mask(int want);
> +extern int richacl_permission(struct inode *,
> + const struct richacl *, unsigned int);
> +extern int richacl_generic_permission(struct inode *, unsigned int);
> +extern int richace_is_same_who(const struct richace *, const struct richace *);
> +extern int richace_set_who(struct richace *ace, const char *who);
> +extern struct richacl *richacl_inherit(const struct richacl *, mode_t);
> +extern int richacl_masks_to_mode(const struct richacl *);
> +extern struct richacl *richacl_chmod(struct richacl *, mode_t);
> +extern int richacl_apply_masks(struct richacl **acl);
> +extern int richacl_write_through(struct richacl **acl);
> +
> +#endif /* __RICHACL_H */
> diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
> new file mode 100644
> index 0000000..5a75284
> --- /dev/null
> +++ b/include/linux/richacl_xattr.h
> @@ -0,0 +1,32 @@
> +#ifndef __RICHACL_XATTR_H
> +#define __RICHACL_XATTR_H
> +
> +#include <linux/richacl.h>
> +
> +#define RICHACL_XATTR "system.richacl"
> +
> +struct richace_xattr {
> + __be16 e_type;
> + __be16 e_flags;
> + __be32 e_mask;
> + __be32 e_id;
> + char e_who[0];
> +};
> +
> +struct richacl_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 richacl *richacl_from_xattr(const void *, size_t);
> +extern size_t richacl_xattr_size(const struct richacl *acl);
> +extern void richacl_to_xattr(const struct richacl *, void *);
> +
> +#endif /* __RICHACL_XATTR_H */
> --
> 1.7.0.rc0.48.gdace5
>
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists