Skip to content

Commit 366a0b8

Browse files
author
Darrick J. Wong
committed
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>
1 parent 35e3b9a commit 366a0b8

5 files changed

Lines changed: 43 additions & 40 deletions

File tree

fs/xfs/libxfs/xfs_ialloc.c

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,33 @@ xfs_inobt_btrec_to_irec(
9595
irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
9696
}
9797

98+
/* Simple checks for inode records. */
99+
xfs_failaddr_t
100+
xfs_inobt_check_irec(
101+
struct xfs_btree_cur *cur,
102+
const struct xfs_inobt_rec_incore *irec)
103+
{
104+
uint64_t realfree;
105+
106+
if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
107+
return __this_address;
108+
if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
109+
irec->ir_count > XFS_INODES_PER_CHUNK)
110+
return __this_address;
111+
if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
112+
return __this_address;
113+
114+
/* if there are no holes, return the first available offset */
115+
if (!xfs_inobt_issparse(irec->ir_holemask))
116+
realfree = irec->ir_free;
117+
else
118+
realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
119+
if (hweight64(realfree) != irec->ir_freecount)
120+
return __this_address;
121+
122+
return NULL;
123+
}
124+
98125
/*
99126
* Get the data from the pointed-to record.
100127
*/
@@ -106,38 +133,25 @@ xfs_inobt_get_rec(
106133
{
107134
struct xfs_mount *mp = cur->bc_mp;
108135
union xfs_btree_rec *rec;
136+
xfs_failaddr_t fa;
109137
int error;
110-
uint64_t realfree;
111138

112139
error = xfs_btree_get_rec(cur, &rec, stat);
113140
if (error || *stat == 0)
114141
return error;
115142

116143
xfs_inobt_btrec_to_irec(mp, rec, irec);
117-
118-
if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
119-
goto out_bad_rec;
120-
if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
121-
irec->ir_count > XFS_INODES_PER_CHUNK)
122-
goto out_bad_rec;
123-
if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
124-
goto out_bad_rec;
125-
126-
/* if there are no holes, return the first available offset */
127-
if (!xfs_inobt_issparse(irec->ir_holemask))
128-
realfree = irec->ir_free;
129-
else
130-
realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
131-
if (hweight64(realfree) != irec->ir_freecount)
144+
fa = xfs_inobt_check_irec(cur, irec);
145+
if (fa)
132146
goto out_bad_rec;
133147

134148
return 0;
135149

136150
out_bad_rec:
137151
xfs_warn(mp,
138-
"%s Inode BTree record corruption in AG %d detected!",
152+
"%s Inode BTree record corruption in AG %d detected at %pS!",
139153
cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free",
140-
cur->bc_ag.pag->pag_agno);
154+
cur->bc_ag.pag->pag_agno, fa);
141155
xfs_warn(mp,
142156
"start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x",
143157
irec->ir_startino, irec->ir_count, irec->ir_freecount,
@@ -2690,6 +2704,9 @@ xfs_ialloc_count_inodes_rec(
26902704
struct xfs_ialloc_count_inodes *ci = priv;
26912705

26922706
xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
2707+
if (xfs_inobt_check_irec(cur, &irec) != NULL)
2708+
return -EFSCORRUPTED;
2709+
26932710
ci->count += irec.ir_count;
26942711
ci->freecount += irec.ir_freecount;
26952712

fs/xfs/libxfs/xfs_ialloc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ union xfs_btree_rec;
9393
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
9494
const union xfs_btree_rec *rec,
9595
struct xfs_inobt_rec_incore *irec);
96+
xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
97+
const struct xfs_inobt_rec_incore *irec);
9698
int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
9799
xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
98100
int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,

fs/xfs/libxfs/xfs_ialloc_btree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ xfs_iallocbt_maxlevels_ondisk(void)
608608
*/
609609
uint64_t
610610
xfs_inobt_irec_to_allocmask(
611-
struct xfs_inobt_rec_incore *rec)
611+
const struct xfs_inobt_rec_incore *rec)
612612
{
613613
uint64_t bitmap = 0;
614614
uint64_t inodespbit;

fs/xfs/libxfs/xfs_ialloc_btree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ struct xfs_btree_cur *xfs_inobt_stage_cursor(struct xfs_perag *pag,
5353
extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
5454

5555
/* ir_holemask to inode allocation bitmap conversion */
56-
uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *);
56+
uint64_t xfs_inobt_irec_to_allocmask(const struct xfs_inobt_rec_incore *irec);
5757

5858
#if defined(DEBUG) || defined(XFS_WARN)
5959
int xfs_inobt_rec_check_count(struct xfs_mount *,

fs/xfs/scrub/ialloc.c

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,6 @@ xchk_iallocbt_chunk(
119119
return true;
120120
}
121121

122-
/* Count the number of free inodes. */
123-
static unsigned int
124-
xchk_iallocbt_freecount(
125-
xfs_inofree_t freemask)
126-
{
127-
BUILD_BUG_ON(sizeof(freemask) != sizeof(__u64));
128-
return hweight64(freemask);
129-
}
130-
131122
/*
132123
* Check that an inode's allocation status matches ir_free in the inobt
133124
* record. First we try querying the in-core inode state, and if the inode
@@ -431,24 +422,17 @@ xchk_iallocbt_rec(
431422
int holecount;
432423
int i;
433424
int error = 0;
434-
unsigned int real_freecount;
435425
uint16_t holemask;
436426

437427
xfs_inobt_btrec_to_irec(mp, rec, &irec);
438-
439-
if (irec.ir_count > XFS_INODES_PER_CHUNK ||
440-
irec.ir_freecount > XFS_INODES_PER_CHUNK)
441-
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
442-
443-
real_freecount = irec.ir_freecount +
444-
(XFS_INODES_PER_CHUNK - irec.ir_count);
445-
if (real_freecount != xchk_iallocbt_freecount(irec.ir_free))
428+
if (xfs_inobt_check_irec(bs->cur, &irec) != NULL) {
446429
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
430+
return 0;
431+
}
447432

448433
agino = irec.ir_startino;
449434
/* Record has to be properly aligned within the AG. */
450-
if (!xfs_verify_agino(pag, agino) ||
451-
!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
435+
if (!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
452436
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
453437
goto out;
454438
}

0 commit comments

Comments
 (0)