Merge tag 'vfs-6.18-rc1.async' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs async directory updates from Christian Brauner:
 "This contains further preparatory changes for the asynchronous directory
  locking scheme:

   - Add lookup_one_positive_killable() which allows overlayfs to
     perform lookup that won't block on a fatal signal

   - Unify the mount idmap handling in struct renamedata as a rename can
     only happen within a single mount

   - Introduce kern_path_parent() for audit which sets the path to the
     parent and returns a dentry for the target without holding any
     locks on return

   - Rename kern_path_locked() as it is only used to prepare for the
     removal of an object from the filesystem:

	kern_path_locked()    => start_removing_path()
	kern_path_create()    => start_creating_path()
	user_path_create()    => start_creating_user_path()
	user_path_locked_at() => start_removing_user_path_at()
	done_path_create()    => end_creating_path()
	NA                    => end_removing_path()"

* tag 'vfs-6.18-rc1.async' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  debugfs: rename start_creating() to debugfs_start_creating()
  VFS: rename kern_path_locked() and related functions.
  VFS/audit: introduce kern_path_parent() for audit
  VFS: unify old_mnt_idmap and new_mnt_idmap in renamedata
  VFS: discard err2 in filename_create()
  VFS/ovl: add lookup_one_positive_killable()
This commit is contained in:
Linus Torvalds
2025-09-29 11:55:15 -07:00
20 changed files with 215 additions and 129 deletions

View File

@@ -1285,3 +1285,15 @@ rather than a VMA, as the VMA at this stage is not yet valid.
The vm_area_desc provides the minimum required information for a filesystem The vm_area_desc provides the minimum required information for a filesystem
to initialise state upon memory mapping of a file-backed region, and output to initialise state upon memory mapping of a file-backed region, and output
parameters for the file system to set this state. parameters for the file system to set this state.
---
**mandatory**
Several functions are renamed:
- kern_path_locked -> start_removing_path
- kern_path_create -> start_creating_path
- user_path_create -> start_creating_user_path
- user_path_locked_at -> start_removing_user_path_at
- done_path_create -> end_creating_path

View File

@@ -67,11 +67,11 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
struct dentry *dentry; struct dentry *dentry;
int ret; int ret;
dentry = user_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY); dentry = start_creating_user_path(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY);
ret = PTR_ERR(dentry); ret = PTR_ERR(dentry);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
ret = spufs_create(&path, dentry, flags, mode, neighbor); ret = spufs_create(&path, dentry, flags, mode, neighbor);
done_path_create(&path, dentry); end_creating_path(&path, dentry);
} }
return ret; return ret;

View File

