#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"
        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.
  *
                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);
                        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;
 }