diff options
Diffstat (limited to 'fs/configfs')
-rw-r--r-- | fs/configfs/dir.c | 104 |
1 files changed, 72 insertions, 32 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 810c1395d6b2..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,14 +714,14 @@ 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) { |
@@ -754,43 +774,63 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
754 | 774 | ||
755 | kfree(name); | 775 | kfree(name); |
756 | if (!item) { | 776 | if (!item) { |
777 | /* | ||
778 | * If item == NULL, then link_obj() was never called. | ||
779 | * There are no extra references to clean up. | ||
780 | */ | ||
757 | ret = -ENOMEM; | 781 | ret = -ENOMEM; |
758 | goto out_put; | 782 | goto out_put; |
759 | } | 783 | } |
760 | 784 | ||
761 | 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 | |||
762 | type = item->ci_type; | 790 | type = item->ci_type; |
763 | if (type) { | 791 | if (!type) { |
764 | owner = type->ct_owner; | 792 | ret = -EINVAL; |
765 | if (try_module_get(owner)) { | 793 | goto out_unlink; |
766 | if (group) { | 794 | } |
767 | ret = configfs_attach_group(parent_item, | ||
768 | item, | ||
769 | dentry); | ||
770 | } else { | ||
771 | ret = configfs_attach_item(parent_item, | ||
772 | item, | ||
773 | dentry); | ||
774 | } | ||
775 | 795 | ||
776 | if (ret) { | 796 | owner = type->ct_owner; |
777 | down(&subsys->su_sem); | 797 | if (!try_module_get(owner)) { |
778 | if (group) | 798 | ret = -EINVAL; |
779 | unlink_group(group); | 799 | goto out_unlink; |
780 | else | 800 | } |
781 | unlink_obj(item); | ||
782 | client_drop_item(parent_item, item); | ||
783 | up(&subsys->su_sem); | ||
784 | 801 | ||
785 | module_put(owner); | 802 | /* |
786 | } | 803 | * I hate doing it this way, but if there is |
787 | } | 804 | * an error, module_put() probably should |
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); | ||
788 | } | 827 | } |
789 | 828 | ||
790 | out_put: | 829 | out_put: |
791 | /* | 830 | /* |
792 | * link_obj()/link_group() took a reference from child->parent. | 831 | * link_obj()/link_group() took a reference from child->parent, |
793 | * Drop our working ref | 832 | * so the parent is safely pinned. We can drop our working |
833 | * reference. | ||
794 | */ | 834 | */ |
795 | config_item_put(parent_item); | 835 | config_item_put(parent_item); |
796 | 836 | ||