|
@@ -37,6 +37,7 @@
|
|
|
#include "xfs_bmap_util.h"
|
|
|
#include "xfs_bmap_btree.h"
|
|
|
#include "xfs_rmap.h"
|
|
|
+#include "xfs_rmap_btree.h"
|
|
|
#include "xfs_refcount.h"
|
|
|
#include "scrub/xfs_scrub.h"
|
|
|
#include "scrub/scrub.h"
|
|
@@ -423,6 +424,169 @@ xfs_scrub_bmap_btree(
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+struct xfs_scrub_bmap_check_rmap_info {
|
|
|
+ struct xfs_scrub_context *sc;
|
|
|
+ int whichfork;
|
|
|
+ struct xfs_iext_cursor icur;
|
|
|
+};
|
|
|
+
|
|
|
+/* Can we find bmaps that fit this rmap? */
|
|
|
+STATIC int
|
|
|
+xfs_scrub_bmap_check_rmap(
|
|
|
+ struct xfs_btree_cur *cur,
|
|
|
+ struct xfs_rmap_irec *rec,
|
|
|
+ void *priv)
|
|
|
+{
|
|
|
+ struct xfs_bmbt_irec irec;
|
|
|
+ struct xfs_scrub_bmap_check_rmap_info *sbcri = priv;
|
|
|
+ struct xfs_ifork *ifp;
|
|
|
+ struct xfs_scrub_context *sc = sbcri->sc;
|
|
|
+ bool have_map;
|
|
|
+
|
|
|
+ /* Is this even the right fork? */
|
|
|
+ if (rec->rm_owner != sc->ip->i_ino)
|
|
|
+ return 0;
|
|
|
+ if ((sbcri->whichfork == XFS_ATTR_FORK) ^
|
|
|
+ !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
|
|
|
+ return 0;
|
|
|
+ if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Now look up the bmbt record. */
|
|
|
+ ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
|
|
|
+ if (!ifp) {
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
|
|
|
+ &sbcri->icur, &irec);
|
|
|
+ if (!have_map)
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ /*
|
|
|
+ * bmap extent record lengths are constrained to 2^21 blocks in length
|
|
|
+ * because of space constraints in the on-disk metadata structure.
|
|
|
+ * However, rmap extent record lengths are constrained only by AG
|
|
|
+ * length, so we have to loop through the bmbt to make sure that the
|
|
|
+ * entire rmap is covered by bmbt records.
|
|
|
+ */
|
|
|
+ while (have_map) {
|
|
|
+ if (irec.br_startoff != rec->rm_offset)
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
|
|
|
+ cur->bc_private.a.agno, rec->rm_startblock))
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ if (irec.br_blockcount > rec->rm_blockcount)
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
|
|
+ break;
|
|
|
+ rec->rm_startblock += irec.br_blockcount;
|
|
|
+ rec->rm_offset += irec.br_blockcount;
|
|
|
+ rec->rm_blockcount -= irec.br_blockcount;
|
|
|
+ if (rec->rm_blockcount == 0)
|
|
|
+ break;
|
|
|
+ have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
|
|
|
+ if (!have_map)
|
|
|
+ xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
|
|
|
+ rec->rm_offset);
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
|
|
+ return XFS_BTREE_QUERY_RANGE_ABORT;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Make sure each rmap has a corresponding bmbt entry. */
|
|
|
+STATIC int
|
|
|
+xfs_scrub_bmap_check_ag_rmaps(
|
|
|
+ struct xfs_scrub_context *sc,
|
|
|
+ int whichfork,
|
|
|
+ xfs_agnumber_t agno)
|
|
|
+{
|
|
|
+ struct xfs_scrub_bmap_check_rmap_info sbcri;
|
|
|
+ struct xfs_btree_cur *cur;
|
|
|
+ struct xfs_buf *agf;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
|
|
|
+ if (!cur) {
|
|
|
+ error = -ENOMEM;
|
|
|
+ goto out_agf;
|
|
|
+ }
|
|
|
+
|
|
|
+ sbcri.sc = sc;
|
|
|
+ sbcri.whichfork = whichfork;
|
|
|
+ error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
|
|
|
+ if (error == XFS_BTREE_QUERY_RANGE_ABORT)
|
|
|
+ error = 0;
|
|
|
+
|
|
|
+ xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
|
|
|
+out_agf:
|
|
|
+ xfs_trans_brelse(sc->tp, agf);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/* Make sure each rmap has a corresponding bmbt entry. */
|
|
|
+STATIC int
|
|
|
+xfs_scrub_bmap_check_rmaps(
|
|
|
+ struct xfs_scrub_context *sc,
|
|
|
+ int whichfork)
|
|
|
+{
|
|
|
+ loff_t size;
|
|
|
+ xfs_agnumber_t agno;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
|
|
|
+ whichfork == XFS_COW_FORK ||
|
|
|
+ (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Don't support realtime rmap checks yet. */
|
|
|
+ if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Only do this for complex maps that are in btree format, or for
|
|
|
+ * situations where we would seem to have a size but zero extents.
|
|
|
+ * The inode repair code can zap broken iforks, which means we have
|
|
|
+ * to flag this bmap as corrupt if there are rmaps that need to be
|
|
|
+ * reattached.
|
|
|
+ */
|
|
|
+ switch (whichfork) {
|
|
|
+ case XFS_DATA_FORK:
|
|
|
+ size = i_size_read(VFS_I(sc->ip));
|
|
|
+ break;
|
|
|
+ case XFS_ATTR_FORK:
|
|
|
+ size = XFS_IFORK_Q(sc->ip);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ size = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
|
|
|
+ (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
|
|
|
+ error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Scrub an inode fork's block mappings.
|
|
|
*
|
|
@@ -463,7 +627,7 @@ xfs_scrub_bmap(
|
|
|
break;
|
|
|
case XFS_ATTR_FORK:
|
|
|
if (!ifp)
|
|
|
- goto out;
|
|
|
+ goto out_check_rmap;
|
|
|
if (!xfs_sb_version_hasattr(&mp->m_sb) &&
|
|
|
!xfs_sb_version_hasattr2(&mp->m_sb))
|
|
|
xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
|
|
@@ -534,6 +698,10 @@ xfs_scrub_bmap(
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+out_check_rmap:
|
|
|
+ error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
|
|
|
+ if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
|
|
|
+ goto out;
|
|
|
out:
|
|
|
return error;
|
|
|
}
|