diff mbox

[RFC,v7,29/41] nfsd: Use richacls as internal acl representation

Message ID 1441448856-13478-30-git-send-email-agruenba@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andreas Gruenbacher Sept. 5, 2015, 10:27 a.m. UTC
When converting from NFSv4 ACLs to POSIX ACLs, nfsd so far was using
struct nfs4_acl as its internal representation. This representation is a
subset of richacls, so get rid of struct nfs4_acl. Richacls even have a
more compact in-memory representation, so a few more ACL entries can
easily be supported.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/Kconfig              |   6 +
 fs/nfs_common/Makefile  |   1 +
 fs/nfs_common/nfs4acl.c |  44 ++++++
 fs/nfsd/Kconfig         |   1 +
 fs/nfsd/acl.h           |  24 ++--
 fs/nfsd/nfs4acl.c       | 367 ++++++++++++++++++++++--------------------------
 fs/nfsd/nfs4proc.c      |  15 +-
 fs/nfsd/nfs4xdr.c       |  64 +++------
 fs/nfsd/xdr4.h          |   6 +-
 include/linux/nfs4.h    |  23 ---
 include/linux/nfs4acl.h |   7 +
 11 files changed, 273 insertions(+), 285 deletions(-)
 create mode 100644 fs/nfs_common/nfs4acl.c
 create mode 100644 include/linux/nfs4acl.h

Comments

J. Bruce Fields Sept. 24, 2015, 7:29 p.m. UTC | #1
On Sat, Sep 05, 2015 at 12:27:24PM +0200, Andreas Gruenbacher wrote:
> When converting from NFSv4 ACLs to POSIX ACLs, nfsd so far was using
> struct nfs4_acl as its internal representation. This representation is a
> subset of richacls, so get rid of struct nfs4_acl. Richacls even have a
> more compact in-memory representation, so a few more ACL entries can
> easily be supported.

Looks good to me, ACK.

--b.