@@ -176,7 +176,7 @@ static int dev_mkdir(const char *name, umode_t mode)
struct dentry *dentry; struct dentry *dentry;
struct path path; struct path path;
dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); dentry = start_creating_path(AT_FDCWD, name, &path, LOOKUP_DIRECTORY);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
@@ -184,7 +184,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (!IS_ERR(dentry)) if (!IS_ERR(dentry))
/* mark as kernel-created inode */ /* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread; d_inode(dentry)->i_private = &thread;
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return PTR_ERR_OR_ZERO(dentry); return PTR_ERR_OR_ZERO(dentry);
} }
@@ -222,10 +222,10 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
struct path path; struct path path;
int err; int err;
dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); dentry = start_creating_path(AT_FDCWD, nodename, &path, 0);
if (dentry == ERR_PTR(-ENOENT)) { if (dentry == ERR_PTR(-ENOENT)) {
create_path(nodename); create_path(nodename);
dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); dentry = start_creating_path(AT_FDCWD, nodename, &path, 0);
} }
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
@@ -246,7 +246,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
/* mark as kernel-created inode */ /* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread; d_inode(dentry)->i_private = &thread;
} }
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return err; return err;
} }
@@ -256,7 +256,7 @@ static int dev_rmdir(const char *name)
struct dentry *dentry; struct dentry *dentry;
int err; int err;
dentry = kern_path_locked(name, &parent); dentry = start_removing_path(name, &parent);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
if (d_inode(dentry)->i_private == &thread) if (d_inode(dentry)->i_private == &thread)
@@ -265,9 +265,7 @@ static int dev_rmdir(const char *name)
else else
err = -EPERM; err = -EPERM;
dput(dentry); end_removing_path(&parent, dentry);
inode_unlock(d_inode(parent.dentry));
path_put(&parent);
return err; return err;
} }
@@ -325,7 +323,7 @@ static int handle_remove(const char *nodename, struct device *dev)
int deleted = 0; int deleted = 0;
int err = 0; int err = 0;
dentry = kern_path_locked(nodename, &parent); dentry = start_removing_path(nodename, &parent);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
@@ -349,10 +347,8 @@ static int handle_remove(const char *nodename, struct device *dev)
if (!err || err == -ENOENT) if (!err || err == -ENOENT)
deleted = 1; deleted = 1;
} }
dput(dentry); end_removing_path(&parent, dentry);
inode_unlock(d_inode(parent.dentry));
path_put(&parent);
if (deleted && strchr(nodename, '/')) if (deleted && strchr(nodename, '/'))
delete_path(nodename); delete_path(nodename);
return err; return err;

View File

@@ -255,7 +255,7 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
snapshot_src = inode_inum(to_bch_ei(src_path.dentry->d_inode)); snapshot_src = inode_inum(to_bch_ei(src_path.dentry->d_inode));
} }
dst_dentry = user_path_create(arg.dirfd, dst_dentry = start_creating_user_path(arg.dirfd,
(const char __user *)(unsigned long)arg.dst_ptr, (const char __user *)(unsigned long)arg.dst_ptr,
&dst_path, lookup_flags); &dst_path, lookup_flags);
error = PTR_ERR_OR_ZERO(dst_dentry); error = PTR_ERR_OR_ZERO(dst_dentry);
@@ -314,7 +314,7 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
d_instantiate(dst_dentry, &inode->v); d_instantiate(dst_dentry, &inode->v);
fsnotify_mkdir(dir, dst_dentry); fsnotify_mkdir(dir, dst_dentry);
err3: err3:
done_path_create(&dst_path, dst_dentry); end_creating_path(&dst_path, dst_dentry);
err2: err2:
if (arg.src_ptr) if (arg.src_ptr)
path_put(&src_path); path_put(&src_path);
@@ -334,7 +334,7 @@ static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
if (arg.flags) if (arg.flags)
return -EINVAL; return -EINVAL;
victim = user_path_locked_at(arg.dirfd, name, &path); victim = start_removing_user_path_at(arg.dirfd, name, &path);
if (IS_ERR(victim)) if (IS_ERR(victim))
return PTR_ERR(victim); return PTR_ERR(victim);
@@ -351,9 +351,7 @@ static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
d_invalidate(victim); d_invalidate(victim);
} }
err: err:
inode_unlock(dir); end_removing_path(&path, victim);
dput(victim);
path_put(&path);
return ret; return ret;
} }

View File

@@ -387,10 +387,9 @@ try_again:
cachefiles_io_error(cache, "Rename security error %d", ret); cachefiles_io_error(cache, "Rename security error %d", ret);
} else { } else {
struct renamedata rd = { struct renamedata rd = {
.old_mnt_idmap = &nop_mnt_idmap, .mnt_idmap = &nop_mnt_idmap,
.old_parent = dir, .old_parent = dir,
.old_dentry = rep, .old_dentry = rep,
.new_mnt_idmap = &nop_mnt_idmap,
.new_parent = cache->graveyard, .new_parent = cache->graveyard,
.new_dentry = grave, .new_dentry = grave,
}; };

View File

@@ -362,7 +362,8 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent)
} }
EXPORT_SYMBOL_GPL(debugfs_lookup); EXPORT_SYMBOL_GPL(debugfs_lookup);
static struct dentry *start_creating(const char *name, struct dentry *parent) static struct dentry *debugfs_start_creating(const char *name,
struct dentry *parent)
{ {
struct dentry *dentry; struct dentry *dentry;
int error; int error;
@@ -428,7 +429,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
if (!(mode & S_IFMT)) if (!(mode & S_IFMT))
mode |= S_IFREG; mode |= S_IFREG;
BUG_ON(!S_ISREG(mode)); BUG_ON(!S_ISREG(mode));
dentry = start_creating(name, parent); dentry = debugfs_start_creating(name, parent);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
@@ -577,7 +578,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_file_size);
*/ */
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
{ {
struct dentry *dentry = start_creating(name, parent); struct dentry *dentry = debugfs_start_creating(name, parent);
struct inode *inode; struct inode *inode;
if (IS_ERR(dentry)) if (IS_ERR(dentry))
@@ -624,7 +625,7 @@ struct dentry *debugfs_create_automount(const char *name,
debugfs_automount_t f, debugfs_automount_t f,
void *data) void *data)
{ {
struct dentry *dentry = start_creating(name, parent); struct dentry *dentry = debugfs_start_creating(name, parent);
struct inode *inode; struct inode *inode;
if (IS_ERR(dentry)) if (IS_ERR(dentry))
@@ -687,7 +688,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
if (!link) if (!link)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dentry = start_creating(name, parent); dentry = debugfs_start_creating(name, parent);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
kfree(link); kfree(link);
return dentry; return dentry;

View File

@@ -634,10 +634,9 @@ ecryptfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
goto out_lock; goto out_lock;
} }
rd.old_mnt_idmap = &nop_mnt_idmap; rd.mnt_idmap = &nop_mnt_idmap;
rd.old_parent = lower_old_dir_dentry; rd.old_parent = lower_old_dir_dentry;
rd.old_dentry = lower_old_dentry; rd.old_dentry = lower_old_dentry;
rd.new_mnt_idmap = &nop_mnt_idmap;
rd.new_parent = lower_new_dir_dentry; rd.new_parent = lower_new_dir_dentry;
rd.new_dentry = lower_new_dentry; rd.new_dentry = lower_new_dentry;
rc = vfs_rename(&rd); rc = vfs_rename(&rd);

View File

@@ -149,7 +149,7 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
else if (!(S_ISBLK(mode) || S_ISCHR(mode))) else if (!(S_ISBLK(mode) || S_ISCHR(mode)))
return -EINVAL; return -EINVAL;
dentry = kern_path_create(AT_FDCWD, filename, &path, 0); dentry = start_creating_path(AT_FDCWD, filename, &path, 0);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
@@ -158,7 +158,7 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
if (!error) if (!error)
error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode, error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode,
dentry, mode, new_decode_dev(dev)); dentry, mode, new_decode_dev(dev));
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return error; return error;
} }
@@ -173,7 +173,7 @@ int __init init_link(const char *oldname, const char *newname)
if (error) if (error)
return error; return error;
new_dentry = kern_path_create(AT_FDCWD, newname, &new_path, 0); new_dentry = start_creating_path(AT_FDCWD, newname, &new_path, 0);
error = PTR_ERR(new_dentry); error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry)) if (IS_ERR(new_dentry))
goto out; goto out;
@@ -191,7 +191,7 @@ int __init init_link(const char *oldname, const char *newname)
error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode, error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode,
new_dentry, NULL); new_dentry, NULL);
out_dput: out_dput:
done_path_create(&new_path, new_dentry); end_creating_path(&new_path, new_dentry);
out: out:
path_put(&old_path); path_put(&old_path);
return error; return error;
@@ -203,14 +203,14 @@ int __init init_symlink(const char *oldname, const char *newname)
struct path path; struct path path;
int error; int error;
dentry = kern_path_create(AT_FDCWD, newname, &path, 0); dentry = start_creating_path(AT_FDCWD, newname, &path, 0);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
error = security_path_symlink(&path, dentry, oldname); error = security_path_symlink(&path, dentry, oldname);
if (!error) if (!error)
error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode, error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
dentry, oldname); dentry, oldname);
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return error; return error;
} }
@@ -225,7 +225,8 @@ int __init init_mkdir(const char *pathname, umode_t mode)
struct path path; struct path path;
int error; int error;
dentry = kern_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY); dentry = start_creating_path(AT_FDCWD, pathname, &path,
LOOKUP_DIRECTORY);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
mode = mode_strip_umask(d_inode(path.dentry), mode); mode = mode_strip_umask(d_inode(path.dentry), mode);
@@ -236,7 +237,7 @@ int __init init_mkdir(const char *pathname, umode_t mode)
if (IS_ERR(dentry)) if (IS_ERR(dentry))
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
} }
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return error; return error;
} }

