diff mbox

[03/23] vfs: rich ACL in-memory representation and manipulation

Message ID 1265002505-8387-4-git-send-email-aneesh.kumar@linux.vnet.ibm.com
State Not Applicable, archived
Headers show

Commit Message

Aneesh Kumar K.V Feb. 1, 2010, 5:34 a.m. UTC
From: Andreas Gruenbacher <agruen@suse.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.
* Convert to/from xattrs.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.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

Comments

Brad Boyer Feb. 1, 2010, 7:28 a.m. UTC | #1
I have one suggestion about this part of the code.

On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:

<snip>

> +/*
> + * 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);

<snip>

> +/*
> + * 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;
> +}

<snip>

> +struct richace {
> +	unsigned short	e_type;
> +	unsigned short	e_flags;
> +	unsigned int	e_mask;
> +	union {
> +		unsigned int	e_id;
> +		const char	*e_who;
> +	} u;
> +};

<snip>

> +/* 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);
> +}

Wouldn't it make more sense to just store some small numeric value
in ace->u.e_who rather than a pointer? It seems to me that the
savings of storing and comparing an integer type would more than
make up for the slight overhead of needing to lookup a pointer
in the places where the code must handle an external representation.
I know there is really only a difference on 64-bit systems, but my
impression has been that most people are going that way. In particular,
the struct richace would go to 12 bytes with 4-byte alignment from
16 bytes with 8-byte alignment on an architecture with 8-byte pointers.
Plus doing an integer instead of a pointer should eliminate the need
to export the constant pointer values. Even in 32-bit, there are a
few odd architectures (like m68k) that have separate address and
data registers, so using an integer may have some benefits there
due to the fact that the instruction set treats them differently.

I know you mentioned that you didn't originally write this code, but
it seems a logical change to me.

	Brad Boyer
	flar@allandria.com

--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Aneesh Kumar K.V Feb. 1, 2010, 6:02 p.m. UTC | #2
On Sun, 31 Jan 2010 23:28:52 -0800, Brad Boyer <flar@allandria.com> wrote:
> 
> I have one suggestion about this part of the code.
> 
> On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:
> 
> <snip>
> 
> > +/*
> > + * 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);
> 
> <snip>
> 
> > +/*
> > + * 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;
> > +}
> 
> <snip>
> 
> > +struct richace {
> > +	unsigned short	e_type;
> > +	unsigned short	e_flags;
> > +	unsigned int	e_mask;
> > +	union {
> > +		unsigned int	e_id;
> > +		const char	*e_who;
> > +	} u;
> > +};
> 
> <snip>
> 
> > +/* 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);
> > +}
> 
> Wouldn't it make more sense to just store some small numeric value
> in ace->u.e_who rather than a pointer? It seems to me that the
> savings of storing and comparing an integer type would more than
> make up for the slight overhead of needing to lookup a pointer
> in the places where the code must handle an external representation.
> I know there is really only a difference on 64-bit systems, but my
> impression has been that most people are going that way. In particular,
> the struct richace would go to 12 bytes with 4-byte alignment from
> 16 bytes with 8-byte alignment on an architecture with 8-byte pointers.
> Plus doing an integer instead of a pointer should eliminate the need
> to export the constant pointer values. Even in 32-bit, there are a
> few odd architectures (like m68k) that have separate address and
> data registers, so using an integer may have some benefits there
> due to the fact that the instruction set treats them differently.
> 
> I know you mentioned that you didn't originally write this code, but
> it seems a logical change to me.

I guess id mapping needs more work in the patch. I would really like
to hear from both NFS and Samba people in how they would like the
id details to be stored. If we can't map an incoming user@domain
request on nfs, I guess we definitely don't want to store the acl with
'nobody' id

-aneesh
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
J. Bruce Fields Feb. 1, 2010, 11:06 p.m. UTC | #3
On Mon, Feb 01, 2010 at 11:32:59PM +0530, Aneesh Kumar K. V wrote:
> I guess id mapping needs more work in the patch. I would really like
> to hear from both NFS and Samba people in how they would like the
> id details to be stored. If we can't map an incoming user@domain
> request on nfs, I guess we definitely don't want to store the acl with
> 'nobody' id

I don't see the point in allowing the acl's to refer to arbitrary
user@domain strings unless we're also going to allow those strings as
file owners, allow processes to run *as* one of those strings, etc.

If we're really going to try to teach the core kernel to handle foreign
NFS or Samba identities, that's a separate project.

As long as the kernel's working with ordinary uid's and gid's, the acl's
should do the same, and NFS and Samba can take care of the conversion as
needed.

So I agree that we should be able to use a more compact representation
here.

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
J. Bruce Fields Feb. 1, 2010, 11:21 p.m. UTC | #4
On Mon, Feb 01, 2010 at 11:04:45AM +0530, Aneesh Kumar K.V wrote:
> From: Andreas Gruenbacher <agruen@suse.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@suse.de>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.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@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/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@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/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@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/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@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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@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/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@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/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@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/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 */