xfs: check AGI unlinked inode buckets
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:55:00 +0000 (14:55 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:58:58 +0000 (14:58 -0700)
Look for corruptions in the AGI unlinked bucket chains.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/scrub/agheader.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h

index e954f07679dd785f9ccead8118e900ea1a9ffbbe..1528f14bd9251891373855dbc0ebb3a2f06c5552 100644 (file)
@@ -15,6 +15,7 @@
 #include "xfs_ialloc.h"
 #include "xfs_rmap.h"
 #include "xfs_ag.h"
+#include "xfs_inode.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 
@@ -865,6 +866,43 @@ xchk_agi_xref(
        /* scrub teardown will take care of sc->sa for us */
 }
 
+/*
+ * Check the unlinked buckets for links to bad inodes.  We hold the AGI, so
+ * there cannot be any threads updating unlinked list pointers in this AG.
+ */
+STATIC void
+xchk_iunlink(
+       struct xfs_scrub        *sc,
+       struct xfs_agi          *agi)
+{
+       unsigned int            i;
+       struct xfs_inode        *ip;
+
+       for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
+               xfs_agino_t     agino = be32_to_cpu(agi->agi_unlinked[i]);
+
+               while (agino != NULLAGINO) {
+                       if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
+                               xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+                               return;
+                       }
+
+                       ip = xfs_iunlink_lookup(sc->sa.pag, agino);
+                       if (!ip) {
+                               xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+                               return;
+                       }
+
+                       if (!xfs_inode_on_unlinked_list(ip)) {
+                               xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+                               return;
+                       }
+
+                       agino = ip->i_next_unlinked;
+               }
+       }
+}
+
 /* Scrub the AGI. */
 int
 xchk_agi(
@@ -949,6 +987,8 @@ xchk_agi(
        if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
                xchk_block_set_corrupt(sc, sc->sa.agi_bp);
 
+       xchk_iunlink(sc, agi);
+
        xchk_agi_xref(sc);
 out:
        return error;
index 803a6468701453038ef869172e58a57192b65984..fed0cd6bffdf0b296816a5df148cf85476f834b1 100644 (file)
@@ -1985,7 +1985,7 @@ out:
  * only unlinked, referenced inodes can be on the unlinked inode list.  If we
  * don't find the inode in cache, then let the caller handle the situation.
  */
-static struct xfs_inode *
+struct xfs_inode *
 xfs_iunlink_lookup(
        struct xfs_perag        *pag,
        xfs_agino_t             agino)
index 18bc3d7750a04c2b865edd71ab434d681b1c114b..c74c48bc0945331d73bbde961dba2fe50aa512a5 100644 (file)
@@ -619,6 +619,7 @@ bool xfs_inode_needs_inactive(struct xfs_inode *ip);
 int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
 int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
                struct xfs_inode *ip);
+struct xfs_inode *xfs_iunlink_lookup(struct xfs_perag *pag, xfs_agino_t agino);
 
 void xfs_end_io(struct work_struct *work);