xref: /openbmc/linux/fs/sysfs/group.c (revision 0da85d1e)
1 /*
2  * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
3  *
4  * Copyright (c) 2003 Patrick Mochel
5  * Copyright (c) 2003 Open Source Development Lab
6  * Copyright (c) 2013 Greg Kroah-Hartman
7  * Copyright (c) 2013 The Linux Foundation
8  *
9  * This file is released undert the GPL v2.
10  *
11  */
12 
13 #include <linux/kobject.h>
14 #include <linux/module.h>
15 #include <linux/dcache.h>
16 #include <linux/namei.h>
17 #include <linux/err.h>
18 #include "sysfs.h"
19 
20 
21 static void remove_files(struct kernfs_node *parent,
22 			 const struct attribute_group *grp)
23 {
24 	struct attribute *const *attr;
25 	struct bin_attribute *const *bin_attr;
26 
27 	if (grp->attrs)
28 		for (attr = grp->attrs; *attr; attr++)
29 			kernfs_remove_by_name(parent, (*attr)->name);
30 	if (grp->bin_attrs)
31 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
32 			kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
33 }
34 
35 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
36 			const struct attribute_group *grp, int update)
37 {
38 	struct attribute *const *attr;
39 	struct bin_attribute *const *bin_attr;
40 	int error = 0, i;
41 
42 	if (grp->attrs) {
43 		for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
44 			umode_t mode = (*attr)->mode;
45 
46 			/*
47 			 * In update mode, we're changing the permissions or
48 			 * visibility.  Do this by first removing then
49 			 * re-adding (if required) the file.
50 			 */
51 			if (update)
52 				kernfs_remove_by_name(parent, (*attr)->name);
53 			if (grp->is_visible) {
54 				mode = grp->is_visible(kobj, *attr, i);
55 				if (!mode)
56 					continue;
57 			}
58 
59 			WARN(mode & ~(SYSFS_PREALLOC | 0664),
60 			     "Attribute %s: Invalid permissions 0%o\n",
61 			     (*attr)->name, mode);
62 
63 			mode &= SYSFS_PREALLOC | 0664;
64 			error = sysfs_add_file_mode_ns(parent, *attr, false,
65 						       mode, NULL);
66 			if (unlikely(error))
67 				break;
68 		}
69 		if (error) {
70 			remove_files(parent, grp);
71 			goto exit;
72 		}
73 	}
74 
75 	if (grp->bin_attrs) {
76 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
77 			if (update)
78 				kernfs_remove_by_name(parent,
79 						(*bin_attr)->attr.name);
80 			error = sysfs_add_file_mode_ns(parent,
81 					&(*bin_attr)->attr, true,
82 					(*bin_attr)->attr.mode, NULL);
83 			if (error)
84 				break;
85 		}
86 		if (error)
87 			remove_files(parent, grp);
88 	}
89 exit:
90 	return error;
91 }
92 
93 
94 static int internal_create_group(struct kobject *kobj, int update,
95 				 const struct attribute_group *grp)
96 {
97 	struct kernfs_node *kn;
98 	int error;
99 
100 	BUG_ON(!kobj || (!update && !kobj->sd));
101 
102 	/* Updates may happen before the object has been instantiated */
103 	if (unlikely(update && !kobj->sd))
104 		return -EINVAL;
105 	if (!grp->attrs && !grp->bin_attrs) {
106 		WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
107 			kobj->name, grp->name ?: "");
108 		return -EINVAL;
109 	}
110 	if (grp->name) {
111 		kn = kernfs_create_dir(kobj->sd, grp->name,
112 				       S_IRWXU | S_IRUGO | S_IXUGO, kobj);
113 		if (IS_ERR(kn)) {
114 			if (PTR_ERR(kn) == -EEXIST)
115 				sysfs_warn_dup(kobj->sd, grp->name);
116 			return PTR_ERR(kn);
117 		}
118 	} else
119 		kn = kobj->sd;
120 	kernfs_get(kn);
121 	error = create_files(kn, kobj, grp, update);
122 	if (error) {
123 		if (grp->name)
124 			kernfs_remove(kn);
125 	}
126 	kernfs_put(kn);
127 	return error;
128 }
129 
130 /**
131  * sysfs_create_group - given a directory kobject, create an attribute group
132  * @kobj:	The kobject to create the group on
133  * @grp:	The attribute group to create
134  *
135  * This function creates a group for the first time.  It will explicitly
136  * warn and error if any of the attribute files being created already exist.
137  *
138  * Returns 0 on success or error.
139  */
140 int sysfs_create_group(struct kobject *kobj,
141 		       const struct attribute_group *grp)
142 {
143 	return internal_create_group(kobj, 0, grp);
144 }
145 EXPORT_SYMBOL_GPL(sysfs_create_group);
146 
147 /**
148  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
149  * @kobj:	The kobject to create the group on
150  * @groups:	The attribute groups to create, NULL terminated
151  *
152  * This function creates a bunch of attribute groups.  If an error occurs when
153  * creating a group, all previously created groups will be removed, unwinding
154  * everything back to the original state when this function was called.
155  * It will explicitly warn and error if any of the attribute files being
156  * created already exist.
157  *
158  * Returns 0 on success or error code from sysfs_create_group on error.
159  */
160 int sysfs_create_groups(struct kobject *kobj,
161 			const struct attribute_group **groups)
162 {
163 	int error = 0;
164 	int i;
165 
166 	if (!groups)
167 		return 0;
168 
169 	for (i = 0; groups[i]; i++) {
170 		error = sysfs_create_group(kobj, groups[i]);
171 		if (error) {
172 			while (--i >= 0)
173 				sysfs_remove_group(kobj, groups[i]);
174 			break;
175 		}
176 	}
177 	return error;
178 }
179 EXPORT_SYMBOL_GPL(sysfs_create_groups);
180 
181 /**
182  * sysfs_update_group - given a directory kobject, update an attribute group
183  * @kobj:	The kobject to update the group on
184  * @grp:	The attribute group to update
185  *
186  * This function updates an attribute group.  Unlike
187  * sysfs_create_group(), it will explicitly not warn or error if any
188  * of the attribute files being created already exist.  Furthermore,
189  * if the visibility of the files has changed through the is_visible()
190  * callback, it will update the permissions and add or remove the
191  * relevant files.
192  *
193  * The primary use for this function is to call it after making a change
194  * that affects group visibility.
195  *
196  * Returns 0 on success or error.
197  */
198 int sysfs_update_group(struct kobject *kobj,
199 		       const struct attribute_group *grp)
200 {
201 	return internal_create_group(kobj, 1, grp);
202 }
203 EXPORT_SYMBOL_GPL(sysfs_update_group);
204 
205 /**
206  * sysfs_remove_group: remove a group from a kobject
207  * @kobj:	kobject to remove the group from
208  * @grp:	group to remove
209  *
210  * This function removes a group of attributes from a kobject.  The attributes
211  * previously have to have been created for this group, otherwise it will fail.
212  */
213 void sysfs_remove_group(struct kobject *kobj,
214 			const struct attribute_group *grp)
215 {
216 	struct kernfs_node *parent = kobj->sd;
217 	struct kernfs_node *kn;
218 
219 	if (grp->name) {
220 		kn = kernfs_find_and_get(parent, grp->name);
221 		if (!kn) {
222 			WARN(!kn, KERN_WARNING
223 			     "sysfs group %p not found for kobject '%s'\n",
224 			     grp, kobject_name(kobj));
225 			return;
226 		}
227 	} else {
228 		kn = parent;
229 		kernfs_get(kn);
230 	}
231 
232 	remove_files(kn, grp);
233 	if (grp->name)
234 		kernfs_remove(kn);
235 
236 	kernfs_put(kn);
237 }
238 EXPORT_SYMBOL_GPL(sysfs_remove_group);
239 
240 /**
241  * sysfs_remove_groups - remove a list of groups
242  *
243  * @kobj:	The kobject for the groups to be removed from
244  * @groups:	NULL terminated list of groups to be removed
245  *
246  * If groups is not NULL, remove the specified groups from the kobject.
247  */
248 void sysfs_remove_groups(struct kobject *kobj,
249 			 const struct attribute_group **groups)
250 {
251 	int i;
252 
253 	if (!groups)
254 		return;
255 	for (i = 0; groups[i]; i++)
256 		sysfs_remove_group(kobj, groups[i]);
257 }
258 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
259 
260 /**
261  * sysfs_merge_group - merge files into a pre-existing attribute group.
262  * @kobj:	The kobject containing the group.
263  * @grp:	The files to create and the attribute group they belong to.
264  *
265  * This function returns an error if the group doesn't exist or any of the
266  * files already exist in that group, in which case none of the new files
267  * are created.
268  */
269 int sysfs_merge_group(struct kobject *kobj,
270 		       const struct attribute_group *grp)
271 {
272 	struct kernfs_node *parent;
273 	int error = 0;
274 	struct attribute *const *attr;
275 	int i;
276 
277 	parent = kernfs_find_and_get(kobj->sd, grp->name);
278 	if (!parent)
279 		return -ENOENT;
280 
281 	for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
282 		error = sysfs_add_file(parent, *attr, false);
283 	if (error) {
284 		while (--i >= 0)
285 			kernfs_remove_by_name(parent, (*--attr)->name);
286 	}
287 	kernfs_put(parent);
288 
289 	return error;
290 }
291 EXPORT_SYMBOL_GPL(sysfs_merge_group);
292 
293 /**
294  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
295  * @kobj:	The kobject containing the group.
296  * @grp:	The files to remove and the attribute group they belong to.
297  */
298 void sysfs_unmerge_group(struct kobject *kobj,
299 		       const struct attribute_group *grp)
300 {
301 	struct kernfs_node *parent;
302 	struct attribute *const *attr;
303 
304 	parent = kernfs_find_and_get(kobj->sd, grp->name);
305 	if (parent) {
306 		for (attr = grp->attrs; *attr; ++attr)
307 			kernfs_remove_by_name(parent, (*attr)->name);
308 		kernfs_put(parent);
309 	}
310 }
311 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
312 
313 /**
314  * sysfs_add_link_to_group - add a symlink to an attribute group.
315  * @kobj:	The kobject containing the group.
316  * @group_name:	The name of the group.
317  * @target:	The target kobject of the symlink to create.
318  * @link_name:	The name of the symlink to create.
319  */
320 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
321 			    struct kobject *target, const char *link_name)
322 {
323 	struct kernfs_node *parent;
324 	int error = 0;
325 
326 	parent = kernfs_find_and_get(kobj->sd, group_name);
327 	if (!parent)
328 		return -ENOENT;
329 
330 	error = sysfs_create_link_sd(parent, target, link_name);
331 	kernfs_put(parent);
332 
333 	return error;
334 }
335 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
336 
337 /**
338  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
339  * @kobj:	The kobject containing the group.
340  * @group_name:	The name of the group.
341  * @link_name:	The name of the symlink to remove.
342  */
343 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
344 				  const char *link_name)
345 {
346 	struct kernfs_node *parent;
347 
348 	parent = kernfs_find_and_get(kobj->sd, group_name);
349 	if (parent) {
350 		kernfs_remove_by_name(parent, link_name);
351 		kernfs_put(parent);
352 	}
353 }
354 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
355