diff mbox series

[06/13] NFSD: add posix ACLs to struct nfsd_attrs

Message ID 165881793057.21666.7472233362797480106.stgit@noble.brown (mailing list archive)
State New, archived
Headers show
Series NFSD: clean up locking | expand

Commit Message

NeilBrown July 26, 2022, 6:45 a.m. UTC
pacl and dpacl pointers are added to struct nfsd_attrs, which requires
that we have an nfsd_attrs_free() function to free them.
Those nfsv4 functions that can set ACLs now set up these pointers
based on the passed in NFSv4 ACL.

nfsd_setattr() sets the acls as appropriate.

Errors are handled as with security labels.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/nfsd/acl.h      |    6 ++++--
 fs/nfsd/nfs4acl.c  |   46 +++++++---------------------------------------
 fs/nfsd/nfs4proc.c |   46 ++++++++++++++++------------------------------
 fs/nfsd/vfs.c      |    8 ++++++++
 fs/nfsd/vfs.h      |   10 ++++++++++
 5 files changed, 45 insertions(+), 71 deletions(-)

Comments

Chuck Lever July 29, 2022, 2:24 p.m. UTC | #1
> On Jul 26, 2022, at 2:45 AM, NeilBrown <neilb@suse.de> wrote:
> 
> pacl and dpacl pointers are added to struct nfsd_attrs, which requires
> that we have an nfsd_attrs_free() function to free them.
> Those nfsv4 functions that can set ACLs now set up these pointers
> based on the passed in NFSv4 ACL.
> 
> nfsd_setattr() sets the acls as appropriate.
> 
> Errors are handled as with security labels.
> 
> Signed-off-by: NeilBrown <neilb@suse.de>
> ---
> fs/nfsd/acl.h      |    6 ++++--
> fs/nfsd/nfs4acl.c  |   46 +++++++---------------------------------------
> fs/nfsd/nfs4proc.c |   46 ++++++++++++++++------------------------------
> fs/nfsd/vfs.c      |    8 ++++++++
> fs/nfsd/vfs.h      |   10 ++++++++++
> 5 files changed, 45 insertions(+), 71 deletions(-)
> 
> diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
> index ba14d2f4b64f..4b7324458a94 100644
> --- a/fs/nfsd/acl.h
> +++ b/fs/nfsd/acl.h
> @@ -38,6 +38,8 @@
> struct nfs4_acl;
> struct svc_fh;
> struct svc_rqst;
> +struct nfsd_attrs;
> +enum nfs_ftype4;
> 
> int nfs4_acl_bytes(int entries);
> int nfs4_acl_get_whotype(char *, u32);
> @@ -45,7 +47,7 @@ __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
> 
> 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);
> +__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
> +			 struct nfsd_attrs *attr);
> 
> #endif /* LINUX_NFS4_ACL_H */
> diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
> index eaa3a0cf38f1..8a5b847b6c73 100644
> --- a/fs/nfsd/nfs4acl.c
> +++ b/fs/nfsd/nfs4acl.c
> @@ -751,58 +751,26 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
> 	return ret;
> }
> 
> -__be32
> -nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> -		struct nfs4_acl *acl)
> +__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
> +			 struct nfsd_attrs *attr)
> {
> -	__be32 error;
> 	int host_error;
> -	struct dentry *dentry;
> -	struct inode *inode;
> -	struct posix_acl *pacl = NULL, *dpacl = NULL;
> 	unsigned int flags = 0;
> 
> -	/* Get inode */
> -	error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
> -	if (error)
> -		return error;
> -
> -	dentry = fhp->fh_dentry;
> -	inode = d_inode(dentry);
> +	if (!acl)
> +		return nfs_ok;
> 
> -	if (S_ISDIR(inode->i_mode))
> +	if (type == NF4DIR)
> 		flags = NFS4_ACL_DIR;
> 
> -	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
> +	host_error = nfs4_acl_nfsv4_to_posix(acl, &attr->pacl, &attr->dpacl,
> +					     flags);
> 	if (host_error == -EINVAL)
> 		return nfserr_attrnotsupp;
> -	if (host_error < 0)
> -		goto out_nfserr;
> -
> -	fh_lock(fhp);
> -
> -	host_error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, pacl);
> -	if (host_error < 0)
> -		goto out_drop_lock;
> -
> -	if (S_ISDIR(inode->i_mode)) {
> -		host_error = set_posix_acl(&init_user_ns, inode,
> -					   ACL_TYPE_DEFAULT, dpacl);
> -	}
> -
> -out_drop_lock:
> -	fh_unlock(fhp);
> -
> -	posix_acl_release(pacl);
> -	posix_acl_release(dpacl);
> -out_nfserr:
> -	if (host_error == -EOPNOTSUPP)
> -		return nfserr_attrnotsupp;
> 	else
> 		return nfserrno(host_error);
> }
> 
> -
> static short
> ace2type(struct nfs4_ace *ace)
> {
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 83d2b645b0d6..c49cd04cb567 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -128,26 +128,6 @@ is_create_with_attrs(struct nfsd4_open *open)
> 		    || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1);
> }
> 
> -/*
> - * if error occurs when setting the acl, just clear the acl bit
> - * 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)
> -{
> -	__be32 status;
> -
> -	status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
> -	if (status)
> -		/*
> -		 * We should probably fail the whole open at this point,
> -		 * but we've already created the file, so it's too late;
> -		 * So this seems the least of evils:
> -		 */
> -		bmval[0] &= ~FATTR4_WORD0_ACL;
> -}
> -
> static inline void
> fh_dup2(struct svc_fh *dst, struct svc_fh *src)
> {
> @@ -281,6 +261,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> 	if (host_err)
> 		return nfserrno(host_err);
> 
> +	if (is_create_with_attrs(open))
> +		nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
> +
> 	fh_lock_nested(fhp, I_MUTEX_PARENT);
> 
> 	child = lookup_one_len(open->op_fname, parent, open->op_fnamelen);
> @@ -382,8 +365,11 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> 
> 	if (attrs.label_failed)
> 		open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
> +	if (attrs.acl_failed)
> +		open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
> out:
> 	fh_unlock(fhp);
> +	nfsd_attrs_free(&attrs);
> 	if (child && !IS_ERR(child))
> 		dput(child);
> 	fh_drop_write(fhp);
> @@ -446,9 +432,6 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
> 	if (status)
> 		goto out;
> 
> -	if (is_create_with_attrs(open) && open->op_acl != NULL)
> -		do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
> -
> 	nfsd4_set_open_owner_reply_cache(cstate, open, *resfh);
> 	accmode = NFSD_MAY_NOP;
> 	if (open->op_created ||
> @@ -779,6 +762,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 	if (status)
> 		return status;
> 
> +	status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs);
> 	current->fs->umask = create->cr_umask;
> 	switch (create->cr_type) {
> 	case NF4LNK:
> @@ -837,10 +821,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 
> 	if (attrs.label_failed)
> 		create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
> -
> -	if (create->cr_acl != NULL)
> -		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
> -				create->cr_bmval);
> +	if (attrs.label_failed)
> +		create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;

I think this needs to be "if (attrs.acl_failed)". I've fixed this
in my tree.


> 	fh_unlock(&cstate->current_fh);
> 	set_change_info(&create->cr_cinfo, &cstate->current_fh);
> @@ -849,6 +831,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 	fh_put(&resfh);
> out_umask:
> 	current->fs->umask = 0;
> +	nfsd_attrs_free(&attrs);
> 	return status;
> }
> 
> @@ -1123,6 +1106,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 		.iattr = &setattr->sa_iattr,
> 		.label = &setattr->sa_label,
> 	};
> +	struct inode *inode;
> 	__be32 status = nfs_ok;
> 	int err;
> 
> @@ -1145,9 +1129,10 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 	if (status)
> 		goto out;
> 
> -	if (setattr->sa_acl != NULL)
> -		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
> -					    setattr->sa_acl);
> +	inode = cstate->current_fh.fh_dentry->d_inode;
> +	status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG,
> +				   setattr->sa_acl, &attrs);
> +
> 	if (status)
> 		goto out;
> 	status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs,
> @@ -1155,6 +1140,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> 	if (!status)
> 		status = attrs.label_failed;
> out:
> +	nfsd_attrs_free(&attrs);
> 	fh_drop_write(&cstate->current_fh);
> 	return status;
> }
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index e7a18bedf499..4bb05586a258 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -461,6 +461,14 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
> 	if (attr->label && attr->label->len)
> 		attr->label_failed = security_inode_setsecctx(
> 			dentry, attr->label->data, attr->label->len);
> +	if (attr->pacl)
> +		attr->acl_failed = set_posix_acl(&init_user_ns,
> +						 inode, ACL_TYPE_ACCESS,
> +						 attr->pacl);
> +	if (!attr->acl_failed && attr->dpacl && S_ISDIR(inode->i_mode))
> +		attr->acl_failed = set_posix_acl(&init_user_ns,
> +						 inode, ACL_TYPE_DEFAULT,
> +						 attr->dpacl);
> 	fh_unlock(fhp);
> 	if (size_change)
> 		put_write_access(inode);
> diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
> index 8464e04af1ea..9343aac0bd15 100644
> --- a/fs/nfsd/vfs.h
> +++ b/fs/nfsd/vfs.h
> @@ -6,6 +6,8 @@
> #ifndef LINUX_NFSD_VFS_H
> #define LINUX_NFSD_VFS_H
> 
> +#include <linux/fs.h>
> +#include <linux/posix_acl.h>
> #include "nfsfh.h"
> #include "nfsd.h"
> 
> @@ -45,9 +47,17 @@ typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
> struct nfsd_attrs {
> 	struct iattr *iattr;
> 	struct xdr_netobj *label;
> +	struct posix_acl *pacl, *dpacl;
> 	int label_failed;
> +	int acl_failed;
> };
> 
> +static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
> +{
> +	posix_acl_release(attrs->pacl);
> +	posix_acl_release(attrs->dpacl);
> +}
> +
> int		nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
> 		                struct svc_export **expp);
> __be32		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
> 
> 

--
Chuck Lever
NeilBrown Aug. 1, 2022, 12:13 a.m. UTC | #2
On Sat, 30 Jul 2022, Chuck Lever III wrote:
> > @@ -837,10 +821,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> > 
> > 	if (attrs.label_failed)
> > 		create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
> > -
> > -	if (create->cr_acl != NULL)
> > -		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
> > -				create->cr_bmval);
> > +	if (attrs.label_failed)
> > +		create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;
> 
> I think this needs to be "if (attrs.acl_failed)". I've fixed this
> in my tree.

Yes, of course :-(.
Thanks,
NeilBrown
diff mbox series

Patch

diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index ba14d2f4b64f..4b7324458a94 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -38,6 +38,8 @@ 
 struct nfs4_acl;
 struct svc_fh;
 struct svc_rqst;
+struct nfsd_attrs;
+enum nfs_ftype4;
 
 int nfs4_acl_bytes(int entries);
 int nfs4_acl_get_whotype(char *, u32);
@@ -45,7 +47,7 @@  __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
 
 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);
+__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
+			 struct nfsd_attrs *attr);
 
 #endif /* LINUX_NFS4_ACL_H */
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index eaa3a0cf38f1..8a5b847b6c73 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -751,58 +751,26 @@  static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
 	return ret;
 }
 
