btrfs-progs: sb-mod: add csum_type
[platform/upstream/btrfs-progs.git] / cmds-inspect-dump-tree.c
index 8f36144..0802b31 100644 (file)
 #include "volumes.h"
 #include "commands.h"
 #include "utils.h"
-#include "cmds-inspect-dump-tree.h"
+#include "help.h"
 
 static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
 {
+       struct extent_buffer *next;
        int i;
        u32 nr;
-       u32 size;
 
        if (!eb)
                return;
@@ -47,24 +47,35 @@ static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
                return;
        }
 
-       size = btrfs_level_size(root, btrfs_header_level(eb) - 1);
        nr = btrfs_header_nritems(eb);
        for (i = 0; i < nr; i++) {
-               struct extent_buffer *next = read_tree_block(root,
-                                            btrfs_node_blockptr(eb, i),
-                                            size,
-                                            btrfs_node_ptr_generation(eb, i));
+               next = read_tree_block(root->fs_info,
+                               btrfs_node_blockptr(eb, i),
+                               btrfs_node_ptr_generation(eb, i));
                if (!extent_buffer_uptodate(next))
                        continue;
-               if (btrfs_is_leaf(next) &&
-                   btrfs_header_level(eb) != 1)
-                       BUG();
-               if (btrfs_header_level(next) !=
-                       btrfs_header_level(eb) - 1)
-                       BUG();
+               if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) {
+                       warning(
+       "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+                               i, btrfs_header_level(next),
+                               btrfs_header_level(eb));
+                       goto out;
+               }
+               if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) {
+                       warning(
+       "eb corrupted: item %d eb level %d next level %d, skipping the rest",
+                               i, btrfs_header_level(next),
+                               btrfs_header_level(eb));
+                       goto out;
+               }
                print_extents(root, next);
                free_extent_buffer(next);
        }
+
+       return;
+
+out:
+       free_extent_buffer(next);
 }
 
 static void print_old_roots(struct btrfs_super_block *super)
@@ -106,20 +117,92 @@ static void print_old_roots(struct btrfs_super_block *super)
        }
 }
 
