| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* -*- mode: c; c-basic-offset: 8; -*- |
|---|
| 2 | 3 | * vim: noexpandtab sw=8 ts=8 sts=0: |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * symlink.c - operations for configfs symlinks. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public |
|---|
| 8 | | - * License as published by the Free Software Foundation; either |
|---|
| 9 | | - * version 2 of the License, or (at your option) any later version. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 14 | | - * General Public License for more details. |
|---|
| 15 | | - * |
|---|
| 16 | | - * You should have received a copy of the GNU General Public |
|---|
| 17 | | - * License along with this program; if not, write to the |
|---|
| 18 | | - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|---|
| 19 | | - * Boston, MA 021110-1307, USA. |
|---|
| 20 | 6 | * |
|---|
| 21 | 7 | * Based on sysfs: |
|---|
| 22 | 8 | * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel |
|---|
| .. | .. |
|---|
| 69 | 55 | } |
|---|
| 70 | 56 | } |
|---|
| 71 | 57 | |
|---|
| 58 | +static int configfs_get_target_path(struct config_item *item, |
|---|
| 59 | + struct config_item *target, char *path) |
|---|
| 60 | +{ |
|---|
| 61 | + int depth, size; |
|---|
| 62 | + char *s; |
|---|
| 63 | + |
|---|
| 64 | + depth = item_depth(item); |
|---|
| 65 | + size = item_path_length(target) + depth * 3 - 1; |
|---|
| 66 | + if (size > PATH_MAX) |
|---|
| 67 | + return -ENAMETOOLONG; |
|---|
| 68 | + |
|---|
| 69 | + pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); |
|---|
| 70 | + |
|---|
| 71 | + for (s = path; depth--; s += 3) |
|---|
| 72 | + strcpy(s,"../"); |
|---|
| 73 | + |
|---|
| 74 | + fill_item_path(target, path, size); |
|---|
| 75 | + pr_debug("%s: path = '%s'\n", __func__, path); |
|---|
| 76 | + return 0; |
|---|
| 77 | +} |
|---|
| 78 | + |
|---|
| 72 | 79 | static int create_link(struct config_item *parent_item, |
|---|
| 73 | 80 | struct config_item *item, |
|---|
| 74 | 81 | struct dentry *dentry) |
|---|
| 75 | 82 | { |
|---|
| 76 | 83 | struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; |
|---|
| 77 | | - struct configfs_symlink *sl; |
|---|
| 84 | + char *body; |
|---|
| 78 | 85 | int ret; |
|---|
| 79 | 86 | |
|---|
| 80 | | - ret = -ENOENT; |
|---|
| 81 | 87 | if (!configfs_dirent_is_ready(target_sd)) |
|---|
| 82 | | - goto out; |
|---|
| 83 | | - ret = -ENOMEM; |
|---|
| 84 | | - sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); |
|---|
| 85 | | - if (sl) { |
|---|
| 86 | | - spin_lock(&configfs_dirent_lock); |
|---|
| 87 | | - if (target_sd->s_type & CONFIGFS_USET_DROPPING) { |
|---|
| 88 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 89 | | - kfree(sl); |
|---|
| 90 | | - return -ENOENT; |
|---|
| 91 | | - } |
|---|
| 92 | | - sl->sl_target = config_item_get(item); |
|---|
| 93 | | - list_add(&sl->sl_list, &target_sd->s_links); |
|---|
| 94 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 95 | | - ret = configfs_create_link(sl, parent_item->ci_dentry, |
|---|
| 96 | | - dentry); |
|---|
| 97 | | - if (ret) { |
|---|
| 98 | | - spin_lock(&configfs_dirent_lock); |
|---|
| 99 | | - list_del_init(&sl->sl_list); |
|---|
| 100 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 101 | | - config_item_put(item); |
|---|
| 102 | | - kfree(sl); |
|---|
| 103 | | - } |
|---|
| 104 | | - } |
|---|
| 88 | + return -ENOENT; |
|---|
| 105 | 89 | |
|---|
| 106 | | -out: |
|---|
| 90 | + body = kzalloc(PAGE_SIZE, GFP_KERNEL); |
|---|
| 91 | + if (!body) |
|---|
| 92 | + return -ENOMEM; |
|---|
| 93 | + |
|---|
| 94 | + configfs_get(target_sd); |
|---|
| 95 | + spin_lock(&configfs_dirent_lock); |
|---|
| 96 | + if (target_sd->s_type & CONFIGFS_USET_DROPPING) { |
|---|
| 97 | + spin_unlock(&configfs_dirent_lock); |
|---|
| 98 | + configfs_put(target_sd); |
|---|
| 99 | + kfree(body); |
|---|
| 100 | + return -ENOENT; |
|---|
| 101 | + } |
|---|
| 102 | + target_sd->s_links++; |
|---|
| 103 | + spin_unlock(&configfs_dirent_lock); |
|---|
| 104 | + ret = configfs_get_target_path(parent_item, item, body); |
|---|
| 105 | + if (!ret) |
|---|
| 106 | + ret = configfs_create_link(target_sd, parent_item->ci_dentry, |
|---|
| 107 | + dentry, body); |
|---|
| 108 | + if (ret) { |
|---|
| 109 | + spin_lock(&configfs_dirent_lock); |
|---|
| 110 | + target_sd->s_links--; |
|---|
| 111 | + spin_unlock(&configfs_dirent_lock); |
|---|
| 112 | + configfs_put(target_sd); |
|---|
| 113 | + kfree(body); |
|---|
| 114 | + } |
|---|
| 107 | 115 | return ret; |
|---|
| 108 | 116 | } |
|---|
| 109 | 117 | |
|---|
| .. | .. |
|---|
| 145 | 153 | * Fake invisibility if dir belongs to a group/default groups hierarchy |
|---|
| 146 | 154 | * being attached |
|---|
| 147 | 155 | */ |
|---|
| 148 | | - ret = -ENOENT; |
|---|
| 149 | 156 | if (!configfs_dirent_is_ready(sd)) |
|---|
| 150 | | - goto out; |
|---|
| 157 | + return -ENOENT; |
|---|
| 151 | 158 | |
|---|
| 152 | 159 | parent_item = configfs_get_config_item(dentry->d_parent); |
|---|
| 153 | 160 | type = parent_item->ci_type; |
|---|
| .. | .. |
|---|
| 207 | 214 | |
|---|
| 208 | 215 | out_put: |
|---|
| 209 | 216 | config_item_put(parent_item); |
|---|
| 210 | | - |
|---|
| 211 | | -out: |
|---|
| 212 | 217 | return ret; |
|---|
| 213 | 218 | } |
|---|
| 214 | 219 | |
|---|
| 215 | 220 | int configfs_unlink(struct inode *dir, struct dentry *dentry) |
|---|
| 216 | 221 | { |
|---|
| 217 | | - struct configfs_dirent *sd = dentry->d_fsdata; |
|---|
| 218 | | - struct configfs_symlink *sl; |
|---|
| 222 | + struct configfs_dirent *sd = dentry->d_fsdata, *target_sd; |
|---|
| 219 | 223 | struct config_item *parent_item; |
|---|
| 220 | 224 | const struct config_item_type *type; |
|---|
| 221 | 225 | int ret; |
|---|
| .. | .. |
|---|
| 224 | 228 | if (!(sd->s_type & CONFIGFS_ITEM_LINK)) |
|---|
| 225 | 229 | goto out; |
|---|
| 226 | 230 | |
|---|
| 227 | | - sl = sd->s_element; |
|---|
| 231 | + target_sd = sd->s_element; |
|---|
| 228 | 232 | |
|---|
| 229 | 233 | parent_item = configfs_get_config_item(dentry->d_parent); |
|---|
| 230 | 234 | type = parent_item->ci_type; |
|---|
| .. | .. |
|---|
| 238 | 242 | |
|---|
| 239 | 243 | /* |
|---|
| 240 | 244 | * drop_link() must be called before |
|---|
| 241 | | - * list_del_init(&sl->sl_list), so that the order of |
|---|
| 245 | + * decrementing target's ->s_links, so that the order of |
|---|
| 242 | 246 | * drop_link(this, target) and drop_item(target) is preserved. |
|---|
| 243 | 247 | */ |
|---|
| 244 | 248 | if (type && type->ct_item_ops && |
|---|
| 245 | 249 | type->ct_item_ops->drop_link) |
|---|
| 246 | 250 | type->ct_item_ops->drop_link(parent_item, |
|---|
| 247 | | - sl->sl_target); |
|---|
| 251 | + target_sd->s_element); |
|---|
| 248 | 252 | |
|---|
| 249 | 253 | spin_lock(&configfs_dirent_lock); |
|---|
| 250 | | - list_del_init(&sl->sl_list); |
|---|
| 254 | + target_sd->s_links--; |
|---|
| 251 | 255 | spin_unlock(&configfs_dirent_lock); |
|---|
| 252 | | - |
|---|
| 253 | | - /* Put reference from create_link() */ |
|---|
| 254 | | - config_item_put(sl->sl_target); |
|---|
| 255 | | - kfree(sl); |
|---|
| 256 | + configfs_put(target_sd); |
|---|
| 256 | 257 | |
|---|
| 257 | 258 | config_item_put(parent_item); |
|---|
| 258 | 259 | |
|---|
| .. | .. |
|---|
| 262 | 263 | return ret; |
|---|
| 263 | 264 | } |
|---|
| 264 | 265 | |
|---|
| 265 | | -static int configfs_get_target_path(struct config_item * item, struct config_item * target, |
|---|
| 266 | | - char *path) |
|---|
| 267 | | -{ |
|---|
| 268 | | - char * s; |
|---|
| 269 | | - int depth, size; |
|---|
| 270 | | - |
|---|
| 271 | | - depth = item_depth(item); |
|---|
| 272 | | - size = item_path_length(target) + depth * 3 - 1; |
|---|
| 273 | | - if (size > PATH_MAX) |
|---|
| 274 | | - return -ENAMETOOLONG; |
|---|
| 275 | | - |
|---|
| 276 | | - pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); |
|---|
| 277 | | - |
|---|
| 278 | | - for (s = path; depth--; s += 3) |
|---|
| 279 | | - strcpy(s,"../"); |
|---|
| 280 | | - |
|---|
| 281 | | - fill_item_path(target, path, size); |
|---|
| 282 | | - pr_debug("%s: path = '%s'\n", __func__, path); |
|---|
| 283 | | - |
|---|
| 284 | | - return 0; |
|---|
| 285 | | -} |
|---|
| 286 | | - |
|---|
| 287 | | -static int configfs_getlink(struct dentry *dentry, char * path) |
|---|
| 288 | | -{ |
|---|
| 289 | | - struct config_item *item, *target_item; |
|---|
| 290 | | - int error = 0; |
|---|
| 291 | | - |
|---|
| 292 | | - item = configfs_get_config_item(dentry->d_parent); |
|---|
| 293 | | - if (!item) |
|---|
| 294 | | - return -EINVAL; |
|---|
| 295 | | - |
|---|
| 296 | | - target_item = configfs_get_config_item(dentry); |
|---|
| 297 | | - if (!target_item) { |
|---|
| 298 | | - config_item_put(item); |
|---|
| 299 | | - return -EINVAL; |
|---|
| 300 | | - } |
|---|
| 301 | | - |
|---|
| 302 | | - down_read(&configfs_rename_sem); |
|---|
| 303 | | - error = configfs_get_target_path(item, target_item, path); |
|---|
| 304 | | - up_read(&configfs_rename_sem); |
|---|
| 305 | | - |
|---|
| 306 | | - config_item_put(item); |
|---|
| 307 | | - config_item_put(target_item); |
|---|
| 308 | | - return error; |
|---|
| 309 | | - |
|---|
| 310 | | -} |
|---|
| 311 | | - |
|---|
| 312 | | -static const char *configfs_get_link(struct dentry *dentry, |
|---|
| 313 | | - struct inode *inode, |
|---|
| 314 | | - struct delayed_call *done) |
|---|
| 315 | | -{ |
|---|
| 316 | | - char *body; |
|---|
| 317 | | - int error; |
|---|
| 318 | | - |
|---|
| 319 | | - if (!dentry) |
|---|
| 320 | | - return ERR_PTR(-ECHILD); |
|---|
| 321 | | - |
|---|
| 322 | | - body = kzalloc(PAGE_SIZE, GFP_KERNEL); |
|---|
| 323 | | - if (!body) |
|---|
| 324 | | - return ERR_PTR(-ENOMEM); |
|---|
| 325 | | - |
|---|
| 326 | | - error = configfs_getlink(dentry, body); |
|---|
| 327 | | - if (!error) { |
|---|
| 328 | | - set_delayed_call(done, kfree_link, body); |
|---|
| 329 | | - return body; |
|---|
| 330 | | - } |
|---|
| 331 | | - |
|---|
| 332 | | - kfree(body); |
|---|
| 333 | | - return ERR_PTR(error); |
|---|
| 334 | | -} |
|---|
| 335 | | - |
|---|
| 336 | 266 | const struct inode_operations configfs_symlink_inode_operations = { |
|---|
| 337 | | - .get_link = configfs_get_link, |
|---|
| 267 | + .get_link = simple_get_link, |
|---|
| 338 | 268 | .setattr = configfs_setattr, |
|---|
| 339 | 269 | }; |
|---|
| 340 | 270 | |
|---|