Commit 2619ba1f authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse

Btrfs: subvolumes

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 2932f3ec
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Check compat and incompat flags on the inode * Check compat and incompat flags on the inode
* Add virtual filesystems, mountable snapshots * Add virtual filesystems, mountable snapshots
* Get rid of struct ctree_path, limiting tree levels held at one time * Get rid of struct ctree_path, limiting tree levels held at one time
* EEXIST for dirs instead of hash overflow
* Release * Release
* Do real tree locking * Do real tree locking
* Add extent mirroring (backup copies of blocks) * Add extent mirroring (backup copies of blocks)
......
...@@ -301,6 +301,12 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info, ...@@ -301,6 +301,12 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
int ret = 0; int ret = 0;
printk("read_fs_root looking for %Lu %Lu %u\n", location->objectid, location->offset, location->flags); printk("read_fs_root looking for %Lu %Lu %u\n", location->objectid, location->offset, location->flags);
root = radix_tree_lookup(&fs_info->fs_roots_radix,
(unsigned long)location->objectid);
if (root) {
printk("found %p in cache\n", root);
return root;
}
root = kmalloc(sizeof(*root), GFP_NOFS); root = kmalloc(sizeof(*root), GFP_NOFS);
if (!root) { if (!root) {
printk("failed1\n"); printk("failed1\n");
...@@ -349,7 +355,8 @@ printk("internal search_slot gives us %d\n", ret); ...@@ -349,7 +355,8 @@ printk("internal search_slot gives us %d\n", ret);
insert: insert:
printk("inserting %p\n", root); printk("inserting %p\n", root);
root->ref_cows = 1; root->ref_cows = 1;
ret = radix_tree_insert(&fs_info->fs_roots_radix, (unsigned long)root, ret = radix_tree_insert(&fs_info->fs_roots_radix,
(unsigned long)root->root_key.objectid,
root); root);
if (ret) { if (ret) {
printk("radix_tree_insert gives us %d\n", ret); printk("radix_tree_insert gives us %d\n", ret);
...@@ -460,6 +467,20 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -460,6 +467,20 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
return 0; return 0;
} }
static int free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
{
radix_tree_delete(&fs_info->fs_roots_radix,
(unsigned long)root->root_key.objectid);
if (root->inode)
iput(root->inode);
if (root->node)
brelse(root->node);
if (root->commit_root)
brelse(root->commit_root);
kfree(root);
return 0;
}
int del_fs_roots(struct btrfs_fs_info *fs_info) int del_fs_roots(struct btrfs_fs_info *fs_info)
{ {
int ret; int ret;
...@@ -472,19 +493,8 @@ int del_fs_roots(struct btrfs_fs_info *fs_info) ...@@ -472,19 +493,8 @@ int del_fs_roots(struct btrfs_fs_info *fs_info)
ARRAY_SIZE(gang)); ARRAY_SIZE(gang));
if (!ret) if (!ret)
break; break;
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++)
radix_tree_delete(&fs_info->fs_roots_radix, free_fs_root(fs_info, gang[i]);
(unsigned long)gang[i]);
if (gang[i]->inode)
iput(gang[i]->inode);
else
printk("no inode for root %p\n", gang[i]);
if (gang[i]->node)
brelse(gang[i]->node);
if (gang[i]->commit_root)
brelse(gang[i]->commit_root);
kfree(gang[i]);
}
} }
return 0; return 0;
} }
......
...@@ -495,10 +495,6 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -495,10 +495,6 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
if (inode->i_state & I_NEW) { if (inode->i_state & I_NEW) {
if (sub_root != root) { if (sub_root != root) {
ret = radix_tree_insert(
&root->fs_info->fs_roots_radix,
(unsigned long)sub_root,
sub_root);
printk("adding new root for inode %lu root %p (found %p)\n", inode->i_ino, sub_root, BTRFS_I(inode)->root); printk("adding new root for inode %lu root %p (found %p)\n", inode->i_ino, sub_root, BTRFS_I(inode)->root);
igrab(inode); igrab(inode);
sub_root->inode = inode; sub_root->inode = inode;
...@@ -723,22 +719,19 @@ static int btrfs_write_inode(struct inode *inode, int wait) ...@@ -723,22 +719,19 @@ static int btrfs_write_inode(struct inode *inode, int wait)
} }
static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
struct inode *dir, int mode) struct btrfs_root *root,
u64 objectid, int mode)
{ {
struct inode *inode; struct inode *inode;
struct btrfs_inode_item inode_item; struct btrfs_inode_item inode_item;
struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_key *location; struct btrfs_key *location;
int ret; int ret;
u64 objectid;
inode = new_inode(dir->i_sb); inode = new_inode(root->fs_info->sb);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
BTRFS_I(inode)->root = BTRFS_I(dir)->root; BTRFS_I(inode)->root = root;
ret = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
BUG_ON(ret);
inode->i_uid = current->fsuid; inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid; inode->i_gid = current->fsgid;
...@@ -804,10 +797,18 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, ...@@ -804,10 +797,18 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
struct inode *inode; struct inode *inode;
int err; int err;
int drop_inode = 0; int drop_inode = 0;
u64 objectid;
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
inode = btrfs_new_inode(trans, dir, mode);
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
if (err) {
err = -ENOSPC;
goto out_unlock;
}
inode = btrfs_new_inode(trans, root, objectid, mode);
err = PTR_ERR(inode); err = PTR_ERR(inode);
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_unlock; goto out_unlock;
...@@ -833,9 +834,9 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, ...@@ -833,9 +834,9 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
} }
static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans, static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *dir) struct btrfs_root *root,
u64 objectid, u64 dirid)
{ {
struct btrfs_root *root = BTRFS_I(dir)->root;
int ret; int ret;
char buf[2]; char buf[2];
struct btrfs_key key; struct btrfs_key key;
...@@ -843,22 +844,20 @@ static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans, ...@@ -843,22 +844,20 @@ static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans,
buf[0] = '.'; buf[0] = '.';
buf[1] = '.'; buf[1] = '.';
key.objectid = inode->i_ino; key.objectid = objectid;
key.offset = 0; key.offset = 0;
key.flags = 0; key.flags = 0;
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
ret = btrfs_insert_dir_item(trans, root, buf, 1, inode->i_ino, ret = btrfs_insert_dir_item(trans, root, buf, 1, objectid,
&key, 1); &key, 1);
if (ret) if (ret)
goto error; goto error;
key.objectid = dir->i_ino; key.objectid = dirid;
ret = btrfs_insert_dir_item(trans, root, buf, 2, inode->i_ino, ret = btrfs_insert_dir_item(trans, root, buf, 2, objectid,
&key, 1); &key, 1);
if (ret) if (ret)
goto error; goto error;
inode->i_size = 6;
ret = btrfs_update_inode(trans, root, inode);
error: error:
return ret; return ret;
} }
...@@ -870,6 +869,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -870,6 +869,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *root = BTRFS_I(dir)->root;
int err = 0; int err = 0;
int drop_on_err = 0; int drop_on_err = 0;
u64 objectid;
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
...@@ -877,7 +877,14 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -877,7 +877,14 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
err = PTR_ERR(trans); err = PTR_ERR(trans);
goto out_unlock; goto out_unlock;
} }
inode = btrfs_new_inode(trans, dir, S_IFDIR | mode);
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
if (err) {
err = -ENOSPC;
goto out_unlock;
}
inode = btrfs_new_inode(trans, root, objectid, S_IFDIR | mode);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
goto out_fail; goto out_fail;
...@@ -886,7 +893,12 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -886,7 +893,12 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
inode->i_op = &btrfs_dir_inode_operations; inode->i_op = &btrfs_dir_inode_operations;
inode->i_fop = &btrfs_dir_file_operations; inode->i_fop = &btrfs_dir_file_operations;
err = btrfs_make_empty_dir(trans, inode, dir); err = btrfs_make_empty_dir(trans, root, inode->i_ino, dir->i_ino);
if (err)
goto out_fail;
inode->i_size = 6;
err = btrfs_update_inode(trans, root, inode);
if (err) if (err)
goto out_fail; goto out_fail;
err = btrfs_add_link(trans, dentry, inode); err = btrfs_add_link(trans, dentry, inode);
...@@ -1666,6 +1678,102 @@ static ssize_t btrfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1666,6 +1678,102 @@ static ssize_t btrfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
return retval; return retval;
} }
static int create_subvol(struct btrfs_root *root, 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 buffer_head *subvol;
struct btrfs_leaf *leaf;
struct btrfs_root *new_root;
struct inode *inode;
int ret;
u64 objectid;
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
subvol = btrfs_alloc_free_block(trans, root);
leaf = btrfs_buffer_leaf(subvol);
btrfs_set_header_nritems(&leaf->header, 0);
btrfs_set_header_level(&leaf->header, 0);
btrfs_set_header_blocknr(&leaf->header, subvol->b_blocknr);
btrfs_set_header_generation(&leaf->header, trans->transid);
memcpy(leaf->header.fsid, root->fs_info->disk_super->fsid,
sizeof(leaf->header.fsid));
inode_item = &root_item.inode;
memset(inode_item, 0, sizeof(*inode_item));
btrfs_set_inode_generation(inode_item, 1);
btrfs_set_inode_size(inode_item, 3);
btrfs_set_inode_nlink(inode_item, 1);
btrfs_set_inode_nblocks(inode_item, 1);
btrfs_set_inode_mode(inode_item, S_IFDIR | 0755);
btrfs_set_root_blocknr(&root_item, subvol->b_blocknr);
btrfs_set_root_refs(&root_item, 1);
mark_buffer_dirty(subvol);
brelse(subvol);
subvol = NULL;
ret = btrfs_find_free_objectid(trans, root->fs_info->tree_root,
0, &objectid);
BUG_ON(ret);
btrfs_set_root_dirid(&root_item, new_dirid);
key.objectid = objectid;
key.offset = 1;
key.flags = 0;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
&root_item);
BUG_ON(ret);
/*
* insert the directory item
*/
key.offset = (u64)-1;
ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
name, namelen,
root->fs_info->sb->s_root->d_inode->i_ino,
&key, 0);
BUG_ON(ret);
ret = btrfs_commit_transaction(trans, root);
BUG_ON(ret);
new_root = btrfs_read_fs_root(root->fs_info, &key);
BUG_ON(!new_root);
trans = btrfs_start_transaction(new_root, 1);
BUG_ON(!trans);
inode = btrfs_new_inode(trans, new_root, new_dirid, S_IFDIR | 0700);
inode->i_op = &btrfs_dir_inode_operations;
inode->i_fop = &btrfs_dir_file_operations;
ret = btrfs_make_empty_dir(trans, new_root, new_dirid, new_dirid);
BUG_ON(ret);
inode->i_nlink = 1;
inode->i_size = 6;
ret = btrfs_update_inode(trans, new_root, inode);
BUG_ON(ret);
ret = btrfs_commit_transaction(trans, new_root);
BUG_ON(ret);
iput(inode);
mutex_unlock(&root->fs_info->fs_mutex);
return 0;
}
static int create_snapshot(struct btrfs_root *root, char *name, int namelen) static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
{ {
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
...@@ -1674,6 +1782,9 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -1674,6 +1782,9 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
int ret; int ret;
u64 objectid; u64 objectid;
if (!root->ref_cows)
return -EINVAL;
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans); BUG_ON(!trans);
...@@ -1685,7 +1796,6 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -1685,7 +1796,6 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
0, &objectid); 0, &objectid);
BUG_ON(ret); BUG_ON(ret);
memset(&new_root_item, 0, sizeof(new_root_item));
memcpy(&new_root_item, &root->root_item, memcpy(&new_root_item, &root->root_item,
sizeof(new_root_item)); sizeof(new_root_item));
...@@ -1728,9 +1838,9 @@ static int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int ...@@ -1728,9 +1838,9 @@ static int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
struct btrfs_ioctl_vol_args vol_args; struct btrfs_ioctl_vol_args vol_args;
int ret; int ret;
int namelen; int namelen;
struct btrfs_path *path;
u64 root_dirid;
if (!root->ref_cows)
return -EINVAL;
switch (cmd) { switch (cmd) {
case BTRFS_IOC_SNAP_CREATE: case BTRFS_IOC_SNAP_CREATE:
if (copy_from_user(&vol_args, if (copy_from_user(&vol_args,
...@@ -1740,6 +1850,22 @@ static int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int ...@@ -1740,6 +1850,22 @@ static int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
namelen = strlen(vol_args.name); namelen = strlen(vol_args.name);
if (namelen > BTRFS_VOL_NAME_MAX) if (namelen > BTRFS_VOL_NAME_MAX)
return -EINVAL; return -EINVAL;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
root_dirid = btrfs_root_dirid(
&root->fs_info->tree_root->root_item);
mutex_lock(&root->fs_info->fs_mutex);
ret = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root,
path, root_dirid,
vol_args.name, namelen, 0);
mutex_unlock(&root->fs_info->fs_mutex);
if (ret == 0)
return -EEXIST;
if (root == root->fs_info->tree_root)
ret = create_subvol(root, vol_args.name, namelen);
else
ret = create_snapshot(root, vol_args.name, namelen); ret = create_snapshot(root, vol_args.name, namelen);
WARN_ON(ret); WARN_ON(ret);
break; break;
......
...@@ -64,7 +64,8 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, ...@@ -64,7 +64,8 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
if (root != root->fs_info->tree_root && root->last_trans < if (root != root->fs_info->tree_root && root->last_trans <
running_trans_id) { running_trans_id) {
radix_tree_tag_set(&root->fs_info->fs_roots_radix, radix_tree_tag_set(&root->fs_info->fs_roots_radix,
(unsigned long)root, BTRFS_ROOT_TRANS_TAG); (unsigned long)root->root_key.objectid,
BTRFS_ROOT_TRANS_TAG);
root->commit_root = root->node; root->commit_root = root->node;
get_bh(root->node); get_bh(root->node);
} }
...@@ -171,7 +172,8 @@ int add_dirty_roots(struct btrfs_trans_handle *trans, ...@@ -171,7 +172,8 @@ int add_dirty_roots(struct btrfs_trans_handle *trans,
break; break;
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
root = gang[i]; root = gang[i];
radix_tree_tag_clear(radix, (unsigned long)root, radix_tree_tag_clear(radix,
(unsigned long)root->root_key.objectid,
BTRFS_ROOT_TRANS_TAG); BTRFS_ROOT_TRANS_TAG);
if (root->commit_root == root->node) { if (root->commit_root == root->node) {
WARN_ON(root->node->b_blocknr != WARN_ON(root->node->b_blocknr !=
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment