diff mbox series

[v2,1/2] nfsd: handle failure to collect pre/post-op attrs more sanely

Message ID 20230720-bz2223560-v2-1-070aaf2660b7@kernel.org (mailing list archive)
State New, archived
Headers show
Series nfsd: sanely handle inabilty to fetch pre/post attributes | expand

Commit Message

Jeff Layton July 20, 2023, 6:23 p.m. UTC
Collecting pre_op_attrs can fail, in which case it's probably best to
fail the whole operation.

Change fh_fill_{pre,post,both}_attrs to return __be32. For the pre and
both functions, have the callers check the return code and abort the
operation if it failed.

If fh_fill_post_attrs fails, then it's too late to do anything about it,
so most of those callers ignore the return value. If this happens, then
fh_post_saved will be false, which should cue the later stages to deal
with it.

Suggested-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/nfsd/nfs3proc.c |  4 +++-
 fs/nfsd/nfs4proc.c | 14 ++++++------
 fs/nfsd/nfsfh.c    | 26 ++++++++++++++---------
 fs/nfsd/nfsfh.h    |  6 +++---
 fs/nfsd/vfs.c      | 62 ++++++++++++++++++++++++++++++++++--------------------
 5 files changed, 69 insertions(+), 43 deletions(-)

Comments

NeilBrown July 20, 2023, 9:46 p.m. UTC | #1
On Fri, 21 Jul 2023, Jeff Layton wrote:
> Collecting pre_op_attrs can fail, in which case it's probably best to
> fail the whole operation.
> 
> Change fh_fill_{pre,post,both}_attrs to return __be32. For the pre and
> both functions, have the callers check the return code and abort the
> operation if it failed.
> 
> If fh_fill_post_attrs fails, then it's too late to do anything about it,
> so most of those callers ignore the return value. If this happens, then
> fh_post_saved will be false, which should cue the later stages to deal
> with it.
> 
> Suggested-by: Chuck Lever <chuck.lever@oracle.com>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/nfsd/nfs3proc.c |  4 +++-
>  fs/nfsd/nfs4proc.c | 14 ++++++------
>  fs/nfsd/nfsfh.c    | 26 ++++++++++++++---------
>  fs/nfsd/nfsfh.h    |  6 +++---
>  fs/nfsd/vfs.c      | 62 ++++++++++++++++++++++++++++++++++--------------------
>  5 files changed, 69 insertions(+), 43 deletions(-)
> 
> diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
> index fc8d5b7db9f8..268ef57751c4 100644
> --- a/fs/nfsd/nfs3proc.c
> +++ b/fs/nfsd/nfs3proc.c
> @@ -307,7 +307,9 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  	if (!IS_POSIXACL(inode))
>  		iap->ia_mode &= ~current_umask();
>  
> -	fh_fill_pre_attrs(fhp);
> +	status = fh_fill_pre_attrs(fhp);
> +	if (status != nfs_ok)
> +		goto out;
>  	host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true);
>  	if (host_err < 0) {
>  		status = nfserrno(host_err);
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index d8e7a533f9d2..9285e1eab4d5 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -297,12 +297,12 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  	}
>  
>  	if (d_really_is_positive(child)) {
> -		status = nfs_ok;
> -
>  		/* NFSv4 protocol requires change attributes even though
>  		 * no change happened.
>  		 */
> -		fh_fill_both_attrs(fhp);
> +		status = fh_fill_both_attrs(fhp);
> +		if (status != nfs_ok)
> +			goto out;
>  
>  		switch (open->op_createmode) {
>  		case NFS4_CREATE_UNCHECKED:
> @@ -345,7 +345,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  	if (!IS_POSIXACL(inode))
>  		iap->ia_mode &= ~current_umask();
>  
> -	fh_fill_pre_attrs(fhp);
> +	status = fh_fill_pre_attrs(fhp);
> +	if (status != nfs_ok)
> +		goto out;
>  	status = nfsd4_vfs_create(fhp, child, open);
>  	if (status != nfs_ok)
>  		goto out;
> @@ -424,11 +426,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
>  	} else {
>  		status = nfsd_lookup(rqstp, current_fh,
>  				     open->op_fname, open->op_fnamelen, *resfh);
> -		if (!status)
> +		if (status == nfs_ok)
>  			/* NFSv4 protocol requires change attributes even though
>  			 * no change happened.
>  			 */
> -			fh_fill_both_attrs(current_fh);
> +			status = fh_fill_both_attrs(current_fh);
>  	}
>  	if (status)
>  		goto out;
> diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> index c291389a1d71..f7e68a91e826 100644
> --- a/fs/nfsd/nfsfh.c
> +++ b/fs/nfsd/nfsfh.c
> @@ -614,7 +614,7 @@ fh_update(struct svc_fh *fhp)
>   * @fhp: file handle to be updated
>   *
>   */
> -void fh_fill_pre_attrs(struct svc_fh *fhp)
> +__be32 fh_fill_pre_attrs(struct svc_fh *fhp)
>  {
>  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
>  	struct inode *inode;
> @@ -622,12 +622,12 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
>  	__be32 err;
>  
>  	if (fhp->fh_no_wcc || fhp->fh_pre_saved)
> -		return;
> +		return nfs_ok;
>  
>  	inode = d_inode(fhp->fh_dentry);
>  	err = fh_getattr(fhp, &stat);
>  	if (err)
> -		return;
> +		return err;
>  
>  	if (v4)
>  		fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
> @@ -636,6 +636,7 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
>  	fhp->fh_pre_ctime = stat.ctime;
>  	fhp->fh_pre_size  = stat.size;
>  	fhp->fh_pre_saved = true;
> +	return nfs_ok;
>  }
>  
>  /**
> @@ -643,26 +644,27 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
>   * @fhp: file handle to be updated
>   *
>   */
> -void fh_fill_post_attrs(struct svc_fh *fhp)
> +__be32 fh_fill_post_attrs(struct svc_fh *fhp)
>  {
>  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
>  	struct inode *inode = d_inode(fhp->fh_dentry);
>  	__be32 err;
>  
>  	if (fhp->fh_no_wcc)
> -		return;
> +		return nfs_ok;
>  
>  	if (fhp->fh_post_saved)
>  		printk("nfsd: inode locked twice during operation.\n");
>  
>  	err = fh_getattr(fhp, &fhp->fh_post_attr);
>  	if (err)
> -		return;
> +		return err;
>  
>  	fhp->fh_post_saved = true;
>  	if (v4)
>  		fhp->fh_post_change =
>  			nfsd4_change_attribute(&fhp->fh_post_attr, inode);
> +	return nfs_ok;
>  }
>  
>  /**
> @@ -672,16 +674,20 @@ void fh_fill_post_attrs(struct svc_fh *fhp)
>   * This is used when the directory wasn't changed, but wcc attributes
>   * are needed anyway.
>   */
> -void fh_fill_both_attrs(struct svc_fh *fhp)
> +__be32 fh_fill_both_attrs(struct svc_fh *fhp)
>  {
> -	fh_fill_post_attrs(fhp);
> -	if (!fhp->fh_post_saved)
> -		return;
> +	__be32 err;
> +
> +	err = fh_fill_post_attrs(fhp);
> +	if (err)
> +		return err;
> +
>  	fhp->fh_pre_change = fhp->fh_post_change;
>  	fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
>  	fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
>  	fhp->fh_pre_size = fhp->fh_post_attr.size;
>  	fhp->fh_pre_saved = true;
> +	return nfs_ok;
>  }
>  
>  /*
> diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
> index 4e0ecf0ae2cf..486803694acc 100644
> --- a/fs/nfsd/nfsfh.h
> +++ b/fs/nfsd/nfsfh.h
> @@ -294,7 +294,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
>  }
>  
>  u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode);
> -extern void fh_fill_pre_attrs(struct svc_fh *fhp);
> -extern void fh_fill_post_attrs(struct svc_fh *fhp);
> -extern void fh_fill_both_attrs(struct svc_fh *fhp);
> +__be32 fh_fill_pre_attrs(struct svc_fh *fhp);
> +__be32 fh_fill_post_attrs(struct svc_fh *fhp);
> +__be32 fh_fill_both_attrs(struct svc_fh *fhp);
>  #endif /* _LINUX_NFSD_NFSFH_H */
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 8a2321d19194..f200afd33630 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1537,9 +1537,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  	dput(dchild);
>  	if (err)
>  		goto out_unlock;
> -	fh_fill_pre_attrs(fhp);
> -	err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> -	fh_fill_post_attrs(fhp);
> +	err = fh_fill_pre_attrs(fhp);
> +	if (err == nfs_ok) {
> +		err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> +		fh_fill_post_attrs(fhp);

Most error handling in nfsd is
 
   if (err)
       goto ....

Here (and one other place I think) you have
   if (not err)
       do stuff;

Do we want to be more consistent?  I'm in two minds about this and I
don't dislike your patch.  But I noticed the inconsistency and thought I
should mention it.

Also, should we put a __must_check annotation on fh_fill_pre_attrs() and
..post..?  Then I wouldn't have to manually check that you found and
fixed all callers (which I haven't).

Thanks,
NeilBrown
Chuck Lever July 20, 2023, 11:09 p.m. UTC | #2
On Fri, Jul 21, 2023 at 07:46:20AM +1000, NeilBrown wrote:
> On Fri, 21 Jul 2023, Jeff Layton wrote:
> > Collecting pre_op_attrs can fail, in which case it's probably best to
> > fail the whole operation.
> > 
> > Change fh_fill_{pre,post,both}_attrs to return __be32. For the pre and
> > both functions, have the callers check the return code and abort the
> > operation if it failed.
> > 
> > If fh_fill_post_attrs fails, then it's too late to do anything about it,
> > so most of those callers ignore the return value. If this happens, then
> > fh_post_saved will be false, which should cue the later stages to deal
> > with it.
> > 
> > Suggested-by: Chuck Lever <chuck.lever@oracle.com>
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >  fs/nfsd/nfs3proc.c |  4 +++-
> >  fs/nfsd/nfs4proc.c | 14 ++++++------
> >  fs/nfsd/nfsfh.c    | 26 ++++++++++++++---------
> >  fs/nfsd/nfsfh.h    |  6 +++---
> >  fs/nfsd/vfs.c      | 62 ++++++++++++++++++++++++++++++++++--------------------
> >  5 files changed, 69 insertions(+), 43 deletions(-)
> > 
> > diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
> > index fc8d5b7db9f8..268ef57751c4 100644
> > --- a/fs/nfsd/nfs3proc.c
> > +++ b/fs/nfsd/nfs3proc.c
> > @@ -307,7 +307,9 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> >  	if (!IS_POSIXACL(inode))
> >  		iap->ia_mode &= ~current_umask();
> >  
> > -	fh_fill_pre_attrs(fhp);
> > +	status = fh_fill_pre_attrs(fhp);
> > +	if (status != nfs_ok)
> > +		goto out;
> >  	host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true);
> >  	if (host_err < 0) {
> >  		status = nfserrno(host_err);
> > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > index d8e7a533f9d2..9285e1eab4d5 100644
> > --- a/fs/nfsd/nfs4proc.c
> > +++ b/fs/nfsd/nfs4proc.c
> > @@ -297,12 +297,12 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> >  	}
> >  
> >  	if (d_really_is_positive(child)) {
> > -		status = nfs_ok;
> > -
> >  		/* NFSv4 protocol requires change attributes even though
> >  		 * no change happened.
> >  		 */
> > -		fh_fill_both_attrs(fhp);
> > +		status = fh_fill_both_attrs(fhp);
> > +		if (status != nfs_ok)
> > +			goto out;
> >  
> >  		switch (open->op_createmode) {
> >  		case NFS4_CREATE_UNCHECKED:
> > @@ -345,7 +345,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> >  	if (!IS_POSIXACL(inode))
> >  		iap->ia_mode &= ~current_umask();
> >  
> > -	fh_fill_pre_attrs(fhp);
> > +	status = fh_fill_pre_attrs(fhp);
> > +	if (status != nfs_ok)
> > +		goto out;
> >  	status = nfsd4_vfs_create(fhp, child, open);
> >  	if (status != nfs_ok)
> >  		goto out;
> > @@ -424,11 +426,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
> >  	} else {
> >  		status = nfsd_lookup(rqstp, current_fh,
> >  				     open->op_fname, open->op_fnamelen, *resfh);
> > -		if (!status)
> > +		if (status == nfs_ok)
> >  			/* NFSv4 protocol requires change attributes even though
> >  			 * no change happened.
> >  			 */
> > -			fh_fill_both_attrs(current_fh);
> > +			status = fh_fill_both_attrs(current_fh);
> >  	}
> >  	if (status)
> >  		goto out;
> > diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> > index c291389a1d71..f7e68a91e826 100644
> > --- a/fs/nfsd/nfsfh.c
> > +++ b/fs/nfsd/nfsfh.c
> > @@ -614,7 +614,7 @@ fh_update(struct svc_fh *fhp)
> >   * @fhp: file handle to be updated
> >   *
> >   */
> > -void fh_fill_pre_attrs(struct svc_fh *fhp)
> > +__be32 fh_fill_pre_attrs(struct svc_fh *fhp)
> >  {
> >  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
> >  	struct inode *inode;
> > @@ -622,12 +622,12 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> >  	__be32 err;
> >  
> >  	if (fhp->fh_no_wcc || fhp->fh_pre_saved)
> > -		return;
> > +		return nfs_ok;
> >  
> >  	inode = d_inode(fhp->fh_dentry);
> >  	err = fh_getattr(fhp, &stat);
> >  	if (err)
> > -		return;
> > +		return err;
> >  
> >  	if (v4)
> >  		fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
> > @@ -636,6 +636,7 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> >  	fhp->fh_pre_ctime = stat.ctime;
> >  	fhp->fh_pre_size  = stat.size;
> >  	fhp->fh_pre_saved = true;
> > +	return nfs_ok;
> >  }
> >  
> >  /**
> > @@ -643,26 +644,27 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> >   * @fhp: file handle to be updated
> >   *
> >   */
> > -void fh_fill_post_attrs(struct svc_fh *fhp)
> > +__be32 fh_fill_post_attrs(struct svc_fh *fhp)
> >  {
> >  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
> >  	struct inode *inode = d_inode(fhp->fh_dentry);
> >  	__be32 err;
> >  
> >  	if (fhp->fh_no_wcc)
> > -		return;
> > +		return nfs_ok;
> >  
> >  	if (fhp->fh_post_saved)
> >  		printk("nfsd: inode locked twice during operation.\n");
> >  
> >  	err = fh_getattr(fhp, &fhp->fh_post_attr);
> >  	if (err)
> > -		return;
> > +		return err;
> >  
> >  	fhp->fh_post_saved = true;
> >  	if (v4)
> >  		fhp->fh_post_change =
> >  			nfsd4_change_attribute(&fhp->fh_post_attr, inode);
> > +	return nfs_ok;
> >  }
> >  
> >  /**
> > @@ -672,16 +674,20 @@ void fh_fill_post_attrs(struct svc_fh *fhp)
> >   * This is used when the directory wasn't changed, but wcc attributes
> >   * are needed anyway.
> >   */
> > -void fh_fill_both_attrs(struct svc_fh *fhp)
> > +__be32 fh_fill_both_attrs(struct svc_fh *fhp)
> >  {
> > -	fh_fill_post_attrs(fhp);
> > -	if (!fhp->fh_post_saved)
> > -		return;
> > +	__be32 err;
> > +
> > +	err = fh_fill_post_attrs(fhp);
> > +	if (err)
> > +		return err;
> > +
> >  	fhp->fh_pre_change = fhp->fh_post_change;
> >  	fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
> >  	fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
> >  	fhp->fh_pre_size = fhp->fh_post_attr.size;
> >  	fhp->fh_pre_saved = true;
> > +	return nfs_ok;
> >  }
> >  
> >  /*
> > diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
> > index 4e0ecf0ae2cf..486803694acc 100644
> > --- a/fs/nfsd/nfsfh.h
> > +++ b/fs/nfsd/nfsfh.h
> > @@ -294,7 +294,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
> >  }
> >  
> >  u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode);
> > -extern void fh_fill_pre_attrs(struct svc_fh *fhp);
> > -extern void fh_fill_post_attrs(struct svc_fh *fhp);
> > -extern void fh_fill_both_attrs(struct svc_fh *fhp);
> > +__be32 fh_fill_pre_attrs(struct svc_fh *fhp);
> > +__be32 fh_fill_post_attrs(struct svc_fh *fhp);
> > +__be32 fh_fill_both_attrs(struct svc_fh *fhp);
> >  #endif /* _LINUX_NFSD_NFSFH_H */
> > diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> > index 8a2321d19194..f200afd33630 100644
> > --- a/fs/nfsd/vfs.c
> > +++ b/fs/nfsd/vfs.c
> > @@ -1537,9 +1537,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
> >  	dput(dchild);
> >  	if (err)
> >  		goto out_unlock;
> > -	fh_fill_pre_attrs(fhp);
> > -	err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> > -	fh_fill_post_attrs(fhp);
> > +	err = fh_fill_pre_attrs(fhp);
> > +	if (err == nfs_ok) {
> > +		err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> > +		fh_fill_post_attrs(fhp);
> 
> Most error handling in nfsd is
>  
>    if (err)
>        goto ....
> 
> Here (and one other place I think) you have
>    if (not err)
>        do stuff;
> 
> Do we want to be more consistent?

Yes, unless being consistent makes this code unreadable. There
doesn't seem to be a reason to drop that convention here.


> I'm in two minds about this and I
> don't dislike your patch.  But I noticed the inconsistency and thought I
> should mention it.
> 
> Also, should we put a __must_check annotation on fh_fill_pre_attrs() and
> ..post..?  Then I wouldn't have to manually check that you found and
> fixed all callers (which I haven't).
Jeff Layton July 21, 2023, 12:17 p.m. UTC | #3
On Thu, 2023-07-20 at 19:09 -0400, Chuck Lever wrote:
> On Fri, Jul 21, 2023 at 07:46:20AM +1000, NeilBrown wrote:
> > On Fri, 21 Jul 2023, Jeff Layton wrote:
> > > Collecting pre_op_attrs can fail, in which case it's probably best to
> > > fail the whole operation.
> > > 
> > > Change fh_fill_{pre,post,both}_attrs to return __be32. For the pre and
> > > both functions, have the callers check the return code and abort the
> > > operation if it failed.
> > > 
> > > If fh_fill_post_attrs fails, then it's too late to do anything about it,
> > > so most of those callers ignore the return value. If this happens, then
> > > fh_post_saved will be false, which should cue the later stages to deal
> > > with it.
> > > 
> > > Suggested-by: Chuck Lever <chuck.lever@oracle.com>
> > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > ---
> > >  fs/nfsd/nfs3proc.c |  4 +++-
> > >  fs/nfsd/nfs4proc.c | 14 ++++++------
> > >  fs/nfsd/nfsfh.c    | 26 ++++++++++++++---------
> > >  fs/nfsd/nfsfh.h    |  6 +++---
> > >  fs/nfsd/vfs.c      | 62 ++++++++++++++++++++++++++++++++++--------------------
> > >  5 files changed, 69 insertions(+), 43 deletions(-)
> > > 
> > > diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
> > > index fc8d5b7db9f8..268ef57751c4 100644
> > > --- a/fs/nfsd/nfs3proc.c
> > > +++ b/fs/nfsd/nfs3proc.c
> > > @@ -307,7 +307,9 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> > >  	if (!IS_POSIXACL(inode))
> > >  		iap->ia_mode &= ~current_umask();
> > >  
> > > -	fh_fill_pre_attrs(fhp);
> > > +	status = fh_fill_pre_attrs(fhp);
> > > +	if (status != nfs_ok)
> > > +		goto out;
> > >  	host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true);
> > >  	if (host_err < 0) {
> > >  		status = nfserrno(host_err);
> > > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > > index d8e7a533f9d2..9285e1eab4d5 100644
> > > --- a/fs/nfsd/nfs4proc.c
> > > +++ b/fs/nfsd/nfs4proc.c
> > > @@ -297,12 +297,12 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> > >  	}
> > >  
> > >  	if (d_really_is_positive(child)) {
> > > -		status = nfs_ok;
> > > -
> > >  		/* NFSv4 protocol requires change attributes even though
> > >  		 * no change happened.
> > >  		 */
> > > -		fh_fill_both_attrs(fhp);
> > > +		status = fh_fill_both_attrs(fhp);
> > > +		if (status != nfs_ok)
> > > +			goto out;
> > >  
> > >  		switch (open->op_createmode) {
> > >  		case NFS4_CREATE_UNCHECKED:
> > > @@ -345,7 +345,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
> > >  	if (!IS_POSIXACL(inode))
> > >  		iap->ia_mode &= ~current_umask();
> > >  
> > > -	fh_fill_pre_attrs(fhp);
> > > +	status = fh_fill_pre_attrs(fhp);
> > > +	if (status != nfs_ok)
> > > +		goto out;
> > >  	status = nfsd4_vfs_create(fhp, child, open);
> > >  	if (status != nfs_ok)
> > >  		goto out;
> > > @@ -424,11 +426,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
> > >  	} else {
> > >  		status = nfsd_lookup(rqstp, current_fh,
> > >  				     open->op_fname, open->op_fnamelen, *resfh);
> > > -		if (!status)
> > > +		if (status == nfs_ok)
> > >  			/* NFSv4 protocol requires change attributes even though
> > >  			 * no change happened.
> > >  			 */
> > > -			fh_fill_both_attrs(current_fh);
> > > +			status = fh_fill_both_attrs(current_fh);
> > >  	}
> > >  	if (status)
> > >  		goto out;
> > > diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> > > index c291389a1d71..f7e68a91e826 100644
> > > --- a/fs/nfsd/nfsfh.c
> > > +++ b/fs/nfsd/nfsfh.c
> > > @@ -614,7 +614,7 @@ fh_update(struct svc_fh *fhp)
> > >   * @fhp: file handle to be updated
> > >   *
> > >   */
> > > -void fh_fill_pre_attrs(struct svc_fh *fhp)
> > > +__be32 fh_fill_pre_attrs(struct svc_fh *fhp)
> > >  {
> > >  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
> > >  	struct inode *inode;
> > > @@ -622,12 +622,12 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> > >  	__be32 err;
> > >  
> > >  	if (fhp->fh_no_wcc || fhp->fh_pre_saved)
> > > -		return;
> > > +		return nfs_ok;
> > >  
> > >  	inode = d_inode(fhp->fh_dentry);
> > >  	err = fh_getattr(fhp, &stat);
> > >  	if (err)
> > > -		return;
> > > +		return err;
> > >  
> > >  	if (v4)
> > >  		fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
> > > @@ -636,6 +636,7 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> > >  	fhp->fh_pre_ctime = stat.ctime;
> > >  	fhp->fh_pre_size  = stat.size;
> > >  	fhp->fh_pre_saved = true;
> > > +	return nfs_ok;
> > >  }
> > >  
> > >  /**
> > > @@ -643,26 +644,27 @@ void fh_fill_pre_attrs(struct svc_fh *fhp)
> > >   * @fhp: file handle to be updated
> > >   *
> > >   */
> > > -void fh_fill_post_attrs(struct svc_fh *fhp)
> > > +__be32 fh_fill_post_attrs(struct svc_fh *fhp)
> > >  {
> > >  	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
> > >  	struct inode *inode = d_inode(fhp->fh_dentry);
> > >  	__be32 err;
> > >  
> > >  	if (fhp->fh_no_wcc)
> > > -		return;
> > > +		return nfs_ok;
> > >  
> > >  	if (fhp->fh_post_saved)
> > >  		printk("nfsd: inode locked twice during operation.\n");
> > >  
> > >  	err = fh_getattr(fhp, &fhp->fh_post_attr);
> > >  	if (err)
> > > -		return;
> > > +		return err;
> > >  
> > >  	fhp->fh_post_saved = true;
> > >  	if (v4)
> > >  		fhp->fh_post_change =
> > >  			nfsd4_change_attribute(&fhp->fh_post_attr, inode);
> > > +	return nfs_ok;
> > >  }
> > >  
> > >  /**
> > > @@ -672,16 +674,20 @@ void fh_fill_post_attrs(struct svc_fh *fhp)
> > >   * This is used when the directory wasn't changed, but wcc attributes
> > >   * are needed anyway.
> > >   */
> > > -void fh_fill_both_attrs(struct svc_fh *fhp)
> > > +__be32 fh_fill_both_attrs(struct svc_fh *fhp)
> > >  {
> > > -	fh_fill_post_attrs(fhp);
> > > -	if (!fhp->fh_post_saved)
> > > -		return;
> > > +	__be32 err;
> > > +
> > > +	err = fh_fill_post_attrs(fhp);
> > > +	if (err)
> > > +		return err;
> > > +
> > >  	fhp->fh_pre_change = fhp->fh_post_change;
> > >  	fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
> > >  	fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
> > >  	fhp->fh_pre_size = fhp->fh_post_attr.size;
> > >  	fhp->fh_pre_saved = true;
> > > +	return nfs_ok;
> > >  }
> > >  
> > >  /*
> > > diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
> > > index 4e0ecf0ae2cf..486803694acc 100644
> > > --- a/fs/nfsd/nfsfh.h
> > > +++ b/fs/nfsd/nfsfh.h
> > > @@ -294,7 +294,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
> > >  }
> > >  
> > >  u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode);
> > > -extern void fh_fill_pre_attrs(struct svc_fh *fhp);
> > > -extern void fh_fill_post_attrs(struct svc_fh *fhp);
> > > -extern void fh_fill_both_attrs(struct svc_fh *fhp);
> > > +__be32 fh_fill_pre_attrs(struct svc_fh *fhp);
> > > +__be32 fh_fill_post_attrs(struct svc_fh *fhp);
> > > +__be32 fh_fill_both_attrs(struct svc_fh *fhp);
> > >  #endif /* _LINUX_NFSD_NFSFH_H */
> > > diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> > > index 8a2321d19194..f200afd33630 100644
> > > --- a/fs/nfsd/vfs.c
> > > +++ b/fs/nfsd/vfs.c
> > > @@ -1537,9 +1537,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
> > >  	dput(dchild);
> > >  	if (err)
> > >  		goto out_unlock;
> > > -	fh_fill_pre_attrs(fhp);
> > > -	err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> > > -	fh_fill_post_attrs(fhp);
> > > +	err = fh_fill_pre_attrs(fhp);
> > > +	if (err == nfs_ok) {
> > > +		err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
> > > +		fh_fill_post_attrs(fhp);
> > 
> > Most error handling in nfsd is
> >  
> >    if (err)
> >        goto ....
> > 
> > Here (and one other place I think) you have
> >    if (not err)
> >        do stuff;
> > 
> > Do we want to be more consistent?
> 
> Yes, unless being consistent makes this code unreadable. There
> doesn't seem to be a reason to drop that convention here.
> 

My usual test for this is to use gotos if unwinding errors is complex
enough to warrant it, and to just use the second form if the code is
fairly simple.

But...if you want gotos everywhere, then so be it. I'll respin this.

> 
> > I'm in two minds about this and I
> > don't dislike your patch.  But I noticed the inconsistency and thought I
> > should mention it.
> > 
> > Also, should we put a __must_check annotation on fh_fill_pre_attrs() and
> > ..post..?  Then I wouldn't have to manually check that you found and
> > fixed all callers (which I haven't).

Maybe for the "pre" and "both" ones. We would _not_ want to add
__must_check for the post one, since most of the callers (correctly)
ignore that return value.

I'll plan to roll that in.
diff mbox series

Patch

diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index fc8d5b7db9f8..268ef57751c4 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -307,7 +307,9 @@  nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	if (!IS_POSIXACL(inode))
 		iap->ia_mode &= ~current_umask();
 
-	fh_fill_pre_attrs(fhp);
+	status = fh_fill_pre_attrs(fhp);
+	if (status != nfs_ok)
+		goto out;
 	host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true);
 	if (host_err < 0) {
 		status = nfserrno(host_err);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index d8e7a533f9d2..9285e1eab4d5 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -297,12 +297,12 @@  nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	}
 
 	if (d_really_is_positive(child)) {
-		status = nfs_ok;
-
 		/* NFSv4 protocol requires change attributes even though
 		 * no change happened.
 		 */
-		fh_fill_both_attrs(fhp);
+		status = fh_fill_both_attrs(fhp);
+		if (status != nfs_ok)
+			goto out;
 
 		switch (open->op_createmode) {
 		case NFS4_CREATE_UNCHECKED:
@@ -345,7 +345,9 @@  nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	if (!IS_POSIXACL(inode))
 		iap->ia_mode &= ~current_umask();
 
-	fh_fill_pre_attrs(fhp);
+	status = fh_fill_pre_attrs(fhp);
+	if (status != nfs_ok)
+		goto out;
 	status = nfsd4_vfs_create(fhp, child, open);
 	if (status != nfs_ok)
 		goto out;
@@ -424,11 +426,11 @@  do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
 	} else {
 		status = nfsd_lookup(rqstp, current_fh,
 				     open->op_fname, open->op_fnamelen, *resfh);
-		if (!status)
+		if (status == nfs_ok)
 			/* NFSv4 protocol requires change attributes even though
 			 * no change happened.
 			 */
-			fh_fill_both_attrs(current_fh);
+			status = fh_fill_both_attrs(current_fh);
 	}
 	if (status)
 		goto out;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index c291389a1d71..f7e68a91e826 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -614,7 +614,7 @@  fh_update(struct svc_fh *fhp)
  * @fhp: file handle to be updated
  *
  */
-void fh_fill_pre_attrs(struct svc_fh *fhp)
+__be32 fh_fill_pre_attrs(struct svc_fh *fhp)
 {
 	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
 	struct inode *inode;
@@ -622,12 +622,12 @@  void fh_fill_pre_attrs(struct svc_fh *fhp)
 	__be32 err;
 
 	if (fhp->fh_no_wcc || fhp->fh_pre_saved)
-		return;
+		return nfs_ok;
 
 	inode = d_inode(fhp->fh_dentry);
 	err = fh_getattr(fhp, &stat);
 	if (err)
-		return;
+		return err;
 
 	if (v4)
 		fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
@@ -636,6 +636,7 @@  void fh_fill_pre_attrs(struct svc_fh *fhp)
 	fhp->fh_pre_ctime = stat.ctime;
 	fhp->fh_pre_size  = stat.size;
 	fhp->fh_pre_saved = true;
+	return nfs_ok;
 }
 
 /**
@@ -643,26 +644,27 @@  void fh_fill_pre_attrs(struct svc_fh *fhp)
  * @fhp: file handle to be updated
  *
  */
-void fh_fill_post_attrs(struct svc_fh *fhp)
+__be32 fh_fill_post_attrs(struct svc_fh *fhp)
 {
 	bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
 	struct inode *inode = d_inode(fhp->fh_dentry);
 	__be32 err;
 
 	if (fhp->fh_no_wcc)
-		return;
+		return nfs_ok;
 
 	if (fhp->fh_post_saved)
 		printk("nfsd: inode locked twice during operation.\n");
 
 	err = fh_getattr(fhp, &fhp->fh_post_attr);
 	if (err)
-		return;
+		return err;
 
 	fhp->fh_post_saved = true;
 	if (v4)
 		fhp->fh_post_change =
 			nfsd4_change_attribute(&fhp->fh_post_attr, inode);
+	return nfs_ok;
 }
 
 /**
@@ -672,16 +674,20 @@  void fh_fill_post_attrs(struct svc_fh *fhp)
  * This is used when the directory wasn't changed, but wcc attributes
  * are needed anyway.
  */
-void fh_fill_both_attrs(struct svc_fh *fhp)
+__be32 fh_fill_both_attrs(struct svc_fh *fhp)
 {
-	fh_fill_post_attrs(fhp);
-	if (!fhp->fh_post_saved)
-		return;
+	__be32 err;
+
+	err = fh_fill_post_attrs(fhp);
+	if (err)
+		return err;
+
 	fhp->fh_pre_change = fhp->fh_post_change;
 	fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
 	fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
 	fhp->fh_pre_size = fhp->fh_post_attr.size;
 	fhp->fh_pre_saved = true;
+	return nfs_ok;
 }
 
 /*
diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
index 4e0ecf0ae2cf..486803694acc 100644
--- a/fs/nfsd/nfsfh.h
+++ b/fs/nfsd/nfsfh.h
@@ -294,7 +294,7 @@  static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
 }
 
 u64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode);
-extern void fh_fill_pre_attrs(struct svc_fh *fhp);
-extern void fh_fill_post_attrs(struct svc_fh *fhp);
-extern void fh_fill_both_attrs(struct svc_fh *fhp);
+__be32 fh_fill_pre_attrs(struct svc_fh *fhp);
+__be32 fh_fill_post_attrs(struct svc_fh *fhp);
+__be32 fh_fill_both_attrs(struct svc_fh *fhp);
 #endif /* _LINUX_NFSD_NFSFH_H */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 8a2321d19194..f200afd33630 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1537,9 +1537,11 @@  nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
 	dput(dchild);
 	if (err)
 		goto out_unlock;
-	fh_fill_pre_attrs(fhp);
-	err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
-	fh_fill_post_attrs(fhp);
+	err = fh_fill_pre_attrs(fhp);
+	if (err == nfs_ok) {
+		err = nfsd_create_locked(rqstp, fhp, attrs, type, rdev, resfhp);
+		fh_fill_post_attrs(fhp);
+	}
 out_unlock:
 	inode_unlock(dentry->d_inode);
 	return err;
@@ -1632,13 +1634,16 @@  nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
 		inode_unlock(dentry->d_inode);
 		goto out_drop_write;
 	}
-	fh_fill_pre_attrs(fhp);
+	err = fh_fill_pre_attrs(fhp);
+	if (err)
+		goto out_unlock;
 	host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path);
 	err = nfserrno(host_err);
 	cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
 	if (!err)
 		nfsd_create_setattr(rqstp, fhp, resfhp, attrs);
 	fh_fill_post_attrs(fhp);
+out_unlock:
 	inode_unlock(dentry->d_inode);
 	if (!err)
 		err = nfserrno(commit_metadata(fhp));
@@ -1700,7 +1705,9 @@  nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
 	err = nfserr_noent;
 	if (d_really_is_negative(dold))
 		goto out_dput;
-	fh_fill_pre_attrs(ffhp);
+	err = fh_fill_pre_attrs(ffhp);
+	if (err != nfs_ok)
+		goto out_dput;
 	host_err = vfs_link(dold, &nop_mnt_idmap, dirp, dnew, NULL);
 	fh_fill_post_attrs(ffhp);
 	inode_unlock(dirp);
@@ -1786,8 +1793,12 @@  nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
 	}
 
 	trap = lock_rename(tdentry, fdentry);
-	fh_fill_pre_attrs(ffhp);
-	fh_fill_pre_attrs(tfhp);
+	err = fh_fill_pre_attrs(ffhp);
+	if (err != nfs_ok)
+		goto out_unlock;
+	err = fh_fill_pre_attrs(tfhp);
+	if (err != nfs_ok)
+		goto out_unlock;
 
 	odentry = lookup_one_len(fname, fdentry, flen);
 	host_err = PTR_ERR(odentry);
@@ -1854,6 +1865,7 @@  nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
 		fh_fill_post_attrs(ffhp);
 		fh_fill_post_attrs(tfhp);
 	}
+out_unlock:
 	unlock_rename(tdentry, fdentry);
 	fh_drop_write(ffhp);
 
@@ -1913,12 +1925,14 @@  nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
 		goto out_unlock;
 	}
 	rinode = d_inode(rdentry);
-	ihold(rinode);
+	err = fh_fill_pre_attrs(fhp);
+	if (err != nfs_ok)
+		goto out_unlock;
 
+	ihold(rinode);
 	if (!type)
 		type = d_inode(rdentry)->i_mode & S_IFMT;
 
-	fh_fill_pre_attrs(fhp);
 	if (type != S_IFDIR) {
 		int retries;
 
@@ -2338,16 +2352,17 @@  nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name)
 		return nfserrno(ret);
 
 	inode_lock(fhp->fh_dentry->d_inode);
-	fh_fill_pre_attrs(fhp);
-
-	ret = __vfs_removexattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
-				       name, NULL);
-
-	fh_fill_post_attrs(fhp);
+	err = fh_fill_pre_attrs(fhp);
+	if (err == nfs_ok) {
+		ret = __vfs_removexattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
+					       name, NULL);
+		err = nfsd_xattr_errno(ret);
+		fh_fill_post_attrs(fhp);
+	}
 	inode_unlock(fhp->fh_dentry->d_inode);
 	fh_drop_write(fhp);
 
-	return nfsd_xattr_errno(ret);
+	return err;
 }
 
 __be32
@@ -2365,15 +2380,16 @@  nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
 	if (ret)
 		return nfserrno(ret);
 	inode_lock(fhp->fh_dentry->d_inode);
-	fh_fill_pre_attrs(fhp);
-
-	ret = __vfs_setxattr_locked(&nop_mnt_idmap, fhp->fh_dentry, name, buf,
-				    len, flags, NULL);
-	fh_fill_post_attrs(fhp);
+	err = fh_fill_pre_attrs(fhp);
+	if (err != nfs_ok) {
+		ret = __vfs_setxattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
+					    name, buf, len, flags, NULL);
+		fh_fill_post_attrs(fhp);
+		err = nfsd_xattr_errno(ret);
+	}
 	inode_unlock(fhp->fh_dentry->d_inode);
 	fh_drop_write(fhp);
-
-	return nfsd_xattr_errno(ret);
+	return err;
 }
 #endif