diff options
author | Louis Rilling <louis.rilling@kerlabs.com> | 2008-07-04 10:56:06 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-07-31 19:21:13 -0400 |
commit | 2e2ce171c3ba6f2753fb1fd2706b63683394da2d (patch) | |
tree | b3b75b6b140ab6807754fd500c5a55b0cb7324f8 /fs/configfs/dir.c | |
parent | 2a109f2a4155f168047aa2f5b3a170e279bef89a (diff) |
[PATCH] configfs: Lock new directory inodes before removing on cleanup after failure
Once a new configfs directory is created by configfs_attach_item() or
configfs_attach_group(), a failure in the remaining initialization steps leads
to removing a directory which inode the VFS may have already accessed.
This commit adds the necessary inode locking to safely remove configfs
directories while cleaning up after a failure. As an advantage, the locking
rules of populate_groups() and detach_groups() become the same: the caller must
have the group's inode mutex locked.
Signed-off-by: Louis Rilling <louis.rilling@kerlabs.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/configfs/dir.c')
-rw-r--r-- | fs/configfs/dir.c | 48 |
1 files changed, 29 insertions, 19 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 647499a3479b..4d11479cf2c3 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -324,6 +324,8 @@ static void remove_dir(struct dentry * d) | |||
324 | * The only thing special about this is that we remove any files in | 324 | * The only thing special about this is that we remove any files in |
325 | * the directory before we remove the directory, and we've inlined | 325 | * the directory before we remove the directory, and we've inlined |
326 | * what used to be configfs_rmdir() below, instead of calling separately. | 326 | * what used to be configfs_rmdir() below, instead of calling separately. |
327 | * | ||
328 | * Caller holds the mutex of the item's inode | ||
327 | */ | 329 | */ |
328 | 330 | ||
329 | static void configfs_remove_dir(struct config_item * item) | 331 | static void configfs_remove_dir(struct config_item * item) |
@@ -612,36 +614,21 @@ static int create_default_group(struct config_group *parent_group, | |||
612 | static int populate_groups(struct config_group *group) | 614 | static int populate_groups(struct config_group *group) |
613 | { | 615 | { |
614 | struct config_group *new_group; | 616 | struct config_group *new_group; |
615 | struct dentry *dentry = group->cg_item.ci_dentry; | ||
616 | int ret = 0; | 617 | int ret = 0; |
617 | int i; | 618 | int i; |
618 | 619 | ||
619 | if (group->default_groups) { | 620 | if (group->default_groups) { |
620 | /* | ||
621 | * FYI, we're faking mkdir here | ||
622 | * I'm not sure we need this semaphore, as we're called | ||
623 | * from our parent's mkdir. That holds our parent's | ||
624 | * i_mutex, so afaik lookup cannot continue through our | ||
625 | * parent to find us, let alone mess with our tree. | ||
626 | * That said, taking our i_mutex is closer to mkdir | ||
627 | * emulation, and shouldn't hurt. | ||
628 | */ | ||
629 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); | ||
630 | |||
631 | for (i = 0; group->default_groups[i]; i++) { | 621 | for (i = 0; group->default_groups[i]; i++) { |
632 | new_group = group->default_groups[i]; | 622 | new_group = group->default_groups[i]; |
633 | 623 | ||
634 | ret = create_default_group(group, new_group); | 624 | ret = create_default_group(group, new_group); |
635 | if (ret) | 625 | if (ret) { |
626 | detach_groups(group); | ||
636 | break; | 627 | break; |
628 | } | ||
637 | } | 629 | } |
638 | |||
639 | mutex_unlock(&dentry->d_inode->i_mutex); | ||
640 | } | 630 | } |
641 | 631 | ||
642 | if (ret) | ||
643 | detach_groups(group); | ||
644 | |||
645 | return ret; | 632 | return ret; |
646 | } | 633 | } |
647 | 634 | ||
@@ -756,7 +743,15 @@ static int configfs_attach_item(struct config_item *parent_item, | |||
756 | if (!ret) { | 743 | if (!ret) { |
757 | ret = populate_attrs(item); | 744 | ret = populate_attrs(item); |
758 | if (ret) { | 745 | if (ret) { |
746 | /* | ||
747 | * We are going to remove an inode and its dentry but | ||
748 | * the VFS may already have hit and used them. Thus, | ||
749 | * we must lock them as rmdir() would. | ||
750 | */ | ||
751 | mutex_lock(&dentry->d_inode->i_mutex); | ||
759 | configfs_remove_dir(item); | 752 | configfs_remove_dir(item); |
753 | dentry->d_inode->i_flags |= S_DEAD; | ||
754 | mutex_unlock(&dentry->d_inode->i_mutex); | ||
760 | d_delete(dentry); | 755 | d_delete(dentry); |
761 | } | 756 | } |
762 | } | 757 | } |
@@ -764,6 +759,7 @@ static int configfs_attach_item(struct config_item *parent_item, | |||
764 | return ret; | 759 | return ret; |
765 | } | 760 | } |
766 | 761 | ||
762 | /* Caller holds the mutex of the item's inode */ | ||
767 | static void configfs_detach_item(struct config_item *item) | 763 | static void configfs_detach_item(struct config_item *item) |
768 | { | 764 | { |
769 | detach_attrs(item); | 765 | detach_attrs(item); |
@@ -782,16 +778,30 @@ static int configfs_attach_group(struct config_item *parent_item, | |||
782 | sd = dentry->d_fsdata; | 778 | sd = dentry->d_fsdata; |
783 | sd->s_type |= CONFIGFS_USET_DIR; | 779 | sd->s_type |= CONFIGFS_USET_DIR; |
784 | 780 | ||
781 | /* | ||
782 | * FYI, we're faking mkdir in populate_groups() | ||
783 | * We must lock the group's inode to avoid races with the VFS | ||
784 | * which can already hit the inode and try to add/remove entries | ||
785 | * under it. | ||
786 | * | ||
787 | * We must also lock the inode to remove it safely in case of | ||
788 | * error, as rmdir() would. | ||
789 | */ | ||
790 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); | ||
785 | ret = populate_groups(to_config_group(item)); | 791 | ret = populate_groups(to_config_group(item)); |
786 | if (ret) { | 792 | if (ret) { |
787 | configfs_detach_item(item); | 793 | configfs_detach_item(item); |
788 | d_delete(dentry); | 794 | dentry->d_inode->i_flags |= S_DEAD; |
789 | } | 795 | } |
796 | mutex_unlock(&dentry->d_inode->i_mutex); | ||
797 | if (ret) | ||
798 | d_delete(dentry); | ||
790 | } | 799 | } |
791 | 800 | ||
792 | return ret; | 801 | return ret; |
793 | } | 802 | } |
794 | 803 | ||
804 | /* Caller holds the mutex of the group's inode */ | ||
795 | static void configfs_detach_group(struct config_item *item) | 805 | static void configfs_detach_group(struct config_item *item) |
796 | { | 806 | { |
797 | detach_groups(to_config_group(item)); | 807 | detach_groups(to_config_group(item)); |