diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-11-17 21:02:50 -0500 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-11-17 21:02:50 -0500 |
commit | 3de4586c5278a28107030c336956381f69ff7a9d (patch) | |
tree | d636e3806cd5ecff71927d0300e46526fa23de1a /fs/btrfs/ioctl.c | |
parent | 4ce4cb526f67775c1cce3e3fa01c292672ba874e (diff) |
Btrfs: Allow subvolumes and snapshots anywhere in the directory tree
Before, all snapshots and subvolumes lived in a single flat directory. This
was awkward and confusing because the single flat directory was only writable
with the ioctls.
This commit changes the ioctls to create subvols and snapshots at any
point in the directory tree. This requires making separate ioctls for
snapshot and subvol creation instead of a combining them into one.
The subvol ioctl does:
btrfsctl -S subvol_name parent_dir
After the ioctl is done subvol_name lives inside parent_dir.
The snapshot ioctl does:
btrfsctl -s path_for_snapshot root_to_snapshot
path_for_snapshot can be an absolute or relative path. btrfsctl breaks it up
into directory and basename components.
root_to_snapshot can be any file or directory in the FS. The snapshot
is taken of the entire root where that file lives.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
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: |