static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, u64 root_objectid, u32 generation) { struct btrfs_root *root; struct inode *inode; struct btrfs_key key; key.objectid = root_objectid; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); key.offset = (u64)-1; root = btrfs_read_fs_root_no_name(btrfs_sb(sb)->fs_info, &key); if (IS_ERR(root)) return ERR_CAST(root); key.objectid = objectid; btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); key.offset = 0; inode = btrfs_iget(sb, &key, root, NULL); if (IS_ERR(inode)) return (void *)inode; if (generation != inode->i_generation) { iput(inode); return ERR_PTR(-ESTALE); } return d_obtain_alias(inode); }
/* * 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 struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, u64 root_objectid, u32 generation, int check_generation) { struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_root *root; struct inode *inode; struct btrfs_key key; int index; int err = 0; if (objectid < BTRFS_FIRST_FREE_OBJECTID) return ERR_PTR(-ESTALE); key.objectid = root_objectid; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); key.offset = (u64)-1; index = srcu_read_lock(&fs_info->subvol_srcu); root = btrfs_read_fs_root_no_name(fs_info, &key); if (IS_ERR(root)) { err = PTR_ERR(root); goto fail; } if (btrfs_root_refs(&root->root_item) == 0) { err = -ENOENT; goto fail; } key.objectid = objectid; btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); key.offset = 0; inode = btrfs_iget(sb, &key, root, NULL); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto fail; } srcu_read_unlock(&fs_info->subvol_srcu, index); if (check_generation && generation != inode->i_generation) { iput(inode); return ERR_PTR(-ESTALE); } return d_obtain_alias(inode); fail: srcu_read_unlock(&fs_info->subvol_srcu, index); return ERR_PTR(err); }
int btrfs_find_orphan_roots(struct btrfs_root *tree_root) { struct extent_buffer *leaf; struct btrfs_path *path; struct btrfs_key key; struct btrfs_key root_key; struct btrfs_root *root; 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; root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.offset = (u64)-1; 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; root_key.objectid = key.offset; key.offset++; root = btrfs_read_fs_root_no_name(tree_root->fs_info, &root_key); if (!IS_ERR(root)) continue; ret = PTR_ERR(root); if (ret != -ENOENT) { err = ret; break; } ret = btrfs_find_dead_roots(tree_root, root_key.objectid); if (ret) { err = ret; break; } } btrfs_free_path(path); return err; }
static noinline int create_subvol(struct btrfs_root *root, struct dentry *dentry, char *name, int namelen) { struct btrfs_trans_handle *trans; struct btrfs_key key; struct btrfs_root_item root_item; struct btrfs_inode_item *inode_item; struct extent_buffer *leaf; struct btrfs_root *new_root; struct inode *dir = dentry->d_parent->d_inode; int ret; int err; u64 objectid; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root, 0, &objectid); if (ret) return ret; /* * 1 - inode item * 2 - refs * 1 - root item * 2 - dir items */ trans = btrfs_start_transaction(root, 6); if (IS_ERR(trans)) return PTR_ERR(trans); leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, objectid, NULL, 0, 0, 0); if (IS_ERR(leaf)) { ret = PTR_ERR(leaf); goto fail; } memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header)); btrfs_set_header_bytenr(leaf, leaf->start); btrfs_set_header_generation(leaf, trans->transid); btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(leaf, objectid); write_extent_buffer(leaf, root->fs_info->fsid, (unsigned long)btrfs_header_fsid(leaf), BTRFS_FSID_SIZE); write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, (unsigned long)btrfs_header_chunk_tree_uuid(leaf), BTRFS_UUID_SIZE); btrfs_mark_buffer_dirty(leaf); inode_item = &root_item.inode; memset(inode_item, 0, sizeof(*inode_item)); inode_item->generation = cpu_to_le64(1); inode_item->size = cpu_to_le64(3); inode_item->nlink = cpu_to_le32(1); inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->mode = cpu_to_le32(S_IFDIR | 0755); root_item.flags = 0; root_item.byte_limit = 0; inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_level(&root_item, 0); btrfs_set_root_refs(&root_item, 1); btrfs_set_root_used(&root_item, leaf->len); btrfs_set_root_last_snapshot(&root_item, 0); memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress)); root_item.drop_level = 0; btrfs_tree_unlock(leaf); free_extent_buffer(leaf); leaf = NULL; btrfs_set_root_dirid(&root_item, new_dirid); key.objectid = objectid; key.offset = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, &root_item); if (ret) goto fail; key.offset = (u64)-1; new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); BUG_ON(IS_ERR(new_root)); btrfs_record_root_in_trans(trans, new_root); ret = btrfs_create_subvol_root(trans, new_root, new_dirid, BTRFS_I(dir)->block_group); /* * insert the directory item */ ret = btrfs_set_inode_index(dir, &index); BUG_ON(ret); ret = btrfs_insert_dir_item(trans, root, name, namelen, dir->i_ino, &key, BTRFS_FT_DIR, index); if (ret) goto fail; btrfs_i_size_write(dir, dir->i_size + namelen * 2); ret = btrfs_update_inode(trans, root, dir); BUG_ON(ret); ret = btrfs_add_root_ref(trans, root->fs_info->tree_root, objectid, root->root_key.objectid, dir->i_ino, index, name, namelen); BUG_ON(ret); d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); fail: err = btrfs_commit_transaction(trans, root); if (err && !ret) ret = err; return ret; }
static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_pending_snapshot *pending) { struct btrfs_key key; struct btrfs_root_item *new_root_item; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *root = pending->root; struct btrfs_root *parent_root; struct inode *parent_inode; struct dentry *dentry; struct extent_buffer *tmp; struct extent_buffer *old; int ret; int retries = 0; u64 to_reserve = 0; u64 index = 0; u64 objectid; new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); if (!new_root_item) { pending->error = -ENOMEM; goto fail; } ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid); if (ret) { pending->error = ret; goto fail; } btrfs_reloc_pre_snapshot(trans, pending, &to_reserve); btrfs_orphan_pre_snapshot(trans, pending, &to_reserve); if (to_reserve > 0) { ret = btrfs_block_rsv_add(trans, root, &pending->block_rsv, to_reserve, &retries); if (ret) { pending->error = ret; goto fail; } } key.objectid = objectid; key.offset = (u64)-1; key.type = BTRFS_ROOT_ITEM_KEY; trans->block_rsv = &pending->block_rsv; dentry = pending->dentry; parent_inode = dentry->d_parent->d_inode; parent_root = BTRFS_I(parent_inode)->root; record_root_in_trans(trans, parent_root); /* * insert the directory item */ ret = btrfs_set_inode_index(parent_inode, &index); BUG_ON(ret); ret = btrfs_insert_dir_item(trans, parent_root, dentry->d_name.name, dentry->d_name.len, parent_inode->i_ino, &key, BTRFS_FT_DIR, index); BUG_ON(ret); btrfs_i_size_write(parent_inode, parent_inode->i_size + dentry->d_name.len * 2); ret = btrfs_update_inode(trans, parent_root, parent_inode); BUG_ON(ret); record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); old = btrfs_lock_root_node(root); btrfs_cow_block(trans, root, old, NULL, 0, &old); btrfs_set_lock_blocking(old); btrfs_copy_root(trans, root, old, &tmp, objectid); btrfs_tree_unlock(old); free_extent_buffer(old); btrfs_set_root_node(new_root_item, tmp); /* record when the snapshot was created in key.offset */ key.offset = trans->transid; ret = btrfs_insert_root(trans, tree_root, &key, new_root_item); btrfs_tree_unlock(tmp); free_extent_buffer(tmp); BUG_ON(ret); /* * insert root back/forward references */ ret = btrfs_add_root_ref(trans, tree_root, objectid, parent_root->root_key.objectid, parent_inode->i_ino, index, dentry->d_name.name, dentry->d_name.len); BUG_ON(ret); key.offset = (u64)-1; pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key); BUG_ON(IS_ERR(pending->snap)); btrfs_reloc_post_snapshot(trans, pending); btrfs_orphan_post_snapshot(trans, pending); fail: kfree(new_root_item); btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1); return 0; }
/* * 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, u64 total_refs) { struct btrfs_root *root; struct btrfs_key root_key; struct extent_buffer *eb; int ret = 0; int root_level; int level = ref->level; int index; root_key.objectid = ref->root_id; root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.offset = (u64)-1; index = srcu_read_lock(&fs_info->subvol_srcu); root = btrfs_read_fs_root_no_name(fs_info, &root_key); if (IS_ERR(root)) { srcu_read_unlock(&fs_info->subvol_srcu, index); ret = PTR_ERR(root); goto out; } root_level = btrfs_old_root_level(root, time_seq); if (root_level + 1 == level) { srcu_read_unlock(&fs_info->subvol_srcu, index); goto out; } path->lowest_level = level; ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq); /* root node has been locked, we can release @subvol_srcu safely here */ srcu_read_unlock(&fs_info->subvol_srcu, index); pr_debug("search slot in root %llu (level %d, ref count %d) returned " "%d for key (%llu %u %llu)\n", ref->root_id, level, ref->count, ret, ref->key_for_search.objectid, ref->key_for_search.type, ref->key_for_search.offset); if (ret < 0) goto out; eb = path->nodes[level]; while (!eb) { if (WARN_ON(!level)) { ret = 1; goto out; } level--; eb = path->nodes[level]; } ret = add_all_parents(root, path, parents, ref, level, time_seq, extent_item_pos, total_refs); out: path->lowest_level = 0; btrfs_release_path(path); return ret; }
/* * run through the list of inodes in the FS that need * defragging */ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) { struct inode_defrag *defrag; struct btrfs_root *inode_root; struct inode *inode; struct rb_node *n; struct btrfs_key key; struct btrfs_ioctl_defrag_range_args range; u64 first_ino = 0; u64 root_objectid = 0; int num_defrag; int defrag_batch = 1024; memset(&range, 0, sizeof(range)); range.len = (u64)-1; atomic_inc(&fs_info->defrag_running); spin_lock(&fs_info->defrag_inodes_lock); while(1) { n = NULL; /* find an inode to defrag */ defrag = btrfs_find_defrag_inode(fs_info, root_objectid, first_ino, &n); if (!defrag) { if (n) { defrag = rb_entry(n, struct inode_defrag, rb_node); } else if (root_objectid || first_ino) { root_objectid = 0; first_ino = 0; continue; } else { break; } } /* remove it from the rbtree */ first_ino = defrag->ino + 1; root_objectid = defrag->root; rb_erase(&defrag->rb_node, &fs_info->defrag_inodes); if (btrfs_fs_closing(fs_info)) goto next_free; spin_unlock(&fs_info->defrag_inodes_lock); /* get the inode */ key.objectid = defrag->root; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); key.offset = (u64)-1; inode_root = btrfs_read_fs_root_no_name(fs_info, &key); if (IS_ERR(inode_root)) goto next; key.objectid = defrag->ino; btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); key.offset = 0; inode = btrfs_iget(fs_info->sb, &key, inode_root, NULL); if (IS_ERR(inode)) goto next; /* do a chunk of defrag */ clear_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags); range.start = defrag->last_offset; num_defrag = btrfs_defrag_file(inode, NULL, &range, defrag->transid, defrag_batch); /* * if we filled the whole defrag batch, there * must be more work to do. Queue this defrag * again */ if (num_defrag == defrag_batch) { defrag->last_offset = range.start; __btrfs_add_inode_defrag(inode, defrag); /* * we don't want to kfree defrag, we added it back to * the rbtree */ defrag = NULL; } else if (defrag->last_offset && !defrag->cycled) { /* * we didn't fill our defrag batch, but * we didn't start at zero. Make sure we loop * around to the start of the file. */ defrag->last_offset = 0; defrag->cycled = 1; __btrfs_add_inode_defrag(inode, defrag); defrag = NULL; } iput(inode); next: spin_lock(&fs_info->defrag_inodes_lock); next_free: kfree(defrag); }