> 
> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
>  fs/Kconfig              |   6 +
>  fs/nfs_common/Makefile  |   1 +
>  fs/nfs_common/nfs4acl.c |  44 ++++++
>  fs/nfsd/Kconfig         |   1 +
>  fs/nfsd/acl.h           |  24 ++--
>  fs/nfsd/nfs4acl.c       | 367 ++++++++++++++++++++++--------------------------
>  fs/nfsd/nfs4proc.c      |  15 +-
>  fs/nfsd/nfs4xdr.c       |  64 +++------
>  fs/nfsd/xdr4.h          |   6 +-
>  include/linux/nfs4.h    |  23 ---
>  include/linux/nfs4acl.h |   7 +
>  11 files changed, 273 insertions(+), 285 deletions(-)
>  create mode 100644 fs/nfs_common/nfs4acl.c
>  create mode 100644 include/linux/nfs4acl.h
> 
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 3e09c06..dd3f2d6 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -268,6 +268,12 @@ config NFS_COMMON
>  	depends on NFSD || NFS_FS || LOCKD
>  	default y
>  
> +config NFS_RICHACL
> +	bool
> +	depends on NFSD_V4 || NFS_V4
> +	select FS_RICHACL
> +	default y
> +
>  source "net/sunrpc/Kconfig"
>  source "fs/ceph/Kconfig"
>  source "fs/cifs/Kconfig"
> diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
> index d153ca3..e055139 100644
> --- a/fs/nfs_common/Makefile
> +++ b/fs/nfs_common/Makefile
> @@ -4,5 +4,6 @@
>  
>  obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
>  nfs_acl-objs := nfsacl.o
> +obj-$(CONFIG_NFS_RICHACL) += nfs4acl.o
>  
>  obj-$(CONFIG_GRACE_PERIOD) += grace.o
> diff --git a/fs/nfs_common/nfs4acl.c b/fs/nfs_common/nfs4acl.c
> new file mode 100644
> index 0000000..02df064
> --- /dev/null
> +++ b/fs/nfs_common/nfs4acl.c
> @@ -0,0 +1,44 @@
> +#include <linux/fs.h>
> +#include <linux/richacl.h>
> +#include <linux/nfs4acl.h>
> +
> +static struct special_id {
> +	char *who;
> +	int   len;
> +} special_who_map[] = {
> +	[RICHACE_OWNER_SPECIAL_ID] = {
> +		.who = "OWNER@",
> +		.len = sizeof("OWNER@") - 1 },
> +	[RICHACE_GROUP_SPECIAL_ID] = {
> +		.who = "GROUP@",
> +		.len = sizeof("GROUP@") - 1 },
> +	[RICHACE_EVERYONE_SPECIAL_ID] = {
> +		.who = "EVERYONE@",
> +		.len = sizeof("EVERYONE@") - 1 }
> +};
> +
> +int nfs4acl_who_to_special_id(const char *who, u32 len)
> +{
> +	int n;
> +
> +	for (n = 0; n < ARRAY_SIZE(special_who_map); n++) {
> +		if (len == special_who_map[n].len &&
> +		    !memcmp(who, special_who_map[n].who, len))
> +			return n;
> +	}
> +	return -1;
> +}
> +EXPORT_SYMBOL(nfs4acl_who_to_special_id);
> +
> +bool nfs4acl_special_id_to_who(unsigned int special_who,
> +			       const char **who, unsigned int *len)
> +{
> +	struct special_id *special = &special_who_map[special_who];
> +
> +	if (special_who > ARRAY_SIZE(special_who_map) || !special->len)
> +		return false;
> +	*who = special->who;
> +	*len = special->len;
> +	return true;
> +}
> +EXPORT_SYMBOL(nfs4acl_special_id_to_who);
> diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
> index a0b77fc..811379a 100644
> --- a/fs/nfsd/Kconfig
> +++ b/fs/nfsd/Kconfig
> @@ -70,6 +70,7 @@ config NFSD_V4
>  	depends on NFSD && PROC_FS
>  	select NFSD_V3
>  	select FS_POSIX_ACL
> +	select FS_RICHACL
>  	select SUNRPC_GSS
>  	select CRYPTO
>  	select GRACE_PERIOD
> diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
> index 4cd7c69..1c5deb5 100644
> --- a/fs/nfsd/acl.h
> +++ b/fs/nfsd/acl.h
> @@ -35,25 +35,27 @@
>  #ifndef LINUX_NFS4_ACL_H
>  #define LINUX_NFS4_ACL_H
>  
> -struct nfs4_acl;
> +struct richacl;
> +struct richace;
>  struct svc_fh;
>  struct svc_rqst;
>  
>  /*
>   * Maximum ACL we'll accept from a client; chosen (somewhat
>   * arbitrarily) so that kmalloc'ing the ACL shouldn't require a
> - * high-order allocation.  This allows 204 ACEs on x86_64:
> + * high-order allocation.  This allows 339 ACEs on x86_64:
>   */
> -#define NFS4_ACL_MAX ((PAGE_SIZE - sizeof(struct nfs4_acl)) \
> -			/ sizeof(struct nfs4_ace))
> +#define NFSD4_ACL_MAX ((PAGE_SIZE - sizeof(struct richacl)) \
> +			/ sizeof(struct richace))
>  
> -int nfs4_acl_bytes(int entries);
> -int nfs4_acl_get_whotype(char *, u32);
> -__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
> +__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
> +			    char *who, u32 len);
> +__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> +			    struct richace *ace);
>  
> -int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> -		struct nfs4_acl **acl);
> -__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> -		struct nfs4_acl *acl);
> +int nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> +		  struct richacl **acl);
> +__be32 nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> +		     struct richacl *acl);
>  
>  #endif /* LINUX_NFS4_ACL_H */
> diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
> index eb5accf..582f772 100644
> --- a/fs/nfsd/nfs4acl.c
> +++ b/fs/nfsd/nfs4acl.c
> @@ -36,45 +36,48 @@
>  
>  #include <linux/slab.h>
>  #include <linux/nfs_fs.h>
> +#include <linux/richacl_compat.h>
> +#include <linux/nfs4acl.h>
>  #include "nfsfh.h"
>  #include "nfsd.h"
> +#include "idmap.h"
>  #include "acl.h"
>  #include "vfs.h"
>  
> -#define NFS4_ACL_TYPE_DEFAULT	0x01
> -#define NFS4_ACL_DIR		0x02
> -#define NFS4_ACL_OWNER		0x04
> +#define FLAG_DEFAULT_ACL	0x01
> +#define FLAG_DIRECTORY		0x02
> +#define FLAG_OWNER		0x04
>  
>  /* mode bit translations: */
> -#define NFS4_READ_MODE (NFS4_ACE_READ_DATA)
> -#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA)
> -#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
> -#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
> -#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
> +#define RICHACE_READ_MODE (RICHACE_READ_DATA)
> +#define RICHACE_WRITE_MODE (RICHACE_WRITE_DATA | RICHACE_APPEND_DATA)
> +#define RICHACE_EXECUTE_MODE RICHACE_EXECUTE
> +#define RICHACE_ANYONE_MODE (RICHACE_READ_ATTRIBUTES | RICHACE_READ_ACL | RICHACE_SYNCHRONIZE)
> +#define RICHACE_OWNER_MODE (RICHACE_WRITE_ATTRIBUTES | RICHACE_WRITE_ACL)
>  
>  /* flags used to simulate posix default ACLs */
> -#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
> -		| NFS4_ACE_DIRECTORY_INHERIT_ACE)
> -
> -#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS \
> -		| NFS4_ACE_INHERIT_ONLY_ACE \
> -		| NFS4_ACE_IDENTIFIER_GROUP)
> +#define RICHACE_SUPPORTED_FLAGS (		\
> +	RICHACE_FILE_INHERIT_ACE |		\
> +	RICHACE_DIRECTORY_INHERIT_ACE |		\
> +	RICHACE_INHERIT_ONLY_ACE |		\
> +	RICHACE_IDENTIFIER_GROUP |		\
> +	RICHACE_SPECIAL_WHO)
>  
>  static u32
>  mask_from_posix(unsigned short perm, unsigned int flags)
>  {
> -	int mask = NFS4_ANYONE_MODE;
> +	int mask = RICHACE_ANYONE_MODE;
>  
> -	if (flags & NFS4_ACL_OWNER)
> -		mask |= NFS4_OWNER_MODE;
> +	if (flags & FLAG_OWNER)
> +		mask |= RICHACE_OWNER_MODE;
>  	if (perm & ACL_READ)
> -		mask |= NFS4_READ_MODE;
> +		mask |= RICHACE_READ_MODE;
>  	if (perm & ACL_WRITE)
> -		mask |= NFS4_WRITE_MODE;
> -	if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
> -		mask |= NFS4_ACE_DELETE_CHILD;
> +		mask |= RICHACE_WRITE_MODE;
> +	if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
> +		mask |= RICHACE_DELETE_CHILD;
>  	if (perm & ACL_EXECUTE)
> -		mask |= NFS4_EXECUTE_MODE;
> +		mask |= RICHACE_EXECUTE_MODE;
>  	return mask;
>  }
>  
> @@ -84,13 +87,13 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
>  	u32 mask = 0;
>  
>  	if (perm & ACL_READ)
> -		mask |= NFS4_READ_MODE;
> +		mask |= RICHACE_READ_MODE;
>  	if (perm & ACL_WRITE)
> -		mask |= NFS4_WRITE_MODE;
> -	if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
> -		mask |= NFS4_ACE_DELETE_CHILD;
> +		mask |= RICHACE_WRITE_MODE;
> +	if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
> +		mask |= RICHACE_DELETE_CHILD;
>  	if (perm & ACL_EXECUTE)
> -		mask |= NFS4_EXECUTE_MODE;
> +		mask |= RICHACE_EXECUTE_MODE;
>  	return mask;
>  }
>  
> @@ -106,32 +109,33 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
>  static void
>  low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
>  {
> -	u32 write_mode = NFS4_WRITE_MODE;
> +	u32 write_mode = RICHACE_WRITE_MODE;
>  
> -	if (flags & NFS4_ACL_DIR)
> -		write_mode |= NFS4_ACE_DELETE_CHILD;
> +	if (flags & FLAG_DIRECTORY)
> +		write_mode |= RICHACE_DELETE_CHILD;
>  	*mode = 0;
> -	if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
> +	if ((perm & RICHACE_READ_MODE) == RICHACE_READ_MODE)
>  		*mode |= ACL_READ;
>  	if ((perm & write_mode) == write_mode)
>  		*mode |= ACL_WRITE;
> -	if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
> +	if ((perm & RICHACE_EXECUTE_MODE) == RICHACE_EXECUTE_MODE)
>  		*mode |= ACL_EXECUTE;
>  }
>  
> -static short ace2type(struct nfs4_ace *);
> -static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
> +static short ace2type(struct richace *);
> +static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
>  				unsigned int);
>  
>  int
> -nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> -		struct nfs4_acl **acl)
> +nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> +	      struct richacl **acl)
>  {
>  	struct inode *inode = d_inode(dentry);
>  	int error = 0;
>  	struct posix_acl *pacl = NULL, *dpacl = NULL;
> +	struct richacl_alloc alloc;
>  	unsigned int flags = 0;
> -	int size = 0;
> +	int count;
>  
>  	pacl = get_acl(inode, ACL_TYPE_ACCESS);
>  	if (!pacl)
> @@ -141,10 +145,10 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
>  		return PTR_ERR(pacl);
>  
>  	/* allocate for worst case: one (deny, allow) pair each: */
> -	size += 2 * pacl->a_count;
> +	count = 2 * pacl->a_count;
>  
>  	if (S_ISDIR(inode->i_mode)) {
> -		flags = NFS4_ACL_DIR;
> +		flags = FLAG_DIRECTORY;
>  		dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
>  		if (IS_ERR(dpacl)) {
>  			error = PTR_ERR(dpacl);
> @@ -152,20 +156,20 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
>  		}
>  
>  		if (dpacl)
> -			size += 2 * dpacl->a_count;
> +			count += 2 * dpacl->a_count;
>  	}
>  
> -	*acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
> -	if (*acl == NULL) {
> +	if (!richacl_prepare(&alloc, count)) {
>  		error = -ENOMEM;
>  		goto out;
>  	}
> -	(*acl)->naces = 0;
>  
> -	_posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);
> +	_posix_to_richacl_one(pacl, &alloc, flags);
>  
>  	if (dpacl)
> -		_posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);
> +		_posix_to_richacl_one(dpacl, &alloc, flags | FLAG_DEFAULT_ACL);
> +
> +	*acl = alloc.acl;
>  
>  out:
>  	posix_acl_release(dpacl);
> @@ -228,21 +232,22 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
>  
>  /* We assume the acl has been verified with posix_acl_valid. */
>  static void
> -_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
> -						unsigned int flags)
> +_posix_to_richacl_one(struct posix_acl *pacl, struct richacl_alloc *alloc,
> +		unsigned int flags)
>  {
>  	struct posix_acl_entry *pa, *group_owner_entry;
> -	struct nfs4_ace *ace;
> +	struct richace *ace;
>  	struct posix_acl_summary pas;
>  	unsigned short deny;
> -	int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
> -		NFS4_INHERITANCE_FLAGS | NFS4_ACE_INHERIT_ONLY_ACE : 0);
> +	int e_flags = ((flags & FLAG_DEFAULT_ACL) ?
> +		       (RICHACE_FILE_INHERIT_ACE |
> +		        RICHACE_DIRECTORY_INHERIT_ACE |
> +		        RICHACE_INHERIT_ONLY_ACE) : 0);
>  
>  	BUG_ON(pacl->a_count < 3);
>  	summarize_posix_acl(pacl, &pas);
>  
>  	pa = pacl->a_entries;
> -	ace = acl->aces + acl->naces;
>  
>  	/* We could deny everything not granted by the owner: */
>  	deny = ~pas.owner;
> @@ -252,42 +257,35 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>  	 */
>  	deny &= pas.users | pas.group | pas.groups | pas.other;
>  	if (deny) {
> -		ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> -		ace->flag = eflag;
> -		ace->access_mask = deny_mask_from_posix(deny, flags);
> -		ace->whotype = NFS4_ACL_WHO_OWNER;
> -		ace++;
> -		acl->naces++;
> +		ace = richacl_append_entry(alloc);
> +		ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> +		ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> +		ace->e_mask = deny_mask_from_posix(deny, flags);
> +		ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
>  	}
>  
> -	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> -	ace->flag = eflag;
> -	ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
> -	ace->whotype = NFS4_ACL_WHO_OWNER;
> -	ace++;
> -	acl->naces++;
> +	ace = richacl_append_entry(alloc);
> +	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> +	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> +	ace->e_mask = mask_from_posix(pa->e_perm, flags | FLAG_OWNER);
> +	ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
>  	pa++;
>  
>  	while (pa->e_tag == ACL_USER) {
>  		deny = ~(pa->e_perm & pas.mask);
>  		deny &= pas.groups | pas.group | pas.other;
>  		if (deny) {
> -			ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> -			ace->flag = eflag;
> -			ace->access_mask = deny_mask_from_posix(deny, flags);
> -			ace->whotype = NFS4_ACL_WHO_NAMED;
> -			ace->who_uid = pa->e_uid;
> -			ace++;
> -			acl->naces++;
> +			ace = richacl_append_entry(alloc);
> +			ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> +			ace->e_flags = e_flags;
> +			ace->e_mask = deny_mask_from_posix(deny, flags);
> +			ace->e_id.uid = pa->e_uid;
>  		}
> -		ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> -		ace->flag = eflag;
> -		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
> -						   flags);
> -		ace->whotype = NFS4_ACL_WHO_NAMED;
> -		ace->who_uid = pa->e_uid;
> -		ace++;
> -		acl->naces++;
> +		ace = richacl_append_entry(alloc);
> +		ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> +		ace->e_flags = e_flags;
> +		ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
> +		ace->e_id.uid = pa->e_uid;
>  		pa++;
>  	}
>  
> @@ -298,23 +296,19 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>  
>  	group_owner_entry = pa;
>  
> -	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> -	ace->flag = eflag;
> -	ace->access_mask = mask_from_posix(pas.group, flags);
> -	ace->whotype = NFS4_ACL_WHO_GROUP;
> -	ace++;
> -	acl->naces++;
> +	ace = richacl_append_entry(alloc);
> +	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> +	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> +	ace->e_mask = mask_from_posix(pas.group, flags);
> +	ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
>  	pa++;
>  
>  	while (pa->e_tag == ACL_GROUP) {
> -		ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> -		ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
> -		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
> -						   flags);
> -		ace->whotype = NFS4_ACL_WHO_NAMED;
> -		ace->who_gid = pa->e_gid;
> -		ace++;
> -		acl->naces++;
> +		ace = richacl_append_entry(alloc);
> +		ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> +		ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
> +		ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
> +		ace->e_id.gid = pa->e_gid;
>  		pa++;
>  	}
>  
> @@ -324,12 +318,11 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>  
>  	deny = ~pas.group & pas.other;
>  	if (deny) {
> -		ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> -		ace->flag = eflag;
> -		ace->access_mask = deny_mask_from_posix(deny, flags);
> -		ace->whotype = NFS4_ACL_WHO_GROUP;
> -		ace++;
> -		acl->naces++;
> +		ace = richacl_append_entry(alloc);
> +		ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> +		ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> +		ace->e_mask = deny_mask_from_posix(deny, flags);
> +		ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
>  	}
>  	pa++;
>  
> @@ -337,24 +330,22 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>  		deny = ~(pa->e_perm & pas.mask);
>  		deny &= pas.other;
>  		if (deny) {
> -			ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> -			ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
> -			ace->access_mask = deny_mask_from_posix(deny, flags);
> -			ace->whotype = NFS4_ACL_WHO_NAMED;
> -			ace->who_gid = pa->e_gid;
> -			ace++;
> -			acl->naces++;
> +			ace = richacl_append_entry(alloc);
> +			ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> +			ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
> +			ace->e_mask = deny_mask_from_posix(deny, flags);
> +			ace->e_id.gid = pa->e_gid;
>  		}
>  		pa++;
>  	}
>  
>  	if (pa->e_tag == ACL_MASK)
>  		pa++;
> -	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> -	ace->flag = eflag;
> -	ace->access_mask = mask_from_posix(pa->e_perm, flags);
> -	ace->whotype = NFS4_ACL_WHO_EVERYONE;
> -	acl->naces++;
> +	ace = richacl_append_entry(alloc);
> +	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> +	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> +	ace->e_mask = mask_from_posix(pa->e_perm, flags);
> +	ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
>  }
>  
>  static bool
> @@ -498,7 +489,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
>  	 * and effective cases: when there are no inheritable ACEs,
>  	 * calls ->set_acl with a NULL ACL structure.
>  	 */
> -	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
> +	if (state->empty && (flags & FLAG_DEFAULT_ACL))
>  		return NULL;
>  
>  	/*
> @@ -617,24 +608,24 @@ static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
>  }
>  
>  static void process_one_v4_ace(struct posix_acl_state *state,
> -				struct nfs4_ace *ace)
> +				struct richace *ace)
>  {
> -	u32 mask = ace->access_mask;
> +	u32 mask = ace->e_mask;
>  	int i;
>  
>  	state->empty = 0;
>  
>  	switch (ace2type(ace)) {
>  	case ACL_USER_OBJ:
> -		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> +		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
>  			allow_bits(&state->owner, mask);
>  		} else {
>  			deny_bits(&state->owner, mask);
>  		}
>  		break;
>  	case ACL_USER:
> -		i = find_uid(state, ace->who_uid);
> -		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> +		i = find_uid(state, ace->e_id.uid);
> +		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
>  			allow_bits(&state->users->aces[i].perms, mask);
>  		} else {
>  			deny_bits(&state->users->aces[i].perms, mask);
> @@ -643,7 +634,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
>  		}
>  		break;
>  	case ACL_GROUP_OBJ:
> -		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> +		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
>  			allow_bits(&state->group, mask);
>  		} else {
>  			deny_bits(&state->group, mask);
> @@ -655,8 +646,8 @@ static void process_one_v4_ace(struct posix_acl_state *state,
>  		}
>  		break;
>  	case ACL_GROUP:
> -		i = find_gid(state, ace->who_gid);
> -		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> +		i = find_gid(state, ace->e_id.gid);
> +		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
>  			allow_bits(&state->groups->aces[i].perms, mask);
>  		} else {
>  			deny_bits(&state->groups->aces[i].perms, mask);
> @@ -669,7 +660,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
>  		}
>  		break;
>  	case ACL_OTHER:
> -		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> +		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
>  			allow_bits(&state->owner, mask);
>  			allow_bits(&state->group, mask);
>  			allow_bits(&state->other, mask);
> @@ -687,32 +678,33 @@ static void process_one_v4_ace(struct posix_acl_state *state,
>  	}
>  }
>  
> -static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
> +static int nfs4_richacl_to_posix(struct richacl *acl,
>  		struct posix_acl **pacl, struct posix_acl **dpacl,
>  		unsigned int flags)
>  {
>  	struct posix_acl_state effective_acl_state, default_acl_state;
> -	struct nfs4_ace *ace;
> +	struct richace *ace;
>  	int ret;
>  
> -	ret = init_state(&effective_acl_state, acl->naces);
> +	ret = init_state(&effective_acl_state, acl->a_count);
>  	if (ret)
>  		return ret;
> -	ret = init_state(&default_acl_state, acl->naces);
> +	ret = init_state(&default_acl_state, acl->a_count);
>  	if (ret)
>  		goto out_estate;
>  	ret = -EINVAL;
> -	for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
> -		if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
> -		    ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
> +	richacl_for_each_entry(ace, acl) {
> +		if (ace->e_type != RICHACE_ACCESS_ALLOWED_ACE_TYPE &&
> +		    ace->e_type != RICHACE_ACCESS_DENIED_ACE_TYPE)
>  			goto out_dstate;
> -		if (ace->flag & ~NFS4_SUPPORTED_FLAGS)
> +		if (ace->e_flags & ~RICHACE_SUPPORTED_FLAGS)
>  			goto out_dstate;
> -		if ((ace->flag & NFS4_INHERITANCE_FLAGS) == 0) {
> +		if ((ace->e_flags & (RICHACE_FILE_INHERIT_ACE |
> +				     RICHACE_DIRECTORY_INHERIT_ACE)) == 0) {
>  			process_one_v4_ace(&effective_acl_state, ace);
>  			continue;
>  		}
> -		if (!(flags & NFS4_ACL_DIR))
> +		if (!(flags & FLAG_DIRECTORY))
>  			goto out_dstate;
>  		/*
>  		 * Note that when only one of FILE_INHERIT or DIRECTORY_INHERIT
> @@ -721,7 +713,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
>  		 */
>  		process_one_v4_ace(&default_acl_state, ace);
>  
> -		if (!(ace->flag & NFS4_ACE_INHERIT_ONLY_ACE))
> +		if (!(ace->e_flags & RICHACE_INHERIT_ONLY_ACE))
>  			process_one_v4_ace(&effective_acl_state, ace);
>  	}
>  	*pacl = posix_state_to_acl(&effective_acl_state, flags);
> @@ -731,7 +723,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
>  		goto out_dstate;
>  	}
>  	*dpacl = posix_state_to_acl(&default_acl_state,
> -						flags | NFS4_ACL_TYPE_DEFAULT);
> +						flags | FLAG_DEFAULT_ACL);
>  	if (IS_ERR(*dpacl)) {
>  		ret = PTR_ERR(*dpacl);
>  		*dpacl = NULL;
> @@ -750,8 +742,7 @@ out_estate:
>  }
>  
>  __be32
> -nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> -		struct nfs4_acl *acl)
> +nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
>  {
>  	__be32 error;
>  	int host_error;
> @@ -772,9 +763,9 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  		return nfserr_attrnotsupp;
>  
>  	if (S_ISDIR(inode->i_mode))
> -		flags = NFS4_ACL_DIR;
> +		flags = FLAG_DIRECTORY;
>  
> -	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
> +	host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
>  	if (host_error == -EINVAL)
>  		return nfserr_attrnotsupp;
>  	if (host_error < 0)
> @@ -801,82 +792,62 @@ out_nfserr:
>  
>  
>  static short
> -ace2type(struct nfs4_ace *ace)
> +ace2type(struct richace *ace)
>  {
> -	switch (ace->whotype) {
> -		case NFS4_ACL_WHO_NAMED:
> -			return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
> -					ACL_GROUP : ACL_USER);
> -		case NFS4_ACL_WHO_OWNER:
> +	if (ace->e_flags & RICHACE_SPECIAL_WHO) {
> +		switch (ace->e_id.special) {
> +		case RICHACE_OWNER_SPECIAL_ID:
>  			return ACL_USER_OBJ;
> -		case NFS4_ACL_WHO_GROUP:
> +		case RICHACE_GROUP_SPECIAL_ID:
>  			return ACL_GROUP_OBJ;
> -		case NFS4_ACL_WHO_EVERYONE:
> +		case RICHACE_EVERYONE_SPECIAL_ID:
>  			return ACL_OTHER;
> +		default:
> +			BUG();
> +		}
>  	}
> -	BUG();
> -	return -1;
> -}
> -
> -/*
> - * return the size of the struct nfs4_acl required to represent an acl
> - * with @entries entries.
> - */
> -int nfs4_acl_bytes(int entries)
> -{
> -	return sizeof(struct nfs4_acl) + entries * sizeof(struct nfs4_ace);
> +	return ace->e_flags & RICHACE_IDENTIFIER_GROUP ? ACL_GROUP : ACL_USER;
>  }
>  
> -static struct {
> -	char *string;
> -	int   stringlen;
> -	int type;
> -} s2t_map[] = {
> -	{
> -		.string    = "OWNER@",
> -		.stringlen = sizeof("OWNER@") - 1,
> -		.type      = NFS4_ACL_WHO_OWNER,
> -	},
> -	{
> -		.string    = "GROUP@",
> -		.stringlen = sizeof("GROUP@") - 1,
> -		.type      = NFS4_ACL_WHO_GROUP,
> -	},
> -	{
> -		.string    = "EVERYONE@",
> -		.stringlen = sizeof("EVERYONE@") - 1,
> -		.type      = NFS4_ACL_WHO_EVERYONE,
> -	},
> -};
> -
> -int
> -nfs4_acl_get_whotype(char *p, u32 len)
> +__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
> +			    char *who, u32 len)
>  {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
> -		if (s2t_map[i].stringlen == len &&
> -				0 == memcmp(s2t_map[i].string, p, len))
> -			return s2t_map[i].type;
> +	int special_id;
> +
> +	special_id = nfs4acl_who_to_special_id(who, len);
> +	if (special_id >= 0) {
> +		ace->e_flags |= RICHACE_SPECIAL_WHO;
> +		ace->e_flags &= ~RICHACE_IDENTIFIER_GROUP;
> +		ace->e_id.special = special_id;
> +		return nfs_ok;
>  	}
> -	return NFS4_ACL_WHO_NAMED;
> +	if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
> +		return nfsd_map_name_to_gid(rqstp, who, len, &ace->e_id.gid);
> +	else
> +		return nfsd_map_name_to_uid(rqstp, who, len, &ace->e_id.uid);
>  }
>  
> -__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
> +__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> +			    struct richace *ace)
>  {
> -	__be32 *p;
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
> -		if (s2t_map[i].type != who)
> -			continue;
> -		p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
> +	if (ace->e_flags & RICHACE_SPECIAL_WHO) {
> +		unsigned int special_id = ace->e_id.special;
> +		const char *who;
> +		unsigned int len;
> +		__be32 *p;
> +
> +		if (!nfs4acl_special_id_to_who(special_id, &who, &len)) {
> +			WARN_ON_ONCE(1);
> +			return nfserr_serverfault;
> +		}
> +		p = xdr_reserve_space(xdr, len + 4);
>  		if (!p)
>  			return nfserr_resource;
> -		p = xdr_encode_opaque(p, s2t_map[i].string,
> -					s2t_map[i].stringlen);
> +		p = xdr_encode_opaque(p, who, len);
>  		return 0;
>  	}
> -	WARN_ON_ONCE(1);
> -	return nfserr_serverfault;
> +	if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
> +		return nfsd4_encode_group(xdr, rqstp, ace->e_id.gid);
> +	else
> +		return nfsd4_encode_user(xdr, rqstp, ace->e_id.uid);
>  }
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 90cfda7..8c2cb16 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -159,12 +159,12 @@ is_create_with_attrs(struct nfsd4_open *open)
>   * in the returned attr bitmap.
>   */
>  static void
> -do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> -		struct nfs4_acl *acl, u32 *bmval)
> +do_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl,
> +	   u32 *bmval)
>  {
>  	__be32 status;
>  
> -	status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
> +	status = nfsd4_set_acl(rqstp, fhp, acl);
>  	if (status)
>  		/*
>  		 * We should probably fail the whole open at this point,
> @@ -299,7 +299,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
>  		goto out;
>  
>  	if (is_create_with_attrs(open) && open->op_acl != NULL)
> -		do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
> +		do_set_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
>  
>  	nfsd4_set_open_owner_reply_cache(cstate, open, *resfh);
>  	accmode = NFSD_MAY_NOP;
> @@ -674,8 +674,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  		nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);
>  
>  	if (create->cr_acl != NULL)
> -		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
> -				create->cr_bmval);
> +		do_set_acl(rqstp, &resfh, create->cr_acl, create->cr_bmval);
>  
>  	fh_unlock(&cstate->current_fh);
>  	set_change_info(&create->cr_cinfo, &cstate->current_fh);
> @@ -940,8 +939,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
>  		goto out;
>  
>  	if (setattr->sa_acl != NULL)
> -		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
> -					    setattr->sa_acl);
> +		status = nfsd4_set_acl(rqstp, &cstate->current_fh,
> +				       setattr->sa_acl);
>  	if (status)
>  		goto out;
>  	if (setattr->sa_label.len)
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 0768251..465f82a 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -303,7 +303,7 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
>  
>  static __be32
>  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
> -		   struct iattr *iattr, struct nfs4_acl **acl,
> +		   struct iattr *iattr, struct richacl **acl,
>  		   struct xdr_netobj *label)
>  {
>  	int expected_len, len = 0;
> @@ -326,38 +326,31 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
>  	}
>  	if (bmval[0] & FATTR4_WORD0_ACL) {
>  		u32 nace;
> -		struct nfs4_ace *ace;
> +		struct richace *ace;
>  
>  		READ_BUF(4); len += 4;
>  		nace = be32_to_cpup(p++);
>  
> -		if (nace > NFS4_ACL_MAX)
> +		if (nace > NFSD4_ACL_MAX)
>  			return nfserr_fbig;
>  
> -		*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
> +		*acl = svcxdr_alloc_richacl(argp, nace);
>  		if (*acl == NULL)
>  			return nfserr_jukebox;
>  
> -		(*acl)->naces = nace;
> -		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
> +		richacl_for_each_entry(ace, *acl) {
>  			READ_BUF(16); len += 16;
> -			ace->type = be32_to_cpup(p++);
> -			ace->flag = be32_to_cpup(p++);
> -			ace->access_mask = be32_to_cpup(p++);
> +			ace->e_type = be32_to_cpup(p++);
> +			ace->e_flags = be32_to_cpup(p++);
> +			ace->e_mask = be32_to_cpup(p++);
> +			if (ace->e_flags & RICHACE_SPECIAL_WHO)
> +				return nfserr_inval;
>  			dummy32 = be32_to_cpup(p++);
>  			READ_BUF(dummy32);
>  			len += XDR_QUADLEN(dummy32) << 2;
>  			READMEM(buf, dummy32);
> -			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
> -			status = nfs_ok;
> -			if (ace->whotype != NFS4_ACL_WHO_NAMED)
> -				;
> -			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
> -				status = nfsd_map_name_to_gid(argp->rqstp,
> -						buf, dummy32, &ace->who_gid);
> -			else
> -				status = nfsd_map_name_to_uid(argp->rqstp,
> -						buf, dummy32, &ace->who_uid);
> +			status = nfsd4_decode_ace_who(ace, argp->rqstp,
> +						      buf, dummy32);
>  			if (status)
>  				return status;
>  		}
> @@ -2147,18 +2140,6 @@ static u32 nfs4_file_type(umode_t mode)
>  	};
>  }
>  
> -static inline __be32
> -nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> -		     struct nfs4_ace *ace)
> -{
> -	if (ace->whotype != NFS4_ACL_WHO_NAMED)
> -		return nfs4_acl_write_who(xdr, ace->whotype);
> -	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
> -		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
> -	else
> -		return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
> -}
> -
>  #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
>  			      FATTR4_WORD0_RDATTR_ERROR)
>  #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
> @@ -2249,7 +2230,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  	u32 rdattr_err = 0;
>  	__be32 status;
>  	int err;
> -	struct nfs4_acl *acl = NULL;
> +	struct richacl *acl = NULL;
>  	void *context = NULL;
>  	int contextlen;
>  	bool contextsupport = false;
> @@ -2295,7 +2276,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  		fhp = tempfh;
>  	}
>  	if (bmval0 & FATTR4_WORD0_ACL) {
> -		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
> +		err = nfsd4_get_acl(rqstp, dentry, &acl);
>  		if (err == -EOPNOTSUPP)
>  			bmval0 &= ~FATTR4_WORD0_ACL;
>  		else if (err == -EINVAL) {
> @@ -2469,7 +2450,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  		*p++ = cpu_to_be32(rdattr_err);
>  	}
>  	if (bmval0 & FATTR4_WORD0_ACL) {
> -		struct nfs4_ace *ace;
> +		struct richace *ace;
>  
>  		if (acl == NULL) {
>  			p = xdr_reserve_space(xdr, 4);
> @@ -2482,17 +2463,16 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  		p = xdr_reserve_space(xdr, 4);
>  		if (!p)
>  			goto out_resource;
> -		*p++ = cpu_to_be32(acl->naces);
> +		*p++ = cpu_to_be32(acl->a_count);
>  
> -		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
> +		richacl_for_each_entry(ace, acl) {
>  			p = xdr_reserve_space(xdr, 4*3);
>  			if (!p)
>  				goto out_resource;
> -			*p++ = cpu_to_be32(ace->type);
> -			*p++ = cpu_to_be32(ace->flag);
> -			*p++ = cpu_to_be32(ace->access_mask &
> -							NFS4_ACE_MASK_ALL);
> -			status = nfsd4_encode_aclname(xdr, rqstp, ace);
> +			*p++ = cpu_to_be32(ace->e_type);
> +			*p++ = cpu_to_be32(ace->e_flags & ~RICHACE_SPECIAL_WHO);
> +			*p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
> +			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
>  			if (status)
>  				goto out;
>  		}
> @@ -2755,7 +2735,7 @@ out:
>  	if (context)
>  		security_release_secctx(context, contextlen);
>  #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
> -	kfree(acl);
> +	richacl_put(acl);
>  	if (tempfh) {
>  		fh_put(tempfh);
>  		kfree(tempfh);
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index b698585..c311066 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -118,7 +118,7 @@ struct nfsd4_create {
>  	u32		cr_bmval[3];        /* request */
>  	struct iattr	cr_iattr;           /* request */
>  	struct nfsd4_change_info  cr_cinfo; /* response */
> -	struct nfs4_acl *cr_acl;
> +	struct richacl *cr_acl;
>  	struct xdr_netobj cr_label;
>  };
>  #define cr_datalen	u.link.datalen
> @@ -248,7 +248,7 @@ struct nfsd4_open {
>  	struct nfs4_file *op_file;          /* used during processing */
>  	struct nfs4_ol_stateid *op_stp;	    /* used during processing */
>  	struct nfs4_clnt_odstate *op_odstate; /* used during processing */
> -	struct nfs4_acl *op_acl;
> +	struct richacl *op_acl;
>  	struct xdr_netobj op_label;
>  };
>  
> @@ -332,7 +332,7 @@ struct nfsd4_setattr {
>  	stateid_t	sa_stateid;         /* request */
>  	u32		sa_bmval[3];        /* request */
>  	struct iattr	sa_iattr;           /* request */
> -	struct nfs4_acl *sa_acl;
> +	struct richacl *sa_acl;
>  	struct xdr_netobj sa_label;
>  };
>  
> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index b8e72aa..992ddc4 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -16,29 +16,6 @@
>  #include <linux/uidgid.h>
>  #include <uapi/linux/nfs4.h>
>  
> -enum nfs4_acl_whotype {
> -	NFS4_ACL_WHO_NAMED = 0,
> -	NFS4_ACL_WHO_OWNER,
> -	NFS4_ACL_WHO_GROUP,
> -	NFS4_ACL_WHO_EVERYONE,
> -};
> -
> -struct nfs4_ace {
> -	uint32_t	type;
> -	uint32_t	flag;
> -	uint32_t	access_mask;
> -	int		whotype;
> -	union {
> -		kuid_t	who_uid;
> -		kgid_t	who_gid;
> -	};
> -};
> -
> -struct nfs4_acl {
> -	uint32_t	naces;
> -	struct nfs4_ace	aces[0];
> -};
> -
>  #define NFS4_MAXLABELLEN	2048
>  
>  struct nfs4_label {
> diff --git a/include/linux/nfs4acl.h b/include/linux/nfs4acl.h
> new file mode 100644
> index 0000000..db9f9a6
> --- /dev/null
> +++ b/include/linux/nfs4acl.h
> @@ -0,0 +1,7 @@
> +#ifndef __LINUX_NFS4ACL_H
> +#define __LINUX_NFS4ACL_H
> +
> +int nfs4acl_who_to_special_id(const char *, u32);
> +bool nfs4acl_special_id_to_who(unsigned int, const char **, unsigned int *);
> +
> +#endif
> -- 
> 2.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" 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 3e09c06..dd3f2d6 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -268,6 +268,12 @@  config NFS_COMMON
 	depends on NFSD || NFS_FS || LOCKD
 	default y
 
+config NFS_RICHACL
+	bool
+	depends on NFSD_V4 || NFS_V4
+	select FS_RICHACL
+	default y
+
 source "net/sunrpc/Kconfig"
 source "fs/ceph/Kconfig"
 source "fs/cifs/Kconfig"
diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
index d153ca3..e055139 100644
--- a/fs/nfs_common/Makefile
+++ b/fs/nfs_common/Makefile
@@ -4,5 +4,6 @@ 
 
 obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
 nfs_acl-objs := nfsacl.o
+obj-$(CONFIG_NFS_RICHACL) += nfs4acl.o
 
 obj-$(CONFIG_GRACE_PERIOD) += grace.o
diff --git a/fs/nfs_common/nfs4acl.c b/fs/nfs_common/nfs4acl.c
new file mode 100644
index 0000000..02df064
--- /dev/null
+++ b/fs/nfs_common/nfs4acl.c
@@ -0,0 +1,44 @@ 
+#include <linux/fs.h>
+#include <linux/richacl.h>
+#include <linux/nfs4acl.h>
+
+static struct special_id {
+	char *who;
+	int   len;
+} special_who_map[] = {
+	[RICHACE_OWNER_SPECIAL_ID] = {
+		.who = "OWNER@",
+		.len = sizeof("OWNER@") - 1 },
+	[RICHACE_GROUP_SPECIAL_ID] = {
+		.who = "GROUP@",
+		.len = sizeof("GROUP@") - 1 },
+	[RICHACE_EVERYONE_SPECIAL_ID] = {
+		.who = "EVERYONE@",
+		.len = sizeof("EVERYONE@") - 1 }
+};
+
+int nfs4acl_who_to_special_id(const char *who, u32 len)
+{
+	int n;
+
+	for (n = 0; n < ARRAY_SIZE(special_who_map); n++) {
+		if (len == special_who_map[n].len &&
+		    !memcmp(who, special_who_map[n].who, len))
+			return n;
+	}
+	return -1;
+}
+EXPORT_SYMBOL(nfs4acl_who_to_special_id);
+
+bool nfs4acl_special_id_to_who(unsigned int special_who,
+			       const char **who, unsigned int *len)
+{
+	struct special_id *special = &special_who_map[special_who];
+
+	if (special_who > ARRAY_SIZE(special_who_map) || !special->len)
+		return false;
+	*who = special->who;
+	*len = special->len;
+	return true;
+}
+EXPORT_SYMBOL(nfs4acl_special_id_to_who);
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index a0b77fc..811379a 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -70,6 +70,7 @@  config NFSD_V4
 	depends on NFSD && PROC_FS
 	select NFSD_V3
 	select FS_POSIX_ACL
+	select FS_RICHACL
 	select SUNRPC_GSS
 	select CRYPTO
 	select GRACE_PERIOD
diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 4cd7c69..1c5deb5 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -35,25 +35,27 @@ 
 #ifndef LINUX_NFS4_ACL_H
 #define LINUX_NFS4_ACL_H
 
-struct nfs4_acl;
+struct richacl;
+struct richace;
 struct svc_fh;
 struct svc_rqst;
 
 /*
  * Maximum ACL we'll accept from a client; chosen (somewhat
  * arbitrarily) so that kmalloc'ing the ACL shouldn't require a
- * high-order allocation.  This allows 204 ACEs on x86_64:
+ * high-order allocation.  This allows 339 ACEs on x86_64:
  */
-#define NFS4_ACL_MAX ((PAGE_SIZE - sizeof(struct nfs4_acl)) \
-			/ sizeof(struct nfs4_ace))
+#define NFSD4_ACL_MAX ((PAGE_SIZE - sizeof(struct richacl)) \
+			/ sizeof(struct richace))
 
