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

Pull vfs directory updates from Christian Brauner:
 "Recently 'start_creating', 'start_removing', 'start_renaming' and
  related interfaces were added which combine the locking and the
  lookup.

  At that time many callers were changed to use the new interfaces.
  However there are still an assortment of places out side of the core
  vfs where the directory is locked explictly, whether with inode_lock()
  or lock_rename() or similar. These were missed in the first pass for
  an assortment of uninteresting reasons.

  This addresses the remaining places where explicit locking is used,
  and changes them to use the new interfaces, or otherwise removes the
  explicit locking.

  The biggest changes are in overlayfs. The other changes are quite
  simple, though maybe the cachefiles changes is the least simple of
  those"

* tag 'vfs-7.1-rc1.directory' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  VFS: unexport lock_rename(), lock_rename_child(), unlock_rename()
  ovl: remove ovl_lock_rename_workdir()
  ovl: use is_subdir() for testing if one thing is a subdir of another
  ovl: change ovl_create_real() to get a new lock when re-opening created file.
  ovl: pass name buffer to ovl_start_creating_temp()
  cachefiles: change cachefiles_bury_object to use start_renaming_dentry()
  ovl: Simplify ovl_lookup_real_one()
  VFS: make lookup_one_qstr_excl() static.
  nfsd: switch purge_old() to use start_removing_noperm()
  selinux: Use simple_start_creating() / simple_done_creating()
  Apparmor: Use simple_start_creating() / simple_done_creating()
  libfs: change simple_done_creating() to use end_creating()
  VFS: move the start_dirop() kerndoc comment to before start_dirop()
  fs/proc: Don't lock root inode when creating "self" and "thread-self"
  VFS: note error returns in documentation for various lookup functions
This commit is contained in:
Linus Torvalds
2026-04-13 10:24:33 -07:00
15 changed files with 190 additions and 244 deletions

View File

@@ -66,10 +66,9 @@ void ovl_tempname(char name[OVL_TEMPNAME_SIZE])
}
static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs,
struct dentry *workdir)
struct dentry *workdir,
char name[OVL_TEMPNAME_SIZE])
{
char name[OVL_TEMPNAME_SIZE];
ovl_tempname(name);
return start_creating(ovl_upper_mnt_idmap(ofs), workdir,
&QSTR(name));
@@ -81,11 +80,12 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
struct dentry *whiteout, *link;
struct dentry *workdir = ofs->workdir;
struct inode *wdir = workdir->d_inode;
char name[OVL_TEMPNAME_SIZE];
guard(mutex)(&ofs->whiteout_lock);
if (!ofs->whiteout) {
whiteout = ovl_start_creating_temp(ofs, workdir);
whiteout = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(whiteout))
return whiteout;
err = ovl_do_whiteout(ofs, wdir, whiteout);
@@ -97,7 +97,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
}
if (!ofs->no_shared_whiteout) {
link = ovl_start_creating_temp(ofs, workdir);
link = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(link))
return link;
err = ovl_do_link(ofs, ofs->whiteout, wdir, link);
@@ -159,7 +159,8 @@ kill_whiteout:
}
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
struct dentry *newdentry, struct ovl_cattr *attr)
struct dentry *newdentry, struct qstr *qname,
struct ovl_cattr *attr)
{
struct inode *dir = parent->d_inode;
int err;
@@ -221,19 +222,30 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
struct dentry *d;
/*
* Some filesystems (i.e. casefolded) may return an unhashed
* negative dentry from the ovl_lookup_upper() call before
* negative dentry from the ovl_start_creating_upper() call before
* ovl_create_real().
* In that case, lookup again after making the newdentry
* positive, so ovl_create_upper() always returns a hashed
* positive dentry.
* positive dentry. We lookup using qname which should be
* the same name as newentry, but is certain not to change.
* As we have to drop the lock before the lookup a race
* could result in a lookup failure. In that case we return
* an error.
*/
d = ovl_lookup_upper(ofs, newdentry->d_name.name, parent,
newdentry->d_name.len);
dput(newdentry);
if (IS_ERR_OR_NULL(d))
end_creating_keep(newdentry);
d = ovl_start_creating_upper(ofs, parent, qname);
if (IS_ERR_OR_NULL(d)) {
err = d ? PTR_ERR(d) : -ENOENT;
else
} else if (d->d_inode != newdentry->d_inode) {
err = -EIO;
} else {
dput(newdentry);
return d;
}
end_creating(d);
dput(newdentry);
return ERR_PTR(err);
}
out:
if (err) {
@@ -247,10 +259,12 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
struct ovl_cattr *attr)
{
struct dentry *ret;
ret = ovl_start_creating_temp(ofs, workdir);
char name[OVL_TEMPNAME_SIZE];
ret = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(ret))
return ret;
ret = ovl_create_real(ofs, workdir, ret, attr);
ret = ovl_create_real(ofs, workdir, ret, &QSTR(name), attr);
return end_creating_keep(ret);
}
@@ -350,14 +364,15 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *newdentry;
struct qstr qname = QSTR_LEN(dentry->d_name.name,
dentry->d_name.len);
int err;
newdentry = ovl_start_creating_upper(ofs, upperdir,
&QSTR_LEN(dentry->d_name.name,
dentry->d_name.len));
&qname);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);
newdentry = ovl_create_real(ofs, upperdir, newdentry, attr);
newdentry = ovl_create_real(ofs, upperdir, newdentry, &qname, attr);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);

View File

@@ -349,69 +349,64 @@ static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx)
return NULL;
}
/*
* Lookup a child overlay dentry to get a connected overlay dentry whose real
* dentry is @real. If @real is on upper layer, we lookup a child overlay
* dentry with the same name as the real dentry. Otherwise, we need to consult
* index for lookup.
/**
* ovl_lookup_real_one - Lookup a child overlay dentry to get an overlay dentry whose real dentry is given
* @connected: parent overlay dentry
* @real: given child real dentry
* @layer: layer in which @real exists
*
*
* Lookup a child overlay dentry in @connected with the same name as the @real
* dentry. Then check that the parent of the result is the real dentry for
* @connected, and @real is the real dentry for the result.
*
* Returns:
* %-ECHILD if the parent of @real is no longer the real dentry for @connected.
* %-ESTALE if @real is not the real dentry of the found dentry.
* Otherwise the found dentry is returned.
*/
static struct dentry *ovl_lookup_real_one(struct dentry *connected,
struct dentry *real,
const struct ovl_layer *layer)
{
struct inode *dir = d_inode(connected);
struct dentry *this, *parent = NULL;
struct dentry *this;
struct name_snapshot name;
int err;
/*
* Lookup child overlay dentry by real name. The dir mutex protects us
* from racing with overlay rename. If the overlay dentry that is above
* real has already been moved to a parent that is not under the
* connected overlay dir, we return -ECHILD and restart the lookup of
* connected real path from the top.
*/
inode_lock_nested(dir, I_MUTEX_PARENT);
err = -ECHILD;
parent = dget_parent(real);
if (ovl_dentry_real_at(connected, layer->idx) != parent)
goto fail;
/*
* We also need to take a snapshot of real dentry name to protect us
* We need to take a snapshot of real dentry name to protect us
* from racing with underlying layer rename. In this case, we don't
* care about returning ESTALE, only from dereferencing a free name
* pointer because we hold no lock on the real dentry.
*/
take_dentry_name_snapshot(&name, real);
/*
* No idmap handling here: it's an internal lookup.
*/
this = lookup_noperm(&name.name, connected);
this = lookup_noperm_unlocked(&name.name, connected);
release_dentry_name_snapshot(&name);
err = PTR_ERR(this);
if (IS_ERR(this)) {
goto fail;
} else if (!this || !this->d_inode) {
dput(this);
err = -ENOENT;
goto fail;
} else if (ovl_dentry_real_at(this, layer->idx) != real) {
dput(this);
err = -ESTALE;
goto fail;
}
out:
dput(parent);
inode_unlock(dir);
err = -ECHILD;
if (ovl_dentry_real_at(connected, layer->idx) != real->d_parent)
goto fail;
err = PTR_ERR(this);
if (IS_ERR(this))
goto fail;
err = -ENOENT;
if (!this || !this->d_inode)
goto fail;
err = -ESTALE;
if (ovl_dentry_real_at(this, layer->idx) != real)
goto fail;
return this;
fail:
pr_warn_ratelimited("failed to lookup one by real (%pd2, layer=%d, connected=%pd2, err=%i)\n",
real, layer->idx, connected, err);
this = ERR_PTR(err);
goto out;
if (!IS_ERR(this))
dput(this);
return ERR_PTR(err);
}
static struct dentry *ovl_lookup_real(struct super_block *sb,

View File

@@ -412,13 +412,6 @@ static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
return file;
}
static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
const char *name,
struct dentry *base, int len)
{
return lookup_one(ovl_upper_mnt_idmap(ofs), &QSTR_LEN(name, len), base);
}
static inline struct dentry *ovl_lookup_upper_unlocked(struct ovl_fs *ofs,
const char *name,
struct dentry *base,
@@ -582,8 +575,6 @@ bool ovl_is_inuse(struct dentry *dentry);
bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *work,
struct dentry *upperdir, struct dentry *upper);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
struct ovl_metacopy *data);
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d,
@@ -909,6 +900,7 @@ struct ovl_cattr {
struct dentry *ovl_create_real(struct ovl_fs *ofs,
struct dentry *parent, struct dentry *newdentry,
struct qstr *qname,
struct ovl_cattr *attr);
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
#define OVL_TEMPNAME_SIZE 20

View File

@@ -451,18 +451,13 @@ static int ovl_lower_dir(const char *name, const struct path *path,
return 0;
}
/* Workdir should not be subdir of upperdir and vice versa */
/*
* Workdir should not be subdir of upperdir and vice versa, and
* they should not be the same.
*/
static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
{
bool ok = false;
if (workdir != upperdir) {
struct dentry *trap = lock_rename(workdir, upperdir);
if (!IS_ERR(trap))
unlock_rename(workdir, upperdir);
ok = (trap == NULL);
}
return ok;
return !is_subdir(workdir, upperdir) && !is_subdir(upperdir, workdir);
}
static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
@@ -634,6 +629,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
if (!IS_ERR(child)) {
if (!child->d_inode)
child = ovl_create_real(ofs, parent, child,
&QSTR(name),
OVL_CATTR(mode));
end_creating_keep(child);
}

View File

@@ -1216,31 +1216,6 @@ void ovl_nlink_end(struct dentry *dentry)
ovl_inode_unlock(inode);
}
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *work,
struct dentry *upperdir, struct dentry *upper)
{
struct dentry *trap;
/* Workdir should not be subdir of upperdir and vice versa */
trap = lock_rename(workdir, upperdir);
if (IS_ERR(trap))
goto err;
if (trap)
goto err_unlock;
if (work && (work->d_parent != workdir || d_unhashed(work)))
goto err_unlock;
if (upper && (upper->d_parent != upperdir || d_unhashed(upper)))
goto err_unlock;
return 0;
err_unlock:
unlock_rename(workdir, upperdir);
err:
pr_err("failed to lock workdir+upperdir\n");
return -EIO;
}
/*
* err < 0, 0 if no metacopy xattr, metacopy data size if xattr found.
* an empty xattr returns OVL_METACOPY_MIN_SIZE to distinguish from no xattr value.