summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/scrub/agheader.c63
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
479struct 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. */
480STATIC int 486STATIC int
481xfs_scrub_agfl_block( 487xfs_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
505static int
506xfs_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. */
496int 517int
497xfs_scrub_agfl( 518xfs_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
570out_free:
571 kmem_free(sai.entries);
513out: 572out:
514 return error; 573 return error;
515} 574}