mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ext4: find old entry again if failed to rename whiteout
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: 6b4b8e6b4a ("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>
			
			
This commit is contained in:
		
							parent
							
								
									f053cf7aa6
								
							
						
					
					
						commit
						b7ff91fd03
					
				
					 1 changed files with 27 additions and 2 deletions
				
			
		| 
						 | 
					@ -3613,6 +3613,31 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
 | 
				
			||||||
	return retval;
 | 
						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,
 | 
					static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
 | 
				
			||||||
				  const struct qstr *d_name)
 | 
									  const struct qstr *d_name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -3937,8 +3962,8 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
 | 
				
			||||||
end_rename:
 | 
					end_rename:
 | 
				
			||||||
	if (whiteout) {
 | 
						if (whiteout) {
 | 
				
			||||||
		if (retval) {
 | 
							if (retval) {
 | 
				
			||||||
			ext4_setent(handle, &old,
 | 
								ext4_resetent(handle, &old,
 | 
				
			||||||
				old.inode->i_ino, old_file_type);
 | 
									      old.inode->i_ino, old_file_type);
 | 
				
			||||||
			drop_nlink(whiteout);
 | 
								drop_nlink(whiteout);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		unlock_new_inode(whiteout);
 | 
							unlock_new_inode(whiteout);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue