xfs: standardize ondisk to incore conversion for refcount btrees
authorDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:02 +0000 (19:00 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:02 +0000 (19:00 -0700)
Create a xfs_refcount_check_irec function to detect corruption in btree
records.  Fix all xfs_refcount_btrec_to_irec callsites to call the new
helper 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_refcount.c
fs/xfs/libxfs/xfs_refcount.h
fs/xfs/scrub/refcount.c

index 6dc9686..b77dea1 100644 (file)
@@ -120,6 +120,30 @@ xfs_refcount_btrec_to_irec(
        irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
 }
 
+/* Simple checks for refcount records. */
+xfs_failaddr_t
+xfs_refcount_check_irec(
+       struct xfs_btree_cur            *cur,
+       const struct xfs_refcount_irec  *irec)
+{
+       struct xfs_perag                *pag = cur->bc_ag.pag;
+
+       if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
+               return __this_address;
+
+       if (!xfs_refcount_check_domain(irec))
+               return __this_address;
+
+       /* check for valid extent range, including overflow */
+       if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount))
+               return __this_address;
+
+       if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
+               return __this_address;
+
+       return NULL;
+}
+
 /*
  * Get the data from the pointed-to record.
  */
@@ -132,6 +156,7 @@ xfs_refcount_get_rec(
        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);
@@ -139,17 +164,8 @@ xfs_refcount_get_rec(
                return error;
 
        xfs_refcount_btrec_to_irec(rec, irec);
-       if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
-               goto out_bad_rec;
-
-       if (!xfs_refcount_check_domain(irec))
-               goto out_bad_rec;
-
-       /* check for valid extent range, including overflow */
-       if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount))
-               goto out_bad_rec;
-
-       if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
+       fa = xfs_refcount_check_irec(cur, irec);
+       if (fa)
                goto out_bad_rec;
 
        trace_xfs_refcount_get(cur->bc_mp, pag->pag_agno, irec);
@@ -157,8 +173,8 @@ xfs_refcount_get_rec(
 
 out_bad_rec:
        xfs_warn(mp,
-               "Refcount BTree record corruption in AG %d detected!",
-               pag->pag_agno);
+               "Refcount BTree record corruption in AG %d detected at %pS!",
+               pag->pag_agno, fa);
        xfs_warn(mp,
                "Start block 0x%x, block count 0x%x, references 0x%x",
                irec->rc_startblock, irec->rc_blockcount, irec->rc_refcount);
@@ -1871,7 +1887,8 @@ xfs_refcount_recover_extent(
        INIT_LIST_HEAD(&rr->rr_list);
        xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
 
-       if (XFS_IS_CORRUPT(cur->bc_mp,
+       if (xfs_refcount_check_irec(cur, &rr->rr_rrec) != NULL ||
+           XFS_IS_CORRUPT(cur->bc_mp,
                           rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
                kfree(rr);
                return -EFSCORRUPTED;
index c89f0fc..fc0b58d 100644 (file)
@@ -117,6 +117,8 @@ extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
 union xfs_btree_rec;
 extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
                struct xfs_refcount_irec *irec);
+xfs_failaddr_t xfs_refcount_check_irec(struct xfs_btree_cur *cur,
+               const struct xfs_refcount_irec *irec);
 extern int xfs_refcount_insert(struct xfs_btree_cur *cur,
                struct xfs_refcount_irec *irec, int *stat);
 
index 6f649cc..4d77049 100644 (file)
@@ -340,24 +340,16 @@ xchk_refcountbt_rec(
 {
        struct xfs_refcount_irec irec;
        xfs_agblock_t           *cow_blocks = bs->private;
-       struct xfs_perag        *pag = bs->cur->bc_ag.pag;
 
        xfs_refcount_btrec_to_irec(rec, &irec);
-
-       /* Check the domain and refcount are not incompatible. */
-       if (!xfs_refcount_check_domain(&irec))
+       if (xfs_refcount_check_irec(bs->cur, &irec) != NULL) {
                xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+               return 0;
+       }
 
        if (irec.rc_domain == XFS_REFC_DOMAIN_COW)
                (*cow_blocks) += irec.rc_blockcount;
 
-       /* Check the extent. */
-       if (!xfs_verify_agbext(pag, irec.rc_startblock, irec.rc_blockcount))
-               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
-
-       if (irec.rc_refcount == 0)
-               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
-
        xchk_refcountbt_xref(bs->sc, &irec);
 
        return 0;