+/*
+ * Convert a tree name from various forms to the numerical id if possible
+ * Accepted forms:
+ * - case does not matter
+ * - same as the key name, BTRFS_ROOT_TREE_OBJECTID
+ * - dtto shortened, BTRFS_ROOT_TREE
+ * - dtto without prefix, ROOT_TREE
+ * - common name, ROOT, CHUNK, EXTENT, ...
+ * - dtto alias, DEVICE for DEV, CHECKSUM for CSUM
+ *
+ * Returns 0 if the tree id was not recognized.
+ */
+static u64 treeid_from_string(const char *str, const char **end)
+{
+       int match = 0;
+       int i;
+       u64 id;
+       static struct treename {
+               const char *name;
+               u64 id;
+       } tn[] = {
+               { "ROOT", BTRFS_ROOT_TREE_OBJECTID },
+               { "EXTENT", BTRFS_EXTENT_TREE_OBJECTID },
+               { "CHUNK", BTRFS_CHUNK_TREE_OBJECTID },
+               { "DEVICE", BTRFS_DEV_TREE_OBJECTID },
+               { "DEV", BTRFS_DEV_TREE_OBJECTID },
+               { "FS", BTRFS_FS_TREE_OBJECTID },
+               { "CSUM", BTRFS_CSUM_TREE_OBJECTID },
+               { "CHECKSUM", BTRFS_CSUM_TREE_OBJECTID },
+               { "QUOTA", BTRFS_QUOTA_TREE_OBJECTID },
+               { "UUID", BTRFS_UUID_TREE_OBJECTID },
+               { "FREE_SPACE", BTRFS_FREE_SPACE_TREE_OBJECTID },
+               { "TREE_LOG_FIXUP", BTRFS_TREE_LOG_FIXUP_OBJECTID },
+               { "TREE_LOG", BTRFS_TREE_LOG_OBJECTID },
+               { "TREE_RELOC", BTRFS_TREE_RELOC_OBJECTID },
+               { "DATA_RELOC", BTRFS_DATA_RELOC_TREE_OBJECTID }
+       };
+
+       if (strncasecmp("BTRFS_", str, strlen("BTRFS_")) == 0)
+               str += strlen("BTRFS_");
+
+       for (i = 0; i < ARRAY_SIZE(tn); i++) {
+               int len = strlen(tn[i].name);
+
+               if (strncasecmp(tn[i].name, str, len) == 0) {
+                       id = tn[i].id;
+                       match = 1;
+                       str += len;
+                       break;
+               }
+       }
+
+       if (!match)
+               return 0;
+
+       if (strncasecmp("_TREE", str, strlen("_TREE")) == 0)
+               str += strlen("_TREE");
+
+       if (strncasecmp("_OBJECTID", str, strlen("_OBJECTID")) == 0)
+               str += strlen("_OBJECTID");
+
+       *end = str;
+
+       return id;
+}
+
 const char * const cmd_inspect_dump_tree_usage[] = {
        "btrfs inspect-internal dump-tree [options] device",
-       "Dump structures from a device",
-       "-e|--extents           print detailed extents info",
-       "-d|--device            print info of btrfs device and root tree dir only",
-       "-r|--roots             print info of roots only",
-       "-R|--backups           print info of roots and root backups",
-       "-u|--uuid              print info of uuid tree only",
-       "-b|--block <block_num> print info of the specified block only",
-       "-t|--tree  <tree_id>   print only the tree with the given id",
+       "Dump tree structures from a given device",
+       "Dump tree structures from a given device in textual form, expand keys to human",
+       "readable equivalents where possible.",
+       "Note: contains file names, consider that if you're asked to send the dump",
+       "for analysis.",
+       "",
+       "-e|--extents           print only extent info: extent and device trees",
+       "-d|--device            print only device info: tree root, chunk and device trees",
+       "-r|--roots             print only short root node info",
+       "-R|--backups           same as --roots plus print backup root info",
+       "-u|--uuid              print only the uuid tree",
+       "-b|--block <block_num> print info from the specified block only",
+       "-t|--tree <tree_id>    print only tree with the given id (string or number)",
+       "--follow               use with -b, to show all children tree blocks of <block_num>",
        NULL
 };
 
-int cmd_inspect_dump_tree(int ac, char **av)
+int cmd_inspect_dump_tree(int argc, char **argv)
 {
        struct btrfs_root *root;
        struct btrfs_fs_info *info;
@@ -137,12 +220,15 @@ int cmd_inspect_dump_tree(int ac, char **av)
        int uuid_tree_only = 0;
        int roots_only = 0;
        int root_backups = 0;
+       unsigned open_ctree_flags = OPEN_CTREE_FS_PARTIAL;
        u64 block_only = 0;
        struct btrfs_root *tree_root_scan;
        u64 tree_id = 0;
+       bool follow = false;
 
        while (1) {
                int c;
+               enum { GETOPT_VAL_FOLLOW = 256 };
                static const struct option long_options[] = {
                        { "extents", no_argument, NULL, 'e'},
                        { "device", no_argument, NULL, 'd'},
@@ -151,10 +237,11 @@ int cmd_inspect_dump_tree(int ac, char **av)
                        { "uuid", no_argument, NULL, 'u'},
                        { "block", required_argument, NULL, 'b'},
                        { "tree", required_argument, NULL, 't'},
+                       { "follow", no_argument, NULL, GETOPT_VAL_FOLLOW },
                        { NULL, 0, NULL, 0 }
                };
 
-               c = getopt_long(ac, av, "deb:rRut:", long_options, NULL);
+               c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL);
                if (c < 0)
                        break;
                switch (c) {
@@ -175,65 +262,86 @@ int cmd_inspect_dump_tree(int ac, char **av)
                        root_backups = 1;
                        break;
                case 'b':
+                       /*
+                        * If only showing one block, no need to fill roots
+                        * other than chunk root
+                        */
+                       open_ctree_flags |= __OPEN_CTREE_RETURN_CHUNK_ROOT;
                        block_only = arg_strtou64(optarg);
                        break;
-               case 't':
-                       tree_id = arg_strtou64(optarg);
+               case 't': {
+                       const char *end = NULL;
+
+                       if (string_is_numerical(optarg))
+                               tree_id = arg_strtou64(optarg);
+                       else
+                               tree_id = treeid_from_string(optarg, &end);
+
+                       if (!tree_id) {
+                               error("unrecognized tree id: %s",
+                                               optarg);
+                               exit(1);
+                       }
+
+                       if (end && *end) {
+                               error("unexpected tree id suffix of '%s': %s",
+                                               optarg, end);
+                               exit(1);
+                       }
+                       break;
+                       }
+               case GETOPT_VAL_FOLLOW:
+                       follow = true;
                        break;
                default:
                        usage(cmd_inspect_dump_tree_usage);
                }
        }
 
