xfs: don't double-lock when deleting a self-referential directory

LOLLM notices that the dirtree scrubber can detect a directory that
refers to itself.  In this case, it's not correct for the directory tree
repair code to try to iolock/ilock both sc->ip and dp, because they're
the same inode.  Fix this by detecting that corner case and handling it
appropriately.

Cc: stable@vger.kernel.org # v6.10
Fixes: 3f31406aef ("xfs: fix corruptions in the directory tree")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Darrick J. Wong
2026-07-26 22:23:15 -07:00
committed by Carlos Maiolino
parent 97efed1091
commit 5fc643fb86

View File

@@ -349,6 +349,8 @@ xrep_dirtree_unlink_iolock(
ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
if (sc->ip == dp)
return 0;
if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL))
return 0;
@@ -400,8 +402,18 @@ xrep_dirtree_unlink(
* directory code can handle a reservationless update.
*/
resblks = xfs_remove_space_res(mp, step->name_len);
error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
&resblks, &sc->tp, &dontcare);
if (sc->ip == dp) {
again:
error = xfs_trans_alloc_inode(dp, &M_RES(mp)->tr_remove,
resblks, 0, false, &sc->tp);
if ((error == -ENOSPC || error == -EDQUOT) && resblks > 0) {
resblks = 0;
goto again;
}
} else {
error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
&resblks, &sc->tp, &dontcare);
}
if (error)
goto out_iolock;
@@ -489,9 +501,11 @@ xrep_dirtree_unlink(
xchk_trans_cancel(sc);
out_ilock:
xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
if (dp != sc->ip)
xfs_iunlock(dp, XFS_ILOCK_EXCL);
out_iolock:
xfs_iunlock(dp, XFS_IOLOCK_EXCL);
if (dp != sc->ip)
xfs_iunlock(dp, XFS_IOLOCK_EXCL);
return error;
}