/* * 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; }
/* * 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; }