|
|
@@ -125,16 +125,53 @@ xfs_refcount_get_rec(
|
|
|
struct xfs_refcount_irec *irec,
|
|
|
int *stat)
|
|
|
{
|
|
|
+ struct xfs_mount *mp = cur->bc_mp;
|
|
|
+ xfs_agnumber_t agno = cur->bc_private.a.agno;
|
|
|
union xfs_btree_rec *rec;
|
|
|
int error;
|
|
|
+ xfs_agblock_t realstart;
|
|
|
|
|
|
error = xfs_btree_get_rec(cur, &rec, stat);
|
|
|
- if (!error && *stat == 1) {
|
|
|
- xfs_refcount_btrec_to_irec(rec, irec);
|
|
|
- trace_xfs_refcount_get(cur->bc_mp, cur->bc_private.a.agno,
|
|
|
- irec);
|
|
|
+ if (error || !*stat)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ xfs_refcount_btrec_to_irec(rec, irec);
|
|
|
+
|
|
|
+ agno = cur->bc_private.a.agno;
|
|
|
+ if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
|
|
|
+ goto out_bad_rec;
|
|
|
+
|
|
|
+ /* handle special COW-staging state */
|
|
|
+ realstart = irec->rc_startblock;
|
|
|
+ if (realstart & XFS_REFC_COW_START) {
|
|
|
+ if (irec->rc_refcount != 1)
|
|
|
+ goto out_bad_rec;
|
|
|
+ realstart &= ~XFS_REFC_COW_START;
|
|
|
+ } else if (irec->rc_refcount < 2) {
|
|
|
+ goto out_bad_rec;
|
|
|
}
|
|
|
- return error;
|
|
|
+
|
|
|
+ /* check for valid extent range, including overflow */
|
|
|
+ if (!xfs_verify_agbno(mp, agno, realstart))
|
|
|
+ goto out_bad_rec;
|
|
|
+ if (realstart > realstart + irec->rc_blockcount)
|
|
|
+ goto out_bad_rec;
|
|
|
+ if (!xfs_verify_agbno(mp, agno, realstart + irec->rc_blockcount - 1))
|
|
|
+ goto out_bad_rec;
|
|
|
+
|
|
|
+ if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
|
|
|
+ goto out_bad_rec;
|
|
|
+
|
|
|
+ trace_xfs_refcount_get(cur->bc_mp, cur->bc_private.a.agno, irec);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_bad_rec:
|
|
|
+ xfs_warn(mp,
|
|
|
+ "Refcount BTree record corruption in AG %d detected!", 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;
|
|
|
}
|
|
|
|
|
|
/*
|