-int nfs4_acl_bytes(int entries);
-int nfs4_acl_get_whotype(char *, u32);
-__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
+__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
+			    char *who, u32 len);
+__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+			    struct richace *ace);
 
-int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-		struct nfs4_acl **acl);
-__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
-		struct nfs4_acl *acl);
+int nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
+		  struct richacl **acl);
+__be32 nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		     struct richacl *acl);
 
 #endif /* LINUX_NFS4_ACL_H */
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index eb5accf..582f772 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -36,45 +36,48 @@ 
 
 #include <linux/slab.h>
 #include <linux/nfs_fs.h>
+#include <linux/richacl_compat.h>
+#include <linux/nfs4acl.h>
 #include "nfsfh.h"
 #include "nfsd.h"
+#include "idmap.h"
 #include "acl.h"
 #include "vfs.h"
 
-#define NFS4_ACL_TYPE_DEFAULT	0x01
-#define NFS4_ACL_DIR		0x02
-#define NFS4_ACL_OWNER		0x04
+#define FLAG_DEFAULT_ACL	0x01
+#define FLAG_DIRECTORY		0x02
+#define FLAG_OWNER		0x04
 
 /* mode bit translations: */
-#define NFS4_READ_MODE (NFS4_ACE_READ_DATA)
-#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA)
-#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
-#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
-#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
+#define RICHACE_READ_MODE (RICHACE_READ_DATA)
+#define RICHACE_WRITE_MODE (RICHACE_WRITE_DATA | RICHACE_APPEND_DATA)
+#define RICHACE_EXECUTE_MODE RICHACE_EXECUTE
+#define RICHACE_ANYONE_MODE (RICHACE_READ_ATTRIBUTES | RICHACE_READ_ACL | RICHACE_SYNCHRONIZE)
+#define RICHACE_OWNER_MODE (RICHACE_WRITE_ATTRIBUTES | RICHACE_WRITE_ACL)
 
 /* flags used to simulate posix default ACLs */
