mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 07:03:03 +02:00
xfs: strengthen file mapping scrub
This series strengthens the file extent mapping scrubber in various ways, such as confirming that there are enough bmap records to match up with the rmap records for this file, checking delalloc reservations, checking for no unwritten extents in metadata files, invalid CoW fork formats, and weird things like shared CoW fork extents. Signed-off-by: Darrick J. Wong <djwong@kernel.org> -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEUzaAxoMeQq6m2jMV+H93GTRKtOsFAmN1fwsACgkQ+H93GTRK tOtCsQ/7BZO0D8QyAJH3DBBNoK90EpM19YjB7LbD6ExHLMTCzR2bitrR0W0NPey1 kS74/owG6cmCvqIem5bLIQB8hhnQAnhswEVEW4HmhmKq0QBZFdGhJK+C/pgpL072 SgKzNzU86CB0+StVJTGRd5jgWWzLSajEPbRXoNS1nat63F6aqx44zI8Gehn5qjhD S2ldj7FUIWK0qV2dfb0szP8zzD8H7GvE1msXjEuFRWdVqFMHbKaTC+sfoYIE0cMK WllJ8RVXX4BqgWmeJSuidy/ryWhOXGsW0/UDXxe8yLlw/eIYSP2hm/J/KPBf+Zd8 2ntJnnK19ggjM0TusmkXjupjrXR8zMP3gh3lcYJ3a37HM991aIZ18ffMNXj7y4+n 1T2CerePVWaUQ0K2L/tErBAeY9dolM0pux1dwEdvU9LdYTD1SgAmnOJ3HhVYiXbG vIZ/e7F6VrccUKXHxmokj21PZldxJ5GpJgPMIsVOuMilh8Thnc+C/rD8XALytHww fydvqTSKuIgKw7VJNnXhLgZK2JQBr6+jHhsC9nUay394tooNdoyZDwLeYJcM0Tnt 8d8/PalGOdGJHZH212LqeyQ44xIjAxjx4CuE4XLkuTEz7FPDqiXpzFpK+wYa3Ocn c75ngS5WAqslbna2U/UtV/vol2np2gA+QYhELpCl5r2EPsrPyes= =ZRqi -----END PGP SIGNATURE----- Merge tag 'scrub-bmap-enhancements-6.2_2022-11-16' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.2-mergeA xfs: strengthen file mapping scrub This series strengthens the file extent mapping scrubber in various ways, such as confirming that there are enough bmap records to match up with the rmap records for this file, checking delalloc reservations, checking for no unwritten extents in metadata files, invalid CoW fork formats, and weird things like shared CoW fork extents. Signed-off-by: Darrick J. Wong <djwong@kernel.org> * tag 'scrub-bmap-enhancements-6.2_2022-11-16' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: teach scrub to flag non-extents format cow forks xfs: check that CoW fork extents are not shared xfs: check quota files for unwritten extents xfs: block map scrub should handle incore delalloc reservations xfs: teach scrub to check for adjacent bmaps when rmap larger than bmap xfs: fix perag loop in xchk_bmap_check_rmaps
This commit is contained in:
commit
cc5f38fa12
|
|
@ -90,6 +90,7 @@ xchk_setup_inode_bmap(
|
|||
|
||||
struct xchk_bmap_info {
|
||||
struct xfs_scrub *sc;
|
||||
struct xfs_iext_cursor icur;
|
||||
xfs_fileoff_t lastoff;
|
||||
bool is_rt;
|
||||
bool is_shared;
|
||||
|
|
@ -146,6 +147,48 @@ xchk_bmap_get_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. */
|
||||
STATIC void
|
||||
xchk_bmap_xref_rmap(
|
||||
|
|
@ -214,6 +257,34 @@ xchk_bmap_xref_rmap(
|
|||
if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
|
||||
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
|
||||
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. */
|
||||
|
|
@ -264,6 +335,8 @@ xchk_bmap_iextent_xref(
|
|||
case XFS_COW_FORK:
|
||||
xchk_xref_is_cow_staging(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
xchk_xref_is_not_shared(info->sc, agbno,
|
||||
irec->br_blockcount);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -297,14 +370,13 @@ xchk_bmap_dirattr_extent(
|
|||
}
|
||||
|
||||
/* Scrub a single extent record. */
|
||||
STATIC int
|
||||
STATIC void
|
||||
xchk_bmap_iextent(
|
||||
struct xfs_inode *ip,
|
||||
struct xchk_bmap_info *info,
|
||||
struct xfs_bmbt_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = info->sc->mp;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
* Check for out-of-order extents. This record could have come
|
||||
|
|
@ -325,14 +397,6 @@ xchk_bmap_iextent(
|
|||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
irec->br_startoff);
|
||||
|
||||
/*
|
||||
* Check for delalloc extents. We never iterate the ones in the
|
||||
* in-core extent scan, and we should never see these in the bmbt.
|
||||
*/
|
||||
if (isnullstartblock(irec->br_startblock))
|
||||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
irec->br_startoff);
|
||||
|
||||
/* Make sure the extent points to a valid place. */
|
||||
if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN)
|
||||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
|
|
@ -353,15 +417,12 @@ xchk_bmap_iextent(
|
|||
irec->br_startoff);
|
||||
|
||||
if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (info->is_rt)
|
||||
xchk_bmap_rt_iextent_xref(ip, info, irec);
|
||||
else
|
||||
xchk_bmap_iextent_xref(ip, info, irec);
|
||||
|
||||
info->lastoff = irec->br_startoff + irec->br_blockcount;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Scrub a bmbt record. */
|
||||
|
|
@ -599,14 +660,41 @@ xchk_bmap_check_rmaps(
|
|||
|
||||
for_each_perag(sc->mp, agno, pag) {
|
||||
error = xchk_bmap_check_ag_rmaps(sc, whichfork, pag);
|
||||
if (error)
|
||||
break;
|
||||
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
||||
break;
|
||||
if (error ||
|
||||
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
|
||||
xfs_perag_put(pag);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
if (pag)
|
||||
xfs_perag_put(pag);
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Scrub a delalloc reservation from the incore extent map tree. */
|
||||
STATIC void
|
||||
xchk_bmap_iextent_delalloc(
|
||||
struct xfs_inode *ip,
|
||||
struct xchk_bmap_info *info,
|
||||
struct xfs_bmbt_irec *irec)
|
||||
{
|
||||
struct xfs_mount *mp = info->sc->mp;
|
||||
|
||||
/*
|
||||
* Check for out-of-order extents. This record could have come
|
||||
* from the incore list, for which there is no ordering check.
|
||||
*/
|
||||
if (irec->br_startoff < info->lastoff)
|
||||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
irec->br_startoff);
|
||||
|
||||
if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
|
||||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
irec->br_startoff);
|
||||
|
||||
/* Make sure the extent points to a valid place. */
|
||||
if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN)
|
||||
xchk_fblock_set_corrupt(info->sc, info->whichfork,
|
||||
irec->br_startoff);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -626,7 +714,6 @@ xchk_bmap(
|
|||
struct xfs_inode *ip = sc->ip;
|
||||
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
|
||||
xfs_fileoff_t endoff;
|
||||
struct xfs_iext_cursor icur;
|
||||
int error = 0;
|
||||
|
||||
/* Non-existent forks can be ignored. */
|
||||
|
|
@ -661,6 +748,8 @@ xchk_bmap(
|
|||
case XFS_DINODE_FMT_DEV:
|
||||
case XFS_DINODE_FMT_LOCAL:
|
||||
/* No mappings to check. */
|
||||
if (whichfork == XFS_COW_FORK)
|
||||
xchk_fblock_set_corrupt(sc, whichfork, 0);
|
||||
goto out;
|
||||
case XFS_DINODE_FMT_EXTENTS:
|
||||
break;
|
||||
|
|
@ -690,20 +779,22 @@ xchk_bmap(
|
|||
/* Scrub extent records. */
|
||||
info.lastoff = 0;
|
||||
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) ||
|
||||
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
|
||||
goto out;
|
||||
if (isnullstartblock(irec.br_startblock))
|
||||
continue;
|
||||
|
||||
if (irec.br_startoff >= endoff) {
|
||||
xchk_fblock_set_corrupt(sc, whichfork,
|
||||
irec.br_startoff);
|
||||
goto out;
|
||||
}
|
||||
error = xchk_bmap_iextent(ip, &info, &irec);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (isnullstartblock(irec.br_startblock))
|
||||
xchk_bmap_iextent_delalloc(ip, &info, &irec);
|
||||
else
|
||||
xchk_bmap_iextent(ip, &info, &irec);
|
||||
info.lastoff = irec.br_startoff + irec.br_blockcount;
|
||||
}
|
||||
|
||||
error = xchk_bmap_check_rmaps(sc, whichfork);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "xfs_inode.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
|
||||
|
|
@ -189,11 +190,12 @@ xchk_quota_data_fork(
|
|||
for_each_xfs_iext(ifp, &icur, &irec) {
|
||||
if (xchk_should_terminate(sc, &error))
|
||||
break;
|
||||
|
||||
/*
|
||||
* delalloc extents or blocks mapped above the highest
|
||||
* delalloc/unwritten extents or blocks mapped above the highest
|
||||
* quota id shouldn't happen.
|
||||
*/
|
||||
if (isnullstartblock(irec.br_startblock) ||
|
||||
if (!xfs_bmap_is_written_extent(&irec) ||
|
||||
irec.br_startoff > max_dqid_off ||
|
||||
irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
|
||||
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user