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-12-15 17:29:08 -0800
commit52ef80084aa116f623bd79a5bd99e2ecbd1697e0 (patch)
tree36d3af95b2fafd109120df1e5c840122cb6fb54e
parentc30358814d4e757e02cd713f367801076cd9077c (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.c14
-rw-r--r--fs/xfs/scrub/parent.c18
-rw-r--r--fs/xfs/scrub/parent_repair.c27
4 files changed, 63 insertions, 5 deletions
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 48b6960db5ed..0f94f94d3918 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -95,6 +95,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 071126d3fe12..791ec9b5d991 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -213,6 +213,12 @@ xrep_directory_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;
+ }
+
entry.ftype = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
xfs_irele(ip);
@@ -1080,8 +1086,14 @@ xrep_directory_self_parent(
if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino)
return sc->mp->m_sb.sb_rootino;
- if (VFS_I(sc->ip)->i_nlink == 0)
+ if (sc->ip->i_ino == sc->mp->m_sb.sb_metadirino)
+ return sc->mp->m_sb.sb_metadirino;
+
+ if (VFS_I(sc->ip)->i_nlink == 0) {
+ if (xfs_is_metadata_inode(sc->ip))
+ return sc->mp->m_sb.sb_metadirino;
return sc->mp->m_sb.sb_rootino;
+ }
return NULLFSINO;
}
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index ea169669e8c3..e4de4cb6ea19 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -215,6 +215,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
* of the alleged parent, then we can move ahead to counting dirents
@@ -313,5 +323,13 @@ xchk_parent(
return 0;
}
+ /* 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 != parent_ino)
+ xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
+ return 0;
+ }
+
return xchk_parent_validate(sc, parent_ino);
}
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
index 134726bb20bf..743476192cf8 100644
--- a/fs/xfs/scrub/parent_repair.c
+++ b/fs/xfs/scrub/parent_repair.c
@@ -137,6 +137,10 @@ xrep_findparent_walk_directory(
if (dp == sc->ip)
return 0;
+ /* Don't mix metadata and regular directory trees. */
+ if (xfs_is_metadata_inode(dp) ^ xfs_is_metadata_inode(sc->ip))
+ return 0;
+
/* Try to lock dp; if we can, we're ready to scan! */
if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
xfs_ino_t orig_parent, new_parent;
@@ -245,12 +249,27 @@ xrep_parent_confirm(
};
int error;
+ /* The root directory always points to itself. */
+ if (sc->ip == sc->mp->m_rootip) {
+ *parent_ino = sc->mp->m_sb.sb_rootino;
+ return 0;
+ }
+
+ /* The metadata root directory always points to itself. */
+ if (sc->ip == sc->mp->m_metadirip) {
+ *parent_ino = sc->mp->m_sb.sb_metadirino;
+ return 0;
+ }
+
/*
- * The root directory always points to itself. Unlinked dirs can point
- * anywhere, so we point them at the root dir too.
+ * Unlinked dirs can point anywhere, so we point them at the root dir
+ * of whichever tree is appropriate.
*/
- if (sc->ip == sc->mp->m_rootip || VFS_I(sc->ip)->i_nlink == 0) {
- *parent_ino = sc->mp->m_sb.sb_rootino;
+ if (VFS_I(sc->ip)->i_nlink == 0) {
+ 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;
}