Commit 3f113c27 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: make xchk_iget safer in the presence of corrupt inode btrees



When scrub is trying to iget an inode, ensure that it won't end up
deadlocked on a cycle in the inode btree by using an empty transaction
to store all the buffers.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 9c07bca7
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -733,6 +733,8 @@ xchk_iget(
	xfs_ino_t		inum,
	struct xfs_inode	**ipp)
{
	ASSERT(sc->tp != NULL);

	return xfs_iget(sc->mp, sc->tp, inum, XFS_IGET_UNTRUSTED, 0, ipp);
}

@@ -882,8 +884,8 @@ xchk_iget_for_scrubbing(
	if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
		return -ENOENT;

	/* Try a regular untrusted iget. */
	error = xchk_iget(sc, sc->sm->sm_ino, &ip);
	/* Try a safe untrusted iget. */
	error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
	if (!error)
		return xchk_install_handle_inode(sc, ip);
	if (error == -ENOENT)
+25 −0
Original line number Diff line number Diff line
@@ -151,12 +151,37 @@ void xchk_iunlock(struct xfs_scrub *sc, unsigned int ilock_flags);

void xchk_buffer_recheck(struct xfs_scrub *sc, struct xfs_buf *bp);

/*
 * Grab the inode at @inum.  The caller must have created a scrub transaction
 * so that we can confirm the inumber by walking the inobt and not deadlock on
 * a loop in the inobt.
 */
int xchk_iget(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp);
int xchk_iget_agi(struct xfs_scrub *sc, xfs_ino_t inum,
		struct xfs_buf **agi_bpp, struct xfs_inode **ipp);
void xchk_irele(struct xfs_scrub *sc, struct xfs_inode *ip);
int xchk_install_handle_inode(struct xfs_scrub *sc, struct xfs_inode *ip);

/*
 * Safe version of (untrusted) xchk_iget that uses an empty transaction to
 * avoid deadlocking on loops in the inobt.  This should only be used in a
 * scrub or repair setup routine, and only prior to grabbing a transaction.
 */
static inline int
xchk_iget_safe(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp)
{
	int	error;

	ASSERT(sc->tp == NULL);

	error = xchk_trans_alloc(sc, 0);
	if (error)
		return error;
	error = xchk_iget(sc, inum, ipp);
	xchk_trans_cancel(sc);
	return error;
}

/*
 * Don't bother cross-referencing if we already found corruption or cross
 * referencing discrepancies.
+2 −2
Original line number Diff line number Diff line
@@ -95,8 +95,8 @@ xchk_setup_inode(
	if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
		return -ENOENT;

	/* Try a regular untrusted iget. */
	error = xchk_iget(sc, sc->sm->sm_ino, &ip);
	/* Try a safe untrusted iget. */
	error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
	if (!error)
		return xchk_install_handle_iscrub(sc, ip);
	if (error == -ENOENT)