]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_db: dump metadata btrees via 'btdump'
authorDarrick J. Wong <darrick.wong@oracle.com>
Tue, 2 May 2017 16:12:57 +0000 (11:12 -0500)
committerEric Sandeen <sandeen@redhat.com>
Tue, 2 May 2017 16:12:57 +0000 (11:12 -0500)
Introduce a new 'btdump' command that can print the contents of all
blocks of any metadata subtree in the filesystem.  This enables
developers and forensic analyst to view a metadata structure without
having to navigate the btree manually.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
db/Makefile
db/btdump.c [new file with mode: 0644]
db/command.c
db/command.h
man/man8/xfs_db.8

index cdc0b99f38067cb922af8e299a4466360b27ae0a..6618bff2dd1ae451691bade9709c9f36eb8d5cbf 100644 (file)
@@ -13,7 +13,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.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
-CFILES = $(HFILES:.h=.c)
+CFILES = $(HFILES:.h=.c) btdump.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
 LLDLIBS        = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
diff --git a/db/btdump.c b/db/btdump.c
new file mode 100644 (file)
index 0000000..3b76e17
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "output.h"
+#include "init.h"
+#include "io.h"
+#include "type.h"
+#include "input.h"
+
+static void
+btdump_help(void)
+{
+       dbprintf(_(
+"\n"
+" If the cursor points to a btree block, 'btdump' dumps the btree\n"
+" downward from that block.  If the cursor points to an inode,\n"
+" the data fork btree root is selected by default.\n"
+"\n"
+" Options:\n"
+"   -a -- Display an inode's extended attribute fork btree.\n"
+"   -i -- Print internal btree nodes.\n"
+"\n"
+));
+
+}
+
+static int
+eval(
+       const char      *fmt, ...)
+{
+       va_list         ap;
+       char            buf[PATH_MAX];
+       char            **v;
+       int             c;
+       int             ret;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+
+       v = breakline(buf, &c);
+       ret = command(c, v);
+       free(v);
+       return ret;
+}
+
+static bool
+btblock_has_rightsib(
+       struct xfs_btree_block  *block,
+       bool                    long_format)
+{
+       if (long_format)
+               return block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK);
+       return block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK);
+}
+
+static int
+dump_btlevel(
+       int                     level,
+       bool                    long_format)
+{
+       xfs_daddr_t             orig_daddr = iocur_top->bb;
+       xfs_daddr_t             last_daddr;
+       unsigned int            nr;
+       int                     ret;
+
+       ret = eval("push");
+       if (ret)
+               return ret;
+
+       nr = 1;
+       do {
+               last_daddr = iocur_top->bb;
+               dbprintf(_("%s level %u block %u daddr %llu\n"),
+                        iocur_top->typ->name, level, nr, last_daddr);
+               if (level > 0) {
+                       ret = eval("print keys");
+                       if (ret)
+                               goto err;
+                       ret = eval("print ptrs");
+               } else {
+                       ret = eval("print recs");
+               }
+               if (ret)
+                       goto err;
+               if (btblock_has_rightsib(iocur_top->data, long_format)) {
+                       ret = eval("addr rightsib");
+                       if (ret)
+                               goto err;
+               }
+               nr++;
+       } while (iocur_top->bb != orig_daddr && iocur_top->bb != last_daddr);
+
+       ret = eval("pop");
+       return ret;
+err:
+       eval("pop");
+       return ret;
+}
+
+static int
+dump_btree(
+       bool            dump_node_blocks,
+       bool            long_format)
+{
+       xfs_daddr_t     orig_daddr = iocur_top->bb;
+       xfs_daddr_t     last_daddr;
+       int             level;
+       int             ret;
+
+       ret = eval("push");
+       if (ret)
+               return ret;
+
+       cur_agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
+       level = xfs_btree_get_level(iocur_top->data);
+       do {
+               last_daddr = iocur_top->bb;
+               if (level > 0) {
+                       if (dump_node_blocks) {
+                               ret = dump_btlevel(level, long_format);
+                               if (ret)
+                                       goto err;
+                       }
+                       ret = eval("addr ptrs[1]");
+               } else {
+                       ret = dump_btlevel(level, long_format);
+               }
+               if (ret)
+                       goto err;
+               level--;
+       } while (level >= 0 &&
+                iocur_top->bb != orig_daddr &&
+                iocur_top->bb != last_daddr);
+
+       ret = eval("pop");
+       return ret;
+err:
+       eval("pop");
+       return ret;
+}
+
+static inline int dump_btree_short(bool dump_node_blocks)
+{
+       return dump_btree(dump_node_blocks, false);
+}
+
+static inline int dump_btree_long(bool dump_node_blocks)
+{
+       return dump_btree(dump_node_blocks, true);
+}
+
+static int
+dump_inode(
+       bool                    dump_node_blocks,
+       bool                    attrfork)
+{
+       char                    *prefix;
+       struct xfs_dinode       *dip;
+       int                     ret;
+
+       if (attrfork)
+               prefix = "a.bmbt";
+       else if (xfs_sb_version_hascrc(&mp->m_sb))
+               prefix = "u3.bmbt";
+       else
+               prefix = "u.bmbt";
+
+       dip = iocur_top->data;
+       if (attrfork) {
+               if (!dip->di_anextents ||
+                   dip->di_aformat != XFS_DINODE_FMT_BTREE) {
+                       dbprintf(_("attr fork not in btree format\n"));
+                       return 0;
+               }
+       } else {
+               if (!dip->di_nextents ||
+                   dip->di_format != XFS_DINODE_FMT_BTREE) {
+                       dbprintf(_("data fork not in btree format\n"));
+                       return 0;
+               }
+       }
+
+       ret = eval("push");
+       if (ret)
+               return ret;
+
+       if (dump_node_blocks) {
+               ret = eval("print %s.keys", prefix);
+               if (ret)
+                       goto err;
+               ret = eval("print %s.ptrs", prefix);
+               if (ret)
+                       goto err;
+       }
+
+       ret = eval("addr %s.ptrs[1]", prefix);
+       if (ret)
+               goto err;
+
+       ret = dump_btree_long(dump_node_blocks);
+       if (ret)
+               goto err;
+
+       ret = eval("pop");
+       return ret;
+err:
+       eval("pop");
+       return ret;
+}
+
+static int
+btdump_f(
+       int             argc,
+       char            **argv)
+{
+       bool            aflag = false;
+       bool            iflag = false;
+       int             c;
+
+       if (cur_typ == NULL) {
+               dbprintf(_("no current type\n"));
+               return 0;
+       }
+       while ((c = getopt(argc, argv, "ai")) != EOF) {
+               switch (c) {
+               case 'a':
+                       aflag = true;
+                       break;
+               case 'i':
+                       iflag = true;
+                       break;
+               default:
+                       dbprintf(_("bad option for btdump command\n"));
+                       return 0;
+               }
+       }
+
+       if (optind != argc) {
+               dbprintf(_("bad options for btdump command\n"));
+               return 0;
+       }
+       if (aflag && cur_typ->typnm != TYP_INODE) {
+               dbprintf(_("attrfork flag doesn't apply here\n"));
+               return 0;
+       }
+
+       switch (cur_typ->typnm) {
+       case TYP_BNOBT:
+       case TYP_CNTBT:
+       case TYP_INOBT:
+       case TYP_FINOBT:
+       case TYP_RMAPBT:
+       case TYP_REFCBT:
+               return dump_btree_short(iflag);
+       case TYP_BMAPBTA:
+       case TYP_BMAPBTD:
+               return dump_btree_long(iflag);
+       case TYP_INODE:
+               return dump_inode(iflag, aflag);
+       default:
+               dbprintf(_("type \"%s\" is not a btree type or inode\n"),
+                               cur_typ->name);
+               return 0;
+       }
+}
+
+static const cmdinfo_t btdump_cmd =
+       { "btdump", "b", btdump_f, 0, 2, 0, "[-a] [-i]",
+         N_("dump btree"), btdump_help };
+
+void
+btdump_init(void)
+{
+       add_command(&btdump_cmd);
+}
index 3d7cfd71f40016869fb6b6d55eb2faa90de22f7b..c90c85c560ca3e84a2746bc66c0cc5fdde2f8e5c 100644 (file)
@@ -124,6 +124,7 @@ init_commands(void)
        attrset_init();
        block_init();
        bmap_init();
