[RFC PATCH 1/1] xfsprogs/db: add a command for dumping the btree blocks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently there is no way to dump the whole blocks of btrees in the
xfs_db except manually step through the btree nodes, dumping the blocks
of the whole btree by a command is more convenient than interactive
walking of the tree in some circumstances.

This patch adds a new command to the xfs_db utility called 'treedump',
which can dump the specific btree or dump all btrees of the xfs, below
is an example usage of the command:

sudo xfs_db -r -c "agf 0" -c "treedump -b" /dev/sda1

The blocks of the bnobt tree are dumped to the stdout.

Signed-off-by: Shan Hai <shan.hai@xxxxxxxxxx>
---
 db/Makefile   |   4 +-
 db/command.c  |   1 +
 db/treedump.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 db/treedump.h |  20 ++++
 4 files changed, 329 insertions(+), 2 deletions(-)
 create mode 100644 db/treedump.c
 create mode 100644 db/treedump.h

diff --git a/db/Makefile b/db/Makefile
index cdc0b99..03a2283 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -8,8 +8,8 @@ include $(TOPDIR)/include/builddefs
 LTCOMMAND = xfs_db
 
 HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
-	btblock.h bmroot.h check.h command.h convert.h crc.h debug.h \
-	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
+	btblock.h bmroot.h treedump.h check.h command.h convert.h crc.h \
+	debug.h dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
 	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
 	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
 	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
diff --git a/db/command.c b/db/command.c
index 3d7cfd7..060ebed 100644
--- a/db/command.c
+++ b/db/command.c
@@ -143,6 +143,7 @@ init_commands(void)
 	print_init();
 	quit_init();
 	sb_init();
+	treedump_init();
 	type_init();
 	write_init();
 	dquot_init();
diff --git a/db/treedump.c b/db/treedump.c
new file mode 100644
index 0000000..8d5a33e
--- /dev/null
+++ b/db/treedump.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * 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
+ */
+
+#include "libxfs.h"
+#include <math.h>
+#include <sys/time.h>
+#include "command.h"
+#include "io.h"
+#include "type.h"
+#include "fprint.h"
+#include "faddr.h"
+#include "field.h"
+#include "sb.h"
+#include "input.h"
+#include "output.h"
+#include "init.h"
+#include "malloc.h"
+
+#define TYP_MASK ( \
+	TYP_AGF | TYP_AGFL |  TYP_AGI | TYP_ATTR \
+	| TYP_BMAPBTA | TYP_BMAPBTD | TYP_BNOBT | TYP_CNTBT \
+	| TYP_RMAPBT | TYP_REFCBT | TYP_DATA | TYP_DIR2 \
+	| TYP_DQBLK | TYP_INOBT | TYP_INODATA | TYP_INODE \
+	| TYP_LOG | TYP_RTBITMAP | TYP_RTSUMMARY | TYP_SB \
+	| TYP_SYMLINK | TYP_TEXT | TYP_FINOBT | TYP_NONE)
+
+static struct btree_types {
+	xfs_btnum_t	btnum;
+	uint32_t	magic;
+	uint32_t	crc_magic;
+} const type_to_btree_table[TYP_NONE] = {
+	[0 ... TYP_NONE - 1]	= { XFS_BTNUM_MAX,  -1, -1 },
+
+	[TYP_BNOBT]		= { XFS_BTNUM_BNO,
+				    XFS_ABTB_MAGIC, XFS_ABTB_CRC_MAGIC },
+	[TYP_CNTBT]		= { XFS_BTNUM_CNT,
+				    XFS_ABTC_MAGIC, XFS_ABTC_CRC_MAGIC },
+	[TYP_RMAPBT]		= { XFS_BTNUM_RMAP,
+				    XFS_RMAP_CRC_MAGIC, XFS_RMAP_CRC_MAGIC },
+	[TYP_REFCBT]		= { XFS_BTNUM_REFC,
+				    XFS_REFC_CRC_MAGIC, XFS_REFC_CRC_MAGIC },
+};
+
+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



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux