Skip to content

Commit 29ab991

Browse files
author
Darrick J. Wong
committed
xfs: check overlapping rmap btree records
The rmap btree scrubber doesn't contain sufficient checking for records that cannot overlap but do anyway. For the other btrees, this is enforced by the inorder checks in xchk_btree_rec, but the rmap btree is special because it allows overlapping records to handle shared data extents. Therefore, enhance the rmap btree record check function to compare each record against the previous one so that we can detect overlapping rmap records for space allocations that do not allow sharing. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent db0502b commit 29ab991

1 file changed

Lines changed: 72 additions & 2 deletions

File tree

fs/xfs/scrub/rmap.c

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ xchk_setup_ag_rmapbt(
3232

3333
/* Reverse-mapping scrubber. */
3434

35+
struct xchk_rmap {
36+
/*
37+
* The furthest-reaching of the rmapbt records that we've already
38+
* processed. This enables us to detect overlapping records for space
39+
* allocations that cannot be shared.
40+
*/
41+
struct xfs_rmap_irec overlap_rec;
42+
};
43+
3544
/* Cross-reference a rmap against the refcount btree. */
3645
STATIC void
3746
xchk_rmapbt_xref_refc(
@@ -139,12 +148,63 @@ xchk_rmapbt_check_unwritten_in_keyflags(
139148
}
140149
}
141150

151+
static inline bool
152+
xchk_rmapbt_is_shareable(
153+
struct xfs_scrub *sc,
154+
const struct xfs_rmap_irec *irec)
155+
{
156+
if (!xfs_has_reflink(sc->mp))
157+
return false;
158+
if (XFS_RMAP_NON_INODE_OWNER(irec->rm_owner))
159+
return false;
160+
if (irec->rm_flags & (XFS_RMAP_BMBT_BLOCK | XFS_RMAP_ATTR_FORK |
161+
XFS_RMAP_UNWRITTEN))
162+
return false;
163+
return true;
164+
}
165+
166+
/* Flag failures for records that overlap but cannot. */
167+
STATIC void
168+
xchk_rmapbt_check_overlapping(
169+
struct xchk_btree *bs,
170+
struct xchk_rmap *cr,
171+
const struct xfs_rmap_irec *irec)
172+
{
173+
xfs_agblock_t pnext, inext;
174+
175+
if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
176+
return;
177+
178+
/* No previous record? */
179+
if (cr->overlap_rec.rm_blockcount == 0)
180+
goto set_prev;
181+
182+
/* Do overlap_rec and irec overlap? */
183+
pnext = cr->overlap_rec.rm_startblock + cr->overlap_rec.rm_blockcount;
184+
if (pnext <= irec->rm_startblock)
185+
goto set_prev;
186+
187+
/* Overlap is only allowed if both records are data fork mappings. */
188+
if (!xchk_rmapbt_is_shareable(bs->sc, &cr->overlap_rec) ||
189+
!xchk_rmapbt_is_shareable(bs->sc, irec))
190+
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
191+
192+
/* Save whichever rmap record extends furthest. */
193+
inext = irec->rm_startblock + irec->rm_blockcount;
194+
if (pnext > inext)
195+
return;
196+
197+
set_prev:
198+
memcpy(&cr->overlap_rec, irec, sizeof(struct xfs_rmap_irec));
199+
}
200+
142201
/* Scrub an rmapbt record. */
143202
STATIC int
144203
xchk_rmapbt_rec(
145204
struct xchk_btree *bs,
146205
const union xfs_btree_rec *rec)
147206
{
207+
struct xchk_rmap *cr = bs->private;
148208
struct xfs_rmap_irec irec;
149209

150210
if (xfs_rmap_btrec_to_irec(rec, &irec) != NULL ||
@@ -154,6 +214,7 @@ xchk_rmapbt_rec(
154214
}
155215

156216
xchk_rmapbt_check_unwritten_in_keyflags(bs);
217+
xchk_rmapbt_check_overlapping(bs, cr, &irec);
157218
xchk_rmapbt_xref(bs->sc, &irec);
158219
return 0;
159220
}
@@ -163,8 +224,17 @@ int
163224
xchk_rmapbt(
164225
struct xfs_scrub *sc)
165226
{
166-
return xchk_btree(sc, sc->sa.rmap_cur, xchk_rmapbt_rec,
167-
&XFS_RMAP_OINFO_AG, NULL);
227+
struct xchk_rmap *cr;
228+
int error;
229+
230+
cr = kzalloc(sizeof(struct xchk_rmap), XCHK_GFP_FLAGS);
231+
if (!cr)
232+
return -ENOMEM;
233+
234+
error = xchk_btree(sc, sc->sa.rmap_cur, xchk_rmapbt_rec,
235+
&XFS_RMAP_OINFO_AG, cr);
236+
kfree(cr);
237+
return error;
168238
}
169239

170240
/* xref check that the extent is owned only by a given owner */

0 commit comments

Comments
 (0)