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); |
