Skip to content

Commit 01822a7

Browse files
dchinnerdgchinner
authored andcommitted
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>
2 parents b634aba + 6a3bd8f commit 01822a7

18 files changed

Lines changed: 303 additions & 198 deletions

fs/xfs/libxfs/xfs_alloc.c

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,52 @@ xfs_alloc_update(
233233
return xfs_btree_update(cur, &rec);
234234
}
235235

236+
/* Convert the ondisk btree record to its incore representation. */
237+
void
238+
xfs_alloc_btrec_to_irec(
239+
const union xfs_btree_rec *rec,
240+
struct xfs_alloc_rec_incore *irec)
241+
{
242+
irec->ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
243+
irec->ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
244+
}
245+
246+
/* Simple checks for free space records. */
247+
xfs_failaddr_t
248+
xfs_alloc_check_irec(
249+
struct xfs_btree_cur *cur,
250+
const struct xfs_alloc_rec_incore *irec)
251+
{
252+
struct xfs_perag *pag = cur->bc_ag.pag;
253+
254+
if (irec->ar_blockcount == 0)
255+
return __this_address;
256+
257+
/* check for valid extent range, including overflow */
258+
if (!xfs_verify_agbext(pag, irec->ar_startblock, irec->ar_blockcount))
259+
return __this_address;
260+
261+
return NULL;
262+
}
263+
264+
static inline int
265+
xfs_alloc_complain_bad_rec(
266+
struct xfs_btree_cur *cur,
267+
xfs_failaddr_t fa,
268+
const struct xfs_alloc_rec_incore *irec)
269+
{
270+
struct xfs_mount *mp = cur->bc_mp;
271+
272+
xfs_warn(mp,
273+
"%s Freespace BTree record corruption in AG %d detected at %pS!",
274+
cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
275+
cur->bc_ag.pag->pag_agno, fa);
276+
xfs_warn(mp,
277+
"start block 0x%x block count 0x%x", irec->ar_startblock,
278+
irec->ar_blockcount);
279+
return -EFSCORRUPTED;
280+
}
281+
236282
/*
237283
* Get the data from the pointed-to record.
238284
*/
@@ -243,35 +289,23 @@ xfs_alloc_get_rec(
243289
xfs_extlen_t *len, /* output: length of extent */
244290
int *stat) /* output: success/failure */
245291
{
246-
struct xfs_mount *mp = cur->bc_mp;
247-
struct xfs_perag *pag = cur->bc_ag.pag;
292+
struct xfs_alloc_rec_incore irec;
248293
union xfs_btree_rec *rec;
294+
xfs_failaddr_t fa;
249295
int error;
250296

251297
error = xfs_btree_get_rec(cur, &rec, stat);
252298
if (error || !(*stat))
253299
return error;
254300

255-
*bno = be32_to_cpu(rec->alloc.ar_startblock);
256-
*len = be32_to_cpu(rec->alloc.ar_blockcount);
257-
258-
if (*len == 0)
259-
goto out_bad_rec;
260-
261-
/* check for valid extent range, including overflow */
262-
if (!xfs_verify_agbext(pag, *bno, *len))
263-
goto out_bad_rec;
301+
xfs_alloc_btrec_to_irec(rec, &irec);
302+
fa = xfs_alloc_check_irec(cur, &irec);
303+
if (fa)
304+
return xfs_alloc_complain_bad_rec(cur, fa, &irec);
264305

306+
*bno = irec.ar_startblock;
307+
*len = irec.ar_blockcount;
265308
return 0;
266-
267-
out_bad_rec:
268-
xfs_warn(mp,
269-
"%s Freespace BTree record corruption in AG %d detected!",
270-
cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
271-
pag->pag_agno);
272-
xfs_warn(mp,
273-
"start block 0x%x block count 0x%x", *bno, *len);
274-
return -EFSCORRUPTED;
275309
}
276310

277311
/*
@@ -3664,9 +3698,13 @@ xfs_alloc_query_range_helper(
36643698
{
36653699
struct xfs_alloc_query_range_info *query = priv;
36663700
struct xfs_alloc_rec_incore irec;
3701+
xfs_failaddr_t fa;
3702+
3703+
xfs_alloc_btrec_to_irec(rec, &irec);
3704+
fa = xfs_alloc_check_irec(cur, &irec);
3705+
if (fa)
3706+
return xfs_alloc_complain_bad_rec(cur, fa, &irec);
36673707

3668-
irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
3669-
irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
36703708
return query->fn(cur, &irec, query->priv);
36713709
}
36723710

fs/xfs/libxfs/xfs_alloc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ xfs_alloc_get_rec(
181181
xfs_extlen_t *len, /* output: length of extent */
182182
int *stat); /* output: success/failure */
183183

184+
union xfs_btree_rec;
185+
void xfs_alloc_btrec_to_irec(const union xfs_btree_rec *rec,
186+
struct xfs_alloc_rec_incore *irec);
187+
xfs_failaddr_t xfs_alloc_check_irec(struct xfs_btree_cur *cur,
188+
const struct xfs_alloc_rec_incore *irec);
189+
184190
int xfs_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
185191
struct xfs_buf **agfbpp);
186192
int xfs_alloc_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,34 @@ struct xfs_iread_state {
10831083
xfs_extnum_t loaded;
10841084
};
10851085

1086+
int
1087+
xfs_bmap_complain_bad_rec(
1088+
struct xfs_inode *ip,
1089+
int whichfork,
1090+
xfs_failaddr_t fa,
1091+
const struct xfs_bmbt_irec *irec)
1092+
{
1093+
struct xfs_mount *mp = ip->i_mount;
1094+
const char *forkname;
1095+
1096+
switch (whichfork) {
1097+
case XFS_DATA_FORK: forkname = "data"; break;
1098+
case XFS_ATTR_FORK: forkname = "attr"; break;
1099+
case XFS_COW_FORK: forkname = "CoW"; break;
1100+
default: forkname = "???"; break;
1101+
}
1102+
1103+
xfs_warn(mp,
1104+
"Bmap BTree record corruption in inode 0x%llx %s fork detected at %pS!",
1105+
ip->i_ino, forkname, fa);
1106+
xfs_warn(mp,
1107+
"Offset 0x%llx, start block 0x%llx, block count 0x%llx state 0x%x",
1108+
irec->br_startoff, irec->br_startblock, irec->br_blockcount,
1109+
irec->br_state);
1110+
1111+
return -EFSCORRUPTED;
1112+
}
1113+
10861114
/* Stuff every bmbt record from this block into the incore extent map. */
10871115
static int
10881116
xfs_iread_bmbt_block(
@@ -1125,7 +1153,8 @@ xfs_iread_bmbt_block(
11251153
xfs_inode_verifier_error(ip, -EFSCORRUPTED,
11261154
"xfs_iread_extents(2)", frp,
11271155
sizeof(*frp), fa);
1128-
return -EFSCORRUPTED;
1156+
return xfs_bmap_complain_bad_rec(ip, whichfork, fa,
1157+
&new);
11291158
}
11301159
xfs_iext_insert(ip, &ir->icur, &new,
11311160
xfs_bmap_fork_to_state(whichfork));

fs/xfs/libxfs/xfs_bmap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ static inline uint32_t xfs_bmap_fork_to_state(int whichfork)
265265

266266
xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
267267
struct xfs_bmbt_irec *irec);
268+
int xfs_bmap_complain_bad_rec(struct xfs_inode *ip, int whichfork,
269+
xfs_failaddr_t fa, const struct xfs_bmbt_irec *irec);
268270

269271
int xfs_bmapi_remap(struct xfs_trans *tp, struct xfs_inode *ip,
270272
xfs_fileoff_t bno, xfs_filblks_t len, xfs_fsblock_t startblock,

fs/xfs/libxfs/xfs_ialloc.c

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

98-
/*
99-
* Get the data from the pointed-to record.
100-
*/
101-
int
102-
xfs_inobt_get_rec(
103-
struct xfs_btree_cur *cur,
104-
struct xfs_inobt_rec_incore *irec,
105-
int *stat)
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)
106103
{
107-
struct xfs_mount *mp = cur->bc_mp;
108-
union xfs_btree_rec *rec;
109-
int error;
110104
uint64_t realfree;
111105

112-
error = xfs_btree_get_rec(cur, &rec, stat);
113-
if (error || *stat == 0)
114-
return error;
115-
116-
xfs_inobt_btrec_to_irec(mp, rec, irec);
117-
118106
if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
119-
goto out_bad_rec;
107+
return __this_address;
120108
if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
121109
irec->ir_count > XFS_INODES_PER_CHUNK)
122-
goto out_bad_rec;
110+
return __this_address;
123111
if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
124-
goto out_bad_rec;
112+
return __this_address;
125113

126114
/* if there are no holes, return the first available offset */
127115
if (!xfs_inobt_issparse(irec->ir_holemask))
128116
realfree = irec->ir_free;
129117
else
130118
realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
131119
if (hweight64(realfree) != irec->ir_freecount)
132-
goto out_bad_rec;
120+
return __this_address;
133121

134-
return 0;
122+
return NULL;
123+
}
124+
125+
static inline int
126+
xfs_inobt_complain_bad_rec(
127+
struct xfs_btree_cur *cur,
128+
xfs_failaddr_t fa,
129+
const struct xfs_inobt_rec_incore *irec)
130+
{
131+
struct xfs_mount *mp = cur->bc_mp;
135132

136-
out_bad_rec:
137133
xfs_warn(mp,
138-
"%s Inode BTree record corruption in AG %d detected!",
134+
"%s Inode BTree record corruption in AG %d detected at %pS!",
139135
cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free",
140-
cur->bc_ag.pag->pag_agno);
136+
cur->bc_ag.pag->pag_agno, fa);
141137
xfs_warn(mp,
142138
"start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x",
143139
irec->ir_startino, irec->ir_count, irec->ir_freecount,
144140
irec->ir_free, irec->ir_holemask);
145141
return -EFSCORRUPTED;
146142
}
147143

144+
/*
145+
* Get the data from the pointed-to record.
146+
*/
147+
int
148+
xfs_inobt_get_rec(
149+
struct xfs_btree_cur *cur,
150+
struct xfs_inobt_rec_incore *irec,
151+
int *stat)
152+
{
153+
struct xfs_mount *mp = cur->bc_mp;
154+
union xfs_btree_rec *rec;
155+
xfs_failaddr_t fa;
156+
int error;
157+
158+
error = xfs_btree_get_rec(cur, &rec, stat);
159+
if (error || *stat == 0)
160+
return error;
161+
162+
xfs_inobt_btrec_to_irec(mp, rec, irec);
163+
fa = xfs_inobt_check_irec(cur, irec);
164+
if (fa)
165+
return xfs_inobt_complain_bad_rec(cur, fa, irec);
166+
167+
return 0;
168+
}
169+
148170
/*
149171
* Insert a single inobt record. Cursor must already point to desired location.
150172
*/
@@ -2688,8 +2710,13 @@ xfs_ialloc_count_inodes_rec(
26882710
{
26892711
struct xfs_inobt_rec_incore irec;
26902712
struct xfs_ialloc_count_inodes *ci = priv;
2713+
xfs_failaddr_t fa;
26912714

26922715
xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
2716+
fa = xfs_inobt_check_irec(cur, &irec);
2717+
if (fa)
2718+
return xfs_inobt_complain_bad_rec(cur, fa, &irec);
2719+
26932720
ci->count += irec.ir_count;
26942721
ci->freecount += irec.ir_freecount;
26952722

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/libxfs/xfs_inode_fork.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ xfs_iformat_extents(
140140
xfs_inode_verifier_error(ip, -EFSCORRUPTED,
141141
"xfs_iformat_extents(2)",
142142
dp, sizeof(*dp), fa);
143-
return -EFSCORRUPTED;
143+
return xfs_bmap_complain_bad_rec(ip, whichfork,
144+
fa, &new);
144145
}
145146

146147
xfs_iext_insert(ip, &icur, &new, state);

0 commit comments

Comments
 (0)