btrfs-progs: inspect-dump-tree: Allow --block to be specified multiple times

Reuse extent-cache facility to record multiple bytenr so --block can be
specified multiple times.

Despite that, add a sector size alignment check before we try to print a
tree block.  Please note that, nodesize alignment check is not suitable
here as meta chunk start bytenr could be unaligned to nodesize.

Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
master
Qu Wenruo 2018-06-01 16:27:46 +08:00 committed by David Sterba
parent 7a12d8470e
commit f312aff23f
2 changed files with 91 additions and 20 deletions

View File

@ -86,7 +86,7 @@ the respective tree root block offset
-u|--uuid::::
print only the uuid tree information, empty output if the tree does not exist
-b <block_num>::::
print info of the specified block only
print info of the specified block only, can be specified multiple times
--follow::::
use with '-b', print all children tree blocks of '<block_num>'
--dfs::::

View File

@ -198,11 +198,92 @@ const char * const cmd_inspect_dump_tree_usage[] = {
"-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",
" can be specified multiple times",
"-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
};
/*
* 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;
}
/*
* Print all tree blocks recorded.
* All tree block bytenr record will also be freed in this function.
*
* 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_print_tree_blocks(struct btrfs_fs_info *fs_info,
struct cache_tree *tree, bool follow)
{
struct cache_extent *ce;
struct extent_buffer *eb;
u64 bytenr;
int ret = 0;
ce = first_cache_extent(tree);
while (ce) {
bytenr = ce->start;
/*
* 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);
ret = -EINVAL;
goto next;
}
eb = read_tree_block(fs_info, bytenr, 0);
if (!extent_buffer_uptodate(eb)) {
error("failed to read tree block %llu", bytenr);
ret = -EIO;
goto next;
}
btrfs_print_tree(eb, follow, BTRFS_PRINT_TREE_DEFAULT);
free_extent_buffer(eb);
next:
remove_cache_extent(tree, ce);
free(ce);
ce = first_cache_extent(tree);
}
return ret;
}
int cmd_inspect_dump_tree(int argc, char **argv)
{
struct btrfs_root *root;
@ -213,6 +294,7 @@ int cmd_inspect_dump_tree(int argc, char **argv)
struct extent_buffer *leaf;
struct btrfs_disk_key disk_key;
struct btrfs_key found_key;
struct cache_tree block_root; /* for multiple --block parameters */
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
int ret;
int slot;
@ -223,7 +305,7 @@ int cmd_inspect_dump_tree(int argc, char **argv)
int root_backups = 0;
int traverse = BTRFS_PRINT_TREE_DEFAULT;
unsigned open_ctree_flags;
u64 block_only = 0;
u64 block_bytenr;
struct btrfs_root *tree_root_scan;
u64 tree_id = 0;
bool follow = false;
@ -236,6 +318,7 @@ int cmd_inspect_dump_tree(int argc, char **argv)
* tree blocks as possible.
*/
open_ctree_flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_NO_BLOCK_GROUPS;
cache_tree_init(&block_root);
optind = 0;
while (1) {
int c;
@ -281,7 +364,10 @@ int cmd_inspect_dump_tree(int argc, char **argv)
* other than chunk root
*/
open_ctree_flags |= __OPEN_CTREE_RETURN_CHUNK_ROOT;
block_only = arg_strtou64(optarg);
block_bytenr = arg_strtou64(optarg);
ret = dump_add_tree_block(&block_root, block_bytenr);
if (ret < 0)
goto out;
break;
case 't': {
const char *end = NULL;
@ -341,24 +427,9 @@ int cmd_inspect_dump_tree(int argc, char **argv)
goto out;
}
if (block_only) {
if (!cache_tree_empty(&block_root)) {
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(info, block_only, 0);
if (!extent_buffer_uptodate(leaf)) {
error("failed to read %llu",
(unsigned long long)block_only);
goto close_root;
}
btrfs_print_tree(leaf, follow, BTRFS_PRINT_TREE_DEFAULT);
free_extent_buffer(leaf);
ret = dump_print_tree_blocks(info, &block_root, follow);
goto close_root;
}