/* * reads the tree block backref for an extent. tree level and root are returned * through out_level and out_root. ptr must point to a 0 value for the first * call and may be modified (see __get_extent_inline_ref comment). * returns 0 if data was provided, 1 if there was no more data to provide or * <0 on error. */ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, struct btrfs_extent_item *ei, u32 item_size, u64 *out_root, u8 *out_level) { int ret; int type; struct btrfs_tree_block_info *info; struct btrfs_extent_inline_ref *eiref; if (*ptr == (unsigned long)-1) return 1; while (1) { ret = __get_extent_inline_ref(ptr, eb, ei, item_size, &eiref, &type); if (ret < 0) return ret; if (type == BTRFS_TREE_BLOCK_REF_KEY || type == BTRFS_SHARED_BLOCK_REF_KEY) break; if (ret == 1) return 1; } /* we can treat both ref types equally here */ info = (struct btrfs_tree_block_info *)(ei + 1); *out_root = btrfs_extent_inline_ref_offset(eb, eiref); *out_level = btrfs_tree_block_level(eb, info); if (ret == 1) *ptr = (unsigned long)-1; return 0; }
static void print_extent_item(struct extent_buffer *eb, int slot, int metadata) { struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; struct btrfs_disk_key key; unsigned long end; unsigned long ptr; int type; u32 item_size = btrfs_item_size_nr(eb, slot); u64 flags; u64 offset; if (item_size < sizeof(*ei)) { #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 struct btrfs_extent_item_v0 *ei0; BUG_ON(item_size != sizeof(*ei0)); ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); printf("\t\textent refs %u\n", btrfs_extent_refs_v0(eb, ei0)); return; #else BUG(); #endif } ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); flags = btrfs_extent_flags(eb, ei); printf("\t\textent refs %llu gen %llu flags %llu\n", (unsigned long long)btrfs_extent_refs(eb, ei), (unsigned long long)btrfs_extent_generation(eb, ei), (unsigned long long)flags); if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !metadata) { struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)(ei + 1); btrfs_tree_block_key(eb, info, &key); printf("\t\ttree block "); btrfs_print_key(&key); printf(" level %d\n", btrfs_tree_block_level(eb, info)); iref = (struct btrfs_extent_inline_ref *)(info + 1); } else if (metadata) { struct btrfs_key tmp; btrfs_item_key_to_cpu(eb, &tmp, slot); printf("\t\ttree block skinny level %d\n", (int)tmp.offset); iref = (struct btrfs_extent_inline_ref *)(ei + 1); } else{ iref = (struct btrfs_extent_inline_ref *)(ei + 1); } ptr = (unsigned long)iref; end = (unsigned long)ei + item_size; while (ptr < end) { iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(eb, iref); offset = btrfs_extent_inline_ref_offset(eb, iref); switch (type) { case BTRFS_TREE_BLOCK_REF_KEY: printf("\t\ttree block backref root %llu\n", (unsigned long long)offset); break; case BTRFS_SHARED_BLOCK_REF_KEY: printf("\t\tshared block backref parent %llu\n", (unsigned long long)offset); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = (struct btrfs_extent_data_ref *)(&iref->offset); printf("\t\textent data backref root %llu " "objectid %llu offset %llu count %u\n", (unsigned long long)btrfs_extent_data_ref_root(eb, dref), (unsigned long long)btrfs_extent_data_ref_objectid(eb, dref), (unsigned long long)btrfs_extent_data_ref_offset(eb, dref), btrfs_extent_data_ref_count(eb, dref)); break; case BTRFS_SHARED_DATA_REF_KEY: sref = (struct btrfs_shared_data_ref *)(iref + 1); printf("\t\tshared data backref parent %llu count %u\n", (unsigned long long)offset, btrfs_shared_data_ref_count(eb, sref)); break; default: return; } ptr += btrfs_extent_inline_ref_size(type); } WARN_ON(ptr > end); }
/* * add all inline backrefs for bytenr to the list */ static int __add_inline_refs(struct btrfs_fs_info *fs_info, struct btrfs_path *path, u64 bytenr, int *info_level, struct list_head *prefs) { int ret = 0; int slot; struct extent_buffer *leaf; struct btrfs_key key; struct btrfs_key found_key; unsigned long ptr; unsigned long end; struct btrfs_extent_item *ei; u64 flags; u64 item_size; /* * enumerate all inline refs */ leaf = path->nodes[0]; slot = path->slots[0]; item_size = btrfs_item_size_nr(leaf, slot); BUG_ON(item_size < sizeof(*ei)); ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); flags = btrfs_extent_flags(leaf, ei); btrfs_item_key_to_cpu(leaf, &found_key, slot); ptr = (unsigned long)(ei + 1); end = (unsigned long)ei + item_size; if (found_key.type == BTRFS_EXTENT_ITEM_KEY && flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)ptr; *info_level = btrfs_tree_block_level(leaf, info); ptr += sizeof(struct btrfs_tree_block_info); BUG_ON(ptr > end); } else if (found_key.type == BTRFS_METADATA_ITEM_KEY) { *info_level = found_key.offset; } else { BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA)); } while (ptr < end) { struct btrfs_extent_inline_ref *iref; u64 offset; int type; iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(leaf, iref); offset = btrfs_extent_inline_ref_offset(leaf, iref); switch (type) { case BTRFS_SHARED_BLOCK_REF_KEY: ret = __add_prelim_ref(prefs, 0, NULL, *info_level + 1, offset, bytenr, 1); break; case BTRFS_SHARED_DATA_REF_KEY: { struct btrfs_shared_data_ref *sdref; int count; sdref = (struct btrfs_shared_data_ref *)(iref + 1); count = btrfs_shared_data_ref_count(leaf, sdref); ret = __add_prelim_ref(prefs, 0, NULL, 0, offset, bytenr, count); break; } case BTRFS_TREE_BLOCK_REF_KEY: ret = __add_prelim_ref(prefs, offset, NULL, *info_level + 1, 0, bytenr, 1); break; case BTRFS_EXTENT_DATA_REF_KEY: { struct btrfs_extent_data_ref *dref; int count; u64 root; dref = (struct btrfs_extent_data_ref *)(&iref->offset); count = btrfs_extent_data_ref_count(leaf, dref); key.objectid = btrfs_extent_data_ref_objectid(leaf, dref); key.type = BTRFS_EXTENT_DATA_KEY; key.offset = btrfs_extent_data_ref_offset(leaf, dref); root = btrfs_extent_data_ref_root(leaf, dref); ret = __add_prelim_ref(prefs, root, &key, 0, 0, bytenr, count); break; } default: WARN_ON(1); } if (ret) return ret; ptr += btrfs_extent_inline_ref_size(type); } return 0; }
static void print_extent_item(struct extent_buffer *eb, int slot, int type) { struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; struct btrfs_disk_key key; unsigned long end; unsigned long ptr; u32 item_size = btrfs_item_size_nr(eb, slot); u64 flags; u64 offset; if (item_size < sizeof(*ei)) { #ifdef BTRFS_COMPAT_EXTENT_TREE_V0 struct btrfs_extent_item_v0 *ei0; BUG_ON(item_size != sizeof(*ei0)); ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0); printk(KERN_INFO "\t\textent refs %u\n", btrfs_extent_refs_v0(eb, ei0)); return; #else BUG(); #endif } ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); flags = btrfs_extent_flags(eb, ei); printk(KERN_INFO "\t\textent refs %llu gen %llu flags %llu\n", btrfs_extent_refs(eb, ei), btrfs_extent_generation(eb, ei), flags); if ((type == BTRFS_EXTENT_ITEM_KEY) && flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)(ei + 1); btrfs_tree_block_key(eb, info, &key); printk(KERN_INFO "\t\ttree block key (%llu %u %llu) " "level %d\n", btrfs_disk_key_objectid(&key), key.type, btrfs_disk_key_offset(&key), btrfs_tree_block_level(eb, info)); iref = (struct btrfs_extent_inline_ref *)(info + 1); } else { iref = (struct btrfs_extent_inline_ref *)(ei + 1); } ptr = (unsigned long)iref; end = (unsigned long)ei + item_size; while (ptr < end) { iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(eb, iref); offset = btrfs_extent_inline_ref_offset(eb, iref); switch (type) { case BTRFS_TREE_BLOCK_REF_KEY: printk(KERN_INFO "\t\ttree block backref " "root %llu\n", offset); break; case BTRFS_SHARED_BLOCK_REF_KEY: printk(KERN_INFO "\t\tshared block backref " "parent %llu\n", offset); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = (struct btrfs_extent_data_ref *)(&iref->offset); print_extent_data_ref(eb, dref); break; case BTRFS_SHARED_DATA_REF_KEY: sref = (struct btrfs_shared_data_ref *)(iref + 1); printk(KERN_INFO "\t\tshared data backref " "parent %llu count %u\n", offset, btrfs_shared_data_ref_count(eb, sref)); break; default: BUG(); } ptr += btrfs_extent_inline_ref_size(type); } WARN_ON(ptr > end); }
/* * calls iterate() for every inode that references the extent identified by * the given parameters. will use the path given as a parameter and return it * released. * when the iterator function returns a non-zero value, iteration stops. */ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, struct btrfs_path *path, u64 extent_item_objectid, u64 extent_offset, iterate_extent_inodes_t *iterate, void *ctx) { unsigned long ptr = 0; int last; int ret; int type; u64 logical; u32 item_size; struct btrfs_extent_inline_ref *eiref; struct btrfs_extent_data_ref *dref; struct extent_buffer *eb; struct btrfs_extent_item *ei; struct btrfs_key key; struct list_head data_refs = LIST_HEAD_INIT(data_refs); struct list_head shared_refs = LIST_HEAD_INIT(shared_refs); struct __data_ref *ref_d; struct __shared_ref *ref_s; eb = path->nodes[0]; ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); item_size = btrfs_item_size_nr(eb, path->slots[0]); /* first we iterate the inline refs, ... */ do { last = __get_extent_inline_ref(&ptr, eb, ei, item_size, &eiref, &type); if (last == -ENOENT) { ret = 0; break; } if (last < 0) { ret = last; break; } if (type == BTRFS_EXTENT_DATA_REF_KEY) { dref = (struct btrfs_extent_data_ref *)(&eiref->offset); ret = __data_list_add_eb(&data_refs, eb, dref); } else if (type == BTRFS_SHARED_DATA_REF_KEY) { logical = btrfs_extent_inline_ref_offset(eb, eiref); ret = __shared_list_add(&shared_refs, logical); } } while (!ret && !last); /* ... then we proceed to in-tree references and ... */ while (!ret) { ++path->slots[0]; if (path->slots[0] > btrfs_header_nritems(eb)) { ret = btrfs_next_leaf(fs_info->extent_root, path); if (ret) { if (ret == 1) ret = 0; /* we're done */ break; } eb = path->nodes[0]; } btrfs_item_key_to_cpu(eb, &key, path->slots[0]); if (key.objectid != extent_item_objectid) break; if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { dref = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_data_ref); ret = __data_list_add_eb(&data_refs, eb, dref); } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
static int __iter_shared_inline_ref_inodes(struct btrfs_fs_info *fs_info, u64 logical, u64 inum, u64 extent_data_item_offset, u64 extent_offset, struct btrfs_path *path, struct list_head *data_refs, iterate_extent_inodes_t *iterate, void *ctx) { u64 ref_root; u32 item_size; struct btrfs_key key; struct extent_buffer *eb; struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *eiref; struct __data_ref *ref; int ret; int type; int last; unsigned long ptr = 0; WARN_ON(!list_empty(data_refs)); ret = extent_from_logical(fs_info, logical, path, &key); if (ret & BTRFS_EXTENT_FLAG_DATA) ret = -EIO; if (ret < 0) goto out; eb = path->nodes[0]; ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); item_size = btrfs_item_size_nr(eb, path->slots[0]); ret = 0; ref_root = 0; /* * as done in iterate_extent_inodes, we first build a list of refs to * iterate, then free the path and then iterate them to avoid deadlocks. */ do { last = __get_extent_inline_ref(&ptr, eb, ei, item_size, &eiref, &type); if (last < 0) { ret = last; goto out; } if (type == BTRFS_TREE_BLOCK_REF_KEY || type == BTRFS_SHARED_BLOCK_REF_KEY) { ref_root = btrfs_extent_inline_ref_offset(eb, eiref); ret = __data_list_add(data_refs, inum, extent_data_item_offset, ref_root); } } while (!ret && !last); btrfs_release_path(path); if (ref_root == 0) { printk(KERN_ERR "btrfs: failed to find tree block ref " "for shared data backref %llu\n", logical); WARN_ON(1); ret = -EIO; } out: while (!list_empty(data_refs)) { ref = list_first_entry(data_refs, struct __data_ref, list); list_del(&ref->list); if (!ret) ret = iterate(ref->inum, extent_offset + ref->extent_data_item_offset, ref->root, ctx); kfree(ref); } return ret; }