-#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
-		| NFS4_ACE_DIRECTORY_INHERIT_ACE)
-
-#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS \
-		| NFS4_ACE_INHERIT_ONLY_ACE \
-		| NFS4_ACE_IDENTIFIER_GROUP)
+#define RICHACE_SUPPORTED_FLAGS (		\
+	RICHACE_FILE_INHERIT_ACE |		\
+	RICHACE_DIRECTORY_INHERIT_ACE |		\
+	RICHACE_INHERIT_ONLY_ACE |		\
+	RICHACE_IDENTIFIER_GROUP |		\
+	RICHACE_SPECIAL_WHO)
 
 static u32
 mask_from_posix(unsigned short perm, unsigned int flags)
 {
-	int mask = NFS4_ANYONE_MODE;
+	int mask = RICHACE_ANYONE_MODE;
 
-	if (flags & NFS4_ACL_OWNER)
-		mask |= NFS4_OWNER_MODE;
+	if (flags & FLAG_OWNER)
+		mask |= RICHACE_OWNER_MODE;
 	if (perm & ACL_READ)
-		mask |= NFS4_READ_MODE;
+		mask |= RICHACE_READ_MODE;
 	if (perm & ACL_WRITE)
-		mask |= NFS4_WRITE_MODE;
-	if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
-		mask |= NFS4_ACE_DELETE_CHILD;
+		mask |= RICHACE_WRITE_MODE;
+	if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
+		mask |= RICHACE_DELETE_CHILD;
 	if (perm & ACL_EXECUTE)
-		mask |= NFS4_EXECUTE_MODE;
+		mask |= RICHACE_EXECUTE_MODE;
 	return mask;
 }
 
