Skip to content

Commit fb70bf1

Browse files
committed
NFSD: Instantiate a struct file when creating a regular NFSv4 file
There have been reports of races that cause NFSv4 OPEN(CREATE) to return an error even though the requested file was created. NFSv4 does not provide a status code for this case. To mitigate some of these problems, reorganize the NFSv4 OPEN(CREATE) logic to allocate resources before the file is actually created, and open the new file while the parent directory is still locked. Two new APIs are added: + Add an API that works like nfsd_file_acquire() but does not open the underlying file. The OPEN(CREATE) path can use this API when it already has an open file. + Add an API that is kin to dentry_open(). NFSD needs to create a file and grab an open "struct file *" atomically. The alloc_empty_file() has to be done before the inode create. If it fails (for example, because the NFS server has exceeded its max_files limit), we avoid creating the file and can still return an error to the NFS client. BugLink: https://bugzilla.linux-nfs.org/show_bug.cgi?id=382 Signed-off-by: Chuck Lever <[email protected]> Tested-by: JianHong Yin <[email protected]>
1 parent f4d84c5 commit fb70bf1

File tree

7 files changed

+143
-14
lines changed

7 files changed

+143
-14
lines changed

fs/nfsd/filecache.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -897,9 +897,9 @@ nfsd_file_is_cached(struct inode *inode)
897897
return ret;
898898
}
899899

900-
__be32
901-
nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
902-
unsigned int may_flags, struct nfsd_file **pnf)
900+
static __be32
901+
nfsd_do_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
902+
unsigned int may_flags, struct nfsd_file **pnf, bool open)
903903
{
904904
__be32 status;
905905
struct net *net = SVC_NET(rqstp);
@@ -994,10 +994,13 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
994994
nfsd_file_gc();
995995

996996
nf->nf_mark = nfsd_file_mark_find_or_create(nf);
997-
if (nf->nf_mark)
998-
status = nfsd_open_verified(rqstp, fhp, may_flags,
999-
&nf->nf_file);
1000-
else
997+
if (nf->nf_mark) {
998+
if (open)
999+
status = nfsd_open_verified(rqstp, fhp, may_flags,
1000+
&nf->nf_file);
1001+
else
1002+
status = nfs_ok;
1003+
} else
10011004
status = nfserr_jukebox;
10021005
/*
10031006
* If construction failed, or we raced with a call to unlink()
@@ -1017,6 +1020,40 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
10171020
goto out;
10181021
}
10191022

1023+
/**
1024+
* nfsd_file_acquire - Get a struct nfsd_file with an open file
1025+
* @rqstp: the RPC transaction being executed
1026+
* @fhp: the NFS filehandle of the file to be opened
1027+
* @may_flags: NFSD_MAY_ settings for the file
1028+
* @pnf: OUT: new or found "struct nfsd_file" object
1029+
*
1030+
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
1031+
* network byte order is returned.
1032+
*/
1033+
__be32
1034+
nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
1035+
unsigned int may_flags, struct nfsd_file **pnf)
1036+
{
1037+
return nfsd_do_file_acquire(rqstp, fhp, may_flags, pnf, true);
1038+
}
1039+
1040+
/**
1041+
* nfsd_file_create - Get a struct nfsd_file, do not open
1042+
* @rqstp: the RPC transaction being executed
1043+
* @fhp: the NFS filehandle of the file just created
1044+
* @may_flags: NFSD_MAY_ settings for the file
1045+
* @pnf: OUT: new or found "struct nfsd_file" object
1046+
*
1047+
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
1048+
* network byte order is returned.
1049+
*/
1050+
__be32
1051+
nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
1052+
unsigned int may_flags, struct nfsd_file **pnf)
1053+
{
1054+
return nfsd_do_file_acquire(rqstp, fhp, may_flags, pnf, false);
1055+
}
1056+
10201057
/*
10211058
* Note that fields may be added, removed or reordered in the future. Programs
10221059
* scraping this file for info should test the labels to ensure they're

fs/nfsd/filecache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,7 @@ void nfsd_file_close_inode_sync(struct inode *inode);
5959
bool nfsd_file_is_cached(struct inode *inode);
6060
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
6161
unsigned int may_flags, struct nfsd_file **nfp);
62+
__be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
63+
unsigned int may_flags, struct nfsd_file **nfp);
6264
int nfsd_file_cache_stats_open(struct inode *, struct file *);
6365
#endif /* _FS_NFSD_FILECACHE_H */

fs/nfsd/nfs4proc.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,37 @@ static inline bool nfsd4_create_is_exclusive(int createmode)
243243
createmode == NFS4_CREATE_EXCLUSIVE4_1;
244244
}
245245

