From 983d09ffe396ed5d5339a1b9ff994dd0b0f2069f Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 3 Apr 2013 16:11:15 +1100 Subject: [PATCH] xfs: add CRC checks to the AGI Same set of changes made to the AGF need to be made to the AGI. This patch has a similar history to the AGF, hence a similar sign-off chain. Signed-off-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_ag.h | 8 ++++++ fs/xfs/xfs_buf_item.h | 4 ++- fs/xfs/xfs_fsops.c | 3 +++ fs/xfs/xfs_ialloc.c | 57 ++++++++++++++++++++++++++++++---------- fs/xfs/xfs_log_recover.c | 8 ++++++ 5 files changed, 65 insertions(+), 15 deletions(-) diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 272ef097e2be..317aa86d96ea 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -152,6 +152,7 @@ typedef struct xfs_agi { __be32 agi_root; /* root of inode btree */ __be32 agi_level; /* levels in inode btree */ __be32 agi_freecount; /* number of free inodes */ + __be32 agi_newino; /* new inode just allocated */ __be32 agi_dirino; /* last directory inode chunk */ /* @@ -159,6 +160,13 @@ typedef struct xfs_agi { * still being referenced. */ __be32 agi_unlinked[XFS_AGI_UNLINKED_BUCKETS]; + + uuid_t agi_uuid; /* uuid of filesystem */ + __be32 agi_crc; /* crc of agi sector */ + __be32 agi_pad32; + __be64 agi_lsn; /* last write sequence */ + + /* structure must be padded to 64 bit alignment */ } xfs_agi_t; #define XFS_AGI_MAGICNUM 0x00000001 diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 067d5f0fc233..c25660691e08 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -47,6 +47,7 @@ extern kmem_zone_t *xfs_buf_item_zone; #define XFS_BLF_BTREE_BUF (1<<5) #define XFS_BLF_AGF_BUF (1<<6) #define XFS_BLF_AGFL_BUF (1<<7) +#define XFS_BLF_AGI_BUF (1<<8) #define XFS_BLF_TYPE_MASK \ (XFS_BLF_UDQUOT_BUF | \ @@ -54,7 +55,8 @@ extern kmem_zone_t *xfs_buf_item_zone; XFS_BLF_GDQUOT_BUF | \ XFS_BLF_BTREE_BUF | \ XFS_BLF_AGF_BUF | \ - XFS_BLF_AGFL_BUF) + XFS_BLF_AGFL_BUF | \ + XFS_BLF_AGI_BUF) #define XFS_BLF_CHUNK 128 #define XFS_BLF_SHIFT 7 diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index a693a54799e5..87595b211da1 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -304,8 +304,11 @@ xfs_growfs_data_private( agi->agi_freecount = 0; agi->agi_newino = cpu_to_be32(NULLAGINO); agi->agi_dirino = cpu_to_be32(NULLAGINO); + if (xfs_sb_version_hascrc(&mp->m_sb)) + uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_uuid); for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO); + error = xfs_bwrite(bp); xfs_buf_relse(bp); if (error) diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index ba626613bf49..6d0a4954aa8c 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -36,6 +36,8 @@ #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_bmap.h" +#include "xfs_cksum.h" +#include "xfs_buf_item.h" /* @@ -1453,6 +1455,7 @@ xfs_ialloc_log_agi( /* * Log the allocation group inode header buffer. */ + xfs_trans_buf_set_type(tp, bp, XFS_BLF_AGI_BUF); xfs_trans_log_buf(tp, bp, first, last); } @@ -1470,19 +1473,23 @@ xfs_check_agi_unlinked( #define xfs_check_agi_unlinked(agi) #endif -static void +static bool xfs_agi_verify( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_target->bt_mount; struct xfs_agi *agi = XFS_BUF_TO_AGI(bp); - int agi_ok; + if (xfs_sb_version_hascrc(&mp->m_sb) && + !uuid_equal(&agi->agi_uuid, &mp->m_sb.sb_uuid)) + return false; /* * Validate the magic number of the agi block. */ - agi_ok = agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC) && - XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)); + if (agi->agi_magicnum != cpu_to_be32(XFS_AGI_MAGIC)) + return false; + if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum))) + return false; /* * during growfs operations, the perag is not fully initialised, @@ -1490,30 +1497,52 @@ xfs_agi_verify( * use it by using uncached buffers that don't have the perag attached * so we can detect and avoid this problem. */ - if (bp->b_pag) - agi_ok = agi_ok && be32_to_cpu(agi->agi_seqno) == - bp->b_pag->pag_agno; + if (bp->b_pag && be32_to_cpu(agi->agi_seqno) != bp->b_pag->pag_agno) + return false; - if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI, - XFS_RANDOM_IALLOC_READ_AGI))) { - XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, agi); - xfs_buf_ioerror(bp, EFSCORRUPTED); - } xfs_check_agi_unlinked(agi); + return true; } static void xfs_agi_read_verify( struct xfs_buf *bp) { - xfs_agi_verify(bp); + struct xfs_mount *mp = bp->b_target->bt_mount; + int agi_ok = 1; + + if (xfs_sb_version_hascrc(&mp->m_sb)) + agi_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length), + offsetof(struct xfs_agi, agi_crc)); + agi_ok = agi_ok && xfs_agi_verify(bp); + + if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI, + XFS_RANDOM_IALLOC_READ_AGI))) { + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); + xfs_buf_ioerror(bp, EFSCORRUPTED); + } } static void xfs_agi_write_verify( struct xfs_buf *bp) { - xfs_agi_verify(bp); + struct xfs_mount *mp = bp->b_target->bt_mount; + struct xfs_buf_log_item *bip = bp->b_fspriv; + + if (!xfs_agi_verify(bp)) { + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); + xfs_buf_ioerror(bp, EFSCORRUPTED); + return; + } + + if (!xfs_sb_version_hascrc(&mp->m_sb)) + return; + + if (bip) + XFS_BUF_TO_AGI(bp)->agi_lsn = cpu_to_be64(bip->bli_item.li_lsn); + xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), + offsetof(struct xfs_agi, agi_crc)); } const struct xfs_buf_ops xfs_agi_buf_ops = { diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index da2a19e0554e..6778a7943db4 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1971,6 +1971,14 @@ xlog_recover_do_reg_buffer( } bp->b_ops = &xfs_agfl_buf_ops; break; + case XFS_BLF_AGI_BUF: + if (*(__be32 *)bp->b_addr != cpu_to_be32(XFS_AGI_MAGIC)) { + xfs_warn(mp, "Bad AGI block magic!"); + ASSERT(0); + break; + } + bp->b_ops = &xfs_agi_buf_ops; + break; default: break; } -- 2.34.1