Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a12890ae authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

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: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent ab9d5dc5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -487,9 +487,10 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_SB	1	/* superblock */
#define XFS_SCRUB_TYPE_AGF	2	/* AG free header */
#define XFS_SCRUB_TYPE_AGFL	3	/* AG free list */
#define XFS_SCRUB_TYPE_AGI	4	/* AG inode header */

/* Number of scrub subcommands. */
#define XFS_SCRUB_TYPE_NR	4
#define XFS_SCRUB_TYPE_NR	5

/* i: Repair this metadata. */
#define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
+85 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -512,3 +513,87 @@ xfs_scrub_agfl(
out:
	return error;
}

/* AGI */

/* Scrub the AGI. */
int
xfs_scrub_agi(
	struct xfs_scrub_context	*sc)
{
	struct xfs_mount		*mp = sc->mp;
	struct xfs_agi			*agi;
	xfs_agnumber_t			agno;
	xfs_agblock_t			agbno;
	xfs_agblock_t			eoag;
	xfs_agino_t			agino;
	xfs_agino_t			first_agino;
	xfs_agino_t			last_agino;
	xfs_agino_t			icount;
	int				i;
	int				level;
	int				error = 0;

	agno = sc->sa.agno = sc->sm->sm_agno;
	error = xfs_scrub_ag_read_headers(sc, agno, &sc->sa.agi_bp,
			&sc->sa.agf_bp, &sc->sa.agfl_bp);
	if (!xfs_scrub_process_error(sc, agno, XFS_AGI_BLOCK(sc->mp), &error))
		goto out;

	agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);

	/* Check the AG length */
	eoag = be32_to_cpu(agi->agi_length);
	if (eoag != xfs_ag_block_count(mp, agno))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	/* Check btree roots and levels */
	agbno = be32_to_cpu(agi->agi_root);
	if (!xfs_verify_agbno(mp, agno, agbno))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	level = be32_to_cpu(agi->agi_level);
	if (level <= 0 || level > XFS_BTREE_MAXLEVELS)
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
		agbno = be32_to_cpu(agi->agi_free_root);
		if (!xfs_verify_agbno(mp, agno, agbno))
			xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

		level = be32_to_cpu(agi->agi_free_level);
		if (level <= 0 || level > XFS_BTREE_MAXLEVELS)
			xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
	}

	/* Check inode counters */
	xfs_ialloc_agino_range(mp, agno, &first_agino, &last_agino);
	icount = be32_to_cpu(agi->agi_count);
	if (icount > last_agino - first_agino + 1 ||
	    icount < be32_to_cpu(agi->agi_freecount))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	/* Check inode pointers */
	agino = be32_to_cpu(agi->agi_newino);
	if (agino != NULLAGINO && !xfs_verify_agino(mp, agno, agino))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	agino = be32_to_cpu(agi->agi_dirino);
	if (agino != NULLAGINO && !xfs_verify_agino(mp, agno, agino))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

	/* Check unlinked inode buckets */
	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
		agino = be32_to_cpu(agi->agi_unlinked[i]);
		if (agino == NULLAGINO)
			continue;
		if (!xfs_verify_agino(mp, agno, agino))
			xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
	}

	if (agi->agi_pad32 != cpu_to_be32(0))
		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);

out:
	return error;
}
+3 −2
Original line number Diff line number Diff line
@@ -254,7 +254,8 @@ want_ag_read_header_failure(
{
	/* Return all AG header read failures when scanning btrees. */
	if (sc->sm->sm_type != XFS_SCRUB_TYPE_AGF &&
	    sc->sm->sm_type != XFS_SCRUB_TYPE_AGFL)
	    sc->sm->sm_type != XFS_SCRUB_TYPE_AGFL &&
	    sc->sm->sm_type != XFS_SCRUB_TYPE_AGI)
		return true;
	/*
	 * If we're scanning a given type of AG header, we only want to
@@ -285,7 +286,7 @@ xfs_scrub_ag_read_headers(
	int				error;

	error = xfs_ialloc_read_agi(mp, sc->tp, agno, agi);
	if (error)
	if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGI))
		goto out;

	error = xfs_alloc_read_agf(mp, sc->tp, agno, 0, agf);
+4 −0
Original line number Diff line number Diff line
@@ -170,6 +170,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
		.setup	= xfs_scrub_setup_ag_header,
		.scrub	= xfs_scrub_agfl,
	},
	{ /* agi */
		.setup	= xfs_scrub_setup_ag_header,
		.scrub	= xfs_scrub_agi,
	},
};

/* This isn't a stable feature, warn once per day. */
+1 −0
Original line number Diff line number Diff line
@@ -70,5 +70,6 @@ int xfs_scrub_tester(struct xfs_scrub_context *sc);
int xfs_scrub_superblock(struct xfs_scrub_context *sc);
int xfs_scrub_agf(struct xfs_scrub_context *sc);
int xfs_scrub_agfl(struct xfs_scrub_context *sc);
int xfs_scrub_agi(struct xfs_scrub_context *sc);

#endif	/* __XFS_SCRUB_SCRUB_H__ */