xfs: check the ondisk space mapping behind a dquot

Each xfs_dquot object caches the file offset and daddr of the ondisk
block that backs the dquot.  Make sure these cached values are the same
as the bmapi data, and that the block state is written.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong
2023-12-15 10:03:44 -08:00
parent ffd37b22bd
commit 7d1f0e167a

View File

@@ -6,6 +6,7 @@
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_bit.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
@@ -75,6 +76,47 @@ struct xchk_quota_info {
xfs_dqid_t last_id;
};
/* There's a written block backing this dquot, right? */
STATIC int
xchk_quota_item_bmap(
struct xfs_scrub *sc,
struct xfs_dquot *dq,
xfs_fileoff_t offset)
{
struct xfs_bmbt_irec irec;
struct xfs_mount *mp = sc->mp;
int nmaps = 1;
int error;
if (!xfs_verify_fileoff(mp, offset)) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
return 0;
}
if (dq->q_fileoffset != offset) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
return 0;
}
error = xfs_bmapi_read(sc->ip, offset, 1, &irec, &nmaps, 0);
if (error)
return error;
if (nmaps != 1) {
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
return 0;
}
if (!xfs_verify_fsbno(mp, irec.br_startblock))
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
if (XFS_FSB_TO_DADDR(mp, irec.br_startblock) != dq->q_blkno)
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
if (!xfs_bmap_is_written_extent(&irec))
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
return 0;
}
/* Scrub the fields in an individual quota item. */
STATIC int
xchk_quota_item(
@@ -93,6 +135,17 @@ xchk_quota_item(
if (xchk_should_terminate(sc, &error))
return error;
/*
* We want to validate the bmap record for the storage backing this
* dquot, so we need to lock the dquot and the quota file. For quota
* operations, the locking order is first the ILOCK and then the dquot.
* However, dqiterate gave us a locked dquot, so drop the dquot lock to
* get the ILOCK.
*/
xfs_dqunlock(dq);
xchk_ilock(sc, XFS_ILOCK_SHARED);
xfs_dqlock(dq);
/*
* Except for the root dquot, the actual dquot we got must either have
* the same or higher id as we saw before.
@@ -103,6 +156,11 @@ xchk_quota_item(
sqi->last_id = dq->q_id;
error = xchk_quota_item_bmap(sc, dq, offset);
xchk_iunlock(sc, XFS_ILOCK_SHARED);
if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error))
return error;
/*
* Warn if the hard limits are larger than the fs.
* Administrators can do this, though in production this seems