View File

@@ -1835,6 +1835,20 @@ static struct dentry *lookup_slow(const struct qstr *name,
return res; return res;
} }
static struct dentry *lookup_slow_killable(const struct qstr *name,
struct dentry *dir,
unsigned int flags)
{
struct inode *inode = dir->d_inode;
struct dentry *res;
if (inode_lock_shared_killable(inode))
return ERR_PTR(-EINTR);
res = __lookup_slow(name, dir, flags);
inode_unlock_shared(inode);
return res;
}
static inline int may_lookup(struct mnt_idmap *idmap, static inline int may_lookup(struct mnt_idmap *idmap,
struct nameidata *restrict nd) struct nameidata *restrict nd)
{ {
@@ -2752,7 +2766,8 @@ static int filename_parentat(int dfd, struct filename *name,
} }
/* does lookup, returns the object with parent locked */ /* does lookup, returns the object with parent locked */
static struct dentry *__kern_path_locked(int dfd, struct filename *name, struct path *path) static struct dentry *__start_removing_path(int dfd, struct filename *name,
struct path *path)
{ {
struct path parent_path __free(path_put) = {}; struct path parent_path __free(path_put) = {};
struct dentry *d; struct dentry *d;
@@ -2764,18 +2779,42 @@ static struct dentry *__kern_path_locked(int dfd, struct filename *name, struct
return ERR_PTR(error); return ERR_PTR(error);
if (unlikely(type != LAST_NORM)) if (unlikely(type != LAST_NORM))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
/* don't fail immediately if it's r/o, at least try to report other errors */
error = mnt_want_write(parent_path.mnt);
inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT); inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
d = lookup_one_qstr_excl(&last, parent_path.dentry, 0); d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
if (IS_ERR(d)) { if (IS_ERR(d))
inode_unlock(parent_path.dentry->d_inode); goto unlock;
return d; if (error)
} goto fail;
path->dentry = no_free_ptr(parent_path.dentry); path->dentry = no_free_ptr(parent_path.dentry);
path->mnt = no_free_ptr(parent_path.mnt); path->mnt = no_free_ptr(parent_path.mnt);
return d; return d;
fail:
dput(d);
d = ERR_PTR(error);
unlock:
inode_unlock(parent_path.dentry->d_inode);
if (!error)
mnt_drop_write(parent_path.mnt);
return d;
} }
struct dentry *kern_path_locked_negative(const char *name, struct path *path) /**
* kern_path_parent: lookup path returning parent and target
* @name: path name
* @path: path to store parent in
*
* The path @name should end with a normal component, not "." or ".." or "/".
* A lookup is performed and if successful the parent information
* is store in @parent and the dentry is returned.
*
* The dentry maybe negative, the parent will be positive.
*
* Returns: dentry or error.
*/
struct dentry *kern_path_parent(const char *name, struct path *path)
{ {
struct path parent_path __free(path_put) = {}; struct path parent_path __free(path_put) = {};
struct filename *filename __free(putname) = getname_kernel(name); struct filename *filename __free(putname) = getname_kernel(name);
@@ -2788,35 +2827,35 @@ struct dentry *kern_path_locked_negative(const char *name, struct path *path)
return ERR_PTR(error); return ERR_PTR(error);
if (unlikely(type != LAST_NORM)) if (unlikely(type != LAST_NORM))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
d = lookup_one_qstr_excl(&last, parent_path.dentry, LOOKUP_CREATE); d = lookup_noperm_unlocked(&last, parent_path.dentry);
if (IS_ERR(d)) { if (IS_ERR(d))
inode_unlock(parent_path.dentry->d_inode);
return d; return d;
}
path->dentry = no_free_ptr(parent_path.dentry); path->dentry = no_free_ptr(parent_path.dentry);
path->mnt = no_free_ptr(parent_path.mnt); path->mnt = no_free_ptr(parent_path.mnt);
return d; return d;
} }
struct dentry *kern_path_locked(const char *name, struct path *path) struct dentry *start_removing_path(const char *name, struct path *path)
{ {
struct filename *filename = getname_kernel(name); struct filename *filename = getname_kernel(name);
struct dentry *res = __kern_path_locked(AT_FDCWD, filename, path); struct dentry *res = __start_removing_path(AT_FDCWD, filename, path);
putname(filename); putname(filename);
return res; return res;
} }
struct dentry *user_path_locked_at(int dfd, const char __user *name, struct path *path) struct dentry *start_removing_user_path_at(int dfd,
const char __user *name,
struct path *path)
{ {
struct filename *filename = getname(name); struct filename *filename = getname(name);
struct dentry *res = __kern_path_locked(dfd, filename, path); struct dentry *res = __start_removing_path(dfd, filename, path);
putname(filename); putname(filename);
return res; return res;
} }
EXPORT_SYMBOL(user_path_locked_at); EXPORT_SYMBOL(start_removing_user_path_at);
int kern_path(const char *name, unsigned int flags, struct path *path) int kern_path(const char *name, unsigned int flags, struct path *path)
{ {
@@ -3018,6 +3057,47 @@ struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap, struct qstr *name,
} }
EXPORT_SYMBOL(lookup_one_unlocked); EXPORT_SYMBOL(lookup_one_unlocked);
/**
* lookup_one_positive_killable - lookup single pathname component
* @idmap: idmap of the mount the lookup is performed from
* @name: qstr olding pathname component to lookup
* @base: base directory to lookup from
*
* This helper will yield ERR_PTR(-ENOENT) on negatives. The helper returns
* known positive or ERR_PTR(). This is what most of the users want.
*
* Note that pinned negative with unlocked parent _can_ become positive at any
* time, so callers of lookup_one_unlocked() need to be very careful; pinned
* positives have >d_inode stable, so this one avoids such problems.
*
* This can be used for in-kernel filesystem clients such as file servers.
*
* It should be called without the parent i_rwsem held, and will take
* the i_rwsem itself if necessary. If a fatal signal is pending or
* delivered, it will return %-EINTR if the lock is needed.
*/
struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
struct qstr *name,
struct dentry *base)
{
int err;
struct dentry *ret;
err = lookup_one_common(idmap, name, base);
if (err)
return ERR_PTR(err);
ret = lookup_dcache(name, base, 0);
if (!ret)
ret = lookup_slow_killable(name, base, 0);
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
dput(ret);
ret = ERR_PTR(-ENOENT);
}
return ret;
}
EXPORT_SYMBOL(lookup_one_positive_killable);
/** /**
* lookup_one_positive_unlocked - lookup single pathname component * lookup_one_positive_unlocked - lookup single pathname component
* @idmap: idmap of the mount the lookup is performed from * @idmap: idmap of the mount the lookup is performed from
@@ -4122,7 +4202,6 @@ static struct dentry *filename_create(int dfd, struct filename *name,
unsigned int reval_flag = lookup_flags & LOOKUP_REVAL; unsigned int reval_flag = lookup_flags & LOOKUP_REVAL;
unsigned int create_flags = LOOKUP_CREATE | LOOKUP_EXCL; unsigned int create_flags = LOOKUP_CREATE | LOOKUP_EXCL;
int type; int type;
int err2;
int error; int error;
error = filename_parentat(dfd, name, reval_flag, path, &last, &type); error = filename_parentat(dfd, name, reval_flag, path, &last, &type);
@@ -4137,7 +4216,7 @@ static struct dentry *filename_create(int dfd, struct filename *name,
goto out; goto out;
/* don't fail immediately if it's r/o, at least try to report other errors */ /* don't fail immediately if it's r/o, at least try to report other errors */
err2 = mnt_want_write(path->mnt); error = mnt_want_write(path->mnt);
/* /*
* Do the final lookup. Suppress 'create' if there is a trailing * Do the final lookup. Suppress 'create' if there is a trailing
* '/', and a directory wasn't requested. * '/', and a directory wasn't requested.
@@ -4150,25 +4229,24 @@ static struct dentry *filename_create(int dfd, struct filename *name,
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto unlock; goto unlock;
if (unlikely(err2)) { if (unlikely(error))
error = err2;
goto fail; goto fail;
}
return dentry; return dentry;
fail: fail:
dput(dentry); dput(dentry);
dentry = ERR_PTR(error); dentry = ERR_PTR(error);
unlock: unlock:
inode_unlock(path->dentry->d_inode); inode_unlock(path->dentry->d_inode);
if (!err2) if (!error)
mnt_drop_write(path->mnt); mnt_drop_write(path->mnt);
out: out:
path_put(path); path_put(path);
return dentry; return dentry;
} }
struct dentry *kern_path_create(int dfd, const char *pathname, struct dentry *start_creating_path(int dfd, const char *pathname,
struct path *path, unsigned int lookup_flags) struct path *path, unsigned int lookup_flags)
{ {
struct filename *filename = getname_kernel(pathname); struct filename *filename = getname_kernel(pathname);
struct dentry *res = filename_create(dfd, filename, path, lookup_flags); struct dentry *res = filename_create(dfd, filename, path, lookup_flags);
@@ -4176,9 +4254,9 @@ struct dentry *kern_path_create(int dfd, const char *pathname,
putname(filename); putname(filename);
return res; return res;
} }
EXPORT_SYMBOL(kern_path_create); EXPORT_SYMBOL(start_creating_path);
void done_path_create(struct path *path, struct dentry *dentry) void end_creating_path(struct path *path, struct dentry *dentry)
{ {
if (!IS_ERR(dentry)) if (!IS_ERR(dentry))
dput(dentry); dput(dentry);
@@ -4186,10 +4264,11 @@ void done_path_create(struct path *path, struct dentry *dentry)
mnt_drop_write(path->mnt); mnt_drop_write(path->mnt);
path_put(path); path_put(path);
} }
EXPORT_SYMBOL(done_path_create); EXPORT_SYMBOL(end_creating_path);
inline struct dentry *user_path_create(int dfd, const char __user *pathname, inline struct dentry *start_creating_user_path(
struct path *path, unsigned int lookup_flags) int dfd, const char __user *pathname,
struct path *path, unsigned int lookup_flags)
{ {
struct filename *filename = getname(pathname); struct filename *filename = getname(pathname);
struct dentry *res = filename_create(dfd, filename, path, lookup_flags); struct dentry *res = filename_create(dfd, filename, path, lookup_flags);
@@ -4197,7 +4276,7 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
putname(filename); putname(filename);
return res; return res;
} }
EXPORT_SYMBOL(user_path_create); EXPORT_SYMBOL(start_creating_user_path);
/** /**
* vfs_mknod - create device node or file * vfs_mknod - create device node or file
@@ -4305,7 +4384,7 @@ retry:
break; break;
} }
out2: out2:
done_path_create(&path, dentry); end_creating_path(&path, dentry);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
@@ -4409,7 +4488,7 @@ retry:
if (IS_ERR(dentry)) if (IS_ERR(dentry))
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
} }
done_path_create(&path, dentry); end_creating_path(&path, dentry);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
@@ -4763,7 +4842,7 @@ retry:
if (!error) if (!error)
error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode, error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
dentry, from->name); dentry, from->name);
done_path_create(&path, dentry); end_creating_path(&path, dentry);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
@@ -4932,7 +5011,7 @@ retry:
error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode, error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode,
new_dentry, &delegated_inode); new_dentry, &delegated_inode);
out_dput: out_dput:
done_path_create(&new_path, new_dentry); end_creating_path(&new_path, new_dentry);
if (delegated_inode) { if (delegated_inode) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) { if (!error) {
@@ -5032,20 +5111,20 @@ int vfs_rename(struct renamedata *rd)
if (source == target) if (source == target)
return 0; return 0;
error = may_delete(rd->old_mnt_idmap, old_dir, old_dentry, is_dir); error = may_delete(rd->mnt_idmap, old_dir, old_dentry, is_dir);
if (error) if (error)
return error; return error;
if (!target) { if (!target) {
error = may_create(rd->new_mnt_idmap, new_dir, new_dentry); error = may_create(rd->mnt_idmap, new_dir, new_dentry);
} else { } else {
new_is_dir = d_is_dir(new_dentry); new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE)) if (!(flags & RENAME_EXCHANGE))
error = may_delete(rd->new_mnt_idmap, new_dir, error = may_delete(rd->mnt_idmap, new_dir,
new_dentry, is_dir); new_dentry, is_dir);
else else
error = may_delete(rd->new_mnt_idmap, new_dir, error = may_delete(rd->mnt_idmap, new_dir,
new_dentry, new_is_dir); new_dentry, new_is_dir);
} }
if (error) if (error)
@@ -5060,13 +5139,13 @@ int vfs_rename(struct renamedata *rd)
*/ */
if (new_dir != old_dir) { if (new_dir != old_dir) {
if (is_dir) { if (is_dir) {
error = inode_permission(rd->old_mnt_idmap, source, error = inode_permission(rd->mnt_idmap, source,
MAY_WRITE); MAY_WRITE);
if (error) if (error)
return error; return error;
} }
if ((flags & RENAME_EXCHANGE) && new_is_dir) { if ((flags & RENAME_EXCHANGE) && new_is_dir) {
error = inode_permission(rd->new_mnt_idmap, target, error = inode_permission(rd->mnt_idmap, target,
MAY_WRITE); MAY_WRITE);
if (error) if (error)
return error; return error;
@@ -5134,7 +5213,7 @@ int vfs_rename(struct renamedata *rd)
if (error) if (error)
goto out; goto out;
} }
error = old_dir->i_op->rename(rd->new_mnt_idmap, old_dir, old_dentry, error = old_dir->i_op->rename(rd->mnt_idmap, old_dir, old_dentry,
new_dir, new_dentry, flags); new_dir, new_dentry, flags);
if (error) if (error)
goto out; goto out;
@@ -5277,10 +5356,9 @@ retry_deleg:
rd.old_parent = old_path.dentry; rd.old_parent = old_path.dentry;
rd.old_dentry = old_dentry; rd.old_dentry = old_dentry;
rd.old_mnt_idmap = mnt_idmap(old_path.mnt); rd.mnt_idmap = mnt_idmap(old_path.mnt);
rd.new_parent = new_path.dentry; rd.new_parent = new_path.dentry;
rd.new_dentry = new_dentry; rd.new_dentry = new_dentry;
rd.new_mnt_idmap = mnt_idmap(new_path.mnt);
rd.delegated_inode = &delegated_inode; rd.delegated_inode = &delegated_inode;
rd.flags = flags; rd.flags = flags;
error = vfs_rename(&rd); error = vfs_rename(&rd);

View File

@@ -1951,10 +1951,9 @@ retry:
goto out_dput_old; goto out_dput_old;
} else { } else {
struct renamedata rd = { struct renamedata rd = {
.old_mnt_idmap = &nop_mnt_idmap, .mnt_idmap = &nop_mnt_idmap,
.old_parent = fdentry, .old_parent = fdentry,
.old_dentry = odentry, .old_dentry = odentry,
.new_mnt_idmap = &nop_mnt_idmap,
.new_parent = tdentry, .new_parent = tdentry,
.new_dentry = ndentry, .new_dentry = ndentry,
}; };

View File

@@ -4418,7 +4418,7 @@ int ocfs2_reflink_ioctl(struct inode *inode,
return error; return error;
} }
new_dentry = user_path_create(AT_FDCWD, newname, &new_path, 0); new_dentry = start_creating_user_path(AT_FDCWD, newname, &new_path, 0);
error = PTR_ERR(new_dentry); error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry)) { if (IS_ERR(new_dentry)) {
mlog_errno(error); mlog_errno(error);
@@ -4435,7 +4435,7 @@ int ocfs2_reflink_ioctl(struct inode *inode,
d_inode(new_path.dentry), d_inode(new_path.dentry),
new_dentry, preserve); new_dentry, preserve);
out_dput: out_dput:
done_path_create(&new_path, new_dentry); end_creating_path(&new_path, new_dentry);
out: out:
path_put(&old_path); path_put(&old_path);

View File

@@ -361,10 +361,9 @@ static inline int ovl_do_rename(struct ovl_fs *ofs, struct dentry *olddir,
{ {
int err; int err;
struct renamedata rd = { struct renamedata rd = {
.old_mnt_idmap = ovl_upper_mnt_idmap(ofs), .mnt_idmap = ovl_upper_mnt_idmap(ofs),
.old_parent = olddir, .old_parent = olddir,
.old_dentry = olddentry, .old_dentry = olddentry,
.new_mnt_idmap = ovl_upper_mnt_idmap(ofs),
.new_parent = newdir, .new_parent = newdir,
.new_dentry = newdentry, .new_dentry = newdentry,
.flags = flags, .flags = flags,

View File

@@ -270,26 +270,26 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name,
static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd) static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd)
{ {
int err; int err = 0;
struct dentry *dentry, *dir = path->dentry; struct dentry *dentry, *dir = path->dentry;
const struct cred *old_cred; const struct cred *old_cred;
old_cred = ovl_override_creds(rdd->dentry->d_sb); old_cred = ovl_override_creds(rdd->dentry->d_sb);
err = down_write_killable(&dir->d_inode->i_rwsem); while (rdd->first_maybe_whiteout) {
if (!err) { struct ovl_cache_entry *p =
while (rdd->first_maybe_whiteout) { rdd->first_maybe_whiteout;
struct ovl_cache_entry *p = rdd->first_maybe_whiteout = p->next_maybe_whiteout;
rdd->first_maybe_whiteout; dentry = lookup_one_positive_killable(mnt_idmap(path->mnt),
rdd->first_maybe_whiteout = p->next_maybe_whiteout; &QSTR_LEN(p->name, p->len),
dentry = lookup_one(mnt_idmap(path->mnt), dir);
&QSTR_LEN(p->name, p->len), dir); if (!IS_ERR(dentry)) {
if (!IS_ERR(dentry)) { p->is_whiteout = ovl_is_whiteout(dentry);
p->is_whiteout = ovl_is_whiteout(dentry); dput(dentry);
dput(dentry); } else if (PTR_ERR(dentry) == -EINTR) {
} err = -EINTR;
break;
} }
inode_unlock(dir->d_inode);
} }
ovl_revert_creds(old_cred); ovl_revert_creds(old_cred);

View File

@@ -196,7 +196,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
pr_err("File(%s): creation failed (err:%d)\n", name, err); pr_err("File(%s): creation failed (err:%d)\n", name, err);
} }
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return err; return err;
} }
@@ -237,7 +237,7 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
if (!err && dentry != d) if (!err && dentry != d)
ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry));
done_path_create(&path, dentry); end_creating_path(&path, dentry);
if (err) if (err)
pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
return err; return err;
@@ -669,7 +669,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
ksmbd_debug(VFS, "vfs_link failed err %d\n", err); ksmbd_debug(VFS, "vfs_link failed err %d\n", err);
out3: out3:
done_path_create(&newpath, dentry); end_creating_path(&newpath, dentry);
out2: out2:
path_put(&oldpath); path_put(&oldpath);
out1: out1:
@@ -770,10 +770,9 @@ retry:
goto out4; goto out4;
} }
rd.old_mnt_idmap = mnt_idmap(old_path->mnt), rd.mnt_idmap = mnt_idmap(old_path->mnt),
rd.old_parent = old_parent, rd.old_parent = old_parent,
rd.old_dentry = old_child, rd.old_dentry = old_child,
rd.new_mnt_idmap = mnt_idmap(new_path.mnt),
rd.new_parent = new_path.dentry, rd.new_parent = new_path.dentry,
rd.new_dentry = new_dentry, rd.new_dentry = new_dentry,
rd.flags = flags, rd.flags = flags,
@@ -1326,7 +1325,7 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
if (!abs_name) if (!abs_name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dent = kern_path_create(AT_FDCWD, abs_name, path, flags); dent = start_creating_path(AT_FDCWD, abs_name, path, flags);
kfree(abs_name); kfree(abs_name);
return dent; return dent;
} }

