diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 14:25:37 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 14:25:37 -0800 |
| commit | 8113b3998d5c96aca885b967e6aa47e428ebc632 (patch) | |
| tree | c00b315cf155ad33d1a39baffee2f597e370e648 | |
| parent | c84bb79f70c634a95929f21c14340ab2078d7977 (diff) | |
| parent | 6ea258d1f6895c61af212473b51477d39b8c99d2 (diff) | |
Merge tag 'vfs-7.0-rc1.atomic_open' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs atomic_open updates from Christian Brauner:
"Allow knfsd to use atomic_open()
While knfsd offers combined exclusive create and open results to
clients, on some filesystems those results are not atomic. The
separate vfs_create() + vfs_open() sequence in dentry_create() can
produce races and unexpected errors. For example, open O_CREAT with
mode 0 will succeed in creating the file but return -EACCES from
vfs_open(). Additionally, network filesystems benefit from reducing
remote round-trip operations by using a single atomic_open() call.
Teach dentry_create() -- whose sole caller is knfsd -- to use
atomic_open() for filesystems that support it"
* tag 'vfs-7.0-rc1.atomic_open' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
fs/namei: fix kernel-doc markup for dentry_create
VFS/knfsd: Teach dentry_create() to use atomic_open()
VFS: Prepare atomic_open() for dentry_create()
VFS: move dentry_create() from fs/open.c to fs/namei.c
| -rw-r--r-- | fs/namei.c | 80 | ||||
| -rw-r--r-- | fs/nfsd/nfs4proc.c | 11 | ||||
| -rw-r--r-- | fs/open.c | 39 | ||||
| -rw-r--r-- | include/linux/fs.h | 2 |
4 files changed, 82 insertions, 50 deletions
diff --git a/fs/namei.c b/fs/namei.c index 3e5884f46faf..76bc569ace8e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4290,19 +4290,16 @@ static int may_o_create(struct mnt_idmap *idmap, * * Returns an error code otherwise. */ -static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry, +static struct dentry *atomic_open(const struct path *path, struct dentry *dentry, struct file *file, int open_flag, umode_t mode) { struct dentry *const DENTRY_NOT_SET = (void *) -1UL; - struct inode *dir = nd->path.dentry->d_inode; + struct inode *dir = path->dentry->d_inode; int error; - if (nd->flags & LOOKUP_DIRECTORY) - open_flag |= O_DIRECTORY; - file->__f_path.dentry = DENTRY_NOT_SET; - file->__f_path.mnt = nd->path.mnt; + file->__f_path.mnt = path->mnt; error = dir->i_op->atomic_open(dir, dentry, file, open_to_namei_flags(open_flag), mode); d_lookup_done(dentry); @@ -4414,7 +4411,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (create_error) open_flag &= ~O_CREAT; if (dir_inode->i_op->atomic_open) { - dentry = atomic_open(nd, dentry, file, open_flag, mode); + if (nd->flags & LOOKUP_DIRECTORY) + open_flag |= O_DIRECTORY; + dentry = atomic_open(&nd->path, dentry, file, open_flag, mode); if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT)) dentry = ERR_PTR(create_error); return dentry; @@ -4948,6 +4947,73 @@ inline struct dentry *start_creating_user_path( } EXPORT_SYMBOL(start_creating_user_path); +/** + * dentry_create - Create and open a file + * @path: path to create + * @flags: O\_ flags + * @mode: mode bits for new file + * @cred: credentials to use + * + * Caller must hold the parent directory's lock, and have prepared + * a negative dentry, placed in @path->dentry, for the new file. + * + * Caller sets @path->mnt to the vfsmount of the filesystem where + * the new file is to be created. The parent directory and the + * negative dentry must reside on the same filesystem instance. + * + * On success, returns a ``struct file *``. Otherwise an ERR_PTR + * is returned. + */ +struct file *dentry_create(struct path *path, int flags, umode_t mode, + const struct cred *cred) +{ + struct file *file __free(fput) = NULL; + struct dentry *dentry = path->dentry; + struct dentry *dir = dentry->d_parent; + struct inode *dir_inode = d_inode(dir); + struct mnt_idmap *idmap; + int error, create_error; + + file = alloc_empty_file(flags, cred); + if (IS_ERR(file)) + return file; + + idmap = mnt_idmap(path->mnt); + + if (dir_inode->i_op->atomic_open) { + path->dentry = dir; + mode = vfs_prepare_mode(idmap, dir_inode, mode, S_IALLUGO, S_IFREG); + + create_error = may_o_create(idmap, path, dentry, mode); + if (create_error) + flags &= ~O_CREAT; + + dentry = atomic_open(path, dentry, file, flags, mode); + error = PTR_ERR_OR_ZERO(dentry); + + if (unlikely(create_error) && error == -ENOENT) + error = create_error; + + if (!error) { + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir->d_inode, dentry); + if (file->f_mode & FMODE_OPENED) + fsnotify_open(file); + } + + path->dentry = dentry; + + } else { + error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL); + if (!error) + error = vfs_open(path, file); + } + if (unlikely(error)) + return ERR_PTR(error); + + return no_free_ptr(file); +} +EXPORT_SYMBOL(dentry_create); /** * vfs_mknod - create device node or file diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9ec08dd4fe82..e400f3beef61 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -194,7 +194,7 @@ static inline bool nfsd4_create_is_exclusive(int createmode) } static __be32 -nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, +nfsd4_vfs_create(struct svc_fh *fhp, struct dentry **child, struct nfsd4_open *open) { struct file *filp; @@ -202,6 +202,9 @@ 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)) + oflags |= O_EXCL; + switch (open->op_share_access & NFS4_SHARE_ACCESS_BOTH) { case NFS4_SHARE_ACCESS_WRITE: oflags |= O_WRONLY; @@ -214,9 +217,11 @@ nfsd4_vfs_create(struct svc_fh *fhp, struct dentry *child, } path.mnt = fhp->fh_export->ex_path.mnt; - path.dentry = child; + path.dentry = *child; filp = dentry_create(&path, oflags, open->op_iattr.ia_mode, current_cred()); + *child = path.dentry; + if (IS_ERR(filp)) return nfserrno(PTR_ERR(filp)); @@ -350,7 +355,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, status = fh_fill_pre_attrs(fhp); if (status != nfs_ok) goto out; - status = nfsd4_vfs_create(fhp, child, open); + status = nfsd4_vfs_create(fhp, &child, open); if (status != nfs_ok) goto out; open->op_created = true; diff --git a/fs/open.c b/fs/open.c index f328622061c5..74c4c1462b3e 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1142,45 +1142,6 @@ struct file *dentry_open_nonotify(const struct path *path, int flags, } /** - * dentry_create - Create and open a file - * @path: path to create - * @flags: O_ flags - * @mode: mode bits for new file - * @cred: credentials to use - * - * Caller must hold the parent directory's lock, and have prepared - * a negative dentry, placed in @path->dentry, for the new file. - * - * Caller sets @path->mnt to the vfsmount of the filesystem where - * the new file is to be created. The parent directory and the - * negative dentry must reside on the same filesystem instance. - * - * On success, returns a "struct file *". Otherwise a ERR_PTR - * is returned. - */ -struct file *dentry_create(const struct path *path, int flags, umode_t mode, - const struct cred *cred) -{ - struct file *f; - int error; - - f = alloc_empty_file(flags, cred); - if (IS_ERR(f)) - return f; - - error = vfs_create(mnt_idmap(path->mnt), path->dentry, mode, NULL); - if (!error) - error = vfs_open(path, f); - - if (unlikely(error)) { - fput(f); - return ERR_PTR(error); - } - return f; -} -EXPORT_SYMBOL(dentry_create); - -/** * kernel_file_open - open a file for kernel internal use * @path: path of the file to open * @flags: open flags diff --git a/include/linux/fs.h b/include/linux/fs.h index a3ff30bccf08..01f73f1eeb7b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2467,7 +2467,7 @@ struct file *dentry_open(const struct path *path, int flags, const struct cred *creds); struct file *dentry_open_nonotify(const struct path *path, int flags, const struct cred *cred); -struct file *dentry_create(const struct path *path, int flags, umode_t mode, +struct file *dentry_create(struct path *path, int flags, umode_t mode, const struct cred *cred); const struct path *backing_file_user_path(const struct file *f); |
