.. | .. |
---|
| 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 | |
---|