--- /dev/null
+From dd84a8b95f91f69bf752d0973ab674f75450955f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Jan 2021 14:28:57 +0800
+Subject: ext4: fix bug for rename with RENAME_WHITEOUT
+
+From: yangerkun <yangerkun@huawei.com>
+
+[ Upstream commit 6b4b8e6b4ad8553660421d6360678b3811d5deb9 ]
+
+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: cd808deced43 ("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>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/namei.c | 16 +++++++++-------
+ 1 file changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
+index 3c238006870d3..8f7e0ad5b5ef1 100644
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -3437,8 +3437,6 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
+ return retval;
+ }
+ }
+- brelse(ent->bh);
+- ent->bh = NULL;
+
+ return 0;
+ }
+@@ -3638,6 +3636,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ }
+ }
+
++ old_file_type = old.de->file_type;
+ if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
+ ext4_handle_sync(handle);
+
+@@ -3665,7 +3664,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ force_reread = (new.dir->i_ino == old.dir->i_ino &&
+ ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
+
+- old_file_type = old.de->file_type;
+ if (whiteout) {
+ /*
+ * Do this before adding a new entry, so the old entry is sure
+@@ -3737,15 +3735,19 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ retval = 0;
+
+ end_rename:
+- brelse(old.dir_bh);
+- brelse(old.bh);
+- brelse(new.bh);
+ if (whiteout) {
+- if (retval)
++ if (retval) {
++ ext4_setent(handle, &old,
++ old.inode->i_ino, old_file_type);
+ drop_nlink(whiteout);
++ }
+ unlock_new_inode(whiteout);
+ iput(whiteout);
++
+ }
++ brelse(old.dir_bh);
++ brelse(old.bh);
++ brelse(new.bh);
+ if (handle)
+ ext4_journal_stop(handle);
+ return retval;
+--
+2.27.0
+