|
@@ -230,8 +230,8 @@ static noinline int create_subvol(struct btrfs_root *root,
|
|
struct btrfs_root_item root_item;
|
|
struct btrfs_root_item root_item;
|
|
struct btrfs_inode_item *inode_item;
|
|
struct btrfs_inode_item *inode_item;
|
|
struct extent_buffer *leaf;
|
|
struct extent_buffer *leaf;
|
|
- struct btrfs_root *new_root = root;
|
|
|
|
- struct inode *dir;
|
|
|
|
|
|
+ struct btrfs_root *new_root;
|
|
|
|
+ struct inode *dir = dentry->d_parent->d_inode;
|
|
int ret;
|
|
int ret;
|
|
int err;
|
|
int err;
|
|
u64 objectid;
|
|
u64 objectid;
|
|
@@ -241,7 +241,7 @@ static noinline int create_subvol(struct btrfs_root *root,
|
|
|
|
|
|
ret = btrfs_check_metadata_free_space(root);
|
|
ret = btrfs_check_metadata_free_space(root);
|
|
if (ret)
|
|
if (ret)
|
|
- goto fail_commit;
|
|
|
|
|
|
+ return ret;
|
|
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
|
trans = btrfs_start_transaction(root, 1);
|
|
BUG_ON(!trans);
|
|
BUG_ON(!trans);
|
|
@@ -304,11 +304,17 @@ static noinline int create_subvol(struct btrfs_root *root,
|
|
if (ret)
|
|
if (ret)
|
|
goto fail;
|
|
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
|
|
* insert the directory item
|
|
*/
|
|
*/
|
|
- key.offset = (u64)-1;
|
|
|
|
- dir = dentry->d_parent->d_inode;
|
|
|
|
ret = btrfs_set_inode_index(dir, &index);
|
|
ret = btrfs_set_inode_index(dir, &index);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
|
|
|
|
@@ -325,30 +331,15 @@ static noinline int create_subvol(struct btrfs_root *root,
|
|
ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
|
|
ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
|
|
objectid, root->root_key.objectid,
|
|
objectid, root->root_key.objectid,
|
|
dir->i_ino, index, name, namelen);
|
|
dir->i_ino, index, name, namelen);
|
|
- BUG_ON(ret);
|
|
|
|
|
|
|
|
- ret = btrfs_commit_transaction(trans, root);
|
|
|
|
- if (ret)
|
|
|
|
- goto fail_commit;
|
|
|
|
-
|
|
|
|
- new_root = btrfs_read_fs_root_no_name(root->fs_info, &key);
|
|
|
|
- BUG_ON(!new_root);
|
|
|
|
-
|
|
|
|
- trans = btrfs_start_transaction(new_root, 1);
|
|
|
|
- BUG_ON(!trans);
|
|
|
|
-
|
|
|
|
- ret = btrfs_create_subvol_root(trans, new_root, dentry, new_dirid,
|
|
|
|
- BTRFS_I(dir)->block_group);
|
|
|
|
- if (ret)
|
|
|
|
- goto fail;
|
|
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
|
|
|
|
+ d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));
|
|
fail:
|
|
fail:
|
|
nr = trans->blocks_used;
|
|
nr = trans->blocks_used;
|
|
- err = btrfs_commit_transaction(trans, new_root);
|
|
|
|
|
|
+ err = btrfs_commit_transaction(trans, root);
|
|
if (err && !ret)
|
|
if (err && !ret)
|
|
ret = err;
|
|
ret = err;
|
|
-fail_commit:
|
|
|
|
- btrfs_btree_balance_dirty(root, nr);
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -409,14 +400,15 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
|
|
* sys_mkdirat and vfs_mkdir, but we only do a single component lookup
|
|
* sys_mkdirat and vfs_mkdir, but we only do a single component lookup
|
|
* inside this filesystem so it's quite a bit simpler.
|
|
* inside this filesystem so it's quite a bit simpler.
|
|
*/
|
|
*/
|
|
-static noinline int btrfs_mksubvol(struct path *parent, char *name,
|
|
|
|
- int mode, int namelen,
|
|
|
|
|
|
+static noinline int btrfs_mksubvol(struct path *parent,
|
|
|
|
+ char *name, int namelen,
|
|
struct btrfs_root *snap_src)
|
|
struct btrfs_root *snap_src)
|
|
{
|
|
{
|
|
|
|
+ struct inode *dir = parent->dentry->d_inode;
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
int error;
|
|
int error;
|
|
|
|
|
|
- mutex_lock_nested(&parent->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
|
|
|
|
|
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
|
|
|
|
|
|
dentry = lookup_one_len(name, parent->dentry, namelen);
|
|
dentry = lookup_one_len(name, parent->dentry, namelen);
|
|
error = PTR_ERR(dentry);
|
|
error = PTR_ERR(dentry);
|
|
@@ -427,99 +419,39 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name,
|
|
if (dentry->d_inode)
|
|
if (dentry->d_inode)
|
|
goto out_dput;
|
|
goto out_dput;
|
|
|
|
|
|
- if (!IS_POSIXACL(parent->dentry->d_inode))
|
|
|
|
- mode &= ~current_umask();
|
|
|
|
-
|
|
|
|
error = mnt_want_write(parent->mnt);
|
|
error = mnt_want_write(parent->mnt);
|
|
if (error)
|
|
if (error)
|
|
goto out_dput;
|
|
goto out_dput;
|
|
|
|
|
|
- error = btrfs_may_create(parent->dentry->d_inode, dentry);
|
|
|
|
|
|
+ error = btrfs_may_create(dir, dentry);
|
|
if (error)
|
|
if (error)
|
|
goto out_drop_write;
|
|
goto out_drop_write;
|
|
|
|
|
|
- /*
|
|
|
|
- * Actually perform the low-level subvolume creation after all
|
|
|
|
- * this VFS fuzz.
|
|
|
|
- *
|
|
|
|
- * Eventually we want to pass in an inode under which we create this
|
|
|
|
- * subvolume, but for now all are under the filesystem root.
|
|
|
|
- *
|
|
|
|
- * Also we should pass on the mode eventually to allow creating new
|
|
|
|
- * subvolume with specific mode bits.
|
|
|
|
- */
|
|
|
|
|
|
+ down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
|
|
|
|
+
|
|
|
|
+ if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0)
|
|
|
|
+ goto out_up_read;
|
|
|
|
+
|
|
if (snap_src) {
|
|
if (snap_src) {
|
|
- struct dentry *dir = dentry->d_parent;
|
|
|
|
- struct dentry *test = dir->d_parent;
|
|
|
|
- struct btrfs_path *path = btrfs_alloc_path();
|
|
|
|
- int ret;
|
|
|
|
- u64 test_oid;
|
|
|
|
- u64 parent_oid = BTRFS_I(dir->d_inode)->root->root_key.objectid;
|
|
|
|
-
|
|
|
|
- test_oid = snap_src->root_key.objectid;
|
|
|
|
-
|
|
|
|
- ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
|
|
|
|
- path, parent_oid, test_oid);
|
|
|
|
- if (ret == 0)
|
|
|
|
- goto create;
|
|
|
|
- btrfs_release_path(snap_src->fs_info->tree_root, path);
|
|
|
|
-
|
|
|
|
- /* we need to make sure we aren't creating a directory loop
|
|
|
|
- * by taking a snapshot of something that has our current
|
|
|
|
- * subvol in its directory tree. So, this loops through
|
|
|
|
- * the dentries and checks the forward refs for each subvolume
|
|
|
|
- * to see if is references the subvolume where we are
|
|
|
|
- * placing this new snapshot.
|
|
|
|
- */
|
|
|
|
- while (1) {
|
|
|
|
- if (!test ||
|
|
|
|
- dir == snap_src->fs_info->sb->s_root ||
|
|
|
|
- test == snap_src->fs_info->sb->s_root ||
|
|
|
|
- test->d_inode->i_sb != snap_src->fs_info->sb) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (S_ISLNK(test->d_inode->i_mode)) {
|
|
|
|
- printk(KERN_INFO "Btrfs symlink in snapshot "
|
|
|
|
- "path, failed\n");
|
|
|
|
- error = -EMLINK;
|
|
|
|
- btrfs_free_path(path);
|
|
|
|
- goto out_drop_write;
|
|
|
|
- }
|
|
|
|
- test_oid =
|
|
|
|
- BTRFS_I(test->d_inode)->root->root_key.objectid;
|
|
|
|
- ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
|
|
|
|
- path, test_oid, parent_oid);
|
|
|
|
- if (ret == 0) {
|
|
|
|
- printk(KERN_INFO "Btrfs snapshot creation "
|
|
|
|
- "failed, looping\n");
|
|
|
|
- error = -EMLINK;
|
|
|
|
- btrfs_free_path(path);
|
|
|
|
- goto out_drop_write;
|
|
|
|
- }
|
|
|
|
- btrfs_release_path(snap_src->fs_info->tree_root, path);
|
|
|
|
- test = test->d_parent;
|
|
|
|
- }
|
|
|
|
-create:
|
|
|
|
- btrfs_free_path(path);
|
|
|
|
- error = create_snapshot(snap_src, dentry, name, namelen);
|
|
|
|
|
|
+ error = create_snapshot(snap_src, dentry,
|
|
|
|
+ name, namelen);
|
|
} else {
|
|
} else {
|
|
- error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root,
|
|
|
|
- dentry, name, namelen);
|
|
|
|
|
|
+ error = create_subvol(BTRFS_I(dir)->root, dentry,
|
|
|
|
+ name, namelen);
|
|
}
|
|
}
|
|
- if (error)
|
|
|
|
- goto out_drop_write;
|
|
|
|
-
|
|
|
|
- fsnotify_mkdir(parent->dentry->d_inode, dentry);
|
|
|
|
|
|
+ if (!error)
|
|
|
|
+ fsnotify_mkdir(dir, dentry);
|
|
|
|
+out_up_read:
|
|
|
|
+ up_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
|
|
out_drop_write:
|
|
out_drop_write:
|
|
mnt_drop_write(parent->mnt);
|
|
mnt_drop_write(parent->mnt);
|
|
out_dput:
|
|
out_dput:
|
|
dput(dentry);
|
|
dput(dentry);
|
|
out_unlock:
|
|
out_unlock:
|
|
- mutex_unlock(&parent->dentry->d_inode->i_mutex);
|
|
|
|
|
|
+ mutex_unlock(&dir->i_mutex);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
static int btrfs_defrag_file(struct file *file)
|
|
static int btrfs_defrag_file(struct file *file)
|
|
{
|
|
{
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
@@ -597,7 +529,8 @@ out_unlock:
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
|
|
|
|
|
|
+static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
|
|
|
|
+ void __user *arg)
|
|
{
|
|
{
|
|
u64 new_size;
|
|
u64 new_size;
|
|
u64 old_size;
|
|
u64 old_size;
|
|
@@ -706,10 +639,7 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
|
|
{
|
|
{
|
|
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
|
|
struct btrfs_ioctl_vol_args *vol_args;
|
|
struct btrfs_ioctl_vol_args *vol_args;
|
|
- struct btrfs_dir_item *di;
|
|
|
|
- struct btrfs_path *path;
|
|
|
|
struct file *src_file;
|
|
struct file *src_file;
|
|
- u64 root_dirid;
|
|
|
|
int namelen;
|
|
int namelen;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
@@ -727,32 +657,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- path = btrfs_alloc_path();
|
|
|
|
- if (!path) {
|
|
|
|
- ret = -ENOMEM;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- root_dirid = root->fs_info->sb->s_root->d_inode->i_ino,
|
|
|
|
- di = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root,
|
|
|
|
- path, root_dirid,
|
|
|
|
- vol_args->name, namelen, 0);
|
|
|
|
- btrfs_free_path(path);
|
|
|
|
-
|
|
|
|
- if (di && !IS_ERR(di)) {
|
|
|
|
- ret = -EEXIST;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (IS_ERR(di)) {
|
|
|
|
- ret = PTR_ERR(di);
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (subvol) {
|
|
if (subvol) {
|
|
- ret = btrfs_mksubvol(&file->f_path, vol_args->name,
|
|
|
|
- file->f_path.dentry->d_inode->i_mode,
|
|
|
|
- namelen, NULL);
|
|
|
|
|
|
+ ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen,
|
|
|
|
+ NULL);
|
|
} else {
|
|
} else {
|
|
struct inode *src_inode;
|
|
struct inode *src_inode;
|
|
src_file = fget(vol_args->fd);
|
|
src_file = fget(vol_args->fd);
|
|
@@ -769,17 +676,156 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
|
|
fput(src_file);
|
|
fput(src_file);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
- ret = btrfs_mksubvol(&file->f_path, vol_args->name,
|
|
|
|
- file->f_path.dentry->d_inode->i_mode,
|
|
|
|
- namelen, BTRFS_I(src_inode)->root);
|
|
|
|
|
|
+ ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen,
|
|
|
|
+ BTRFS_I(src_inode)->root);
|
|
fput(src_file);
|
|
fput(src_file);
|
|
}
|
|
}
|
|
-
|
|
|
|
out:
|
|
out:
|
|
kfree(vol_args);
|
|
kfree(vol_args);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * helper to check if the subvolume references other subvolumes
|
|
|
|
+ */
|
|
|
|
+static noinline int may_destroy_subvol(struct btrfs_root *root)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_path *path;
|
|
|
|
+ struct btrfs_key key;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ path = btrfs_alloc_path();
|
|
|
|
+ if (!path)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ key.objectid = root->root_key.objectid;
|
|
|
|
+ key.type = BTRFS_ROOT_REF_KEY;
|
|
|
|
+ key.offset = (u64)-1;
|
|
|
|
+
|
|
|
|
+ ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
|
|
|
|
+ &key, path, 0, 0);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+ BUG_ON(ret == 0);
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
+ if (path->slots[0] > 0) {
|
|
|
|
+ path->slots[0]--;
|
|
|
|
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
|
|
|
|
+ if (key.objectid == root->root_key.objectid &&
|
|
|
|
+ key.type == BTRFS_ROOT_REF_KEY)
|
|
|
|
+ ret = -ENOTEMPTY;
|
|
|
|
+ }
|
|
|
|
+out:
|
|
|
|
+ btrfs_free_path(path);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|
|
|
+ void __user *arg)
|
|
|
|
+{
|
|
|
|
+ struct dentry *parent = fdentry(file);
|
|
|
|
+ struct dentry *dentry;
|
|
|
|
+ struct inode *dir = parent->d_inode;
|
|
|
|
+ struct inode *inode;
|
|
|
|
+ struct btrfs_root *root = BTRFS_I(dir)->root;
|
|
|
|
+ struct btrfs_root *dest = NULL;
|
|
|
|
+ struct btrfs_ioctl_vol_args *vol_args;
|
|
|
|
+ struct btrfs_trans_handle *trans;
|
|
|
|
+ int namelen;
|
|
|
|
+ int ret;
|
|
|
|
+ int err = 0;
|
|
|
|
+
|
|
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ vol_args = memdup_user(arg, sizeof(*vol_args));
|
|
|
|
+ if (IS_ERR(vol_args))
|
|
|
|
+ return PTR_ERR(vol_args);
|
|
|
|
+
|
|
|
|
+ vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
|
|
|
|
+ namelen = strlen(vol_args->name);
|
|
|
|
+ if (strchr(vol_args->name, '/') ||
|
|
|
|
+ strncmp(vol_args->name, "..", namelen) == 0) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = mnt_want_write(file->f_path.mnt);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
|
|
|
|
+ dentry = lookup_one_len(vol_args->name, parent, namelen);
|
|
|
|
+ if (IS_ERR(dentry)) {
|
|
|
|
+ err = PTR_ERR(dentry);
|
|
|
|
+ goto out_unlock_dir;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!dentry->d_inode) {
|
|
|
|
+ err = -ENOENT;
|
|
|
|
+ goto out_dput;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ inode = dentry->d_inode;
|
|
|
|
+ if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_dput;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dest = BTRFS_I(inode)->root;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&inode->i_mutex);
|
|
|
|
+ err = d_invalidate(dentry);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+
|
|
|
|
+ down_write(&root->fs_info->subvol_sem);
|
|
|
|
+
|
|
|
|
+ err = may_destroy_subvol(dest);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_up_write;
|
|
|
|
+
|
|
|
|
+ trans = btrfs_start_transaction(root, 1);
|
|
|
|
+ ret = btrfs_unlink_subvol(trans, root, dir,
|
|
|
|
+ dest->root_key.objectid,
|
|
|
|
+ dentry->d_name.name,
|
|
|
|
+ dentry->d_name.len);
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
+
|
|
|
|
+ btrfs_record_root_in_trans(trans, dest);
|
|
|
|
+
|
|
|
|
+ memset(&dest->root_item.drop_progress, 0,
|
|
|
|
+ sizeof(dest->root_item.drop_progress));
|
|
|
|
+ dest->root_item.drop_level = 0;
|
|
|
|
+ btrfs_set_root_refs(&dest->root_item, 0);
|
|
|
|
+
|
|
|
|
+ ret = btrfs_insert_orphan_item(trans,
|
|
|
|
+ root->fs_info->tree_root,
|
|
|
|
+ dest->root_key.objectid);
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
+
|
|
|
|
+ ret = btrfs_commit_transaction(trans, root);
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
+ inode->i_flags |= S_DEAD;
|
|
|
|
+out_up_write:
|
|
|
|
+ up_write(&root->fs_info->subvol_sem);
|
|
|
|
+out_unlock:
|
|
|
|
+ mutex_unlock(&inode->i_mutex);
|
|
|
|
+ if (!err) {
|
|
|
|
+ btrfs_invalidate_inodes(dest);
|
|
|
|
+ d_delete(dentry);
|
|
|
|
+ }
|
|
|
|
+out_dput:
|
|
|
|
+ dput(dentry);
|
|
|
|
+out_unlock_dir:
|
|
|
|
+ mutex_unlock(&dir->i_mutex);
|
|
|
|
+ mnt_drop_write(file->f_path.mnt);
|
|
|
|
+out:
|
|
|
|
+ kfree(vol_args);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
static int btrfs_ioctl_defrag(struct file *file)
|
|
static int btrfs_ioctl_defrag(struct file *file)
|
|
{
|
|
{
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
@@ -853,8 +899,8 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
|
|
- u64 off, u64 olen, u64 destoff)
|
|
|
|
|
|
+static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
|
|
+ u64 off, u64 olen, u64 destoff)
|
|
{
|
|
{
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
struct inode *inode = fdentry(file)->d_inode;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
@@ -1246,6 +1292,8 @@ long btrfs_ioctl(struct file *file, unsigned int
|
|
return btrfs_ioctl_snap_create(file, argp, 0);
|
|
return btrfs_ioctl_snap_create(file, argp, 0);
|
|
case BTRFS_IOC_SUBVOL_CREATE:
|
|
case BTRFS_IOC_SUBVOL_CREATE:
|
|
return btrfs_ioctl_snap_create(file, argp, 1);
|
|
return btrfs_ioctl_snap_create(file, argp, 1);
|
|
|
|
+ case BTRFS_IOC_SNAP_DESTROY:
|
|
|
|
+ return btrfs_ioctl_snap_destroy(file, argp);
|
|
case BTRFS_IOC_DEFRAG:
|
|
case BTRFS_IOC_DEFRAG:
|
|
return btrfs_ioctl_defrag(file);
|
|
return btrfs_ioctl_defrag(file);
|
|
case BTRFS_IOC_RESIZE:
|
|
case BTRFS_IOC_RESIZE:
|