summaryrefslogtreecommitdiff
path: root/fs/overlayfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-01-12 07:00:22 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2024-01-12 07:00:22 +0300
commitbf4e7080aeed29354cb156a8eb5d221ab2b6a8cc (patch)
treee73826bb1b0b9170a3f410ce622bf62774ecdbf1 /fs/overlayfs
parent2f444347a8d6b03b4e6a9aeff13d67b8cbbe08ce (diff)
parenta8b0026847b8c43445c921ad2c85521c92eb175f (diff)
downloadlinux-bf4e7080aeed29354cb156a8eb5d221ab2b6a8cc.tar.xz
Merge tag 'pull-rename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull rename updates from Al Viro: "Fix directory locking scheme on rename This was broken in 6.5; we really can't lock two unrelated directories without holding ->s_vfs_rename_mutex first and in case of same-parent rename of a subdirectory 6.5 ends up doing just that" * tag 'pull-rename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: rename(): avoid a deadlock in the case of parents having no common ancestor kill lock_two_inodes() rename(): fix the locking of subdirectories f2fs: Avoid reading renamed directory if parent does not change ext4: don't access the source subdirectory content on same-directory rename ext2: Avoid reading renamed directory if parent does not change udf_rename(): only access the child content on cross-directory rename ocfs2: Avoid touching renamed directory if parent does not change reiserfs: Avoid touching renamed directory if parent does not change
Diffstat (limited to 'fs/overlayfs')
-rw-r--r--fs/overlayfs/copy_up.c9
-rw-r--r--fs/overlayfs/dir.c4
-rw-r--r--fs/overlayfs/super.c6
-rw-r--r--fs/overlayfs/util.c7
4 files changed, 20 insertions, 6 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 696478f09cc1..b8e25ca51016 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -744,7 +744,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
struct inode *inode;
struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
struct path path = { .mnt = ovl_upper_mnt(ofs) };
- struct dentry *temp, *upper;
+ struct dentry *temp, *upper, *trap;
struct ovl_cu_creds cc;
int err;
struct ovl_cattr cattr = {
@@ -781,11 +781,13 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
* temp wasn't moved before copy up completion or cleanup.
*/
ovl_start_write(c->dentry);
- if (lock_rename(c->workdir, c->destdir) != NULL ||
- temp->d_parent != c->workdir) {
+ trap = lock_rename(c->workdir, c->destdir);
+ if (trap || temp->d_parent != c->workdir) {
/* temp or workdir moved underneath us? abort without cleanup */
dput(temp);
err = -EIO;
+ if (IS_ERR(trap))
+ goto out;
goto unlock;
} else if (err) {
goto cleanup;
@@ -826,6 +828,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
ovl_set_flag(OVL_WHITEOUTS, inode);
unlock:
unlock_rename(c->workdir, c->destdir);
+out:
ovl_end_write(c->dentry);
return err;
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index aab3f5d93556..0f8b4a719237 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1180,6 +1180,10 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
}
trap = lock_rename(new_upperdir, old_upperdir);
+ if (IS_ERR(trap)) {
+ err = PTR_ERR(trap);
+ goto out_revert_creds;
+ }
olddentry = ovl_lookup_upper(ofs, old->d_name.name, old_upperdir,
old->d_name.len);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 0bbbe4818f67..4ab66e3d4cff 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -439,8 +439,10 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
bool ok = false;
if (workdir != upperdir) {
- ok = (lock_rename(workdir, upperdir) == NULL);
- unlock_rename(workdir, upperdir);
+ struct dentry *trap = lock_rename(workdir, upperdir);
+ if (!IS_ERR(trap))
+ unlock_rename(workdir, upperdir);
+ ok = (trap == NULL);
}
return ok;
}
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 22b519763267..0217094c23ea 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -1198,12 +1198,17 @@ void ovl_nlink_end(struct dentry *dentry)
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)
{
+ struct dentry *trap;
+
/* Workdir should not be the same as upperdir */
if (workdir == upperdir)
goto err;
/* Workdir should not be subdir of upperdir and vice versa */
- if (lock_rename(workdir, upperdir) != NULL)
+ trap = lock_rename(workdir, upperdir);
+ if (IS_ERR(trap))
+ goto err;
+ if (trap)
goto err_unlock;
return 0;