| .. | .. |
|---|
| 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; |
|---|