提交 6a577786 编写于 作者: D Darrick J. Wong

xfs: teach scrub to check for adjacent bmaps when rmap larger than bmap

When scrub is checking file fork mappings against rmap records and
the rmap record starts before or ends after the bmap record, check the
adjacent bmap records to make sure that they're adjacent to the one
we're checking.  This helps us to detect cases where the rmaps cover
territory that the bmaps do not.
Signed-off-by: NDarrick J. Wong <djwong@kernel.org>
Reviewed-by: NDave Chinner <dchinner@redhat.com>
上级 033985b6
...@@ -90,6 +90,7 @@ xchk_setup_inode_bmap( ...@@ -90,6 +90,7 @@ xchk_setup_inode_bmap(
struct xchk_bmap_info { struct xchk_bmap_info {
struct xfs_scrub *sc; struct xfs_scrub *sc;
struct xfs_iext_cursor icur;
xfs_fileoff_t lastoff; xfs_fileoff_t lastoff;
bool is_rt; bool is_rt;
bool is_shared; bool is_shared;
...@@ -146,6 +147,48 @@ xchk_bmap_get_rmap( ...@@ -146,6 +147,48 @@ xchk_bmap_get_rmap(
return has_rmap; return has_rmap;
} }
static inline bool
xchk_bmap_has_prev(
struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec)
{
struct xfs_bmbt_irec got;
struct xfs_ifork *ifp;
ifp = xfs_ifork_ptr(info->sc->ip, info->whichfork);
if (!xfs_iext_peek_prev_extent(ifp, &info->icur, &got))
return false;
if (got.br_startoff + got.br_blockcount != irec->br_startoff)
return false;
if (got.br_startblock + got.br_blockcount != irec->br_startblock)
return false;
if (got.br_state != irec->br_state)
return false;
return true;
}
static inline bool
xchk_bmap_has_next(
struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec)
{
struct xfs_bmbt_irec got;
struct xfs_ifork *ifp;
ifp = xfs_ifork_ptr(info->sc->ip, info->whichfork);
if (!xfs_iext_peek_next_extent(ifp, &info->icur, &got))
return false;
if (irec->br_startoff + irec->br_blockcount != got.br_startoff)
return false;
if (irec->br_startblock + irec->br_blockcount != got.br_startblock)
return false;
if (got.br_state != irec->br_state)
return false;
return true;
}
/* Make sure that we have rmapbt records for this extent. */ /* Make sure that we have rmapbt records for this extent. */
STATIC void STATIC void
xchk_bmap_xref_rmap( xchk_bmap_xref_rmap(
...@@ -214,6 +257,34 @@ xchk_bmap_xref_rmap( ...@@ -214,6 +257,34 @@ xchk_bmap_xref_rmap(
if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK) if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); irec->br_startoff);
/*
* If the rmap starts before this bmbt record, make sure there's a bmbt
* record for the previous offset that is contiguous with this mapping.
* Skip this for CoW fork extents because the refcount btree (and not
* the inode) is the ondisk owner for those extents.
*/
if (info->whichfork != XFS_COW_FORK && rmap.rm_startblock < agbno &&
!xchk_bmap_has_prev(info, irec)) {
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
return;
}
/*
* If the rmap ends after this bmbt record, make sure there's a bmbt
* record for the next offset that is contiguous with this mapping.
* Skip this for CoW fork extents because the refcount btree (and not
* the inode) is the ondisk owner for those extents.
*/
rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
if (info->whichfork != XFS_COW_FORK &&
rmap_end > agbno + irec->br_blockcount &&
!xchk_bmap_has_next(info, irec)) {
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
return;
}
} }
/* Cross-reference a single rtdev extent record. */ /* Cross-reference a single rtdev extent record. */
...@@ -626,7 +697,6 @@ xchk_bmap( ...@@ -626,7 +697,6 @@ xchk_bmap(
struct xfs_inode *ip = sc->ip; struct xfs_inode *ip = sc->ip;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
xfs_fileoff_t endoff; xfs_fileoff_t endoff;
struct xfs_iext_cursor icur;
int error = 0; int error = 0;
/* Non-existent forks can be ignored. */ /* Non-existent forks can be ignored. */
...@@ -690,7 +760,7 @@ xchk_bmap( ...@@ -690,7 +760,7 @@ xchk_bmap(
/* Scrub extent records. */ /* Scrub extent records. */
info.lastoff = 0; info.lastoff = 0;
ifp = xfs_ifork_ptr(ip, whichfork); ifp = xfs_ifork_ptr(ip, whichfork);
for_each_xfs_iext(ifp, &icur, &irec) { for_each_xfs_iext(ifp, &info.icur, &irec) {
if (xchk_should_terminate(sc, &error) || if (xchk_should_terminate(sc, &error) ||
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
goto out; goto out;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册