diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 71 |
1 files changed, 52 insertions, 19 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f43df72b0e17..ec45b3086136 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -67,6 +67,7 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
67 | int err; | 67 | int err; |
68 | u64 objectid; | 68 | u64 objectid; |
69 | u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; | 69 | u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; |
70 | u64 index = 0; | ||
70 | unsigned long nr = 1; | 71 | unsigned long nr = 1; |
71 | 72 | ||
72 | ret = btrfs_check_free_space(root, 1, 0); | 73 | ret = btrfs_check_free_space(root, 1, 0); |
@@ -126,6 +127,7 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
126 | key.objectid = objectid; | 127 | key.objectid = objectid; |
127 | key.offset = 1; | 128 | key.offset = 1; |
128 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | 129 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
130 | printk("inserting root objectid %Lu\n", objectid); | ||
129 | ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, | 131 | ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, |
130 | &root_item); | 132 | &root_item); |
131 | if (ret) | 133 | if (ret) |
@@ -135,24 +137,27 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
135 | * insert the directory item | 137 | * insert the directory item |
136 | */ | 138 | */ |
137 | key.offset = (u64)-1; | 139 | key.offset = (u64)-1; |
138 | dir = root->fs_info->sb->s_root->d_inode; | 140 | dir = dentry->d_parent->d_inode; |
139 | ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root, | 141 | ret = btrfs_set_inode_index(dir, &index); |
142 | BUG_ON(ret); | ||
143 | |||
144 | ret = btrfs_insert_dir_item(trans, root, | ||
140 | name, namelen, dir->i_ino, &key, | 145 | name, namelen, dir->i_ino, &key, |
141 | BTRFS_FT_DIR, 0); | 146 | BTRFS_FT_DIR, index); |
142 | if (ret) | 147 | if (ret) |
143 | goto fail; | 148 | goto fail; |
144 | 149 | #if 0 | |
145 | ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root, | 150 | ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root, |
146 | name, namelen, objectid, | 151 | name, namelen, objectid, |
147 | root->fs_info->sb->s_root->d_inode->i_ino, 0); | 152 | root->fs_info->sb->s_root->d_inode->i_ino, 0); |
148 | if (ret) | 153 | if (ret) |
149 | goto fail; | 154 | goto fail; |
150 | 155 | #endif | |
151 | ret = btrfs_commit_transaction(trans, root); | 156 | ret = btrfs_commit_transaction(trans, root); |
152 | if (ret) | 157 | if (ret) |
153 | goto fail_commit; | 158 | goto fail_commit; |
154 | 159 | ||
155 | new_root = btrfs_read_fs_root(root->fs_info, &key, name, namelen); | 160 | new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); |
156 | BUG_ON(!new_root); | 161 | BUG_ON(!new_root); |
157 | 162 | ||
158 | trans = btrfs_start_transaction(new_root, 1); | 163 | trans = btrfs_start_transaction(new_root, 1); |
@@ -170,14 +175,16 @@ fail: | |||
170 | ret = err; | 175 | ret = err; |
171 | fail_commit: | 176 | fail_commit: |
172 | btrfs_btree_balance_dirty(root, nr); | 177 | btrfs_btree_balance_dirty(root, nr); |
178 | printk("all done ret %d\n", ret); | ||
173 | return ret; | 179 | return ret; |
174 | } | 180 | } |
175 | 181 | ||
176 | static int create_snapshot(struct btrfs_root *root, char *name, int namelen) | 182 | static int create_snapshot(struct btrfs_root *root, struct dentry *dentry, |
183 | char *name, int namelen) | ||
177 | { | 184 | { |
178 | struct btrfs_pending_snapshot *pending_snapshot; | 185 | struct btrfs_pending_snapshot *pending_snapshot; |
179 | struct btrfs_trans_handle *trans; | 186 | struct btrfs_trans_handle *trans; |
180 | int ret; | 187 | int ret = 0; |
181 | int err; | 188 | int err; |
182 | unsigned long nr = 0; | 189 | unsigned long nr = 0; |
183 | 190 | ||
@@ -188,7 +195,7 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) | |||
188 | if (ret) | 195 | if (ret) |
189 | goto fail_unlock; | 196 | goto fail_unlock; |
190 | 197 | ||
191 | pending_snapshot = kmalloc(sizeof(*pending_snapshot), GFP_NOFS); | 198 | pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS); |
192 | if (!pending_snapshot) { | 199 | if (!pending_snapshot) { |
193 | ret = -ENOMEM; | 200 | ret = -ENOMEM; |
194 | goto fail_unlock; | 201 | goto fail_unlock; |
@@ -201,12 +208,12 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) | |||
201 | } | 208 | } |
202 | memcpy(pending_snapshot->name, name, namelen); | 209 | memcpy(pending_snapshot->name, name, namelen); |
203 | pending_snapshot->name[namelen] = '\0'; | 210 | pending_snapshot->name[namelen] = '\0'; |
211 | pending_snapshot->dentry = dentry; | ||
204 | trans = btrfs_start_transaction(root, 1); | 212 | trans = btrfs_start_transaction(root, 1); |
205 | BUG_ON(!trans); | 213 | BUG_ON(!trans); |
206 | pending_snapshot->root = root; | 214 | pending_snapshot->root = root; |
207 | list_add(&pending_snapshot->list, | 215 | list_add(&pending_snapshot->list, |
208 | &trans->transaction->pending_snapshots); | 216 | &trans->transaction->pending_snapshots); |
209 | ret = btrfs_update_inode(trans, root, root->inode); | ||
210 | err = btrfs_commit_transaction(trans, root); | 217 | err = btrfs_commit_transaction(trans, root); |
211 | 218 | ||
212 | fail_unlock: | 219 | fail_unlock: |
@@ -230,7 +237,8 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) | |||
230 | * inside this filesystem so it's quite a bit simpler. | 237 | * inside this filesystem so it's quite a bit simpler. |
231 | */ | 238 | */ |
232 | static noinline int btrfs_mksubvol(struct path *parent, char *name, | 239 | static noinline int btrfs_mksubvol(struct path *parent, char *name, |
233 | int mode, int namelen) | 240 | int mode, int namelen, |
241 | struct btrfs_root *snap_src) | ||
234 | { | 242 | { |
235 | struct dentry *dentry; | 243 | struct dentry *dentry; |
236 | int error; | 244 | int error; |
@@ -248,6 +256,7 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name, | |||
248 | 256 | ||
249 | if (!IS_POSIXACL(parent->dentry->d_inode)) | 257 | if (!IS_POSIXACL(parent->dentry->d_inode)) |
250 | mode &= ~current->fs->umask; | 258 | mode &= ~current->fs->umask; |
259 | |||
251 | error = mnt_want_write(parent->mnt); | 260 | error = mnt_want_write(parent->mnt); |
252 | if (error) | 261 | if (error) |
253 | goto out_dput; | 262 | goto out_dput; |
@@ -266,8 +275,12 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name, | |||
266 | * Also we should pass on the mode eventually to allow creating new | 275 | * Also we should pass on the mode eventually to allow creating new |
267 | * subvolume with specific mode bits. | 276 | * subvolume with specific mode bits. |
268 | */ | 277 | */ |
269 | error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root, dentry, | 278 | if (snap_src) { |
270 | name, namelen); | 279 | error = create_snapshot(snap_src, dentry, name, namelen); |
280 | } else { | ||
281 | error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root, | ||
282 | dentry, name, namelen); | ||
283 | } | ||
271 | if (error) | 284 | if (error) |
272 | goto out_drop_write; | 285 | goto out_drop_write; |
273 | 286 | ||
@@ -471,15 +484,16 @@ out: | |||
471 | } | 484 | } |
472 | 485 | ||
473 | static noinline int btrfs_ioctl_snap_create(struct file *file, | 486 | static noinline int btrfs_ioctl_snap_create(struct file *file, |
474 | void __user *arg) | 487 | void __user *arg, int subvol) |
475 | { | 488 | { |
476 | struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; | 489 | struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; |
477 | struct btrfs_ioctl_vol_args *vol_args; | 490 | struct btrfs_ioctl_vol_args *vol_args; |
478 | struct btrfs_dir_item *di; | 491 | struct btrfs_dir_item *di; |
479 | struct btrfs_path *path; | 492 | struct btrfs_path *path; |
493 | struct file *src_file; | ||
480 | u64 root_dirid; | 494 | u64 root_dirid; |
481 | int namelen; | 495 | int namelen; |
482 | int ret; | 496 | int ret = 0; |
483 | 497 | ||
484 | if (root->fs_info->sb->s_flags & MS_RDONLY) | 498 | if (root->fs_info->sb->s_flags & MS_RDONLY) |
485 | return -EROFS; | 499 | return -EROFS; |
@@ -523,12 +537,29 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, | |||
523 | goto out; | 537 | goto out; |
524 | } | 538 | } |
525 | 539 | ||
526 | if (root == root->fs_info->tree_root) { | 540 | if (subvol) { |
527 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, | 541 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, |
528 | file->f_path.dentry->d_inode->i_mode, | 542 | file->f_path.dentry->d_inode->i_mode, |
529 | namelen); | 543 | namelen, NULL); |
530 | } else { | 544 | } else { |
531 | ret = create_snapshot(root, vol_args->name, namelen); | 545 | struct inode *src_inode; |
546 | src_file = fget(vol_args->fd); | ||
547 | if (!src_file) { | ||
548 | ret = -EINVAL; | ||
549 | goto out; | ||
550 | } | ||
551 | |||
552 | src_inode = src_file->f_path.dentry->d_inode; | ||
553 | if (src_inode->i_sb != file->f_path.dentry->d_inode->i_sb) { | ||
554 | printk("btrfs: Snapshot src from another FS\n"); | ||
555 | ret = -EINVAL; | ||
556 | fput(src_file); | ||
557 | goto out; | ||
558 | } | ||
559 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, | ||
560 | file->f_path.dentry->d_inode->i_mode, | ||
561 | namelen, BTRFS_I(src_inode)->root); | ||
562 | fput(src_file); | ||
532 | } | 563 | } |
533 | 564 | ||
534 | out: | 565 | out: |
@@ -1030,7 +1061,9 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
1030 | 1061 | ||
1031 | switch (cmd) { | 1062 | switch (cmd) { |
1032 | case BTRFS_IOC_SNAP_CREATE: | 1063 | case BTRFS_IOC_SNAP_CREATE: |
1033 | return btrfs_ioctl_snap_create(file, (void __user *)arg); | 1064 | return btrfs_ioctl_snap_create(file, (void __user *)arg, 0); |
1065 | case BTRFS_IOC_SUBVOL_CREATE: | ||
1066 | return btrfs_ioctl_snap_create(file, (void __user *)arg, 1); | ||
1034 | case BTRFS_IOC_DEFRAG: | 1067 | case BTRFS_IOC_DEFRAG: |
1035 | return btrfs_ioctl_defrag(file); | 1068 | return btrfs_ioctl_defrag(file); |
1036 | case BTRFS_IOC_RESIZE: | 1069 | case BTRFS_IOC_RESIZE: |