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 | 75 ++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/scrub/trace.h | 1 + 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index c484e6f36ca0..1e253feaa15d 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -746,7 +746,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, @@ -757,8 +760,10 @@ xrep_dir_dump_tempdir( void *priv) { struct xrep_dir *rd = priv; + xfs_ino_t child_ino; bool child_dirent = true; - int error = 0; + bool compare_dirent = true; + int error; /* * The tempdir was created with a dotdot entry pointing to the root @@ -781,11 +786,30 @@ xrep_dir_dump_tempdir( } if (xrep_dir_samename(name, &xfs_name_dot)) { child_dirent = false; + compare_dirent = false; ino = sc->ip->i_ino; } trace_xrep_dir_dumpname(sc->tempip, name, ino); + /* Check that the dir being repaired has the same entry. */ + if (compare_dirent) { + error = xchk_dir_lookup(sc, sc->ip, name, &child_ino); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->ip, name, NULLFSINO); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->ip, name, child_ino); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + } + /* * Set ourselves up to free every dirent in the tempdir because * directory inactivation won't do it for us. The rest of the online @@ -801,6 +825,49 @@ xrep_dir_dump_tempdir( return error; } +/* + * 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; + 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); + + /* Check that the tempdir has the same entry. */ + error = xchk_dir_lookup(sc, sc->tempip, name, &child_ino); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->tempip, name, NULLFSINO); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->tempip, name, child_ino); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + + return 0; +} + /* * "Commit" the new directory structure to the file that we're repairing. * @@ -886,6 +953,10 @@ xrep_dir_rebuild_tree( if (error) return error; + error = xchk_dir_walk(sc, sc->ip, xrep_dir_dump_baddir, rd); + if (error) + return error; + error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd); if (error) return error; diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index af5b5cd6d55b..d97a6bab9a4a 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1279,6 +1279,7 @@ 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); DECLARE_EVENT_CLASS(xrep_dir_class, TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino),