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
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 @@ open_file: · 88 unmodified lines
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 @@ open_file: · 13 unmodified lines
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+2−0
@@ -59,5 +59,7 @@ void nfsd_file_close_inode_sync(struct inode *inode); · 58 unmodified lines
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+39−4
@@ -243,6 +243,37 @@ static inline bool nfsd4_create_is_exclusive(int createmode) · 242 unmodified lines
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, · 106 unmodified lines
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, · 151 unmodified lines
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, · 90 unmodified lines
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+13−3
@@ -5093,9 +5093,19 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, · 5092 unmodified lines
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+1−0
@@ -273,6 +273,7 @@ struct nfsd4_open { · 272 unmodified lines
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+42−0
@@ -981,6 +981,48 @@ struct file *dentry_open(const struct path *path, int flags, · 980 unmodified lines
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+2−0
@@ -2640,6 +2640,8 @@ static inline struct file *file_open_root_mnt(struct vfsmount *mnt, · 2639 unmodified lines
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)

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