summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:15:39 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-09-17 18:55:07 -0700
commitef45eb63b2906de79321672ac456a3ad883d5bfb (patch)
treee7a44d60ca13ff5fc270ee5483816173d8e718e2
parent2778bc0ecc1cfce0f54b83d38e87f33bcc0942ee (diff)
xfs: scrub metadata directories
Teach online scrub about the metadata directory tree. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/scrub/dir.c9
-rw-r--r--fs/xfs/scrub/dir_repair.c6
-rw-r--r--fs/xfs/scrub/parent.c18
-rw-r--r--fs/xfs/scrub/parent_repair.c21
4 files changed, 52 insertions, 2 deletions
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 3811f21b7697..acca10a72fc1 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -134,6 +134,15 @@ xchk_dir_check_ftype(
xfs_mode_to_ftype(VFS_I(ip)->i_mode));
if (ino_dtype != dtype)
xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, offset);
+
+ /*
+ * Metadata and regular inodes cannot cross trees. This property
+ * cannot change without a full inode free and realloc cycle, so it's
+ * safe to check this without holding locks.
+ */
+ if (xfs_is_metadata_inode(ip) ^ xfs_is_metadata_inode(sdc->sc->ip))
+ xchk_fblock_set_corrupt(sdc->sc, XFS_DATA_FORK, 0);
+
xfs_irele(ip);
out:
return error;
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
index 8f069636ae1a..91d812dbdb1d 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -180,6 +180,12 @@ xrep_dir_salvage_entry(
if (error)
return 0;
+ /* Don't mix metadata and regular directory trees. */
+ if (xfs_is_metadata_inode(ip) ^ xfs_is_metadata_inode(rd->sc->ip)) {
+ xfs_irele(ip);
+ return 0;
+ }
+
key.ftype = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
xfs_irele(ip);
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 02c682be29de..519acb248d97 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -221,6 +221,16 @@ xchk_parent_validate(
}
/*
+ * Metadata and regular inodes cannot cross trees. This property
+ * cannot change without a full inode free and realloc cycle, so it's
+ * safe to check this without holding locks.
+ */
+ if (xfs_is_metadata_inode(dp) ^ xfs_is_metadata_inode(sc->ip)) {
+ xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
+ goto out_rele;
+ }
+
+ /*
* We prefer to keep the inode locked while we lock and search
* its alleged parent for a forward reference. If we can grab
* the iolock, validate the pointers and we're done. We must
@@ -348,6 +358,14 @@ xchk_parent(
goto out;
}
+ /* Is this the metadata root dir? Then '..' must point to itself. */
+ if (sc->ip == mp->m_metadirip) {
+ if (sc->ip->i_ino != mp->m_sb.sb_metadirino ||
+ sc->ip->i_ino != dnum)
+ xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
+ goto out;
+ }
+
do {
error = xchk_parent_validate(sc, dnum, &try_again);
if (error)
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
index 75e0db34b3b3..4570e7c94c32 100644
--- a/fs/xfs/scrub/parent_repair.c
+++ b/fs/xfs/scrub/parent_repair.c
@@ -129,6 +129,10 @@ findparent_walk_directory(
if (!S_ISDIR(VFS_I(dp)->i_mode))
goto out_rele;
+ /* Don't mix metadata and regular directory trees. */
+ if (xfs_is_metadata_inode(dp) ^ xfs_is_metadata_inode(fpi->sc->ip))
+ goto out_rele;
+
/*
* Try to get the parent directory's IOLOCK. We still hold the child's
* IOLOCK in exclusive mode, so we must avoid an ABBA deadlock.
@@ -304,14 +308,27 @@ xrep_findparent(
}
/*
+ * If we are the metadata root directory, then we are our own parent.
+ * Return the root directory.
+ */
+ if (sc->ip == sc->mp->m_metadirip) {
+ *parent_ino = sc->mp->m_sb.sb_metadirino;
+ return 0;
+ }
+
+ /*
* If we are an unlinked directory, the parent won't have a link to us.
* We might as well return the suggestion, or the root directory if the
* suggestion is NULLFSINO or garbage. There's no point in scanning
* the filesystem.
*/
if (VFS_I(sc->ip)->i_nlink == 0) {
- if (!xfs_verify_dir_ino(sc->mp, *parent_ino))
- *parent_ino = sc->mp->m_sb.sb_rootino;
+ if (!xfs_verify_dir_ino(sc->mp, *parent_ino)) {
+ if (xfs_is_metadata_inode(sc->ip))
+ *parent_ino = sc->mp->m_sb.sb_metadirino;
+ else
+ *parent_ino = sc->mp->m_sb.sb_rootino;
+ }
return 0;
}