.. | .. |
---|
9 | 9 | #include "xfs_format.h" |
---|
10 | 10 | #include "xfs_trans_resv.h" |
---|
11 | 11 | #include "xfs_mount.h" |
---|
12 | | -#include "xfs_defer.h" |
---|
13 | 12 | #include "xfs_btree.h" |
---|
14 | 13 | #include "xfs_bit.h" |
---|
15 | 14 | #include "xfs_log_format.h" |
---|
16 | 15 | #include "xfs_trans.h" |
---|
17 | | -#include "xfs_sb.h" |
---|
18 | 16 | #include "xfs_inode.h" |
---|
19 | | -#include "xfs_inode_fork.h" |
---|
20 | 17 | #include "xfs_alloc.h" |
---|
21 | | -#include "xfs_rtalloc.h" |
---|
22 | 18 | #include "xfs_bmap.h" |
---|
23 | | -#include "xfs_bmap_util.h" |
---|
24 | 19 | #include "xfs_bmap_btree.h" |
---|
25 | 20 | #include "xfs_rmap.h" |
---|
26 | 21 | #include "xfs_rmap_btree.h" |
---|
27 | | -#include "xfs_refcount.h" |
---|
28 | | -#include "scrub/xfs_scrub.h" |
---|
29 | 22 | #include "scrub/scrub.h" |
---|
30 | 23 | #include "scrub/common.h" |
---|
31 | 24 | #include "scrub/btree.h" |
---|
32 | | -#include "scrub/trace.h" |
---|
33 | 25 | |
---|
34 | 26 | /* Set us up with an inode's bmap. */ |
---|
35 | 27 | int |
---|
.. | .. |
---|
101 | 93 | xfs_fileoff_t lastoff; |
---|
102 | 94 | bool is_rt; |
---|
103 | 95 | bool is_shared; |
---|
| 96 | + bool was_loaded; |
---|
104 | 97 | int whichfork; |
---|
105 | 98 | }; |
---|
106 | 99 | |
---|
.. | .. |
---|
241 | 234 | |
---|
242 | 235 | /* Cross-reference a single rtdev extent record. */ |
---|
243 | 236 | STATIC void |
---|
244 | | -xchk_bmap_rt_extent_xref( |
---|
245 | | - struct xchk_bmap_info *info, |
---|
| 237 | +xchk_bmap_rt_iextent_xref( |
---|
246 | 238 | struct xfs_inode *ip, |
---|
247 | | - struct xfs_btree_cur *cur, |
---|
| 239 | + struct xchk_bmap_info *info, |
---|
248 | 240 | struct xfs_bmbt_irec *irec) |
---|
249 | 241 | { |
---|
250 | | - if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
---|
251 | | - return; |
---|
252 | | - |
---|
253 | 242 | xchk_xref_is_used_rt_space(info->sc, irec->br_startblock, |
---|
254 | 243 | irec->br_blockcount); |
---|
255 | 244 | } |
---|
256 | 245 | |
---|
257 | 246 | /* Cross-reference a single datadev extent record. */ |
---|
258 | 247 | STATIC void |
---|
259 | | -xchk_bmap_extent_xref( |
---|
260 | | - struct xchk_bmap_info *info, |
---|
| 248 | +xchk_bmap_iextent_xref( |
---|
261 | 249 | struct xfs_inode *ip, |
---|
262 | | - struct xfs_btree_cur *cur, |
---|
| 250 | + struct xchk_bmap_info *info, |
---|
263 | 251 | struct xfs_bmbt_irec *irec) |
---|
264 | 252 | { |
---|
265 | 253 | struct xfs_mount *mp = info->sc->mp; |
---|
.. | .. |
---|
267 | 255 | xfs_agblock_t agbno; |
---|
268 | 256 | xfs_extlen_t len; |
---|
269 | 257 | int error; |
---|
270 | | - |
---|
271 | | - if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
---|
272 | | - return; |
---|
273 | 258 | |
---|
274 | 259 | agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock); |
---|
275 | 260 | agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock); |
---|
.. | .. |
---|
301 | 286 | xchk_ag_free(info->sc, &info->sc->sa); |
---|
302 | 287 | } |
---|
303 | 288 | |
---|
| 289 | +/* |
---|
| 290 | + * Directories and attr forks should never have blocks that can't be addressed |
---|
| 291 | + * by a xfs_dablk_t. |
---|
| 292 | + */ |
---|
| 293 | +STATIC void |
---|
| 294 | +xchk_bmap_dirattr_extent( |
---|
| 295 | + struct xfs_inode *ip, |
---|
| 296 | + struct xchk_bmap_info *info, |
---|
| 297 | + struct xfs_bmbt_irec *irec) |
---|
| 298 | +{ |
---|
| 299 | + struct xfs_mount *mp = ip->i_mount; |
---|
| 300 | + xfs_fileoff_t off; |
---|
| 301 | + |
---|
| 302 | + if (!S_ISDIR(VFS_I(ip)->i_mode) && info->whichfork != XFS_ATTR_FORK) |
---|
| 303 | + return; |
---|
| 304 | + |
---|
| 305 | + if (!xfs_verify_dablk(mp, irec->br_startoff)) |
---|
| 306 | + xchk_fblock_set_corrupt(info->sc, info->whichfork, |
---|
| 307 | + irec->br_startoff); |
---|
| 308 | + |
---|
| 309 | + off = irec->br_startoff + irec->br_blockcount - 1; |
---|
| 310 | + if (!xfs_verify_dablk(mp, off)) |
---|
| 311 | + xchk_fblock_set_corrupt(info->sc, info->whichfork, off); |
---|
| 312 | +} |
---|
| 313 | + |
---|
304 | 314 | /* Scrub a single extent record. */ |
---|
305 | 315 | STATIC int |
---|
306 | | -xchk_bmap_extent( |
---|
| 316 | +xchk_bmap_iextent( |
---|
307 | 317 | struct xfs_inode *ip, |
---|
308 | | - struct xfs_btree_cur *cur, |
---|
309 | 318 | struct xchk_bmap_info *info, |
---|
310 | 319 | struct xfs_bmbt_irec *irec) |
---|
311 | 320 | { |
---|
312 | 321 | struct xfs_mount *mp = info->sc->mp; |
---|
313 | | - struct xfs_buf *bp = NULL; |
---|
314 | 322 | xfs_filblks_t end; |
---|
315 | 323 | int error = 0; |
---|
316 | | - |
---|
317 | | - if (cur) |
---|
318 | | - xfs_btree_get_block(cur, 0, &bp); |
---|
319 | 324 | |
---|
320 | 325 | /* |
---|
321 | 326 | * Check for out-of-order extents. This record could have come |
---|
.. | .. |
---|
324 | 329 | if (irec->br_startoff < info->lastoff) |
---|
325 | 330 | xchk_fblock_set_corrupt(info->sc, info->whichfork, |
---|
326 | 331 | irec->br_startoff); |
---|
| 332 | + |
---|
| 333 | + xchk_bmap_dirattr_extent(ip, info, irec); |
---|
327 | 334 | |
---|
328 | 335 | /* There should never be a "hole" extent in either extent list. */ |
---|
329 | 336 | if (irec->br_startblock == HOLESTARTBLOCK) |
---|
.. | .. |
---|
365 | 372 | xchk_fblock_set_corrupt(info->sc, info->whichfork, |
---|
366 | 373 | irec->br_startoff); |
---|
367 | 374 | |
---|
| 375 | + if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
---|
| 376 | + return 0; |
---|
| 377 | + |
---|
368 | 378 | if (info->is_rt) |
---|
369 | | - xchk_bmap_rt_extent_xref(info, ip, cur, irec); |
---|
| 379 | + xchk_bmap_rt_iextent_xref(ip, info, irec); |
---|
370 | 380 | else |
---|
371 | | - xchk_bmap_extent_xref(info, ip, cur, irec); |
---|
| 381 | + xchk_bmap_iextent_xref(ip, info, irec); |
---|
372 | 382 | |
---|
373 | 383 | info->lastoff = irec->br_startoff + irec->br_blockcount; |
---|
374 | 384 | return error; |
---|
.. | .. |
---|
381 | 391 | union xfs_btree_rec *rec) |
---|
382 | 392 | { |
---|
383 | 393 | struct xfs_bmbt_irec irec; |
---|
| 394 | + struct xfs_bmbt_irec iext_irec; |
---|
| 395 | + struct xfs_iext_cursor icur; |
---|
384 | 396 | struct xchk_bmap_info *info = bs->private; |
---|
385 | | - struct xfs_inode *ip = bs->cur->bc_private.b.ip; |
---|
| 397 | + struct xfs_inode *ip = bs->cur->bc_ino.ip; |
---|
386 | 398 | struct xfs_buf *bp = NULL; |
---|
387 | 399 | struct xfs_btree_block *block; |
---|
| 400 | + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, info->whichfork); |
---|
388 | 401 | uint64_t owner; |
---|
389 | 402 | int i; |
---|
390 | 403 | |
---|
.. | .. |
---|
403 | 416 | } |
---|
404 | 417 | } |
---|
405 | 418 | |
---|
406 | | - /* Set up the in-core record and scrub it. */ |
---|
| 419 | + /* |
---|
| 420 | + * Check that the incore extent tree contains an extent that matches |
---|
| 421 | + * this one exactly. We validate those cached bmaps later, so we don't |
---|
| 422 | + * need to check them here. If the incore extent tree was just loaded |
---|
| 423 | + * from disk by the scrubber, we assume that its contents match what's |
---|
| 424 | + * on disk (we still hold the ILOCK) and skip the equivalence check. |
---|
| 425 | + */ |
---|
| 426 | + if (!info->was_loaded) |
---|
| 427 | + return 0; |
---|
| 428 | + |
---|
407 | 429 | xfs_bmbt_disk_get_all(&rec->bmbt, &irec); |
---|
408 | | - return xchk_bmap_extent(ip, bs->cur, info, &irec); |
---|
| 430 | + if (!xfs_iext_lookup_extent(ip, ifp, irec.br_startoff, &icur, |
---|
| 431 | + &iext_irec) || |
---|
| 432 | + irec.br_startoff != iext_irec.br_startoff || |
---|
| 433 | + irec.br_startblock != iext_irec.br_startblock || |
---|
| 434 | + irec.br_blockcount != iext_irec.br_blockcount || |
---|
| 435 | + irec.br_state != iext_irec.br_state) |
---|
| 436 | + xchk_fblock_set_corrupt(bs->sc, info->whichfork, |
---|
| 437 | + irec.br_startoff); |
---|
| 438 | + return 0; |
---|
409 | 439 | } |
---|
410 | 440 | |
---|
411 | 441 | /* Scan the btree records. */ |
---|
.. | .. |
---|
416 | 446 | struct xchk_bmap_info *info) |
---|
417 | 447 | { |
---|
418 | 448 | struct xfs_owner_info oinfo; |
---|
| 449 | + struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); |
---|
419 | 450 | struct xfs_mount *mp = sc->mp; |
---|
420 | 451 | struct xfs_inode *ip = sc->ip; |
---|
421 | 452 | struct xfs_btree_cur *cur; |
---|
422 | 453 | int error; |
---|
423 | 454 | |
---|
| 455 | + /* Load the incore bmap cache if it's not loaded. */ |
---|
| 456 | + info->was_loaded = ifp->if_flags & XFS_IFEXTENTS; |
---|
| 457 | + if (!info->was_loaded) { |
---|
| 458 | + error = xfs_iread_extents(sc->tp, ip, whichfork); |
---|
| 459 | + if (!xchk_fblock_process_error(sc, whichfork, 0, &error)) |
---|
| 460 | + goto out; |
---|
| 461 | + } |
---|
| 462 | + |
---|
| 463 | + /* Check the btree structure. */ |
---|
424 | 464 | cur = xfs_bmbt_init_cursor(mp, sc->tp, ip, whichfork); |
---|
425 | 465 | xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork); |
---|
426 | 466 | error = xchk_btree(sc, cur, xchk_bmapbt_rec, &oinfo, info); |
---|
427 | 467 | xfs_btree_del_cursor(cur, error); |
---|
| 468 | +out: |
---|
428 | 469 | return error; |
---|
429 | 470 | } |
---|
430 | 471 | |
---|
.. | .. |
---|
480 | 521 | xchk_fblock_set_corrupt(sc, sbcri->whichfork, |
---|
481 | 522 | rec->rm_offset); |
---|
482 | 523 | if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp, |
---|
483 | | - cur->bc_private.a.agno, rec->rm_startblock)) |
---|
| 524 | + cur->bc_ag.agno, rec->rm_startblock)) |
---|
484 | 525 | xchk_fblock_set_corrupt(sc, sbcri->whichfork, |
---|
485 | 526 | rec->rm_offset); |
---|
486 | 527 | if (irec.br_blockcount > rec->rm_blockcount) |
---|
.. | .. |
---|
501 | 542 | |
---|
502 | 543 | out: |
---|
503 | 544 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
---|
504 | | - return XFS_BTREE_QUERY_RANGE_ABORT; |
---|
| 545 | + return -ECANCELED; |
---|
505 | 546 | return 0; |
---|
506 | 547 | } |
---|
507 | 548 | |
---|
.. | .. |
---|
530 | 571 | sbcri.sc = sc; |
---|
531 | 572 | sbcri.whichfork = whichfork; |
---|
532 | 573 | error = xfs_rmap_query_all(cur, xchk_bmap_check_rmap, &sbcri); |
---|
533 | | - if (error == XFS_BTREE_QUERY_RANGE_ABORT) |
---|
| 574 | + if (error == -ECANCELED) |
---|
534 | 575 | error = 0; |
---|
535 | 576 | |
---|
536 | 577 | xfs_btree_del_cursor(cur, error); |
---|
.. | .. |
---|
545 | 586 | struct xfs_scrub *sc, |
---|
546 | 587 | int whichfork) |
---|
547 | 588 | { |
---|
548 | | - loff_t size; |
---|
| 589 | + struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); |
---|
549 | 590 | xfs_agnumber_t agno; |
---|
| 591 | + bool zero_size; |
---|
550 | 592 | int error; |
---|
551 | 593 | |
---|
552 | 594 | if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) || |
---|
.. | .. |
---|
558 | 600 | if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK) |
---|
559 | 601 | return 0; |
---|
560 | 602 | |
---|
| 603 | + ASSERT(XFS_IFORK_PTR(sc->ip, whichfork) != NULL); |
---|
| 604 | + |
---|
561 | 605 | /* |
---|
562 | 606 | * Only do this for complex maps that are in btree format, or for |
---|
563 | 607 | * situations where we would seem to have a size but zero extents. |
---|
.. | .. |
---|
565 | 609 | * to flag this bmap as corrupt if there are rmaps that need to be |
---|
566 | 610 | * reattached. |
---|
567 | 611 | */ |
---|
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)) |
---|
| 612 | + |
---|
| 613 | + if (whichfork == XFS_DATA_FORK) |
---|
| 614 | + zero_size = i_size_read(VFS_I(sc->ip)) == 0; |
---|
| 615 | + else |
---|
| 616 | + zero_size = false; |
---|
| 617 | + |
---|
| 618 | + if (ifp->if_format != XFS_DINODE_FMT_BTREE && |
---|
| 619 | + (zero_size || ifp->if_nextents > 0)) |
---|
581 | 620 | return 0; |
---|
582 | 621 | |
---|
583 | 622 | for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) { |
---|
.. | .. |
---|
606 | 645 | struct xchk_bmap_info info = { NULL }; |
---|
607 | 646 | struct xfs_mount *mp = sc->mp; |
---|
608 | 647 | struct xfs_inode *ip = sc->ip; |
---|
609 | | - struct xfs_ifork *ifp; |
---|
| 648 | + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); |
---|
610 | 649 | xfs_fileoff_t endoff; |
---|
611 | 650 | struct xfs_iext_cursor icur; |
---|
612 | 651 | int error = 0; |
---|
613 | 652 | |
---|
614 | | - ifp = XFS_IFORK_PTR(ip, whichfork); |
---|
| 653 | + /* Non-existent forks can be ignored. */ |
---|
| 654 | + if (!ifp) |
---|
| 655 | + goto out; |
---|
615 | 656 | |
---|
616 | 657 | info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip); |
---|
617 | 658 | info.whichfork = whichfork; |
---|
.. | .. |
---|
620 | 661 | |
---|
621 | 662 | switch (whichfork) { |
---|
622 | 663 | case XFS_COW_FORK: |
---|
623 | | - /* Non-existent CoW forks are ignorable. */ |
---|
624 | | - if (!ifp) |
---|
625 | | - goto out; |
---|
626 | 664 | /* No CoW forks on non-reflink inodes/filesystems. */ |
---|
627 | 665 | if (!xfs_is_reflink_inode(ip)) { |
---|
628 | 666 | xchk_ino_set_corrupt(sc, sc->ip->i_ino); |
---|
.. | .. |
---|
630 | 668 | } |
---|
631 | 669 | break; |
---|
632 | 670 | case XFS_ATTR_FORK: |
---|
633 | | - if (!ifp) |
---|
634 | | - goto out_check_rmap; |
---|
635 | 671 | if (!xfs_sb_version_hasattr(&mp->m_sb) && |
---|
636 | 672 | !xfs_sb_version_hasattr2(&mp->m_sb)) |
---|
637 | 673 | xchk_ino_set_corrupt(sc, sc->ip->i_ino); |
---|
.. | .. |
---|
642 | 678 | } |
---|
643 | 679 | |
---|
644 | 680 | /* Check the fork values */ |
---|
645 | | - switch (XFS_IFORK_FORMAT(ip, whichfork)) { |
---|
| 681 | + switch (ifp->if_format) { |
---|
646 | 682 | case XFS_DINODE_FMT_UUID: |
---|
647 | 683 | case XFS_DINODE_FMT_DEV: |
---|
648 | 684 | case XFS_DINODE_FMT_LOCAL: |
---|
.. | .. |
---|
672 | 708 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
---|
673 | 709 | goto out; |
---|
674 | 710 | |
---|
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 | 711 | /* Find the offset of the last extent in the mapping. */ |
---|
683 | 712 | error = xfs_bmap_last_offset(ip, &endoff, whichfork); |
---|
684 | 713 | if (!xchk_fblock_process_error(sc, whichfork, 0, &error)) |
---|
.. | .. |
---|
690 | 719 | for_each_xfs_iext(ifp, &icur, &irec) { |
---|
691 | 720 | if (xchk_should_terminate(sc, &error) || |
---|
692 | 721 | (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) |
---|
693 | | - break; |
---|
| 722 | + goto out; |
---|
694 | 723 | if (isnullstartblock(irec.br_startblock)) |
---|
695 | 724 | continue; |
---|
696 | 725 | if (irec.br_startoff >= endoff) { |
---|
.. | .. |
---|
698 | 727 | irec.br_startoff); |
---|
699 | 728 | goto out; |
---|
700 | 729 | } |
---|
701 | | - error = xchk_bmap_extent(ip, NULL, &info, &irec); |
---|
| 730 | + error = xchk_bmap_iextent(ip, &info, &irec); |
---|
702 | 731 | if (error) |
---|
703 | 732 | goto out; |
---|
704 | 733 | } |
---|
705 | 734 | |
---|
706 | | -out_check_rmap: |
---|
707 | 735 | error = xchk_bmap_check_rmaps(sc, whichfork); |
---|
708 | 736 | if (!xchk_fblock_xref_process_error(sc, whichfork, 0, &error)) |
---|
709 | 737 | goto out; |
---|