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

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
...@@ -733,6 +733,8 @@ xchk_iget( ...@@ -733,6 +733,8 @@ xchk_iget(
xfs_ino_t inum, xfs_ino_t inum,
struct xfs_inode **ipp) struct xfs_inode **ipp)
{ {
ASSERT(sc->tp != NULL);
return xfs_iget(sc->mp, sc->tp, inum, XFS_IGET_UNTRUSTED, 0, ipp); return xfs_iget(sc->mp, sc->tp, inum, XFS_IGET_UNTRUSTED, 0, ipp);
} }
...@@ -882,8 +884,8 @@ xchk_iget_for_scrubbing( ...@@ -882,8 +884,8 @@ xchk_iget_for_scrubbing(
if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino)) if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
return -ENOENT; return -ENOENT;
/* Try a regular untrusted iget. */ /* Try a safe untrusted iget. */
error = xchk_iget(sc, sc->sm->sm_ino, &ip); error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
if (!error) if (!error)
return xchk_install_handle_inode(sc, ip); return xchk_install_handle_inode(sc, ip);
if (error == -ENOENT) if (error == -ENOENT)
......
...@@ -151,12 +151,37 @@ void xchk_iunlock(struct xfs_scrub *sc, unsigned int ilock_flags); ...@@ -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); 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(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp);
int xchk_iget_agi(struct xfs_scrub *sc, xfs_ino_t inum, int xchk_iget_agi(struct xfs_scrub *sc, xfs_ino_t inum,
struct xfs_buf **agi_bpp, struct xfs_inode **ipp); struct xfs_buf **agi_bpp, struct xfs_inode **ipp);
void xchk_irele(struct xfs_scrub *sc, struct xfs_inode *ip); void xchk_irele(struct xfs_scrub *sc, struct xfs_inode *ip);
int xchk_install_handle_inode(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 * Don't bother cross-referencing if we already found corruption or cross
* referencing discrepancies. * referencing discrepancies.
......
...@@ -95,8 +95,8 @@ xchk_setup_inode( ...@@ -95,8 +95,8 @@ xchk_setup_inode(
if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino)) if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
return -ENOENT; return -ENOENT;
/* Try a regular untrusted iget. */ /* Try a safe untrusted iget. */
error = xchk_iget(sc, sc->sm->sm_ino, &ip); error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
if (!error) if (!error)
return xchk_install_handle_iscrub(sc, ip); return xchk_install_handle_iscrub(sc, ip);
if (error == -ENOENT) if (error == -ENOENT)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment