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 /fs/namei.c | |
| parent | c84bb79f70c634a95929f21c14340ab2078d7977 (diff) | |
| parent | 6ea258d1f6895c61af212473b51477d39b8c99d2 (diff) | |
| download | linux-next-8113b3998d5c96aca885b967e6aa47e428ebc632.tar.gz linux-next-8113b3998d5c96aca885b967e6aa47e428ebc632.zip | |
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
Diffstat (limited to 'fs/namei.c')
| -rw-r--r-- | fs/namei.c | 80 |
1 files changed, 73 insertions, 7 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 |
