]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: repair inodes that have a refcount btree in the data fork
authorDarrick J. Wong <djwong@kernel.org>
Thu, 21 Nov 2024 00:21:15 +0000 (16:21 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 23 Dec 2024 21:06:16 +0000 (13:06 -0800)
Plumb knowledge of refcount btrees into the inode core repair code.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/scrub/inode_repair.c

index 701baee144a66ebff9d86aa199e4f88d48dff36c..2f641b6d663eb2b8d231a80265879c08df6be7a5 100644 (file)
@@ -40,6 +40,7 @@
 #include "xfs_symlink_remote.h"
 #include "xfs_rtgroup.h"
 #include "xfs_rtrmap_btree.h"
+#include "xfs_rtrefcount_btree.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -970,6 +971,34 @@ xrep_dinode_bad_rtrmapbt_fork(
        return false;
 }
 
+/* Return true if this refcount-format ifork looks like garbage. */
+STATIC bool
+xrep_dinode_bad_rtrefcountbt_fork(
+       struct xfs_scrub        *sc,
+       struct xfs_dinode       *dip,
+       unsigned int            dfork_size)
+{
+       struct xfs_rtrefcount_root *dfp;
+       unsigned int            nrecs;
+       unsigned int            level;
+
+       if (dfork_size < sizeof(struct xfs_rtrefcount_root))
+               return true;
+
+       dfp = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+       nrecs = be16_to_cpu(dfp->bb_numrecs);
+       level = be16_to_cpu(dfp->bb_level);
+
+       if (level > sc->mp->m_rtrefc_maxlevels)
+               return true;
+       if (xfs_rtrefcount_droot_space_calc(level, nrecs) > dfork_size)
+               return true;
+       if (level > 0 && nrecs == 0)
+               return true;
+
+       return false;
+}
+
 /* Check a metadata-btree fork. */
 STATIC bool
 xrep_dinode_bad_metabt_fork(
@@ -984,6 +1013,8 @@ xrep_dinode_bad_metabt_fork(
        switch (be16_to_cpu(dip->di_metatype)) {
        case XFS_METAFILE_RTRMAP:
                return xrep_dinode_bad_rtrmapbt_fork(sc, dip, dfork_size);
+       case XFS_METAFILE_RTREFCOUNT:
+               return xrep_dinode_bad_rtrefcountbt_fork(sc, dip, dfork_size);
        default:
                return true;
        }
@@ -1249,6 +1280,7 @@ xrep_dinode_ensure_forkoff(
 {
        struct xfs_bmdr_block   *bmdr;
        struct xfs_rtrmap_root  *rmdr;
+       struct xfs_rtrefcount_root *rcdr;
        struct xfs_scrub        *sc = ri->sc;
        xfs_extnum_t            attr_extents, data_extents;
        size_t                  bmdr_minsz = xfs_bmdr_space_calc(1);
@@ -1361,6 +1393,10 @@ xrep_dinode_ensure_forkoff(
                        rmdr = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
                        dfork_min = xfs_rtrmap_broot_space(sc->mp, rmdr);
                        break;
+               case XFS_METAFILE_RTREFCOUNT:
+                       rcdr = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+                       dfork_min = xfs_rtrefcount_broot_space(sc->mp, rcdr);
+                       break;
                default:
                        dfork_min = 0;
                        break;