From 13d782173d9d600cf43c2ca13cbb38fe65762b27 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 3 Jul 2024 14:22:34 -0700 Subject: [PATCH] xfs_repair: find and mark the rtrefcountbt inode Make sure that we find the realtime refcountbt inode and mark it appropriately, just in case we find a rogue inode claiming to be an rtrefcount, or just plain garbage in the superblock field. Signed-off-by: Darrick J. Wong --- repair/dino_chunks.c | 11 +++++ repair/dinode.c | 30 +++++++++++++- repair/dir2.c | 5 +++ repair/incore.h | 1 + repair/rmap.c | 99 +++++++++++++++++++++++++++++++++++++++++++- repair/rmap.h | 3 +- repair/scan.c | 8 ++-- 7 files changed, 149 insertions(+), 8 deletions(-) diff --git a/repair/dino_chunks.c b/repair/dino_chunks.c index fc882a49b..6d612a91e 100644 --- a/repair/dino_chunks.c +++ b/repair/dino_chunks.c @@ -1016,6 +1016,17 @@ next_readbuf: _("would clear realtime rmap inode %" PRIu64 "\n"), ino); } + } else if (is_rtrefcount_ino(ino)) { + refcount_avoid_check(mp); + if (!no_modify) { + do_warn( + _("cleared realtime refcount inode %" PRIu64 "\n"), + ino); + } else { + do_warn( + _("would clear realtime refcount 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 696c234fc..ae5f24279 100644 --- a/repair/dinode.c +++ b/repair/dinode.c @@ -156,6 +156,9 @@ clear_dinode(xfs_mount_t *mp, struct xfs_dinode *dino, xfs_ino_t ino_num) if (is_rtrmap_inode(ino_num)) rmap_avoid_check(mp); + if (is_rtrefcount_ino(ino_num)) + refcount_avoid_check(mp); + /* and clear the forks */ memset(XFS_DFORK_DPTR(dino), 0, XFS_LITINO(mp)); return; @@ -1076,6 +1079,12 @@ _("rtrefcount inode %" PRIu64 " not flagged as metadata\n"), lino); return 1; } + if (type != XR_INO_RTREFC) { + do_warn( +_("rtrefcount inode %" PRIu64 " was not found in the metadata directory tree\n"), + lino); + return 1; + } priv.rgno = rtgroup_for_rtrefcount_inode(mp, lino); if (priv.rgno == NULLRGNUMBER) { @@ -1116,7 +1125,7 @@ _("computed size of rtrefcountbt root (%zu bytes) is greater than space in " error = process_rtrefc_reclist(mp, rp, numrecs, &priv, "rtrefcountbt root"); if (error) { - refcount_avoid_check(); + refcount_avoid_check(mp); return 1; } return 0; @@ -2074,6 +2083,9 @@ process_check_sb_inodes( if (is_rtrmap_inode(lino)) return process_check_rt_inode(mp, dinoc, lino, type, dirty, XR_INO_RTRMAP, _("realtime rmap btree")); + if (is_rtrefcount_ino(lino)) + return process_check_rt_inode(mp, dinoc, lino, type, dirty, + XR_INO_RTREFC, _("realtime refcount btree")); return 0; } @@ -2183,6 +2195,18 @@ _("found inode %" PRIu64 " claiming to be a rtrmapbt file, but rmapbt is disable } break; + case XR_INO_RTREFC: + /* + * if we have no refcountbt, any inode claiming + * to be a real-time file is bogus + */ + if (!xfs_has_reflink(mp)) { + do_warn( +_("found inode %" PRIu64 " claiming to be a rtrefcountbt file, but reflink is disabled\n"), lino); + return 1; + } + break; + default: break; } @@ -2212,6 +2236,7 @@ _("bad attr fork offset %d in dev inode %" PRIu64 ", should be %d\n"), } break; case XFS_DINODE_FMT_RMAP: + case XFS_DINODE_FMT_REFCOUNT: if (!(xfs_has_metadir(mp) && xfs_has_parent(mp))) { do_warn( _("metadata inode %" PRIu64 " type %d cannot have attr fork\n"), @@ -3355,6 +3380,8 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"), type = XR_INO_PQUOTA; else if (is_rtrmap_inode(lino)) type = XR_INO_RTRMAP; + else if (is_rtrefcount_ino(lino)) + type = XR_INO_RTREFC; else type = XR_INO_DATA; break; @@ -3462,6 +3489,7 @@ _("Bad CoW extent size %u on inode %" PRIu64 ", "), case XR_INO_GQUOTA: case XR_INO_PQUOTA: case XR_INO_RTRMAP: + case XR_INO_RTREFC: /* * This inode was recognized as being filesystem * metadata, so preserve the inode and its contents for diff --git a/repair/dir2.c b/repair/dir2.c index e5f643bb7..efd9fef0d 100644 --- a/repair/dir2.c +++ b/repair/dir2.c @@ -279,6 +279,9 @@ process_sf_dir2( } else if (is_rtrmap_inode(lino)) { junkit = 1; junkreason = _("realtime rmap"); + } else if (is_rtrefcount_ino(lino)) { + junkit = 1; + junkreason = _("realtime refcount"); } else if ((irec_p = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, lino), XFS_INO_TO_AGINO(mp, lino))) != NULL) { @@ -756,6 +759,8 @@ process_dir2_data( clearreason = _("metadata directory root"); } else if (is_rtrmap_inode(ent_ino)) { clearreason = _("realtime rmap"); + } else if (is_rtrefcount_ino(ent_ino)) { + clearreason = _("realtime refcount"); } 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 6ee7a6629..5014a2d30 100644 --- a/repair/incore.h +++ b/repair/incore.h @@ -222,6 +222,7 @@ int count_bcnt_extents(xfs_agnumber_t); #define XR_INO_GQUOTA 13 /* group quota inode */ #define XR_INO_PQUOTA 14 /* project quota inode */ #define XR_INO_RTRMAP 15 /* realtime rmap */ +#define XR_INO_RTREFC 16 /* realtime refcount */ /* inode allocation tree */ diff --git a/repair/rmap.c b/repair/rmap.c index ac1e0a3f3..ac4ed4bcf 100644 --- a/repair/rmap.c +++ b/repair/rmap.c @@ -44,6 +44,12 @@ struct xfs_ag_rmap { */ xfs_ino_t rg_rmap_ino; + /* + * inumber of the refcount btree for this rtgroup. This can be set to + * NULLFSINO to signal to phase 6 to link a new inode into the metadir. + */ + xfs_ino_t rg_refcount_ino; + /* agfl entries from leftover agbt allocations */ int ar_flcount; }; @@ -56,6 +62,9 @@ static bool refcbt_suspect; /* Bitmap of rt group rmap inodes reachable via /realtime/$rgno.rmap. */ static struct bitmap *rmap_inodes; +/* Bitmap of rt group refcount inodes reachable via /realtime/$rgno.refcount. */ +static struct bitmap *refcount_inodes; + static struct xfs_ag_rmap *rmaps_for_group(bool isrt, unsigned int group) { if (isrt) @@ -134,6 +143,7 @@ rmaps_init_rt( goto nomem; ag_rmap->rg_rmap_ino = NULLFSINO; + ag_rmap->rg_refcount_ino = NULLFSINO; return; nomem: do_error( @@ -242,6 +252,51 @@ out_path: return error; } +static inline int +set_rtgroup_refcount_inode( + struct xfs_mount *mp, + xfs_rgnumber_t rgno) +{ + struct xfs_imeta_path *path; + struct xfs_ag_rmap *ar = rmaps_for_group(true, rgno); + struct xfs_trans *tp; + xfs_ino_t ino; + int error; + + if (!xfs_has_rtreflink(mp)) + return 0; + + path = xfs_rtrefcountbt_create_path(mp, rgno); + if (!path) + return ENOMEM; + + error = -libxfs_trans_alloc_empty(mp, &tp); + if (error) + goto out_path; + + error = -libxfs_imeta_lookup(tp, path, &ino); + if (error) + goto out_trans; + + if (!libxfs_verify_ino(mp, ino) || + bitmap_test(refcount_inodes, ino, 1)) { + error = EFSCORRUPTED; + goto out_trans; + } + + error = bitmap_set(refcount_inodes, ino, 1); + if (error) + goto out_trans; + + ar->rg_refcount_ino = ino; + +out_trans: + libxfs_trans_cancel(tp); +out_path: + libxfs_imeta_free_path(path); + return error; +} + static void discover_rtgroup_inodes( struct xfs_mount *mp) @@ -253,10 +308,20 @@ discover_rtgroup_inodes( if (error) goto out; + error = bitmap_alloc(&refcount_inodes); + if (error) { + bitmap_free(&rmap_inodes); + goto out; + } + for (rgno = 0; rgno < mp->m_sb.sb_rgcount; rgno++) { int err2 = set_rtgroup_rmap_inode(mp, rgno); if (err2 && !error) error = err2; + + err2 = set_rtgroup_refcount_inode(mp, rgno); + if (err2 && !error) + error = err2; } out: @@ -272,6 +337,7 @@ out: static inline void free_rtmeta_inode_bitmaps(void) { + bitmap_free(&refcount_inodes); bitmap_free(&rmap_inodes); } @@ -287,10 +353,28 @@ rtgroup_for_rtrefcount_inode( struct xfs_mount *mp, xfs_ino_t ino) { - /* This will be implemented later. */ + xfs_rgnumber_t rgno; + + if (!refcount_inodes) + return NULLRGNUMBER; + + for (rgno = 0; rgno < mp->m_sb.sb_rgcount; rgno++) { + if (rg_rmaps[rgno].rg_refcount_ino == ino) + return rgno; + } + return NULLRGNUMBER; } +bool +is_rtrefcount_ino( + xfs_ino_t ino) +{ + if (!refcount_inodes) + return false; + return bitmap_test(refcount_inodes, ino, 1); +} + /* * Initialize per-AG reverse map data. */ @@ -1870,8 +1954,19 @@ init_refcount_cursor( * Disable the refcount btree check. */ void -refcount_avoid_check(void) +refcount_avoid_check( + struct xfs_mount *mp) { + struct xfs_rtgroup *rtg; + xfs_rgnumber_t rgno; + + for_each_rtgroup(mp, rgno, rtg) { + struct xfs_ag_rmap *ar = rmaps_for_group(true, rtg->rtg_rgno); + + ar->rg_refcount_ino = NULLFSINO; + } + + bitmap_clear(refcount_inodes, 0, XFS_MAXINUMBER); refcbt_suspect = true; } diff --git a/repair/rmap.h b/repair/rmap.h index c4e3b7f3a..75974fccb 100644 --- a/repair/rmap.h +++ b/repair/rmap.h @@ -41,7 +41,7 @@ extern void rmap_high_key_from_rec(struct xfs_rmap_irec *rec, extern int compute_refcounts(struct xfs_mount *, xfs_agnumber_t); uint64_t refcount_record_count(struct xfs_mount *mp, xfs_agnumber_t agno); extern int init_refcount_cursor(xfs_agnumber_t, struct xfs_slab_cursor **); -extern void refcount_avoid_check(void); +extern void refcount_avoid_check(struct xfs_mount *mp); void check_refcounts(struct xfs_mount *mp, xfs_agnumber_t agno); extern void record_inode_reflink_flag(struct xfs_mount *, struct xfs_dinode *, @@ -67,5 +67,6 @@ xfs_filblks_t estimate_rtrmapbt_blocks(struct xfs_rtgroup *rtg); xfs_rgnumber_t rtgroup_for_rtrefcount_inode(struct xfs_mount *mp, xfs_ino_t ino); +bool is_rtrefcount_ino(xfs_ino_t ino); #endif /* RMAP_H_ */ diff --git a/repair/scan.c b/repair/scan.c index 0811a7e21..c469f60c4 100644 --- a/repair/scan.c +++ b/repair/scan.c @@ -1987,7 +1987,7 @@ _("extent (%u/%u) len %u claimed, state is %d\n"), libxfs_perag_put(pag); out: if (suspect) - refcount_avoid_check(); + refcount_avoid_check(mp); return; } @@ -2277,7 +2277,7 @@ _("%s btree block claimed (state %d), agno %d, agbno %d, suspect %d\n"), } out: if (suspect) { - refcount_avoid_check(); + refcount_avoid_check(mp); return 1; } @@ -3140,7 +3140,7 @@ validate_agf( if (levels == 0 || levels > mp->m_refc_maxlevels) { do_warn(_("bad levels %u for refcountbt root, agno %d\n"), levels, agno); - refcount_avoid_check(); + refcount_avoid_check(mp); } bno = be32_to_cpu(agf->agf_refcount_root); @@ -3158,7 +3158,7 @@ validate_agf( } else { do_warn(_("bad agbno %u for refcntbt root, agno %d\n"), bno, agno); - refcount_avoid_check(); + refcount_avoid_check(mp); } } -- 2.50.1