xref: /openbmc/linux/fs/sysfs/group.c (revision 61f4d204)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
4  *
5  * Copyright (c) 2003 Patrick Mochel
6  * Copyright (c) 2003 Open Source Development Lab
7  * Copyright (c) 2013 Greg Kroah-Hartman
8  * Copyright (c) 2013 The Linux Foundation
9  */
10 
11 #include <linux/kobject.h>
12 #include <linux/module.h>
13 #include <linux/dcache.h>
14 #include <linux/namei.h>
15 #include <linux/err.h>
16 #include <linux/fs.h>
17 #include "sysfs.h"
18 
19 
20 static void remove_files(struct kernfs_node *parent,
21 			 const struct attribute_group *grp)
22 {
23 	struct attribute *const *attr;
24 	struct bin_attribute *const *bin_attr;
25 
26 	if (grp->attrs)
27 		for (attr = grp->attrs; *attr; attr++)
28 			kernfs_remove_by_name(parent, (*attr)->name);
29 	if (grp->bin_attrs)
30 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
31 			kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
32 }
33 
34 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
35 			kuid_t uid, kgid_t gid,
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, mode, uid,
65 						       gid, 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 (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
77 			umode_t mode = (*bin_attr)->attr.mode;
78 
79 			if (update)
80 				kernfs_remove_by_name(parent,
81 						(*bin_attr)->attr.name);
82 			if (grp->is_bin_visible) {
83 				mode = grp->is_bin_visible(kobj, *bin_attr, i);
84 				if (!mode)
85 					continue;
86 			}
87 
88 			WARN(mode & ~(SYSFS_PREALLOC | 0664),
89 			     "Attribute %s: Invalid permissions 0%o\n",
90 			     (*bin_attr)->attr.name, mode);
91 
92 			mode &= SYSFS_PREALLOC | 0664;
93 			error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,
94 							   mode, uid, gid,
95 							   NULL);
96 			if (error)
97 				break;
98 		}
99 		if (error)
100 			remove_files(parent, grp);
101 	}
102 exit:
103 	return error;
104 }
105 
106 
107 static int internal_create_group(struct kobject *kobj, int update,
108 				 const struct attribute_group *grp)
109 {
110 	struct kernfs_node *kn;
111 	kuid_t uid;
112 	kgid_t gid;
113 	int error;
114 
115 	if (WARN_ON(!kobj || (!update && !kobj->sd)))
116 		return -EINVAL;
117 
118 	/* Updates may happen before the object has been instantiated */
119 	if (unlikely(update && !kobj->sd))
120 		return -EINVAL;
121 
122 	if (!grp->attrs && !grp->bin_attrs) {
123 		pr_debug("sysfs: (bin_)attrs not set by subsystem for group: %s/%s, skipping\n",
124 			 kobj->name, grp->name ?: "");
125 		return 0;
126 	}
127 
128 	kobject_get_ownership(kobj, &uid, &gid);
129 	if (grp->name) {
130 		if (update) {
131 			kn = kernfs_find_and_get(kobj->sd, grp->name);
132 			if (!kn) {
133 				pr_warn("Can't update unknown attr grp name: %s/%s\n",
134 					kobj->name, grp->name);
135 				return -EINVAL;
136 			}
137 		} else {
138 			kn = kernfs_create_dir_ns(kobj->sd, grp->name,
139 						  S_IRWXU | S_IRUGO | S_IXUGO,
140 						  uid, gid, kobj, NULL);
141 			if (IS_ERR(kn)) {
142 				if (PTR_ERR(kn) == -EEXIST)
143 					sysfs_warn_dup(kobj->sd, grp->name);
144 				return PTR_ERR(kn);
145 			}
146 		}
147 	} else {
148 		kn = kobj->sd;
149 	}
150 
151 	kernfs_get(kn);
152 	error = create_files(kn, kobj, uid, gid, grp, update);
153 	if (error) {
154 		if (grp->name)
155 			kernfs_remove(kn);
156 	}
157 	kernfs_put(kn);
158 
159 	if (grp->name && update)
160 		kernfs_put(kn);
161 
162 	return error;
163 }
164 
165 /**
166  * sysfs_create_group - given a directory kobject, create an attribute group
167  * @kobj:	The kobject to create the group on
168  * @grp:	The attribute group to create
169  *
170  * This function creates a group for the first time.  It will explicitly
171  * warn and error if any of the attribute files being created already exist.
172  *
173  * Returns 0 on success or error code on failure.
174  */
175 int sysfs_create_group(struct kobject *kobj,
176 		       const struct attribute_group *grp)
177 {
178 	return internal_create_group(kobj, 0, grp);
179 }
180 EXPORT_SYMBOL_GPL(sysfs_create_group);
181 
182 static int internal_create_groups(struct kobject *kobj, int update,
183 				  const struct attribute_group **groups)
184 {
185 	int error = 0;
186 	int i;
187 
188 	if (!groups)
189 		return 0;
190 
191 	for (i = 0; groups[i]; i++) {
192 		error = internal_create_group(kobj, update, groups[i]);
193 		if (error) {
194 			while (--i >= 0)
195 				sysfs_remove_group(kobj, groups[i]);
196 			break;
197 		}
198 	}
199 	return error;
200 }
201 
202 /**
203  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
204  * @kobj:	The kobject to create the group on
205  * @groups:	The attribute groups to create, NULL terminated
206  *
207  * This function creates a bunch of attribute groups.  If an error occurs when
208  * creating a group, all previously created groups will be removed, unwinding
209  * everything back to the original state when this function was called.
210  * It will explicitly warn and error if any of the attribute files being
211  * created already exist.
212  *
213  * Returns 0 on success or error code from sysfs_create_group on failure.
214  */
215 int sysfs_create_groups(struct kobject *kobj,
216 			const struct attribute_group **groups)
217 {
218 	return internal_create_groups(kobj, 0, groups);
219 }
220 EXPORT_SYMBOL_GPL(sysfs_create_groups);
221 
222 /**
223  * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups
224  * @kobj:	The kobject to update the group on
225  * @groups:	The attribute groups to update, NULL terminated
226  *
227  * This function update a bunch of attribute groups.  If an error occurs when
228  * updating a group, all previously updated groups will be removed together
229  * with already existing (not updated) attributes.
230  *
231  * Returns 0 on success or error code from sysfs_update_group on failure.
232  */
233 int sysfs_update_groups(struct kobject *kobj,
234 			const struct attribute_group **groups)
235 {
236 	return internal_create_groups(kobj, 1, groups);
237 }
238 EXPORT_SYMBOL_GPL(sysfs_update_groups);
239 
240 /**
241  * sysfs_update_group - given a directory kobject, update an attribute group
242  * @kobj:	The kobject to update the group on
243  * @grp:	The attribute group to update
244  *
245  * This function updates an attribute group.  Unlike
246  * sysfs_create_group(), it will explicitly not warn or error if any
247  * of the attribute files being created already exist.  Furthermore,
248  * if the visibility of the files has changed through the is_visible()
249  * callback, it will update the permissions and add or remove the
250  * relevant files. Changing a group's name (subdirectory name under
251  * kobj's directory in sysfs) is not allowed.
252  *
253  * The primary use for this function is to call it after making a change
254  * that affects group visibility.
255  *
256  * Returns 0 on success or error code on failure.
257  */
258 int sysfs_update_group(struct kobject *kobj,
259 		       const struct attribute_group *grp)
260 {
261 	return internal_create_group(kobj, 1, grp);
262 }
263 EXPORT_SYMBOL_GPL(sysfs_update_group);
264 
265 /**
266  * sysfs_remove_group: remove a group from a kobject
267  * @kobj:	kobject to remove the group from
268  * @grp:	group to remove
269  *
270  * This function removes a group of attributes from a kobject.  The attributes
271  * previously have to have been created for this group, otherwise it will fail.
272  */
273 void sysfs_remove_group(struct kobject *kobj,
274 			const struct attribute_group *grp)
275 {
276 	struct kernfs_node *parent = kobj->sd;
277 	struct kernfs_node *kn;
278 
279 	if (grp->name) {
280 		kn = kernfs_find_and_get(parent, grp->name);
281 		if (!kn) {
282 			WARN(!kn, KERN_WARNING
283 			     "sysfs group '%s' not found for kobject '%s'\n",
284 			     grp->name, kobject_name(kobj));
285 			return;
286 		}
287 	} else {
288 		kn = parent;
289 		kernfs_get(kn);
290 	}
291 
292 	remove_files(kn, grp);
293 	if (grp->name)
294 		kernfs_remove(kn);
295 
296 	kernfs_put(kn);
297 }
298 EXPORT_SYMBOL_GPL(sysfs_remove_group);
299 
300 /**
301  * sysfs_remove_groups - remove a list of groups
302  *
303  * @kobj:	The kobject for the groups to be removed from
304  * @groups:	NULL terminated list of groups to be removed
305  *
306  * If groups is not NULL, remove the specified groups from the kobject.
307  */
308 void sysfs_remove_groups(struct kobject *kobj,
309 			 const struct attribute_group **groups)
310 {
311 	int i;
312 
313 	if (!groups)
314 		return;
315 	for (i = 0; groups[i]; i++)
316 		sysfs_remove_group(kobj, groups[i]);
317 }
318 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
319 
320 /**
321  * sysfs_merge_group - merge files into a pre-existing attribute group.
322  * @kobj:	The kobject containing the group.
323  * @grp:	The files to create and the attribute group they belong to.
324  *
325  * This function returns an error if the group doesn't exist or any of the
326  * files already exist in that group, in which case none of the new files
327  * are created.
328  */
329 int sysfs_merge_group(struct kobject *kobj,
330 		       const struct attribute_group *grp)
331 {
332 	struct kernfs_node *parent;
333 	kuid_t uid;
334 	kgid_t gid;
335 	int error = 0;
336 	struct attribute *const *attr;
337 	int i;
338 
339 	parent = kernfs_find_and_get(kobj->sd, grp->name);
340 	if (!parent)
341 		return -ENOENT;
342 
343 	kobject_get_ownership(kobj, &uid, &gid);
344 
345 	for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
346 		error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
347 					       uid, gid, NULL);
348 	if (error) {
349 		while (--i >= 0)
350 			kernfs_remove_by_name(parent, (*--attr)->name);
351 	}
352 	kernfs_put(parent);
353 
354 	return error;
355 }
356 EXPORT_SYMBOL_GPL(sysfs_merge_group);
357 
358 /**
359  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
360  * @kobj:	The kobject containing the group.
361  * @grp:	The files to remove and the attribute group they belong to.
362  */
363 void sysfs_unmerge_group(struct kobject *kobj,
364 		       const struct attribute_group *grp)
365 {
366 	struct kernfs_node *parent;
367 	struct attribute *const *attr;
368 
369 	parent = kernfs_find_and_get(kobj->sd, grp->name);
370 	if (parent) {
371 		for (attr = grp->attrs; *attr; ++attr)
372 			kernfs_remove_by_name(parent, (*attr)->name);
373 		kernfs_put(parent);
374 	}
375 }
376 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
377 
378 /**
379  * sysfs_add_link_to_group - add a symlink to an attribute group.
380  * @kobj:	The kobject containing the group.
381  * @group_name:	The name of the group.
382  * @target:	The target kobject of the symlink to create.
383  * @link_name:	The name of the symlink to create.
384  */
385 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
386 			    struct kobject *target, const char *link_name)
387 {
388 	struct kernfs_node *parent;
389 	int error = 0;
390 
391 	parent = kernfs_find_and_get(kobj->sd, group_name);
392 	if (!parent)
393 		return -ENOENT;
394 
395 	error = sysfs_create_link_sd(parent, target, link_name);
396 	kernfs_put(parent);
397 
398 	return error;
399 }
400 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
401 
402 /**
403  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
404  * @kobj:	The kobject containing the group.
405  * @group_name:	The name of the group.
406  * @link_name:	The name of the symlink to remove.
407  */
408 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
409 				  const char *link_name)
410 {
411 	struct kernfs_node *parent;
412 
413 	parent = kernfs_find_and_get(kobj->sd, group_name);
414 	if (parent) {
415 		kernfs_remove_by_name(parent, link_name);
416 		kernfs_put(parent);
417 	}
418 }
419 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
420 
421 /**
422  * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
423  * to a group or an attribute
424  * @kobj:		The kobject containing the group.
425  * @target_kobj:	The target kobject.
426  * @target_name:	The name of the target group or attribute.
427  * @symlink_name:	The name of the symlink file (target_name will be
428  *			considered if symlink_name is NULL).
429  */
430 int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
431 					 struct kobject *target_kobj,
432 					 const char *target_name,
433 					 const char *symlink_name)
434 {
435 	struct kernfs_node *target;
436 	struct kernfs_node *entry;
437 	struct kernfs_node *link;
438 
439 	/*
440 	 * We don't own @target_kobj and it may be removed at any time.
441 	 * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
442 	 * for details.
443 	 */
444 	spin_lock(&sysfs_symlink_target_lock);
445 	target = target_kobj->sd;
446 	if (target)
447 		kernfs_get(target);
448 	spin_unlock(&sysfs_symlink_target_lock);
449 	if (!target)
450 		return -ENOENT;
451 
452 	entry = kernfs_find_and_get(target, target_name);
453 	if (!entry) {
454 		kernfs_put(target);
455 		return -ENOENT;
456 	}
457 
458 	if (!symlink_name)
459 		symlink_name = target_name;
460 
461 	link = kernfs_create_link(kobj->sd, symlink_name, entry);
462 	if (PTR_ERR(link) == -EEXIST)
463 		sysfs_warn_dup(kobj->sd, symlink_name);
464 
465 	kernfs_put(entry);
466 	kernfs_put(target);
467 	return PTR_ERR_OR_ZERO(link);
468 }
469 EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
470 
471 static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,
472 					  const struct attribute_group *grp,
473 					  struct iattr *newattrs)
474 {
475 	struct kernfs_node *kn;
476 	int error;
477 
478 	if (grp->attrs) {
479 		struct attribute *const *attr;
480 
481 		for (attr = grp->attrs; *attr; attr++) {
482 			kn = kernfs_find_and_get(grp_kn, (*attr)->name);
483 			if (!kn)
484 				return -ENOENT;
485 
486 			error = kernfs_setattr(kn, newattrs);
487 			kernfs_put(kn);
488 			if (error)
489 				return error;
490 		}
491 	}
492 
493 	if (grp->bin_attrs) {
494 		struct bin_attribute *const *bin_attr;
495 
496 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
497 			kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
498 			if (!kn)
499 				return -ENOENT;
500 
501 			error = kernfs_setattr(kn, newattrs);
502 			kernfs_put(kn);
503 			if (error)
504 				return error;
505 		}
506 	}
507 
508 	return 0;
509 }
510 
511 /**
512  * sysfs_group_change_owner - change owner of an attribute group.
513  * @kobj:	The kobject containing the group.
514  * @grp:	The attribute group.
515  * @kuid:	new owner's kuid
516  * @kgid:	new owner's kgid
517  *
518  * Returns 0 on success or error code on failure.
519  */
520 int sysfs_group_change_owner(struct kobject *kobj,
521 			     const struct attribute_group *grp, kuid_t kuid,
522 			     kgid_t kgid)
523 {
524 	struct kernfs_node *grp_kn;
525 	int error;
526 	struct iattr newattrs = {
527 		.ia_valid = ATTR_UID | ATTR_GID,
528 		.ia_uid = kuid,
529 		.ia_gid = kgid,
530 	};
531 
532 	if (!kobj->state_in_sysfs)
533 		return -EINVAL;
534 
535 	if (grp->name) {
536 		grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
537 	} else {
538 		kernfs_get(kobj->sd);
539 		grp_kn = kobj->sd;
540 	}
541 	if (!grp_kn)
542 		return -ENOENT;
543 
544 	error = kernfs_setattr(grp_kn, &newattrs);
545 	if (!error)
546 		error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
547 
548 	kernfs_put(grp_kn);
549 
550 	return error;
551 }
552 EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
553 
554 /**
555  * sysfs_groups_change_owner - change owner of a set of attribute groups.
556  * @kobj:	The kobject containing the groups.
557  * @groups:	The attribute groups.
558  * @kuid:	new owner's kuid
559  * @kgid:	new owner's kgid
560  *
561  * Returns 0 on success or error code on failure.
562  */
563 int sysfs_groups_change_owner(struct kobject *kobj,
564 			      const struct attribute_group **groups,
565 			      kuid_t kuid, kgid_t kgid)
566 {
567 	int error = 0, i;
568 
569 	if (!kobj->state_in_sysfs)
570 		return -EINVAL;
571 
572 	if (!groups)
573 		return 0;
574 
575 	for (i = 0; groups[i]; i++) {
576 		error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
577 		if (error)
578 			break;
579 	}
580 
581 	return error;
582 }
583 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);
584