-       ac = ac - optind;
-       if (check_argc_exact(ac, 1))
+       if (check_argc_exact(argc - optind, 1))
                usage(cmd_inspect_dump_tree_usage);
 
-       ret = check_arg_type(av[optind]);
+       ret = check_arg_type(argv[optind]);
        if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
-               fprintf(stderr, "'%s' is not a block device or regular file\n",
-                       av[optind]);
+               error("not a block device or regular file: %s", argv[optind]);
                goto out;
        }
 
-       info = open_ctree_fs_info(av[optind], 0, 0, OPEN_CTREE_PARTIAL);
-       if (!info) {
-               fprintf(stderr, "unable to open %s\n", av[optind]);
-               goto out;
-       }
+       printf("%s\n", PACKAGE_STRING);
 
-       root = info->fs_root;
-       if (!root) {
-               fprintf(stderr, "unable to open %s\n", av[optind]);
+       info = open_ctree_fs_info(argv[optind], 0, 0, 0, open_ctree_flags);
+       if (!info) {
+               error("unable to open %s", argv[optind]);
                goto out;
        }
 
        if (block_only) {
-               leaf = read_tree_block(root,
-                                     block_only,
-                                     root->leafsize, 0);
-
+               root = info->chunk_root;
+               leaf = read_tree_block(info, block_only, 0);
                if (extent_buffer_uptodate(leaf) &&
                    btrfs_header_level(leaf) != 0) {
                        free_extent_buffer(leaf);
                        leaf = NULL;
                }
 
-               if (!leaf) {
-                       leaf = read_tree_block(root,
-                                             block_only,
-                                             root->nodesize, 0);
-               }
+               if (!leaf)
+                       leaf = read_tree_block(info, block_only, 0);
                if (!extent_buffer_uptodate(leaf)) {
-                       fprintf(stderr, "failed to read %llu\n",
+                       error("failed to read %llu",
                                (unsigned long long)block_only);
                        goto close_root;
                }
-               btrfs_print_tree(root, leaf, 0);
+               btrfs_print_tree(root, leaf, follow);
                free_extent_buffer(leaf);
                goto close_root;
        }
 
+       root = info->fs_root;
+       if (!root) {
+               error("unable to open %s", argv[optind]);
+               goto out;
+       }
+
        if (!(extent_only || uuid_tree_only || tree_id)) {
                if (roots_only) {
                        printf("root tree: %llu level %d\n",
@@ -242,6 +350,11 @@ int cmd_inspect_dump_tree(int ac, char **av)
                        printf("chunk tree: %llu level %d\n",
                             (unsigned long long)info->chunk_root->node->start,
                             btrfs_header_level(info->chunk_root->node));
+                       if (info->log_root_tree)
+                               printf("log root tree: %llu level %d\n",
+                                      info->log_root_tree->node->start,
+                                       btrfs_header_level(
+                                               info->log_root_tree->node));
                } else {
                        if (info->tree_root->node) {
                                printf("root tree\n");
@@ -254,6 +367,12 @@ int cmd_inspect_dump_tree(int ac, char **av)
                                btrfs_print_tree(info->chunk_root,
                                                 info->chunk_root->node, 1);
                        }
+
+                       if (info->log_root_tree) {
+                               printf("log root tree\n");
+                               btrfs_print_tree(info->log_root_tree,
+                                               info->log_root_tree->node, 1);
+                       }
                }
        }
        tree_root_scan = info->tree_root;
@@ -269,28 +388,44 @@ again:
        if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) {
                if (!info->tree_root->node) {
                        error("cannot print root tree, invalid pointer");
-                       goto no_node;
+                       goto close_root;
                }
                printf("root tree\n");
                btrfs_print_tree(info->tree_root, info->tree_root->node, 1);
-               goto no_node;
+               goto close_root;
        }
 
        if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) {
                if (!info->chunk_root->node) {
                        error("cannot print chunk tree, invalid pointer");
-                       goto no_node;
+                       goto close_root;
                }
                printf("chunk tree\n");
                btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1);
-               goto no_node;
+               goto close_root;
+       }
+
+       if (tree_id && tree_id == BTRFS_TREE_LOG_OBJECTID) {
+               if (!info->log_root_tree) {
+                       error("cannot print log root tree, invalid pointer");
+                       goto close_root;
+               }
+               printf("log root tree\n");
+               btrfs_print_tree(info->log_root_tree, info->log_root_tree->node,
+                                1);
+               goto close_root;
        }
 
        key.offset = 0;
        key.objectid = 0;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       key.type = BTRFS_ROOT_ITEM_KEY;
        ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0);
