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