1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * symlink.c - operations for configfs symlinks. |
4 | * |
5 | * Based on sysfs: |
6 | * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel |
7 | * |
8 | * configfs Copyright (C) 2005 Oracle. All rights reserved. |
9 | */ |
10 | |
11 | #include <linux/fs.h> |
12 | #include <linux/module.h> |
13 | #include <linux/namei.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <linux/configfs.h> |
17 | #include "configfs_internal.h" |
18 | |
19 | /* Protects attachments of new symlinks */ |
20 | DEFINE_MUTEX(configfs_symlink_mutex); |
21 | |
22 | static int item_depth(struct config_item * item) |
23 | { |
24 | struct config_item * p = item; |
25 | int depth = 0; |
26 | do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(item: p)); |
27 | return depth; |
28 | } |
29 | |
30 | static int item_path_length(struct config_item * item) |
31 | { |
32 | struct config_item * p = item; |
33 | int length = 1; |
34 | do { |
35 | length += strlen(config_item_name(p)) + 1; |
36 | p = p->ci_parent; |
37 | } while (p && !configfs_is_root(item: p)); |
38 | return length; |
39 | } |
40 | |
41 | static void fill_item_path(struct config_item * item, char * buffer, int length) |
42 | { |
43 | struct config_item * p; |
44 | |
45 | --length; |
46 | for (p = item; p && !configfs_is_root(item: p); p = p->ci_parent) { |
47 | int cur = strlen(config_item_name(p)); |
48 | |
49 | /* back up enough to print this bus id with '/' */ |
50 | length -= cur; |
51 | memcpy(buffer + length, config_item_name(p), cur); |
52 | *(buffer + --length) = '/'; |
53 | } |
54 | } |
55 | |
56 | static int configfs_get_target_path(struct config_item *item, |
57 | struct config_item *target, char *path) |
58 | { |
59 | int depth, size; |
60 | char *s; |
61 | |
62 | depth = item_depth(item); |
63 | size = item_path_length(item: target) + depth * 3 - 1; |
64 | if (size > PATH_MAX) |
65 | return -ENAMETOOLONG; |
66 | |
67 | pr_debug("%s: depth = %d, size = %d\n" , __func__, depth, size); |
68 | |
69 | for (s = path; depth--; s += 3) |
70 | strcpy(p: s,q: "../" ); |
71 | |
72 | fill_item_path(item: target, buffer: path, length: size); |
73 | pr_debug("%s: path = '%s'\n" , __func__, path); |
74 | return 0; |
75 | } |
76 | |
77 | static int create_link(struct config_item *parent_item, |
78 | struct config_item *item, |
79 | struct dentry *dentry) |
80 | { |
81 | struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; |
82 | char *body; |
83 | int ret; |
84 | |
85 | if (!configfs_dirent_is_ready(target_sd)) |
86 | return -ENOENT; |
87 | |
88 | body = kzalloc(PAGE_SIZE, GFP_KERNEL); |
89 | if (!body) |
90 | return -ENOMEM; |
91 | |
92 | configfs_get(sd: target_sd); |
93 | spin_lock(lock: &configfs_dirent_lock); |
94 | if (target_sd->s_type & CONFIGFS_USET_DROPPING) { |
95 | spin_unlock(lock: &configfs_dirent_lock); |
96 | configfs_put(sd: target_sd); |
97 | kfree(objp: body); |
98 | return -ENOENT; |
99 | } |
100 | target_sd->s_links++; |
101 | spin_unlock(lock: &configfs_dirent_lock); |
102 | ret = configfs_get_target_path(item: parent_item, target: item, path: body); |
103 | if (!ret) |
104 | ret = configfs_create_link(target: target_sd, parent: parent_item->ci_dentry, |
105 | dentry, body); |
106 | if (ret) { |
107 | spin_lock(lock: &configfs_dirent_lock); |
108 | target_sd->s_links--; |
109 | spin_unlock(lock: &configfs_dirent_lock); |
110 | configfs_put(sd: target_sd); |
111 | kfree(objp: body); |
112 | } |
113 | return ret; |
114 | } |
115 | |
116 | |
117 | static int get_target(const char *symname, struct path *path, |
118 | struct config_item **target, struct super_block *sb) |
119 | { |
120 | int ret; |
121 | |
122 | ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); |
123 | if (!ret) { |
124 | if (path->dentry->d_sb == sb) { |
125 | *target = configfs_get_config_item(dentry: path->dentry); |
126 | if (!*target) { |
127 | ret = -ENOENT; |
128 | path_put(path); |
129 | } |
130 | } else { |
131 | ret = -EPERM; |
132 | path_put(path); |
133 | } |
134 | } |
135 | |
136 | return ret; |
137 | } |
138 | |
139 | |
140 | int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, |
141 | struct dentry *dentry, const char *symname) |
142 | { |
143 | int ret; |
144 | struct path path; |
145 | struct configfs_dirent *sd; |
146 | struct config_item *parent_item; |
147 | struct config_item *target_item = NULL; |
148 | const struct config_item_type *type; |
149 | |
150 | sd = dentry->d_parent->d_fsdata; |
151 | /* |
152 | * Fake invisibility if dir belongs to a group/default groups hierarchy |
153 | * being attached |
154 | */ |
155 | if (!configfs_dirent_is_ready(sd)) |
156 | return -ENOENT; |
157 | |
158 | parent_item = configfs_get_config_item(dentry: dentry->d_parent); |
159 | type = parent_item->ci_type; |
160 | |
161 | ret = -EPERM; |
162 | if (!type || !type->ct_item_ops || |
163 | !type->ct_item_ops->allow_link) |
164 | goto out_put; |
165 | |
166 | /* |
167 | * This is really sick. What they wanted was a hybrid of |
168 | * link(2) and symlink(2) - they wanted the target resolved |
169 | * at syscall time (as link(2) would've done), be a directory |
170 | * (which link(2) would've refused to do) *AND* be a deep |
171 | * fucking magic, making the target busy from rmdir POV. |
172 | * symlink(2) is nothing of that sort, and the locking it |
173 | * gets matches the normal symlink(2) semantics. Without |
174 | * attempts to resolve the target (which might very well |
175 | * not even exist yet) done prior to locking the parent |
176 | * directory. This perversion, OTOH, needs to resolve |
177 | * the target, which would lead to obvious deadlocks if |
178 | * attempted with any directories locked. |
179 | * |
180 | * Unfortunately, that garbage is userland ABI and we should've |
181 | * said "no" back in 2005. Too late now, so we get to |
182 | * play very ugly games with locking. |
183 | * |
184 | * Try *ANYTHING* of that sort in new code, and you will |
185 | * really regret it. Just ask yourself - what could a BOFH |
186 | * do to me and do I want to find it out first-hand? |
187 | * |
188 | * AV, a thoroughly annoyed bastard. |
189 | */ |
190 | inode_unlock(inode: dir); |
191 | ret = get_target(symname, path: &path, target: &target_item, sb: dentry->d_sb); |
192 | inode_lock(inode: dir); |
193 | if (ret) |
194 | goto out_put; |
195 | |
196 | if (dentry->d_inode || d_unhashed(dentry)) |
197 | ret = -EEXIST; |
198 | else |
199 | ret = inode_permission(&nop_mnt_idmap, dir, |
200 | MAY_WRITE | MAY_EXEC); |
201 | if (!ret) |
202 | ret = type->ct_item_ops->allow_link(parent_item, target_item); |
203 | if (!ret) { |
204 | mutex_lock(&configfs_symlink_mutex); |
205 | ret = create_link(parent_item, item: target_item, dentry); |
206 | mutex_unlock(lock: &configfs_symlink_mutex); |
207 | if (ret && type->ct_item_ops->drop_link) |
208 | type->ct_item_ops->drop_link(parent_item, |
209 | target_item); |
210 | } |
211 | |
212 | config_item_put(target_item); |
213 | path_put(&path); |
214 | |
215 | out_put: |
216 | config_item_put(parent_item); |
217 | return ret; |
218 | } |
219 | |
220 | int configfs_unlink(struct inode *dir, struct dentry *dentry) |
221 | { |
222 | struct configfs_dirent *sd = dentry->d_fsdata, *target_sd; |
223 | struct config_item *parent_item; |
224 | const struct config_item_type *type; |
225 | int ret; |
226 | |
227 | ret = -EPERM; /* What lack-of-symlink returns */ |
228 | if (!(sd->s_type & CONFIGFS_ITEM_LINK)) |
229 | goto out; |
230 | |
231 | target_sd = sd->s_element; |
232 | |
233 | parent_item = configfs_get_config_item(dentry: dentry->d_parent); |
234 | type = parent_item->ci_type; |
235 | |
236 | spin_lock(lock: &configfs_dirent_lock); |
237 | list_del_init(entry: &sd->s_sibling); |
238 | spin_unlock(lock: &configfs_dirent_lock); |
239 | configfs_drop_dentry(sd, parent: dentry->d_parent); |
240 | dput(dentry); |
241 | configfs_put(sd); |
242 | |
243 | /* |
244 | * drop_link() must be called before |
245 | * decrementing target's ->s_links, so that the order of |
246 | * drop_link(this, target) and drop_item(target) is preserved. |
247 | */ |
248 | if (type && type->ct_item_ops && |
249 | type->ct_item_ops->drop_link) |
250 | type->ct_item_ops->drop_link(parent_item, |
251 | target_sd->s_element); |
252 | |
253 | spin_lock(lock: &configfs_dirent_lock); |
254 | target_sd->s_links--; |
255 | spin_unlock(lock: &configfs_dirent_lock); |
256 | configfs_put(sd: target_sd); |
257 | |
258 | config_item_put(parent_item); |
259 | |
260 | ret = 0; |
261 | |
262 | out: |
263 | return ret; |
264 | } |
265 | |
266 | const struct inode_operations configfs_symlink_inode_operations = { |
267 | .get_link = simple_get_link, |
268 | .setattr = configfs_setattr, |
269 | }; |
270 | |
271 | |