xfs: standardize ondisk to incore conversion for free space btrees
authorDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:01 +0000 (19:00 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:01 +0000 (19:00 -0700)
Create a xfs_alloc_btrec_to_irec function to convert an ondisk record to
an incore record, and a xfs_alloc_check_irec function to detect
corruption.  Replace all the open-coded logic with calls to the new
helpers and bubble up corruption reports.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/libxfs/xfs_alloc.c
fs/xfs/libxfs/xfs_alloc.h
fs/xfs/scrub/alloc.c

index d724830..89c935c 100644 (file)
@@ -233,6 +233,34 @@ xfs_alloc_update(
        return xfs_btree_update(cur, &rec);
 }
 
+/* Convert the ondisk btree record to its incore representation. */
+void
+xfs_alloc_btrec_to_irec(
+       const union xfs_btree_rec       *rec,
+       struct xfs_alloc_rec_incore     *irec)
+{
+       irec->ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
+       irec->ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
+}
+
+/* Simple checks for free space records. */
+xfs_failaddr_t
+xfs_alloc_check_irec(
+       struct xfs_btree_cur            *cur,
+       const struct xfs_alloc_rec_incore *irec)
+{
+       struct xfs_perag                *pag = cur->bc_ag.pag;
+
+       if (irec->ar_blockcount == 0)
+               return __this_address;
+
+       /* check for valid extent range, including overflow */
+       if (!xfs_verify_agbext(pag, irec->ar_startblock, irec->ar_blockcount))
+               return __this_address;
+
+       return NULL;
+}
+
 /*
  * Get the data from the pointed-to record.
  */
@@ -243,34 +271,34 @@ xfs_alloc_get_rec(
        xfs_extlen_t            *len,   /* output: length of extent */
        int                     *stat)  /* output: success/failure */
 {
+       struct xfs_alloc_rec_incore irec;
        struct xfs_mount        *mp = cur->bc_mp;
        struct xfs_perag        *pag = cur->bc_ag.pag;
        union xfs_btree_rec     *rec;
+       xfs_failaddr_t          fa;
        int                     error;
 
        error = xfs_btree_get_rec(cur, &rec, stat);
        if (error || !(*stat))
                return error;
 
-       *bno = be32_to_cpu(rec->alloc.ar_startblock);
-       *len = be32_to_cpu(rec->alloc.ar_blockcount);
-
-       if (*len == 0)
-               goto out_bad_rec;
-
-       /* check for valid extent range, including overflow */
-       if (!xfs_verify_agbext(pag, *bno, *len))
+       xfs_alloc_btrec_to_irec(rec, &irec);
+       fa = xfs_alloc_check_irec(cur, &irec);
+       if (fa)
                goto out_bad_rec;
 
+       *bno = irec.ar_startblock;
+       *len = irec.ar_blockcount;
        return 0;
 
 out_bad_rec:
        xfs_warn(mp,
-               "%s Freespace BTree record corruption in AG %d detected!",
+               "%s Freespace BTree record corruption in AG %d detected at %pS!",
                cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
-               pag->pag_agno);
+               pag->pag_agno, fa);
        xfs_warn(mp,
-               "start block 0x%x block count 0x%x", *bno, *len);
+               "start block 0x%x block count 0x%x", irec.ar_startblock,
+               irec.ar_blockcount);
        return -EFSCORRUPTED;
 }
 
@@ -3665,8 +3693,10 @@ xfs_alloc_query_range_helper(
        struct xfs_alloc_query_range_info       *query = priv;
        struct xfs_alloc_rec_incore             irec;
 
-       irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
-       irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
+       xfs_alloc_btrec_to_irec(rec, &irec);
+       if (xfs_alloc_check_irec(cur, &irec) != NULL)
+               return -EFSCORRUPTED;
+
        return query->fn(cur, &irec, query->priv);
 }
 
index 5569cb2..56bd059 100644 (file)
@@ -181,6 +181,12 @@ xfs_alloc_get_rec(
        xfs_extlen_t            *len,   /* output: length of extent */
        int                     *stat); /* output: success/failure */
 
+union xfs_btree_rec;
+void xfs_alloc_btrec_to_irec(const union xfs_btree_rec *rec,
+               struct xfs_alloc_rec_incore *irec);
+xfs_failaddr_t xfs_alloc_check_irec(struct xfs_btree_cur *cur,
+               const struct xfs_alloc_rec_incore *irec);
+
 int xfs_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
                struct xfs_buf **agfbpp);
 int xfs_alloc_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
index de313df..53de04c 100644 (file)
@@ -78,9 +78,11 @@ xchk_allocbt_xref_other(
 STATIC void
 xchk_allocbt_xref(
        struct xfs_scrub        *sc,
-       xfs_agblock_t           agbno,
-       xfs_extlen_t            len)
+       const struct xfs_alloc_rec_incore *irec)
 {
+       xfs_agblock_t           agbno = irec->ar_startblock;
+       xfs_extlen_t            len = irec->ar_blockcount;
+
        if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
                return;
 
@@ -93,20 +95,18 @@ xchk_allocbt_xref(
 /* Scrub a bnobt/cntbt record. */
 STATIC int
 xchk_allocbt_rec(
-       struct xchk_btree       *bs,
-       const union xfs_btree_rec *rec)
+       struct xchk_btree               *bs,
+       const union xfs_btree_rec       *rec)
 {
-       struct xfs_perag        *pag = bs->cur->bc_ag.pag;
-       xfs_agblock_t           bno;
-       xfs_extlen_t            len;
+       struct xfs_alloc_rec_incore     irec;
 
-       bno = be32_to_cpu(rec->alloc.ar_startblock);
-       len = be32_to_cpu(rec->alloc.ar_blockcount);
-
-       if (!xfs_verify_agbext(pag, bno, len))
+       xfs_alloc_btrec_to_irec(rec, &irec);
+       if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) {
                xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+               return 0;
+       }
 
-       xchk_allocbt_xref(bs->sc, bno, len);
+       xchk_allocbt_xref(bs->sc, &irec);
 
        return 0;
 }