Commit 0813299c authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

ext4: Fix possible corruption when moving a directory

When we are renaming a directory to a different directory, we need to
update '..' entry in the moved directory. However nothing prevents moved
directory from being modified and even converted from the inline format
to the normal format. When such race happens the rename code gets
confused and we crash. Fix the problem by locking the moved directory.

CC: stable@vger.kernel.org
Fixes: 32f7f22c ("ext4: let ext4_rename handle inline dir")
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20230126112221.11866-1-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 172e344e
...@@ -3872,10 +3872,17 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir, ...@@ -3872,10 +3872,17 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir)) if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
goto end_rename; goto end_rename;
} }
/*
* We need to protect against old.inode directory getting
* converted from inline directory format into a normal one.
*/
inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
retval = ext4_rename_dir_prepare(handle, &old); retval = ext4_rename_dir_prepare(handle, &old);
if (retval) if (retval) {
inode_unlock(old.inode);
goto end_rename; goto end_rename;
} }
}
/* /*
* If we're renaming a file within an inline_data dir and adding or * If we're renaming a file within an inline_data dir and adding or
* setting the new dirent causes a conversion from inline_data to * setting the new dirent causes a conversion from inline_data to
...@@ -4006,6 +4013,8 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir, ...@@ -4006,6 +4013,8 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
} else { } else {
ext4_journal_stop(handle); ext4_journal_stop(handle);
} }
if (old.dir_bh)
inode_unlock(old.inode);
release_bh: release_bh:
brelse(old.dir_bh); brelse(old.dir_bh);
brelse(old.bh); brelse(old.bh);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment