diff options
Diffstat (limited to 'fs/configfs/symlink.c')
-rw-r--r-- | fs/configfs/symlink.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 0004d18c40ac..bf74973b0492 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c | |||
@@ -31,6 +31,9 @@ | |||
31 | #include <linux/configfs.h> | 31 | #include <linux/configfs.h> |
32 | #include "configfs_internal.h" | 32 | #include "configfs_internal.h" |
33 | 33 | ||
34 | /* Protects attachments of new symlinks */ | ||
35 | DEFINE_MUTEX(configfs_symlink_mutex); | ||
36 | |||
34 | static int item_depth(struct config_item * item) | 37 | static int item_depth(struct config_item * item) |
35 | { | 38 | { |
36 | struct config_item * p = item; | 39 | struct config_item * p = item; |
@@ -73,11 +76,20 @@ static int create_link(struct config_item *parent_item, | |||
73 | struct configfs_symlink *sl; | 76 | struct configfs_symlink *sl; |
74 | int ret; | 77 | int ret; |
75 | 78 | ||
79 | ret = -ENOENT; | ||
80 | if (!configfs_dirent_is_ready(target_sd)) | ||
81 | goto out; | ||
76 | ret = -ENOMEM; | 82 | ret = -ENOMEM; |
77 | sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); | 83 | sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); |
78 | if (sl) { | 84 | if (sl) { |
79 | sl->sl_target = config_item_get(item); | 85 | sl->sl_target = config_item_get(item); |
80 | spin_lock(&configfs_dirent_lock); | 86 | spin_lock(&configfs_dirent_lock); |
87 | if (target_sd->s_type & CONFIGFS_USET_DROPPING) { | ||
88 | spin_unlock(&configfs_dirent_lock); | ||
89 | config_item_put(item); | ||
90 | kfree(sl); | ||
91 | return -ENOENT; | ||
92 | } | ||
81 | list_add(&sl->sl_list, &target_sd->s_links); | 93 | list_add(&sl->sl_list, &target_sd->s_links); |
82 | spin_unlock(&configfs_dirent_lock); | 94 | spin_unlock(&configfs_dirent_lock); |
83 | ret = configfs_create_link(sl, parent_item->ci_dentry, | 95 | ret = configfs_create_link(sl, parent_item->ci_dentry, |
@@ -91,6 +103,7 @@ static int create_link(struct config_item *parent_item, | |||
91 | } | 103 | } |
92 | } | 104 | } |
93 | 105 | ||
106 | out: | ||
94 | return ret; | 107 | return ret; |
95 | } | 108 | } |
96 | 109 | ||
@@ -120,6 +133,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna | |||
120 | { | 133 | { |
121 | int ret; | 134 | int ret; |
122 | struct nameidata nd; | 135 | struct nameidata nd; |
136 | struct configfs_dirent *sd; | ||
123 | struct config_item *parent_item; | 137 | struct config_item *parent_item; |
124 | struct config_item *target_item; | 138 | struct config_item *target_item; |
125 | struct config_item_type *type; | 139 | struct config_item_type *type; |
@@ -128,9 +142,19 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna | |||
128 | if (dentry->d_parent == configfs_sb->s_root) | 142 | if (dentry->d_parent == configfs_sb->s_root) |
129 | goto out; | 143 | goto out; |
130 | 144 | ||
145 | sd = dentry->d_parent->d_fsdata; | ||
146 | /* | ||
147 | * Fake invisibility if dir belongs to a group/default groups hierarchy | ||
148 | * being attached | ||
149 | */ | ||
150 | ret = -ENOENT; | ||
151 | if (!configfs_dirent_is_ready(sd)) | ||
152 | goto out; | ||
153 | |||
131 | parent_item = configfs_get_config_item(dentry->d_parent); | 154 | parent_item = configfs_get_config_item(dentry->d_parent); |
132 | type = parent_item->ci_type; | 155 | type = parent_item->ci_type; |
133 | 156 | ||
157 | ret = -EPERM; | ||
134 | if (!type || !type->ct_item_ops || | 158 | if (!type || !type->ct_item_ops || |
135 | !type->ct_item_ops->allow_link) | 159 | !type->ct_item_ops->allow_link) |
136 | goto out_put; | 160 | goto out_put; |
@@ -141,7 +165,9 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna | |||
141 | 165 | ||
142 | ret = type->ct_item_ops->allow_link(parent_item, target_item); | 166 | ret = type->ct_item_ops->allow_link(parent_item, target_item); |
143 | if (!ret) { | 167 | if (!ret) { |
168 | mutex_lock(&configfs_symlink_mutex); | ||
144 | ret = create_link(parent_item, target_item, dentry); | 169 | ret = create_link(parent_item, target_item, dentry); |
170 | mutex_unlock(&configfs_symlink_mutex); | ||
145 | if (ret && type->ct_item_ops->drop_link) | 171 | if (ret && type->ct_item_ops->drop_link) |
146 | type->ct_item_ops->drop_link(parent_item, | 172 | type->ct_item_ops->drop_link(parent_item, |
147 | target_item); | 173 | target_item); |