Commit 366a0b8d authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: standardize ondisk to incore conversion for inode btrees

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: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 35e3b9a1
...@@ -95,6 +95,33 @@ xfs_inobt_btrec_to_irec( ...@@ -95,6 +95,33 @@ xfs_inobt_btrec_to_irec(
irec->ir_free = be64_to_cpu(rec->inobt.ir_free); 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. * Get the data from the pointed-to record.
*/ */
...@@ -106,38 +133,25 @@ xfs_inobt_get_rec( ...@@ -106,38 +133,25 @@ xfs_inobt_get_rec(
{ {
struct xfs_mount *mp = cur->bc_mp; struct xfs_mount *mp = cur->bc_mp;
union xfs_btree_rec *rec; union xfs_btree_rec *rec;
xfs_failaddr_t fa;
int error; int error;
uint64_t realfree;
error = xfs_btree_get_rec(cur, &rec, stat); error = xfs_btree_get_rec(cur, &rec, stat);
if (error || *stat == 0) if (error || *stat == 0)
return error; return error;
xfs_inobt_btrec_to_irec(mp, rec, irec); xfs_inobt_btrec_to_irec(mp, rec, irec);
fa = xfs_inobt_check_irec(cur, irec);
if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino)) if (fa)
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)
goto out_bad_rec; goto out_bad_rec;
return 0; return 0;
out_bad_rec: out_bad_rec:
xfs_warn(mp, 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_btnum == XFS_BTNUM_INO ? "Used" : "Free",
cur->bc_ag.pag->pag_agno); cur->bc_ag.pag->pag_agno, fa);
xfs_warn(mp, xfs_warn(mp,
"start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x", "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, irec->ir_startino, irec->ir_count, irec->ir_freecount,
...@@ -2690,6 +2704,9 @@ xfs_ialloc_count_inodes_rec( ...@@ -2690,6 +2704,9 @@ xfs_ialloc_count_inodes_rec(
struct xfs_ialloc_count_inodes *ci = priv; struct xfs_ialloc_count_inodes *ci = priv;
xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec); 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->count += irec.ir_count;
ci->freecount += irec.ir_freecount; ci->freecount += irec.ir_freecount;
......
...@@ -93,6 +93,8 @@ union xfs_btree_rec; ...@@ -93,6 +93,8 @@ union xfs_btree_rec;
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
const union xfs_btree_rec *rec, const union xfs_btree_rec *rec,
struct xfs_inobt_rec_incore *irec); 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, int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
xfs_agblock_t bno, xfs_extlen_t len, bool *exists); 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, int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
......
...@@ -608,7 +608,7 @@ xfs_iallocbt_maxlevels_ondisk(void) ...@@ -608,7 +608,7 @@ xfs_iallocbt_maxlevels_ondisk(void)
*/ */
uint64_t uint64_t
xfs_inobt_irec_to_allocmask( xfs_inobt_irec_to_allocmask(
struct xfs_inobt_rec_incore *rec) const struct xfs_inobt_rec_incore *rec)
{ {
uint64_t bitmap = 0; uint64_t bitmap = 0;
uint64_t inodespbit; uint64_t inodespbit;
......
...@@ -53,7 +53,7 @@ struct xfs_btree_cur *xfs_inobt_stage_cursor(struct xfs_perag *pag, ...@@ -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); extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
/* ir_holemask to inode allocation bitmap conversion */ /* 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) #if defined(DEBUG) || defined(XFS_WARN)
int xfs_inobt_rec_check_count(struct xfs_mount *, int xfs_inobt_rec_check_count(struct xfs_mount *,
......
...@@ -119,15 +119,6 @@ xchk_iallocbt_chunk( ...@@ -119,15 +119,6 @@ xchk_iallocbt_chunk(
return true; return true;
} }
/* Count the number of free inodes. */
static unsigned int
xchk_iallocbt_freecount(
xfs_inofree_t freemask)
{
BUILD_BUG_ON(sizeof(freemask) != sizeof(__u64));
return hweight64(freemask);
}
/* /*
* Check that an inode's allocation status matches ir_free in the inobt * Check that an inode's allocation status matches ir_free in the inobt
* record. First we try querying the in-core inode state, and if the inode * record. First we try querying the in-core inode state, and if the inode
...@@ -431,24 +422,17 @@ xchk_iallocbt_rec( ...@@ -431,24 +422,17 @@ xchk_iallocbt_rec(
int holecount; int holecount;
int i; int i;
int error = 0; int error = 0;
unsigned int real_freecount;
uint16_t holemask; uint16_t holemask;
xfs_inobt_btrec_to_irec(mp, rec, &irec); xfs_inobt_btrec_to_irec(mp, rec, &irec);
if (xfs_inobt_check_irec(bs->cur, &irec) != NULL) {
if (irec.ir_count > XFS_INODES_PER_CHUNK ||
irec.ir_freecount > XFS_INODES_PER_CHUNK)
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
real_freecount = irec.ir_freecount +
(XFS_INODES_PER_CHUNK - irec.ir_count);
if (real_freecount != xchk_iallocbt_freecount(irec.ir_free))
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
return 0;
}
agino = irec.ir_startino; agino = irec.ir_startino;
/* Record has to be properly aligned within the AG. */ /* Record has to be properly aligned within the AG. */
if (!xfs_verify_agino(pag, agino) || if (!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment