diff options
-rw-r--r-- | fs/xfs/scrub/agheader.c | 63 |
1 files changed, 61 insertions, 2 deletions
diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 5495aa50002c..2a9b4f9e93c6 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c | |||
@@ -476,6 +476,12 @@ out: | |||
476 | 476 | ||
477 | /* AGFL */ | 477 | /* AGFL */ |
478 | 478 | ||
479 | struct xfs_scrub_agfl_info { | ||
480 | unsigned int sz_entries; | ||
481 | unsigned int nr_entries; | ||
482 | xfs_agblock_t *entries; | ||
483 | }; | ||
484 | |||
479 | /* Scrub an AGFL block. */ | 485 | /* Scrub an AGFL block. */ |
480 | STATIC int | 486 | STATIC int |
481 | xfs_scrub_agfl_block( | 487 | xfs_scrub_agfl_block( |
@@ -484,20 +490,39 @@ xfs_scrub_agfl_block( | |||
484 | void *priv) | 490 | void *priv) |
485 | { | 491 | { |
486 | struct xfs_mount *mp = sc->mp; | 492 | struct xfs_mount *mp = sc->mp; |
493 | struct xfs_scrub_agfl_info *sai = priv; | ||
487 | xfs_agnumber_t agno = sc->sa.agno; | 494 | xfs_agnumber_t agno = sc->sa.agno; |
488 | 495 | ||
489 | if (!xfs_verify_agbno(mp, agno, agbno)) | 496 | if (xfs_verify_agbno(mp, agno, agbno) && |
497 | sai->nr_entries < sai->sz_entries) | ||
498 | sai->entries[sai->nr_entries++] = agbno; | ||
499 | else | ||
490 | xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp); | 500 | xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp); |
491 | 501 | ||
492 | return 0; | 502 | return 0; |
493 | } | 503 | } |
494 | 504 | ||
505 | static int | ||
506 | xfs_scrub_agblock_cmp( | ||
507 | const void *pa, | ||
508 | const void *pb) | ||
509 | { | ||
510 | const xfs_agblock_t *a = pa; | ||
511 | const xfs_agblock_t *b = pb; | ||
512 | |||
513 | return (int)*a - (int)*b; | ||
514 | } | ||
515 | |||
495 | /* Scrub the AGFL. */ | 516 | /* Scrub the AGFL. */ |
496 | int | 517 | int |
497 | xfs_scrub_agfl( | 518 | xfs_scrub_agfl( |
498 | struct xfs_scrub_context *sc) | 519 | struct xfs_scrub_context *sc) |
499 | { | 520 | { |
521 | struct xfs_scrub_agfl_info sai = { 0 }; | ||
522 | struct xfs_agf *agf; | ||
500 | xfs_agnumber_t agno; | 523 | xfs_agnumber_t agno; |
524 | unsigned int agflcount; | ||
525 | unsigned int i; | ||
501 | int error; | 526 | int error; |
502 | 527 | ||
503 | agno = sc->sa.agno = sc->sm->sm_agno; | 528 | agno = sc->sa.agno = sc->sm->sm_agno; |
@@ -508,8 +533,42 @@ xfs_scrub_agfl( | |||
508 | if (!sc->sa.agf_bp) | 533 | if (!sc->sa.agf_bp) |
509 | return -EFSCORRUPTED; | 534 | return -EFSCORRUPTED; |
510 | 535 | ||
536 | /* Allocate buffer to ensure uniqueness of AGFL entries. */ | ||
537 | agf = XFS_BUF_TO_AGF(sc->sa.agf_bp); | ||
538 | agflcount = be32_to_cpu(agf->agf_flcount); | ||
539 | if (agflcount > XFS_AGFL_SIZE(sc->mp)) { | ||
540 | xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); | ||
541 | goto out; | ||
542 | } | ||
543 | sai.sz_entries = agflcount; | ||
544 | sai.entries = kmem_zalloc(sizeof(xfs_agblock_t) * agflcount, KM_NOFS); | ||
545 | if (!sai.entries) { | ||
546 | error = -ENOMEM; | ||
547 | goto out; | ||
548 | } | ||
549 | |||
511 | /* Check the blocks in the AGFL. */ | 550 | /* Check the blocks in the AGFL. */ |
512 | return xfs_scrub_walk_agfl(sc, xfs_scrub_agfl_block, NULL); | 551 | error = xfs_scrub_walk_agfl(sc, xfs_scrub_agfl_block, &sai); |
552 | if (error) | ||
553 | goto out_free; | ||
554 | |||
555 | if (agflcount != sai.nr_entries) { | ||
556 | xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); | ||
557 | goto out_free; | ||
558 | } | ||
559 | |||
560 | /* Sort entries, check for duplicates. */ | ||
561 | sort(sai.entries, sai.nr_entries, sizeof(sai.entries[0]), | ||
562 | xfs_scrub_agblock_cmp, NULL); | ||
563 | for (i = 1; i < sai.nr_entries; i++) { | ||
564 | if (sai.entries[i] == sai.entries[i - 1]) { | ||
565 | xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); | ||
566 | break; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | out_free: | ||
571 | kmem_free(sai.entries); | ||
513 | out: | 572 | out: |
514 | return error; | 573 | return error; |
515 | } | 574 | } |