@@ -84,13 +87,13 @@  deny_mask_from_posix(unsigned short perm, u32 flags)
 	u32 mask = 0;
 
 	if (perm & ACL_READ)
-		mask |= NFS4_READ_MODE;
+		mask |= RICHACE_READ_MODE;
 	if (perm & ACL_WRITE)
-		mask |= NFS4_WRITE_MODE;
-	if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
-		mask |= NFS4_ACE_DELETE_CHILD;
+		mask |= RICHACE_WRITE_MODE;
+	if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
+		mask |= RICHACE_DELETE_CHILD;
 	if (perm & ACL_EXECUTE)
-		mask |= NFS4_EXECUTE_MODE;
+		mask |= RICHACE_EXECUTE_MODE;
 	return mask;
 }
 
@@ -106,32 +109,33 @@  deny_mask_from_posix(unsigned short perm, u32 flags)
 static void
 low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
 {
-	u32 write_mode = NFS4_WRITE_MODE;
+	u32 write_mode = RICHACE_WRITE_MODE;
 
-	if (flags & NFS4_ACL_DIR)
-		write_mode |= NFS4_ACE_DELETE_CHILD;
+	if (flags & FLAG_DIRECTORY)
+		write_mode |= RICHACE_DELETE_CHILD;
 	*mode = 0;
-	if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
+	if ((perm & RICHACE_READ_MODE) == RICHACE_READ_MODE)
 		*mode |= ACL_READ;
 	if ((perm & write_mode) == write_mode)
 		*mode |= ACL_WRITE;
-	if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
+	if ((perm & RICHACE_EXECUTE_MODE) == RICHACE_EXECUTE_MODE)
 		*mode |= ACL_EXECUTE;
 }
 
