Re: [PATCH v2 06/14] nfsd: in nfsd4_create_file() let VFS report if file was created.

Jeff Layton <[email protected]>
Newsgroups gmane.linux.nfs
Message-ID <[email protected]>
On Mon, 2026-07-06 at 08:19 +1000, NeilBrown wrote:
> From: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]>
> 
> nfsd4_create_file() currently assumes that if a lookup failed but then a
> create succeeds, then the "create" operation actually created the file.
> With atomic_open this may not be the case - some other actor might have
> created the file between the lookup and the create.
> 
> So we move the call to nfsd4_vfs_create() earlier and set ->op_created
> based on the FMODE_CREATED flag that it set.  Then use "!  ->op_created"
> to trigger nfserr_exist handling.
> 
> The switch statement is split up into two if() statements.
> First we check for the possibility of a successful exclusive
> create and set ->op_create to true if appropriate.
> Then we check for NFS4_CREATE_UNCHECKED to decide if a
> pre-existing file means an error or success.
> 
> This allows us to combine the two fh_compose() calls to one place.
> 
> A subtle difference here is that we now must only pass O_EXCL to
> dentry_create() for NFS4_CREATE_GUARDED.  For the EXCLUSIVE create modes
> we want a successful open even if the file already exists.  We then
> check the verifier after the open succeeded to see if it was exclusive.
> 

Do we really want a successful open in the EXCLUSIVE cases?

Opens have side effects (notably, that they can cause delegation
recalls). If you have two racing clients creating a file, the first
gets an open and write delegation and then the second ends up
immediately causing a delegrecall for the first, even though it may
never touch the file again after the OPEN fails.

I think we may want to reconsider that logic, if possible: Maybe we
should keep using O_EXCL in those cases and just re-drive the open
without it if it fails and the verifier looks right? That's a bit
uglier, but that may cause fewer delegation recalls.

> The above requires changing dentry_create() to reliably set
> FMODE_CREATED when the file was actually created.  Previously it only
> sets this flag when atomic_open is used.
> 
> Signed-off-by: NeilBrown <neil-+NVA1uvv1dVBDLzU/[email protected]>
> ---
>  fs/namei.c         |  2 ++
>  fs/nfsd/nfs4proc.c | 69 ++++++++++++++++++++--------------------------
>  2 files changed, 32 insertions(+), 39 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index 5cc9f0f466b8..e0a62198fc60 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -5073,6 +5073,8 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
>  		error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL);
>  		if (!error)
>  			error = vfs_open(path, file);
> +		if (!error)
> +			file->f_mode |= FMODE_CREATED;
>  	}
>  	if (unlikely(error))
>  		return ERR_PTR(error);
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 69cdbdcde7e9..f59ee074c0c9 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -210,7 +210,11 @@ nfsd4_vfs_create(struct svc_fh *fhp, struct dentry **child,
>  	int oflags;
>  
>  	oflags = O_CREAT | O_LARGEFILE;
> -	if (nfsd4_create_is_exclusive(open->op_createmode))
> +	/*
> +	 * For the EXCLUSIVE modes we do our own uniqueness tests
> +	 * so don't want O_EXCL.
> +	 */
> +	if (open->op_createmode == NFS4_CREATE_GUARDED)
>  		oflags |= O_EXCL;
>  
>  	switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) {
> @@ -333,22 +337,30 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  		status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
>  		if (status != nfs_ok)
>  			goto out;
> -	}
>  
> -	if (d_really_is_positive(child)) {
> -		/* NFSv4 protocol requires change attributes even though
> -		 * no change happened.
> -		 */
> -		fh_fill_post_noop(fhp);
> -
> -		status = fh_compose(resfhp, fhp->fh_export, child, fhp);
> +		status = nfsd4_vfs_create(fhp, &child, open);
>  		if (status != nfs_ok)
>  			goto out;
> +		open->op_created = open->op_filp->f_mode & FMODE_CREATED;
> +	}
>  
> -		switch (open->op_createmode) {
> -		case NFS4_CREATE_UNCHECKED:
> -			if (!d_is_reg(child))
> -				break;
> +	status = fh_compose(resfhp, fhp->fh_export, child, fhp);
> +	if (status != nfs_ok)
> +		goto out;
> +
> +	if (!open->op_created &&
> +	    nfsd4_create_is_exclusive(open->op_createmode) &&
> +	    inode_get_mtime_sec(d_inode(child)) == v_mtime &&
> +	    inode_get_atime_sec(d_inode(child)) == v_atime &&
> +	    d_inode(child)->i_size == 0)
> +		open->op_created = true;
> +
> +	if (!open->op_created) {
> +		if (open->op_createmode == NFS4_CREATE_UNCHECKED) {
> +			/* NFSv4 protocol requires change attributes
> +			 * even though no change happened.
> +			 */
> +			fh_fill_post_noop(fhp);
>  
>  			/*
>  			 * In NFSv4, we don't want to truncate the file
> @@ -356,41 +368,20 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
>  			 * some other reason. Furthermore, if the size is
>  			 * nonzero, we should ignore it according to spec!
>  			 */
> -			open->op_truncate = (iap->ia_valid & ATTR_SIZE) &&
> -						!iap->ia_size;
> -			break;
> -		case NFS4_CREATE_GUARDED:
> -			status = nfserr_exist;
> -			break;
> -		case NFS4_CREATE_EXCLUSIVE:
> -		case NFS4_CREATE_EXCLUSIVE4_1:
> -			if (inode_get_mtime_sec(d_inode(child)) == v_mtime &&
> -			    inode_get_atime_sec(d_inode(child)) == v_atime &&
> -			    d_inode(child)->i_size == 0) {
> -				open->op_created = true;
> -				goto set_attr;
> -			}
> +			open->op_truncate = (d_is_reg(child) &&
> +					     (iap->ia_valid & ATTR_SIZE) &&
> +					     !iap->ia_size);
> +		} else
>  			status = nfserr_exist;
> -			break;
> -		}
>  		goto out;
>  	}
> -
> -	status = nfsd4_vfs_create(fhp, &child, open);
> -	if (status != nfs_ok)
> -		goto out;
> -	open->op_created = true;
> +	/* file was created */
>  	fh_fill_post_attrs(fhp);
>  
> -	status = fh_compose(resfhp, fhp->fh_export, child, fhp);
> -	if (status != nfs_ok)
> -		goto out;
> -
>  	/* A newly created file already has a file size of zero. */
>  	if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
>  		iap->ia_valid &= ~ATTR_SIZE;
>  
> -set_attr:
>  	status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
>  
>  	if (attrs.na_labelerr)

-- 
Jeff Layton <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.