xfs: fix broken i_nlink accounting for whiteout tmpfile inode
XFS uses the internal tmpfile() infrastructure for the whiteout inode used for RENAME_WHITEOUT operations. For tmpfile inodes, XFS allocates the inode, drops di_nlink, adds the inode to the agi unlinked list, calls d_tmpfile() which correspondingly drops i_nlink of the vfs inode, and then finishes the common inode setup (e.g., clear I_NEW and unlock). The d_tmpfile() call was originally made inxfs_create_tmpfile(), but was pulled up out of that function as part of the following commit to resolve a deadlock issue: 330033d6 xfs: fix tmpfile/selinux deadlock and initialize security As a result, callers of xfs_create_tmpfile() are responsible for either calling d_tmpfile() or fixing up i_nlink appropriately. The whiteout tmpfile allocation helper does neither. As a result, the vfs ->i_nlink becomes inconsistent with the on-disk ->di_nlink once xfs_rename() links it back into the source dentry and calls xfs_bumplink(). Update the assert in xfs_rename() to help detect this problem in the future and update xfs_rename_alloc_whiteout() to decrement the link count as part of the manual tmpfile inode setup. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
parent
cddc116228
commit
22419ac9fe
@ -2879,7 +2879,13 @@ xfs_rename_alloc_whiteout(
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Satisfy xfs_bumplink that this is a real tmpfile */
|
||||
/*
|
||||
* Prepare the tmpfile inode as if it were created through the VFS.
|
||||
* Otherwise, the link increment paths will complain about nlink 0->1.
|
||||
* Drop the link count as done by d_tmpfile(), complete the inode setup
|
||||
* and flag it as linkable.
|
||||
*/
|
||||
drop_nlink(VFS_I(tmpfile));
|
||||
xfs_finish_inode_setup(tmpfile);
|
||||
VFS_I(tmpfile)->i_state |= I_LINKABLE;
|
||||
|
||||
@ -3147,7 +3153,7 @@ xfs_rename(
|
||||
* intermediate state on disk.
|
||||
*/
|
||||
if (wip) {
|
||||
ASSERT(wip->i_d.di_nlink == 0);
|
||||
ASSERT(VFS_I(wip)->i_nlink == 0 && wip->i_d.di_nlink == 0);
|
||||
error = xfs_bumplink(tp, wip);
|
||||
if (error)
|
||||
goto out_trans_abort;
|
||||
|
Loading…
x
Reference in New Issue
Block a user