diff options
author | Christoph Hellwig <hch@lst.de> | 2013-04-03 01:11:14 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2013-04-21 15:55:34 -0400 |
commit | 77c95bba013089fa868217283eb6d98a05913e53 (patch) | |
tree | 7b694c2baa200ac1bf2770d7bdfa4f2a292a2a58 /fs/xfs | |
parent | 4e0e6040c4052aff15a494ac05778f4086d24c33 (diff) |
xfs: add CRC checks to the AGFL
Add CRC checks, location information and a magic number to the AGFL.
Previously the AGFL was just a block containing nothing but the
free block pointers. The new AGFL has a real header with the usual
boilerplate instead, so that we can verify it's not corrupted and
written into the right place.
[dchinner@redhat.com] Added LSN field, reworked significantly to fit
into new verifier structure and growfs structure, enabled full
verifier functionality now there is a header to verify and we can
guarantee an initialised AGFL.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_ag.h | 25 | ||||
-rw-r--r-- | fs/xfs/xfs_alloc.c | 119 | ||||
-rw-r--r-- | fs/xfs/xfs_buf_item.h | 4 | ||||
-rw-r--r-- | fs/xfs/xfs_fsops.c | 5 | ||||
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 10 |
5 files changed, 119 insertions, 44 deletions
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 9f4815e0c96f..272ef097e2be 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h | |||
@@ -30,6 +30,7 @@ struct xfs_trans; | |||
30 | 30 | ||
31 | #define XFS_AGF_MAGIC 0x58414746 /* 'XAGF' */ | 31 | #define XFS_AGF_MAGIC 0x58414746 /* 'XAGF' */ |
32 | #define XFS_AGI_MAGIC 0x58414749 /* 'XAGI' */ | 32 | #define XFS_AGI_MAGIC 0x58414749 /* 'XAGI' */ |
33 | #define XFS_AGFL_MAGIC 0x5841464c /* 'XAFL' */ | ||
33 | #define XFS_AGF_VERSION 1 | 34 | #define XFS_AGF_VERSION 1 |
34 | #define XFS_AGI_VERSION 1 | 35 | #define XFS_AGI_VERSION 1 |
35 | 36 | ||
@@ -190,11 +191,31 @@ extern const struct xfs_buf_ops xfs_agi_buf_ops; | |||
190 | */ | 191 | */ |
191 | #define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log)) | 192 | #define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log)) |
192 | #define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp)) | 193 | #define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp)) |
193 | #define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t)) | ||
194 | #define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)((bp)->b_addr)) | 194 | #define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)((bp)->b_addr)) |
195 | 195 | ||
196 | #define XFS_BUF_TO_AGFL_BNO(mp, bp) \ | ||
197 | (xfs_sb_version_hascrc(&((mp)->m_sb)) ? \ | ||
198 | &(XFS_BUF_TO_AGFL(bp)->agfl_bno[0]) : \ | ||
199 | (__be32 *)(bp)->b_addr) | ||
200 | |||
201 | /* | ||
202 | * Size of the AGFL. For CRC-enabled filesystes we steal a couple of | ||
203 | * slots in the beginning of the block for a proper header with the | ||
204 | * location information and CRC. | ||
205 | */ | ||
206 | #define XFS_AGFL_SIZE(mp) \ | ||
207 | (((mp)->m_sb.sb_sectsize - \ | ||
208 | (xfs_sb_version_hascrc(&((mp)->m_sb)) ? \ | ||
209 | sizeof(struct xfs_agfl) : 0)) / \ | ||
210 | sizeof(xfs_agblock_t)) | ||
211 | |||
196 | typedef struct xfs_agfl { | 212 | typedef struct xfs_agfl { |
197 | __be32 agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */ | 213 | __be32 agfl_magicnum; |
214 | __be32 agfl_seqno; | ||
215 | uuid_t agfl_uuid; | ||
216 | __be64 agfl_lsn; | ||
217 | __be32 agfl_crc; | ||
218 | __be32 agfl_bno[]; /* actually XFS_AGFL_SIZE(mp) */ | ||
198 | } xfs_agfl_t; | 219 | } xfs_agfl_t; |
199 | 220 | ||
200 | /* | 221 | /* |
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index c34f799aa92e..40d3d576f473 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c | |||
@@ -432,53 +432,84 @@ xfs_alloc_fixup_trees( | |||
432 | return 0; | 432 | return 0; |
433 | } | 433 | } |
434 | 434 | ||
435 | static void | 435 | static bool |
436 | xfs_agfl_verify( | 436 | xfs_agfl_verify( |
437 | struct xfs_buf *bp) | 437 | struct xfs_buf *bp) |
438 | { | 438 | { |
439 | #ifdef WHEN_CRCS_COME_ALONG | ||
440 | /* | ||
441 | * we cannot actually do any verification of the AGFL because mkfs does | ||
442 | * not initialise the AGFL to zero or NULL. Hence the only valid part of | ||
443 | * the AGFL is what the AGF says is active. We can't get to the AGF, so | ||
444 | * we can't verify just those entries are valid. | ||
445 | * | ||
446 | * This problem goes away when the CRC format change comes along as that | ||
447 | * requires the AGFL to be initialised by mkfs. At that point, we can | ||
448 | * verify the blocks in the agfl -active or not- lie within the bounds | ||
449 | * of the AG. Until then, just leave this check ifdef'd out. | ||
450 | */ | ||
451 | struct xfs_mount *mp = bp->b_target->bt_mount; | 439 | struct xfs_mount *mp = bp->b_target->bt_mount; |
452 | struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(bp); | 440 | struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(bp); |
453 | int agfl_ok = 1; | ||
454 | |||
455 | int i; | 441 | int i; |
456 | 442 | ||
443 | if (!uuid_equal(&agfl->agfl_uuid, &mp->m_sb.sb_uuid)) | ||
444 | return false; | ||
445 | if (be32_to_cpu(agfl->agfl_magicnum) != XFS_AGFL_MAGIC) | ||
446 | return false; | ||
447 | /* | ||
448 | * during growfs operations, the perag is not fully initialised, | ||
449 | * so we can't use it for any useful checking. growfs ensures we can't | ||
450 | * use it by using uncached buffers that don't have the perag attached | ||
451 | * so we can detect and avoid this problem. | ||
452 | */ | ||
453 | if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno) | ||
454 | return false; | ||
455 | |||
457 | for (i = 0; i < XFS_AGFL_SIZE(mp); i++) { | 456 | for (i = 0; i < XFS_AGFL_SIZE(mp); i++) { |
458 | if (be32_to_cpu(agfl->agfl_bno[i]) == NULLAGBLOCK || | 457 | if (be32_to_cpu(agfl->agfl_bno[i]) != NULLAGBLOCK && |
459 | be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks) | 458 | be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks) |
460 | agfl_ok = 0; | 459 | return false; |
461 | } | 460 | } |
461 | return true; | ||
462 | } | ||
463 | |||
464 | static void | ||
465 | xfs_agfl_read_verify( | ||
466 | struct xfs_buf *bp) | ||
467 | { | ||
468 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
469 | int agfl_ok = 1; | ||
470 | |||
471 | /* | ||
472 | * There is no verification of non-crc AGFLs because mkfs does not | ||
473 | * initialise the AGFL to zero or NULL. Hence the only valid part of the | ||
474 | * AGFL is what the AGF says is active. We can't get to the AGF, so we | ||
475 | * can't verify just those entries are valid. | ||
476 | */ | ||
477 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
478 | return; | ||
479 | |||
480 | agfl_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length), | ||
481 | offsetof(struct xfs_agfl, agfl_crc)); | ||
482 | |||
483 | agfl_ok = agfl_ok && xfs_agfl_verify(bp); | ||
462 | 484 | ||
463 | if (!agfl_ok) { | 485 | if (!agfl_ok) { |
464 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, agfl); | 486 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); |
465 | xfs_buf_ioerror(bp, EFSCORRUPTED); | 487 | xfs_buf_ioerror(bp, EFSCORRUPTED); |
466 | } | 488 | } |
467 | #endif | ||
468 | } | 489 | } |
469 | 490 | ||
470 | static void | 491 | static void |
471 | xfs_agfl_write_verify( | 492 | xfs_agfl_write_verify( |
472 | struct xfs_buf *bp) | 493 | struct xfs_buf *bp) |
473 | { | 494 | { |
474 | xfs_agfl_verify(bp); | 495 | struct xfs_mount *mp = bp->b_target->bt_mount; |
475 | } | 496 | struct xfs_buf_log_item *bip = bp->b_fspriv; |
476 | 497 | ||
477 | static void | 498 | /* no verification of non-crc AGFLs */ |
478 | xfs_agfl_read_verify( | 499 | if (!xfs_sb_version_hascrc(&mp->m_sb)) |
479 | struct xfs_buf *bp) | 500 | return; |
480 | { | 501 | |
481 | xfs_agfl_verify(bp); | 502 | if (!xfs_agfl_verify(bp)) { |
503 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); | ||
504 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
505 | return; | ||
506 | } | ||
507 | |||
508 | if (bip) | ||
509 | XFS_BUF_TO_AGFL(bp)->agfl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | ||
510 | |||
511 | xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), | ||
512 | offsetof(struct xfs_agfl, agfl_crc)); | ||
482 | } | 513 | } |
483 | 514 | ||
484 | const struct xfs_buf_ops xfs_agfl_buf_ops = { | 515 | const struct xfs_buf_ops xfs_agfl_buf_ops = { |
@@ -1984,18 +2015,18 @@ xfs_alloc_get_freelist( | |||
1984 | int btreeblk) /* destination is a AGF btree */ | 2015 | int btreeblk) /* destination is a AGF btree */ |
1985 | { | 2016 | { |
1986 | xfs_agf_t *agf; /* a.g. freespace structure */ | 2017 | xfs_agf_t *agf; /* a.g. freespace structure */ |
1987 | xfs_agfl_t *agfl; /* a.g. freelist structure */ | ||
1988 | xfs_buf_t *agflbp;/* buffer for a.g. freelist structure */ | 2018 | xfs_buf_t *agflbp;/* buffer for a.g. freelist structure */ |
1989 | xfs_agblock_t bno; /* block number returned */ | 2019 | xfs_agblock_t bno; /* block number returned */ |
2020 | __be32 *agfl_bno; | ||
1990 | int error; | 2021 | int error; |
1991 | int logflags; | 2022 | int logflags; |
1992 | xfs_mount_t *mp; /* mount structure */ | 2023 | xfs_mount_t *mp = tp->t_mountp; |
1993 | xfs_perag_t *pag; /* per allocation group data */ | 2024 | xfs_perag_t *pag; /* per allocation group data */ |
1994 | 2025 | ||
1995 | agf = XFS_BUF_TO_AGF(agbp); | ||
1996 | /* | 2026 | /* |
1997 | * Freelist is empty, give up. | 2027 | * Freelist is empty, give up. |
1998 | */ | 2028 | */ |
2029 | agf = XFS_BUF_TO_AGF(agbp); | ||
1999 | if (!agf->agf_flcount) { | 2030 | if (!agf->agf_flcount) { |
2000 | *bnop = NULLAGBLOCK; | 2031 | *bnop = NULLAGBLOCK; |
2001 | return 0; | 2032 | return 0; |
@@ -2003,15 +2034,17 @@ xfs_alloc_get_freelist( | |||
2003 | /* | 2034 | /* |
2004 | * Read the array of free blocks. | 2035 | * Read the array of free blocks. |
2005 | */ | 2036 | */ |
2006 | mp = tp->t_mountp; | 2037 | error = xfs_alloc_read_agfl(mp, tp, be32_to_cpu(agf->agf_seqno), |
2007 | if ((error = xfs_alloc_read_agfl(mp, tp, | 2038 | &agflbp); |
2008 | be32_to_cpu(agf->agf_seqno), &agflbp))) | 2039 | if (error) |
2009 | return error; | 2040 | return error; |
2010 | agfl = XFS_BUF_TO_AGFL(agflbp); | 2041 | |
2042 | |||
2011 | /* | 2043 | /* |
2012 | * Get the block number and update the data structures. | 2044 | * Get the block number and update the data structures. |
2013 | */ | 2045 | */ |
2014 | bno = be32_to_cpu(agfl->agfl_bno[be32_to_cpu(agf->agf_flfirst)]); | 2046 | agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp); |
2047 | bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]); | ||
2015 | be32_add_cpu(&agf->agf_flfirst, 1); | 2048 | be32_add_cpu(&agf->agf_flfirst, 1); |
2016 | xfs_trans_brelse(tp, agflbp); | 2049 | xfs_trans_brelse(tp, agflbp); |
2017 | if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp)) | 2050 | if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp)) |
@@ -2104,12 +2137,13 @@ xfs_alloc_put_freelist( | |||
2104 | int btreeblk) /* block came from a AGF btree */ | 2137 | int btreeblk) /* block came from a AGF btree */ |
2105 | { | 2138 | { |
2106 | xfs_agf_t *agf; /* a.g. freespace structure */ | 2139 | xfs_agf_t *agf; /* a.g. freespace structure */ |
2107 | xfs_agfl_t *agfl; /* a.g. free block array */ | ||
2108 | __be32 *blockp;/* pointer to array entry */ | 2140 | __be32 *blockp;/* pointer to array entry */ |
2109 | int error; | 2141 | int error; |
2110 | int logflags; | 2142 | int logflags; |
2111 | xfs_mount_t *mp; /* mount structure */ | 2143 | xfs_mount_t *mp; /* mount structure */ |
2112 | xfs_perag_t *pag; /* per allocation group data */ | 2144 | xfs_perag_t *pag; /* per allocation group data */ |
2145 | __be32 *agfl_bno; | ||
2146 | int startoff; | ||
2113 | 2147 | ||
2114 | agf = XFS_BUF_TO_AGF(agbp); | 2148 | agf = XFS_BUF_TO_AGF(agbp); |
2115 | mp = tp->t_mountp; | 2149 | mp = tp->t_mountp; |
@@ -2117,7 +2151,6 @@ xfs_alloc_put_freelist( | |||
2117 | if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp, | 2151 | if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp, |
2118 | be32_to_cpu(agf->agf_seqno), &agflbp))) | 2152 | be32_to_cpu(agf->agf_seqno), &agflbp))) |
2119 | return error; | 2153 | return error; |
2120 | agfl = XFS_BUF_TO_AGFL(agflbp); | ||
2121 | be32_add_cpu(&agf->agf_fllast, 1); | 2154 | be32_add_cpu(&agf->agf_fllast, 1); |
2122 | if (be32_to_cpu(agf->agf_fllast) == XFS_AGFL_SIZE(mp)) | 2155 | if (be32_to_cpu(agf->agf_fllast) == XFS_AGFL_SIZE(mp)) |
2123 | agf->agf_fllast = 0; | 2156 | agf->agf_fllast = 0; |
@@ -2138,13 +2171,17 @@ xfs_alloc_put_freelist( | |||
2138 | xfs_alloc_log_agf(tp, agbp, logflags); | 2171 | xfs_alloc_log_agf(tp, agbp, logflags); |
2139 | 2172 | ||
2140 | ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)); | 2173 | ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)); |
2141 | blockp = &agfl->agfl_bno[be32_to_cpu(agf->agf_fllast)]; | 2174 | |
2175 | agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp); | ||
2176 | blockp = &agfl_bno[be32_to_cpu(agf->agf_fllast)]; | ||
2142 | *blockp = cpu_to_be32(bno); | 2177 | *blockp = cpu_to_be32(bno); |
2178 | startoff = (char *)blockp - (char *)agflbp->b_addr; | ||
2179 | |||
2143 | xfs_alloc_log_agf(tp, agbp, logflags); | 2180 | xfs_alloc_log_agf(tp, agbp, logflags); |
2144 | xfs_trans_log_buf(tp, agflbp, | 2181 | |
2145 | (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl), | 2182 | xfs_trans_buf_set_type(tp, agflbp, XFS_BLF_AGFL_BUF); |
2146 | (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl + | 2183 | xfs_trans_log_buf(tp, agflbp, startoff, |
2147 | sizeof(xfs_agblock_t) - 1)); | 2184 | startoff + sizeof(xfs_agblock_t) - 1); |
2148 | return 0; | 2185 | return 0; |
2149 | } | 2186 | } |
2150 | 2187 | ||
diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 76bd7a167a70..067d5f0fc233 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h | |||
@@ -46,13 +46,15 @@ extern kmem_zone_t *xfs_buf_item_zone; | |||
46 | */ | 46 | */ |
47 | #define XFS_BLF_BTREE_BUF (1<<5) | 47 | #define XFS_BLF_BTREE_BUF (1<<5) |
48 | #define XFS_BLF_AGF_BUF (1<<6) | 48 | #define XFS_BLF_AGF_BUF (1<<6) |
49 | #define XFS_BLF_AGFL_BUF (1<<7) | ||
49 | 50 | ||
50 | #define XFS_BLF_TYPE_MASK \ | 51 | #define XFS_BLF_TYPE_MASK \ |
51 | (XFS_BLF_UDQUOT_BUF | \ | 52 | (XFS_BLF_UDQUOT_BUF | \ |
52 | XFS_BLF_PDQUOT_BUF | \ | 53 | XFS_BLF_PDQUOT_BUF | \ |
53 | XFS_BLF_GDQUOT_BUF | \ | 54 | XFS_BLF_GDQUOT_BUF | \ |
54 | XFS_BLF_BTREE_BUF | \ | 55 | XFS_BLF_BTREE_BUF | \ |
55 | XFS_BLF_AGF_BUF) | 56 | XFS_BLF_AGF_BUF | \ |
57 | XFS_BLF_AGFL_BUF) | ||
56 | 58 | ||
57 | #define XFS_BLF_CHUNK 128 | 59 | #define XFS_BLF_CHUNK 128 |
58 | #define XFS_BLF_SHIFT 7 | 60 | #define XFS_BLF_SHIFT 7 |
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index b6cca27e9132..a693a54799e5 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c | |||
@@ -268,6 +268,11 @@ xfs_growfs_data_private( | |||
268 | } | 268 | } |
269 | 269 | ||
270 | agfl = XFS_BUF_TO_AGFL(bp); | 270 | agfl = XFS_BUF_TO_AGFL(bp); |
271 | if (xfs_sb_version_hascrc(&mp->m_sb)) { | ||
272 | agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC); | ||
273 | agfl->agfl_seqno = cpu_to_be32(agno); | ||
274 | uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid); | ||
275 | } | ||
271 | for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++) | 276 | for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++) |
272 | agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK); | 277 | agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK); |
273 | 278 | ||
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index dadf24566d16..da2a19e0554e 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c | |||
@@ -1961,6 +1961,16 @@ xlog_recover_do_reg_buffer( | |||
1961 | } | 1961 | } |
1962 | bp->b_ops = &xfs_agf_buf_ops; | 1962 | bp->b_ops = &xfs_agf_buf_ops; |
1963 | break; | 1963 | break; |
1964 | case XFS_BLF_AGFL_BUF: | ||
1965 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
1966 | break; | ||
1967 | if (*(__be32 *)bp->b_addr != cpu_to_be32(XFS_AGFL_MAGIC)) { | ||
1968 | xfs_warn(mp, "Bad AGFL block magic!"); | ||
1969 | ASSERT(0); | ||
1970 | break; | ||
1971 | } | ||
1972 | bp->b_ops = &xfs_agfl_buf_ops; | ||
1973 | break; | ||
1964 | default: | 1974 | default: |
1965 | break; | 1975 | break; |
1966 | } | 1976 | } |