From: Darrick J. Wong <djwong@xxxxxxxxxx> Check our work to make sure we found all the dirents that the original directory had. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/scrub/dir_repair.c | 101 ++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/scrub/trace.h | 2 + 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index 25af002df1da..ec48b3268809 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -757,7 +757,10 @@ xrep_dir_scan_dirtree( return 0; } -/* Dump a dirent from the temporary dir. */ +/* + * Dump a dirent from the temporary dir and check it against the dir we're + * rebuilding. We are not committing any of this. + */ STATIC int xrep_dir_dump_tempdir( struct xfs_scrub *sc, @@ -768,7 +771,9 @@ xrep_dir_dump_tempdir( void *priv) { struct xrep_dir *rd = priv; + xfs_ino_t child_ino; bool child = true; + xfs_dir2_dataptr_t child_diroffset = XFS_DIR2_NULL_DATAPTR; int error; /* @@ -809,7 +814,88 @@ xrep_dir_dump_tempdir( mutex_lock(&rd->lock); error = xrep_dir_remove_dirent(rd, name, ino, dapos); mutex_unlock(&rd->lock); - return error; + if (error) + return error; + + /* Check that the dir being repaired has the same entry. */ + error = xchk_dir_lookup(sc, sc->ip, name, &child_ino, + &child_diroffset); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->ip, name, NULLFSINO, + XFS_DIR2_NULL_DATAPTR); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->ip, name, child_ino, + child_diroffset); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + + if (dapos != child_diroffset) { + trace_xrep_dir_badposname(sc->ip, name, child_ino, + child_diroffset); + /* We have no way to update this, so we just leave it. */ + } + + return 0; +} + +/* + * Dump a dirent from the dir we're rebuilding and check it against the + * temporary dir. This assumes that the directory wasn't really corrupt to + * begin with. + */ +STATIC int +xrep_dir_dump_baddir( + struct xfs_scrub *sc, + struct xfs_inode *dp, + xfs_dir2_dataptr_t dapos, + const struct xfs_name *name, + xfs_ino_t ino, + void *priv) +{ + xfs_ino_t child_ino; + xfs_dir2_dataptr_t child_diroffset = XFS_DIR2_NULL_DATAPTR; + int error; + + /* Ignore the directory's dot and dotdot entries. */ + if (xrep_dir_samename(name, &xfs_name_dotdot) || + xrep_dir_samename(name, &xfs_name_dot)) + return 0; + + trace_xrep_dir_dumpname(sc->ip, name, ino, dapos); + + /* Check that the tempdir has the same entry. */ + error = xchk_dir_lookup(sc, sc->tempip, name, &child_ino, + &child_diroffset); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->tempip, name, NULLFSINO, + XFS_DIR2_NULL_DATAPTR); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->tempip, name, child_ino, + child_diroffset); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + + if (dapos != child_diroffset) { + trace_xrep_dir_badposname(sc->ip, name, child_ino, + child_diroffset); + /* We have no way to update this, so we just leave it. */ + } + + return 0; } /* @@ -876,12 +962,21 @@ xrep_dir_rebuild_tree( trace_xrep_dir_rebuild_tree(sc->ip, rd->parent_ino); - xrep_tempfile_ilock(sc); + xchk_ilock(sc, XFS_ILOCK_EXCL); + error = xrep_tempfile_ilock_polled(sc); + if (error) + return error; + error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd); if (error) return error; + error = xchk_dir_walk(sc, sc->ip, xrep_dir_dump_baddir, rd); + if (error) + return error; + xrep_tempfile_iunlock(sc); + xchk_iunlock(sc, XFS_ILOCK_EXCL); xchk_trans_cancel(sc); return xrep_dir_replay_updates(rd); diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index cbf914bce6db..81d26be0ef3b 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1224,6 +1224,8 @@ DEFINE_XREP_DIRENT_CLASS(xrep_dir_createname); DEFINE_XREP_DIRENT_CLASS(xrep_dir_removename); DEFINE_XREP_DIRENT_CLASS(xrep_dir_replacename); DEFINE_XREP_DIRENT_CLASS(xrep_dir_dumpname); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_checkname); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_badposname); DECLARE_EVENT_CLASS(xrep_dir_class, TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino),