[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20230523131408.13470-1-jack@suse.cz>
Date: Tue, 23 May 2023 15:14:08 +0200
From: Jan Kara <jack@...e.cz>
To: Ted Tso <tytso@....edu>
Cc: <linux-ext4@...r.kernel.org>,
"Darrick J. Wong" <djwong@...nel.org>, Jan Kara <jack@...e.cz>,
stable@...r.kernel.org
Subject: [PATCH] ext4: Fix possible corruption when moving a directory with RENAME_EXCHANGE
Commit 0813299c586b ("ext4: Fix possible corruption when moving a
directory") forgot that handling of RENAME_EXCHANGE renames needs the
protection of inode lock when changing directory parents for moved
directories. Add proper locking for that case as well.
CC: stable@...r.kernel.org
Fixes: 0813299c586b ("ext4: Fix possible corruption when moving a directory")
Reported-by: "Darrick J. Wong" <djwong@...nel.org>
Signed-off-by: Jan Kara <jack@...e.cz>
---
fs/ext4/namei.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 45b579805c95..b91abea1c781 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -4083,10 +4083,25 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
if (retval)
return retval;
+ /*
+ * We need to protect against old.inode and new.inode directory getting
+ * converted from inline directory format into a normal one. The lock
+ * ordering does not matter here as old and new are guaranteed to be
+ * incomparable in the directory hierarchy.
+ */
+ if (S_ISDIR(old.inode->i_mode))
+ inode_lock(old.inode);
+ if (S_ISDIR(new.inode->i_mode))
+ inode_lock_nested(new.inode, I_MUTEX_NONDIR2);
+
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
&old.de, &old.inlined);
- if (IS_ERR(old.bh))
- return PTR_ERR(old.bh);
+ if (IS_ERR(old.bh)) {
+ retval = PTR_ERR(old.bh);
+ old.bh = NULL;
+ goto end_rename;
+ }
+
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
@@ -4186,6 +4201,10 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
retval = 0;
end_rename:
+ if (S_ISDIR(old.inode->i_mode))
+ inode_unlock(old.inode);
+ if (S_ISDIR(new.inode->i_mode))
+ inode_unlock(new.inode);
brelse(old.dir_bh);
brelse(new.dir_bh);
brelse(old.bh);
--
2.35.3
Powered by blists - more mailing lists