-static short ace2type(struct nfs4_ace *);
-static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
+static short ace2type(struct richace *);
+static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
 				unsigned int);
 
 int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-		struct nfs4_acl **acl)
+nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
+	      struct richacl **acl)
 {
 	struct inode *inode = d_inode(dentry);
 	int error = 0;
 	struct posix_acl *pacl = NULL, *dpacl = NULL;
+	struct richacl_alloc alloc;
 	unsigned int flags = 0;
-	int size = 0;
+	int count;
 
 	pacl = get_acl(inode, ACL_TYPE_ACCESS);
 	if (!pacl)
@@ -141,10 +145,10 @@  nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
 		return PTR_ERR(pacl);
 
 	/* allocate for worst case: one (deny, allow) pair each: */
-	size += 2 * pacl->a_count;
+	count = 2 * pacl->a_count;
 
 	if (S_ISDIR(inode->i_mode)) {
-		flags = NFS4_ACL_DIR;
+		flags = FLAG_DIRECTORY;
 		dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
 		if (IS_ERR(dpacl)) {
 			error = PTR_ERR(dpacl);
@@ -152,20 +156,20 @@  nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
 		}
 
 		if (dpacl)
-			size += 2 * dpacl->a_count;
+			count += 2 * dpacl->a_count;
 	}
 
-	*acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
-	if (*acl == NULL) {
+	if (!richacl_prepare(&alloc, count)) {
 		error = -ENOMEM;
 		goto out;
 	}
-	(*acl)->naces = 0;
 
-	_posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);
+	_posix_to_richacl_one(pacl, &alloc, flags);
 
 	if (dpacl)
-		_posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);
+		_posix_to_richacl_one(dpacl, &alloc, flags | FLAG_DEFAULT_ACL);
+
+	*acl = alloc.acl;
 
 out:
 	posix_acl_release(dpacl);
@@ -228,21 +232,22 @@  summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
 
 /* We assume the acl has been verified with posix_acl_valid. */
 static void
-_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
-						unsigned int flags)
+_posix_to_richacl_one(struct posix_acl *pacl, struct richacl_alloc *alloc,
+		unsigned int flags)
 {
 	struct posix_acl_entry *pa, *group_owner_entry;
-	struct nfs4_ace *ace;
+	struct richace *ace;
 	struct posix_acl_summary pas;
 	unsigned short deny;
-	int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
-		NFS4_INHERITANCE_FLAGS | NFS4_ACE_INHERIT_ONLY_ACE : 0);
+	int e_flags = ((flags & FLAG_DEFAULT_ACL) ?
+		       (RICHACE_FILE_INHERIT_ACE |
+		        RICHACE_DIRECTORY_INHERIT_ACE |
+		        RICHACE_INHERIT_ONLY_ACE) : 0);
 
 	BUG_ON(pacl->a_count < 3);
 	summarize_posix_acl(pacl, &pas);
 
 	pa = pacl->a_entries;
-	ace = acl->aces + acl->naces;
 
 	/* We could deny everything not granted by the owner: */
 	deny = ~pas.owner;
@@ -252,42 +257,35 @@  _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
 	 */
 	deny &= pas.users | pas.group | pas.groups | pas.other;
 	if (deny) {
-		ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
-		ace->flag = eflag;
-		ace->access_mask = deny_mask_from_posix(deny, flags);
-		ace->whotype = NFS4_ACL_WHO_OWNER;
-		ace++;
-		acl->naces++;
+		ace = richacl_append_entry(alloc);
+		ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+		ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+		ace->e_mask = deny_mask_from_posix(deny, flags);
+		ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
 	}
 
-	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
-	ace->flag = eflag;
-	ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
-	ace->whotype = NFS4_ACL_WHO_OWNER;
-	ace++;
-	acl->naces++;
+	ace = richacl_append_entry(alloc);
+	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+	ace->e_mask = mask_from_posix(pa->e_perm, flags | FLAG_OWNER);
+	ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
 	pa++;
 
 	while (pa->e_tag == ACL_USER) {
 		deny = ~(pa->e_perm & pas.mask);
 		deny &= pas.groups | pas.group | pas.other;
 		if (deny) {
-			ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
-			ace->flag = eflag;
-			ace->access_mask = deny_mask_from_posix(deny, flags);
-			ace->whotype = NFS4_ACL_WHO_NAMED;
-			ace->who_uid = pa->e_uid;
-			ace++;
-			acl->naces++;
+			ace = richacl_append_entry(alloc);
+			ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+			ace->e_flags = e_flags;
+			ace->e_mask = deny_mask_from_posix(deny, flags);
+			ace->e_id.uid = pa->e_uid;
 		}
-		ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
-		ace->flag = eflag;
-		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
-						   flags);
-		ace->whotype = NFS4_ACL_WHO_NAMED;
-		ace->who_uid = pa->e_uid;
-		ace++;
-		acl->naces++;
+		ace = richacl_append_entry(alloc);
+		ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+		ace->e_flags = e_flags;
+		ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
+		ace->e_id.uid = pa->e_uid;
 		pa++;
 	}
 
@@ -298,23 +296,19 @@  _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
 
 	group_owner_entry = pa;
 
-	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
-	ace->flag = eflag;
-	ace->access_mask = mask_from_posix(pas.group, flags);
-	ace->whotype = NFS4_ACL_WHO_GROUP;
-	ace++;
-	acl->naces++;
+	ace = richacl_append_entry(alloc);
+	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+	ace->e_mask = mask_from_posix(pas.group, flags);
+	ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
 	pa++;
 
 	while (pa->e_tag == ACL_GROUP) {
-		ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
-		ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
-		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
-						   flags);
-		ace->whotype = NFS4_ACL_WHO_NAMED;
-		ace->who_gid = pa->e_gid;
-		ace++;
-		acl->naces++;
+		ace = richacl_append_entry(alloc);
+		ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+		ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
+		ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
+		ace->e_id.gid = pa->e_gid;
 		pa++;
 	}
 
@@ -324,12 +318,11 @@  _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
 
 	deny = ~pas.group & pas.other;
 	if (deny) {
-		ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
-		ace->flag = eflag;
-		ace->access_mask = deny_mask_from_posix(deny, flags);
-		ace->whotype = NFS4_ACL_WHO_GROUP;
-		ace++;
-		acl->naces++;
+		ace = richacl_append_entry(alloc);
+		ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+		ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+		ace->e_mask = deny_mask_from_posix(deny, flags);
+		ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
 	}
 	pa++;
 
@@ -337,24 +330,22 @@  _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
 		deny = ~(pa->e_perm & pas.mask);
 		deny &= pas.other;
 		if (deny) {
-			ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
-			ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
-			ace->access_mask = deny_mask_from_posix(deny, flags);
-			ace->whotype = NFS4_ACL_WHO_NAMED;
-			ace->who_gid = pa->e_gid;
-			ace++;
-			acl->naces++;
+			ace = richacl_append_entry(alloc);
+			ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+			ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
+			ace->e_mask = deny_mask_from_posix(deny, flags);
+			ace->e_id.gid = pa->e_gid;
 		}
 		pa++;
 	}
 
 	if (pa->e_tag == ACL_MASK)
 		pa++;
