Skip to content

Commit 6b4b8e6

Browse files
yangerkuntytso
authored andcommitted
ext4: fix bug for rename with RENAME_WHITEOUT
We got a "deleted inode referenced" warning cross our fsstress test. The bug can be reproduced easily with following steps: cd /dev/shm mkdir test/ fallocate -l 128M img mkfs.ext4 -b 1024 img mount img test/ dd if=/dev/zero of=test/foo bs=1M count=128 mkdir test/dir/ && cd test/dir/ for ((i=0;i<1000;i++)); do touch file$i; done # consume all block cd ~ && renameat2(AT_FDCWD, /dev/shm/test/dir/file1, AT_FDCWD, /dev/shm/test/dir/dst_file, RENAME_WHITEOUT) # ext4_add_entry in ext4_rename will return ENOSPC!! cd /dev/shm/ && umount test/ && mount img test/ && ls -li test/dir/file1 We will get the output: "ls: cannot access 'test/dir/file1': Structure needs cleaning" and the dmesg show: "EXT4-fs error (device loop0): ext4_lookup:1626: inode #2049: comm ls: deleted inode referenced: 139" ext4_rename will create a special inode for whiteout and use this 'ino' to replace the source file's dir entry 'ino'. Once error happens latter(the error above was the ENOSPC return from ext4_add_entry in ext4_rename since all space has been consumed), the cleanup do drop the nlink for whiteout, but forget to restore 'ino' with source file. This will trigger the bug describle as above. Signed-off-by: yangerkun <yangerkun@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Cc: stable@vger.kernel.org Fixes: cd808de ("ext4: support RENAME_WHITEOUT") Link: https://lore.kernel.org/r/20210105062857.3566-1-yangerkun@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent 31e203e commit 6b4b8e6

1 file changed

Lines changed: 9 additions & 8 deletions

File tree

fs/ext4/namei.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3601,9 +3601,6 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
36013601
return retval2;
36023602
}
36033603
}
3604-
brelse(ent->bh);
3605-
ent->bh = NULL;
3606-
36073604
return retval;
36083605
}
36093606

@@ -3802,6 +3799,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
38023799
}
38033800
}
38043801

3802+
old_file_type = old.de->file_type;
38053803
if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
38063804
ext4_handle_sync(handle);
38073805

@@ -3829,7 +3827,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
38293827
force_reread = (new.dir->i_ino == old.dir->i_ino &&
38303828
ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
38313829

3832-
old_file_type = old.de->file_type;
38333830
if (whiteout) {
38343831
/*
38353832
* Do this before adding a new entry, so the old entry is sure
@@ -3927,15 +3924,19 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
39273924
retval = 0;
39283925

39293926
end_rename:
3930-
brelse(old.dir_bh);
3931-
brelse(old.bh);
3932-
brelse(new.bh);
39333927
if (whiteout) {
3934-
if (retval)
3928+
if (retval) {
3929+
ext4_setent(handle, &old,
3930+
old.inode->i_ino, old_file_type);
39353931
drop_nlink(whiteout);
3932+
}
39363933
unlock_new_inode(whiteout);
39373934
iput(whiteout);
3935+
39383936
}
3937+
brelse(old.dir_bh);
3938+
brelse(old.bh);
3939+
brelse(new.bh);
39393940
if (handle)
39403941
ext4_journal_stop(handle);
39413942
return retval;

0 commit comments

Comments
 (0)