-       BUG_ON(ret < 0);
+       if (ret < 0) {
+               error("cannot read ROOT_ITEM from tree %llu: %s",
+                       (unsigned long long)tree_root_scan->root_key.objectid,
+                       strerror(-ret));
+               goto close_root;
+       }
        while (1) {
                leaf = path.nodes[0];
                slot = path.slots[0];
@@ -303,18 +438,14 @@ again:
                }
                btrfs_item_key(leaf, &disk_key, path.slots[0]);
                btrfs_disk_key_to_cpu(&found_key, &disk_key);
-               if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
+               if (found_key.type == BTRFS_ROOT_ITEM_KEY) {
                        unsigned long offset;
                        struct extent_buffer *buf;
                        int skip = extent_only | device_only | uuid_tree_only;
 
                        offset = btrfs_item_ptr_offset(leaf, slot);
                        read_extent_buffer(leaf, &ri, offset, sizeof(ri));
-                       buf = read_tree_block(tree_root_scan,
-                                             btrfs_root_bytenr(&ri),
-                                             btrfs_level_size(tree_root_scan,
-                                                       btrfs_root_level(&ri)),
-                                             0);
+                       buf = read_tree_block(info, btrfs_root_bytenr(&ri), 0);
                        if (!extent_buffer_uptodate(buf))
                                goto next;
                        if (tree_id && found_key.objectid != tree_id) {
@@ -414,6 +545,9 @@ again:
                                }
                        }
                        if (extent_only && !skip) {
+                               printf(" tree ");
+                               btrfs_print_key(&disk_key);
+                               printf("\n");
                                print_extents(tree_root_scan, buf);
                        } else if (!skip) {
                                printf(" tree ");
@@ -435,8 +569,7 @@ next:
 no_node:
        btrfs_release_path(&path);
 
-       if (tree_root_scan == info->tree_root &&
-           info->log_root_tree) {
+       if (tree_root_scan == info->tree_root && info->log_root_tree) {
                tree_root_scan = info->log_root_tree;
                goto again;
        }
@@ -454,7 +587,6 @@ no_node:
        uuidbuf[BTRFS_UUID_UNPARSED_SIZE - 1] = '\0';
        uuid_unparse(info->super_copy->fsid, uuidbuf);
        printf("uuid %s\n", uuidbuf);
-       printf("%s\n", PACKAGE_STRING);
 close_root:
        ret = close_ctree(root);
 out: