@@ -710,6 +710,55 @@ copy_refcount_btree(
return scan_btree(agno, root, levels, TYP_REFCBT, agf, scanfunc_refcntbt);
}
+static int
+scanfunc_rtrefcbt(
+ struct xfs_btree_block *block,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg)
+{
+ xfs_rtrefcount_ptr_t *pp;
+ int i;
+ int numrecs;
+
+ if (level == 0)
+ return 1;
+
+ numrecs = be16_to_cpu(block->bb_numrecs);
+ if (numrecs > mp->m_rtrefc_mxr[1]) {
+ if (metadump.show_warnings)
+ print_warning("invalid numrecs (%u) in %s block %u/%u",
+ numrecs, typtab[btype].name, agno, agbno);
+ return 1;
+ }
+
+ pp = xfs_rtrefcount_ptr_addr(block, 1, mp->m_rtrefc_mxr[1]);
+ for (i = 0; i < numrecs; i++) {
+ xfs_agnumber_t pagno;
+ xfs_agblock_t pbno;
+
+ pagno = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i]));
+ pbno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i]));
+
+ if (pbno == 0 || pbno > mp->m_sb.sb_agblocks ||
+ pagno > mp->m_sb.sb_agcount) {
+ if (metadump.show_warnings)
+ print_warning("invalid block number (%u/%u) "
+ "in inode %llu %s block %u/%u",
+ pagno, pbno,
+ (unsigned long long)metadump.cur_ino,
+ typtab[btype].name, agno, agbno);
+ continue;
+ }
+ if (!scan_btree(pagno, pbno, level, btype, arg,
+ scanfunc_rtrefcbt))
+ return 0;
+ }
+ return 1;
+}
+
/* filename and extended attribute obfuscation routines */
struct name_ent {
@@ -2445,6 +2494,83 @@ process_rtrmap(
return 1;
}
+static int
+process_rtrefc(
+ struct xfs_dinode *dip,
+ typnm_t itype)
+{
+ struct xfs_rtrefcount_root *dib;
+ int i;
+ xfs_rtrefcount_ptr_t *pp;
+ int level;
+ int nrecs;
+ int maxrecs;
+ int whichfork;
+ typnm_t btype;
+
+ if (itype == TYP_ATTR && metadump.show_warnings) {
+ print_warning("ignoring rtrefcbt root in inode %llu attr fork",
+ (unsigned long long)metadump.cur_ino);
+ return 1;
+ }
+
+ whichfork = XFS_DATA_FORK;
+ btype = TYP_RTREFCBT;
+
+ dib = (struct xfs_rtrefcount_root *)XFS_DFORK_PTR(dip, whichfork);
+ level = be16_to_cpu(dib->bb_level);
+ nrecs = be16_to_cpu(dib->bb_numrecs);
+
+ if (level > mp->m_rtrefc_maxlevels) {
+ if (metadump.show_warnings)
+ print_warning("invalid level (%u) in inode %lld %s "
+ "root", level,
+ (unsigned long long)metadump.cur_ino,
+ typtab[btype].name);
+ return 1;
+ }
+
+ if (level == 0)
+ return 1;
+
+ maxrecs = libxfs_rtrefcountbt_droot_maxrecs(
+ XFS_DFORK_SIZE(dip, mp, whichfork),
+ false);
+ if (nrecs > maxrecs) {
+ if (metadump.show_warnings)
+ print_warning("invalid numrecs (%u) in inode %lld %s "
+ "root", nrecs,
+ (unsigned long long)metadump.cur_ino,
+ typtab[btype].name);
+ return 1;
+ }
+
+ pp = xfs_rtrefcount_droot_ptr_addr(dib, 1, maxrecs);
+ for (i = 0; i < nrecs; i++) {
+ xfs_agnumber_t ag;
+ xfs_agblock_t bno;
+
+ ag = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i]));
+ bno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i]));
+
+ if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
+ ag > mp->m_sb.sb_agcount) {
+ if (metadump.show_warnings)
+ print_warning("invalid block number (%u/%u) "
+ "in inode %llu %s root", ag,
+ bno,
+ (unsigned long long)metadump.cur_ino,
+ typtab[btype].name);
+ continue;
+ }
+
+ if (!scan_btree(ag, bno, level, btype, &itype,
+ scanfunc_rtrefcbt))
+ return 0;
+ }
+ return 1;
+}
+
static int
process_inode_data(
struct xfs_dinode *dip,
@@ -2492,6 +2618,9 @@ process_inode_data(
case XFS_DINODE_FMT_RMAP:
return process_rtrmap(dip, itype);
+
+ case XFS_DINODE_FMT_REFCOUNT:
+ return process_rtrefc(dip, itype);
}
return 1;
}