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