View File

@@ -2118,20 +2118,18 @@ int vfs_unlink(struct mnt_idmap *, struct inode *, struct dentry *,
/** /**
* struct renamedata - contains all information required for renaming * struct renamedata - contains all information required for renaming
* @old_mnt_idmap: idmap of the old mount the inode was found from * @mnt_idmap: idmap of the mount in which the rename is happening.
* @old_parent: parent of source * @old_parent: parent of source
* @old_dentry: source * @old_dentry: source
* @new_mnt_idmap: idmap of the new mount the inode was found from
* @new_parent: parent of destination * @new_parent: parent of destination
* @new_dentry: destination * @new_dentry: destination
* @delegated_inode: returns an inode needing a delegation break * @delegated_inode: returns an inode needing a delegation break
* @flags: rename flags * @flags: rename flags
*/ */
struct renamedata { struct renamedata {
struct mnt_idmap *old_mnt_idmap; struct mnt_idmap *mnt_idmap;
struct dentry *old_parent; struct dentry *old_parent;
struct dentry *old_dentry; struct dentry *old_dentry;
struct mnt_idmap *new_mnt_idmap;
struct dentry *new_parent; struct dentry *new_parent;
struct dentry *new_dentry; struct dentry *new_dentry;
struct inode **delegated_inode; struct inode **delegated_inode;

View File

@@ -57,13 +57,17 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
struct dentry *base, struct dentry *base,
unsigned int flags); unsigned int flags);
extern int kern_path(const char *, unsigned, struct path *); extern int kern_path(const char *, unsigned, struct path *);
struct dentry *kern_path_parent(const char *name, struct path *parent);
extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int); extern struct dentry *start_creating_path(int, const char *, struct path *, unsigned int);
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); extern struct dentry *start_creating_user_path(int, const char __user *, struct path *, unsigned int);
extern void done_path_create(struct path *, struct dentry *); extern void end_creating_path(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *start_removing_path(const char *, struct path *);
extern struct dentry *kern_path_locked_negative(const char *, struct path *); extern struct dentry *start_removing_user_path_at(int , const char __user *, struct path *);
extern struct dentry *user_path_locked_at(int , const char __user *, struct path *); static inline void end_removing_path(struct path *path , struct dentry *dentry)
{
end_creating_path(path, dentry);
}
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
struct path *parent, struct qstr *last, int *type, struct path *parent, struct qstr *last, int *type,
const struct path *root); const struct path *root);
@@ -80,6 +84,9 @@ struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap, struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
struct qstr *name, struct qstr *name,
struct dentry *base); struct dentry *base);
struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
struct qstr *name,
struct dentry *base);
extern int follow_down_one(struct path *); extern int follow_down_one(struct path *);
extern int follow_down(struct path *path, unsigned int flags); extern int follow_down(struct path *path, unsigned int flags);

