This command allows to inspect raw blocks.
It also shows information about where specified block is read from.
---
 Android.mk              |   3 +-
 Makefile                |   3 +-
 cmds/commands.h         |   1 +
 cmds/inspect-dump-raw.c | 343 ++++++++++++++++++++++++++++++++++++++++
 cmds/inspect.c          |   1 +
 5 files changed, 349 insertions(+), 2 deletions(-)
 create mode 100644 cmds/inspect-dump-raw.c

diff --git a/Android.mk b/Android.mk
index a45e87aa..40873dd5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -31,7 +31,8 @@ cmds_objects := cmds-subvolume.c cmds-filesystem.c 
cmds-device.c cmds-scrub.c \
                cmds-quota.c cmds-qgroup.c cmds-replace.c cmds-check.c \
                cmds-restore.c cmds-rescue.c chunk-recover.c super-recover.c \
                cmds-property.c cmds-fi-usage.c cmds-inspect-dump-tree.c \
-               cmds-inspect-dump-super.c cmds-inspect-tree-stats.c 
cmds-fi-du.c \
+               cmds-inspect-dump-raw.c cmds-inspect-dump-super.c \
+               cmds-inspect-tree-stats.c cmds-fi-du.c \
                mkfs/common.c
 libbtrfs_objects := common/send-stream.c common/send-utils.c 
kernel-lib/rbtree.c btrfs-list.c \
                    crypto/crc32c.c messages.c \
diff --git a/Makefile b/Makefile
index 40c5aee0..ea6e4921 100644
--- a/Makefile
+++ b/Makefile
@@ -165,7 +165,8 @@ cmds_objects = cmds/subvolume.o cmds/filesystem.o 
cmds/device.o cmds/scrub.o \
               cmds/restore.o cmds/rescue.o cmds/rescue-chunk-recover.o \
               cmds/rescue-super-recover.o \
               cmds/property.o cmds/filesystem-usage.o cmds/inspect-dump-tree.o 
\
-              cmds/inspect-dump-super.o cmds/inspect-tree-stats.o 
cmds/filesystem-du.o \
+              cmds/inspect-dump-raw.o cmds/inspect-dump-super.o \
+              cmds/inspect-tree-stats.o cmds/filesystem-du.o \
               mkfs/common.o check/mode-common.o check/mode-lowmem.o
 libbtrfs_objects = common/send-stream.o common/send-utils.o 
kernel-lib/rbtree.o btrfs-list.o \
                   kernel-lib/radix-tree.o common/extent-cache.o 
kernel-shared/extent_io.o \
diff --git a/cmds/commands.h b/cmds/commands.h
index 8fa85d6c..57a976bb 100644
--- a/cmds/commands.h
+++ b/cmds/commands.h
@@ -142,6 +142,7 @@ DECLARE_COMMAND(super_recover);
 DECLARE_COMMAND(inspect);
 DECLARE_COMMAND(inspect_dump_super);
 DECLARE_COMMAND(inspect_dump_tree);
+DECLARE_COMMAND(inspect_dump_raw);
 DECLARE_COMMAND(inspect_tree_stats);
 DECLARE_COMMAND(property);
 DECLARE_COMMAND(send);
diff --git a/cmds/inspect-dump-raw.c b/cmds/inspect-dump-raw.c
new file mode 100644
index 00000000..5f41aea4
--- /dev/null
+++ b/cmds/inspect-dump-raw.c
@@ -0,0 +1,343 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will 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 to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "kerncompat.h"
+#include "kernel-shared/ctree.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/volumes.h"
+#include "common/device-scan.h"
+#include "common/utils.h"
+#include "cmds/commands.h"
+
+static const char * const cmd_inspect_dump_raw_usage[] = {
+       "btrfs inspect-internal dump-raw [options] <device> [<device> ..]",
+       "Dump blocks from a given device in raw form",
+       "",
+       "-b|--block <block_num>   dump specified block",
+       "                         can be specified multiple times",
+       "-m|--mirror <mirror_num> use specific mirror, useful only when they 
differ",
+       "--force                  continue even if block is corrupted",
+       NULL
+};
+
+/*
+ * Helper function to record all tree block bytenr so we don't need to put
+ * all code into deep indent.
+ *
+ * Return >0 if we hit a duplicated bytenr (already recorded)
+ * Return 0 if nothing went wrong
+ * Return <0 if error happens (ENOMEM)
+ *
+ * For != 0 return value, all warning/error will be outputted by this function.
+ */
+static int dump_add_tree_block(struct cache_tree *tree, u64 bytenr)
+{
+       int ret;
+
+       /*
+        * We don't really care about the size and we don't have
+        * nodesize before we open the fs, so just use 1 as size here.
+        */
+       ret = add_cache_extent(tree, bytenr, 1);
+       if (ret == -EEXIST) {
+               warning("tree block bytenr %llu is duplicated", bytenr);
+               return 1;
+       }
+       if (ret < 0) {
+               error("failed to record tree block bytenr %llu: %d(%s)",
+                       bytenr, ret, strerror(-ret));
+               return ret;
+       }
+       return ret;
+}
+
+int read_whole_eb_verbose(struct btrfs_fs_info *info, struct extent_buffer 
*eb, int mirror, u64 bytenr)
+{
+       unsigned long offset = 0;
+       struct btrfs_multi_bio *multi = NULL;
+       struct btrfs_device *device;
+       int ret = 0;
+       u64 read_len;
+       unsigned long bytes_left = eb->len;
+       char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
+
+       while (bytes_left) {
+               read_len = bytes_left;
+               device = NULL;
+
+               ret = btrfs_map_block(info, READ, eb->start + offset,
+                                     &read_len, &multi, mirror, NULL);
+               if (ret) {
+                       printk("Couldn't map the block %Lu\n", eb->start + 
offset);
+                       kfree(multi);
+                       return -EIO;
+               }
+               device = multi->stripes[0].dev;
+
+               if (device->fd <= 0) {
+                       kfree(multi);
+                       return -EIO;
+               }
+
+               eb->fd = device->fd;
+               device->total_ios++;
+               eb->dev_bytenr = multi->stripes[0].physical;
+               kfree(multi);
+               multi = NULL;
+
+
+               if (read_len > bytes_left)
+                       read_len = bytes_left;
+
+               uuid_unparse(device->uuid, uuid_str);
+               fprintf(stderr, "block %llu, mirror %d: reading %llu bytes from 
device %s at offset %llu\n",
+                       bytenr, mirror, read_len, uuid_str, eb->dev_bytenr);
+               ret = read_extent_from_disk(eb, offset, read_len);
+               if (ret)
+                       return -EIO;
+               offset += read_len;
+               bytes_left -= read_len;
+       }
+       return 0;
+}
+
+static struct extent_buffer* read_tree_block_mirror(struct btrfs_fs_info 
*fs_info, u64 bytenr,
+                                                   int mirror_num, bool force)
+{
+       struct extent_buffer *eb;
+       int ret = 0;
+       int num_copies;
+       int current_mirror = 0;
+
+       /*
+        * Please note that here we can't check it against nodesize,
+        * as it's possible a chunk is just aligned to sectorsize but
+        * not aligned to nodesize.
+        */
+       if (!IS_ALIGNED(bytenr, fs_info->sectorsize)) {
+               error("tree block bytenr %llu is not aligned to sectorsize %u",
+                     bytenr, fs_info->sectorsize);
+               return ERR_PTR(-EINVAL);
+       }
+
+       eb = btrfs_find_create_tree_block(fs_info, bytenr);
+       if (!eb)
+               return ERR_PTR(-ENOMEM);
+
+       num_copies = btrfs_num_copies(fs_info, eb->start, eb->len);
+       if (mirror_num >= 0) {
+               if (mirror_num >= num_copies) {
+                       error("block %llu has only %d mirror(s), can't use 
specified mirror",
+                              bytenr, num_copies);
+
+                       ret = -EINVAL;
+                       goto error;
+               }
+               fprintf(stderr, "block %llu: using mirror %d from %d 
mirrors\n", bytenr, mirror_num, num_copies);
+               current_mirror = mirror_num;
+       }
+
+       while (current_mirror < num_copies) {
+               ret = read_whole_eb_verbose(fs_info, eb, current_mirror, 
bytenr);
+               if (ret == 0) {
+                       if (csum_tree_block(fs_info, eb, 1) == 0 ||
+                           (force && (mirror_num >= 0 || current_mirror + 1 == 
num_copies))
+                       ) {
+                               btrfs_set_buffer_uptodate(eb);
+                               return eb;
+                       }
+                       ret = -EIO;
+               }
+               if (mirror_num >= 0) {
+                       break;
+               }
+               current_mirror++;
+       }
+
+error:
+       /*
+        * We failed to read this tree block, it be should deleted right now
+        * to avoid stale cache populate the cache.
+        */
+       free_extent_buffer_nocache(eb);
+       return ERR_PTR(ret);
+}
+
+/*
+ *
+ * Return 0 if nothing wrong happened for *each* tree blocks
+ * Return <0 if anything wrong happened, and return value will be the last
+ * error.
+ */
+static int dump_raw_blocks(struct btrfs_fs_info *fs_info,
+                          struct cache_tree *tree, int mirror_num, bool force)
+{
+       FILE *out = stdout;
+       struct cache_extent *ce;
+       struct extent_buffer *eb;
+       u64 bytenr;
+       int ret = 0;
+       int i = 0;
+
+
+       ce = first_cache_extent(tree);
+       while (ce) {
+               bytenr = ce->start;
+
+               eb = read_tree_block_mirror(fs_info, bytenr, mirror_num, force);
+               if (!extent_buffer_uptodate(eb)) {
+                       error("failed to read tree block %llu", bytenr);
+                       ret = -EIO;
+                       goto next;
+               }
+
+               if (isatty(fileno(out))) {
+                       for (i = 0; i < eb->len; i++) {
+                               if (i % 16 == 0) {
+                                       fprintf(out, "\n%08x  ", i);
+                               } else if ((i + 8) % 16 == 0) {
+                                       fprintf(out, " ");
+                               }
+                               fprintf(out, "%02x ", eb->data[i] & 0xff);
+                       }
+                       fprintf(out, "\n");
+               } else {
+                       fwrite(eb->data, eb->len, 1, out);
+               }
+
+               free_extent_buffer(eb);
+next:
+               remove_cache_extent(tree, ce);
+               free(ce);
+               ce = first_cache_extent(tree);
+       }
+       return ret;
+}
+
+static int cmd_inspect_dump_raw(const struct cmd_struct *cmd,
+                               int argc, char **argv)
+{
+       struct btrfs_fs_info *info;
+       struct cache_tree block_root;   /* for multiple --block parameters */
+       int ret = 0;
+       int dev_optind;
+       unsigned open_ctree_flags;
+       u64 block_bytenr;
+       int mirror_num = -1;
+       bool force = false;
+
+       open_ctree_flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_NO_BLOCK_GROUPS;
+       cache_tree_init(&block_root);
+       optind = 0;
+       while (1) {
+               int c;
+               static const struct option long_options[] = {
+                       { "block", required_argument, NULL, 'b'},
+                       { "mirror", required_argument, NULL, 'm'},
+                       { "force", no_argument, NULL, 'f'},
+                       { NULL, 0, NULL, 0 }
+               };
+
+               c = getopt_long(argc, argv, "b:m:", long_options, NULL);
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'b':
+                       /*
+                        * No need to fill roots other than chunk root
+                        */
+                       open_ctree_flags |= __OPEN_CTREE_RETURN_CHUNK_ROOT;
+                       block_bytenr = arg_strtou64(optarg);
+                       ret = dump_add_tree_block(&block_root, block_bytenr);
+                       if (ret < 0)
+                               goto out;
+                       break;
+               case 'm':
+                       mirror_num = arg_strtou64(optarg);
+                       break;
+               case 'f':
+                       force = true;
+                       break;
+               default:
+                       usage_unknown_option(cmd, argv);
+               }
+       }
+
+       if (check_argc_min(argc - optind, 1))
+               return 1;
+
+       dev_optind = optind;
+       while (dev_optind < argc) {
+               int fd;
+               struct btrfs_fs_devices *fs_devices;
+               u64 num_devices;
+
+               ret = check_arg_type(argv[optind]);
+               if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
+                       if (ret < 0) {
+                               errno = -ret;
+                               error("invalid argument %s: %m", 
argv[dev_optind]);
+                       } else {
+                               error("not a block device or regular file: %s",
+                                      argv[dev_optind]);
+                       }
+               }
+               fd = open(argv[dev_optind], O_RDONLY);
+               if (fd < 0) {
+                       error("cannot open %s: %m", argv[dev_optind]);
+                       return -EINVAL;
+               }
+               ret = btrfs_scan_one_device(fd, argv[dev_optind], &fs_devices,
+                                           &num_devices,
+                                           BTRFS_SUPER_INFO_OFFSET,
+                                           SBREAD_DEFAULT);
+               close(fd);
+               if (ret < 0) {
+                       errno = -ret;
+                       error("device scan %s: %m", argv[dev_optind]);
+                       return ret;
+               }
+               dev_optind++;
+       }
+
+       fprintf(stderr, "%s\n", PACKAGE_STRING);
+
+       info = open_ctree_fs_info(argv[optind], 0, 0, 0, open_ctree_flags);
+       if (!info) {
+               error("unable to open %s", argv[optind]);
+               ret = -EIO;
+               goto out;
+       }
+
+       if (!cache_tree_empty(&block_root)) {
+               ret = dump_raw_blocks(info, &block_root, mirror_num, force);
+       } else {
+               error("No blocks specified");
+               ret = -EINVAL;
+       }
+
+       close_ctree(info->chunk_root);
+out:
+       return !!ret;
+}
+DEFINE_SIMPLE_COMMAND(inspect_dump_raw, "dump-raw");
diff --git a/cmds/inspect.c b/cmds/inspect.c
index 15f19c8a..2d133830 100644
--- a/cmds/inspect.c
+++ b/cmds/inspect.c
@@ -693,6 +693,7 @@ static const struct cmd_group inspect_cmd_group = {
                &cmd_struct_inspect_min_dev_size,
                &cmd_struct_inspect_dump_tree,
                &cmd_struct_inspect_dump_super,
+               &cmd_struct_inspect_dump_raw,
                &cmd_struct_inspect_tree_stats,
                NULL
        }
-- 
2.30.1

Reply via email to