On 2017年04月11日 22:37, Shan Hai wrote:
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 "addr bnoroot" -c "treedump" /dev/sda1
The blocks of the bnobt tree are dumped to the stdout.
Signed-off-by: Shan Hai <shan.hai@xxxxxxxxxx>
---
db/Makefile | 10 +-
db/command.c | 2 +
db/treedump.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
db/treedump.h | 21 ++++
4 files changed, 389 insertions(+), 5 deletions(-)
create mode 100644 db/treedump.c
create mode 100644 db/treedump.h
diff --git a/db/Makefile b/db/Makefile
index cdc0b99..81eb4c4 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -8,11 +8,11 @@ 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 \
- 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
+ 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 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 treedump.h type.h write.h attrset.h symlink.h fsmap.h
CFILES = $(HFILES:.h=.c)
LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
diff --git a/db/command.c b/db/command.c
index 3d7cfd7..73568b5 100644
--- a/db/command.c
+++ b/db/command.c
@@ -46,6 +46,7 @@
#include "print.h"
#include "quit.h"
#include "sb.h"
+#include "treedump.h"
#include "write.h"
#include "malloc.h"
#include "dquot.h"
@@ -143,6 +144,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..ee728da
--- /dev/null
+++ b/db/treedump.c
@@ -0,0 +1,361 @@
+/*
+ * 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 "command.h"
+#include "io.h"
+#include "type.h"
+#include "fprint.h"
+#include "faddr.h"
+#include "field.h"
+#include "input.h"
+#include "output.h"
+#include "init.h"
+
+static int
+treedump_f(
+ int argc,
+ char **argv);
+
+static void
+dump_level(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block);
+
+static void
+dump_leaves(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block);
+
+static void
+dump_btree(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block);
+
+static void treedump_help(void);
+
+static const cmdinfo_t treedump_cmd =
+ { "treedump", NULL, treedump_f, 0, 2, 0,
+ N_("[-d] [-a]"),
+ N_("dump the btree blocks"), treedump_help };
+
+static void
+treedump_help(void)
+{
+ dbprintf(_(
+"\n"
+" 'treedump' prints the blocks of the specified filesystem btree\n"
+"\n"
+" Examples:\n"
+"\n"
+" agf agfno\n"
+" addr bnoroot\n"
+" treedump\n"
+"\n"
+" inode ino\n"
+" treedump -d (data fork)\n"
+" treedump -a (attr fork)\n"
+"\n"
+));
+
+}
+
+static void run_command(char *str)
+{
+ int c;
+ char **argv;
+ char cmd[1024];
+
+ strcpy(cmd, str);
+ argv = breakline(cmd, &c);
+ command(c, argv);
+}
+
+static void
+dump_fork(int whichfork)
+{
+ xfs_fsblock_t fsbno;
+ xfs_dinode_t *dip;
+ xfs_bmdr_block_t *dib;
+ xfs_bmdr_key_t *kp;
+ xfs_bmdr_ptr_t *pp;
+ int level;
+ int nrecs;
+ int maxrecs;
+ int i;
+
+ dip = iocur_top->data;
+ dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
+ level = be16_to_cpu(dib->bb_level);
+ nrecs = be16_to_cpu(dib->bb_numrecs);
+
+ maxrecs = libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0);
+ if (maxrecs <= 0) {
+ dbprintf(_("an empty (%s) fork in inode %lld\n"),
+ (whichfork == XFS_DATA_FORK) ? "data" : "attr",
+ (long long)iocur_top->ino);
+ return;
+ }
+
+ if (nrecs > maxrecs) {
+ dbprintf(_("invalid numrecs (%u) in inode %lld\n"),
+ nrecs, (long long)iocur_top->ino);
+ return;
+ }
+
+ pp = XFS_BMDR_PTR_ADDR(dib, 1, maxrecs);
+
+ dbprintf(_("level = %d\n"), level);
+ dbprintf(_("keys[1-%d] = [startoff] "), nrecs);
+ for (i = 1; i <= nrecs; i++) {
+ kp = XFS_BMDR_KEY_ADDR(dib, i);
+ dbprintf(_("%d:[%ld] "), i, be64_to_cpu(kp->br_startoff));
+ }
+
+ dbprintf(_("\nptrs[1-%d] = "), nrecs);
+ for (i = 0; i < nrecs; i++)
+ dbprintf(_("%d:%ld "), i+1, be64_to_cpu(pp[i]));
+
+ dbprintf(_("\n"));
+
+ push_cur();
+ for (i = 0; i < nrecs; i++) {
+ fsbno = be64_to_cpu(pp[i]);
+ set_cur(&typtab[TYP_BMAPBTD], XFS_FSB_TO_DADDR(mp, fsbno),
+ blkbb, DB_RING_IGN, NULL);
+ if (!iocur_top->data) {
+ dbprintf(_("cannot read fsblock %lu\n"), fsbno);
+ return;
+ }
+ dump_btree(cur_agno, iocur_top->data);
+ }
+ pop_cur();
+}
+
+/* ARGSUSED */
+static int
+treedump_f(
+ int argc,
+ char **argv)
+{
+ int c;
+ int whichfork = -1;
+
+ if (!iocur_top->data) {
+ dbprintf(_("no btree/inode \n"));
+ return 0;
+ }
+
+ /* btrees except inode data/attr fork */
+ if (cur_typ->typnm != TYP_INODE) {
+ dump_btree(cur_agno, iocur_top->data);
+ return 0;
+ }
+
+ /* below is for inode data/attr fork */
+ while ((c = getopt(argc, argv, "ad")) != EOF) {
+ switch (c) {
+ case 'a':
+ whichfork = XFS_ATTR_FORK;
+ break;
+ case 'd':
+ whichfork = XFS_DATA_FORK;
+ break;
+ default:
+ dbprintf(_("bad option for treedump command\n"));
+ return 0;
+ }
+ }
+
+ /* all/empty options */
+ if (argc > 2 || whichfork < 0) {
+ dump_fork(XFS_DATA_FORK);
+ dump_fork(XFS_ATTR_FORK);
+ } else {
+ dump_fork(whichfork);
+ }
+
+ return 0;
+}
+
+void
+treedump_init(void)
+{
+ add_command(&treedump_cmd);
+}
+
+static void dump_level(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block)
+{
+ xfs_agblock_t agbno;
+ const typ_t *typ = cur_typ;
+
+ run_command("print level keys ptrs");
+
+ push_cur();
+ for (;;) {
+ agbno = be32_to_cpu(block->bb_u.s.bb_rightsib);
+ if (agbno == NULLAGBLOCK)
+ break;
+
+ set_cur(&typtab[typ->typnm], XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+
+ if (!iocur_top->data) {
+ dbprintf(_("cannot read block %u/%u\n"), agno, agbno);
+ return;
+ }
+
+ run_command("print keys ptrs");
+
+ block = iocur_top->data;
+ typ = cur_typ;
+ }
+ pop_cur();
+}
+
+static void dump_leaves(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block)
+{
+ xfs_agblock_t agbno;
+ const typ_t *typ = cur_typ;
+
+ for (;;) {
+ run_command("print level recs");
+
+ agbno = be32_to_cpu(block->bb_u.s.bb_rightsib);
+ if (agbno == NULLAGBLOCK)
+ break;
+
+ set_cur(&typtab[typ->typnm], XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+
+ if (!iocur_top->data) {
+ dbprintf(_("cannot read block %u/%u\n"), agno, agbno);
+ return;
+ }
+
+ block = iocur_top->data;
+ typ = cur_typ;
+ }
+}
+
+static void
+dump_btree(
+ xfs_agnumber_t agno,
+ struct xfs_btree_block *block)
+{
+ int i;
+ const typ_t *typ = cur_typ;
+ xfs_agblock_t agbno = -1;
+ int level = be16_to_cpu(block->bb_level);
+ xfs_alloc_ptr_t *allocp;
+ xfs_inobt_ptr_t *inobtp;
+ xfs_refcount_ptr_t *refcntp;
+ xfs_rmap_ptr_t *rmapp;
+
+ int magic;
+
+ for (i = level; i > 0; i--) {
+ dump_level(agno, block);
+
+ agbno = be32_to_cpu(block->bb_u.s.bb_leftsib);
+ if (agbno == NULLAGBLOCK)
+ break;
+
Oops, should walk down a level, fix it in the v3 patch.
Thanks
Shan Hai
+ set_cur(&typtab[typ->typnm], XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+
+ if (!iocur_top->data) {
+ dbprintf(_("cannot read block %u/%u\n"), agno, agbno);
+ return;
+ }
+
+ block = iocur_top->data;
+ typ = cur_typ;
+ }
+
+ level = be16_to_cpu(block->bb_level);
+ magic = be32_to_cpu(block->bb_magic);
+
+ switch (magic) {
+ case XFS_ABTB_MAGIC:
+ case XFS_ABTB_CRC_MAGIC:
+ case XFS_ABTC_MAGIC:
+ case XFS_ABTC_CRC_MAGIC:
+ allocp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
+ agbno = be32_to_cpu(allocp[0]);
+ if (level > 0)
+ set_cur(&typtab[TYP_BNOBT],
+ XFS_AGB_TO_DADDR(mp, agno, agbno), blkbb,
+ DB_RING_IGN, NULL);
+ break;
+ case XFS_BMAP_MAGIC:
+ case XFS_BMAP_CRC_MAGIC:
+ /* nothing */
+ break;
+ case XFS_FIBT_MAGIC:
+ case XFS_FIBT_CRC_MAGIC:
+ inobtp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]);
+ agbno = be32_to_cpu(inobtp[0]);
+ if (level > 0)
+ set_cur(&typtab[TYP_INOBT],
+ XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+ break;
+ case XFS_IBT_MAGIC:
+ case XFS_IBT_CRC_MAGIC:
+ inobtp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]);
+ agbno = be32_to_cpu(inobtp[0]);
+ if (level > 0)
+ set_cur(&typtab[TYP_INOBT],
+ XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+ break;
+ case XFS_REFC_CRC_MAGIC:
+ refcntp = XFS_REFCOUNT_PTR_ADDR(block, 1, mp->m_refc_mxr[1]);
+ agbno = be32_to_cpu(refcntp[0]);
+ if (level > 0)
+ set_cur(&typtab[TYP_REFCBT],
+ XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+ break;
+ case XFS_RMAP_CRC_MAGIC:
+ rmapp = XFS_RMAP_PTR_ADDR(block, 1, mp->m_rmap_mxr[1]);
+ agbno = be32_to_cpu(rmapp[0]);
+ if (level > 0)
+ set_cur(&typtab[TYP_RMAPBT],
+ XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+ break;
+ default:
+ dbprintf(_("bad magic number %u\n"), magic);
+ return;
+ }
+
+ if (!iocur_top->data) {
+ dbprintf(_("cannot read block %u/%u\n"), agno, agbno);
+ return;
+ }
+
+ dump_leaves(agno, iocur_top->data);
+}
diff --git a/db/treedump.h b/db/treedump.h
new file mode 100644
index 0000000..b8f31e4
--- /dev/null
+++ b/db/treedump.h
@@ -0,0 +1,21 @@
+/*
+ * 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);
--
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