View File

@@ -76,17 +76,18 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa
struct audit_fsnotify_mark *audit_mark; struct audit_fsnotify_mark *audit_mark;
struct path path; struct path path;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode;
int ret; int ret;
if (pathname[0] != '/' || pathname[len-1] == '/') if (pathname[0] != '/' || pathname[len-1] == '/')
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
dentry = kern_path_locked(pathname, &path); dentry = kern_path_parent(pathname, &path);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return ERR_CAST(dentry); /* returning an error */ return ERR_CAST(dentry); /* returning an error */
inode = path.dentry->d_inode; if (d_really_is_negative(dentry)) {
inode_unlock(inode); audit_mark = ERR_PTR(-ENOENT);
goto out;
}
audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL); audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL);
if (unlikely(!audit_mark)) { if (unlikely(!audit_mark)) {
@@ -100,7 +101,7 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa
audit_update_mark(audit_mark, dentry->d_inode); audit_update_mark(audit_mark, dentry->d_inode);
audit_mark->rule = krule; audit_mark->rule = krule;
ret = fsnotify_add_inode_mark(&audit_mark->mark, inode, 0); ret = fsnotify_add_inode_mark(&audit_mark->mark, path.dentry->d_inode, 0);
if (ret < 0) { if (ret < 0) {
audit_mark->path = NULL; audit_mark->path = NULL;
fsnotify_put_mark(&audit_mark->mark); fsnotify_put_mark(&audit_mark->mark);

View File

@@ -349,7 +349,7 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
{ {
struct dentry *d; struct dentry *d;
d = kern_path_locked_negative(watch->path, parent); d = kern_path_parent(watch->path, parent);
if (IS_ERR(d)) if (IS_ERR(d))
return PTR_ERR(d); return PTR_ERR(d);
@@ -359,7 +359,6 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
watch->ino = d_backing_inode(d)->i_ino; watch->ino = d_backing_inode(d)->i_ino;
} }
inode_unlock(d_backing_inode(parent->dentry));
dput(d); dput(d);
return 0; return 0;
} }

View File

@@ -442,7 +442,7 @@ static int bpf_obj_do_pin(int path_fd, const char __user *pathname, void *raw,
umode_t mode; umode_t mode;
int ret; int ret;
dentry = user_path_create(path_fd, pathname, &path, 0); dentry = start_creating_user_path(path_fd, pathname, &path, 0);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
@@ -471,7 +471,7 @@ static int bpf_obj_do_pin(int path_fd, const char __user *pathname, void *raw,
ret = -EPERM; ret = -EPERM;
} }
out: out:
done_path_create(&path, dentry); end_creating_path(&path, dentry);
return ret; return ret;
} }

View File

@@ -1387,7 +1387,7 @@ static int unix_bind_bsd(struct sock *sk, struct sockaddr_un *sunaddr,
* Get the parent directory, calculate the hash for last * Get the parent directory, calculate the hash for last
* component. * component.
*/ */
dentry = kern_path_create(AT_FDCWD, addr->name->sun_path, &parent, 0); dentry = start_creating_path(AT_FDCWD, addr->name->sun_path, &parent, 0);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
err = PTR_ERR(dentry); err = PTR_ERR(dentry);
goto out; goto out;
@@ -1417,7 +1417,7 @@ static int unix_bind_bsd(struct sock *sk, struct sockaddr_un *sunaddr,
unix_table_double_unlock(net, old_hash, new_hash); unix_table_double_unlock(net, old_hash, new_hash);
unix_insert_bsd_socket(sk); unix_insert_bsd_socket(sk);
mutex_unlock(&u->bindlock); mutex_unlock(&u->bindlock);
done_path_create(&parent, dentry); end_creating_path(&parent, dentry);
return 0; return 0;
out_unlock: out_unlock:
@@ -1427,7 +1427,7 @@ out_unlink:
/* failed after successful mknod? unlink what we'd created... */ /* failed after successful mknod? unlink what we'd created... */
vfs_unlink(idmap, d_inode(parent.dentry), dentry, NULL); vfs_unlink(idmap, d_inode(parent.dentry), dentry, NULL);
out_path: out_path:
done_path_create(&parent, dentry); end_creating_path(&parent, dentry);
out: out:
unix_release_addr(addr); unix_release_addr(addr);
return err == -EEXIST ? -EADDRINUSE : err; return err == -EEXIST ? -EADDRINUSE : err;