-	ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
-	ace->flag = eflag;
-	ace->access_mask = mask_from_posix(pa->e_perm, flags);
-	ace->whotype = NFS4_ACL_WHO_EVERYONE;
-	acl->naces++;
+	ace = richacl_append_entry(alloc);
+	ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+	ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+	ace->e_mask = mask_from_posix(pa->e_perm, flags);
+	ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
 }
 
 static bool
@@ -498,7 +489,7 @@  posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
 	 * and effective cases: when there are no inheritable ACEs,
 	 * calls ->set_acl with a NULL ACL structure.
 	 */
-	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
+	if (state->empty && (flags & FLAG_DEFAULT_ACL))
 		return NULL;
 
 	/*
@@ -617,24 +608,24 @@  static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
 }
 
 static void process_one_v4_ace(struct posix_acl_state *state,
-				struct nfs4_ace *ace)
+				struct richace *ace)
 {
-	u32 mask = ace->access_mask;
+	u32 mask = ace->e_mask;
 	int i;
 
 	state->empty = 0;
 
 	switch (ace2type(ace)) {
 	case ACL_USER_OBJ:
-		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
 			allow_bits(&state->owner, mask);
 		} else {
 			deny_bits(&state->owner, mask);
 		}
 		break;
 	case ACL_USER:
-		i = find_uid(state, ace->who_uid);
-		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+		i = find_uid(state, ace->e_id.uid);
+		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
 			allow_bits(&state->users->aces[i].perms, mask);
 		} else {
 			deny_bits(&state->users->aces[i].perms, mask);
@@ -643,7 +634,7 @@  static void process_one_v4_ace(struct posix_acl_state *state,
 		}
 		break;
 	case ACL_GROUP_OBJ:
-		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
 			allow_bits(&state->group, mask);
 		} else {
 			deny_bits(&state->group, mask);
@@ -655,8 +646,8 @@  static void process_one_v4_ace(struct posix_acl_state *state,
 		}
 		break;
 	case ACL_GROUP:
-		i = find_gid(state, ace->who_gid);
-		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+		i = find_gid(state, ace->e_id.gid);
+		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
 			allow_bits(&state->groups->aces[i].perms, mask);
 		} else {
 			deny_bits(&state->groups->aces[i].perms, mask);
@@ -669,7 +660,7 @@  static void process_one_v4_ace(struct posix_acl_state *state,
 		}
 		break;
 	case ACL_OTHER:
-		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+		if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
 			allow_bits(&state->owner, mask);
 			allow_bits(&state->group, mask);
 			allow_bits(&state->other, mask);
@@ -687,32 +678,33 @@  static void process_one_v4_ace(struct posix_acl_state *state,
 	}
 }
 
-static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
+static int nfs4_richacl_to_posix(struct richacl *acl,
 		struct posix_acl **pacl, struct posix_acl **dpacl,
 		unsigned int flags)
 {
 	struct posix_acl_state effective_acl_state, default_acl_state;
-	struct nfs4_ace *ace;
+	struct richace *ace;
 	int ret;
 
-	ret = init_state(&effective_acl_state, acl->naces);
+	ret = init_state(&effective_acl_state, acl->a_count);
 	if (ret)
 		return ret;
-	ret = init_state(&default_acl_state, acl->naces);
+	ret = init_state(&default_acl_state, acl->a_count);
 	if (ret)
 		goto out_estate;
 	ret = -EINVAL;
-	for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
-		if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
-		    ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
+	richacl_for_each_entry(ace, acl) {
+		if (ace->e_type != RICHACE_ACCESS_ALLOWED_ACE_TYPE &&
+		    ace->e_type != RICHACE_ACCESS_DENIED_ACE_TYPE)
 			goto out_dstate;
-		if (ace->flag & ~NFS4_SUPPORTED_FLAGS)
+		if (ace->e_flags & ~RICHACE_SUPPORTED_FLAGS)
 			goto out_dstate;
-		if ((ace->flag & NFS4_INHERITANCE_FLAGS) == 0) {
+		if ((ace->e_flags & (RICHACE_FILE_INHERIT_ACE |
+				     RICHACE_DIRECTORY_INHERIT_ACE)) == 0) {
 			process_one_v4_ace(&effective_acl_state, ace);
 			continue;
 		}
-		if (!(flags & NFS4_ACL_DIR))
+		if (!(flags & FLAG_DIRECTORY))
 			goto out_dstate;
 		/*
 		 * Note that when only one of FILE_INHERIT or DIRECTORY_INHERIT
@@ -721,7 +713,7 @@  static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
 		 */
 		process_one_v4_ace(&default_acl_state, ace);
 
-		if (!(ace->flag & NFS4_ACE_INHERIT_ONLY_ACE))
+		if (!(ace->e_flags & RICHACE_INHERIT_ONLY_ACE))
 			process_one_v4_ace(&effective_acl_state, ace);
 	}
 	*pacl = posix_state_to_acl(&effective_acl_state, flags);
@@ -731,7 +723,7 @@  static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
 		goto out_dstate;
 	}
 	*dpacl = posix_state_to_acl(&default_acl_state,
-						flags | NFS4_ACL_TYPE_DEFAULT);
+						flags | FLAG_DEFAULT_ACL);
 	if (IS_ERR(*dpacl)) {
 		ret = PTR_ERR(*dpacl);
 		*dpacl = NULL;
@@ -750,8 +742,7 @@  out_estate:
 }
 
 __be32
-nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
-		struct nfs4_acl *acl)
+nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
 {
 	__be32 error;
 	int host_error;
@@ -772,9 +763,9 @@  nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
 		return nfserr_attrnotsupp;
 
 	if (S_ISDIR(inode->i_mode))
-		flags = NFS4_ACL_DIR;
+		flags = FLAG_DIRECTORY;
 
-	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
+	host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
 	if (host_error == -EINVAL)
 		return nfserr_attrnotsupp;
 	if (host_error < 0)
@@ -801,82 +792,62 @@  out_nfserr:
 
 
 static short
-ace2type(struct nfs4_ace *ace)
+ace2type(struct richace *ace)
 {
-	switch (ace->whotype) {
-		case NFS4_ACL_WHO_NAMED:
-			return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
-					ACL_GROUP : ACL_USER);
-		case NFS4_ACL_WHO_OWNER:
+	if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+		switch (ace->e_id.special) {
+		case RICHACE_OWNER_SPECIAL_ID:
 			return ACL_USER_OBJ;
-		case NFS4_ACL_WHO_GROUP:
+		case RICHACE_GROUP_SPECIAL_ID:
 			return ACL_GROUP_OBJ;
-		case NFS4_ACL_WHO_EVERYONE:
+		case RICHACE_EVERYONE_SPECIAL_ID:
 			return ACL_OTHER;
+		default:
+			BUG();
+		}
 	}
-	BUG();
-	return -1;
-}
-
-/*
- * return the size of the struct nfs4_acl required to represent an acl
- * with @entries entries.
- */
-int nfs4_acl_bytes(int entries)
-{
-	return sizeof(struct nfs4_acl) + entries * sizeof(struct nfs4_ace);
+	return ace->e_flags & RICHACE_IDENTIFIER_GROUP ? ACL_GROUP : ACL_USER;
 }
 
-static struct {
-	char *string;
-	int   stringlen;
-	int type;
-} s2t_map[] = {
-	{
-		.string    = "OWNER@",
-		.stringlen = sizeof("OWNER@") - 1,
-		.type      = NFS4_ACL_WHO_OWNER,
-	},
-	{
-		.string    = "GROUP@",
-		.stringlen = sizeof("GROUP@") - 1,
-		.type      = NFS4_ACL_WHO_GROUP,
-	},
-	{
-		.string    = "EVERYONE@",
-		.stringlen = sizeof("EVERYONE@") - 1,
-		.type      = NFS4_ACL_WHO_EVERYONE,
-	},
-};
-
-int
-nfs4_acl_get_whotype(char *p, u32 len)
+__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
+			    char *who, u32 len)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
-		if (s2t_map[i].stringlen == len &&
-				0 == memcmp(s2t_map[i].string, p, len))
-			return s2t_map[i].type;
+	int special_id;
+
+	special_id = nfs4acl_who_to_special_id(who, len);
+	if (special_id >= 0) {
+		ace->e_flags |= RICHACE_SPECIAL_WHO;
+		ace->e_flags &= ~RICHACE_IDENTIFIER_GROUP;
+		ace->e_id.special = special_id;
+		return nfs_ok;
 	}
-	return NFS4_ACL_WHO_NAMED;
+	if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+		return nfsd_map_name_to_gid(rqstp, who, len, &ace->e_id.gid);
+	else
+		return nfsd_map_name_to_uid(rqstp, who, len, &ace->e_id.uid);
 }
 
-__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
+__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+			    struct richace *ace)
 {
-	__be32 *p;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
-		if (s2t_map[i].type != who)
-			continue;
-		p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
+	if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+		unsigned int special_id = ace->e_id.special;
+		const char *who;
+		unsigned int len;
+		__be32 *p;
+
+		if (!nfs4acl_special_id_to_who(special_id, &who, &len)) {
+			WARN_ON_ONCE(1);
+			return nfserr_serverfault;
+		}
+		p = xdr_reserve_space(xdr, len + 4);
 		if (!p)
 			return nfserr_resource;
-		p = xdr_encode_opaque(p, s2t_map[i].string,
-					s2t_map[i].stringlen);
+		p = xdr_encode_opaque(p, who, len);
 		return 0;
 	}
