]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: repair inodes that have a refcount btree in the data fork
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:13:28 +0000 (21:13 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 24 Jul 2024 05:33:58 +0000 (22:33 -0700)
Plumb knowledge of refcount btrees into the inode core repair code.

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

index c07412a95d83068f595724f19d05523e2b9c9a26..9d11fb3d35998f0d7f2d5b19ee3bf0acfca24e92 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"
@@ -972,6 +973,37 @@ xrep_dinode_bad_rmapbt_fork(
        return false;
 }
 
+/* Return true if this refcount-format ifork looks like garbage. */
+STATIC bool
+xrep_dinode_bad_refcountbt_fork(
+       struct xfs_scrub        *sc,
+       struct xfs_dinode       *dip,
+       unsigned int            dfork_size,
+       int                     whichfork)
+{
+       struct xfs_rtrefcount_root *dfp;
+       unsigned int            nrecs;
+       unsigned int            level;
+
+       if (whichfork != XFS_DATA_FORK)
+               return true;
+       if (dfork_size < sizeof(struct xfs_rtrefcount_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_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 the data fork for things that will fail the ifork verifiers or the
  * ifork formatters.
@@ -1057,6 +1089,11 @@ xrep_dinode_check_dfork(
                                XFS_DATA_FORK))
                        return true;
                break;
+       case XFS_DINODE_FMT_REFCOUNT:
+               if (xrep_dinode_bad_refcountbt_fork(sc, dip, dfork_size,
+                               XFS_DATA_FORK))
+                       return true;
+               break;
        default:
                return true;
        }
@@ -1182,6 +1219,11 @@ xrep_dinode_check_afork(
                                XFS_ATTR_FORK))
                        return true;
                break;
+       case XFS_DINODE_FMT_REFCOUNT:
+               if (xrep_dinode_bad_refcountbt_fork(sc, dip, afork_size,
+                               XFS_ATTR_FORK))
+                       return true;
+               break;
        default:
                return true;
        }
@@ -1230,6 +1272,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);
@@ -1340,6 +1383,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_DINODE_FMT_REFCOUNT:
+               rcdr = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+               dfork_min = xfs_rtrefcount_broot_space(sc->mp, rcdr);
+               break;
        default:
                dfork_min = 0;
                break;