[PATCH 18/27] xfs_repair: find and mark the rtrmapbt inodes

Darrick J. Wong
2025-02-06T22:54:26Z

Make sure that we find the realtime rmapbt inodes and mark them
appropriately, just in case we find a rogue inode claiming to be an
rtrmap, or garbage in the metadata directory tree.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
repair/dino_chunks.c | 13 ++++++++++
repair/dinode.c | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
repair/dir2.c | 7 +++++
repair/incore.h | 1 +
repair/rmap.c | 19 ++++++---------
repair/rmap.h | 5 ++--
repair/scan.c | 8 +++---
7 files changed, 95 insertions(+), 23 deletions(-)

diff --git a/repair/dino_chunks.c b/repair/dino_chunks.c
index fe106f0b6ab536..8c5387cdf4ea52 100644
--- a/repair/dino_chunks.c
+++ b/repair/dino_chunks.c
@@ -16,6 +16,8 @@
#include "prefetch.h"
#include "progress.h"
#include "rt.h"
+#include "slab.h"
+#include "rmap.h"

/*
* validates inode block or chunk, returns # of good inodes
@@ -1023,6 +1025,17 @@ process_inode_chunk(
_("would clear rtgroup summary inode %" PRIu64 "\n"),
ino);
}
+ } else if (is_rtrmap_inode(ino)) {
+ rmap_avoid_check(mp);
+ if (!no_modify) {
+ do_warn(
+ _("cleared rtgroup rmap inode %" PRIu64 "\n"),
+ ino);
+ } else {
+ do_warn(
+ _("would clear rtgroup rmap inode %" PRIu64 "\n"),
+ ino);
+ }
} else if (!no_modify) {
do_warn(_("cleared inode %" PRIu64 "\n"),
ino);
diff --git a/repair/dinode.c b/repair/dinode.c
index 628f02714abc05..58691b196bc4cb 100644
--- a/repair/dinode.c
+++ b/repair/dinode.c
@@ -178,6 +178,9 @@ clear_dinode(

if (is_rtsummary_inode(ino_num))
mark_rtgroup_inodes_bad(mp, XFS_RTGI_SUMMARY);
+
+ if (is_rtrmap_inode(ino_num))
+ rmap_avoid_check(mp);
}

/*
@@ -823,6 +826,14 @@ get_agino_buf(
return bp;
}

+static inline xfs_rgnumber_t
+metafile_rgnumber(
+ const struct xfs_dinode *dip)
+{
+ return (xfs_rgnumber_t)be16_to_cpu(dip->di_projid_hi) << 16 |
+ be16_to_cpu(dip->di_projid_lo);
+}
+
/*
* higher level inode processing stuff starts here:
* first, one utility routine for each type of inode
@@ -870,7 +881,10 @@ process_rtrmap(

lino = XFS_AGINO_TO_INO(mp, agno, ino);

- /* This rmap btree inode must be a metadata inode. */
+ /*
+ * This rmap btree inode must be a metadata inode reachable via
+ * /rtgroups/$rgno.rmap in the metadata directory tree.
+ */
if (!(dip->di_flags2 & be64_to_cpu(XFS_DIFLAG2_METADATA))) {
do_warn(
_("rtrmap inode %" PRIu64 " not flagged as metadata\n"),
@@ -878,11 +892,25 @@ _("rtrmap inode %" PRIu64 " not flagged as metadata\n"),
return 1;
}

- if (!is_rtrmap_inode(lino)) {
- do_warn(
+ /*
+ * If this rtrmap file claims to be from an rtgroup that actually
+ * exists, check that inode discovery actually found it. Note that
+ * we can have stray rtrmap files from failed growfsrt operations.
+ */
+ if (metafile_rgnumber(dip) < mp->m_sb.sb_rgcount) {
+ if (type != XR_INO_RTRMAP) {
+ do_warn(
+_("rtrmap inode %" PRIu64 " was not found in the metadata directory tree\n"),
+ lino);
+ return 1;
+ }
+
+ if (!is_rtrmap_inode(lino)) {
+ do_warn(
_("could not associate rtrmap inode %" PRIu64 " with any rtgroup\n"),
- lino);
- return 1;
+ lino);
+ return 1;
+ }
}

memset(&priv.high_key, 0xFF, sizeof(priv.high_key));
@@ -921,7 +949,7 @@ _("computed size of rtrmapbt root (%zu bytes) is greater than space in "
error = process_rtrmap_reclist(mp, rp, numrecs,
&priv.last_rec, NULL, "rtrmapbt root");
if (error) {
- rmap_avoid_check();
+ rmap_avoid_check(mp);
return 1;
}
return 0;
@@ -1891,6 +1919,9 @@ process_check_metadata_inodes(
if (lino == mp->m_sb.sb_rbmino || is_rtbitmap_inode(lino))
return process_check_rt_inode(mp, dinoc, lino, type, dirty,
XR_INO_RTBITMAP, _("realtime bitmap"));
+ if (is_rtrmap_inode(lino))
+ return process_check_rt_inode(mp, dinoc, lino, type, dirty,
+ XR_INO_RTRMAP, _("realtime rmap btree"));
return 0;
}

@@ -1989,6 +2020,18 @@ _("realtime summary inode %" PRIu64 " has bad size %" PRIu64 " (should be %" PRI
}
break;

+ case XR_INO_RTRMAP:
+ /*
+ * if we have no rmapbt, any inode claiming
+ * to be a real-time file is bogus
+ */
+ if (!xfs_has_rmapbt(mp)) {
+ do_warn(
+_("found inode %" PRIu64 " claiming to be a rtrmapbt file, but rmapbt is disabled\n"), lino);
+ return 1;
+ }
+ break;
+
default:
break;
}
@@ -2017,6 +2060,14 @@ _("bad attr fork offset %d in dev inode %" PRIu64 ", should be %d\n"),
return 1;
}
break;
+ case XFS_DINODE_FMT_META_BTREE:
+ if (!xfs_has_metadir(mp) || !xfs_has_parent(mp)) {
+ do_warn(
+_("metadata inode %" PRIu64 " type %d cannot have attr fork\n"),
+ lino, dino->di_format);
+ return 1;
+ }
+ fallthrough;
case XFS_DINODE_FMT_LOCAL:
case XFS_DINODE_FMT_EXTENTS:
case XFS_DINODE_FMT_BTREE:
@@ -3173,6 +3224,8 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
type = XR_INO_GQUOTA;
else if (is_quota_inode(XFS_DQTYPE_PROJ, lino))
type = XR_INO_PQUOTA;
+ else if (is_rtrmap_inode(lino))
+ type = XR_INO_RTRMAP;
else
type = XR_INO_DATA;
break;
diff --git a/repair/dir2.c b/repair/dir2.c
index ed615662009957..af00b2d8d6c852 100644
--- a/repair/dir2.c
+++ b/repair/dir2.c
@@ -15,6 +15,8 @@
#include "da_util.h"
#include "prefetch.h"
#include "progress.h"
+#include "slab.h"
+#include "rmap.h"
#include "rt.h"

/*
@@ -277,6 +279,9 @@ process_sf_dir2(
} else if (lino == mp->m_sb.sb_metadirino) {
junkit = 1;
junkreason = _("metadata directory root");
+ } else if (is_rtrmap_inode(lino)) {
+ junkit = 1;
+ junkreason = _("realtime rmap");
} else if ((irec_p = find_inode_rec(mp,
XFS_INO_TO_AGNO(mp, lino),
XFS_INO_TO_AGINO(mp, lino))) != NULL) {
@@ -754,6 +759,8 @@ process_dir2_data(
clearreason = _("project quota");
} else if (ent_ino == mp->m_sb.sb_metadirino) {
clearreason = _("metadata directory root");
+ } else if (is_rtrmap_inode(ent_ino)) {
+ clearreason = _("realtime rmap");
} else {
irec_p = find_inode_rec(mp,
XFS_INO_TO_AGNO(mp, ent_ino),
diff --git a/repair/incore.h b/repair/incore.h
index 61730c330911f7..4add12615e0a04 100644
--- a/repair/incore.h
+++ b/repair/incore.h
@@ -241,6 +241,7 @@ int count_bcnt_extents(xfs_agnumber_t);
#define XR_INO_UQUOTA 12 /* user quota inode */
#define XR_INO_GQUOTA 13 /* group quota inode */
#define XR_INO_PQUOTA 14 /* project quota inode */
+#define XR_INO_RTRMAP 15 /* realtime rmap */

/* inode allocation tree */

diff --git a/repair/rmap.c b/repair/rmap.c
index 13e9a06b04f370..8656c8df3cbc83 100644
--- a/repair/rmap.c
+++ b/repair/rmap.c
@@ -15,6 +15,7 @@
#include "libfrog/bitmap.h"
#include "libfrog/platform.h"
#include "rcbag.h"
+#include "rt.h"

#undef RMAP_DEBUG

@@ -169,15 +170,6 @@ rmaps_init_ag(
_("Insufficient memory while allocating realtime reverse mapping btree."));
}

