summaryrefslogtreecommitdiff
path: root/fs/namei.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 14:25:37 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 14:25:37 -0800
commit8113b3998d5c96aca885b967e6aa47e428ebc632 (patch)
treec00b315cf155ad33d1a39baffee2f597e370e648 /fs/namei.c
parentc84bb79f70c634a95929f21c14340ab2078d7977 (diff)
parent6ea258d1f6895c61af212473b51477d39b8c99d2 (diff)
downloadlinux-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.c80
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