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