| /* |
| * Copyright (C) 2007 Oracle. All rights reserved. |
| * |
| * 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 <stdlib.h> |
| #include <unistd.h> |
| #include <uuid/uuid.h> |
| #include "kerncompat.h" |
| #include "radix-tree.h" |
| #include "ctree.h" |
| #include "disk-io.h" |
| #include "print-tree.h" |
| #include "transaction.h" |
| #include "utils.h" |
| |
| static int print_usage(void) |
| { |
| fprintf(stderr, "usage: btrfs-debug-tree [-e] [-d] [-r] [-R] [-u]\n"); |
| fprintf(stderr, " [-b block_num ] device\n"); |
| fprintf(stderr, "\t-e : print detailed extents info\n"); |
| fprintf(stderr, "\t-d : print info of btrfs device and root tree dirs" |
| " only\n"); |
| fprintf(stderr, "\t-r : print info of roots only\n"); |
| fprintf(stderr, "\t-R : print info of roots and root backups\n"); |
| fprintf(stderr, "\t-u : print info of uuid tree only\n"); |
| fprintf(stderr, "\t-b block_num : print info of the specified block" |
| " only\n"); |
| fprintf(stderr, |
| "\t-t tree_id : print only the tree with the given id\n"); |
| fprintf(stderr, "%s\n", PACKAGE_STRING); |
| exit(1); |
| } |
| |
| static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) |
| { |
| int i; |
| u32 nr; |
| u32 size; |
| |
| if (!eb) |
| return; |
| |
| if (btrfs_is_leaf(eb)) { |
| btrfs_print_leaf(root, 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)); |
| 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(); |
| print_extents(root, next); |
| free_extent_buffer(next); |
| } |
| } |
| |
| static void print_old_roots(struct btrfs_super_block *super) |
| { |
| struct btrfs_root_backup *backup; |
| int i; |
| |
| for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) { |
| backup = super->super_roots + i; |
| printf("btrfs root backup slot %d\n", i); |
| printf("\ttree root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_tree_root_gen(backup), |
| (unsigned long long)btrfs_backup_tree_root(backup)); |
| |
| printf("\t\textent root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_extent_root_gen(backup), |
| (unsigned long long)btrfs_backup_extent_root(backup)); |
| |
| printf("\t\tchunk root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_chunk_root_gen(backup), |
| (unsigned long long)btrfs_backup_chunk_root(backup)); |
| |
| printf("\t\tdevice root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_dev_root_gen(backup), |
| (unsigned long long)btrfs_backup_dev_root(backup)); |
| |
| printf("\t\tcsum root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_csum_root_gen(backup), |
| (unsigned long long)btrfs_backup_csum_root(backup)); |
| |
| printf("\t\tfs root gen %llu block %llu\n", |
| (unsigned long long)btrfs_backup_fs_root_gen(backup), |
| (unsigned long long)btrfs_backup_fs_root(backup)); |
| |
| printf("\t\t%llu used %llu total %llu devices\n", |
| (unsigned long long)btrfs_backup_bytes_used(backup), |
| (unsigned long long)btrfs_backup_total_bytes(backup), |
| (unsigned long long)btrfs_backup_num_devices(backup)); |
| } |
| } |
| |
| int main(int ac, char **av) |
| { |
| struct btrfs_root *root; |
| struct btrfs_fs_info *info; |
| struct btrfs_path path; |
| struct btrfs_key key; |
| struct btrfs_root_item ri; |
| struct extent_buffer *leaf; |
| struct btrfs_disk_key disk_key; |
| struct btrfs_key found_key; |
| char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; |
| int ret; |
| int slot; |
| int extent_only = 0; |
| int device_only = 0; |
| int uuid_tree_only = 0; |
| int roots_only = 0; |
| int root_backups = 0; |
| u64 block_only = 0; |
| struct btrfs_root *tree_root_scan; |
| u64 tree_id = 0; |
| |
| radix_tree_init(); |
| |
| while(1) { |
| int c; |
| c = getopt(ac, av, "deb:rRut:"); |
| if (c < 0) |
| break; |
| switch(c) { |
| case 'e': |
| extent_only = 1; |
| break; |
| case 'd': |
| device_only = 1; |
| break; |
| case 'r': |
| roots_only = 1; |
| break; |
| case 'u': |
| uuid_tree_only = 1; |
| break; |
| case 'R': |
| roots_only = 1; |
| root_backups = 1; |
| break; |
| case 'b': |
| block_only = arg_strtou64(optarg); |
| break; |
| case 't': |
| tree_id = arg_strtou64(optarg); |
| break; |
| default: |
| print_usage(); |
| } |
| } |
| set_argv0(av); |
| ac = ac - optind; |
| if (check_argc_exact(ac, 1)) |
| print_usage(); |
| |
| ret = check_arg_type(av[optind]); |
| if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) { |
| fprintf(stderr, "'%s' is not a block device or regular file\n", |
| av[optind]); |
| exit(1); |
| } |
| |
| info = open_ctree_fs_info(av[optind], 0, 0, OPEN_CTREE_PARTIAL); |
| if (!info) { |
| fprintf(stderr, "unable to open %s\n", av[optind]); |
| exit(1); |
| } |
| |
| root = info->fs_root; |
| if (!root) { |
| fprintf(stderr, "unable to open %s\n", av[optind]); |
| exit(1); |
| } |
| |
| if (block_only) { |
| leaf = read_tree_block(root, |
| block_only, |
| root->leafsize, 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 (!extent_buffer_uptodate(leaf)) { |
| fprintf(stderr, "failed to read %llu\n", |
| (unsigned long long)block_only); |
| goto close_root; |
| } |
| btrfs_print_tree(root, leaf, 0); |
| free_extent_buffer(leaf); |
| goto close_root; |
| } |
| |
| if (!(extent_only || uuid_tree_only || tree_id)) { |
| if (roots_only) { |
| printf("root tree: %llu level %d\n", |
| (unsigned long long)info->tree_root->node->start, |
| btrfs_header_level(info->tree_root->node)); |
| printf("chunk tree: %llu level %d\n", |
| (unsigned long long)info->chunk_root->node->start, |
| btrfs_header_level(info->chunk_root->node)); |
| } else { |
| if (info->tree_root->node) { |
| printf("root tree\n"); |
| btrfs_print_tree(info->tree_root, |
| info->tree_root->node, 1); |
| } |
| |
| if (info->chunk_root->node) { |
| printf("chunk tree\n"); |
| btrfs_print_tree(info->chunk_root, |
| info->chunk_root->node, 1); |
| } |
| } |
| } |
| tree_root_scan = info->tree_root; |
| |
| btrfs_init_path(&path); |
| again: |
| if (!extent_buffer_uptodate(tree_root_scan->node)) |
| goto no_node; |
| |
| key.offset = 0; |
| key.objectid = 0; |
| btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
| ret = btrfs_search_slot(NULL, tree_root_scan, &key, &path, 0, 0); |
| BUG_ON(ret < 0); |
| while(1) { |
| leaf = path.nodes[0]; |
| slot = path.slots[0]; |
| if (slot >= btrfs_header_nritems(leaf)) { |
| ret = btrfs_next_leaf(tree_root_scan, &path); |
| if (ret != 0) |
| break; |
| leaf = path.nodes[0]; |
| slot = path.slots[0]; |
| } |
| 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) { |
| 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); |
| if (!extent_buffer_uptodate(buf)) |
| goto next; |
| if (tree_id && found_key.objectid != tree_id) { |
| free_extent_buffer(buf); |
| goto next; |
| } |
| |
| switch(found_key.objectid) { |
| case BTRFS_ROOT_TREE_OBJECTID: |
| if (!skip) |
| printf("root"); |
| break; |
| case BTRFS_EXTENT_TREE_OBJECTID: |
| if (!device_only && !uuid_tree_only) |
| skip = 0; |
| if (!skip) |
| printf("extent"); |
| break; |
| case BTRFS_CHUNK_TREE_OBJECTID: |
| if (!skip) { |
| printf("chunk"); |
| } |
| break; |
| case BTRFS_DEV_TREE_OBJECTID: |
| if (!uuid_tree_only) |
| skip = 0; |
| if (!skip) |
| printf("device"); |
| break; |
| case BTRFS_FS_TREE_OBJECTID: |
| if (!skip) { |
| printf("fs"); |
| } |
| break; |
| case BTRFS_ROOT_TREE_DIR_OBJECTID: |
| skip = 0; |
| printf("directory"); |
| break; |
| case BTRFS_CSUM_TREE_OBJECTID: |
| if (!skip) { |
| printf("checksum"); |
| } |
| break; |
| case BTRFS_ORPHAN_OBJECTID: |
| if (!skip) { |
| printf("orphan"); |
| } |
| break; |
| case BTRFS_TREE_LOG_OBJECTID: |
| if (!skip) { |
| printf("log"); |
| } |
| break; |
| case BTRFS_TREE_LOG_FIXUP_OBJECTID: |
| if (!skip) { |
| printf("log fixup"); |
| } |
| break; |
| case BTRFS_TREE_RELOC_OBJECTID: |
| if (!skip) { |
| printf("reloc"); |
| } |
| break; |
| case BTRFS_DATA_RELOC_TREE_OBJECTID: |
| if (!skip) { |
| printf("data reloc"); |
| } |
| break; |
| case BTRFS_EXTENT_CSUM_OBJECTID: |
| if (!skip) { |
| printf("extent checksum"); |
| } |
| break; |
| case BTRFS_QUOTA_TREE_OBJECTID: |
| if (!skip) { |
| printf("quota"); |
| } |
| break; |
| case BTRFS_UUID_TREE_OBJECTID: |
| if (!extent_only && !device_only) |
| skip = 0; |
| if (!skip) |
| printf("uuid"); |
| break; |
| case BTRFS_MULTIPLE_OBJECTIDS: |
| if (!skip) { |
| printf("multiple"); |
| } |
| break; |
| default: |
| if (!skip) { |
| printf("file"); |
| } |
| } |
| if (extent_only && !skip) { |
| print_extents(tree_root_scan, buf); |
| } else if (!skip) { |
| printf(" tree "); |
| btrfs_print_key(&disk_key); |
| if (roots_only) { |
| printf(" %llu level %d\n", |
| (unsigned long long)buf->start, |
| btrfs_header_level(buf)); |
| } else { |
| printf(" \n"); |
| btrfs_print_tree(tree_root_scan, buf, 1); |
| } |
| } |
| free_extent_buffer(buf); |
| } |
| next: |
| path.slots[0]++; |
| } |
| no_node: |
| btrfs_release_path(&path); |
| |
| if (tree_root_scan == info->tree_root && |
| info->log_root_tree) { |
| tree_root_scan = info->log_root_tree; |
| goto again; |
| } |
| |
| if (extent_only || device_only || uuid_tree_only) |
| goto close_root; |
| |
| if (root_backups) |
| print_old_roots(info->super_copy); |
| |
| printf("total bytes %llu\n", |
| (unsigned long long)btrfs_super_total_bytes(info->super_copy)); |
| printf("bytes used %llu\n", |
| (unsigned long long)btrfs_super_bytes_used(info->super_copy)); |
| 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: |
| return close_ctree(root); |
| } |