/* * read tree blocks and add keys where required. */ static int __add_missing_keys(struct btrfs_fs_info *fs_info, struct list_head *head) { struct list_head *pos; struct extent_buffer *eb; list_for_each(pos, head) { struct __prelim_ref *ref; ref = list_entry(pos, struct __prelim_ref, list); if (ref->parent) continue; if (ref->key_for_search.type) continue; BUG_ON(!ref->wanted_disk_byte); eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte, fs_info->tree_root->leafsize, 0); if (!eb || !extent_buffer_uptodate(eb)) { free_extent_buffer(eb); return -EIO; } btrfs_tree_read_lock(eb); if (btrfs_header_level(eb) == 0) btrfs_item_key_to_cpu(eb, &ref->key_for_search, 0); else btrfs_node_key_to_cpu(eb, &ref->key_for_search, 0); btrfs_tree_read_unlock(eb); free_extent_buffer(eb); } return 0; }
static int find_and_setup_root(struct btrfs_root *tree_root, struct btrfs_fs_info *fs_info, u64 objectid, struct btrfs_root *root) { int ret; u32 blocksize; u64 generation; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, objectid); ret = btrfs_find_last_root(tree_root, objectid, &root->root_item, &root->root_key); if (ret) return ret; blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); generation = btrfs_root_generation(&root->root_item); root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), blocksize, generation); if (!extent_buffer_uptodate(root->node)) return -EIO; return 0; }
static int find_and_setup_log_root(struct btrfs_root *tree_root, struct btrfs_fs_info *fs_info, struct btrfs_super_block *disk_super) { u32 blocksize; u64 blocknr = btrfs_super_log_root(disk_super); struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root)); if (blocknr == 0) return 0; blocksize = btrfs_level_size(tree_root, btrfs_super_log_root_level(disk_super)); __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, log_root, fs_info, BTRFS_TREE_LOG_OBJECTID); log_root->node = read_tree_block(tree_root, blocknr, blocksize, btrfs_super_generation(disk_super) + 1); fs_info->log_root_tree = log_root; if (!extent_buffer_uptodate(log_root->node)) return -EIO; return 0; }
static int change_extents_uuid(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->extent_root; struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; btrfs_init_path(&path); /* * Here we don't use transaction as it will takes a lot of reserve * space, and that will make a near-full btrfs unable to change uuid */ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; while (1) { struct btrfs_extent_item *ei; struct extent_buffer *eb; u64 flags; u64 bytenr; btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY && key.type != BTRFS_METADATA_ITEM_KEY) goto next; ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item); flags = btrfs_extent_flags(path.nodes[0], ei); if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) goto next; bytenr = key.objectid; eb = read_tree_block(root, bytenr, root->nodesize, 0); if (IS_ERR(eb)) { error("failed to read tree block: %llu", bytenr); ret = PTR_ERR(eb); goto out; } ret = change_header_uuid(root, eb); free_extent_buffer(eb); if (ret < 0) { error("failed to change uuid of tree block: %llu", bytenr); goto out; } next: ret = btrfs_next_item(root, &path); if (ret < 0) goto out; if (ret > 0) { ret = 0; goto out; } } out: btrfs_release_path(&path); return ret; }
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); } }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) { int i; u32 nr; u32 size; struct btrfs_disk_key disk_key; struct btrfs_key key; if (!eb) return; nr = btrfs_header_nritems(eb); if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } printf("node %llu level %d items %d free %u generation %llu owner %llu\n", (unsigned long long)eb->start, btrfs_header_level(eb), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (unsigned long long)btrfs_header_generation(eb), (unsigned long long)btrfs_header_owner(eb)); print_uuids(eb); fflush(stdout); size = btrfs_level_size(root, btrfs_header_level(eb) - 1); for (i = 0; i < nr; i++) { u64 blocknr = btrfs_node_blockptr(eb, i); btrfs_node_key(eb, &disk_key, i); btrfs_disk_key_to_cpu(&key, &disk_key); printf("\t"); btrfs_print_key(&disk_key); printf(" block %llu (%llu) gen %llu\n", (unsigned long long)blocknr, (unsigned long long)blocknr / size, (unsigned long long)btrfs_node_ptr_generation(eb, i)); fflush(stdout); } if (!follow) return; 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 (!next) { fprintf(stderr, "failed to read %llu in tree %llu\n", (unsigned long long)btrfs_node_blockptr(eb, i), (unsigned long long)btrfs_header_owner(eb)); continue; } if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) BUG(); if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) BUG(); btrfs_print_tree(root, next, 1); free_extent_buffer(next); } }
struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info, struct btrfs_key *location) { struct btrfs_root *root; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_path *path; struct extent_buffer *l; u64 generation; u32 blocksize; int ret = 0; root = malloc(sizeof(*root)); if (!root) return ERR_PTR(-ENOMEM); memset(root, 0, sizeof(*root)); if (location->offset == (u64)-1) { ret = find_and_setup_root(tree_root, fs_info, location->objectid, root); if (ret) { free(root); return ERR_PTR(ret); } goto insert; } __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, location->objectid); path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0); if (ret != 0) { if (ret > 0) ret = -ENOENT; goto out; } l = path->nodes[0]; read_extent_buffer(l, &root->root_item, btrfs_item_ptr_offset(l, path->slots[0]), sizeof(root->root_item)); memcpy(&root->root_key, location, sizeof(*location)); ret = 0; out: btrfs_release_path(root, path); btrfs_free_path(path); if (ret) { free(root); return ERR_PTR(ret); } generation = btrfs_root_generation(&root->root_item); blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), blocksize, generation); BUG_ON(!root->node); insert: root->ref_cows = 1; return root; }
/* * helper function for drop_snapshot, this walks down the tree dropping ref * counts as it goes. */ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int *level) { struct btrfs_buffer *next; struct btrfs_buffer *cur; u64 blocknr; int ret; u32 refs; ret = lookup_block_ref(trans, root, path->nodes[*level]->blocknr, &refs); BUG_ON(ret); if (refs > 1) goto out; /* * walk down to the last node level and free all the leaves */ while(*level > 0) { cur = path->nodes[*level]; if (path->slots[*level] >= btrfs_header_nritems(&cur->node.header)) break; blocknr = btrfs_node_blockptr(&cur->node, path->slots[*level]); ret = lookup_block_ref(trans, root, blocknr, &refs); if (refs != 1 || *level == 1) { path->slots[*level]++; ret = btrfs_free_extent(trans, root, blocknr, 1, 1); BUG_ON(ret); continue; } BUG_ON(ret); next = read_tree_block(root, blocknr); if (path->nodes[*level-1]) btrfs_block_release(root, path->nodes[*level-1]); path->nodes[*level-1] = next; *level = btrfs_header_level(&next->node.header); path->slots[*level] = 0; } out: ret = btrfs_free_extent(trans, root, path->nodes[*level]->blocknr, 1, 1); btrfs_block_release(root, path->nodes[*level]); path->nodes[*level] = NULL; *level += 1; BUG_ON(ret); return 0; }
void btrfs_print_tree(struct btrfs_fs_info *fs_info, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(fs_info, c); return; } btrfs_info(fs_info, "node %llu level %d total ptrs %d free spc %u", btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); pr_info("\tkey %d (%llu %u %llu) block %llu\n", i, key.objectid, key.type, key.offset, btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(fs_info, btrfs_node_blockptr(c, i), btrfs_node_ptr_generation(c, i)); if (IS_ERR(next)) { continue; } else if (!extent_buffer_uptodate(next)) { free_extent_buffer(next); continue; } if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(fs_info, next); free_extent_buffer(next); } }
void btrfs_print_tree(struct btrfs_root *root, struct btrfs_buffer *t) { unsigned int i; u32 nr; struct btrfs_node *c; if (!t) return; c = &t->node; nr = btrfs_header_nritems(&c->header); if (btrfs_is_leaf(c)) { btrfs_print_leaf(root, (struct btrfs_leaf *)c); return; } printf("node %llu level %d ptrs %d free %u generation %llu owner %llu\n", (u64)t->blocknr, btrfs_header_level(&c->header), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (u64)btrfs_header_generation(&c->header), (u64)btrfs_header_owner(&c->header)); fflush(stdout); for (i = 0; i < nr; i++) { printf("\tkey %d (%llu %x %llu) block %llu\n", i, (u64)c->ptrs[i].key.objectid, c->ptrs[i].key.flags, (u64)c->ptrs[i].key.offset, (u64)btrfs_node_blockptr(c, i)); fflush(stdout); } for (i = 0; i < nr; i++) { struct btrfs_buffer *next_buf = read_tree_block(root, btrfs_node_blockptr(c, i)); struct btrfs_node *next = &next_buf->node; if (btrfs_is_leaf(next) && btrfs_header_level(&c->header) != 1) BUG(); if (btrfs_header_level(&next->header) != btrfs_header_level(&c->header) - 1) BUG(); btrfs_print_tree(root, next_buf); btrfs_block_release(root, next_buf); } }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(root, c); return; } printk(KERN_INFO "node %llu level %d total ptrs %d free spc %u\n", (unsigned long long)btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); printk(KERN_INFO "\tkey %d (%llu %u %llu) block %llu\n", i, (unsigned long long)key.objectid, key.type, (unsigned long long)key.offset, (unsigned long long)btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(c, i), btrfs_level_size(root, level - 1), btrfs_node_ptr_generation(c, i)); if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(root, next); free_extent_buffer(next); } }
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; static const struct option long_options[] = { { "help", no_argument, NULL, GETOPT_VAL_HELP}, { NULL, 0, NULL, 0 } }; c = getopt_long(ac, av, "deb:rRut:", long_options, NULL); 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; case GETOPT_VAL_HELP: default: print_usage(c != GETOPT_VAL_HELP); } } set_argv0(av); ac = ac - optind; if (check_argc_exact(ac, 1)) print_usage(1); 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; /* * Tree's that are not pointed by the tree of tree roots */ if (tree_id && tree_id == BTRFS_ROOT_TREE_OBJECTID) { if (!info->tree_root->node) { error("cannot print root tree, invalid pointer"); goto no_node; } printf("root tree\n"); btrfs_print_tree(info->tree_root, info->tree_root->node, 1); goto no_node; } if (tree_id && tree_id == BTRFS_CHUNK_TREE_OBJECTID) { if (!info->chunk_root->node) { error("cannot print chunk tree, invalid pointer"); goto no_node; } printf("chunk tree\n"); btrfs_print_tree(info->chunk_root, info->chunk_root->node, 1); 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_FREE_SPACE_TREE_OBJECTID: if (!skip) printf("free space"); 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: ret = close_ctree(root); btrfs_close_all_devices(); return ret; }
static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path, u64 sb_bytenr, u64 root_tree_bytenr, int writes, int partial) { u32 sectorsize; u32 nodesize; u32 leafsize; u32 blocksize; u32 stripesize; u64 generation; struct btrfs_key key; struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root)); struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root)); struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root)); struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root)); struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root)); struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info)); int ret; struct btrfs_super_block *disk_super; struct btrfs_fs_devices *fs_devices = NULL; u64 total_devs; u64 features; if (sb_bytenr == 0) sb_bytenr = BTRFS_SUPER_INFO_OFFSET; /* try to drop all the caches */ if (posix_fadvise(fp, 0, 0, POSIX_FADV_DONTNEED)) fprintf(stderr, "Warning, could not drop caches\n"); ret = btrfs_scan_one_device(fp, path, &fs_devices, &total_devs, sb_bytenr); if (ret) { fprintf(stderr, "No valid Btrfs found on %s\n", path); goto out; } if (total_devs != 1) { ret = btrfs_scan_for_fsid(fs_devices, total_devs, 1); if (ret) goto out; } memset(fs_info, 0, sizeof(*fs_info)); fs_info->super_copy = calloc(1, BTRFS_SUPER_INFO_SIZE); fs_info->tree_root = tree_root; fs_info->extent_root = extent_root; fs_info->chunk_root = chunk_root; fs_info->dev_root = dev_root; fs_info->csum_root = csum_root; if (!writes) fs_info->readonly = 1; extent_io_tree_init(&fs_info->extent_cache); extent_io_tree_init(&fs_info->free_space_cache); extent_io_tree_init(&fs_info->block_group_cache); extent_io_tree_init(&fs_info->pinned_extents); extent_io_tree_init(&fs_info->pending_del); extent_io_tree_init(&fs_info->extent_ins); cache_tree_init(&fs_info->fs_root_cache); cache_tree_init(&fs_info->mapping_tree.cache_tree); mutex_init(&fs_info->fs_mutex); fs_info->fs_devices = fs_devices; INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); INIT_LIST_HEAD(&fs_info->space_info); __setup_root(4096, 4096, 4096, 4096, tree_root, fs_info, BTRFS_ROOT_TREE_OBJECTID); if (writes) ret = btrfs_open_devices(fs_devices, O_RDWR); else ret = btrfs_open_devices(fs_devices, O_RDONLY); if (ret) goto out_cleanup; fs_info->super_bytenr = sb_bytenr; disk_super = fs_info->super_copy; ret = btrfs_read_dev_super(fs_devices->latest_bdev, disk_super, sb_bytenr); if (ret) { printk("No valid btrfs found\n"); goto out_devices; } memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); features = btrfs_super_incompat_flags(disk_super) & ~BTRFS_FEATURE_INCOMPAT_SUPP; if (features) { printk("couldn't open because of unsupported " "option features (%Lx).\n", (unsigned long long)features); goto out_devices; } features = btrfs_super_incompat_flags(disk_super); if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) { features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF; btrfs_set_super_incompat_flags(disk_super, features); } features = btrfs_super_compat_ro_flags(disk_super) & ~BTRFS_FEATURE_COMPAT_RO_SUPP; if (writes && features) { printk("couldn't open RDWR because of unsupported " "option features (%Lx).\n", (unsigned long long)features); goto out_devices; } nodesize = btrfs_super_nodesize(disk_super); leafsize = btrfs_super_leafsize(disk_super); sectorsize = btrfs_super_sectorsize(disk_super); stripesize = btrfs_super_stripesize(disk_super); tree_root->nodesize = nodesize; tree_root->leafsize = leafsize; tree_root->sectorsize = sectorsize; tree_root->stripesize = stripesize; ret = btrfs_read_sys_array(tree_root); if (ret) goto out_devices; blocksize = btrfs_level_size(tree_root, btrfs_super_chunk_root_level(disk_super)); generation = btrfs_super_chunk_root_generation(disk_super); __setup_root(nodesize, leafsize, sectorsize, stripesize, chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); chunk_root->node = read_tree_block(chunk_root, btrfs_super_chunk_root(disk_super), blocksize, generation); if (!extent_buffer_uptodate(chunk_root->node)) { printk("Couldn't read chunk root\n"); goto out_devices; } read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), BTRFS_UUID_SIZE); if (!(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_METADUMP)) { ret = btrfs_read_chunk_tree(chunk_root); if (ret) { printk("Couldn't read chunk tree\n"); goto out_chunk; } } blocksize = btrfs_level_size(tree_root, btrfs_super_root_level(disk_super)); generation = btrfs_super_generation(disk_super); if (!root_tree_bytenr) root_tree_bytenr = btrfs_super_root(disk_super); tree_root->node = read_tree_block(tree_root, root_tree_bytenr, blocksize, generation); if (!extent_buffer_uptodate(tree_root->node)) { printk("Couldn't read tree root\n"); goto out_failed; } ret = find_and_setup_root(tree_root, fs_info, BTRFS_EXTENT_TREE_OBJECTID, extent_root); if (ret) { printk("Couldn't setup extent tree\n"); goto out_failed; } extent_root->track_dirty = 1; ret = find_and_setup_root(tree_root, fs_info, BTRFS_DEV_TREE_OBJECTID, dev_root); if (ret) { printk("Couldn't setup device tree\n"); goto out_failed; } dev_root->track_dirty = 1; ret = find_and_setup_root(tree_root, fs_info, BTRFS_CSUM_TREE_OBJECTID, csum_root); if (ret) { printk("Couldn't setup csum tree\n"); if (!partial) goto out_failed; } csum_root->track_dirty = 1; find_and_setup_log_root(tree_root, fs_info, disk_super); fs_info->generation = generation; fs_info->last_trans_committed = generation; btrfs_read_block_groups(fs_info->tree_root); key.objectid = BTRFS_FS_TREE_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; fs_info->fs_root = btrfs_read_fs_root(fs_info, &key); if (!fs_info->fs_root) goto out_failed; fs_info->data_alloc_profile = (u64)-1; fs_info->metadata_alloc_profile = (u64)-1; fs_info->system_alloc_profile = fs_info->metadata_alloc_profile; return fs_info; out_failed: if (partial) return fs_info; if (fs_info->csum_root) free_extent_buffer(fs_info->csum_root->node); if (fs_info->dev_root) free_extent_buffer(fs_info->dev_root->node); if (fs_info->extent_root) free_extent_buffer(fs_info->extent_root->node); if (fs_info->tree_root) free_extent_buffer(fs_info->tree_root->node); out_chunk: if (fs_info->chunk_root) free_extent_buffer(fs_info->chunk_root->node); out_devices: close_all_devices(fs_info); out_cleanup: extent_io_tree_cleanup(&fs_info->extent_cache); extent_io_tree_cleanup(&fs_info->free_space_cache); extent_io_tree_cleanup(&fs_info->block_group_cache); extent_io_tree_cleanup(&fs_info->pinned_extents); extent_io_tree_cleanup(&fs_info->pending_del); extent_io_tree_cleanup(&fs_info->extent_ins); out: free(tree_root); free(extent_root); free(chunk_root); free(dev_root); free(csum_root); free(fs_info); return NULL; }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) { u32 i; u32 nr; u32 size; struct btrfs_disk_key disk_key; struct btrfs_key key; struct extent_buffer *next; if (!eb) return; nr = btrfs_header_nritems(eb); if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } printf("node %llu level %d items %d free %u generation %llu owner %llu\n", (unsigned long long)eb->start, btrfs_header_level(eb), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (unsigned long long)btrfs_header_generation(eb), (unsigned long long)btrfs_header_owner(eb)); print_uuids(eb); fflush(stdout); size = root->nodesize; for (i = 0; i < nr; i++) { u64 blocknr = btrfs_node_blockptr(eb, i); btrfs_node_key(eb, &disk_key, i); btrfs_disk_key_to_cpu(&key, &disk_key); printf("\t"); btrfs_print_key(&disk_key); printf(" block %llu (%llu) gen %llu\n", (unsigned long long)blocknr, (unsigned long long)blocknr / size, (unsigned long long)btrfs_node_ptr_generation(eb, i)); fflush(stdout); } if (!follow) return; for (i = 0; i < nr; i++) { next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, btrfs_node_ptr_generation(eb, i)); if (!extent_buffer_uptodate(next)) { fprintf(stderr, "failed to read %llu in tree %llu\n", (unsigned long long)btrfs_node_blockptr(eb, i), (unsigned long long)btrfs_header_owner(eb)); continue; } 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; } btrfs_print_tree(root, next, 1); free_extent_buffer(next); } return; out: free_extent_buffer(next); }
/* * this adds all existing backrefs (inline backrefs, backrefs and delayed * refs) for the given bytenr to the refs list, merges duplicates and resolves * indirect refs to their parent bytenr. * When roots are found, they're added to the roots list * * FIXME some caching might speed things up */ static int find_parent_nodes(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 time_seq, struct ulist *refs, struct ulist *roots, const u64 *extent_item_pos) { struct btrfs_key key; struct btrfs_path *path; struct btrfs_delayed_ref_root *delayed_refs = NULL; struct btrfs_delayed_ref_head *head; int info_level = 0; int ret; struct list_head prefs_delayed; struct list_head prefs; struct __prelim_ref *ref; struct extent_inode_elem *eie = NULL; u64 total_refs = 0; INIT_LIST_HEAD(&prefs); INIT_LIST_HEAD(&prefs_delayed); key.objectid = bytenr; key.offset = (u64)-1; if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; path = btrfs_alloc_path(); if (!path) return -ENOMEM; if (!trans) { path->search_commit_root = 1; path->skip_locking = 1; } /* * grab both a lock on the path and a lock on the delayed ref head. * We need both to get a consistent picture of how the refs look * at a specified point in time */ again: head = NULL; ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) goto out; BUG_ON(ret == 0); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS if (trans && likely(trans->type != __TRANS_DUMMY)) { #else if (trans) { #endif /* * look if there are updates for this ref queued and lock the * head */ delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); head = btrfs_find_delayed_ref_head(trans, bytenr); if (head) { if (!mutex_trylock(&head->mutex)) { atomic_inc(&head->node.refs); spin_unlock(&delayed_refs->lock); btrfs_release_path(path); /* * Mutex was contended, block until it's * released and try again */ mutex_lock(&head->mutex); mutex_unlock(&head->mutex); btrfs_put_delayed_ref(&head->node); goto again; } spin_unlock(&delayed_refs->lock); ret = __add_delayed_refs(head, time_seq, &prefs_delayed, &total_refs); mutex_unlock(&head->mutex); if (ret) goto out; } else { spin_unlock(&delayed_refs->lock); } } if (path->slots[0]) { struct extent_buffer *leaf; int slot; path->slots[0]--; leaf = path->nodes[0]; slot = path->slots[0]; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid == bytenr && (key.type == BTRFS_EXTENT_ITEM_KEY || key.type == BTRFS_METADATA_ITEM_KEY)) { ret = __add_inline_refs(fs_info, path, bytenr, &info_level, &prefs, &total_refs); if (ret) goto out; ret = __add_keyed_refs(fs_info, path, bytenr, info_level, &prefs); if (ret) goto out; } } btrfs_release_path(path); list_splice_init(&prefs_delayed, &prefs); ret = __add_missing_keys(fs_info, &prefs); if (ret) goto out; __merge_refs(&prefs, 1); ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs, extent_item_pos, total_refs); if (ret) goto out; __merge_refs(&prefs, 2); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); WARN_ON(ref->count < 0); if (roots && ref->count && ref->root_id && ref->parent == 0) { /* no parent == root of tree */ ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS); if (ret < 0) goto out; } if (ref->count && ref->parent) { if (extent_item_pos && !ref->inode_list && ref->level == 0) { u32 bsz; struct extent_buffer *eb; bsz = btrfs_level_size(fs_info->extent_root, ref->level); eb = read_tree_block(fs_info->extent_root, ref->parent, bsz, 0); if (!eb || !extent_buffer_uptodate(eb)) { free_extent_buffer(eb); ret = -EIO; goto out; } btrfs_tree_read_lock(eb); btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); ret = find_extent_in_eb(eb, bytenr, *extent_item_pos, &eie); btrfs_tree_read_unlock_blocking(eb); free_extent_buffer(eb); if (ret < 0) goto out; ref->inode_list = eie; } ret = ulist_add_merge_ptr(refs, ref->parent, ref->inode_list, (void **)&eie, GFP_NOFS); if (ret < 0) goto out; if (!ret && extent_item_pos) { /* * we've recorded that parent, so we must extend * its inode list here */ BUG_ON(!eie); while (eie->next) eie = eie->next; eie->next = ref->inode_list; } eie = NULL; } list_del(&ref->list); kmem_cache_free(btrfs_prelim_ref_cache, ref); } out: btrfs_free_path(path); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); list_del(&ref->list); kmem_cache_free(btrfs_prelim_ref_cache, ref); } while (!list_empty(&prefs_delayed)) { ref = list_first_entry(&prefs_delayed, struct __prelim_ref, list); list_del(&ref->list); kmem_cache_free(btrfs_prelim_ref_cache, ref); } if (ret < 0) free_inode_elem_list(eie); return ret; } static void free_leaf_list(struct ulist *blocks) { struct ulist_node *node = NULL; struct extent_inode_elem *eie; struct ulist_iterator uiter; ULIST_ITER_INIT(&uiter); while ((node = ulist_next(blocks, &uiter))) { if (!node->aux) continue; eie = (struct extent_inode_elem *)(uintptr_t)node->aux; free_inode_elem_list(eie); node->aux = 0; } ulist_free(blocks); } /* * Finds all leafs with a reference to the specified combination of bytenr and * offset. key_list_head will point to a list of corresponding keys (caller must * free each list element). The leafs will be stored in the leafs ulist, which * must be freed with ulist_free. * * returns 0 on success, <0 on error */ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 time_seq, struct ulist **leafs, const u64 *extent_item_pos) { int ret; *leafs = ulist_alloc(GFP_NOFS); if (!*leafs) return -ENOMEM; ret = find_parent_nodes(trans, fs_info, bytenr, time_seq, *leafs, NULL, extent_item_pos); if (ret < 0 && ret != -ENOENT) { free_leaf_list(*leafs); return ret; } return 0; }
/* * this adds all existing backrefs (inline backrefs, backrefs and delayed * refs) for the given bytenr to the refs list, merges duplicates and resolves * indirect refs to their parent bytenr. * When roots are found, they're added to the roots list * * FIXME some caching might speed things up */ static int find_parent_nodes(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 time_seq, struct ulist *refs, struct ulist *roots, const u64 *extent_item_pos) { struct btrfs_key key; struct btrfs_path *path; struct btrfs_delayed_ref_root *delayed_refs = NULL; struct btrfs_delayed_ref_head *head; int info_level = 0; int ret; struct list_head prefs_delayed; struct list_head prefs; struct __prelim_ref *ref; INIT_LIST_HEAD(&prefs); INIT_LIST_HEAD(&prefs_delayed); key.objectid = bytenr; key.offset = (u64)-1; if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; path = btrfs_alloc_path(); if (!path) return -ENOMEM; if (!trans) path->search_commit_root = 1; /* * grab both a lock on the path and a lock on the delayed ref head. * We need both to get a consistent picture of how the refs look * at a specified point in time */ again: head = NULL; ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) goto out; BUG_ON(ret == 0); if (trans) { /* * look if there are updates for this ref queued and lock the * head */ delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); head = btrfs_find_delayed_ref_head(trans, bytenr); if (head) { if (!mutex_trylock(&head->mutex)) { atomic_inc(&head->node.refs); spin_unlock(&delayed_refs->lock); btrfs_release_path(path); /* * Mutex was contended, block until it's * released and try again */ mutex_lock(&head->mutex); mutex_unlock(&head->mutex); btrfs_put_delayed_ref(&head->node); goto again; } ret = __add_delayed_refs(head, time_seq, &prefs_delayed); mutex_unlock(&head->mutex); if (ret) { spin_unlock(&delayed_refs->lock); goto out; } } spin_unlock(&delayed_refs->lock); } if (path->slots[0]) { struct extent_buffer *leaf; int slot; path->slots[0]--; leaf = path->nodes[0]; slot = path->slots[0]; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid == bytenr && (key.type == BTRFS_EXTENT_ITEM_KEY || key.type == BTRFS_METADATA_ITEM_KEY)) { ret = __add_inline_refs(fs_info, path, bytenr, &info_level, &prefs); if (ret) goto out; ret = __add_keyed_refs(fs_info, path, bytenr, info_level, &prefs); if (ret) goto out; } } btrfs_release_path(path); list_splice_init(&prefs_delayed, &prefs); ret = __add_missing_keys(fs_info, &prefs); if (ret) goto out; __merge_refs(&prefs, 1); ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs, extent_item_pos); if (ret) goto out; __merge_refs(&prefs, 2); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); list_del(&ref->list); WARN_ON(ref->count < 0); if (ref->count && ref->root_id && ref->parent == 0) { /* no parent == root of tree */ ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS); if (ret < 0) goto out; } if (ref->count && ref->parent) { struct extent_inode_elem *eie = NULL; if (extent_item_pos && !ref->inode_list) { u32 bsz; struct extent_buffer *eb; bsz = btrfs_level_size(fs_info->extent_root, info_level); eb = read_tree_block(fs_info->extent_root, ref->parent, bsz, 0); if (!eb || !extent_buffer_uptodate(eb)) { free_extent_buffer(eb); ret = -EIO; goto out; } ret = find_extent_in_eb(eb, bytenr, *extent_item_pos, &eie); ref->inode_list = eie; free_extent_buffer(eb); } ret = ulist_add_merge(refs, ref->parent, (uintptr_t)ref->inode_list, (u64 *)&eie, GFP_NOFS); if (ret < 0) goto out; if (!ret && extent_item_pos) { /* * we've recorded that parent, so we must extend * its inode list here */ BUG_ON(!eie); while (eie->next) eie = eie->next; eie->next = ref->inode_list; } } kfree(ref); } out: btrfs_free_path(path); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); list_del(&ref->list); kfree(ref); } while (!list_empty(&prefs_delayed)) { ref = list_first_entry(&prefs_delayed, struct __prelim_ref, list); list_del(&ref->list); kfree(ref); } return ret; }
/* * this adds all existing backrefs (inline backrefs, backrefs and delayed * refs) for the given bytenr to the refs list, merges duplicates and resolves * indirect refs to their parent bytenr. * When roots are found, they're added to the roots list * * FIXME some caching might speed things up */ static int find_parent_nodes(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 time_seq, struct ulist *refs, struct ulist *roots, const u64 *extent_item_pos) { struct btrfs_key key; struct btrfs_path *path; int info_level = 0; int ret; struct list_head prefs; struct __prelim_ref *ref; struct extent_inode_elem *eie = NULL; u64 total_refs = 0; INIT_LIST_HEAD(&prefs); key.objectid = bytenr; key.offset = (u64)-1; if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; else key.type = BTRFS_EXTENT_ITEM_KEY; path = btrfs_alloc_path(); if (!path) return -ENOMEM; ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) goto out; BUG_ON(ret == 0); if (path->slots[0]) { struct extent_buffer *leaf; int slot; path->slots[0]--; leaf = path->nodes[0]; slot = path->slots[0]; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid == bytenr && (key.type == BTRFS_EXTENT_ITEM_KEY || key.type == BTRFS_METADATA_ITEM_KEY)) { ret = __add_inline_refs(fs_info, path, bytenr, &info_level, &prefs, &total_refs); if (ret) goto out; ret = __add_keyed_refs(fs_info, path, bytenr, info_level, &prefs); if (ret) goto out; } } btrfs_release_path(path); ret = __add_missing_keys(fs_info, &prefs); if (ret) goto out; __merge_refs(&prefs, 1); ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs, extent_item_pos, total_refs); if (ret) goto out; __merge_refs(&prefs, 2); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); WARN_ON(ref->count < 0); if (roots && ref->count && ref->root_id && ref->parent == 0) { /* no parent == root of tree */ ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS); if (ret < 0) goto out; } if (ref->count && ref->parent) { if (extent_item_pos && !ref->inode_list && ref->level == 0) { u32 bsz; struct extent_buffer *eb; bsz = fs_info->extent_root->nodesize; eb = read_tree_block(fs_info->extent_root, ref->parent, bsz, 0); if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); ret = -EIO; goto out; } ret = find_extent_in_eb(eb, bytenr, *extent_item_pos, &eie); free_extent_buffer(eb); if (ret < 0) goto out; ref->inode_list = eie; } ret = ulist_add_merge_ptr(refs, ref->parent, ref->inode_list, (void **)&eie, GFP_NOFS); if (ret < 0) goto out; if (!ret && extent_item_pos) { /* * we've recorded that parent, so we must extend * its inode list here */ BUG_ON(!eie); while (eie->next) eie = eie->next; eie->next = ref->inode_list; } eie = NULL; } list_del(&ref->list); kfree(ref); } out: btrfs_free_path(path); while (!list_empty(&prefs)) { ref = list_first_entry(&prefs, struct __prelim_ref, list); list_del(&ref->list); kfree(ref); } if (ret < 0) free_inode_elem_list(eie); return ret; }
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[37]; int ret; int slot; int extent_only = 0; int device_only = 0; int roots_only = 0; int root_backups = 0; u64 block_only = 0; struct btrfs_root *tree_root_scan; radix_tree_init(); while(1) { int c; c = getopt(ac, av, "deb:rR"); 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 'R': roots_only = 1; root_backups = 1; break; case 'b': block_only = atoll(optarg); break; default: print_usage(); } } ac = ac - optind; if (ac != 1) print_usage(); info = open_ctree_fs_info(av[optind], 0, 0, 1); if (!info) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } root = info->fs_root; if (block_only) { if (!root) { fprintf(stderr, "unable to open %s\n", av[optind]); exit(1); } leaf = read_tree_block(root, block_only, root->leafsize, 0); if (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) { fprintf(stderr, "failed to read %llu\n", (unsigned long long)block_only); return 0; } btrfs_print_tree(root, leaf, 0); return 0; } if (!extent_only) { 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; 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; switch(found_key.objectid) { case BTRFS_ROOT_TREE_OBJECTID: if (!skip) printf("root"); break; case BTRFS_EXTENT_TREE_OBJECTID: if (!device_only) skip = 0; if (!extent_only && !device_only) printf("extent"); break; case BTRFS_CHUNK_TREE_OBJECTID: if (!skip) { printf("chunk"); } break; case BTRFS_DEV_TREE_OBJECTID: skip = 0; 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_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); } } } next: path.slots[0]++; } no_node: btrfs_release_path(root, &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) return 0; 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[36] = '\0'; uuid_unparse(info->super_copy.fsid, uuidbuf); printf("uuid %s\n", uuidbuf); printf("%s\n", BTRFS_BUILD_VERSION); return 0; }
static int __iter_shared_inline_ref(struct btrfs_fs_info *fs_info, u64 logical, u64 orig_extent_item_objectid, u64 extent_offset, struct btrfs_path *path, struct list_head *data_refs, iterate_extent_inodes_t *iterate, void *ctx) { u64 disk_byte; struct btrfs_key key; struct btrfs_file_extent_item *fi; struct extent_buffer *eb; int slot; int nritems; int ret; int found = 0; eb = read_tree_block(fs_info->tree_root, logical, fs_info->tree_root->leafsize, 0); if (!eb) return -EIO; /* * from the shared data ref, we only have the leaf but we need * the key. thus, we must look into all items and see that we * find one (some) with a reference to our extent item. */ nritems = btrfs_header_nritems(eb); for (slot = 0; slot < nritems; ++slot) { btrfs_item_key_to_cpu(eb, &key, slot); if (key.type != BTRFS_EXTENT_DATA_KEY) continue; fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); if (!fi) { free_extent_buffer(eb); return -EIO; } disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); if (disk_byte != orig_extent_item_objectid) { if (found) break; else continue; } ++found; ret = __iter_shared_inline_ref_inodes(fs_info, logical, key.objectid, key.offset, extent_offset, path, data_refs, iterate, ctx); if (ret) break; } if (!found) { printk(KERN_ERR "btrfs: failed to follow shared data backref " "to parent %llu\n", logical); WARN_ON(1); ret = -EIO; } free_extent_buffer(eb); return ret; }