246+
static __be32
247+
nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child,
248+
struct nfsd4_open *open)
249+
{
250+
struct file *filp;
251+
struct path path;
252+
int oflags;
253+
254+
oflags = O_CREAT | O_LARGEFILE;
255+
switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) {
256+
case NFS4_SHARE_ACCESS_WRITE:
257+
oflags |= O_WRONLY;
258+
break;
259+
case NFS4_SHARE_ACCESS_BOTH:
260+
oflags |= O_RDWR;
261+
break;
262+
default:
263+
oflags |= O_RDONLY;
264+
}
265+
266+
path.mnt = fhp->fh_export->ex_path.mnt;
267+
path.dentry = child;
268+
filp = dentry_create(&path, oflags, open->op_iattr.ia_mode,
269+
current_cred());
270+
if (IS_ERR(filp))
271+
return nfserrno(PTR_ERR(filp));
272+
273+
open->op_filp = filp;
274+
return nfs_ok;
275+
}
276+
246277
/*
247278
* Implement NFSv4's unchecked, guarded, and exclusive create
248279
* semantics for regular files. Open state for this new file is
@@ -355,11 +386,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
355386
if (!IS_POSIXACL(inode))
356387
iap->ia_mode &= ~current_umask();
357388

358-
host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true);
359-
if (host_err < 0) {
360-
status = nfserrno(host_err);
389+
status = nfsd4_vfs_create(fhp, child, open);
390+
if (status != nfs_ok)
361391
goto out;
362-
}
363392
open->op_created = true;
364393

365394
/* A newly created file already has a file size of zero. */
@@ -517,6 +546,8 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
517546
(int)open->op_fnamelen, open->op_fname,
518547
open->op_openowner);
519548

549+
open->op_filp = NULL;
550+
520551
/* This check required by spec. */
521552
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
522553
return nfserr_inval;
@@ -613,6 +644,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
613644
if (reclaim && !status)
614645
nn->somebody_reclaimed = true;
615646
out:
647+
if (open->op_filp) {
648+
fput(open->op_filp);
649+
open->op_filp = NULL;
650+
}
616651
if (resfh && resfh != &cstate->current_fh) {
617652
fh_dup2(&cstate->current_fh, resfh);
618653
fh_put(resfh);

fs/nfsd/nfs4state.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5093,9 +5093,19 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
50935093

50945094
if (!fp->fi_fds[oflag]) {
50955095
spin_unlock(&fp->fi_lock);
5096-
status = nfsd_file_acquire(rqstp, cur_fh, access, &nf);
5097-
if (status)
5098-
goto out_put_access;
5096+
5097+
if (!open->op_filp) {
5098+
status = nfsd_file_acquire(rqstp, cur_fh, access, &nf);
5099+
if (status != nfs_ok)
5100+
goto out_put_access;
5101+
} else {
5102+
status = nfsd_file_create(rqstp, cur_fh, access, &nf);
5103+
if (status != nfs_ok)
5104+
goto out_put_access;
5105+
nf->nf_file = open->op_filp;
5106+
open->op_filp = NULL;
5107+
}
5108+
50995109
spin_lock(&fp->fi_lock);
51005110
if (!fp->fi_fds[oflag]) {
51015111
fp->fi_fds[oflag] = nf;

fs/nfsd/xdr4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ struct nfsd4_open {
273273
bool op_truncate; /* used during processing */
274274
bool op_created; /* used during processing */
275275
struct nfs4_openowner *op_openowner; /* used during processing */
276+
struct file *op_filp; /* used during processing */
276277
struct nfs4_file *op_file; /* used during processing */
277278
struct nfs4_ol_stateid *op_stp; /* used during processing */
278279
struct nfs4_clnt_odstate *op_odstate; /* used during processing */

fs/open.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,48 @@ struct file *dentry_open(const struct path *path, int flags,
981981
}
982982
EXPORT_SYMBOL(dentry_open);
983983

984+
/**
985+
* dentry_create - Create and open a file
986+
* @path: path to create
987+
* @flags: O_ flags
988+
* @mode: mode bits for new file
989+
* @cred: credentials to use
990+
*
991+
* Caller must hold the parent directory's lock, and have prepared
992+
* a negative dentry, placed in @path->dentry, for the new file.
993+
*
994+
* Caller sets @path->mnt to the vfsmount of the filesystem where
995+
* the new file is to be created. The parent directory and the
996+
* negative dentry must reside on the same filesystem instance.
997+
*
998+
* On success, returns a "struct file *". Otherwise a ERR_PTR
999+
* is returned.
1000+
*/
1001+
struct file *dentry_create(const struct path *path, int flags, umode_t mode,
1002+
const struct cred *cred)
1003+
{
1004+
struct file *f;
1005+
int error;
1006+
1007+
validate_creds(cred);
1008+
f = alloc_empty_file(flags, cred);
1009+
if (IS_ERR(f))
1010+
return f;
1011+
1012+
error = vfs_create(mnt_user_ns(path->mnt),
1013+
d_inode(path->dentry->d_parent),
1014+
path->dentry, mode, true);
1015+
if (!error)
1016+
error = vfs_open(path, f);
1017+
1018+
if (unlikely(error)) {
1019+
fput(f);
1020+
return ERR_PTR(error);
1021+
}
1022+
return f;
1023+
}
1024+
EXPORT_SYMBOL(dentry_create);
1025+
9841026
struct file *open_with_fake_path(const struct path *path, int flags,
9851027
struct inode *inode, const struct cred *cred)
9861028
{

include/linux/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,6 +2640,8 @@ static inline struct file *file_open_root_mnt(struct vfsmount *mnt,
26402640
name, flags, mode);
26412641
}
26422642
extern struct file * dentry_open(const struct path *, int, const struct cred *);
2643+
extern struct file *dentry_create(const struct path *path, int flags,
2644+
umode_t mode, const struct cred *cred);
26432645
extern struct file * open_with_fake_path(const struct path *, int,
26442646
struct inode*, const struct cred *);
26452647
static inline struct file *file_clone_open(struct file *file)

0 commit comments

Comments
 (0)