[RFC,v7,31/41] nfsd: Add support for the v4.1 dacl attribute
diff mbox

Message ID 1441448856-13478-32-git-send-email-agruenba@redhat.com
State New
Headers show

Commit Message

Andreas Gruenbacher Sept. 5, 2015, 10:27 a.m. UTC
Richacls support the Automatic Inheritance permission propagation
mechanism as specified in NFSv4.1.  Over NFS, this requires support for
the dacl attribute: compared to the acl attribute, the dacl attribute
has an additional flags field which indicates when Automatic Inheritance
is in use.

The server will only indicate dacl attribute support in protocol version
4.1 and later, on file systems with richacl support.

This commit also adds support for the NFSv4.1 NFS4_ACE_WRITE_RETENTION
and NFS4_ACE_WRITE_RETENTION_HOLD ACL permissions.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/nfsd/nfs4proc.c        |   2 +
 fs/nfsd/nfs4xdr.c         | 219 ++++++++++++++++++++++++++++++----------------
 fs/nfsd/nfsd.h            |   6 +-
 include/linux/nfs4.h      |   1 +
 include/uapi/linux/nfs4.h |   3 +-
 5 files changed, 155 insertions(+), 76 deletions(-)

Comments

J . Bruce Fields Sept. 24, 2015, 7:59 p.m. UTC | #1
On Sat, Sep 05, 2015 at 12:27:26PM +0200, Andreas Gruenbacher wrote:
> Richacls support the Automatic Inheritance permission propagation
> mechanism as specified in NFSv4.1.  Over NFS, this requires support for
> the dacl attribute: compared to the acl attribute, the dacl attribute
> has an additional flags field which indicates when Automatic Inheritance
> is in use.
> 
> The server will only indicate dacl attribute support in protocol version
> 4.1 and later, on file systems with richacl support.
> 
> This commit also adds support for the NFSv4.1 NFS4_ACE_WRITE_RETENTION
> and NFS4_ACE_WRITE_RETENTION_HOLD ACL permissions.

OK, not useful at this point, but I guess it makes sense to go ahead and
allow it.

And looks like when accessed over an insufficient protocol version,
you're zeroing out the extra bits on read and erroring out (with INVAL)
on write.  Looks sensible.

> 
> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
>  fs/nfsd/nfs4proc.c        |   2 +
>  fs/nfsd/nfs4xdr.c         | 219 ++++++++++++++++++++++++++++++----------------
>  fs/nfsd/nfsd.h            |   6 +-
>  include/linux/nfs4.h      |   1 +
>  include/uapi/linux/nfs4.h |   3 +-
>  5 files changed, 155 insertions(+), 76 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 9f8f0a8..ef9e6cd 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -1804,6 +1804,8 @@ static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
>  		ret += NFS4_FHSIZE + 4;
>  		bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
>  	}
> +	if (bmap1 & FATTR4_WORD1_DACL)
> +		return svc_max_payload(rqstp);

I'd probably move this up top with the other ACL case.

But that's a nit, looks fine.  ACK to the patch.

--b.

>  	if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
>  		ret += NFS4_MAXLABELLEN + 12;
>  		bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 3280c30..b66063d 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -301,6 +301,68 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
>  	DECODE_TAIL;
>  }
>  
> +static unsigned int
> +nfsd4_ace_mask(int minorversion)
> +{
> +	return minorversion == 0 ?  NFS40_ACE_MASK_ALL : NFS4_ACE_MASK_ALL;
> +}
> +
> +static __be32
> +nfsd4_decode_acl_entries(struct nfsd4_compoundargs *argp, struct richacl **acl,
> +			 unsigned short flags_mask, unsigned int ace_mask,
> +			 int *plen)
> +{
> +	struct richace *ace;
> +	u32 dummy32;
> +	char *buf;
> +	int len = 0;
> +
> +	DECODE_HEAD;
> +
> +	flags_mask &= RICHACE_VALID_FLAGS & ~RICHACE_SPECIAL_WHO;
> +
> +	READ_BUF(4); len += 4;
> +	dummy32 = be32_to_cpup(p++);
> +
> +	if (dummy32 > NFSD4_ACL_MAX)
> +		return nfserr_fbig;
> +
> +	*acl = svcxdr_alloc_richacl(argp, dummy32);
> +	if (*acl == NULL)
> +		return nfserr_jukebox;
> +
> +	richacl_for_each_entry(ace, *acl) {
> +		READ_BUF(16); len += 16;
> +
> +		dummy32 = be32_to_cpup(p++);
> +		if (dummy32 > RICHACE_ACCESS_DENIED_ACE_TYPE)
> +			return nfserr_inval;
> +		ace->e_type = dummy32;
> +
> +		dummy32 = be32_to_cpup(p++);
> +		if (dummy32 & ~flags_mask)
> +			return nfserr_inval;
> +		ace->e_flags = dummy32;
> +
> +		dummy32 = be32_to_cpup(p++);
> +		if (dummy32 & ~ace_mask)
> +			return nfserr_inval;
> +		ace->e_mask = dummy32;
> +
> +		dummy32 = be32_to_cpup(p++);
> +		READ_BUF(dummy32);
> +		len += XDR_QUADLEN(dummy32) << 2;
> +		READMEM(buf, dummy32);
> +		status = nfsd4_decode_ace_who(ace, argp->rqstp,
> +					      buf, dummy32);
> +		if (status)
> +			return status;
> +	}
> +	*plen += len;
> +
> +	DECODE_TAIL;
> +}
> +
>  static __be32
>  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
>  		   struct iattr *iattr, struct richacl **acl,
> @@ -312,6 +374,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
>  
>  	DECODE_HEAD;
>  	iattr->ia_valid = 0;
> +	*acl = NULL;
>  	if ((status = nfsd4_decode_bitmap(argp, bmval)))
>  		return status;
>  
> @@ -325,50 +388,18 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
>  		iattr->ia_valid |= ATTR_SIZE;
>  	}
>  	if (bmval[0] & FATTR4_WORD0_ACL) {
> -		u32 nace;
> -		struct richace *ace;
> -
> -		READ_BUF(4); len += 4;
> -		nace = be32_to_cpup(p++);
> -
> -		if (nace > NFSD4_ACL_MAX)
> -			return nfserr_fbig;
> +		if (bmval[1] & FATTR4_WORD1_DACL)
> +			return nfserr_inval;
>  
> -		*acl = svcxdr_alloc_richacl(argp, nace);
> -		if (*acl == NULL)
> +		status = nfsd4_decode_acl_entries(argp, acl,
> +				~NFS4_ACE_INHERITED_ACE,
> +				nfsd4_ace_mask(argp->minorversion),
> +				&len);
> +		if (status)
> +			return status;
> +		else if (*acl == NULL)
>  			return nfserr_jukebox;
> -
> -		richacl_for_each_entry(ace, *acl) {
> -			READ_BUF(16); len += 16;
> -
> -			dummy32 = be32_to_cpup(p++);
> -			if (dummy32 > RICHACE_ACCESS_DENIED_ACE_TYPE)
> -				return nfserr_inval;
> -			ace->e_type = dummy32;
> -
> -			dummy32 = be32_to_cpup(p++);
> -			if (dummy32 & (~RICHACE_VALID_FLAGS |
> -				       RICHACE_INHERITED_ACE |
> -				       RICHACE_SPECIAL_WHO))
> -				return nfserr_inval;
> -			ace->e_flags = dummy32;
> -
> -			dummy32 = be32_to_cpup(p++);
> -			if (dummy32 & ~NFS4_ACE_MASK_ALL)
> -				return nfserr_inval;
> -			ace->e_mask = dummy32;
> -
> -			dummy32 = be32_to_cpup(p++);
> -			READ_BUF(dummy32);
> -			len += XDR_QUADLEN(dummy32) << 2;
> -			READMEM(buf, dummy32);
> -			status = nfsd4_decode_ace_who(ace, argp->rqstp,
> -						      buf, dummy32);
> -			if (status)
> -				return status;
> -		}
> -	} else
> -		*acl = NULL;
> +	}
>  	if (bmval[1] & FATTR4_WORD1_MODE) {
>  		READ_BUF(4);
>  		len += 4;
> @@ -436,6 +467,22 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
>  			goto xdr_error;
>  		}
>  	}
> +	if (bmval[1] & FATTR4_WORD1_DACL) {
> +		READ_BUF(4);
> +		len += 4;
> +		dummy32 = be32_to_cpup(p++);
> +		if (dummy32 & (~RICHACL_VALID_FLAGS | RICHACL_MASKED))
> +			return nfserr_inval;
> +		status = nfsd4_decode_acl_entries(argp, acl,
> +				~0,
> +				nfsd4_ace_mask(argp->minorversion),
> +				&len);
> +		if (status)
> +			return status;
> +		else if (*acl == NULL)
> +			return nfserr_jukebox;
> +		(*acl)->a_flags = dummy32;
> +	}
>  
>  	label->len = 0;
>  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
> @@ -2218,6 +2265,42 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
>  	return err;
>  }
>  
> +static __be32 nfsd4_encode_acl_entries(struct xdr_stream *xdr,
> +		struct richacl *acl, struct svc_rqst *rqstp,
> +		unsigned short flags_mask, unsigned int ace_mask)
> +{
> +	__be32 *p;
> +
> +	flags_mask &= ~RICHACE_SPECIAL_WHO;
> +
> +	p = xdr_reserve_space(xdr, 4);
> +	if (!p)
> +		return nfserr_resource;
> +
> +	if (acl == NULL) {
> +		*p++ = cpu_to_be32(0);
> +	} else {
> +		struct richace *ace;
> +
> +		*p++ = cpu_to_be32(acl->a_count);
> +
> +		richacl_for_each_entry(ace, acl) {
> +			__be32 status;
> +
> +			p = xdr_reserve_space(xdr, 4*3);
> +			if (!p)
> +				return nfserr_resource;
> +			*p++ = cpu_to_be32(ace->e_type);
> +			*p++ = cpu_to_be32(ace->e_flags & flags_mask);
> +			*p++ = cpu_to_be32(ace->e_mask & ace_mask);
> +			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
> +			if (status)
> +				return status;
> +		}
> +	}
> +	return 0;
> +}
> +
>  /*
>   * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
>   * ourselves.
> @@ -2288,15 +2371,16 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  			goto out;
>  		fhp = tempfh;
>  	}
> -	if (bmval0 & FATTR4_WORD0_ACL) {
> +	if ((bmval0 & FATTR4_WORD0_ACL) || (bmval1 & FATTR4_WORD1_DACL)) {
>  		acl = nfsd4_get_acl(rqstp, dentry);
>  		if (IS_ERR(acl)) {
>  			err = PTR_ERR(acl);
>  			acl = NULL;
>  		}
> -		if (err == -EOPNOTSUPP)
> +		if (err == -EOPNOTSUPP) {
>  			bmval0 &= ~FATTR4_WORD0_ACL;
> -		else if (err == -EINVAL) {
> +			bmval1 &= ~FATTR4_WORD1_DACL;
> +		} else if (err == -EINVAL) {
>  			status = nfserr_attrnotsupp;
>  			goto out;
>  		} else if (err != 0)
> @@ -2354,6 +2438,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  
>  		if (!IS_ACL(d_inode(dentry)))
>  			word0 &= ~FATTR4_WORD0_ACL;
> +		if (!IS_RICHACL(d_inode(dentry)))
> +			word1 &= ~FATTR4_WORD1_DACL;
>  		if (!contextsupport)
>  			word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
>  		if (!word2) {
> @@ -2467,35 +2553,12 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>  		*p++ = cpu_to_be32(rdattr_err);
>  	}
>  	if (bmval0 & FATTR4_WORD0_ACL) {
> -		struct richace *ace;
> -
> -		if (acl == NULL) {
> -			p = xdr_reserve_space(xdr, 4);
> -			if (!p)
> -				goto out_resource;
> -
> -			*p++ = cpu_to_be32(0);
> -			goto out_acl;
> -		}
> -		p = xdr_reserve_space(xdr, 4);
> -		if (!p)
> -			goto out_resource;
> -		*p++ = cpu_to_be32(acl->a_count);
> -
> -		richacl_for_each_entry(ace, acl) {
> -			p = xdr_reserve_space(xdr, 4*3);
> -			if (!p)
> -				goto out_resource;
> -			*p++ = cpu_to_be32(ace->e_type);
> -			*p++ = cpu_to_be32(ace->e_flags &
> -				~(RICHACE_SPECIAL_WHO | RICHACE_INHERITED_ACE));
> -			*p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
> -			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
> -			if (status)
> -				goto out;
> -		}
> +		status = nfsd4_encode_acl_entries(xdr, acl, rqstp,
> +				~NFS4_ACE_INHERITED_ACE,
> +				nfsd4_ace_mask(minorversion));
> +		if (status)
> +			goto out;
>  	}
> -out_acl:
>  	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
>  		p = xdr_reserve_space(xdr, 4);
>  		if (!p)
> @@ -2704,6 +2767,16 @@ out_acl:
>  			get_parent_attributes(exp, &stat);
>  		p = xdr_encode_hyper(p, stat.ino);
>  	}
> +	if (bmval1 & FATTR4_WORD1_DACL) {
> +		p = xdr_reserve_space(xdr, 4);
> +		if (!p)
> +			goto out_resource;
> +		*p++ = cpu_to_be32(acl->a_flags);
> +		status = nfsd4_encode_acl_entries(xdr, acl, rqstp,
> +				~0, nfsd4_ace_mask(minorversion));
> +		if (status)
> +			goto out;
> +	}
>  #ifdef CONFIG_NFSD_PNFS
>  	if ((bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) ||
>  	    (bmval2 & FATTR4_WORD2_LAYOUT_TYPES)) {
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index cf98052..cb5c3ed 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -339,7 +339,8 @@ void		nfsd_lockd_shutdown(void);
>  	NFSD4_SUPPORTED_ATTRS_WORD0
>  
>  #define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
> -	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1)
> +	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1 | \
> +	 FATTR4_WORD1_DACL)
>  
>  #define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
>  	(NFSD4_SUPPORTED_ATTRS_WORD2	| PNFSD_SUPPORTED_ATTRS_WORD2 | \
> @@ -386,7 +387,8 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
>  	(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
>  #define NFSD_WRITEABLE_ATTRS_WORD1 \
>  	(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
> -	| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
> +	| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET \
> +	| FATTR4_WORD1_DACL)
>  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
>  #define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
>  #else
> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index 992ddc4..1bd4aea 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -394,6 +394,7 @@ enum lock_type4 {
>  #define FATTR4_WORD1_TIME_MODIFY        (1UL << 21)
>  #define FATTR4_WORD1_TIME_MODIFY_SET    (1UL << 22)
>  #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
> +#define FATTR4_WORD1_DACL               (1UL << 26)
>  #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
>  #define FATTR4_WORD2_LAYOUT_TYPES       (1UL << 0)
>  #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
> diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
> index 2119c7c..64e4c6c 100644
> --- a/include/uapi/linux/nfs4.h
> +++ b/include/uapi/linux/nfs4.h
> @@ -121,7 +121,8 @@
>  #define NFS4_ACE_GENERIC_READ                 0x00120081
>  #define NFS4_ACE_GENERIC_WRITE                0x00160106
>  #define NFS4_ACE_GENERIC_EXECUTE              0x001200A0
> -#define NFS4_ACE_MASK_ALL                     0x001F01FF
> +#define NFS40_ACE_MASK_ALL                    0x001F01FF
> +#define NFS4_ACE_MASK_ALL                     0x001F07FF
>  
>  #define EXCHGID4_FLAG_SUPP_MOVED_REFER		0x00000001
>  #define EXCHGID4_FLAG_SUPP_MOVED_MIGR		0x00000002
> -- 
> 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-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andreas Gruenbacher Sept. 25, 2015, 4:37 p.m. UTC | #2
2015-09-24 21:59 GMT+02:00 J. Bruce Fields <bfields@fieldses.org>:
> I'd probably move this up top with the other ACL case.

Okay.

Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 9f8f0a8..ef9e6cd 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1804,6 +1804,8 @@  static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
 		ret += NFS4_FHSIZE + 4;
 		bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
 	}
+	if (bmap1 & FATTR4_WORD1_DACL)
+		return svc_max_payload(rqstp);
 	if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
 		ret += NFS4_MAXLABELLEN + 12;
 		bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 3280c30..b66063d 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -301,6 +301,68 @@  nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
 	DECODE_TAIL;
 }
 
+static unsigned int
+nfsd4_ace_mask(int minorversion)
+{
+	return minorversion == 0 ?  NFS40_ACE_MASK_ALL : NFS4_ACE_MASK_ALL;
+}
+
+static __be32
+nfsd4_decode_acl_entries(struct nfsd4_compoundargs *argp, struct richacl **acl,
+			 unsigned short flags_mask, unsigned int ace_mask,
+			 int *plen)
+{
+	struct richace *ace;
+	u32 dummy32;
+	char *buf;
+	int len = 0;
+
+	DECODE_HEAD;
+
+	flags_mask &= RICHACE_VALID_FLAGS & ~RICHACE_SPECIAL_WHO;
+
+	READ_BUF(4); len += 4;
+	dummy32 = be32_to_cpup(p++);
+
+	if (dummy32 > NFSD4_ACL_MAX)
+		return nfserr_fbig;
+
+	*acl = svcxdr_alloc_richacl(argp, dummy32);
+	if (*acl == NULL)
+		return nfserr_jukebox;
+
+	richacl_for_each_entry(ace, *acl) {
+		READ_BUF(16); len += 16;
+
+		dummy32 = be32_to_cpup(p++);
+		if (dummy32 > RICHACE_ACCESS_DENIED_ACE_TYPE)
+			return nfserr_inval;
+		ace->e_type = dummy32;
+
+		dummy32 = be32_to_cpup(p++);
+		if (dummy32 & ~flags_mask)
+			return nfserr_inval;
+		ace->e_flags = dummy32;
+
+		dummy32 = be32_to_cpup(p++);
+		if (dummy32 & ~ace_mask)
+			return nfserr_inval;
+		ace->e_mask = dummy32;
+
+		dummy32 = be32_to_cpup(p++);
+		READ_BUF(dummy32);
+		len += XDR_QUADLEN(dummy32) << 2;
+		READMEM(buf, dummy32);
+		status = nfsd4_decode_ace_who(ace, argp->rqstp,
+					      buf, dummy32);
+		if (status)
+			return status;
+	}
+	*plen += len;
+
+	DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 		   struct iattr *iattr, struct richacl **acl,
@@ -312,6 +374,7 @@  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 
 	DECODE_HEAD;
 	iattr->ia_valid = 0;
+	*acl = NULL;
 	if ((status = nfsd4_decode_bitmap(argp, bmval)))
 		return status;
 
@@ -325,50 +388,18 @@  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 		iattr->ia_valid |= ATTR_SIZE;
 	}
 	if (bmval[0] & FATTR4_WORD0_ACL) {
-		u32 nace;
-		struct richace *ace;
-
-		READ_BUF(4); len += 4;
-		nace = be32_to_cpup(p++);
-
-		if (nace > NFSD4_ACL_MAX)
-			return nfserr_fbig;
+		if (bmval[1] & FATTR4_WORD1_DACL)
+			return nfserr_inval;
 
-		*acl = svcxdr_alloc_richacl(argp, nace);
-		if (*acl == NULL)
+		status = nfsd4_decode_acl_entries(argp, acl,
+				~NFS4_ACE_INHERITED_ACE,
+				nfsd4_ace_mask(argp->minorversion),
+				&len);
+		if (status)
+			return status;
+		else if (*acl == NULL)
 			return nfserr_jukebox;
-
-		richacl_for_each_entry(ace, *acl) {
-			READ_BUF(16); len += 16;
-
-			dummy32 = be32_to_cpup(p++);
-			if (dummy32 > RICHACE_ACCESS_DENIED_ACE_TYPE)
-				return nfserr_inval;
-			ace->e_type = dummy32;
-
-			dummy32 = be32_to_cpup(p++);
-			if (dummy32 & (~RICHACE_VALID_FLAGS |
-				       RICHACE_INHERITED_ACE |
-				       RICHACE_SPECIAL_WHO))
-				return nfserr_inval;
-			ace->e_flags = dummy32;
-
-			dummy32 = be32_to_cpup(p++);
-			if (dummy32 & ~NFS4_ACE_MASK_ALL)
-				return nfserr_inval;
-			ace->e_mask = dummy32;
-
-			dummy32 = be32_to_cpup(p++);
-			READ_BUF(dummy32);
-			len += XDR_QUADLEN(dummy32) << 2;
-			READMEM(buf, dummy32);
-			status = nfsd4_decode_ace_who(ace, argp->rqstp,
-						      buf, dummy32);
-			if (status)
-				return status;
-		}
-	} else
-		*acl = NULL;
+	}
 	if (bmval[1] & FATTR4_WORD1_MODE) {
 		READ_BUF(4);
 		len += 4;
@@ -436,6 +467,22 @@  nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 			goto xdr_error;
 		}
 	}
+	if (bmval[1] & FATTR4_WORD1_DACL) {
+		READ_BUF(4);
+		len += 4;
+		dummy32 = be32_to_cpup(p++);
+		if (dummy32 & (~RICHACL_VALID_FLAGS | RICHACL_MASKED))
+			return nfserr_inval;
+		status = nfsd4_decode_acl_entries(argp, acl,
+				~0,
+				nfsd4_ace_mask(argp->minorversion),
+				&len);
+		if (status)
+			return status;
+		else if (*acl == NULL)
+			return nfserr_jukebox;
+		(*acl)->a_flags = dummy32;
+	}
 
 	label->len = 0;
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
@@ -2218,6 +2265,42 @@  static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
 	return err;
 }
 
+static __be32 nfsd4_encode_acl_entries(struct xdr_stream *xdr,
+		struct richacl *acl, struct svc_rqst *rqstp,
+		unsigned short flags_mask, unsigned int ace_mask)
+{
+	__be32 *p;
+
+	flags_mask &= ~RICHACE_SPECIAL_WHO;
+
+	p = xdr_reserve_space(xdr, 4);
+	if (!p)
+		return nfserr_resource;
+
+	if (acl == NULL) {
+		*p++ = cpu_to_be32(0);
+	} else {
+		struct richace *ace;
+
+		*p++ = cpu_to_be32(acl->a_count);
+
+		richacl_for_each_entry(ace, acl) {
+			__be32 status;
+
+			p = xdr_reserve_space(xdr, 4*3);
+			if (!p)
+				return nfserr_resource;
+			*p++ = cpu_to_be32(ace->e_type);
+			*p++ = cpu_to_be32(ace->e_flags & flags_mask);
+			*p++ = cpu_to_be32(ace->e_mask & ace_mask);
+			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
+			if (status)
+				return status;
+		}
+	}
+	return 0;
+}
+
 /*
  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
  * ourselves.
@@ -2288,15 +2371,16 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 			goto out;
 		fhp = tempfh;
 	}
-	if (bmval0 & FATTR4_WORD0_ACL) {
+	if ((bmval0 & FATTR4_WORD0_ACL) || (bmval1 & FATTR4_WORD1_DACL)) {
 		acl = nfsd4_get_acl(rqstp, dentry);
 		if (IS_ERR(acl)) {
 			err = PTR_ERR(acl);
 			acl = NULL;
 		}
-		if (err == -EOPNOTSUPP)
+		if (err == -EOPNOTSUPP) {
 			bmval0 &= ~FATTR4_WORD0_ACL;
-		else if (err == -EINVAL) {
+			bmval1 &= ~FATTR4_WORD1_DACL;
+		} else if (err == -EINVAL) {
 			status = nfserr_attrnotsupp;
 			goto out;
 		} else if (err != 0)
@@ -2354,6 +2438,8 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 
 		if (!IS_ACL(d_inode(dentry)))
 			word0 &= ~FATTR4_WORD0_ACL;
+		if (!IS_RICHACL(d_inode(dentry)))
+			word1 &= ~FATTR4_WORD1_DACL;
 		if (!contextsupport)
 			word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
 		if (!word2) {
@@ -2467,35 +2553,12 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		*p++ = cpu_to_be32(rdattr_err);
 	}
 	if (bmval0 & FATTR4_WORD0_ACL) {
-		struct richace *ace;
-
-		if (acl == NULL) {
-			p = xdr_reserve_space(xdr, 4);
-			if (!p)
-				goto out_resource;
-
-			*p++ = cpu_to_be32(0);
-			goto out_acl;
-		}
-		p = xdr_reserve_space(xdr, 4);
-		if (!p)
-			goto out_resource;
-		*p++ = cpu_to_be32(acl->a_count);
-
-		richacl_for_each_entry(ace, acl) {
-			p = xdr_reserve_space(xdr, 4*3);
-			if (!p)
-				goto out_resource;
-			*p++ = cpu_to_be32(ace->e_type);
-			*p++ = cpu_to_be32(ace->e_flags &
-				~(RICHACE_SPECIAL_WHO | RICHACE_INHERITED_ACE));
-			*p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
-			status = nfsd4_encode_ace_who(xdr, rqstp, ace);
-			if (status)
-				goto out;
-		}
+		status = nfsd4_encode_acl_entries(xdr, acl, rqstp,
+				~NFS4_ACE_INHERITED_ACE,
+				nfsd4_ace_mask(minorversion));
+		if (status)
+			goto out;
 	}
-out_acl:
 	if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
 		p = xdr_reserve_space(xdr, 4);
 		if (!p)
@@ -2704,6 +2767,16 @@  out_acl:
 			get_parent_attributes(exp, &stat);
 		p = xdr_encode_hyper(p, stat.ino);
 	}
+	if (bmval1 & FATTR4_WORD1_DACL) {
+		p = xdr_reserve_space(xdr, 4);
+		if (!p)
+			goto out_resource;
+		*p++ = cpu_to_be32(acl->a_flags);
+		status = nfsd4_encode_acl_entries(xdr, acl, rqstp,
+				~0, nfsd4_ace_mask(minorversion));
+		if (status)
+			goto out;
+	}
 #ifdef CONFIG_NFSD_PNFS
 	if ((bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) ||
 	    (bmval2 & FATTR4_WORD2_LAYOUT_TYPES)) {
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index cf98052..cb5c3ed 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -339,7 +339,8 @@  void		nfsd_lockd_shutdown(void);
 	NFSD4_SUPPORTED_ATTRS_WORD0
 
 #define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
-	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1)
+	(NFSD4_SUPPORTED_ATTRS_WORD1	| PNFSD_SUPPORTED_ATTRS_WORD1 | \
+	 FATTR4_WORD1_DACL)
 
 #define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
 	(NFSD4_SUPPORTED_ATTRS_WORD2	| PNFSD_SUPPORTED_ATTRS_WORD2 | \
@@ -386,7 +387,8 @@  static inline u32 nfsd_suppattrs2(u32 minorversion)
 	(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
 #define NFSD_WRITEABLE_ATTRS_WORD1 \
 	(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
-	| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
+	| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET \
+	| FATTR4_WORD1_DACL)
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 #define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
 #else
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 992ddc4..1bd4aea 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -394,6 +394,7 @@  enum lock_type4 {
 #define FATTR4_WORD1_TIME_MODIFY        (1UL << 21)
 #define FATTR4_WORD1_TIME_MODIFY_SET    (1UL << 22)
 #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
+#define FATTR4_WORD1_DACL               (1UL << 26)
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
 #define FATTR4_WORD2_LAYOUT_TYPES       (1UL << 0)
 #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index 2119c7c..64e4c6c 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -121,7 +121,8 @@ 
 #define NFS4_ACE_GENERIC_READ                 0x00120081
 #define NFS4_ACE_GENERIC_WRITE                0x00160106
 #define NFS4_ACE_GENERIC_EXECUTE              0x001200A0
-#define NFS4_ACE_MASK_ALL                     0x001F01FF
+#define NFS40_ACE_MASK_ALL                    0x001F01FF
+#define NFS4_ACE_MASK_ALL                     0x001F07FF
 
 #define EXCHGID4_FLAG_SUPP_MOVED_REFER		0x00000001
 #define EXCHGID4_FLAG_SUPP_MOVED_MIGR		0x00000002