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@xxxxxxxxxxxxxxx Fixes: 0813299c586b ("ext4: Fix possible corruption when moving a directory") Reported-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> Signed-off-by: Jan Kara <jack@xxxxxxx> --- 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