-xfs_rgnumber_t
-rtgroup_for_rtrmap_inode(
- struct xfs_mount *mp,
- xfs_ino_t ino)
-{
- /* This will be implemented later. */
- return NULLRGNUMBER;
-}
-
/*
* Initialize per-AG reverse map data.
*/
@@ -203,6 +195,8 @@ rmaps_init(

for (i = 0; i < mp->m_sb.sb_rgcount; i++)
rmaps_init_rt(mp, i, &rg_rmaps[i]);
+
+ discover_rtgroup_inodes(mp);
}

/*
@@ -1142,11 +1136,14 @@ rmap_record_count(
}

/*
- * Disable the refcount btree check.
+ * Disable the rmap btree check.
*/
void
-rmap_avoid_check(void)
+rmap_avoid_check(
+ struct xfs_mount *mp)
{
+ if (xfs_has_rtgroups(mp))
+ mark_rtgroup_inodes_bad(mp, XFS_RTGI_RMAP);
rmapbt_suspect = true;
}

diff --git a/repair/rmap.h b/repair/rmap.h
index 23871e6d60e774..b5c8b4f0bef794 100644
--- a/repair/rmap.h
+++ b/repair/rmap.h
@@ -28,7 +28,7 @@ int rmap_commit_agbtree_mappings(struct xfs_mount *mp, xfs_agnumber_t agno);

