mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
xfs: ensure that all metadata and data blocks are not cow staging extents
Make sure that all filesystem metadata blocks and file data blocks are not also marked as CoW staging extents. The extra checking added here was inspired by an actual VM host filesystem corruption incident due to bugs in the CoW handling of 4.x kernels. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
parent
7ad9ea6398
commit
7ac14fa2bd
7 changed files with 39 additions and 4 deletions
|
@ -53,6 +53,7 @@ xchk_superblock_xref(
|
|||
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
|
||||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
|
||||
/* scrub teardown will take care of sc->sa for us */
|
||||
}
|
||||
|
@ -517,6 +518,7 @@ xchk_agf_xref(
|
|||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
|
||||
xchk_agf_xref_btreeblks(sc);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
xchk_agf_xref_refcblks(sc);
|
||||
|
||||
/* scrub teardown will take care of sc->sa for us */
|
||||
|
@ -644,6 +646,7 @@ xchk_agfl_block_xref(
|
|||
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
|
||||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_AG);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
}
|
||||
|
||||
/* Scrub an AGFL block. */
|
||||
|
@ -700,6 +703,7 @@ xchk_agfl_xref(
|
|||
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
|
||||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
|
||||
/*
|
||||
* Scrub teardown will take care of sc->sa for us. Leave sc->sa
|
||||
|
@ -855,6 +859,7 @@ xchk_agi_xref(
|
|||
xchk_agi_xref_icounts(sc);
|
||||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
xchk_agi_xref_fiblocks(sc);
|
||||
|
||||
/* scrub teardown will take care of sc->sa for us */
|
||||
|
|
|
@ -90,6 +90,7 @@ xchk_allocbt_xref(
|
|||
xchk_xref_is_not_inode_chunk(sc, agbno, len);
|
||||
xchk_xref_has_no_owner(sc, agbno, len);
|
||||
xchk_xref_is_not_shared(sc, agbno, len);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, len);
|
||||
}
|
||||
|
||||
/* Scrub a bnobt/cntbt record. */
|
||||
|
|
|
@ -328,12 +328,17 @@ xchk_bmap_iextent_xref(
|
|||
xchk_bmap_xref_rmap(info, irec, agbno);
|
||||
switch (info->whichfork) {
|
||||
case XFS_DATA_FORK:
|
||||
if (xfs_is_reflink_inode(info->sc->ip))
|
||||
break;
|
||||
fallthrough;
|
||||
if (!xfs_is_reflink_inode(info->sc->ip))
|
||||
xchk_xref_is_not_shared(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
xchk_xref_is_not_cow_staging(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
break;
|
||||
case XFS_ATTR_FORK:
|
||||
xchk_xref_is_not_shared(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
xchk_xref_is_not_cow_staging(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
break;
|
||||
case XFS_COW_FORK:
|
||||
xchk_xref_is_cow_staging(info->sc, agbno,
|
||||
|
|
|
@ -115,7 +115,7 @@ xchk_iallocbt_chunk(
|
|||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
|
||||
xchk_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len);
|
||||
|
||||
xchk_xref_is_not_cow_staging(bs->sc, bno, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -558,6 +558,7 @@ xchk_inode_xref(
|
|||
xchk_inode_xref_finobt(sc, ino);
|
||||
xchk_xref_is_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_INODES);
|
||||
xchk_xref_is_not_shared(sc, agbno, 1);
|
||||
xchk_xref_is_not_cow_staging(sc, agbno, 1);
|
||||
xchk_inode_xref_bmap(sc, dip);
|
||||
|
||||
out_free:
|
||||
|
|
|
@ -555,3 +555,24 @@ xchk_xref_is_not_shared(
|
|||
if (outcome != XBTREE_RECPACKING_EMPTY)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
}
|
||||
|
||||
/* xref check that the extent is not being used for CoW staging. */
|
||||
void
|
||||
xchk_xref_is_not_cow_staging(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_agblock_t agbno,
|
||||
xfs_extlen_t len)
|
||||
{
|
||||
enum xbtree_recpacking outcome;
|
||||
int error;
|
||||
|
||||
if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
|
||||
return;
|
||||
|
||||
error = xfs_refcount_has_records(sc->sa.refc_cur, XFS_REFC_DOMAIN_COW,
|
||||
agbno, len, &outcome);
|
||||
if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
|
||||
return;
|
||||
if (outcome != XBTREE_RECPACKING_EMPTY)
|
||||
xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
|
||||
}
|
||||
|
|
|
@ -172,6 +172,8 @@ void xchk_xref_is_cow_staging(struct xfs_scrub *sc, xfs_agblock_t bno,
|
|||
xfs_extlen_t len);
|
||||
void xchk_xref_is_not_shared(struct xfs_scrub *sc, xfs_agblock_t bno,
|
||||
xfs_extlen_t len);
|
||||
void xchk_xref_is_not_cow_staging(struct xfs_scrub *sc, xfs_agblock_t bno,
|
||||
xfs_extlen_t len);
|
||||
#ifdef CONFIG_XFS_RT
|
||||
void xchk_xref_is_used_rt_space(struct xfs_scrub *sc, xfs_rtblock_t rtbno,
|
||||
xfs_extlen_t len);
|
||||
|
|
Loading…
Add table
Reference in a new issue