]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfs: don't double-lock when deleting a self-referential directory
authorDarrick J. Wong <djwong@kernel.org>
Mon, 27 Jul 2026 05:23:15 +0000 (22:23 -0700)
committerCarlos Maiolino <cem@kernel.org>
Mon, 3 Aug 2026 08:17:34 +0000 (10:17 +0200)
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: 3f31406aef493b ("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>
fs/xfs/scrub/dirtree_repair.c

index 1c0d7ea4a5be6aafb97e085480222154043d7ef0..bbf6acf6fd400ccbeb4817864cf3cda577ab9336 100644 (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 @@ out_trans_cancel:
        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;
 }