From 349aa6876786cbcf9faa3aed3c577f634c03a8b0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 May 2023 13:13:58 +0200 Subject: [PATCH] xfs: standardize ondisk to incore conversion for inode btrees 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 Reviewed-by: Dave Chinner Signed-off-by: Carlos Maiolino --- libxfs/xfs_ialloc.c | 53 ++++++++++++++++++++++++++------------- libxfs/xfs_ialloc.h | 2 ++ libxfs/xfs_ialloc_btree.c | 2 +- libxfs/xfs_ialloc_btree.h | 2 +- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/libxfs/xfs_ialloc.c b/libxfs/xfs_ialloc.c index 50ffa8203..78634c5a7 100644 --- a/libxfs/xfs_ialloc.c +++ b/libxfs/xfs_ialloc.c @@ -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; diff --git a/libxfs/xfs_ialloc.h b/libxfs/xfs_ialloc.h index ab8c30b4e..90b0e5079 100644 --- a/libxfs/xfs_ialloc.h +++ b/libxfs/xfs_ialloc.h @@ -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, diff --git a/libxfs/xfs_ialloc_btree.c b/libxfs/xfs_ialloc_btree.c index 934ae98be..b39971772 100644 --- a/libxfs/xfs_ialloc_btree.c +++ b/libxfs/xfs_ialloc_btree.c @@ -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; diff --git a/libxfs/xfs_ialloc_btree.h b/libxfs/xfs_ialloc_btree.h index e859a6e05..3262c3fe5 100644 --- a/libxfs/xfs_ialloc_btree.h +++ b/libxfs/xfs_ialloc_btree.h @@ -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 *, -- 2.47.2