mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 12:14:06 +00:00
xfs: standardize btree record checking code [v24.5]
While I was cleaning things up for 6.1, I noticed that the btree _query_range and _query_all functions don't perform the same checking that the _get_rec functions perform. In fact, they don't perform /any/ sanity checking, which means that callers aren't warned about impossible records. Therefore, hoist the record validation and complaint logging code into separate functions, and call them from any place where we convert an ondisk record into an incore record. For online scrub, we can replace checking code with a call to the record checking functions in libxfs, thereby reducing the size of the codebase. Signed-off-by: Darrick J. Wong <djwong@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQ2qTKExjcn+O1o2YRKO3ySh0YRpgUCZDYdnwAKCRBKO3ySh0YR ppn6AQCOWjqsq7klLAQdvEDm3O8v4k94geKdn4Ruvbptwa2iUQD/WAJ5LwKnEPuQ +eB5AfzsziMQMNX7DtUwncaDJm1RBgY= =ys9Z -----END PGP SIGNATURE----- Merge tag 'btree-complain-bad-records-6.4_2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into guilt/xfs-for-next xfs: standardize btree record checking code [v24.5] While I was cleaning things up for 6.1, I noticed that the btree _query_range and _query_all functions don't perform the same checking that the _get_rec functions perform. In fact, they don't perform /any/ sanity checking, which means that callers aren't warned about impossible records. Therefore, hoist the record validation and complaint logging code into separate functions, and call them from any place where we convert an ondisk record into an incore record. For online scrub, we can replace checking code with a call to the record checking functions in libxfs, thereby reducing the size of the codebase. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
commit
01822a74ca
18 changed files with 308 additions and 203 deletions
|
@ -233,6 +233,52 @@ xfs_alloc_update(
|
|||
return xfs_btree_update(cur, &rec);
|
||||
}
|
||||
|
||||
/* Convert the ondisk btree record to its incore representation. */
|
||||
void
|
||||
xfs_alloc_btrec_to_irec(
|
||||
const union xfs_btree_rec *rec,
|
||||
struct xfs_alloc_rec_incore *irec)
|
||||
{
|
||||
irec->ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
|
||||
irec->ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
|
||||
}
|
||||
|
||||
/* Simple checks for free space records. */
|
||||
xfs_failaddr_t
|
||||
xfs_alloc_check_irec(
|
||||
struct xfs_btree_cur *cur,
|
||||
const struct xfs_alloc_rec_incore *irec)
|
||||
{
|
||||
struct xfs_perag *pag = cur->bc_ag.pag;
|
||||
|
||||
if (irec->ar_blockcount == 0)
|
||||
return __this_address;
|
||||
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(pag, irec->ar_startblock, irec->ar_blockcount))
|
||||
return __this_address;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
xfs_alloc_complain_bad_rec(
|
||||
struct xfs_btree_cur *cur,
|
||||
xfs_failaddr_t fa,
|
||||
const struct xfs_alloc_rec_incore *irec)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
|
||||
xfs_warn(mp,
|
||||
"%s Freespace BTree record corruption in AG %d detected at %pS!",
|
||||
cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
|
||||
cur->bc_ag.pag->pag_agno, fa);
|
||||
xfs_warn(mp,
|
||||
"start block 0x%x block count 0x%x", irec->ar_startblock,
|
||||
irec->ar_blockcount);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the pointed-to record.
|
||||
*/
|
||||
|
@ -243,35 +289,23 @@ xfs_alloc_get_rec(
|
|||
xfs_extlen_t *len, /* output: length of extent */
|
||||
int *stat) /* output: success/failure */
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
struct xfs_perag *pag = cur->bc_ag.pag;
|
||||
struct xfs_alloc_rec_incore irec;
|
||||
union xfs_btree_rec *rec;
|
||||
xfs_failaddr_t fa;
|
||||
int error;
|
||||
|
||||
error = xfs_btree_get_rec(cur, &rec, stat);
|
||||
if (error || !(*stat))
|
||||
return error;
|
||||
|
||||
*bno = be32_to_cpu(rec->alloc.ar_startblock);
|
||||
*len = be32_to_cpu(rec->alloc.ar_blockcount);
|
||||
|
||||
if (*len == 0)
|
||||
goto out_bad_rec;
|
||||
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(pag, *bno, *len))
|
||||
goto out_bad_rec;
|
||||
xfs_alloc_btrec_to_irec(rec, &irec);
|
||||
fa = xfs_alloc_check_irec(cur, &irec);
|
||||
if (fa)
|
||||
return xfs_alloc_complain_bad_rec(cur, fa, &irec);
|
||||
|
||||
*bno = irec.ar_startblock;
|
||||
*len = irec.ar_blockcount;
|
||||
return 0;
|
||||
|
||||
out_bad_rec:
|
||||
xfs_warn(mp,
|
||||
"%s Freespace BTree record corruption in AG %d detected!",
|
||||
cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
|
||||
pag->pag_agno);
|
||||
xfs_warn(mp,
|
||||
"start block 0x%x block count 0x%x", *bno, *len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3664,9 +3698,13 @@ xfs_alloc_query_range_helper(
|
|||
{
|
||||
struct xfs_alloc_query_range_info *query = priv;
|
||||
struct xfs_alloc_rec_incore irec;
|
||||
xfs_failaddr_t fa;
|
||||
|
||||
xfs_alloc_btrec_to_irec(rec, &irec);
|
||||
fa = xfs_alloc_check_irec(cur, &irec);
|
||||
if (fa)
|
||||
return xfs_alloc_complain_bad_rec(cur, fa, &irec);
|
||||
|
||||
irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
|
||||
irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
|
||||
return query->fn(cur, &irec, query->priv);
|
||||
}
|
||||
|
||||
|
|
|
@ -181,6 +181,12 @@ xfs_alloc_get_rec(
|
|||
xfs_extlen_t *len, /* output: length of extent */
|
||||
int *stat); /* output: success/failure */
|
||||
|
||||
union xfs_btree_rec;
|
||||
void xfs_alloc_btrec_to_irec(const union xfs_btree_rec *rec,
|
||||
struct xfs_alloc_rec_incore *irec);
|
||||
xfs_failaddr_t xfs_alloc_check_irec(struct xfs_btree_cur *cur,
|
||||
const struct xfs_alloc_rec_incore *irec);
|
||||
|
||||
int xfs_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
|
||||
struct xfs_buf **agfbpp);
|
||||
int xfs_alloc_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
|
||||
|
|
|
@ -1083,6 +1083,34 @@ struct xfs_iread_state {
|
|||
xfs_extnum_t loaded;
|
||||
};
|
||||
|
||||
int
|
||||
xfs_bmap_complain_bad_rec(
|
||||
struct xfs_inode *ip,
|
||||
int whichfork,
|
||||
xfs_failaddr_t fa,
|
||||
const struct xfs_bmbt_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
const char *forkname;
|
||||
|
||||
switch (whichfork) {
|
||||
case XFS_DATA_FORK: forkname = "data"; break;
|
||||
case XFS_ATTR_FORK: forkname = "attr"; break;
|
||||
case XFS_COW_FORK: forkname = "CoW"; break;
|
||||
default: forkname = "???"; break;
|
||||
}
|
||||
|
||||
xfs_warn(mp,
|
||||
"Bmap BTree record corruption in inode 0x%llx %s fork detected at %pS!",
|
||||
ip->i_ino, forkname, fa);
|
||||
xfs_warn(mp,
|
||||
"Offset 0x%llx, start block 0x%llx, block count 0x%llx state 0x%x",
|
||||
irec->br_startoff, irec->br_startblock, irec->br_blockcount,
|
||||
irec->br_state);
|
||||
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/* Stuff every bmbt record from this block into the incore extent map. */
|
||||
static int
|
||||
xfs_iread_bmbt_block(
|
||||
|
@ -1125,7 +1153,8 @@ xfs_iread_bmbt_block(
|
|||
xfs_inode_verifier_error(ip, -EFSCORRUPTED,
|
||||
"xfs_iread_extents(2)", frp,
|
||||
sizeof(*frp), fa);
|
||||
return -EFSCORRUPTED;
|
||||
return xfs_bmap_complain_bad_rec(ip, whichfork, fa,
|
||||
&new);
|
||||
}
|
||||
xfs_iext_insert(ip, &ir->icur, &new,
|
||||
xfs_bmap_fork_to_state(whichfork));
|
||||
|
|
|
@ -265,6 +265,8 @@ static inline uint32_t xfs_bmap_fork_to_state(int whichfork)
|
|||
|
||||
xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
|
||||
struct xfs_bmbt_irec *irec);
|
||||
int xfs_bmap_complain_bad_rec(struct xfs_inode *ip, int whichfork,
|
||||
xfs_failaddr_t fa, const struct xfs_bmbt_irec *irec);
|
||||
|
||||
int xfs_bmapi_remap(struct xfs_trans *tp, struct xfs_inode *ip,
|
||||
xfs_fileoff_t bno, xfs_filblks_t len, xfs_fsblock_t startblock,
|
||||
|
|
|
@ -95,6 +95,52 @@ 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;
|
||||
}
|
||||
|
||||
static inline int
|
||||
xfs_inobt_complain_bad_rec(
|
||||
struct xfs_btree_cur *cur,
|
||||
xfs_failaddr_t fa,
|
||||
const struct xfs_inobt_rec_incore *irec)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
|
||||
xfs_warn(mp,
|
||||
"%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, 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,
|
||||
irec->ir_free, irec->ir_holemask);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the pointed-to record.
|
||||
*/
|
||||
|
@ -106,43 +152,19 @@ 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)
|
||||
goto out_bad_rec;
|
||||
fa = xfs_inobt_check_irec(cur, irec);
|
||||
if (fa)
|
||||
return xfs_inobt_complain_bad_rec(cur, fa, irec);
|
||||
|
||||
return 0;
|
||||
|
||||
out_bad_rec:
|
||||
xfs_warn(mp,
|
||||
"%s Inode BTree record corruption in AG %d detected!",
|
||||
cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free",
|
||||
cur->bc_ag.pag->pag_agno);
|
||||
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,
|
||||
irec->ir_free, irec->ir_holemask);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2688,8 +2710,13 @@ xfs_ialloc_count_inodes_rec(
|
|||
{
|
||||
struct xfs_inobt_rec_incore irec;
|
||||
struct xfs_ialloc_count_inodes *ci = priv;
|
||||
xfs_failaddr_t fa;
|
||||
|
||||
xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
|
||||
fa = xfs_inobt_check_irec(cur, &irec);
|
||||
if (fa)
|
||||
return xfs_inobt_complain_bad_rec(cur, fa, &irec);
|
||||
|
||||
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 *,
|
||||
|
|
|
@ -140,7 +140,8 @@ xfs_iformat_extents(
|
|||
xfs_inode_verifier_error(ip, -EFSCORRUPTED,
|
||||
"xfs_iformat_extents(2)",
|
||||
dp, sizeof(*dp), fa);
|
||||
return -EFSCORRUPTED;
|
||||
return xfs_bmap_complain_bad_rec(ip, whichfork,
|
||||
fa, &new);
|
||||
}
|
||||
|
||||
xfs_iext_insert(ip, &icur, &new, state);
|
||||
|
|
|
@ -120,6 +120,47 @@ 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;
|
||||
}
|
||||
|
||||
static inline int
|
||||
xfs_refcount_complain_bad_rec(
|
||||
struct xfs_btree_cur *cur,
|
||||
xfs_failaddr_t fa,
|
||||
const struct xfs_refcount_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
|
||||
xfs_warn(mp,
|
||||
"Refcount BTree record corruption in AG %d detected at %pS!",
|
||||
cur->bc_ag.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);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the pointed-to record.
|
||||
*/
|
||||
|
@ -129,9 +170,8 @@ xfs_refcount_get_rec(
|
|||
struct xfs_refcount_irec *irec,
|
||||
int *stat)
|
||||
{
|
||||
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,30 +179,12 @@ 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;
|
||||
fa = xfs_refcount_check_irec(cur, irec);
|
||||
if (fa)
|
||||
return xfs_refcount_complain_bad_rec(cur, fa, irec);
|
||||
|
||||
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)
|
||||
goto out_bad_rec;
|
||||
|
||||
trace_xfs_refcount_get(cur->bc_mp, pag->pag_agno, irec);
|
||||
trace_xfs_refcount_get(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec);
|
||||
return 0;
|
||||
|
||||
out_bad_rec:
|
||||
xfs_warn(mp,
|
||||
"Refcount BTree record corruption in AG %d detected!",
|
||||
pag->pag_agno);
|
||||
xfs_warn(mp,
|
||||
"Start block 0x%x, block count 0x%x, references 0x%x",
|
||||
irec->rc_startblock, irec->rc_blockcount, irec->rc_refcount);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1871,7 +1893,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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ done:
|
|||
}
|
||||
|
||||
/* Convert an internal btree record to an rmap record. */
|
||||
int
|
||||
xfs_failaddr_t
|
||||
xfs_rmap_btrec_to_irec(
|
||||
const union xfs_btree_rec *rec,
|
||||
struct xfs_rmap_irec *irec)
|
||||
|
@ -205,6 +205,54 @@ xfs_rmap_btrec_to_irec(
|
|||
irec);
|
||||
}
|
||||
|
||||
/* Simple checks for rmap records. */
|
||||
xfs_failaddr_t
|
||||
xfs_rmap_check_irec(
|
||||
struct xfs_btree_cur *cur,
|
||||
const struct xfs_rmap_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
|
||||
if (irec->rm_blockcount == 0)
|
||||
return __this_address;
|
||||
if (irec->rm_startblock <= XFS_AGFL_BLOCK(mp)) {
|
||||
if (irec->rm_owner != XFS_RMAP_OWN_FS)
|
||||
return __this_address;
|
||||
if (irec->rm_blockcount != XFS_AGFL_BLOCK(mp) + 1)
|
||||
return __this_address;
|
||||
} else {
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(cur->bc_ag.pag, irec->rm_startblock,
|
||||
irec->rm_blockcount))
|
||||
return __this_address;
|
||||
}
|
||||
|
||||
if (!(xfs_verify_ino(mp, irec->rm_owner) ||
|
||||
(irec->rm_owner <= XFS_RMAP_OWN_FS &&
|
||||
irec->rm_owner >= XFS_RMAP_OWN_MIN)))
|
||||
return __this_address;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
xfs_rmap_complain_bad_rec(
|
||||
struct xfs_btree_cur *cur,
|
||||
xfs_failaddr_t fa,
|
||||
const struct xfs_rmap_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
|
||||
xfs_warn(mp,
|
||||
"Reverse Mapping BTree record corruption in AG %d detected at %pS!",
|
||||
cur->bc_ag.pag->pag_agno, fa);
|
||||
xfs_warn(mp,
|
||||
"Owner 0x%llx, flags 0x%x, start block 0x%x block count 0x%x",
|
||||
irec->rm_owner, irec->rm_flags, irec->rm_startblock,
|
||||
irec->rm_blockcount);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the pointed-to record.
|
||||
*/
|
||||
|
@ -214,47 +262,21 @@ xfs_rmap_get_rec(
|
|||
struct xfs_rmap_irec *irec,
|
||||
int *stat)
|
||||
{
|
||||
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);
|
||||
if (error || !*stat)
|
||||
return error;
|
||||
|
||||
if (xfs_rmap_btrec_to_irec(rec, irec))
|
||||
goto out_bad_rec;
|
||||
|
||||
if (irec->rm_blockcount == 0)
|
||||
goto out_bad_rec;
|
||||
if (irec->rm_startblock <= XFS_AGFL_BLOCK(mp)) {
|
||||
if (irec->rm_owner != XFS_RMAP_OWN_FS)
|
||||
goto out_bad_rec;
|
||||
if (irec->rm_blockcount != XFS_AGFL_BLOCK(mp) + 1)
|
||||
goto out_bad_rec;
|
||||
} else {
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(pag, irec->rm_startblock,
|
||||
irec->rm_blockcount))
|
||||
goto out_bad_rec;
|
||||
}
|
||||
|
||||
if (!(xfs_verify_ino(mp, irec->rm_owner) ||
|
||||
(irec->rm_owner <= XFS_RMAP_OWN_FS &&
|
||||
irec->rm_owner >= XFS_RMAP_OWN_MIN)))
|
||||
goto out_bad_rec;
|
||||
fa = xfs_rmap_btrec_to_irec(rec, irec);
|
||||
if (!fa)
|
||||
fa = xfs_rmap_check_irec(cur, irec);
|
||||
if (fa)
|
||||
return xfs_rmap_complain_bad_rec(cur, fa, irec);
|
||||
|
||||
return 0;
|
||||
out_bad_rec:
|
||||
xfs_warn(mp,
|
||||
"Reverse Mapping BTree record corruption in AG %d detected!",
|
||||
pag->pag_agno);
|
||||
xfs_warn(mp,
|
||||
"Owner 0x%llx, flags 0x%x, start block 0x%x block count 0x%x",
|
||||
irec->rm_owner, irec->rm_flags, irec->rm_startblock,
|
||||
irec->rm_blockcount);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
struct xfs_find_left_neighbor_info {
|
||||
|
@ -2320,11 +2342,14 @@ xfs_rmap_query_range_helper(
|
|||
{
|
||||
struct xfs_rmap_query_range_info *query = priv;
|
||||
struct xfs_rmap_irec irec;
|
||||
int error;
|
||||
xfs_failaddr_t fa;
|
||||
|
||||
fa = xfs_rmap_btrec_to_irec(rec, &irec);
|
||||
if (!fa)
|
||||
fa = xfs_rmap_check_irec(cur, &irec);
|
||||
if (fa)
|
||||
return xfs_rmap_complain_bad_rec(cur, fa, &irec);
|
||||
|
||||
error = xfs_rmap_btrec_to_irec(rec, &irec);
|
||||
if (error)
|
||||
return error;
|
||||
return query->fn(cur, &irec, query->priv);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,13 +62,14 @@ xfs_rmap_irec_offset_pack(
|
|||
return x;
|
||||
}
|
||||
|
||||
static inline int
|
||||
static inline xfs_failaddr_t
|
||||
xfs_rmap_irec_offset_unpack(
|
||||
__u64 offset,
|
||||
struct xfs_rmap_irec *irec)
|
||||
{
|
||||
if (offset & ~(XFS_RMAP_OFF_MASK | XFS_RMAP_OFF_FLAGS))
|
||||
return -EFSCORRUPTED;
|
||||
return __this_address;
|
||||
|
||||
irec->rm_offset = XFS_RMAP_OFF(offset);
|
||||
irec->rm_flags = 0;
|
||||
if (offset & XFS_RMAP_OFF_ATTR_FORK)
|
||||
|
@ -77,7 +78,7 @@ xfs_rmap_irec_offset_unpack(
|
|||
irec->rm_flags |= XFS_RMAP_BMBT_BLOCK;
|
||||
if (offset & XFS_RMAP_OFF_UNWRITTEN)
|
||||
irec->rm_flags |= XFS_RMAP_UNWRITTEN;
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -192,8 +193,11 @@ int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
|
|||
int xfs_rmap_compare(const struct xfs_rmap_irec *a,
|
||||
const struct xfs_rmap_irec *b);
|
||||
union xfs_btree_rec;
|
||||
int xfs_rmap_btrec_to_irec(const union xfs_btree_rec *rec,
|
||||
xfs_failaddr_t xfs_rmap_btrec_to_irec(const union xfs_btree_rec *rec,
|
||||
struct xfs_rmap_irec *irec);
|
||||
xfs_failaddr_t xfs_rmap_check_irec(struct xfs_btree_cur *cur,
|
||||
const struct xfs_rmap_irec *irec);
|
||||
|
||||
int xfs_rmap_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
|
||||
xfs_extlen_t len, bool *exists);
|
||||
int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno,
|
||||
|
|
|
@ -78,9 +78,11 @@ xchk_allocbt_xref_other(
|
|||
STATIC void
|
||||
xchk_allocbt_xref(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_agblock_t agbno,
|
||||
xfs_extlen_t len)
|
||||
const struct xfs_alloc_rec_incore *irec)
|
||||
{
|
||||
xfs_agblock_t agbno = irec->ar_startblock;
|
||||
xfs_extlen_t len = irec->ar_blockcount;
|
||||
|
||||
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
return;
|
||||
|
||||
|
@ -93,20 +95,18 @@ xchk_allocbt_xref(
|
|||
/* Scrub a bnobt/cntbt record. */
|
||||
STATIC int
|
||||
xchk_allocbt_rec(
|
||||
struct xchk_btree *bs,
|
||||
const union xfs_btree_rec *rec)
|
||||
struct xchk_btree *bs,
|
||||
const union xfs_btree_rec *rec)
|
||||
{
|
||||
struct xfs_perag *pag = bs->cur->bc_ag.pag;
|
||||
xfs_agblock_t bno;
|
||||
xfs_extlen_t len;
|
||||
struct xfs_alloc_rec_incore irec;
|
||||
|
||||
bno = be32_to_cpu(rec->alloc.ar_startblock);
|
||||
len = be32_to_cpu(rec->alloc.ar_blockcount);
|
||||
|
||||
if (!xfs_verify_agbext(pag, bno, len))
|
||||
xfs_alloc_btrec_to_irec(rec, &irec);
|
||||
if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) {
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xchk_allocbt_xref(bs->sc, bno, len);
|
||||
xchk_allocbt_xref(bs->sc, &irec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -471,6 +471,12 @@ xchk_bmapbt_rec(
|
|||
return 0;
|
||||
|
||||
xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
|
||||
if (xfs_bmap_validate_extent(ip, info->whichfork, &irec) != NULL) {
|
||||
xchk_fblock_set_corrupt(bs->sc, info->whichfork,
|
||||
irec.br_startoff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!xfs_iext_lookup_extent(ip, ifp, irec.br_startoff, &icur,
|
||||
&iext_irec) ||
|
||||
irec.br_startoff != iext_irec.br_startoff ||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -93,41 +93,16 @@ xchk_rmapbt_rec(
|
|||
struct xchk_btree *bs,
|
||||
const union xfs_btree_rec *rec)
|
||||
{
|
||||
struct xfs_mount *mp = bs->cur->bc_mp;
|
||||
struct xfs_rmap_irec irec;
|
||||
struct xfs_perag *pag = bs->cur->bc_ag.pag;
|
||||
bool non_inode;
|
||||
bool is_unwritten;
|
||||
bool is_bmbt;
|
||||
bool is_attr;
|
||||
int error;
|
||||
|
||||
error = xfs_rmap_btrec_to_irec(rec, &irec);
|
||||
if (!xchk_btree_process_error(bs->sc, bs->cur, 0, &error))
|
||||
goto out;
|
||||
|
||||
/* Check extent. */
|
||||
if (irec.rm_startblock + irec.rm_blockcount <= irec.rm_startblock)
|
||||
if (xfs_rmap_btrec_to_irec(rec, &irec) != NULL ||
|
||||
xfs_rmap_check_irec(bs->cur, &irec) != NULL) {
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
|
||||
if (irec.rm_owner == XFS_RMAP_OWN_FS) {
|
||||
/*
|
||||
* xfs_verify_agbno returns false for static fs metadata.
|
||||
* Since that only exists at the start of the AG, validate
|
||||
* that by hand.
|
||||
*/
|
||||
if (irec.rm_startblock != 0 ||
|
||||
irec.rm_blockcount != XFS_AGFL_BLOCK(mp) + 1)
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
} else {
|
||||
/*
|
||||
* Otherwise we must point somewhere past the static metadata
|
||||
* but before the end of the FS. Run the regular check.
|
||||
*/
|
||||
if (!xfs_verify_agbno(pag, irec.rm_startblock) ||
|
||||
!xfs_verify_agbno(pag, irec.rm_startblock +
|
||||
irec.rm_blockcount - 1))
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check flags. */
|
||||
|
@ -148,19 +123,8 @@ xchk_rmapbt_rec(
|
|||
if (non_inode && (is_bmbt || is_unwritten || is_attr))
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
|
||||
if (!non_inode) {
|
||||
if (!xfs_verify_ino(mp, irec.rm_owner))
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
} else {
|
||||
/* Non-inode owner within the magic values? */
|
||||
if (irec.rm_owner <= XFS_RMAP_OWN_MIN ||
|
||||
irec.rm_owner > XFS_RMAP_OWN_FS)
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
}
|
||||
|
||||
xchk_rmapbt_xref(bs->sc, &irec);
|
||||
out:
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Scrub the rmap btree for some AG. */
|
||||
|
|
Loading…
Add table
Reference in a new issue