+       btdump_init();
        check_init();
        convert_init();
        crc_init();
index 4d4807d6f24656b66da4782a8aca848dfe19d263..9b4ed2d7d155512b6d95389599b86e6cdf08f0e2 100644 (file)
@@ -39,3 +39,5 @@ extern void           add_command(const cmdinfo_t *ci);
 extern int             command(int argc, char **argv);
 extern const cmdinfo_t *find_command(const char *cmd);
 extern void            init_commands(void);
+
+extern void            btdump_init(void);
index ef7cf843c3689d1f5c6bc79ebd38b7e5f3176eb4..50da54eb824fbf9addb5a9dd3a364761d9541dad 100644 (file)
@@ -332,6 +332,19 @@ and
 options are used to select the attribute or data
 area of the inode, if neither option is given then both areas are shown.
 .TP
+.B btdump [-a] [-i]
+If the cursor points to a btree node, dump the btree from that block downward.
+If instead the cursor points to an inode, dump the data fork block mapping btree if there is one.
+By default, only records stored in the btree are dumped.
+.RS 1.0i
+.TP 0.4i
+.B \-a
+If the cursor points at an inode, dump the extended attribute block mapping btree, if present.
+.TP
+.B \-i
+Dump all keys and pointers in intermediate btree nodes, and all records in leaf btree nodes.
+.RE
+.TP
 .B check
 See the
 .B blockget