summaryrefslogtreecommitdiffstats
path: root/fs/xfs/scrub
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-03-23 13:06:53 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2018-03-23 21:05:07 -0400
commit5e777b62b0bcb645f165fe5e056fe8862782affc (patch)
treea38ce3533a204c4d85951073e6598ca576b620b9 /fs/xfs/scrub
parent6edb181053f067cee64d4239830062cb40ddab00 (diff)
xfs: bmap scrubber should do rmap xref with bmap for sparse files
When we're scanning an extent mapping inode fork, ensure that every rmap record for this ifork has a corresponding bmbt record too. This (mostly) provides the ability to cross-reference rmap records with bmap data. The rmap scrubber cannot do the xref on its own because that requires taking an ilock with the agf lock held, which violates our locking order rules (inode, then agf). Note that we only do this for forks that are in btree format due to the increased complexity; or forks that should have data but suspiciously have zero extents because the inode could have just had its iforks zapped by the inode repair code and now we need to reclaim the old extents. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com>
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r--fs/xfs/scrub/bmap.c170
1 files changed, 169 insertions, 1 deletions
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index d00282130492..75ea2d63429c 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
37#include "xfs_bmap_util.h" 37#include "xfs_bmap_util.h"
38#include "xfs_bmap_btree.h" 38#include "xfs_bmap_btree.h"
39#include "xfs_rmap.h" 39#include "xfs_rmap.h"
40#include "xfs_rmap_btree.h"
40#include "xfs_refcount.h" 41#include "xfs_refcount.h"
41#include "scrub/xfs_scrub.h" 42#include "scrub/xfs_scrub.h"
42#include "scrub/scrub.h" 43#include "scrub/scrub.h"
@@ -423,6 +424,169 @@ xfs_scrub_bmap_btree(
423 return error; 424 return error;
424} 425}
425 426
427struct xfs_scrub_bmap_check_rmap_info {
428 struct xfs_scrub_context *sc;
429 int whichfork;
430 struct xfs_iext_cursor icur;
431};
432
433/* Can we find bmaps that fit this rmap? */
434STATIC int
435xfs_scrub_bmap_check_rmap(
436 struct xfs_btree_cur *cur,
437 struct xfs_rmap_irec *rec,
438 void *priv)
439{
440 struct xfs_bmbt_irec irec;
441 struct xfs_scrub_bmap_check_rmap_info *sbcri = priv;
442 struct xfs_ifork *ifp;
443 struct xfs_scrub_context *sc = sbcri->sc;
444 bool have_map;
445
446 /* Is this even the right fork? */
447 if (rec->rm_owner != sc->ip->i_ino)
448 return 0;
449 if ((sbcri->whichfork == XFS_ATTR_FORK) ^
450 !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
451 return 0;
452 if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
453 return 0;
454
455 /* Now look up the bmbt record. */
456 ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
457 if (!ifp) {
458 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
459 rec->rm_offset);
460 goto out;
461 }
462 have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
463 &sbcri->icur, &irec);
464 if (!have_map)
465 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
466 rec->rm_offset);
467 /*
468 * bmap extent record lengths are constrained to 2^21 blocks in length
469 * because of space constraints in the on-disk metadata structure.
470 * However, rmap extent record lengths are constrained only by AG
471 * length, so we have to loop through the bmbt to make sure that the
472 * entire rmap is covered by bmbt records.
473 */
474 while (have_map) {
475 if (irec.br_startoff != rec->rm_offset)
476 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
477 rec->rm_offset);
478 if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
479 cur->bc_private.a.agno, rec->rm_startblock))
480 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
481 rec->rm_offset);
482 if (irec.br_blockcount > rec->rm_blockcount)
483 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
484 rec->rm_offset);
485 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
486 break;
487 rec->rm_startblock += irec.br_blockcount;
488 rec->rm_offset += irec.br_blockcount;
489 rec->rm_blockcount -= irec.br_blockcount;
490 if (rec->rm_blockcount == 0)
491 break;
492 have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
493 if (!have_map)
494 xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
495 rec->rm_offset);
496 }
497
498out:
499 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
500 return XFS_BTREE_QUERY_RANGE_ABORT;
501 return 0;
502}
503
504/* Make sure each rmap has a corresponding bmbt entry. */
505STATIC int
506xfs_scrub_bmap_check_ag_rmaps(
507 struct xfs_scrub_context *sc,
508 int whichfork,
509 xfs_agnumber_t agno)
510{
511 struct xfs_scrub_bmap_check_rmap_info sbcri;
512 struct xfs_btree_cur *cur;
513 struct xfs_buf *agf;
514 int error;
515
516 error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
517 if (error)
518 return error;
519
520 cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
521 if (!cur) {
522 error = -ENOMEM;
523 goto out_agf;
524 }
525
526 sbcri.sc = sc;
527 sbcri.whichfork = whichfork;
528 error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
529 if (error == XFS_BTREE_QUERY_RANGE_ABORT)
530 error = 0;
531
532 xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
533out_agf:
534 xfs_trans_brelse(sc->tp, agf);
535 return error;
536}
537
538/* Make sure each rmap has a corresponding bmbt entry. */
539STATIC int
540xfs_scrub_bmap_check_rmaps(
541 struct xfs_scrub_context *sc,
542 int whichfork)
543{
544 loff_t size;
545 xfs_agnumber_t agno;
546 int error;
547
548 if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
549 whichfork == XFS_COW_FORK ||
550 (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
551 return 0;
552
553 /* Don't support realtime rmap checks yet. */
554 if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
555 return 0;
556
557 /*
558 * Only do this for complex maps that are in btree format, or for
559 * situations where we would seem to have a size but zero extents.
560 * The inode repair code can zap broken iforks, which means we have
561 * to flag this bmap as corrupt if there are rmaps that need to be
562 * reattached.
563 */
564 switch (whichfork) {
565 case XFS_DATA_FORK:
566 size = i_size_read(VFS_I(sc->ip));
567 break;
568 case XFS_ATTR_FORK:
569 size = XFS_IFORK_Q(sc->ip);
570 break;
571 default:
572 size = 0;
573 break;
574 }
575 if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
576 (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
577 return 0;
578
579 for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
580 error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
581 if (error)
582 return error;
583 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
584 break;
585 }
586
587 return 0;
588}
589
426/* 590/*
427 * Scrub an inode fork's block mappings. 591 * Scrub an inode fork's block mappings.
428 * 592 *
@@ -463,7 +627,7 @@ xfs_scrub_bmap(
463 break; 627 break;
464 case XFS_ATTR_FORK: 628 case XFS_ATTR_FORK:
465 if (!ifp) 629 if (!ifp)
466 goto out; 630 goto out_check_rmap;
467 if (!xfs_sb_version_hasattr(&mp->m_sb) && 631 if (!xfs_sb_version_hasattr(&mp->m_sb) &&
468 !xfs_sb_version_hasattr2(&mp->m_sb)) 632 !xfs_sb_version_hasattr2(&mp->m_sb))
469 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL); 633 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
@@ -534,6 +698,10 @@ xfs_scrub_bmap(
534 goto out; 698 goto out;
535 } 699 }
536 700
701out_check_rmap:
702 error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
703 if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
704 goto out;
537out: 705out:
538 return error; 706 return error;
539} 707}