mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
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: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
parent
35e3b9a117
commit
366a0b8d49
@ -95,6 +95,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.
|
||||
*/
|
||||
@ -106,38 +133,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,
|
||||
@ -2690,6 +2704,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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -608,7 +608,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;
|
||||
|
@ -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 *,
|
||||
|
@ -119,15 +119,6 @@ xchk_iallocbt_chunk(
|
||||
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
|
||||
* record. First we try querying the in-core inode state, and if the inode
|
||||
@ -431,24 +422,17 @@ xchk_iallocbt_rec(
|
||||
int holecount;
|
||||
int i;
|
||||
int error = 0;
|
||||
unsigned int real_freecount;
|
||||
uint16_t holemask;
|
||||
|
||||
xfs_inobt_btrec_to_irec(mp, rec, &irec);
|
||||
|
||||
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))
|
||||
if (xfs_inobt_check_irec(bs->cur, &irec) != NULL) {
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
agino = irec.ir_startino;
|
||||
/* Record has to be properly aligned within the AG. */
|
||||
if (!xfs_verify_agino(pag, agino) ||
|
||||
!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
|
||||
if (!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
goto out;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user