diff options
Diffstat (limited to 'fs/configfs')
-rw-r--r-- | fs/configfs/dir.c | 137 |
1 files changed, 96 insertions, 41 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 5638c8f9362f..5f952187fc53 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -505,13 +505,15 @@ static int populate_groups(struct config_group *group) | |||
505 | int i; | 505 | int i; |
506 | 506 | ||
507 | if (group->default_groups) { | 507 | if (group->default_groups) { |
508 | /* FYI, we're faking mkdir here | 508 | /* |
509 | * FYI, we're faking mkdir here | ||
509 | * I'm not sure we need this semaphore, as we're called | 510 | * I'm not sure we need this semaphore, as we're called |
510 | * from our parent's mkdir. That holds our parent's | 511 | * from our parent's mkdir. That holds our parent's |
511 | * i_mutex, so afaik lookup cannot continue through our | 512 | * i_mutex, so afaik lookup cannot continue through our |
512 | * parent to find us, let alone mess with our tree. | 513 | * parent to find us, let alone mess with our tree. |
513 | * That said, taking our i_mutex is closer to mkdir | 514 | * That said, taking our i_mutex is closer to mkdir |
514 | * emulation, and shouldn't hurt. */ | 515 | * emulation, and shouldn't hurt. |
516 | */ | ||
515 | mutex_lock(&dentry->d_inode->i_mutex); | 517 | mutex_lock(&dentry->d_inode->i_mutex); |
516 | 518 | ||
517 | for (i = 0; group->default_groups[i]; i++) { | 519 | for (i = 0; group->default_groups[i]; i++) { |
@@ -546,20 +548,34 @@ static void unlink_obj(struct config_item *item) | |||
546 | 548 | ||
547 | item->ci_group = NULL; | 549 | item->ci_group = NULL; |
548 | item->ci_parent = NULL; | 550 | item->ci_parent = NULL; |
551 | |||
552 | /* Drop the reference for ci_entry */ | ||
549 | config_item_put(item); | 553 | config_item_put(item); |
550 | 554 | ||
555 | /* Drop the reference for ci_parent */ | ||
551 | config_group_put(group); | 556 | config_group_put(group); |
552 | } | 557 | } |
553 | } | 558 | } |
554 | 559 | ||
555 | static void link_obj(struct config_item *parent_item, struct config_item *item) | 560 | static void link_obj(struct config_item *parent_item, struct config_item *item) |
556 | { | 561 | { |
557 | /* Parent seems redundant with group, but it makes certain | 562 | /* |
558 | * traversals much nicer. */ | 563 | * Parent seems redundant with group, but it makes certain |
564 | * traversals much nicer. | ||
565 | */ | ||
559 | item->ci_parent = parent_item; | 566 | item->ci_parent = parent_item; |
567 | |||
568 | /* | ||
569 | * We hold a reference on the parent for the child's ci_parent | ||
570 | * link. | ||
571 | */ | ||
560 | item->ci_group = config_group_get(to_config_group(parent_item)); | 572 | item->ci_group = config_group_get(to_config_group(parent_item)); |
561 | list_add_tail(&item->ci_entry, &item->ci_group->cg_children); | 573 | list_add_tail(&item->ci_entry, &item->ci_group->cg_children); |
562 | 574 | ||
575 | /* | ||
576 | * We hold a reference on the child for ci_entry on the parent's | ||
577 | * cg_children | ||
578 | */ | ||
563 | config_item_get(item); | 579 | config_item_get(item); |
564 | } | 580 | } |
565 | 581 | ||
@@ -684,6 +700,10 @@ static void client_drop_item(struct config_item *parent_item, | |||
684 | type = parent_item->ci_type; | 700 | type = parent_item->ci_type; |
685 | BUG_ON(!type); | 701 | BUG_ON(!type); |
686 | 702 | ||
703 | /* | ||
704 | * If ->drop_item() exists, it is responsible for the | ||
705 | * config_item_put(). | ||
706 | */ | ||
687 | if (type->ct_group_ops && type->ct_group_ops->drop_item) | 707 | if (type->ct_group_ops && type->ct_group_ops->drop_item) |
688 | type->ct_group_ops->drop_item(to_config_group(parent_item), | 708 | type->ct_group_ops->drop_item(to_config_group(parent_item), |
689 | item); | 709 | item); |
@@ -694,23 +714,28 @@ static void client_drop_item(struct config_item *parent_item, | |||
694 | 714 | ||
695 | static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | 715 | static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) |
696 | { | 716 | { |
697 | int ret; | 717 | int ret, module_got = 0; |
698 | struct config_group *group; | 718 | struct config_group *group; |
699 | struct config_item *item; | 719 | struct config_item *item; |
700 | struct config_item *parent_item; | 720 | struct config_item *parent_item; |
701 | struct configfs_subsystem *subsys; | 721 | struct configfs_subsystem *subsys; |
702 | struct configfs_dirent *sd; | 722 | struct configfs_dirent *sd; |
703 | struct config_item_type *type; | 723 | struct config_item_type *type; |
704 | struct module *owner; | 724 | struct module *owner = NULL; |
705 | char *name; | 725 | char *name; |
706 | 726 | ||
707 | if (dentry->d_parent == configfs_sb->s_root) | 727 | if (dentry->d_parent == configfs_sb->s_root) { |
708 | return -EPERM; | 728 | ret = -EPERM; |
729 | goto out; | ||
730 | } | ||
709 | 731 | ||
710 | sd = dentry->d_parent->d_fsdata; | 732 | sd = dentry->d_parent->d_fsdata; |
711 | if (!(sd->s_type & CONFIGFS_USET_DIR)) | 733 | if (!(sd->s_type & CONFIGFS_USET_DIR)) { |
712 | return -EPERM; | 734 | ret = -EPERM; |
735 | goto out; | ||
736 | } | ||
713 | 737 | ||
738 | /* Get a working ref for the duration of this function */ | ||
714 | parent_item = configfs_get_config_item(dentry->d_parent); | 739 | parent_item = configfs_get_config_item(dentry->d_parent); |
715 | type = parent_item->ci_type; | 740 | type = parent_item->ci_type; |
716 | subsys = to_config_group(parent_item)->cg_subsys; | 741 | subsys = to_config_group(parent_item)->cg_subsys; |
@@ -719,15 +744,16 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
719 | if (!type || !type->ct_group_ops || | 744 | if (!type || !type->ct_group_ops || |
720 | (!type->ct_group_ops->make_group && | 745 | (!type->ct_group_ops->make_group && |
721 | !type->ct_group_ops->make_item)) { | 746 | !type->ct_group_ops->make_item)) { |
722 | config_item_put(parent_item); | 747 | ret = -EPERM; /* Lack-of-mkdir returns -EPERM */ |
723 | return -EPERM; /* What lack-of-mkdir returns */ | 748 | goto out_put; |
724 | } | 749 | } |
725 | 750 | ||
726 | name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL); | 751 | name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL); |
727 | if (!name) { | 752 | if (!name) { |
728 | config_item_put(parent_item); | 753 | ret = -ENOMEM; |
729 | return -ENOMEM; | 754 | goto out_put; |
730 | } | 755 | } |
756 | |||
731 | snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); | 757 | snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); |
732 | 758 | ||
733 | down(&subsys->su_sem); | 759 | down(&subsys->su_sem); |
@@ -748,40 +774,67 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
748 | 774 | ||
749 | kfree(name); | 775 | kfree(name); |
750 | if (!item) { | 776 | if (!item) { |
751 | config_item_put(parent_item); | 777 | /* |
752 | return -ENOMEM; | 778 | * If item == NULL, then link_obj() was never called. |
779 | * There are no extra references to clean up. | ||
780 | */ | ||
781 | ret = -ENOMEM; | ||
782 | goto out_put; | ||
753 | } | 783 | } |
754 | 784 | ||
755 | ret = -EINVAL; | 785 | /* |
786 | * link_obj() has been called (via link_group() for groups). | ||
787 | * From here on out, errors must clean that up. | ||
788 | */ | ||
789 | |||
756 | type = item->ci_type; | 790 | type = item->ci_type; |
757 | if (type) { | 791 | if (!type) { |
758 | owner = type->ct_owner; | 792 | ret = -EINVAL; |
759 | if (try_module_get(owner)) { | 793 | goto out_unlink; |
760 | if (group) { | 794 | } |
761 | ret = configfs_attach_group(parent_item, | ||
762 | item, | ||
763 | dentry); | ||
764 | } else { | ||
765 | ret = configfs_attach_item(parent_item, | ||
766 | item, | ||
767 | dentry); | ||
768 | } | ||
769 | 795 | ||
770 | if (ret) { | 796 | owner = type->ct_owner; |
771 | down(&subsys->su_sem); | 797 | if (!try_module_get(owner)) { |
772 | if (group) | 798 | ret = -EINVAL; |
773 | unlink_group(group); | 799 | goto out_unlink; |
774 | else | 800 | } |
775 | unlink_obj(item); | ||
776 | client_drop_item(parent_item, item); | ||
777 | up(&subsys->su_sem); | ||
778 | 801 | ||
779 | config_item_put(parent_item); | 802 | /* |
780 | module_put(owner); | 803 | * I hate doing it this way, but if there is |
781 | } | 804 | * an error, module_put() probably should |
782 | } | 805 | * happen after any cleanup. |
806 | */ | ||
807 | module_got = 1; | ||
808 | |||
809 | if (group) | ||
810 | ret = configfs_attach_group(parent_item, item, dentry); | ||
811 | else | ||
812 | ret = configfs_attach_item(parent_item, item, dentry); | ||
813 | |||
814 | out_unlink: | ||
815 | if (ret) { | ||
816 | /* Tear down everything we built up */ | ||
817 | down(&subsys->su_sem); | ||
818 | if (group) | ||
819 | unlink_group(group); | ||
820 | else | ||
821 | unlink_obj(item); | ||
822 | client_drop_item(parent_item, item); | ||
823 | up(&subsys->su_sem); | ||
824 | |||
825 | if (module_got) | ||
826 | module_put(owner); | ||
783 | } | 827 | } |
784 | 828 | ||
829 | out_put: | ||
830 | /* | ||
831 | * link_obj()/link_group() took a reference from child->parent, | ||
832 | * so the parent is safely pinned. We can drop our working | ||
833 | * reference. | ||
834 | */ | ||
835 | config_item_put(parent_item); | ||
836 | |||
837 | out: | ||
785 | return ret; | 838 | return ret; |
786 | } | 839 | } |
787 | 840 | ||
@@ -801,6 +854,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
801 | if (sd->s_type & CONFIGFS_USET_DEFAULT) | 854 | if (sd->s_type & CONFIGFS_USET_DEFAULT) |
802 | return -EPERM; | 855 | return -EPERM; |
803 | 856 | ||
857 | /* Get a working ref until we have the child */ | ||
804 | parent_item = configfs_get_config_item(dentry->d_parent); | 858 | parent_item = configfs_get_config_item(dentry->d_parent); |
805 | subsys = to_config_group(parent_item)->cg_subsys; | 859 | subsys = to_config_group(parent_item)->cg_subsys; |
806 | BUG_ON(!subsys); | 860 | BUG_ON(!subsys); |
@@ -817,6 +871,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
817 | return ret; | 871 | return ret; |
818 | } | 872 | } |
819 | 873 | ||
874 | /* Get a working ref for the duration of this function */ | ||
820 | item = configfs_get_config_item(dentry); | 875 | item = configfs_get_config_item(dentry); |
821 | 876 | ||
822 | /* Drop reference from above, item already holds one. */ | 877 | /* Drop reference from above, item already holds one. */ |