-	WARN_ON_ONCE(1);
-	return nfserr_serverfault;
+	if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+		return nfsd4_encode_group(xdr, rqstp, ace->e_id.gid);
+	else
+		return nfsd4_encode_user(xdr, rqstp, ace->e_id.uid);
 }
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 90cfda7..8c2cb16 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -159,12 +159,12 @@  is_create_with_attrs(struct nfsd4_open *open)
  * in the returned attr bitmap.
  */
 static void
-do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
-		struct nfs4_acl *acl, u32 *bmval)
+do_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl,
+	   u32 *bmval)
 {
 	__be32 status;
 
-	status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
+	status = nfsd4_set_acl(rqstp, fhp, acl);
 	if (status)
 		/*
 		 * We should probably fail the whole open at this point,
@@ -299,7 +299,7 @@  do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
 		goto out;
 
 	if (is_create_with_attrs(open) && open->op_acl != NULL)
-		do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
+		do_set_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
 
 	nfsd4_set_open_owner_reply_cache(cstate, open, *resfh);
 	accmode = NFSD_MAY_NOP;
@@ -674,8 +674,7 @@  nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);
 
 	if (create->cr_acl != NULL)
-		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
-				create->cr_bmval);
+		do_set_acl(rqstp, &resfh, create->cr_acl, create->cr_bmval);
 
 	fh_unlock(&cstate->current_fh);
 	set_change_info(&create->cr_cinfo, &cstate->current_fh);
@@ -940,8 +939,8 @@  nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		goto out;
 
 	if (setattr->sa_acl != NULL)
-		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
-					    setattr->sa_acl);
+		status = nfsd4_set_acl(rqstp, &cstate->current_fh,
+				       setattr->sa_acl);
 	if (status)
 		goto out;
 	if (setattr->sa_label.len)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 0768251..465f82a 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -303,7 +303,7 @@  nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
 
 static __be32
 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
-		   struct iattr *iattr, struct nfs4_acl **acl,
+		   struct iattr *iattr, struct richacl **acl,
 		   struct xdr_netobj *label)
 {
 	int expected_len, len = 0;
@@ -326,38 +326,31 @@  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 	}
 	if (bmval[0] & FATTR4_WORD0_ACL) {
 		u32 nace;
-		struct nfs4_ace *ace;
+		struct richace *ace;
 
 		READ_BUF(4); len += 4;
 		nace = be32_to_cpup(p++);
 
-		if (nace > NFS4_ACL_MAX)
+		if (nace > NFSD4_ACL_MAX)
 			return nfserr_fbig;
 
-		*acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
+		*acl = svcxdr_alloc_richacl(argp, nace);
 		if (*acl == NULL)
 			return nfserr_jukebox;
 
-		(*acl)->naces = nace;
-		for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
+		richacl_for_each_entry(ace, *acl) {
 			READ_BUF(16); len += 16;
-			ace->type = be32_to_cpup(p++);
-			ace->flag = be32_to_cpup(p++);
-			ace->access_mask = be32_to_cpup(p++);
+			ace->e_type = be32_to_cpup(p++);
+			ace->e_flags = be32_to_cpup(p++);
+			ace->e_mask = be32_to_cpup(p++);
+			if (ace->e_flags & RICHACE_SPECIAL_WHO)
+				return nfserr_inval;
 			dummy32 = be32_to_cpup(p++);
 			READ_BUF(dummy32);
 			len += XDR_QUADLEN(dummy32) << 2;
 			READMEM(buf, dummy32);
-			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
-			status = nfs_ok;
-			if (ace->whotype != NFS4_ACL_WHO_NAMED)
-				;
-			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
-				status = nfsd_map_name_to_gid(argp->rqstp,
-						buf, dummy32, &ace->who_gid);
-			else
-				status = nfsd_map_name_to_uid(argp->rqstp,
-						buf, dummy32, &ace->who_uid);
+			status = nfsd4_decode_ace_who(ace, argp->rqstp,
+						      buf, dummy32);
 			if (status)
 				return status;
 		}
@@ -2147,18 +2140,6 @@  static u32 nfs4_file_type(umode_t mode)
 	};
 }
 
-static inline __be32
-nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
-		     struct nfs4_ace *ace)
-{
-	if (ace->whotype != NFS4_ACL_WHO_NAMED)
-		return nfs4_acl_write_who(xdr, ace->whotype);
-	else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
-		return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
-	else
-		return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
-}
-
 #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
 			      FATTR4_WORD0_RDATTR_ERROR)
 #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
@@ -2249,7 +2230,7 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 	u32 rdattr_err = 0;
 	__be32 status;
 	int err;
-	struct nfs4_acl *acl = NULL;
+	struct richacl *acl = NULL;
 	void *context = NULL;
 	int contextlen;
 	bool contextsupport = false;
@@ -2295,7 +2276,7 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		fhp = tempfh;
 	}
 	if (bmval0 & FATTR4_WORD0_ACL) {
-		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+		err = nfsd4_get_acl(rqstp, dentry, &acl);
 		if (err == -EOPNOTSUPP)
 			bmval0 &= ~FATTR4_WORD0_ACL;
 		else if (err == -EINVAL) {
@@ -2469,7 +2450,7 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		*p++ = cpu_to_be32(rdattr_err);
 	}
 	if (bmval0 & FATTR4_WORD0_ACL) {
-		struct nfs4_ace *ace;
+		struct richace *ace;
 
 		if (acl == NULL) {
 			p = xdr_reserve_space(xdr, 4);
@@ -2482,17 +2463,16 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		p = xdr_reserve_space(xdr, 4);
 		if (!p)
 			goto out_resource;
-		*p++ = cpu_to_be32(acl->naces);
+		*p++ = cpu_to_be32(acl->a_count);
 
-		for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
+		richacl_for_each_entry(ace, acl) {
 			p = xdr_reserve_space(xdr, 4*3);
 			if (!p)
 				goto out_resource;
-			*p++ = cpu_to_be32(ace->type);
-			*p++ = cpu_to_be32(ace->flag);
-			*p++ = cpu_to_be32(ace->access_mask &
-							NFS4_ACE_MASK_ALL);
-			status = nfsd4_encode_aclname(xdr, rqstp, ace);
+			*p++ = cpu_to_be32(ace->e_type);
+			*p++ = cpu_to_be32(ace->e_flags & ~RICHACE_SPECIAL_WHO);
+			*p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
+			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
 			if (status)
 				goto out;
 		}
@@ -2755,7 +2735,7 @@  out:
 	if (context)
 		security_release_secctx(context, contextlen);
 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
-	kfree(acl);
+	richacl_put(acl);
 	if (tempfh) {
 		fh_put(tempfh);
 		kfree(tempfh);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index b698585..c311066 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -118,7 +118,7 @@  struct nfsd4_create {
 	u32		cr_bmval[3];        /* request */
 	struct iattr	cr_iattr;           /* request */
 	struct nfsd4_change_info  cr_cinfo; /* response */
-	struct nfs4_acl *cr_acl;
+	struct richacl *cr_acl;
 	struct xdr_netobj cr_label;
 };
 #define cr_datalen	u.link.datalen
@@ -248,7 +248,7 @@  struct nfsd4_open {
 	struct nfs4_file *op_file;          /* used during processing */
 	struct nfs4_ol_stateid *op_stp;	    /* used during processing */
 	struct nfs4_clnt_odstate *op_odstate; /* used during processing */
-	struct nfs4_acl *op_acl;
+	struct richacl *op_acl;
 	struct xdr_netobj op_label;
 };
 
@@ -332,7 +332,7 @@  struct nfsd4_setattr {
 	stateid_t	sa_stateid;         /* request */
 	u32		sa_bmval[3];        /* request */
 	struct iattr	sa_iattr;           /* request */
-	struct nfs4_acl *sa_acl;
+	struct richacl *sa_acl;
 	struct xdr_netobj sa_label;
 };
 
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index b8e72aa..992ddc4 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -16,29 +16,6 @@ 
 #include <linux/uidgid.h>
 #include <uapi/linux/nfs4.h>
 
-enum nfs4_acl_whotype {
-	NFS4_ACL_WHO_NAMED = 0,
-	NFS4_ACL_WHO_OWNER,
-	NFS4_ACL_WHO_GROUP,
-	NFS4_ACL_WHO_EVERYONE,
-};
-
-struct nfs4_ace {
-	uint32_t	type;
-	uint32_t	flag;
-	uint32_t	access_mask;
-	int		whotype;
-	union {
-		kuid_t	who_uid;
-		kgid_t	who_gid;
-	};
-};
-
-struct nfs4_acl {
-	uint32_t	naces;
-	struct nfs4_ace	aces[0];
-};
-
 #define NFS4_MAXLABELLEN	2048
 
 struct nfs4_label {
diff --git a/include/linux/nfs4acl.h b/include/linux/nfs4acl.h
new file mode 100644
index 0000000..db9f9a6
--- /dev/null
+++ b/include/linux/nfs4acl.h
@@ -0,0 +1,7 @@ 
+#ifndef __LINUX_NFS4ACL_H
+#define __LINUX_NFS4ACL_H
+
+int nfs4acl_who_to_special_id(const char *, u32);
+bool nfs4acl_special_id_to_who(unsigned int, const char **, unsigned int *);
+
+#endif