1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_trans_resv.h"
11 #include "xfs_mount.h"
12 #include "xfs_defer.h"
13 #include "xfs_btree.h"
14 #include "xfs_bit.h"
15 #include "xfs_log_format.h"
16 #include "xfs_trans.h"
17 #include "xfs_sb.h"
18 #include "xfs_inode.h"
19 #include "xfs_inode_fork.h"
20 #include "xfs_alloc.h"
21 #include "xfs_rtalloc.h"
22 #include "xfs_bmap.h"
23 #include "xfs_bmap_util.h"
24 #include "xfs_bmap_btree.h"
25 #include "xfs_rmap.h"
26 #include "xfs_rmap_btree.h"
27 #include "xfs_refcount.h"
28 #include "scrub/xfs_scrub.h"
29 #include "scrub/scrub.h"
30 #include "scrub/common.h"
31 #include "scrub/btree.h"
32 #include "scrub/trace.h"
33
34 /* Set us up with an inode's bmap. */
35 int
xchk_setup_inode_bmap(struct xfs_scrub * sc,struct xfs_inode * ip)36 xchk_setup_inode_bmap(
37 struct xfs_scrub *sc,
38 struct xfs_inode *ip)
39 {
40 int error;
41
42 error = xchk_get_inode(sc, ip);
43 if (error)
44 goto out;
45
46 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
47 xfs_ilock(sc->ip, sc->ilock_flags);
48
49 /*
50 * We don't want any ephemeral data fork updates sitting around
51 * while we inspect block mappings, so wait for directio to finish
52 * and flush dirty data if we have delalloc reservations.
53 */
54 if (S_ISREG(VFS_I(sc->ip)->i_mode) &&
55 sc->sm->sm_type == XFS_SCRUB_TYPE_BMBTD) {
56 struct address_space *mapping = VFS_I(sc->ip)->i_mapping;
57
58 inode_dio_wait(VFS_I(sc->ip));
59
60 /*
61 * Try to flush all incore state to disk before we examine the
62 * space mappings for the data fork. Leave accumulated errors
63 * in the mapping for the writer threads to consume.
64 *
65 * On ENOSPC or EIO writeback errors, we continue into the
66 * extent mapping checks because write failures do not
67 * necessarily imply anything about the correctness of the file
68 * metadata. The metadata and the file data could be on
69 * completely separate devices; a media failure might only
70 * affect a subset of the disk, etc. We can handle delalloc
71 * extents in the scrubber, so leaving them in memory is fine.
72 */
73 error = filemap_fdatawrite(mapping);
74 if (!error)
75 error = filemap_fdatawait_keep_errors(mapping);
76 if (error && (error != -ENOSPC && error != -EIO))
77 goto out;
78 }
79
80 /* Got the inode, lock it and we're ready to go. */
81 error = xchk_trans_alloc(sc, 0);
82 if (error)
83 goto out;
84 sc->ilock_flags |= XFS_ILOCK_EXCL;
85 xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
86
87 out:
88 /* scrub teardown will unlock and release the inode */
89 return error;
90 }
91
92 /*
93 * Inode fork block mapping (BMBT) scrubber.
94 * More complex than the others because we have to scrub
95 * all the extents regardless of whether or not the fork
96 * is in btree format.
97 */
98
99 struct xchk_bmap_info {
100 struct xfs_scrub *sc;
101 xfs_fileoff_t lastoff;
102 bool is_rt;
103 bool is_shared;
104 int whichfork;
105 };
106
107 /* Look for a corresponding rmap for this irec. */
108 static inline bool
xchk_bmap_get_rmap(struct xchk_bmap_info * info,struct xfs_bmbt_irec * irec,xfs_agblock_t agbno,uint64_t owner,struct xfs_rmap_irec * rmap)109 xchk_bmap_get_rmap(
110 struct xchk_bmap_info *info,
111 struct xfs_bmbt_irec *irec,
112 xfs_agblock_t agbno,
113 uint64_t owner,
114 struct xfs_rmap_irec *rmap)
115 {
116 xfs_fileoff_t offset;
117 unsigned int rflags = 0;
118 int has_rmap;
119 int error;
120
121 if (info->whichfork == XFS_ATTR_FORK)
122 rflags |= XFS_RMAP_ATTR_FORK;
123 if (irec->br_state == XFS_EXT_UNWRITTEN)
124 rflags |= XFS_RMAP_UNWRITTEN;
125
126 /*
127 * CoW staging extents are owned (on disk) by the refcountbt, so
128 * their rmaps do not have offsets.
129 */
130 if (info->whichfork == XFS_COW_FORK)
131 offset = 0;
132 else
133 offset = irec->br_startoff;
134
135 /*
136 * If the caller thinks this could be a shared bmbt extent (IOWs,
137 * any data fork extent of a reflink inode) then we have to use the
138 * range rmap lookup to make sure we get the correct owner/offset.
139 */
140 if (info->is_shared) {
141 error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
142 owner, offset, rflags, rmap, &has_rmap);
143 if (!xchk_should_check_xref(info->sc, &error,
144 &info->sc->sa.rmap_cur))
145 return false;
146 goto out;
147 }
148
149 /*
150 * Otherwise, use the (faster) regular lookup.
151 */
152 error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, 0, owner,
153 offset, rflags, &has_rmap);
154 if (!xchk_should_check_xref(info->sc, &error,
155 &info->sc->sa.rmap_cur))
156 return false;
157 if (!has_rmap)
158 goto out;
159
160 error = xfs_rmap_get_rec(info->sc->sa.rmap_cur, rmap, &has_rmap);
161 if (!xchk_should_check_xref(info->sc, &error,
162 &info->sc->sa.rmap_cur))
163 return false;
164
165 out:
166 if (!has_rmap)
167 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
168 irec->br_startoff);
169 return has_rmap;
170 }
171
172 /* Make sure that we have rmapbt records for this extent. */
173 STATIC void
xchk_bmap_xref_rmap(struct xchk_bmap_info * info,struct xfs_bmbt_irec * irec,xfs_agblock_t agbno)174 xchk_bmap_xref_rmap(
175 struct xchk_bmap_info *info,
176 struct xfs_bmbt_irec *irec,
177 xfs_agblock_t agbno)
178 {
179 struct xfs_rmap_irec rmap;
180 unsigned long long rmap_end;
181 uint64_t owner;
182
183 if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
184 return;
185
186 if (info->whichfork == XFS_COW_FORK)
187 owner = XFS_RMAP_OWN_COW;
188 else
189 owner = info->sc->ip->i_ino;
190
191 /* Find the rmap record for this irec. */
192 if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap))
193 return;
194
195 /* Check the rmap. */
196 rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
197 if (rmap.rm_startblock > agbno ||
198 agbno + irec->br_blockcount > rmap_end)
199 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
200 irec->br_startoff);
201
202 /*
203 * Check the logical offsets if applicable. CoW staging extents
204 * don't track logical offsets since the mappings only exist in
205 * memory.
206 */
207 if (info->whichfork != XFS_COW_FORK) {
208 rmap_end = (unsigned long long)rmap.rm_offset +
209 rmap.rm_blockcount;
210 if (rmap.rm_offset > irec->br_startoff ||
211 irec->br_startoff + irec->br_blockcount > rmap_end)
212 xchk_fblock_xref_set_corrupt(info->sc,
213 info->whichfork, irec->br_startoff);
214 }
215
216 if (rmap.rm_owner != owner)
217 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
218 irec->br_startoff);
219
220 /*
221 * Check for discrepancies between the unwritten flag in the irec and
222 * the rmap. Note that the (in-memory) CoW fork distinguishes between
223 * unwritten and written extents, but we don't track that in the rmap
224 * records because the blocks are owned (on-disk) by the refcountbt,
225 * which doesn't track unwritten state.
226 */
227 if (owner != XFS_RMAP_OWN_COW &&
228 !!(irec->br_state == XFS_EXT_UNWRITTEN) !=
229 !!(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
230 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
231 irec->br_startoff);
232
233 if (!!(info->whichfork == XFS_ATTR_FORK) !=
234 !!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
235 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
236 irec->br_startoff);
237 if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
238 xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
239 irec->br_startoff);
240 }
241
242 /* Cross-reference a single rtdev extent record. */
243 STATIC void
xchk_bmap_rt_extent_xref(struct xchk_bmap_info * info,struct xfs_inode * ip,struct xfs_btree_cur * cur,struct xfs_bmbt_irec * irec)244 xchk_bmap_rt_extent_xref(
245 struct xchk_bmap_info *info,
246 struct xfs_inode *ip,
247 struct xfs_btree_cur *cur,
248 struct xfs_bmbt_irec *irec)
249 {
250 if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
251 return;
252
253 xchk_xref_is_used_rt_space(info->sc, irec->br_startblock,
254 irec->br_blockcount);
255 }
256
257 /* Cross-reference a single datadev extent record. */
258 STATIC void
xchk_bmap_extent_xref(struct xchk_bmap_info * info,struct xfs_inode * ip,struct xfs_btree_cur * cur,struct xfs_bmbt_irec * irec)259 xchk_bmap_extent_xref(
260 struct xchk_bmap_info *info,
261 struct xfs_inode *ip,
262 struct xfs_btree_cur *cur,
263 struct xfs_bmbt_irec *irec)
264 {
265 struct xfs_mount *mp = info->sc->mp;
266 xfs_agnumber_t agno;
267 xfs_agblock_t agbno;
268 xfs_extlen_t len;
269 int error;
270
271 if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
272 return;
273
274 agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock);
275 agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock);
276 len = irec->br_blockcount;
277
278 error = xchk_ag_init(info->sc, agno, &info->sc->sa);
279 if (!xchk_fblock_process_error(info->sc, info->whichfork,
280 irec->br_startoff, &error))
281 return;
282
283 xchk_xref_is_used_space(info->sc, agbno, len);
284 xchk_xref_is_not_inode_chunk(info->sc, agbno, len);
285 xchk_bmap_xref_rmap(info, irec, agbno);
286 switch (info->whichfork) {
287 case XFS_DATA_FORK:
288 if (xfs_is_reflink_inode(info->sc->ip))
289 break;
290 /* fall through */
291 case XFS_ATTR_FORK:
292 xchk_xref_is_not_shared(info->sc, agbno,
293 irec->br_blockcount);
294 break;
295 case XFS_COW_FORK:
296 xchk_xref_is_cow_staging(info->sc, agbno,
297 irec->br_blockcount);
298 break;
299 }
300
301 xchk_ag_free(info->sc, &info->sc->sa);
302 }
303
304 /* Scrub a single extent record. */
305 STATIC int
xchk_bmap_extent(struct xfs_inode * ip,struct xfs_btree_cur * cur,struct xchk_bmap_info * info,struct xfs_bmbt_irec * irec)306 xchk_bmap_extent(
307 struct xfs_inode *ip,
308 struct xfs_btree_cur *cur,
309 struct xchk_bmap_info *info,
310 struct xfs_bmbt_irec *irec)
311 {
312 struct xfs_mount *mp = info->sc->mp;
313 struct xfs_buf *bp = NULL;
314 xfs_filblks_t end;
315 int error = 0;
316
317 if (cur)
318 xfs_btree_get_block(cur, 0, &bp);
319
320 /*
321 * Check for out-of-order extents. This record could have come
322 * from the incore list, for which there is no ordering check.
323 */
324 if (irec->br_startoff < info->lastoff)
325 xchk_fblock_set_corrupt(info->sc, info->whichfork,
326 irec->br_startoff);
327
328 /* There should never be a "hole" extent in either extent list. */
329 if (irec->br_startblock == HOLESTARTBLOCK)
330 xchk_fblock_set_corrupt(info->sc, info->whichfork,
331 irec->br_startoff);
332
333 /*
334 * Check for delalloc extents. We never iterate the ones in the
335 * in-core extent scan, and we should never see these in the bmbt.
336 */
337 if (isnullstartblock(irec->br_startblock))
338 xchk_fblock_set_corrupt(info->sc, info->whichfork,
339 irec->br_startoff);
340
341 /* Make sure the extent points to a valid place. */
342 if (irec->br_blockcount > MAXEXTLEN)
343 xchk_fblock_set_corrupt(info->sc, info->whichfork,
344 irec->br_startoff);
345 if (irec->br_startblock + irec->br_blockcount <= irec->br_startblock)
346 xchk_fblock_set_corrupt(info->sc, info->whichfork,
347 irec->br_startoff);
348 end = irec->br_startblock + irec->br_blockcount - 1;
349 if (info->is_rt &&
350 (!xfs_verify_rtbno(mp, irec->br_startblock) ||
351 !xfs_verify_rtbno(mp, end)))
352 xchk_fblock_set_corrupt(info->sc, info->whichfork,
353 irec->br_startoff);
354 if (!info->is_rt &&
355 (!xfs_verify_fsbno(mp, irec->br_startblock) ||
356 !xfs_verify_fsbno(mp, end) ||
357 XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
358 XFS_FSB_TO_AGNO(mp, end)))
359 xchk_fblock_set_corrupt(info->sc, info->whichfork,
360 irec->br_startoff);
361
362 /* We don't allow unwritten extents on attr forks. */
363 if (irec->br_state == XFS_EXT_UNWRITTEN &&
364 info->whichfork == XFS_ATTR_FORK)
365 xchk_fblock_set_corrupt(info->sc, info->whichfork,
366 irec->br_startoff);
367
368 if (info->is_rt)
369 xchk_bmap_rt_extent_xref(info, ip, cur, irec);
370 else
371 xchk_bmap_extent_xref(info, ip, cur, irec);
372
373 info->lastoff = irec->br_startoff + irec->br_blockcount;
374 return error;
375 }
376
377 /* Scrub a bmbt record. */
378 STATIC int
xchk_bmapbt_rec(struct xchk_btree * bs,union xfs_btree_rec * rec)379 xchk_bmapbt_rec(
380 struct xchk_btree *bs,
381 union xfs_btree_rec *rec)
382 {
383 struct xfs_bmbt_irec irec;
384 struct xchk_bmap_info *info = bs->private;
385 struct xfs_inode *ip = bs->cur->bc_private.b.ip;
386 struct xfs_buf *bp = NULL;
387 struct xfs_btree_block *block;
388 uint64_t owner;
389 int i;
390
391 /*
392 * Check the owners of the btree blocks up to the level below
393 * the root since the verifiers don't do that.
394 */
395 if (xfs_sb_version_hascrc(&bs->cur->bc_mp->m_sb) &&
396 bs->cur->bc_ptrs[0] == 1) {
397 for (i = 0; i < bs->cur->bc_nlevels - 1; i++) {
398 block = xfs_btree_get_block(bs->cur, i, &bp);
399 owner = be64_to_cpu(block->bb_u.l.bb_owner);
400 if (owner != ip->i_ino)
401 xchk_fblock_set_corrupt(bs->sc,
402 info->whichfork, 0);
403 }
404 }
405
406 /* Set up the in-core record and scrub it. */
407 xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
408 return xchk_bmap_extent(ip, bs->cur, info, &irec);
409 }
410
411 /* Scan the btree records. */
412 STATIC int
xchk_bmap_btree(struct xfs_scrub * sc,int whichfork,struct xchk_bmap_info * info)413 xchk_bmap_btree(
414 struct xfs_scrub *sc,
415 int whichfork,
416 struct xchk_bmap_info *info)
417 {
418 struct xfs_owner_info oinfo;
419 struct xfs_mount *mp = sc->mp;
420 struct xfs_inode *ip = sc->ip;
421 struct xfs_btree_cur *cur;
422 int error;
423
424 cur = xfs_bmbt_init_cursor(mp, sc->tp, ip, whichfork);
425 xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork);
426 error = xchk_btree(sc, cur, xchk_bmapbt_rec, &oinfo, info);
427 xfs_btree_del_cursor(cur, error);
428 return error;
429 }
430
431 struct xchk_bmap_check_rmap_info {
432 struct xfs_scrub *sc;
433 int whichfork;
434 struct xfs_iext_cursor icur;
435 };
436
437 /* Can we find bmaps that fit this rmap? */
438 STATIC int
xchk_bmap_check_rmap(struct xfs_btree_cur * cur,struct xfs_rmap_irec * rec,void * priv)439 xchk_bmap_check_rmap(
440 struct xfs_btree_cur *cur,
441 struct xfs_rmap_irec *rec,
442 void *priv)
443 {
444 struct xfs_bmbt_irec irec;
445 struct xchk_bmap_check_rmap_info *sbcri = priv;
446 struct xfs_ifork *ifp;
447 struct xfs_scrub *sc = sbcri->sc;
448 bool have_map;
449
450 /* Is this even the right fork? */
451 if (rec->rm_owner != sc->ip->i_ino)
452 return 0;
453 if ((sbcri->whichfork == XFS_ATTR_FORK) ^
454 !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
455 return 0;
456 if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
457 return 0;
458
459 /* Now look up the bmbt record. */
460 ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
461 if (!ifp) {
462 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
463 rec->rm_offset);
464 goto out;
465 }
466 have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
467 &sbcri->icur, &irec);
468 if (!have_map)
469 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
470 rec->rm_offset);
471 /*
472 * bmap extent record lengths are constrained to 2^21 blocks in length
473 * because of space constraints in the on-disk metadata structure.
474 * However, rmap extent record lengths are constrained only by AG
475 * length, so we have to loop through the bmbt to make sure that the
476 * entire rmap is covered by bmbt records.
477 */
478 while (have_map) {
479 if (irec.br_startoff != rec->rm_offset)
480 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
481 rec->rm_offset);
482 if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
483 cur->bc_private.a.agno, rec->rm_startblock))
484 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
485 rec->rm_offset);
486 if (irec.br_blockcount > rec->rm_blockcount)
487 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
488 rec->rm_offset);
489 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
490 break;
491 rec->rm_startblock += irec.br_blockcount;
492 rec->rm_offset += irec.br_blockcount;
493 rec->rm_blockcount -= irec.br_blockcount;
494 if (rec->rm_blockcount == 0)
495 break;
496 have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
497 if (!have_map)
498 xchk_fblock_set_corrupt(sc, sbcri->whichfork,
499 rec->rm_offset);
500 }
501
502 out:
503 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
504 return XFS_BTREE_QUERY_RANGE_ABORT;
505 return 0;
506 }
507
508 /* Make sure each rmap has a corresponding bmbt entry. */
509 STATIC int
xchk_bmap_check_ag_rmaps(struct xfs_scrub * sc,int whichfork,xfs_agnumber_t agno)510 xchk_bmap_check_ag_rmaps(
511 struct xfs_scrub *sc,
512 int whichfork,
513 xfs_agnumber_t agno)
514 {
515 struct xchk_bmap_check_rmap_info sbcri;
516 struct xfs_btree_cur *cur;
517 struct xfs_buf *agf;
518 int error;
519
520 error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
521 if (error)
522 return error;
523
524 cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
525 if (!cur) {
526 error = -ENOMEM;
527 goto out_agf;
528 }
529
530 sbcri.sc = sc;
531 sbcri.whichfork = whichfork;
532 error = xfs_rmap_query_all(cur, xchk_bmap_check_rmap, &sbcri);
533 if (error == XFS_BTREE_QUERY_RANGE_ABORT)
534 error = 0;
535
536 xfs_btree_del_cursor(cur, error);
537 out_agf:
538 xfs_trans_brelse(sc->tp, agf);
539 return error;
540 }
541
542 /* Make sure each rmap has a corresponding bmbt entry. */
543 STATIC int
xchk_bmap_check_rmaps(struct xfs_scrub * sc,int whichfork)544 xchk_bmap_check_rmaps(
545 struct xfs_scrub *sc,
546 int whichfork)
547 {
548 loff_t size;
549 xfs_agnumber_t agno;
550 int error;
551
552 if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
553 whichfork == XFS_COW_FORK ||
554 (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
555 return 0;
556
557 /* Don't support realtime rmap checks yet. */
558 if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
559 return 0;
560
561 /*
562 * Only do this for complex maps that are in btree format, or for
563 * situations where we would seem to have a size but zero extents.
564 * The inode repair code can zap broken iforks, which means we have
565 * to flag this bmap as corrupt if there are rmaps that need to be
566 * reattached.
567 */
568 switch (whichfork) {
569 case XFS_DATA_FORK:
570 size = i_size_read(VFS_I(sc->ip));
571 break;
572 case XFS_ATTR_FORK:
573 size = XFS_IFORK_Q(sc->ip);
574 break;
575 default:
576 size = 0;
577 break;
578 }
579 if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
580 (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
581 return 0;
582
583 for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
584 error = xchk_bmap_check_ag_rmaps(sc, whichfork, agno);
585 if (error)
586 return error;
587 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
588 break;
589 }
590
591 return 0;
592 }
593
594 /*
595 * Scrub an inode fork's block mappings.
596 *
597 * First we scan every record in every btree block, if applicable.
598 * Then we unconditionally scan the incore extent cache.
599 */
600 STATIC int
xchk_bmap(struct xfs_scrub * sc,int whichfork)601 xchk_bmap(
602 struct xfs_scrub *sc,
603 int whichfork)
604 {
605 struct xfs_bmbt_irec irec;
606 struct xchk_bmap_info info = { NULL };
607 struct xfs_mount *mp = sc->mp;
608 struct xfs_inode *ip = sc->ip;
609 struct xfs_ifork *ifp;
610 xfs_fileoff_t endoff;
611 struct xfs_iext_cursor icur;
612 int error = 0;
613
614 ifp = XFS_IFORK_PTR(ip, whichfork);
615
616 info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip);
617 info.whichfork = whichfork;
618 info.is_shared = whichfork == XFS_DATA_FORK && xfs_is_reflink_inode(ip);
619 info.sc = sc;
620
621 switch (whichfork) {
622 case XFS_COW_FORK:
623 /* Non-existent CoW forks are ignorable. */
624 if (!ifp)
625 goto out;
626 /* No CoW forks on non-reflink inodes/filesystems. */
627 if (!xfs_is_reflink_inode(ip)) {
628 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
629 goto out;
630 }
631 break;
632 case XFS_ATTR_FORK:
633 if (!ifp)
634 goto out_check_rmap;
635 if (!xfs_sb_version_hasattr(&mp->m_sb) &&
636 !xfs_sb_version_hasattr2(&mp->m_sb))
637 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
638 break;
639 default:
640 ASSERT(whichfork == XFS_DATA_FORK);
641 break;
642 }
643
644 /* Check the fork values */
645 switch (XFS_IFORK_FORMAT(ip, whichfork)) {
646 case XFS_DINODE_FMT_UUID:
647 case XFS_DINODE_FMT_DEV:
648 case XFS_DINODE_FMT_LOCAL:
649 /* No mappings to check. */
650 goto out;
651 case XFS_DINODE_FMT_EXTENTS:
652 if (!(ifp->if_flags & XFS_IFEXTENTS)) {
653 xchk_fblock_set_corrupt(sc, whichfork, 0);
654 goto out;
655 }
656 break;
657 case XFS_DINODE_FMT_BTREE:
658 if (whichfork == XFS_COW_FORK) {
659 xchk_fblock_set_corrupt(sc, whichfork, 0);
660 goto out;
661 }
662
663 error = xchk_bmap_btree(sc, whichfork, &info);
664 if (error)
665 goto out;
666 break;
667 default:
668 xchk_fblock_set_corrupt(sc, whichfork, 0);
669 goto out;
670 }
671
672 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
673 goto out;
674
675 /* Now try to scrub the in-memory extent list. */
676 if (!(ifp->if_flags & XFS_IFEXTENTS)) {
677 error = xfs_iread_extents(sc->tp, ip, whichfork);
678 if (!xchk_fblock_process_error(sc, whichfork, 0, &error))
679 goto out;
680 }
681
682 /* Find the offset of the last extent in the mapping. */
683 error = xfs_bmap_last_offset(ip, &endoff, whichfork);
684 if (!xchk_fblock_process_error(sc, whichfork, 0, &error))
685 goto out;
686
687 /* Scrub extent records. */
688 info.lastoff = 0;
689 ifp = XFS_IFORK_PTR(ip, whichfork);
690 for_each_xfs_iext(ifp, &icur, &irec) {
691 if (xchk_should_terminate(sc, &error) ||
692 (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
693 break;
694 if (isnullstartblock(irec.br_startblock))
695 continue;
696 if (irec.br_startoff >= endoff) {
697 xchk_fblock_set_corrupt(sc, whichfork,
698 irec.br_startoff);
699 goto out;
700 }
701 error = xchk_bmap_extent(ip, NULL, &info, &irec);
702 if (error)
703 goto out;
704 }
705
706 out_check_rmap:
707 error = xchk_bmap_check_rmaps(sc, whichfork);
708 if (!xchk_fblock_xref_process_error(sc, whichfork, 0, &error))
709 goto out;
710 out:
711 return error;
712 }
713
714 /* Scrub an inode's data fork. */
715 int
xchk_bmap_data(struct xfs_scrub * sc)716 xchk_bmap_data(
717 struct xfs_scrub *sc)
718 {
719 return xchk_bmap(sc, XFS_DATA_FORK);
720 }
721
722 /* Scrub an inode's attr fork. */
723 int
xchk_bmap_attr(struct xfs_scrub * sc)724 xchk_bmap_attr(
725 struct xfs_scrub *sc)
726 {
727 return xchk_bmap(sc, XFS_ATTR_FORK);
728 }
729
730 /* Scrub an inode's CoW fork. */
731 int
xchk_bmap_cow(struct xfs_scrub * sc)732 xchk_bmap_cow(
733 struct xfs_scrub *sc)
734 {
735 if (!xfs_is_reflink_inode(sc->ip))
736 return -ENOENT;
737
738 return xchk_bmap(sc, XFS_COW_FORK);
739 }
740