]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: repair rmap btree inodes
authorDarrick J. Wong <djwong@kernel.org>
Tue, 15 Oct 2024 19:40:06 +0000 (12:40 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:36:26 +0000 (13:36 -0800)
Teach the inode repair code how to deal with realtime rmap btree inodes
that won't load properly.  This is most likely moot since the filesystem
generally won't mount without the rtrmapbt inodes being usable, but
we'll add this for completeness.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/scrub/inode_repair.c

index 6b931174bdb6d1859451c7bdb1b9abf6414a9b7c..7a1433782bcf6d7043c4417ace9b2ffc471aec97 100644 (file)
@@ -944,6 +944,37 @@ xrep_dinode_bad_bmbt_fork(
        return false;
 }
 
+/* Return true if this rmap-format ifork looks like garbage. */
+STATIC bool
+xrep_dinode_bad_rmapbt_fork(
+       struct xfs_scrub        *sc,
+       struct xfs_dinode       *dip,
+       unsigned int            dfork_size,
+       int                     whichfork)
+{
+       struct xfs_rtrmap_root  *dfp;
+       unsigned int            nrecs;
+       unsigned int            level;
+
+       if (whichfork != XFS_DATA_FORK)
+               return true;
+       if (dfork_size < sizeof(struct xfs_rtrmap_root))
+               return true;
+
+       dfp = XFS_DFORK_PTR(dip, whichfork);
+       nrecs = be16_to_cpu(dfp->bb_numrecs);
+       level = be16_to_cpu(dfp->bb_level);
+
+       if (level > sc->mp->m_rtrmap_maxlevels)
+               return true;
+       if (xfs_rtrmap_droot_space_calc(level, nrecs) > dfork_size)
+               return true;
+       if (level > 0 && nrecs == 0)
+               return true;
+
+       return false;
+}
+
 /*
  * Check the data fork for things that will fail the ifork verifiers or the
  * ifork formatters.
@@ -1024,6 +1055,11 @@ xrep_dinode_check_dfork(
                                XFS_DATA_FORK))
                        return true;
                break;
+       case XFS_DINODE_FMT_RMAP:
+               if (xrep_dinode_bad_rmapbt_fork(sc, dip, dfork_size,
+                               XFS_DATA_FORK))
+                       return true;
+               break;
        default:
                return true;
        }
@@ -1144,6 +1180,11 @@ xrep_dinode_check_afork(
                                        XFS_ATTR_FORK))
                        return true;
                break;
+       case XFS_DINODE_FMT_RMAP:
+               if (xrep_dinode_bad_rmapbt_fork(sc, dip, afork_size,
+                               XFS_ATTR_FORK))
+                       return true;
+               break;
        default:
                return true;
        }
@@ -1191,6 +1232,7 @@ xrep_dinode_ensure_forkoff(
        uint16_t                mode)
 {
        struct xfs_bmdr_block   *bmdr;
+       struct xfs_rtrmap_root  *rmdr;
        struct xfs_scrub        *sc = ri->sc;
        xfs_extnum_t            attr_extents, data_extents;
        size_t                  bmdr_minsz = xfs_bmdr_space_calc(1);
@@ -1297,6 +1339,10 @@ xrep_dinode_ensure_forkoff(
                bmdr = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
                dfork_min = xfs_bmap_broot_space(sc->mp, bmdr);
                break;
+       case XFS_DINODE_FMT_RMAP:
+               rmdr = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+               dfork_min = xfs_rtrmap_broot_space(sc->mp, rmdr);
+               break;
        default:
                dfork_min = 0;
                break;