Commit 7ac14fa2 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

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: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 7ad9ea63
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -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. */
+8 −3
Original line number Diff line number Diff line
@@ -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))
		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;
		fallthrough;
	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,
+1 −1
Original line number Diff line number Diff line
@@ -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;
}

+1 −0
Original line number Diff line number Diff line
@@ -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:
Loading