diff options
author | Yan, Zheng <zheng.yan@oracle.com> | 2009-09-21 16:00:26 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-09-21 16:00:26 -0400 |
commit | 76dda93c6ae2c1dc3e6cde34569d6aca26b0c918 (patch) | |
tree | f5ca46ec89d4ae2c762952d5f35e2c6f95ac046a /fs/btrfs/ioctl.c | |
parent | 4df27c4d5cc1dda54ed7d0a8389347f2df359cf9 (diff) |
Btrfs: add snapshot/subvolume destroy ioctl
This patch adds snapshot/subvolume destroy ioctl. A subvolume that isn't being
used and doesn't contains links to other subvolumes can be destroyed.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 320 |
1 files changed, 184 insertions, 136 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9b3a88755e51..a13fd556db74 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -230,8 +230,8 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
230 | struct btrfs_root_item root_item; | 230 | struct btrfs_root_item root_item; |
231 | struct btrfs_inode_item *inode_item; | 231 | struct btrfs_inode_item *inode_item; |
232 | struct extent_buffer *leaf; | 232 | struct extent_buffer *leaf; |
233 | struct btrfs_root *new_root = root; | 233 | struct btrfs_root *new_root; |
234 | struct inode *dir; | 234 | struct inode *dir = dentry->d_parent->d_inode; |
235 | int ret; | 235 | int ret; |
236 | int err; | 236 | int err; |
237 | u64 objectid; | 237 | u64 objectid; |
@@ -241,7 +241,7 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
241 | 241 | ||
242 | ret = btrfs_check_metadata_free_space(root); | 242 | ret = btrfs_check_metadata_free_space(root); |
243 | if (ret) | 243 | if (ret) |
244 | goto fail_commit; | 244 | return ret; |
245 | 245 | ||
246 | trans = btrfs_start_transaction(root, 1); | 246 | trans = btrfs_start_transaction(root, 1); |
247 | BUG_ON(!trans); | 247 | BUG_ON(!trans); |
@@ -304,11 +304,17 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
304 | if (ret) | 304 | if (ret) |
305 | goto fail; | 305 | goto fail; |
306 | 306 | ||
307 | key.offset = (u64)-1; | ||
308 | new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); | ||
309 | BUG_ON(IS_ERR(new_root)); | ||
310 | |||
311 | btrfs_record_root_in_trans(trans, new_root); | ||
312 | |||
313 | ret = btrfs_create_subvol_root(trans, new_root, new_dirid, | ||
314 | BTRFS_I(dir)->block_group); | ||
307 | /* | 315 | /* |
308 | * insert the directory item | 316 | * insert the directory item |
309 | */ | 317 | */ |
310 | key.offset = (u64)-1; | ||
311 | dir = dentry->d_parent->d_inode; | ||
312 | ret = btrfs_set_inode_index(dir, &index); | 318 | ret = btrfs_set_inode_index(dir, &index); |
313 | BUG_ON(ret); | 319 | BUG_ON(ret); |
314 | 320 | ||
@@ -325,30 +331,15 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
325 | ret = btrfs_add_root_ref(trans, root->fs_info->tree_root, | 331 | ret = btrfs_add_root_ref(trans, root->fs_info->tree_root, |
326 | objectid, root->root_key.objectid, | 332 | objectid, root->root_key.objectid, |
327 | dir->i_ino, index, name, namelen); | 333 | dir->i_ino, index, name, namelen); |
328 | BUG_ON(ret); | ||
329 | 334 | ||
330 | ret = btrfs_commit_transaction(trans, root); | 335 | BUG_ON(ret); |
331 | if (ret) | ||
332 | goto fail_commit; | ||
333 | |||
334 | new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); | ||
335 | BUG_ON(!new_root); | ||
336 | |||
337 | trans = btrfs_start_transaction(new_root, 1); | ||
338 | BUG_ON(!trans); | ||
339 | |||
340 | ret = btrfs_create_subvol_root(trans, new_root, dentry, new_dirid, | ||
341 | BTRFS_I(dir)->block_group); | ||
342 | if (ret) | ||
343 | goto fail; | ||
344 | 336 | ||
337 | d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); | ||
345 | fail: | 338 | fail: |
346 | nr = trans->blocks_used; | 339 | nr = trans->blocks_used; |
347 | err = btrfs_commit_transaction(trans, new_root); | 340 | err = btrfs_commit_transaction(trans, root); |
348 | if (err && !ret) | 341 | if (err && !ret) |
349 | ret = err; | 342 | ret = err; |
350 | fail_commit: | ||
351 | btrfs_btree_balance_dirty(root, nr); | ||
352 | return ret; | 343 | return ret; |
353 | } | 344 | } |
354 | 345 | ||
@@ -409,14 +400,15 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) | |||
409 | * sys_mkdirat and vfs_mkdir, but we only do a single component lookup | 400 | * sys_mkdirat and vfs_mkdir, but we only do a single component lookup |
410 | * inside this filesystem so it's quite a bit simpler. | 401 | * inside this filesystem so it's quite a bit simpler. |
411 | */ | 402 | */ |
412 | static noinline int btrfs_mksubvol(struct path *parent, char *name, | 403 | static noinline int btrfs_mksubvol(struct path *parent, |
413 | int mode, int namelen, | 404 | char *name, int namelen, |
414 | struct btrfs_root *snap_src) | 405 | struct btrfs_root *snap_src) |
415 | { | 406 | { |
407 | struct inode *dir = parent->dentry->d_inode; | ||
416 | struct dentry *dentry; | 408 | struct dentry *dentry; |
417 | int error; | 409 | int error; |
418 | 410 | ||
419 | mutex_lock_nested(&parent->dentry->d_inode->i_mutex, I_MUTEX_PARENT); | 411 | mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); |
420 | 412 | ||
421 | dentry = lookup_one_len(name, parent->dentry, namelen); | 413 | dentry = lookup_one_len(name, parent->dentry, namelen); |
422 | error = PTR_ERR(dentry); | 414 | error = PTR_ERR(dentry); |
@@ -427,99 +419,39 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name, | |||
427 | if (dentry->d_inode) | 419 | if (dentry->d_inode) |
428 | goto out_dput; | 420 | goto out_dput; |
429 | 421 | ||
430 | if (!IS_POSIXACL(parent->dentry->d_inode)) | ||
431 | mode &= ~current_umask(); | ||
432 | |||
433 | error = mnt_want_write(parent->mnt); | 422 | error = mnt_want_write(parent->mnt); |
434 | if (error) | 423 | if (error) |
435 | goto out_dput; | 424 | goto out_dput; |
436 | 425 | ||
437 | error = btrfs_may_create(parent->dentry->d_inode, dentry); | 426 | error = btrfs_may_create(dir, dentry); |
438 | if (error) | 427 | if (error) |
439 | goto out_drop_write; | 428 | goto out_drop_write; |
440 | 429 | ||
441 | /* | 430 | down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); |
442 | * Actually perform the low-level subvolume creation after all | 431 | |
443 | * this VFS fuzz. | 432 | if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) |
444 | * | 433 | goto out_up_read; |
445 | * Eventually we want to pass in an inode under which we create this | 434 | |
446 | * subvolume, but for now all are under the filesystem root. | ||
447 | * | ||
448 | * Also we should pass on the mode eventually to allow creating new | ||
449 | * subvolume with specific mode bits. | ||
450 | */ | ||
451 | if (snap_src) { | 435 | if (snap_src) { |
452 | struct dentry *dir = dentry->d_parent; | 436 | error = create_snapshot(snap_src, dentry, |
453 | struct dentry *test = dir->d_parent; | 437 | name, namelen); |
454 | struct btrfs_path *path = btrfs_alloc_path(); | ||
455 | int ret; | ||
456 | u64 test_oid; | ||
457 | u64 parent_oid = BTRFS_I(dir->d_inode)->root->root_key.objectid; | ||
458 | |||
459 | test_oid = snap_src->root_key.objectid; | ||
460 | |||
461 | ret = btrfs_find_root_ref(snap_src->fs_info->tree_root, | ||
462 | path, parent_oid, test_oid); | ||
463 | if (ret == 0) | ||
464 | goto create; | ||
465 | btrfs_release_path(snap_src->fs_info->tree_root, path); | ||
466 | |||
467 | /* we need to make sure we aren't creating a directory loop | ||
468 | * by taking a snapshot of something that has our current | ||
469 | * subvol in its directory tree. So, this loops through | ||
470 | * the dentries and checks the forward refs for each subvolume | ||
471 | * to see if is references the subvolume where we are | ||
472 | * placing this new snapshot. | ||
473 | */ | ||
474 | while (1) { | ||
475 | if (!test || | ||
476 | dir == snap_src->fs_info->sb->s_root || | ||
477 | test == snap_src->fs_info->sb->s_root || | ||
478 | test->d_inode->i_sb != snap_src->fs_info->sb) { | ||
479 | break; | ||
480 | } | ||
481 | if (S_ISLNK(test->d_inode->i_mode)) { | ||
482 | printk(KERN_INFO "Btrfs symlink in snapshot " | ||
483 | "path, failed\n"); | ||
484 | error = -EMLINK; | ||
485 | btrfs_free_path(path); | ||
486 | goto out_drop_write; | ||
487 | } | ||
488 | test_oid = | ||
489 | BTRFS_I(test->d_inode)->root->root_key.objectid; | ||
490 | ret = btrfs_find_root_ref(snap_src->fs_info->tree_root, | ||
491 | path, test_oid, parent_oid); | ||
492 | if (ret == 0) { | ||
493 | printk(KERN_INFO "Btrfs snapshot creation " | ||
494 | "failed, looping\n"); | ||
495 | error = -EMLINK; | ||
496 | btrfs_free_path(path); | ||
497 | goto out_drop_write; | ||
498 | } | ||
499 | btrfs_release_path(snap_src->fs_info->tree_root, path); | ||
500 | test = test->d_parent; | ||
501 | } | ||
502 | create: | ||
503 | btrfs_free_path(path); | ||
504 | error = create_snapshot(snap_src, dentry, name, namelen); | ||
505 | } else { | 438 | } else { |
506 | error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root, | 439 | error = create_subvol(BTRFS_I(dir)->root, dentry, |
507 | dentry, name, namelen); | 440 | name, namelen); |
508 | } | 441 | } |
509 | if (error) | 442 | if (!error) |
510 | goto out_drop_write; | 443 | fsnotify_mkdir(dir, dentry); |
511 | 444 | out_up_read: | |
512 | fsnotify_mkdir(parent->dentry->d_inode, dentry); | 445 | up_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); |
513 | out_drop_write: | 446 | out_drop_write: |
514 | mnt_drop_write(parent->mnt); | 447 | mnt_drop_write(parent->mnt); |
515 | out_dput: | 448 | out_dput: |
516 | dput(dentry); | 449 | dput(dentry); |
517 | out_unlock: | 450 | out_unlock: |
518 | mutex_unlock(&parent->dentry->d_inode->i_mutex); | 451 | mutex_unlock(&dir->i_mutex); |
519 | return error; | 452 | return error; |
520 | } | 453 | } |
521 | 454 | ||
522 | |||
523 | static int btrfs_defrag_file(struct file *file) | 455 | static int btrfs_defrag_file(struct file *file) |
524 | { | 456 | { |
525 | struct inode *inode = fdentry(file)->d_inode; | 457 | struct inode *inode = fdentry(file)->d_inode; |
@@ -597,7 +529,8 @@ out_unlock: | |||
597 | return 0; | 529 | return 0; |
598 | } | 530 | } |
599 | 531 | ||
600 | static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg) | 532 | static noinline int btrfs_ioctl_resize(struct btrfs_root *root, |
533 | void __user *arg) | ||
601 | { | 534 | { |
602 | u64 new_size; | 535 | u64 new_size; |
603 | u64 old_size; | 536 | u64 old_size; |
@@ -706,10 +639,7 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, | |||
706 | { | 639 | { |
707 | struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; | 640 | struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; |
708 | struct btrfs_ioctl_vol_args *vol_args; | 641 | struct btrfs_ioctl_vol_args *vol_args; |
709 | struct btrfs_dir_item *di; | ||
710 | struct btrfs_path *path; | ||
711 | struct file *src_file; | 642 | struct file *src_file; |
712 | u64 root_dirid; | ||
713 | int namelen; | 643 | int namelen; |
714 | int ret = 0; | 644 | int ret = 0; |
715 | 645 | ||
@@ -727,32 +657,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, | |||
727 | goto out; | 657 | goto out; |
728 | } | 658 | } |
729 | 659 | ||
730 | path = btrfs_alloc_path(); | ||
731 | if (!path) { | ||
732 | ret = -ENOMEM; | ||
733 | goto out; | ||
734 | } | ||
735 | |||
736 | root_dirid = root->fs_info->sb->s_root->d_inode->i_ino, | ||
737 | di = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root, | ||
738 | path, root_dirid, | ||
739 | vol_args->name, namelen, 0); | ||
740 | btrfs_free_path(path); | ||
741 | |||
742 | if (di && !IS_ERR(di)) { | ||
743 | ret = -EEXIST; | ||
744 | goto out; | ||
745 | } | ||
746 | |||
747 | if (IS_ERR(di)) { | ||
748 | ret = PTR_ERR(di); | ||
749 | goto out; | ||
750 | } | ||
751 | |||
752 | if (subvol) { | 660 | if (subvol) { |
753 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, | 661 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen, |
754 | file->f_path.dentry->d_inode->i_mode, | 662 | NULL); |
755 | namelen, NULL); | ||
756 | } else { | 663 | } else { |
757 | struct inode *src_inode; | 664 | struct inode *src_inode; |
758 | src_file = fget(vol_args->fd); | 665 | src_file = fget(vol_args->fd); |
@@ -769,17 +676,156 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, | |||
769 | fput(src_file); | 676 | fput(src_file); |
770 | goto out; | 677 | goto out; |
771 | } | 678 | } |
772 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, | 679 | ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen, |
773 | file->f_path.dentry->d_inode->i_mode, | 680 | BTRFS_I(src_inode)->root); |
774 | namelen, BTRFS_I(src_inode)->root); | ||
775 | fput(src_file); | 681 | fput(src_file); |
776 | } | 682 | } |
777 | |||
778 | out: | 683 | out: |
779 | kfree(vol_args); | 684 | kfree(vol_args); |
780 | return ret; | 685 | return ret; |
781 | } | 686 | } |
782 | 687 | ||
688 | /* | ||
689 | * helper to check if the subvolume references other subvolumes | ||
690 | */ | ||
691 | static noinline int may_destroy_subvol(struct btrfs_root *root) | ||
692 | { | ||
693 | struct btrfs_path *path; | ||
694 | struct btrfs_key key; | ||
695 | int ret; | ||
696 | |||
697 | path = btrfs_alloc_path(); | ||
698 | if (!path) | ||
699 | return -ENOMEM; | ||
700 | |||
701 | key.objectid = root->root_key.objectid; | ||
702 | key.type = BTRFS_ROOT_REF_KEY; | ||
703 | key.offset = (u64)-1; | ||
704 | |||
705 | ret = btrfs_search_slot(NULL, root->fs_info->tree_root, | ||
706 | &key, path, 0, 0); | ||
707 | if (ret < 0) | ||
708 | goto out; | ||
709 | BUG_ON(ret == 0); | ||
710 | |||
711 | ret = 0; | ||
712 | if (path->slots[0] > 0) { | ||
713 | path->slots[0]--; | ||
714 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | ||
715 | if (key.objectid == root->root_key.objectid && | ||
716 | key.type == BTRFS_ROOT_REF_KEY) | ||
717 | ret = -ENOTEMPTY; | ||
718 | } | ||
719 | out: | ||
720 | btrfs_free_path(path); | ||
721 | return ret; | ||
722 | } | ||
723 | |||
724 | static noinline int btrfs_ioctl_snap_destroy(struct file *file, | ||
725 | void __user *arg) | ||
726 | { | ||
727 | struct dentry *parent = fdentry(file); | ||
728 | struct dentry *dentry; | ||
729 | struct inode *dir = parent->d_inode; | ||
730 | struct inode *inode; | ||
731 | struct btrfs_root *root = BTRFS_I(dir)->root; | ||
732 | struct btrfs_root *dest = NULL; | ||
733 | struct btrfs_ioctl_vol_args *vol_args; | ||
734 | struct btrfs_trans_handle *trans; | ||
735 | int namelen; | ||
736 | int ret; | ||
737 | int err = 0; | ||
738 | |||
739 | if (!capable(CAP_SYS_ADMIN)) | ||
740 | return -EPERM; | ||
741 | |||
742 | vol_args = memdup_user(arg, sizeof(*vol_args)); | ||
743 | if (IS_ERR(vol_args)) | ||
744 | return PTR_ERR(vol_args); | ||
745 | |||
746 | vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; | ||
747 | namelen = strlen(vol_args->name); | ||
748 | if (strchr(vol_args->name, '/') || | ||
749 | strncmp(vol_args->name, "..", namelen) == 0) { | ||
750 | err = -EINVAL; | ||
751 | goto out; | ||
752 | } | ||
753 | |||
754 | err = mnt_want_write(file->f_path.mnt); | ||
755 | if (err) | ||
756 | goto out; | ||
757 | |||
758 | mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); | ||
759 | dentry = lookup_one_len(vol_args->name, parent, namelen); | ||
760 | if (IS_ERR(dentry)) { | ||
761 | err = PTR_ERR(dentry); | ||
762 | goto out_unlock_dir; | ||
763 | } | ||
764 | |||
765 | if (!dentry->d_inode) { | ||
766 | err = -ENOENT; | ||
767 | goto out_dput; | ||
768 | } | ||
769 | |||
770 | inode = dentry->d_inode; | ||
771 | if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) { | ||
772 | err = -EINVAL; | ||
773 | goto out_dput; | ||
774 | } | ||
775 | |||
776 | dest = BTRFS_I(inode)->root; | ||
777 | |||
778 | mutex_lock(&inode->i_mutex); | ||
779 | err = d_invalidate(dentry); | ||
780 | if (err) | ||
781 | goto out_unlock; | ||
782 | |||
783 | down_write(&root->fs_info->subvol_sem); | ||
784 | |||
785 | err = may_destroy_subvol(dest); | ||
786 | if (err) | ||
787 | goto out_up_write; | ||
788 | |||
789 | trans = btrfs_start_transaction(root, 1); | ||
790 | ret = btrfs_unlink_subvol(trans, root, dir, | ||
791 | dest->root_key.objectid, | ||
792 | dentry->d_name.name, | ||
793 | dentry->d_name.len); | ||
794 | BUG_ON(ret); | ||
795 | |||
796 | btrfs_record_root_in_trans(trans, dest); | ||
797 | |||
798 | memset(&dest->root_item.drop_progress, 0, | ||
799 | sizeof(dest->root_item.drop_progress)); | ||
800 | dest->root_item.drop_level = 0; | ||
801 | btrfs_set_root_refs(&dest->root_item, 0); | ||
802 | |||
803 | ret = btrfs_insert_orphan_item(trans, | ||
804 | root->fs_info->tree_root, | ||
805 | dest->root_key.objectid); | ||
806 | BUG_ON(ret); | ||
807 | |||
808 | ret = btrfs_commit_transaction(trans, root); | ||
809 | BUG_ON(ret); | ||
810 | inode->i_flags |= S_DEAD; | ||
811 | out_up_write: | ||
812 | up_write(&root->fs_info->subvol_sem); | ||
813 | out_unlock: | ||
814 | mutex_unlock(&inode->i_mutex); | ||
815 | if (!err) { | ||
816 | btrfs_invalidate_inodes(dest); | ||
817 | d_delete(dentry); | ||
818 | } | ||
819 | out_dput: | ||
820 | dput(dentry); | ||
821 | out_unlock_dir: | ||
822 | mutex_unlock(&dir->i_mutex); | ||
823 | mnt_drop_write(file->f_path.mnt); | ||
824 | out: | ||
825 | kfree(vol_args); | ||
826 | return err; | ||
827 | } | ||
828 | |||
783 | static int btrfs_ioctl_defrag(struct file *file) | 829 | static int btrfs_ioctl_defrag(struct file *file) |
784 | { | 830 | { |
785 | struct inode *inode = fdentry(file)->d_inode; | 831 | struct inode *inode = fdentry(file)->d_inode; |
@@ -853,8 +899,8 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg) | |||
853 | return ret; | 899 | return ret; |
854 | } | 900 | } |
855 | 901 | ||
856 | static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, | 902 | static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, |
857 | u64 off, u64 olen, u64 destoff) | 903 | u64 off, u64 olen, u64 destoff) |
858 | { | 904 | { |
859 | struct inode *inode = fdentry(file)->d_inode; | 905 | struct inode *inode = fdentry(file)->d_inode; |
860 | struct btrfs_root *root = BTRFS_I(inode)->root; | 906 | struct btrfs_root *root = BTRFS_I(inode)->root; |
@@ -1246,6 +1292,8 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
1246 | return btrfs_ioctl_snap_create(file, argp, 0); | 1292 | return btrfs_ioctl_snap_create(file, argp, 0); |
1247 | case BTRFS_IOC_SUBVOL_CREATE: | 1293 | case BTRFS_IOC_SUBVOL_CREATE: |
1248 | return btrfs_ioctl_snap_create(file, argp, 1); | 1294 | return btrfs_ioctl_snap_create(file, argp, 1); |
1295 | case BTRFS_IOC_SNAP_DESTROY: | ||
1296 | return btrfs_ioctl_snap_destroy(file, argp); | ||
1249 | case BTRFS_IOC_DEFRAG: | 1297 | case BTRFS_IOC_DEFRAG: |
1250 | return btrfs_ioctl_defrag(file); | 1298 | return btrfs_ioctl_defrag(file); |
1251 | case BTRFS_IOC_RESIZE: | 1299 | case BTRFS_IOC_RESIZE: |