+};
+
+static typnm_t btype_map[TYP_NONE];
+
+#define TYP_MAP_SIZE (sizeof (btype_map) / sizeof (typnm_t))
+
+typedef void (*scan_sbtree_f_t)(struct xfs_btree_block *block,
+ int level, xfs_agf_t *agf,
+ xfs_agblock_t bno, int isroot,
+ typnm_t btype);
+static int treedump_f(int argc, char **argv);
+static void scan_ag(xfs_agnumber_t agno);
+static void scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
+ int nlevels, int isroot,
+ scan_sbtree_f_t func, typnm_t btype);
+static void scanfunc_btree(struct xfs_btree_block *block, int level,
+ xfs_agf_t *agf, xfs_agblock_t bno,
+ int isroot, typnm_t btype);
+/*
+ * [-b]: bnobt, [-c]: cntbt, [-f]: refcntbt, [-r]: rmapbt
+ */
+static const cmdinfo_t treedump_cmd =
+ { "treedump", NULL, treedump_f, 0, 4, 0,
+ N_("[-b] [-c] [-f] [-r]"),
+ N_("dump the metadata of the btrees"), NULL };
+
+static void set_btree_type(
+ typnm_t type)
+{
+ btype_map[type] = type;
+}
+
+/* ARGSUSED */
+static int
+treedump_f(
+ int argc,
+ char **argv)
+{
+ int c;
+
+ if (cur_agno == NULLAGNUMBER)
+ cur_agno = 0;
+
+ /* Dump the BNOBT if none is specified */
+ set_btree_type(TYP_BNOBT);
+
+ while ((c = getopt(argc, argv, "bcfr")) != EOF) {
+ switch (c) {
+ case 'b':
+ set_btree_type(TYP_BNOBT);
+ break;
+ case 'c':
+ set_btree_type(TYP_CNTBT);
+ break;
+ case 'f':
+ set_btree_type(TYP_REFCBT);
+ break;
+ case 'r':
+ set_btree_type(TYP_RMAPBT);
+ break;
+ default:
+ set_btree_type(TYP_BNOBT);
+ break;
+ }
+ }
+
+ scan_ag(cur_agno);
+
+ return 0;
+}
+
+void
+treedump_init(void)
+{
+ add_command(&treedump_cmd);
+}
+
+static void
+scan_ag(
+ xfs_agnumber_t agno)
+{
+ xfs_agf_t *agf;
+ xfs_sb_t tsb;
+ xfs_sb_t *sb = &tsb;
+ typnm_t btype;
+ xfs_btnum_t btree;
+ int i, c;
+ char **argv;
+
+ push_cur(); /* 1 pushed */
+ set_cur(&typtab[TYP_SB],
+ XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+
+ if (!iocur_top->data) {
+ dbprintf(_("can't read superblock for ag %u\n"), agno);
+ goto pop1_out;
+ }
+
+ libxfs_sb_from_disk(sb, iocur_top->data);
+
+ if (sb->sb_magicnum != XFS_SB_MAGIC) {
+ dbprintf(_("bad sb magic # %#x in ag %u\n"),
+ sb->sb_magicnum, agno);
+ }
+
+ if (!xfs_sb_good_version(sb)) {
+ dbprintf(_("bad sb version # %#x in ag %u\n"),
+ sb->sb_versionnum, agno);
+ }
+
+ if (agno == 0 && sb->sb_inprogress != 0) {
+ dbprintf(_("mkfs not completed successfully\n"));
+ }
+
+ push_cur(); /* 2 pushed */
+ set_cur(&typtab[TYP_AGF],
+ XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+
+ if ((agf = iocur_top->data) == NULL) {
+ dbprintf(_("can't read agf block for ag %u\n"), agno);
+ goto pop2_out;
+ }
+ if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
+ dbprintf(_("bad agf magic # %#x in ag %u\n"),
+ be32_to_cpu(agf->agf_magicnum), agno);
+ }
+ if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
+ dbprintf(_("bad agf version # %#x in ag %u\n"),
+ be32_to_cpu(agf->agf_versionnum), agno);
+ }
+
+ dbprintf(_("\nAGF %d\n\n"), agno);
+ argv = breakline("print", &c);
+ command(c, argv);
+
+ for (i = 0; i < TYP_MAP_SIZE; i++) {
+ btype = btype_map[i];
+ if (btype & TYP_MASK) {
+ btree = type_to_btree_table[btype].btnum;
+ scan_sbtree(agf, be32_to_cpu(agf->agf_roots[btree]),
+ be32_to_cpu(agf->agf_levels[btree]),
+ 1, scanfunc_btree, btype);
+ }
+ }
+
+pop2_out:
+ pop_cur();
+pop1_out:
+ pop_cur();
+}
+
+
+static void
+scan_sbtree(
+ xfs_agf_t *agf,
+ xfs_agblock_t root,
+ int nlevels,
+ int isroot,
+ scan_sbtree_f_t func,
+ typnm_t btype)
+{
+ xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
+ push_cur();
+
+ set_cur(&typtab[btype],
+ XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
+ pop_cur();
+ return;
+ }
+ (*func)(iocur_top->data, nlevels - 1, agf, root, isroot, btype);
+ pop_cur();
+}
+
+static int check_magic(
+ struct xfs_btree_block *block,
+ xfs_agnumber_t seqno,
+ xfs_agblock_t bno,
+ typnm_t btype)
+{
+ uint32_t magic = type_to_btree_table[btype].magic;
+ uint32_t crc_magic = type_to_btree_table[btype].crc_magic;
+ char *btree_name = typtab[btype].name;
+
+ if (be32_to_cpu(block->bb_magic) != magic &&
+ be32_to_cpu(block->bb_magic) != crc_magic) {
+ dbprintf(_("bad magic # %#x in %s block %u/%u\n"),
+ be32_to_cpu(block->bb_magic), btree_name, seqno, bno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+scanfunc_btree(
+ struct xfs_btree_block *block,
+ int level,
+ xfs_agf_t *agf,
+ xfs_agblock_t bno,
+ int isroot,
+ typnm_t btype)
+{
+ int i, c;
+ char **argv;
+ xfs_alloc_ptr_t *pp;
+ xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
+
+ if (check_magic(block, seqno, bno, btype))
+ return;
+
+ dbprintf(_("\ncurrent blkno %d\n\n"), bno);
+ if (level == 0) {
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
+ "btbno block %u/%u\n"),
+ be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
+ mp->m_alloc_mxr[0], seqno, bno);
+ return;
+ }
+
+ argv = breakline("print", &c);
+ command(c, argv);
+
+ return;
+ } else {
+ argv = breakline("print", &c);
+ command(c, argv);
+ }
+
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
+ "%u/%u\n"),
+ be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
+ mp->m_alloc_mxr[1], seqno, bno);
+ return;
+ }
+
+ pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
+ for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
+ scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_btree, btype);
+}
diff --git a/db/treedump.h b/db/treedump.h
new file mode 100644
index 0000000..c9dbffc
--- /dev/null
+++ b/db/treedump.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Oracle. All Rights Reserved.
+ *
+ * Author: Shan Hai <shan.hai@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+extern void treedump_init(void);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html