summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/dir_repair.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/scrub/dir_repair.c')
-rw-r--r--fs/xfs/scrub/dir_repair.c28
1 files changed, 24 insertions, 4 deletions
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
index 00922089a5e5..8f069636ae1a 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -1090,6 +1090,7 @@ xrep_dir(
.parent_ino = NULLFSINO,
.new_nlink = 2,
};
+ bool move_orphanage = false;
int error;
/* Set up some storage */
@@ -1147,17 +1148,36 @@ xrep_dir(
/*
* Validate the parent pointer that we observed while salvaging the
* directory or scan the filesystem to find one. If the scan fails
- * to find a single parent, we'll set the parent to the root dir and
- * let the parent pointer repair fix it.
+ * to find a single parent, we'll move the directory to the orphanage.
*/
error = xrep_findparent(rd.sc, &rd.parent_ino);
if (error)
return error;
- if (rd.parent_ino == NULLFSINO)
+ if (rd.parent_ino == NULLFSINO) {
rd.parent_ino = rd.sc->mp->m_sb.sb_rootino;
+ move_orphanage = true;
+ }
/* Now rebuild the directory information. */
- return xrep_dir_rebuild_tree(&rd);
+ error = xrep_dir_rebuild_tree(&rd);
+ if (error || !move_orphanage)
+ return error;
+
+ /*
+ * Before we can move the directory to the orphanage, we must roll to a
+ * clean unjoined transaction and drop the ILOCKs on the dir and the
+ * temp dir. We still hold IOLOCK_EXCL on the dir, so nobody will be
+ * able to access it in the mean time.
+ */
+ error = xfs_trans_roll(&sc->tp);
+ if (error)
+ return error;
+ xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
+ sc->temp_ilock_flags &= ~XFS_ILOCK_EXCL;
+ xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
+ sc->ilock_flags &= ~XFS_ILOCK_EXCL;
+
+ return xrep_move_to_orphanage(sc);
out_names:
xblob_destroy(rd.dir_names);