-__be32
-nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
-		struct nfs4_acl *acl)
+__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
+			 struct nfsd_attrs *attr)
 {
-	__be32 error;
 	int host_error;
-	struct dentry *dentry;
-	struct inode *inode;
-	struct posix_acl *pacl = NULL, *dpacl = NULL;
 	unsigned int flags = 0;
 
-	/* Get inode */
-	error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
-	if (error)
-		return error;
-
-	dentry = fhp->fh_dentry;
-	inode = d_inode(dentry);
+	if (!acl)
+		return nfs_ok;
 
-	if (S_ISDIR(inode->i_mode))
+	if (type == NF4DIR)
 		flags = NFS4_ACL_DIR;
 
-	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
+	host_error = nfs4_acl_nfsv4_to_posix(acl, &attr->pacl, &attr->dpacl,
+					     flags);
 	if (host_error == -EINVAL)
 		return nfserr_attrnotsupp;
-	if (host_error < 0)
-		goto out_nfserr;
-
-	fh_lock(fhp);
-
-	host_error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS, pacl);
-	if (host_error < 0)
-		goto out_drop_lock;
-
-	if (S_ISDIR(inode->i_mode)) {
-		host_error = set_posix_acl(&init_user_ns, inode,
-					   ACL_TYPE_DEFAULT, dpacl);
-	}
-
-out_drop_lock:
-	fh_unlock(fhp);
-
-	posix_acl_release(pacl);
-	posix_acl_release(dpacl);
-out_nfserr:
-	if (host_error == -EOPNOTSUPP)
-		return nfserr_attrnotsupp;
 	else
 		return nfserrno(host_error);
 }
 
-
 static short
 ace2type(struct nfs4_ace *ace)
 {
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 83d2b645b0d6..c49cd04cb567 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -128,26 +128,6 @@  is_create_with_attrs(struct nfsd4_open *open)
 		    || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1);
 }
 
-/*
- * if error occurs when setting the acl, just clear the acl bit
- * 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)
-{
-	__be32 status;
-
-	status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
-	if (status)
-		/*
-		 * We should probably fail the whole open at this point,
-		 * but we've already created the file, so it's too late;
-		 * So this seems the least of evils:
-		 */
-		bmval[0] &= ~FATTR4_WORD0_ACL;
-}
-
 static inline void
 fh_dup2(struct svc_fh *dst, struct svc_fh *src)
 {
@@ -281,6 +261,9 @@  nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	if (host_err)
 		return nfserrno(host_err);
 
+	if (is_create_with_attrs(open))
+		nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
+
 	fh_lock_nested(fhp, I_MUTEX_PARENT);
 
 	child = lookup_one_len(open->op_fname, parent, open->op_fnamelen);
@@ -382,8 +365,11 @@  nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
 
 	if (attrs.label_failed)
 		open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+	if (attrs.acl_failed)
+		open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
 out:
 	fh_unlock(fhp);
+	nfsd_attrs_free(&attrs);
 	if (child && !IS_ERR(child))
 		dput(child);
 	fh_drop_write(fhp);
@@ -446,9 +432,6 @@  do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
 	if (status)
 		goto out;
 
-	if (is_create_with_attrs(open) && open->op_acl != NULL)
-		do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
-
 	nfsd4_set_open_owner_reply_cache(cstate, open, *resfh);
 	accmode = NFSD_MAY_NOP;
 	if (open->op_created ||
@@ -779,6 +762,7 @@  nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	if (status)
 		return status;
 
+	status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs);
 	current->fs->umask = create->cr_umask;
 	switch (create->cr_type) {
 	case NF4LNK:
@@ -837,10 +821,8 @@  nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
 	if (attrs.label_failed)
 		create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
-
-	if (create->cr_acl != NULL)
-		do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
-				create->cr_bmval);
+	if (attrs.label_failed)
+		create->cr_bmval[0] &= ~FATTR4_WORD0_ACL;
 
 	fh_unlock(&cstate->current_fh);
 	set_change_info(&create->cr_cinfo, &cstate->current_fh);
@@ -849,6 +831,7 @@  nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	fh_put(&resfh);
 out_umask:
 	current->fs->umask = 0;
+	nfsd_attrs_free(&attrs);
 	return status;
 }
 
@@ -1123,6 +1106,7 @@  nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		.iattr = &setattr->sa_iattr,
 		.label = &setattr->sa_label,
 	};
+	struct inode *inode;
 	__be32 status = nfs_ok;
 	int err;
 
@@ -1145,9 +1129,10 @@  nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	if (status)
 		goto out;
 
-	if (setattr->sa_acl != NULL)
-		status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
-					    setattr->sa_acl);
+	inode = cstate->current_fh.fh_dentry->d_inode;
+	status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG,
+				   setattr->sa_acl, &attrs);
+
 	if (status)
 		goto out;
 	status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs,
@@ -1155,6 +1140,7 @@  nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 	if (!status)
 		status = attrs.label_failed;
 out:
+	nfsd_attrs_free(&attrs);
 	fh_drop_write(&cstate->current_fh);
 	return status;
 }
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index e7a18bedf499..4bb05586a258 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -461,6 +461,14 @@  nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	if (attr->label && attr->label->len)
 		attr->label_failed = security_inode_setsecctx(
 			dentry, attr->label->data, attr->label->len);
+	if (attr->pacl)
+		attr->acl_failed = set_posix_acl(&init_user_ns,
+						 inode, ACL_TYPE_ACCESS,
+						 attr->pacl);
+	if (!attr->acl_failed && attr->dpacl && S_ISDIR(inode->i_mode))
+		attr->acl_failed = set_posix_acl(&init_user_ns,
+						 inode, ACL_TYPE_DEFAULT,
+						 attr->dpacl);
 	fh_unlock(fhp);
 	if (size_change)
 		put_write_access(inode);
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 8464e04af1ea..9343aac0bd15 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -6,6 +6,8 @@ 
 #ifndef LINUX_NFSD_VFS_H
 #define LINUX_NFSD_VFS_H
 
+#include <linux/fs.h>
+#include <linux/posix_acl.h>
 #include "nfsfh.h"
 #include "nfsd.h"
 
@@ -45,9 +47,17 @@  typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
 struct nfsd_attrs {
 	struct iattr *iattr;
 	struct xdr_netobj *label;
+	struct posix_acl *pacl, *dpacl;
 	int label_failed;
+	int acl_failed;
 };
 
+static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
+{
+	posix_acl_release(attrs->pacl);
+	posix_acl_release(attrs->dpacl);
+}
+
 int		nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
 		                struct svc_export **expp);
 __be32		nfsd_lookup(struct svc_rqst *, struct svc_fh *,