@@ -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 */
@@ -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);
@@ -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 */
@@ -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. */
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(-)