static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root, struct btrfs_path *path, iterate_irefs_t *iterate, void *ctx) { int ret = 0; int slot; u32 cur; u32 len; u32 name_len; u64 parent = 0; int found = 0; struct extent_buffer *eb; struct btrfs_item *item; struct btrfs_inode_ref *iref; struct btrfs_key found_key; while (!ret) { ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, &found_key); if (ret < 0) break; if (ret) { ret = found ? 0 : -ENOENT; break; } ++found; parent = found_key.offset; slot = path->slots[0]; eb = btrfs_clone_extent_buffer(path->nodes[0]); if (!eb) { ret = -ENOMEM; break; } extent_buffer_get(eb); btrfs_release_path(path); item = btrfs_item_nr(slot); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); for (cur = 0; cur < btrfs_item_size(eb, item); cur += len) { name_len = btrfs_inode_ref_name_len(eb, iref); /* path must be released before calling iterate()! */ pr_debug("following ref at offset %u for inode %llu in " "tree %llu\n", cur, found_key.objectid, fs_root->objectid); ret = iterate(parent, name_len, (unsigned long)(iref + 1), eb, ctx); if (ret) break; len = sizeof(*iref) + name_len; iref = (struct btrfs_inode_ref *)((char *)iref + len); } free_extent_buffer(eb); } btrfs_release_path(path); return ret; }
/* * this iterates to turn a name (from iref/extref) into a full filesystem path. * Elements of the path are separated by '/' and the path is guaranteed to be * 0-terminated. the path is only given within the current file system. * Therefore, it never starts with a '/'. the caller is responsible to provide * "size" bytes in "dest". the dest buffer will be filled backwards. finally, * the start point of the resulting string is returned. this pointer is within * dest, normally. * in case the path buffer would overflow, the pointer is decremented further * as if output was written to the buffer, though no more output is actually * generated. that way, the caller can determine how much space would be * required for the path to fit into the buffer. in that case, the returned * value will be smaller than dest. callers must check this! */ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, u32 name_len, unsigned long name_off, struct extent_buffer *eb_in, u64 parent, char *dest, u32 size) { int slot; u64 next_inum; int ret; s64 bytes_left = ((s64)size) - 1; struct extent_buffer *eb = eb_in; struct btrfs_key found_key; struct btrfs_inode_ref *iref; if (bytes_left >= 0) dest[bytes_left] = '\0'; while (1) { bytes_left -= name_len; if (bytes_left >= 0) read_extent_buffer(eb, dest + bytes_left, name_off, name_len); if (eb != eb_in) free_extent_buffer(eb); ret = inode_ref_info(parent, 0, fs_root, path, &found_key); if (ret > 0) ret = -ENOENT; if (ret) break; next_inum = found_key.offset; /* regular exit ahead */ if (parent == next_inum) break; slot = path->slots[0]; eb = path->nodes[0]; /* make sure we can use eb after releasing the path */ if (eb != eb_in) eb->refs++; btrfs_release_path(path); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); name_len = btrfs_inode_ref_name_len(eb, iref); name_off = (unsigned long)(iref + 1); parent = next_inum; --bytes_left; if (bytes_left >= 0) dest[bytes_left] = '/'; } btrfs_release_path(path); if (ret) return ERR_PTR(ret); return dest + bytes_left; }
static int do_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct btrfs_dir_item *di; struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_path *path; size_t name_len = strlen(name); int ret = 0; if (name_len + size > BTRFS_MAX_XATTR_SIZE(root)) return -ENOSPC; path = btrfs_alloc_path(); if (!path) return -ENOMEM; /* first lets see if we already have this xattr */ di = btrfs_lookup_xattr(trans, root, path, btrfs_ino(inode), name, strlen(name), -1); if (IS_ERR(di)) { ret = PTR_ERR(di); goto out; } /* ok we already have this xattr, lets remove it */ if (di) { /* if we want create only exit */ if (flags & XATTR_CREATE) { ret = -EEXIST; goto out; } ret = btrfs_delete_one_dir_name(trans, root, path, di); BUG_ON(ret); btrfs_release_path(path); /* if we don't have a value then we are removing the xattr */ if (!value) goto out; } else { btrfs_release_path(path); if (flags & XATTR_REPLACE) { /* we couldn't find the attr to replace */ ret = -ENODATA; goto out; } } /* ok we have to create a completely new xattr */ ret = btrfs_insert_xattr_item(trans, root, path, btrfs_ino(inode), name, name_len, value, size); BUG_ON(ret); out: btrfs_free_path(path); return ret; }
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, char *name, int name_len, u64 dir, struct btrfs_key *location, u8 type) { int ret = 0; struct btrfs_path path; struct btrfs_dir_item *dir_item; char *name_ptr; struct btrfs_key key; u32 data_size; key.objectid = dir; key.flags = 0; btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); if (name_len == 1 && *name == '.') key.offset = 1; else if (name_len == 2 && name[0] == '.' && name[1] == '.') key.offset = 2; else ret = btrfs_name_hash(name, name_len, &key.offset); BUG_ON(ret); btrfs_init_path(&path); data_size = sizeof(*dir_item) + name_len; dir_item = insert_with_overflow(trans, root, &path, &key, data_size); if (!dir_item) { ret = -1; goto out; } btrfs_cpu_key_to_disk(&dir_item->location, location); btrfs_set_dir_type(dir_item, type); btrfs_set_dir_flags(dir_item, 0); btrfs_set_dir_name_len(dir_item, name_len); name_ptr = (char *)(dir_item + 1); memcpy(name_ptr, name, name_len); /* FIXME, use some real flag for selecting the extra index */ if (root == root->fs_info->tree_root) goto out; btrfs_release_path(root, &path); btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); key.offset = location->objectid; dir_item = insert_with_overflow(trans, root, &path, &key, data_size); if (!dir_item) { ret = -1; goto out; } btrfs_cpu_key_to_disk(&dir_item->location, location); btrfs_set_dir_type(dir_item, type); btrfs_set_dir_flags(dir_item, 0); btrfs_set_dir_name_len(dir_item, name_len); name_ptr = (char *)(dir_item + 1); memcpy(name_ptr, name, name_len); out: btrfs_release_path(root, &path); return ret; }
int btrfs_read_block_groups(struct btrfs_root *root) { struct btrfs_path path; int ret; int err = 0; struct btrfs_block_group_item *bi; struct btrfs_block_group_cache *cache; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_leaf *leaf; u64 group_size_blocks = BTRFS_BLOCK_GROUP_SIZE / root->blocksize; root = root->fs_info->extent_root; key.objectid = 0; key.offset = group_size_blocks; key.flags = 0; btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY); btrfs_init_path(&path); while(1) { ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, &path, 0, 0); if (ret != 0) { err = ret; break; } leaf = &path.nodes[0]->leaf; btrfs_disk_key_to_cpu(&found_key, &leaf->items[path.slots[0]].key); cache = malloc(sizeof(*cache)); if (!cache) { err = -1; break; } bi = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_block_group_item); memcpy(&cache->item, bi, sizeof(*bi)); memcpy(&cache->key, &found_key, sizeof(found_key)); key.objectid = found_key.objectid + found_key.offset; btrfs_release_path(root, &path); ret = radix_tree_insert(&root->fs_info->block_group_radix, found_key.objectid + found_key.offset - 1, (void *)cache); BUG_ON(ret); if (key.objectid >= btrfs_super_total_blocks(root->fs_info->disk_super)) break; } btrfs_release_path(root, &path); return 0; }
/* * copy the data in 'item' into the btree */ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_root_item *item) { struct btrfs_path *path; struct extent_buffer *l; int ret; int slot; unsigned long ptr; path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(trans, root, key, path, 0, 1); if (ret < 0) goto out; if (ret != 0) { btrfs_print_leaf(root, path->nodes[0]); printk(KERN_CRIT "unable to update root key %llu %u %llu\n", (unsigned long long)key->objectid, key->type, (unsigned long long)key->offset); BUG_ON(1); } l = path->nodes[0]; slot = path->slots[0]; ptr = btrfs_item_ptr_offset(l, slot); write_extent_buffer(l, item, ptr, sizeof(*item)); btrfs_mark_buffer_dirty(path->nodes[0]); out: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key) { struct btrfs_path *path; int ret; u32 refs; struct btrfs_root_item *ri; struct extent_buffer *leaf; path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(trans, root, key, path, -1, 1); if (ret < 0) goto out; if (ret) { btrfs_print_leaf(root, path->nodes[0]); printk("failed to del %llu %u %llu\n", (unsigned long long)key->objectid, key->type, (unsigned long long)key->offset); } BUG_ON(ret != 0); leaf = path->nodes[0]; ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); refs = btrfs_disk_root_refs(leaf, ri); BUG_ON(refs != 0); ret = btrfs_del_item(trans, root, path); out: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
static int add_new_free_space_info(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { struct btrfs_root *root = fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key; struct extent_buffer *leaf; int ret; key.objectid = block_group->key.objectid; key.type = BTRFS_FREE_SPACE_INFO_KEY; key.offset = block_group->key.offset; ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(*info)); if (ret) goto out; leaf = path->nodes[0]; info = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_free_space_info); btrfs_set_free_space_extent_count(leaf, info, 0); btrfs_set_free_space_flags(leaf, info, 0); btrfs_mark_buffer_dirty(leaf); ret = 0; out: btrfs_release_path(path); return ret; }
int __add_to_free_space_tree(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { struct btrfs_free_space_info *info; u32 flags; int ret; if (block_group->needs_free_space) { ret = __add_block_group_free_space(trans, fs_info, block_group, path); if (ret) return ret; } info = search_free_space_info(NULL, fs_info, block_group, path, 0); if (IS_ERR(info)) return PTR_ERR(info); flags = btrfs_free_space_flags(path->nodes[0], info); btrfs_release_path(path); if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { return modify_free_space_bitmap(trans, fs_info, block_group, path, start, size, 0); } else { return add_free_space_extent(trans, fs_info, block_group, path, start, size); } }
static int change_devices_uuid(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->chunk_root; struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; btrfs_init_path(&path); /* No transaction again */ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; while (1) { btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_DEV_ITEM_KEY || key.objectid != BTRFS_DEV_ITEMS_OBJECTID) goto next; ret = change_device_uuid(root, path.nodes[0], path.slots[0]); if (ret < 0) 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 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; }
/* drop the root item for 'key' from 'root' */ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key) { struct btrfs_path *path; int ret; u32 refs; struct btrfs_root_item *ri; struct extent_buffer *leaf; path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(trans, root, key, path, -1, 1); if (ret < 0) goto out; BUG_ON(ret != 0); leaf = path->nodes[0]; ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); refs = btrfs_disk_root_refs(leaf, ri); BUG_ON(refs != 0); ret = btrfs_del_item(trans, root, path); out: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_root_item *item) { struct btrfs_path *path; struct extent_buffer *l; int ret; int slot; unsigned long ptr; path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(trans, root, key, path, 0, 1); if (ret < 0) goto out; BUG_ON(ret != 0); l = path->nodes[0]; slot = path->slots[0]; ptr = btrfs_item_ptr_offset(l, slot); write_extent_buffer(l, item, ptr, sizeof(*item)); btrfs_mark_buffer_dirty(path->nodes[0]); out: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 blocknr) { struct btrfs_path path; int ret; struct btrfs_key key; struct btrfs_leaf *l; struct btrfs_extent_item *item; struct btrfs_key ins; u32 refs; find_free_extent(trans, root->fs_info->extent_root, 0, 0, (u64)-1, &ins); btrfs_init_path(&path); key.objectid = blocknr; key.flags = 0; btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); key.offset = 1; ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, 0, 1); if (ret != 0) BUG(); BUG_ON(ret != 0); l = &path.nodes[0]->leaf; item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item); refs = btrfs_extent_refs(item); btrfs_set_extent_refs(item, refs + 1); BUG_ON(list_empty(&path.nodes[0]->dirty)); btrfs_release_path(root->fs_info->extent_root, &path); finish_current_insert(trans, root->fs_info->extent_root); run_pending(trans, root->fs_info->extent_root); return 0; }
static int write_one_cache_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, struct btrfs_block_group_cache *cache) { int ret; int pending_ret; struct btrfs_root *extent_root = root->fs_info->extent_root; struct btrfs_block_group_item *bi; struct btrfs_key ins; ret = find_free_extent(trans, root, 0, 0, (u64)-1, &ins); if (ret) return ret; ret = btrfs_search_slot(trans, root->fs_info->extent_root, &cache->key, path, 0, 1); BUG_ON(ret); bi = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0], struct btrfs_block_group_item); memcpy(bi, &cache->item, sizeof(*bi)); dirty_tree_block(trans, extent_root, path->nodes[0]); btrfs_release_path(extent_root, path); finish_current_insert(trans, root); pending_ret = run_pending(trans, root); if (ret) return ret; if (pending_ret) return pending_ret; return 0; }
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; }
/* * remove an extent from the root, returns 0 on success */ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 blocknr, u64 num_blocks, int pin) { struct btrfs_path path; struct btrfs_key key; struct btrfs_fs_info *info = root->fs_info; struct btrfs_root *extent_root = info->extent_root; int ret; struct btrfs_extent_item *ei; struct btrfs_key ins; u32 refs; BUG_ON(pin && num_blocks != 1); key.objectid = blocknr; key.flags = 0; btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); key.offset = num_blocks; find_free_extent(trans, root, 0, 0, (u64)-1, &ins); btrfs_init_path(&path); ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1); if (ret) { btrfs_print_tree(extent_root, extent_root->node); printf("failed to find %llu\n", (u64)key.objectid); BUG(); } ei = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0], struct btrfs_extent_item); BUG_ON(ei->refs == 0); refs = btrfs_extent_refs(ei) - 1; btrfs_set_extent_refs(ei, refs); if (refs == 0) { u64 super_blocks_used; if (pin) { int err; unsigned long bl = blocknr; radix_tree_preload(GFP_KERNEL); err = radix_tree_insert(&info->pinned_radix, blocknr, (void *)bl); BUG_ON(err); radix_tree_preload_end(); } super_blocks_used = btrfs_super_blocks_used(info->disk_super); btrfs_set_super_blocks_used(info->disk_super, super_blocks_used - num_blocks); ret = btrfs_del_item(trans, extent_root, &path); if (!pin && extent_root->fs_info->last_insert.objectid > blocknr) extent_root->fs_info->last_insert.objectid = blocknr; if (ret) BUG(); ret = update_block_group(trans, root, blocknr, num_blocks, 0); BUG_ON(ret); } btrfs_release_path(extent_root, &path); finish_current_insert(trans, extent_root); return ret; }
/* * resolve an indirect backref in the form (root_id, key, level) * to a logical address */ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, struct btrfs_path *path, u64 time_seq, struct __prelim_ref *ref, struct ulist *parents, const u64 *extent_item_pos) { struct btrfs_root *root; struct btrfs_key root_key; struct extent_buffer *eb; int ret = 0; int root_level; int level = ref->level; root_key.objectid = ref->root_id; root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.offset = (u64)-1; root = btrfs_read_fs_root_no_name(fs_info, &root_key); if (IS_ERR(root)) { ret = PTR_ERR(root); goto out; } root_level = btrfs_old_root_level(root, time_seq); if (root_level + 1 == level) goto out; path->lowest_level = level; ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq); pr_debug("search slot in root %llu (level %d, ref count %d) returned " "%d for key (%llu %u %llu)\n", (unsigned long long)ref->root_id, level, ref->count, ret, (unsigned long long)ref->key_for_search.objectid, ref->key_for_search.type, (unsigned long long)ref->key_for_search.offset); if (ret < 0) goto out; eb = path->nodes[level]; while (!eb) { if (!level) { WARN_ON(1); ret = 1; goto out; } level--; eb = path->nodes[level]; } ret = add_all_parents(root, path, parents, level, &ref->key_for_search, time_seq, ref->wanted_disk_byte, extent_item_pos); out: path->lowest_level = 0; btrfs_release_path(path); return ret; }
static int remove_extent_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid) { struct btrfs_trans_handle trans; struct btrfs_extent_item *item; struct btrfs_path *path; struct btrfs_key key; u64 refs; int ret; btrfs_init_dummy_trans(&trans); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = num_bytes; path = btrfs_alloc_path(); if (!path) { test_msg("Couldn't allocate path\n"); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_search_slot(&trans, root, &key, path, 0, 1); if (ret) { test_msg("Couldn't find extent ref\n"); btrfs_free_path(path); return ret; } item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_extent_item); refs = btrfs_extent_refs(path->nodes[0], item); btrfs_set_extent_refs(path->nodes[0], item, refs - 1); btrfs_release_path(path); key.objectid = bytenr; if (parent) { key.type = BTRFS_SHARED_BLOCK_REF_KEY; key.offset = parent; } else { key.type = BTRFS_TREE_BLOCK_REF_KEY; key.offset = root_objectid; } ret = btrfs_search_slot(&trans, root, &key, path, -1, 1); if (ret) { test_msg("Couldn't find backref %d\n", ret); btrfs_free_path(path); return ret; } btrfs_del_item(&trans, root, path); btrfs_free_path(path); return ret; }
static int check_free_space_extents(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *cache, struct btrfs_path *path, struct free_space_extent *extents, unsigned int num_extents) { struct btrfs_free_space_info *info; u32 flags; int ret; info = search_free_space_info(trans, fs_info, cache, path, 0); if (IS_ERR(info)) { test_msg("Could not find free space info\n"); btrfs_release_path(path); return PTR_ERR(info); } flags = btrfs_free_space_flags(path->nodes[0], info); btrfs_release_path(path); ret = __check_free_space_extents(trans, fs_info, cache, path, extents, num_extents); if (ret) return ret; /* Flip it to the other format and check that for good measure. */ if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { ret = convert_free_space_to_extents(trans, fs_info, cache, path); if (ret) { test_msg("Could not convert to extents\n"); return ret; } } else { ret = convert_free_space_to_bitmaps(trans, fs_info, cache, path); if (ret) { test_msg("Could not convert to bitmaps\n"); return ret; } } return __check_free_space_extents(trans, fs_info, cache, path, extents, num_extents); }
int btrfs_find_orphan_roots(struct btrfs_root *tree_root) { struct extent_buffer *leaf; struct btrfs_path *path; struct btrfs_key key; int err = 0; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; key.objectid = BTRFS_ORPHAN_OBJECTID; key.type = BTRFS_ORPHAN_ITEM_KEY; key.offset = 0; while (1) { ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); if (ret < 0) { err = ret; break; } leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(tree_root, path); if (ret < 0) err = ret; if (ret != 0) break; leaf = path->nodes[0]; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); btrfs_release_path(tree_root, path); if (key.objectid != BTRFS_ORPHAN_OBJECTID || key.type != BTRFS_ORPHAN_ITEM_KEY) break; ret = btrfs_find_dead_roots(tree_root, key.offset); if (ret) { err = ret; break; } key.offset++; } btrfs_free_path(path); return err; }
static int copy_metadata(struct btrfs_root *root, int fd, struct btrfs_key *key) { struct btrfs_path path; struct btrfs_inode_item *inode_item; int ret; btrfs_init_path(&path); ret = btrfs_lookup_inode(NULL, root, &path, key, 0); if (ret == 0) { struct btrfs_timespec *bts; struct timespec times[2]; inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item); ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item), btrfs_inode_gid(path.nodes[0], inode_item)); if (ret) { error("failed to change owner: %m"); goto out; } ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item)); if (ret) { error("failed to change mode: %m"); goto out; } bts = btrfs_inode_atime(inode_item); times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); times[0].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); bts = btrfs_inode_mtime(inode_item); times[1].tv_sec = btrfs_timespec_sec(path.nodes[0], bts); times[1].tv_nsec = btrfs_timespec_nsec(path.nodes[0], bts); ret = futimens(fd, times); if (ret) { error("failed to set times: %m"); goto out; } } out: btrfs_release_path(&path); return ret; }
static int ondisk_del(struct btrfs_trans_handle *trans, struct btrfs_dedup_info *dedup_info, u64 bytenr) { struct btrfs_root *dedup_root = dedup_info->dedup_root; struct btrfs_path *path; struct btrfs_key key; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; key.objectid = bytenr; key.type = BTRFS_DEDUP_BYTENR_ITEM_KEY; key.offset = 0; mutex_lock(&dedup_info->lock); ret = ondisk_search_bytenr(trans, dedup_info, path, bytenr, 1); if (ret <= 0) goto out; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); btrfs_del_item(trans, dedup_root, path); btrfs_release_path(path); /* Search for hash item and delete it */ key.objectid = key.offset; key.type = BTRFS_DEDUP_HASH_ITEM_KEY; key.offset = bytenr; ret = btrfs_search_slot(trans, dedup_root, &key, path, -1, 1); if (WARN_ON(ret > 0)) { ret = -ENOENT; goto out; } if (ret < 0) goto out; btrfs_del_item(trans, dedup_root, path); out: btrfs_free_path(path); mutex_unlock(&dedup_info->lock); return ret; }
/* * We can't use btrfs_next_item() in modify_free_space_bitmap() because * btrfs_next_leaf() doesn't get the path for writing. We can forgo the fancy * tree walking in btrfs_next_leaf() anyways because we know exactly what we're * looking for. */ static int free_space_next_bitmap(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *p) { struct btrfs_key key; if (p->slots[0] + 1 < btrfs_header_nritems(p->nodes[0])) { p->slots[0]++; return 0; } btrfs_item_key_to_cpu(p->nodes[0], &key, p->slots[0]); btrfs_release_path(p); key.objectid += key.offset; key.type = (u8)-1; key.offset = (u64)-1; return btrfs_search_prev_slot(trans, root, &key, p, 0, 1); }
/* * search forward for a root, starting with objectid 'search_start' * if a root key is found, the objectid we find is filled into 'found_objectid' * and 0 is returned. < 0 is returned on error, 1 if there is nothing * left in the tree. */ int btrfs_search_root(struct btrfs_root *root, u64 search_start, u64 *found_objectid) { struct btrfs_path *path; struct btrfs_key search_key; int ret; root = root->fs_info->tree_root; search_key.objectid = search_start; search_key.type = (u8)-1; search_key.offset = (u64)-1; path = btrfs_alloc_path(); BUG_ON(!path); again: ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); if (ret < 0) goto out; if (ret == 0) { ret = 1; goto out; } if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { ret = btrfs_next_leaf(root, path); if (ret) goto out; } btrfs_item_key_to_cpu(path->nodes[0], &search_key, path->slots[0]); if (search_key.type != BTRFS_ROOT_ITEM_KEY) { search_key.offset++; btrfs_release_path(root, path); goto again; } ret = 0; *found_objectid = search_key.objectid; out: btrfs_free_path(path); return ret; }
static int update_free_space_extent_count(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, int new_extents) { struct btrfs_free_space_info *info; u32 flags; u32 extent_count; int ret = 0; if (new_extents == 0) return 0; info = search_free_space_info(trans, fs_info, block_group, path, 1); if (IS_ERR(info)) { ret = PTR_ERR(info); goto out; } flags = btrfs_free_space_flags(path->nodes[0], info); extent_count = btrfs_free_space_extent_count(path->nodes[0], info); extent_count += new_extents; btrfs_set_free_space_extent_count(path->nodes[0], info, extent_count); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_release_path(path); if (!(flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count > block_group->bitmap_high_thresh) { ret = convert_free_space_to_bitmaps(trans, fs_info, block_group, path); } else if ((flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count < block_group->bitmap_low_thresh) { ret = convert_free_space_to_extents(trans, fs_info, block_group, path); } out: return ret; }
static int clear_free_space_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_path *path; struct btrfs_key key; int nr; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; path->leave_spinning = 1; key.objectid = 0; key.type = 0; key.offset = 0; while (1) { ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret < 0) goto out; nr = btrfs_header_nritems(path->nodes[0]); if (!nr) break; path->slots[0] = 0; ret = btrfs_del_items(trans, root, path, 0, nr); if (ret) goto out; btrfs_release_path(path); } ret = 0; out: btrfs_free_path(path); return ret; }
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct btrfs_root_item *item, struct btrfs_key *key) { struct btrfs_path *path; struct btrfs_key search_key; struct btrfs_key found_key; struct extent_buffer *l; int ret; int slot; search_key.objectid = objectid; search_key.type = BTRFS_ROOT_ITEM_KEY; search_key.offset = (u64)-1; path = btrfs_alloc_path(); BUG_ON(!path); ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); if (ret < 0) goto out; BUG_ON(ret == 0); l = path->nodes[0]; BUG_ON(path->slots[0] == 0); slot = path->slots[0] - 1; btrfs_item_key_to_cpu(l, &found_key, slot); if (found_key.objectid != objectid) { ret = 1; goto out; } read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot), sizeof(*item)); memcpy(key, &found_key, sizeof(found_key)); ret = 0; out: btrfs_release_path(root, path); btrfs_free_path(path); return ret; }
static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 blocknr, u32 *refs) { struct btrfs_path path; int ret; struct btrfs_key key; struct btrfs_leaf *l; struct btrfs_extent_item *item; btrfs_init_path(&path); key.objectid = blocknr; key.offset = 1; key.flags = 0; btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, 0, 0); if (ret != 0) BUG(); l = &path.nodes[0]->leaf; item = btrfs_item_ptr(l, path.slots[0], struct btrfs_extent_item); *refs = btrfs_extent_refs(item); btrfs_release_path(root->fs_info->extent_root, &path); return 0; }
int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, struct btrfs_path *path, iterate_extent_inodes_t *iterate, void *ctx) { int ret; u64 extent_item_pos; u64 flags = 0; struct btrfs_key found_key; int search_commit_root = path->search_commit_root; ret = extent_from_logical(fs_info, logical, path, &found_key, &flags); btrfs_release_path(path); if (ret < 0) return ret; if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) return -EINVAL; extent_item_pos = logical - found_key.objectid; ret = iterate_extent_inodes(fs_info, found_key.objectid, extent_item_pos, search_commit_root, iterate, ctx); return ret; }