xref: /openbmc/linux/fs/configfs/symlink.c (revision b7019ac5)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* -*- mode: c; c-basic-offset: 8; -*-
3  * vim: noexpandtab sw=8 ts=8 sts=0:
4  *
5  * symlink.c - operations for configfs symlinks.
6  *
7  * Based on sysfs:
8  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
9  *
10  * configfs Copyright (C) 2005 Oracle.  All rights reserved.
11  */
12 
13 #include <linux/fs.h>
14 #include <linux/module.h>
15 #include <linux/namei.h>
16 #include <linux/slab.h>
17 
18 #include <linux/configfs.h>
19 #include "configfs_internal.h"
20 
21 /* Protects attachments of new symlinks */
22 DEFINE_MUTEX(configfs_symlink_mutex);
23 
24 static int item_depth(struct config_item * item)
25 {
26 	struct config_item * p = item;
27 	int depth = 0;
28 	do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p));
29 	return depth;
30 }
31 
32 static int item_path_length(struct config_item * item)
33 {
34 	struct config_item * p = item;
35 	int length = 1;
36 	do {
37 		length += strlen(config_item_name(p)) + 1;
38 		p = p->ci_parent;
39 	} while (p && !configfs_is_root(p));
40 	return length;
41 }
42 
43 static void fill_item_path(struct config_item * item, char * buffer, int length)
44 {
45 	struct config_item * p;
46 
47 	--length;
48 	for (p = item; p && !configfs_is_root(p); p = p->ci_parent) {
49 		int cur = strlen(config_item_name(p));
50 
51 		/* back up enough to print this bus id with '/' */
52 		length -= cur;
53 		memcpy(buffer + length, config_item_name(p), cur);
54 		*(buffer + --length) = '/';
55 	}
56 }
57 
58 static int create_link(struct config_item *parent_item,
59 		       struct config_item *item,
60 		       struct dentry *dentry)
61 {
62 	struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;
63 	struct configfs_symlink *sl;
64 	int ret;
65 
66 	ret = -ENOENT;
67 	if (!configfs_dirent_is_ready(target_sd))
68 		goto out;
69 	ret = -ENOMEM;
70 	sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL);
71 	if (sl) {
72 		spin_lock(&configfs_dirent_lock);
73 		if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
74 			spin_unlock(&configfs_dirent_lock);
75 			kfree(sl);
76 			return -ENOENT;
77 		}
78 		sl->sl_target = config_item_get(item);
79 		list_add(&sl->sl_list, &target_sd->s_links);
80 		spin_unlock(&configfs_dirent_lock);
81 		ret = configfs_create_link(sl, parent_item->ci_dentry,
82 					   dentry);
83 		if (ret) {
84 			spin_lock(&configfs_dirent_lock);
85 			list_del_init(&sl->sl_list);
86 			spin_unlock(&configfs_dirent_lock);
87 			config_item_put(item);
88 			kfree(sl);
89 		}
90 	}
91 
92 out:
93 	return ret;
94 }
95 
96 
97 static int get_target(const char *symname, struct path *path,
98 		      struct config_item **target, struct super_block *sb)
99 {
100 	int ret;
101 
102 	ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path);
103 	if (!ret) {
104 		if (path->dentry->d_sb == sb) {
105 			*target = configfs_get_config_item(path->dentry);
106 			if (!*target) {
107 				ret = -ENOENT;
108 				path_put(path);
109 			}
110 		} else {
111 			ret = -EPERM;
112 			path_put(path);
113 		}
114 	}
115 
116 	return ret;
117 }
118 
119 
120 int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
121 {
122 	int ret;
123 	struct path path;
124 	struct configfs_dirent *sd;
125 	struct config_item *parent_item;
126 	struct config_item *target_item = NULL;
127 	const struct config_item_type *type;
128 
129 	sd = dentry->d_parent->d_fsdata;
130 	/*
131 	 * Fake invisibility if dir belongs to a group/default groups hierarchy
132 	 * being attached
133 	 */
134 	ret = -ENOENT;
135 	if (!configfs_dirent_is_ready(sd))
136 		goto out;
137 
138 	parent_item = configfs_get_config_item(dentry->d_parent);
139 	type = parent_item->ci_type;
140 
141 	ret = -EPERM;
142 	if (!type || !type->ct_item_ops ||
143 	    !type->ct_item_ops->allow_link)
144 		goto out_put;
145 
146 	ret = get_target(symname, &path, &target_item, dentry->d_sb);
147 	if (ret)
148 		goto out_put;
149 
150 	ret = type->ct_item_ops->allow_link(parent_item, target_item);
151 	if (!ret) {
152 		mutex_lock(&configfs_symlink_mutex);
153 		ret = create_link(parent_item, target_item, dentry);
154 		mutex_unlock(&configfs_symlink_mutex);
155 		if (ret && type->ct_item_ops->drop_link)
156 			type->ct_item_ops->drop_link(parent_item,
157 						     target_item);
158 	}
159 
160 	config_item_put(target_item);
161 	path_put(&path);
162 
163 out_put:
164 	config_item_put(parent_item);
165 
166 out:
167 	return ret;
168 }
169 
170 int configfs_unlink(struct inode *dir, struct dentry *dentry)
171 {
172 	struct configfs_dirent *sd = dentry->d_fsdata;
173 	struct configfs_symlink *sl;
174 	struct config_item *parent_item;
175 	const struct config_item_type *type;
176 	int ret;
177 
178 	ret = -EPERM;  /* What lack-of-symlink returns */
179 	if (!(sd->s_type & CONFIGFS_ITEM_LINK))
180 		goto out;
181 
182 	sl = sd->s_element;
183 
184 	parent_item = configfs_get_config_item(dentry->d_parent);
185 	type = parent_item->ci_type;
186 
187 	spin_lock(&configfs_dirent_lock);
188 	list_del_init(&sd->s_sibling);
189 	spin_unlock(&configfs_dirent_lock);
190 	configfs_drop_dentry(sd, dentry->d_parent);
191 	dput(dentry);
192 	configfs_put(sd);
193 
194 	/*
195 	 * drop_link() must be called before
196 	 * list_del_init(&sl->sl_list), so that the order of
197 	 * drop_link(this, target) and drop_item(target) is preserved.
198 	 */
199 	if (type && type->ct_item_ops &&
200 	    type->ct_item_ops->drop_link)
201 		type->ct_item_ops->drop_link(parent_item,
202 					       sl->sl_target);
203 
204 	spin_lock(&configfs_dirent_lock);
205 	list_del_init(&sl->sl_list);
206 	spin_unlock(&configfs_dirent_lock);
207 
208 	/* Put reference from create_link() */
209 	config_item_put(sl->sl_target);
210 	kfree(sl);
211 
212 	config_item_put(parent_item);
213 
214 	ret = 0;
215 
216 out:
217 	return ret;
218 }
219 
220 static int configfs_get_target_path(struct config_item * item, struct config_item * target,
221 				   char *path)
222 {
223 	char * s;
224 	int depth, size;
225 
226 	depth = item_depth(item);
227 	size = item_path_length(target) + depth * 3 - 1;
228 	if (size > PATH_MAX)
229 		return -ENAMETOOLONG;
230 
231 	pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size);
232 
233 	for (s = path; depth--; s += 3)
234 		strcpy(s,"../");
235 
236 	fill_item_path(target, path, size);
237 	pr_debug("%s: path = '%s'\n", __func__, path);
238 
239 	return 0;
240 }
241 
242 static int configfs_getlink(struct dentry *dentry, char * path)
243 {
244 	struct config_item *item, *target_item;
245 	int error = 0;
246 
247 	item = configfs_get_config_item(dentry->d_parent);
248 	if (!item)
249 		return -EINVAL;
250 
251 	target_item = configfs_get_config_item(dentry);
252 	if (!target_item) {
253 		config_item_put(item);
254 		return -EINVAL;
255 	}
256 
257 	down_read(&configfs_rename_sem);
258 	error = configfs_get_target_path(item, target_item, path);
259 	up_read(&configfs_rename_sem);
260 
261 	config_item_put(item);
262 	config_item_put(target_item);
263 	return error;
264 
265 }
266 
267 static const char *configfs_get_link(struct dentry *dentry,
268 				     struct inode *inode,
269 				     struct delayed_call *done)
270 {
271 	char *body;
272 	int error;
273 
274 	if (!dentry)
275 		return ERR_PTR(-ECHILD);
276 
277 	body = kzalloc(PAGE_SIZE, GFP_KERNEL);
278 	if (!body)
279 		return ERR_PTR(-ENOMEM);
280 
281 	error = configfs_getlink(dentry, body);
282 	if (!error) {
283 		set_delayed_call(done, kfree_link, body);
284 		return body;
285 	}
286 
287 	kfree(body);
288 	return ERR_PTR(error);
289 }
290 
291 const struct inode_operations configfs_symlink_inode_operations = {
292 	.get_link = configfs_get_link,
293 	.setattr = configfs_setattr,
294 };
295 
296