diff mbox

[09/25] xfs: scrub AGF and AGFL

Message ID 147216846953.3108.18345853853421759907.stgit@birch.djwong.org (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Darrick J. Wong Aug. 25, 2016, 11:41 p.m. UTC
Check the block references in the AGF and AGFL headers to make sure
they make sense.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_alloc.c |    2 -
 fs/xfs/libxfs/xfs_alloc.h |    2 +
 fs/xfs/libxfs/xfs_fs.h    |    4 +
 fs/xfs/xfs_scrub.c        |  133 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 139 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 8b3e6b3..37782a1 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -629,7 +629,7 @@  const struct xfs_buf_ops xfs_agfl_buf_ops = {
 /*
  * Read in the allocation group free block array.
  */
-STATIC int				/* error */
+int					/* error */
 xfs_alloc_read_agfl(
 	xfs_mount_t	*mp,		/* mount point structure */
 	xfs_trans_t	*tp,		/* transaction pointer */
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 0b00de0..c3ada6b 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -204,6 +204,8 @@  xfs_alloc_get_rec(
 
 int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
 			xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
+int xfs_alloc_read_agfl(struct xfs_mount *mp, struct xfs_trans *tp,
+			xfs_agnumber_t agno, struct xfs_buf **bpp);
 int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags);
 int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
 		struct xfs_buf **agbp);
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 8d58061..8249ae0 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -536,7 +536,9 @@  struct xfs_scrub_metadata {
  * Metadata types and flags for scrub operation.
  */
 #define XFS_SCRUB_TYPE_SB	0	/* superblock */
-#define XFS_SCRUB_TYPE_MAX	0
+#define XFS_SCRUB_TYPE_AGF	1	/* AG free header */
+#define XFS_SCRUB_TYPE_AGFL	2	/* AG free list */
+#define XFS_SCRUB_TYPE_MAX	2
 
 #define XFS_SCRUB_FLAGS_ALL	0x0	/* no flags yet */
 
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 3def216..c04a097 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -778,6 +778,137 @@  out:
 	return error;
 }
 
+/* Scrub the AGF. */
+STATIC int
+xfs_scrub_agf(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	struct xfs_mount		*mp = ip->i_mount;
+	struct xfs_agf			*agf;
+	struct xfs_buf			*agi_bp = NULL;
+	struct xfs_buf			*agf_bp = NULL;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			agbno;
+	xfs_agblock_t			eoag;
+	xfs_daddr_t			daddr;
+	xfs_daddr_t			eofs;
+	int				error;
+
+	if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+		return -EINVAL;
+	agno = sm->control;
+
+	/* Let the verifier check most of the AGF fields. */
+	error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+	if (error)
+		return error;
+
+	agf = XFS_BUF_TO_AGF(agf_bp);
+	eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+
+	/* Check the AG length */
+	eoag = be32_to_cpu(agf->agf_length);
+	if (agno == mp->m_sb.sb_agcount - 1) {
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+				eoag <= mp->m_sb.sb_agblocks);
+	} else {
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+				eoag == mp->m_sb.sb_agblocks);
+	}
+	daddr = XFS_AGB_TO_DADDR(mp, agno, eoag);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr <= eofs);
+
+	/* Check the AGF btree roots */
+	agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]);
+	daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < mp->m_sb.sb_agblocks);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+
+	agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]);
+	daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < mp->m_sb.sb_agblocks);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+	XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]);
+		daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+				agbno < mp->m_sb.sb_agblocks);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+	}
+
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		agbno = be32_to_cpu(agf->agf_refcount_root);
+		daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+				agbno < mp->m_sb.sb_agblocks);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+	}
+
+	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+	return error;
+}
+
+/* Scrub the AGFL. */
+STATIC int
+xfs_scrub_agfl(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	struct xfs_mount		*mp = ip->i_mount;
+	struct xfs_agf			*agf;
+	struct xfs_buf			*agi_bp = NULL;
+	struct xfs_buf			*agf_bp = NULL;
+	struct xfs_buf			*agfl_bp;
+	__be32				*agfl_bno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			agbno;
+	xfs_agblock_t			eoag;
+	xfs_daddr_t			eofs;
+	int				i;
+	int				error;
+
+	if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+		return -EINVAL;
+	agno = sm->control;
+
+	/* Let the verifier check most of the AGF fields. */
+	error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+	if (error)
+		return error;
+
+	error = xfs_alloc_read_agfl(mp, NULL, agno, &agfl_bp);
+	if (error)
+		goto err_no_agfl;
+
+	agf = XFS_BUF_TO_AGF(agf_bp);
+	eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+	eoag = be32_to_cpu(agf->agf_length);
+
+	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
+	for (i = be32_to_cpu(agf->agf_flfirst);
+	     i <= be32_to_cpu(agf->agf_fllast);
+	     i++) {
+		agbno = be32_to_cpu(agfl_bno[i]);
+		XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+				XFS_AGB_TO_DADDR(mp, agno, agbno) < eofs);
+		XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+				agbno < mp->m_sb.sb_agblocks);
+		XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+				agbno < eoag);
+	}
+
+	xfs_buf_relse(agfl_bp);
+err_no_agfl:
+	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+	return error;
+}
+
 /* Scrubbing dispatch. */
 
 struct xfs_scrub_meta_fns {
@@ -787,6 +918,8 @@  struct xfs_scrub_meta_fns {
 
 static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
 	{xfs_scrub_sb,		NULL},
+	{xfs_scrub_agf,		NULL},
+	{xfs_scrub_agfl,	NULL},
 };
 
 /* Dispatch metadata scrubbing. */