uint64_t rmap_record_count(struct xfs_mount *mp, bool isrt,
xfs_agnumber_t agno);
-extern void rmap_avoid_check(void);
+extern void rmap_avoid_check(struct xfs_mount *mp);
void rmaps_verify_btree(struct xfs_mount *mp, xfs_agnumber_t agno);

extern int64_t rmap_diffkeys(struct xfs_rmap_irec *kp1,
@@ -56,6 +56,7 @@ int rmap_init_mem_cursor(struct xfs_mount *mp, struct xfs_trans *tp,
bool isrt, xfs_agnumber_t agno, struct xfs_btree_cur **rmcurp);
int rmap_get_mem_rec(struct xfs_btree_cur *rmcur, struct xfs_rmap_irec *irec);

-xfs_rgnumber_t rtgroup_for_rtrmap_inode(struct xfs_mount *mp, xfs_ino_t ino);
+void populate_rtgroup_rmapbt(struct xfs_rtgroup *rtg,
+ xfs_filblks_t est_fdblocks);

#endif /* RMAP_H_ */
diff --git a/repair/scan.c b/repair/scan.c
index 386aaa15f78c33..7a74f87c5f0c61 100644
--- a/repair/scan.c
+++ b/repair/scan.c
@@ -1348,7 +1348,7 @@ _("out of order key %u in %s btree block (%u/%u)\n"),

out:
if (suspect)
- rmap_avoid_check();
+ rmap_avoid_check(mp);
}

int
@@ -1728,7 +1728,7 @@ _("bad %s btree ptr 0x%llx in ino %" PRIu64 "\n"),

out:
if (hdr_errors || suspect) {
- rmap_avoid_check();
+ rmap_avoid_check(mp);
return 1;
}
return 0;
@@ -2811,7 +2811,7 @@ validate_agf(
if (levels == 0 || levels > mp->m_rmap_maxlevels) {
do_warn(_("bad levels %u for rmapbt root, agno %d\n"),
levels, agno);
- rmap_avoid_check();
+ rmap_avoid_check(mp);
}

bno = be32_to_cpu(agf->agf_rmap_root);
@@ -2826,7 +2826,7 @@ validate_agf(
} else {
do_warn(_("bad agbno %u for rmapbt root, agno %d\n"),
bno, agno);
- rmap_avoid_check();
+ rmap_avoid_check(mp);
}
}