@@ -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);
@@ -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),