]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: standardize ondisk to incore conversion for inode btrees
authorDarrick J. Wong <djwong@kernel.org>
Tue, 30 May 2023 11:13:58 +0000 (13:13 +0200)
committerCarlos Maiolino <cem@kernel.org>
Fri, 9 Jun 2023 08:27:50 +0000 (10:27 +0200)
Source kernel commit: 366a0b8d49c3a7edcb5331f254af195716ba4bdf

Create a xfs_inobt_check_irec function to detect corruption in btree
records.  Fix all xfs_inobt_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>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
libxfs/xfs_ialloc.c
libxfs/xfs_ialloc.h
libxfs/xfs_ialloc_btree.c
libxfs/xfs_ialloc_btree.h

index 50ffa82034e5e08da75359aea064f593cf1b55a0..78634c5a7b2ffde76f2c8eaed8cfb6bfa0683603 100644 (file)
@@ -90,6 +90,33 @@ xfs_inobt_btrec_to_irec(
        irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
 }
 
+/* Simple checks for inode records. */
+xfs_failaddr_t
+xfs_inobt_check_irec(
+       struct xfs_btree_cur                    *cur,
+       const struct xfs_inobt_rec_incore       *irec)
+{
+       uint64_t                        realfree;
+
+       if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
+               return __this_address;
+       if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
+           irec->ir_count > XFS_INODES_PER_CHUNK)
+               return __this_address;
+       if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
+               return __this_address;
+
+       /* if there are no holes, return the first available offset */
+       if (!xfs_inobt_issparse(irec->ir_holemask))
+               realfree = irec->ir_free;
+       else
+               realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
+       if (hweight64(realfree) != irec->ir_freecount)
+               return __this_address;
+
+       return NULL;
+}
+
 /*
  * Get the data from the pointed-to record.
  */
@@ -101,38 +128,25 @@ xfs_inobt_get_rec(
 {
        struct xfs_mount                *mp = cur->bc_mp;
        union xfs_btree_rec             *rec;
+       xfs_failaddr_t                  fa;
        int                             error;
-       uint64_t                        realfree;
 
        error = xfs_btree_get_rec(cur, &rec, stat);
        if (error || *stat == 0)
                return error;
 
        xfs_inobt_btrec_to_irec(mp, rec, irec);
-
-       if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
-               goto out_bad_rec;
-       if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
-           irec->ir_count > XFS_INODES_PER_CHUNK)
-               goto out_bad_rec;
-       if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
-               goto out_bad_rec;
-
-       /* if there are no holes, return the first available offset */
-       if (!xfs_inobt_issparse(irec->ir_holemask))
-               realfree = irec->ir_free;
-       else
-               realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
-       if (hweight64(realfree) != irec->ir_freecount)
+       fa = xfs_inobt_check_irec(cur, irec);
+       if (fa)
                goto out_bad_rec;
 
        return 0;
 
 out_bad_rec:
        xfs_warn(mp,
-               "%s Inode BTree record corruption in AG %d detected!",
+               "%s Inode BTree record corruption in AG %d detected at %pS!",
                cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free",
-               cur->bc_ag.pag->pag_agno);
+               cur->bc_ag.pag->pag_agno, fa);
        xfs_warn(mp,
 "start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x",
                irec->ir_startino, irec->ir_count, irec->ir_freecount,
@@ -2685,6 +2699,9 @@ xfs_ialloc_count_inodes_rec(
        struct xfs_ialloc_count_inodes  *ci = priv;
 
        xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
+       if (xfs_inobt_check_irec(cur, &irec) != NULL)
+               return -EFSCORRUPTED;
+
        ci->count += irec.ir_count;
        ci->freecount += irec.ir_freecount;
 
index ab8c30b4ec22cdb0b6637c98ff194c57673c09eb..90b0e50793380d82b735ccd5884603941f846095 100644 (file)
@@ -93,6 +93,8 @@ union xfs_btree_rec;
 void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
                const union xfs_btree_rec *rec,
                struct xfs_inobt_rec_incore *irec);
+xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
+               const struct xfs_inobt_rec_incore *irec);
 int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
                xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
 int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
index 934ae98bebe656cbe4e7d464affaa3dc84099cbd..b399717723afbdeffd6fa9134a75eaffb794880f 100644 (file)
@@ -607,7 +607,7 @@ xfs_iallocbt_maxlevels_ondisk(void)
  */
 uint64_t
 xfs_inobt_irec_to_allocmask(
-       struct xfs_inobt_rec_incore     *rec)
+       const struct xfs_inobt_rec_incore       *rec)
 {
        uint64_t                        bitmap = 0;
        uint64_t                        inodespbit;
index e859a6e0523096d56b7b8cef0e6b6c743f40e6a3..3262c3fe5ebee0e023acd9f29c8ee1592ea69e2b 100644 (file)
@@ -53,7 +53,7 @@ struct xfs_btree_cur *xfs_inobt_stage_cursor(struct xfs_perag *pag,
 extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
 
 /* ir_holemask to inode allocation bitmap conversion */
-uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *);
+uint64_t xfs_inobt_irec_to_allocmask(const struct xfs_inobt_rec_incore *irec);
 
 #if defined(DEBUG) || defined(XFS_WARN)
 int xfs_inobt_rec_check_count(struct xfs_mount *,