diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2018-01-16 21:53:11 -0500 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-01-18 00:00:46 -0500 |
commit | cf1b0b8b1a43102cdc0189d76d1c05915c4e16a6 (patch) | |
tree | 779947ce4c0e554ae0ac2da2e31295df89e977fe | |
parent | 561f648ab2bdbb43f2ecc5074854c11537f2aa6c (diff) |
xfs: scrub in-core metadata
Whenever we load a buffer, explicitly re-call the structure verifier to
ensure that memory isn't corrupting things.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
-rw-r--r-- | fs/xfs/scrub/agheader.c | 3 | ||||
-rw-r--r-- | fs/xfs/scrub/btree.c | 4 | ||||
-rw-r--r-- | fs/xfs/scrub/common.c | 23 | ||||
-rw-r--r-- | fs/xfs/scrub/common.h | 1 | ||||
-rw-r--r-- | fs/xfs/scrub/dabtree.c | 22 | ||||
-rw-r--r-- | fs/xfs/scrub/dir.c | 4 |
6 files changed, 57 insertions, 0 deletions
diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 20a3bebdee06..fd975524f460 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c | |||
@@ -611,6 +611,7 @@ xfs_scrub_agf( | |||
611 | &sc->sa.agf_bp, &sc->sa.agfl_bp); | 611 | &sc->sa.agf_bp, &sc->sa.agfl_bp); |
612 | if (!xfs_scrub_process_error(sc, agno, XFS_AGF_BLOCK(sc->mp), &error)) | 612 | if (!xfs_scrub_process_error(sc, agno, XFS_AGF_BLOCK(sc->mp), &error)) |
613 | goto out; | 613 | goto out; |
614 | xfs_scrub_buffer_recheck(sc, sc->sa.agf_bp); | ||
614 | 615 | ||
615 | agf = XFS_BUF_TO_AGF(sc->sa.agf_bp); | 616 | agf = XFS_BUF_TO_AGF(sc->sa.agf_bp); |
616 | 617 | ||
@@ -780,6 +781,7 @@ xfs_scrub_agfl( | |||
780 | goto out; | 781 | goto out; |
781 | if (!sc->sa.agf_bp) | 782 | if (!sc->sa.agf_bp) |
782 | return -EFSCORRUPTED; | 783 | return -EFSCORRUPTED; |
784 | xfs_scrub_buffer_recheck(sc, sc->sa.agfl_bp); | ||
783 | 785 | ||
784 | xfs_scrub_agfl_xref(sc); | 786 | xfs_scrub_agfl_xref(sc); |
785 | 787 | ||
@@ -902,6 +904,7 @@ xfs_scrub_agi( | |||
902 | &sc->sa.agf_bp, &sc->sa.agfl_bp); | 904 | &sc->sa.agf_bp, &sc->sa.agfl_bp); |
903 | if (!xfs_scrub_process_error(sc, agno, XFS_AGI_BLOCK(sc->mp), &error)) | 905 | if (!xfs_scrub_process_error(sc, agno, XFS_AGI_BLOCK(sc->mp), &error)) |
904 | goto out; | 906 | goto out; |
907 | xfs_scrub_buffer_recheck(sc, sc->sa.agi_bp); | ||
905 | 908 | ||
906 | agi = XFS_BUF_TO_AGI(sc->sa.agi_bp); | 909 | agi = XFS_BUF_TO_AGI(sc->sa.agi_bp); |
907 | 910 | ||
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c index 0589d4efbf6b..54218168c8f9 100644 --- a/fs/xfs/scrub/btree.c +++ b/fs/xfs/scrub/btree.c | |||
@@ -314,6 +314,8 @@ xfs_scrub_btree_block_check_sibling( | |||
314 | pp = xfs_btree_ptr_addr(ncur, ncur->bc_ptrs[level + 1], pblock); | 314 | pp = xfs_btree_ptr_addr(ncur, ncur->bc_ptrs[level + 1], pblock); |
315 | if (!xfs_scrub_btree_ptr_ok(bs, level + 1, pp)) | 315 | if (!xfs_scrub_btree_ptr_ok(bs, level + 1, pp)) |
316 | goto out; | 316 | goto out; |
317 | if (pbp) | ||
318 | xfs_scrub_buffer_recheck(bs->sc, pbp); | ||
317 | 319 | ||
318 | if (xfs_btree_diff_two_ptrs(cur, pp, sibling)) | 320 | if (xfs_btree_diff_two_ptrs(cur, pp, sibling)) |
319 | xfs_scrub_btree_set_corrupt(bs->sc, cur, level); | 321 | xfs_scrub_btree_set_corrupt(bs->sc, cur, level); |
@@ -486,6 +488,8 @@ xfs_scrub_btree_get_block( | |||
486 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, level); | 488 | xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, level); |
487 | return 0; | 489 | return 0; |
488 | } | 490 | } |
491 | if (*pbp) | ||
492 | xfs_scrub_buffer_recheck(bs->sc, *pbp); | ||
489 | 493 | ||
490 | /* | 494 | /* |
491 | * Check the block's owner; this function absorbs error codes | 495 | * Check the block's owner; this function absorbs error codes |
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index f5df8f2859d7..8033ab9d8f47 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c | |||
@@ -756,3 +756,26 @@ xfs_scrub_should_check_xref( | |||
756 | *error = 0; | 756 | *error = 0; |
757 | return false; | 757 | return false; |
758 | } | 758 | } |
759 | |||
760 | /* Run the structure verifiers on in-memory buffers to detect bad memory. */ | ||
761 | void | ||
762 | xfs_scrub_buffer_recheck( | ||
763 | struct xfs_scrub_context *sc, | ||
764 | struct xfs_buf *bp) | ||
765 | { | ||
766 | xfs_failaddr_t fa; | ||
767 | |||
768 | if (bp->b_ops == NULL) { | ||
769 | xfs_scrub_block_set_corrupt(sc, bp); | ||
770 | return; | ||
771 | } | ||
772 | if (bp->b_ops->verify_struct == NULL) { | ||
773 | xfs_scrub_set_incomplete(sc); | ||
774 | return; | ||
775 | } | ||
776 | fa = bp->b_ops->verify_struct(bp); | ||
777 | if (!fa) | ||
778 | return; | ||
779 | sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; | ||
780 | trace_xfs_scrub_block_error(sc, bp->b_bn, fa); | ||
781 | } | ||
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index bf88a677f6e7..ddb65d22c76a 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h | |||
@@ -158,5 +158,6 @@ int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc, | |||
158 | int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in); | 158 | int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in); |
159 | int xfs_scrub_setup_inode_contents(struct xfs_scrub_context *sc, | 159 | int xfs_scrub_setup_inode_contents(struct xfs_scrub_context *sc, |
160 | struct xfs_inode *ip, unsigned int resblks); | 160 | struct xfs_inode *ip, unsigned int resblks); |
161 | void xfs_scrub_buffer_recheck(struct xfs_scrub_context *sc, struct xfs_buf *bp); | ||
161 | 162 | ||
162 | #endif /* __XFS_SCRUB_COMMON_H__ */ | 163 | #endif /* __XFS_SCRUB_COMMON_H__ */ |
diff --git a/fs/xfs/scrub/dabtree.c b/fs/xfs/scrub/dabtree.c index d94edd93cba8..bffdb7dc09bf 100644 --- a/fs/xfs/scrub/dabtree.c +++ b/fs/xfs/scrub/dabtree.c | |||
@@ -233,11 +233,28 @@ xfs_scrub_da_btree_write_verify( | |||
233 | return; | 233 | return; |
234 | } | 234 | } |
235 | } | 235 | } |
236 | static void * | ||
237 | xfs_scrub_da_btree_verify( | ||
238 | struct xfs_buf *bp) | ||
239 | { | ||
240 | struct xfs_da_blkinfo *info = bp->b_addr; | ||
241 | |||
242 | switch (be16_to_cpu(info->magic)) { | ||
243 | case XFS_DIR2_LEAF1_MAGIC: | ||
244 | case XFS_DIR3_LEAF1_MAGIC: | ||
245 | bp->b_ops = &xfs_dir3_leaf1_buf_ops; | ||
246 | return bp->b_ops->verify_struct(bp); | ||
247 | default: | ||
248 | bp->b_ops = &xfs_da3_node_buf_ops; | ||
249 | return bp->b_ops->verify_struct(bp); | ||
250 | } | ||
251 | } | ||
236 | 252 | ||
237 | static const struct xfs_buf_ops xfs_scrub_da_btree_buf_ops = { | 253 | static const struct xfs_buf_ops xfs_scrub_da_btree_buf_ops = { |
238 | .name = "xfs_scrub_da_btree", | 254 | .name = "xfs_scrub_da_btree", |
239 | .verify_read = xfs_scrub_da_btree_read_verify, | 255 | .verify_read = xfs_scrub_da_btree_read_verify, |
240 | .verify_write = xfs_scrub_da_btree_write_verify, | 256 | .verify_write = xfs_scrub_da_btree_write_verify, |
257 | .verify_struct = xfs_scrub_da_btree_verify, | ||
241 | }; | 258 | }; |
242 | 259 | ||
243 | /* Check a block's sibling. */ | 260 | /* Check a block's sibling. */ |
@@ -276,6 +293,9 @@ xfs_scrub_da_btree_block_check_sibling( | |||
276 | xfs_scrub_da_set_corrupt(ds, level); | 293 | xfs_scrub_da_set_corrupt(ds, level); |
277 | return error; | 294 | return error; |
278 | } | 295 | } |
296 | if (ds->state->altpath.blk[level].bp) | ||
297 | xfs_scrub_buffer_recheck(ds->sc, | ||
298 | ds->state->altpath.blk[level].bp); | ||
279 | 299 | ||
280 | /* Compare upper level pointer to sibling pointer. */ | 300 | /* Compare upper level pointer to sibling pointer. */ |
281 | if (ds->state->altpath.blk[level].blkno != sibling) | 301 | if (ds->state->altpath.blk[level].blkno != sibling) |
@@ -358,6 +378,8 @@ xfs_scrub_da_btree_block( | |||
358 | &xfs_scrub_da_btree_buf_ops); | 378 | &xfs_scrub_da_btree_buf_ops); |
359 | if (!xfs_scrub_da_process_error(ds, level, &error)) | 379 | if (!xfs_scrub_da_process_error(ds, level, &error)) |
360 | goto out_nobuf; | 380 | goto out_nobuf; |
381 | if (blk->bp) | ||
382 | xfs_scrub_buffer_recheck(ds->sc, blk->bp); | ||
361 | 383 | ||
362 | /* | 384 | /* |
363 | * We didn't find a dir btree root block, which means that | 385 | * We didn't find a dir btree root block, which means that |
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index e75826bb6516..f5a0d179eac0 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c | |||
@@ -237,6 +237,7 @@ xfs_scrub_dir_rec( | |||
237 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); | 237 | xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
238 | goto out; | 238 | goto out; |
239 | } | 239 | } |
240 | xfs_scrub_buffer_recheck(ds->sc, bp); | ||
240 | 241 | ||
241 | /* Retrieve the entry, sanity check it, and compare hashes. */ | 242 | /* Retrieve the entry, sanity check it, and compare hashes. */ |
242 | dent = (struct xfs_dir2_data_entry *)(((char *)bp->b_addr) + off); | 243 | dent = (struct xfs_dir2_data_entry *)(((char *)bp->b_addr) + off); |
@@ -324,6 +325,7 @@ xfs_scrub_directory_data_bestfree( | |||
324 | } | 325 | } |
325 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | 326 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
326 | goto out; | 327 | goto out; |
328 | xfs_scrub_buffer_recheck(sc, bp); | ||
327 | 329 | ||
328 | /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ | 330 | /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ |
329 | 331 | ||
@@ -474,6 +476,7 @@ xfs_scrub_directory_leaf1_bestfree( | |||
474 | error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp); | 476 | error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp); |
475 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | 477 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
476 | goto out; | 478 | goto out; |
479 | xfs_scrub_buffer_recheck(sc, bp); | ||
477 | 480 | ||
478 | leaf = bp->b_addr; | 481 | leaf = bp->b_addr; |
479 | d_ops->leaf_hdr_from_disk(&leafhdr, leaf); | 482 | d_ops->leaf_hdr_from_disk(&leafhdr, leaf); |
@@ -559,6 +562,7 @@ xfs_scrub_directory_free_bestfree( | |||
559 | error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp); | 562 | error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp); |
560 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) | 563 | if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
561 | goto out; | 564 | goto out; |
565 | xfs_scrub_buffer_recheck(sc, bp); | ||
562 | 566 | ||
563 | if (xfs_sb_version_hascrc(&sc->mp->m_sb)) { | 567 | if (xfs_sb_version_hascrc(&sc->mp->m_sb)) { |
564 | struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; | 568 | struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; |