Skip to content

Commit

Permalink
ext4: find old entry again if failed to rename whiteout
Browse files Browse the repository at this point in the history
If we failed to add new entry on rename whiteout, we cannot reset the
old->de entry directly, because the old->de could have moved from under
us during make indexed dir. So find the old entry again before reset is
needed, otherwise it may corrupt the filesystem as below.

  /dev/sda: Entry '00000001' in ??? (12) has deleted/unused inode 15. CLEARED.
  /dev/sda: Unattached inode 75
  /dev/sda: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.

Fixes: 6b4b8e6 ("ext4: fix bug for rename with RENAME_WHITEOUT")
Cc: stable@vger.kernel.org
Signed-off-by: zhangyi (F) <yi.zhang@huawei.com>
Link: https://lore.kernel.org/r/20210303131703.330415-1-yi.zhang@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
zhangyi (F) authored and Theodore Ts'o committed Mar 21, 2021
1 parent f053cf7 commit b7ff91f
Showing 1 changed file with 27 additions and 2 deletions.
29 changes: 27 additions & 2 deletions fs/ext4/namei.c
Original file line number Diff line number Diff line change
@@ -3613,6 +3613,31 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
return retval;
}

static void ext4_resetent(handle_t *handle, struct ext4_renament *ent,
unsigned ino, unsigned file_type)
{
struct ext4_renament old = *ent;
int retval = 0;

/*
* old->de could have moved from under us during make indexed dir,
* so the old->de may no longer valid and need to find it again
* before reset old inode info.
*/
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
if (IS_ERR(old.bh))
retval = PTR_ERR(old.bh);
if (!old.bh)
retval = -ENOENT;
if (retval) {
ext4_std_error(old.dir->i_sb, retval);
return;
}

ext4_setent(handle, &old, ino, file_type);
brelse(old.bh);
}

static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
const struct qstr *d_name)
{
@@ -3937,8 +3962,8 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
end_rename:
if (whiteout) {
if (retval) {
ext4_setent(handle, &old,
old.inode->i_ino, old_file_type);
ext4_resetent(handle, &old,
old.inode->i_ino, old_file_type);
drop_nlink(whiteout);
}
unlock_new_inode(whiteout);

0 comments on commit b7ff91f

Please sign in to comment.