Skip to content

Commit a12890a

Browse files
committed
xfs: scrub the AGI
Add a forgotten check to the AGI verifier, then wire up the scrub infrastructure to check the AGI contents. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent ab9d5dc commit a12890a

File tree

5 files changed

+95
-3
lines changed

5 files changed

+95
-3
lines changed

fs/xfs/libxfs/xfs_fs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,10 @@ struct xfs_scrub_metadata {
487487
#define XFS_SCRUB_TYPE_SB 1 /* superblock */
488488
#define XFS_SCRUB_TYPE_AGF 2 /* AG free header */
489489
#define XFS_SCRUB_TYPE_AGFL 3 /* AG free list */
490+
#define XFS_SCRUB_TYPE_AGI 4 /* AG inode header */
490491

491492
/* Number of scrub subcommands. */
492-
#define XFS_SCRUB_TYPE_NR 4
493+
#define XFS_SCRUB_TYPE_NR 5
493494

494495
/* i: Repair this metadata. */
495496
#define XFS_SCRUB_IFLAG_REPAIR (1 << 0)

fs/xfs/scrub/agheader.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "xfs_sb.h"
3232
#include "xfs_inode.h"
3333
#include "xfs_alloc.h"
34+
#include "xfs_ialloc.h"
3435
#include "scrub/xfs_scrub.h"
3536
#include "scrub/scrub.h"
3637
#include "scrub/common.h"
@@ -512,3 +513,87 @@ xfs_scrub_agfl(
512513
out:
513514
return error;
514515
}
516+
517+
/* AGI */
518+
519+
/* Scrub the AGI. */
520+
int
521+
xfs_scrub_agi(
522+
struct xfs_scrub_context *sc)
523+
{
524+
struct xfs_mount *mp = sc->mp;
525+
struct xfs_agi *agi;
526+
xfs_agnumber_t agno;
527+
xfs_agblock_t agbno;
528+
xfs_agblock_t eoag;
529+
xfs_agino_t agino;
530+
xfs_agino_t first_agino;
531+
xfs_agino_t last_agino;
532+
xfs_agino_t icount;
533+
int i;
534+
int level;
535+
int error = 0;
536+
537+
agno = sc->sa.agno = sc->sm->sm_agno;
538+
error = xfs_scrub_ag_read_headers(sc, agno, &sc->sa.agi_bp,
539+
&sc->sa.agf_bp, &sc->sa.agfl_bp);
540+
if (!xfs_scrub_process_error(sc, agno, XFS_AGI_BLOCK(sc->mp), &error))
541+
goto out;
542+
543+
agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
544+
545+
/* Check the AG length */
546+
eoag = be32_to_cpu(agi->agi_length);
547+
if (eoag != xfs_ag_block_count(mp, agno))
548+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
549+
550+
/* Check btree roots and levels */
551+
agbno = be32_to_cpu(agi->agi_root);
552+
if (!xfs_verify_agbno(mp, agno, agbno))
553+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
554+
555+
level = be32_to_cpu(agi->agi_level);
556+
if (level <= 0 || level > XFS_BTREE_MAXLEVELS)
557+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
558+
559+
if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
560+
agbno = be32_to_cpu(agi->agi_free_root);
561+
if (!xfs_verify_agbno(mp, agno, agbno))
562+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
563+
564+
level = be32_to_cpu(agi->agi_free_level);
565+
if (level <= 0 || level > XFS_BTREE_MAXLEVELS)
566+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
567+
}
568+
569+
/* Check inode counters */
570+
xfs_ialloc_agino_range(mp, agno, &first_agino, &last_agino);
571+
icount = be32_to_cpu(agi->agi_count);
572+
if (icount > last_agino - first_agino + 1 ||
573+
icount < be32_to_cpu(agi->agi_freecount))
574+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
575+
576+
/* Check inode pointers */
577+
agino = be32_to_cpu(agi->agi_newino);
578+
if (agino != NULLAGINO && !xfs_verify_agino(mp, agno, agino))
579+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
580+
581+
agino = be32_to_cpu(agi->agi_dirino);
582+
if (agino != NULLAGINO && !xfs_verify_agino(mp, agno, agino))
583+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
584+
585+
/* Check unlinked inode buckets */
586+
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
587+
agino = be32_to_cpu(agi->agi_unlinked[i]);
588+
if (agino == NULLAGINO)
589+
continue;
590+
if (!xfs_verify_agino(mp, agno, agino))
591+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
592+
}
593+
594+
if (agi->agi_pad32 != cpu_to_be32(0))
595+
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
596+
597+
out:
598+
return error;
599+
}

fs/xfs/scrub/common.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ want_ag_read_header_failure(
254254
{
255255
/* Return all AG header read failures when scanning btrees. */
256256
if (sc->sm->sm_type != XFS_SCRUB_TYPE_AGF &&
257-
sc->sm->sm_type != XFS_SCRUB_TYPE_AGFL)
257+
sc->sm->sm_type != XFS_SCRUB_TYPE_AGFL &&
258+
sc->sm->sm_type != XFS_SCRUB_TYPE_AGI)
258259
return true;
259260
/*
260261
* If we're scanning a given type of AG header, we only want to
@@ -285,7 +286,7 @@ xfs_scrub_ag_read_headers(
285286
int error;
286287

287288
error = xfs_ialloc_read_agi(mp, sc->tp, agno, agi);
288-
if (error)
289+
if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGI))
289290
goto out;
290291

291292
error = xfs_alloc_read_agf(mp, sc->tp, agno, 0, agf);

fs/xfs/scrub/scrub.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
170170
.setup = xfs_scrub_setup_ag_header,
171171
.scrub = xfs_scrub_agfl,
172172
},
173+
{ /* agi */
174+
.setup = xfs_scrub_setup_ag_header,
175+
.scrub = xfs_scrub_agi,
176+
},
173177
};
174178

175179
/* This isn't a stable feature, warn once per day. */

fs/xfs/scrub/scrub.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@ int xfs_scrub_tester(struct xfs_scrub_context *sc);
7070
int xfs_scrub_superblock(struct xfs_scrub_context *sc);
7171
int xfs_scrub_agf(struct xfs_scrub_context *sc);
7272
int xfs_scrub_agfl(struct xfs_scrub_context *sc);
73+
int xfs_scrub_agi(struct xfs_scrub_context *sc);
7374

7475
#endif /* __XFS_SCRUB_SCRUB_H__ */

0 commit comments

Comments
 (0)