History

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.

Chuck Lever committed 4 years ago

Commit fb70bf11 parent f4d84c57 files · +143 −14Browse files

Unified · Split

7 files
  1. fs/nfsd/filecache.c +44 −7
  2. fs/nfsd/filecache.h +2 −0
  3. fs/nfsd/nfs4proc.c +39 −4
  4. fs/nfsd/nfs4state.c +13 −3
  5. fs/nfsd/xdr4.h +1 −0
  6. fs/open.c +42 −0
  7. include/linux/fs.h +2 −0
fs/nfsd/filecache.c+44−7
@@ -897,9 +897,9 @@ nfsd_file_is_cached(struct inode *inode) · 896 unmodified lines
897 return ret;897 return ret;
898}898}
899 899
900__be32900static __be32
901nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,901nfsd_do_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
902 unsigned int may_flags, struct nfsd_file **pnf)902 unsigned int may_flags, struct nfsd_file **pnf, bool open)
903{903{
904 __be32 status;904 __be32 status;
905 struct net *net = SVC_NET(rqstp);905 struct net *net = SVC_NET(rqstp);
@@ -994,10 +994,13 @@ open_file: · 88 unmodified lines
994 nfsd_file_gc();994 nfsd_file_gc();
995 995
996 nf->nf_mark = nfsd_file_mark_find_or_create(nf);996 nf->nf_mark = nfsd_file_mark_find_or_create(nf);
997 if (nf->nf_mark)997 if (nf->nf_mark) {
998 status = nfsd_open_verified(rqstp, fhp, may_flags,998 if (open)
999 &nf->nf_file);999 status = nfsd_open_verified(rqstp, fhp, may_flags,
1000 else1000 &nf->nf_file);
1001 else
1002 status = nfs_ok;
1003 } else
1001 status = nfserr_jukebox;1004 status = nfserr_jukebox;
1002 /*1005 /*
1003 * If construction failed, or we raced with a call to unlink()1006 * If construction failed, or we raced with a call to unlink()
@@ -1017,6 +1020,40 @@ open_file: · 13 unmodified lines
1017 goto out;1020 goto out;
1018}1021}
1019 1022
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
1034nfsd_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
1051nfsd_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
1020/*1057/*
1021 * Note that fields may be added, removed or reordered in the future. Programs1058 * Note that fields may be added, removed or reordered in the future. Programs
1022 * scraping this file for info should test the labels to ensure they're1059 * scraping this file for info should test the labels to ensure they're
fs/nfsd/filecache.h+2−0
@@ -59,5 +59,7 @@ void nfsd_file_close_inode_sync(struct inode *inode); · 58 unmodified lines
59bool nfsd_file_is_cached(struct inode *inode);59bool nfsd_file_is_cached(struct inode *inode);
60__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,60__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
61 unsigned int may_flags, struct nfsd_file **nfp);61 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);
62int nfsd_file_cache_stats_open(struct inode *, struct file *);64int nfsd_file_cache_stats_open(struct inode *, struct file *);
63#endif /* _FS_NFSD_FILECACHE_H */65#endif /* _FS_NFSD_FILECACHE_H */
fs/nfsd/nfs4proc.c+39−4
@@ -243,6 +243,37 @@ static inline bool nfsd4_create_is_exclusive(int createmode) · 242 unmodified lines
243 createmode == NFS4_CREATE_EXCLUSIVE4_1;243 createmode == NFS4_CREATE_EXCLUSIVE4_1;
244}244}
245 245
246static __be32
247nfsd4_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
246/*277/*
247 * Implement NFSv4's unchecked, guarded, and exclusive create278 * Implement NFSv4's unchecked, guarded, and exclusive create
248 * semantics for regular files. Open state for this new file is279 * 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, · 106 unmodified lines
355 if (!IS_POSIXACL(inode))386 if (!IS_POSIXACL(inode))
356 iap->ia_mode &= ~current_umask();387 iap->ia_mode &= ~current_umask();
357 388
358 host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true);389 status = nfsd4_vfs_create(fhp, child, open);
359 if (host_err < 0) {390 if (status != nfs_ok)
360 status = nfserrno(host_err);
361 goto out;391 goto out;
362 }
363 open->op_created = true;392 open->op_created = true;
364 393
365 /* A newly created file already has a file size of zero. */394 /* 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, · 151 unmodified lines
517 (int)open->op_fnamelen, open->op_fname,546 (int)open->op_fnamelen, open->op_fname,
518 open->op_openowner);547 open->op_openowner);
519 548
549 open->op_filp = NULL;
550
520 /* This check required by spec. */551 /* This check required by spec. */
521 if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)552 if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
522 return nfserr_inval;553 return nfserr_inval;
@@ -613,6 +644,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, · 90 unmodified lines
613 if (reclaim && !status)644 if (reclaim && !status)
614 nn->somebody_reclaimed = true;645 nn->somebody_reclaimed = true;
615out:646out:
647 if (open->op_filp) {
648 fput(open->op_filp);
649 open->op_filp = NULL;
650 }
616 if (resfh && resfh != &cstate->current_fh) {651 if (resfh && resfh != &cstate->current_fh) {
617 fh_dup2(&cstate->current_fh, resfh);652 fh_dup2(&cstate->current_fh, resfh);
618 fh_put(resfh);653 fh_put(resfh);
fs/nfsd/nfs4state.c+13−3
@@ -5093,9 +5093,19 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, · 5092 unmodified lines
5093 5093
5094 if (!fp->fi_fds[oflag]) {5094 if (!fp->fi_fds[oflag]) {
5095 spin_unlock(&fp->fi_lock);5095 spin_unlock(&fp->fi_lock);
5096 status = nfsd_file_acquire(rqstp, cur_fh, access, &nf);5096
5097 if (status)5097 if (!open->op_filp) {
5098 goto out_put_access;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
5099 spin_lock(&fp->fi_lock);5109 spin_lock(&fp->fi_lock);
5100 if (!fp->fi_fds[oflag]) {5110 if (!fp->fi_fds[oflag]) {
5101 fp->fi_fds[oflag] = nf;5111 fp->fi_fds[oflag] = nf;
fs/nfsd/xdr4.h+1−0
@@ -273,6 +273,7 @@ struct nfsd4_open { · 272 unmodified lines
273 bool op_truncate; /* used during processing */273 bool op_truncate; /* used during processing */
274 bool op_created; /* used during processing */274 bool op_created; /* used during processing */
275 struct nfs4_openowner *op_openowner; /* used during processing */275 struct nfs4_openowner *op_openowner; /* used during processing */
276 struct file *op_filp; /* used during processing */
276 struct nfs4_file *op_file; /* used during processing */277 struct nfs4_file *op_file; /* used during processing */
277 struct nfs4_ol_stateid *op_stp; /* used during processing */278 struct nfs4_ol_stateid *op_stp; /* used during processing */
278 struct nfs4_clnt_odstate *op_odstate; /* used during processing */279 struct nfs4_clnt_odstate *op_odstate; /* used during processing */
fs/open.c+42−0
@@ -981,6 +981,48 @@ struct file *dentry_open(const struct path *path, int flags, · 980 unmodified lines
981}981}
982EXPORT_SYMBOL(dentry_open);982EXPORT_SYMBOL(dentry_open);
983 983
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 */
1001struct 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}
1024EXPORT_SYMBOL(dentry_create);
1025
984struct file *open_with_fake_path(const struct path *path, int flags,1026struct file *open_with_fake_path(const struct path *path, int flags,
985 struct inode *inode, const struct cred *cred)1027 struct inode *inode, const struct cred *cred)
986{1028{
include/linux/fs.h+2−0
@@ -2640,6 +2640,8 @@ static inline struct file *file_open_root_mnt(struct vfsmount *mnt, · 2639 unmodified lines
2640 name, flags, mode);2640 name, flags, mode);
2641}2641}
2642extern struct file * dentry_open(const struct path *, int, const struct cred *);2642extern struct file * dentry_open(const struct path *, int, const struct cred *);
2643extern struct file *dentry_create(const struct path *path, int flags,
2644 umode_t mode, const struct cred *cred);
2643extern struct file * open_with_fake_path(const struct path *, int,2645extern struct file * open_with_fake_path(const struct path *, int,
2644 struct inode*, const struct cred *);2646 struct inode*, const struct cred *);
2645static inline struct file *file_clone_open(struct file *file)2647static inline struct file *file_clone_open(struct file *file)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t