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 && !update)
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 * const * groups)219 static int internal_create_groups(struct kobject *kobj, int update,
220 const struct attribute_group *const *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 * const * groups)252 int sysfs_create_groups(struct kobject *kobj,
253 const struct attribute_group *const *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 * const * groups)270 int sysfs_update_groups(struct kobject *kobj,
271 const struct attribute_group *const *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 * const * groups)344 void sysfs_remove_groups(struct kobject *kobj,
345 const struct attribute_group *const *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 || grp->is_visible_const) {
521 if (grp->is_visible)
522 mode = grp->is_visible(kobj, *attr, i);
523 else
524 mode = grp->is_visible_const(kobj, *attr, i);
525 if (mode & SYSFS_GROUP_INVISIBLE)
526 break;
527 if (!mode)
528 continue;
529 }
530 kn = kernfs_find_and_get(grp_kn, (*attr)->name);
531 if (!kn)
532 return -ENOENT;
533
534 error = kernfs_setattr(kn, newattrs);
535 kernfs_put(kn);
536 if (error)
537 return error;
538 }
539 }
540
541 if (grp->bin_attrs) {
542 const struct bin_attribute *const *bin_attr;
543
544 for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
545 if (grp->is_bin_visible) {
546 mode = grp->is_bin_visible(kobj, *bin_attr, i);
547 if (mode & SYSFS_GROUP_INVISIBLE)
548 break;
549 if (!mode)
550 continue;
551 }
552 kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
553 if (!kn)
554 return -ENOENT;
555
556 error = kernfs_setattr(kn, newattrs);
557 kernfs_put(kn);
558 if (error)
559 return error;
560 }
561 }
562
563 return 0;
564 }
565
566 /**
567 * sysfs_group_change_owner - change owner of an attribute group.
568 * @kobj: The kobject containing the group.
569 * @grp: The attribute group.
570 * @kuid: new owner's kuid
571 * @kgid: new owner's kgid
572 *
573 * Returns 0 on success or error code on failure.
574 */
sysfs_group_change_owner(struct kobject * kobj,const struct attribute_group * grp,kuid_t kuid,kgid_t kgid)575 int sysfs_group_change_owner(struct kobject *kobj,
576 const struct attribute_group *grp, kuid_t kuid,
577 kgid_t kgid)
578 {
579 struct kernfs_node *grp_kn;
580 int error;
581 struct iattr newattrs = {
582 .ia_valid = ATTR_UID | ATTR_GID,
583 .ia_uid = kuid,
584 .ia_gid = kgid,
585 };
586
587 if (!kobj->state_in_sysfs)
588 return -EINVAL;
589
590 if (grp->name) {
591 grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
592 } else {
593 kernfs_get(kobj->sd);
594 grp_kn = kobj->sd;
595 }
596 if (!grp_kn)
597 return -ENOENT;
598
599 error = kernfs_setattr(grp_kn, &newattrs);
600 if (!error)
601 error = sysfs_group_attrs_change_owner(kobj, grp_kn, grp, &newattrs);
602
603 kernfs_put(grp_kn);
604
605 return error;
606 }
607 EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
608
609 /**
610 * sysfs_groups_change_owner - change owner of a set of attribute groups.
611 * @kobj: The kobject containing the groups.
612 * @groups: The attribute groups.
613 * @kuid: new owner's kuid
614 * @kgid: new owner's kgid
615 *
616 * Returns 0 on success or error code on failure.
617 */
sysfs_groups_change_owner(struct kobject * kobj,const struct attribute_group * const * groups,kuid_t kuid,kgid_t kgid)618 int sysfs_groups_change_owner(struct kobject *kobj,
619 const struct attribute_group *const *groups,
620 kuid_t kuid, kgid_t kgid)
621 {
622 int error = 0, i;
623
624 if (!kobj->state_in_sysfs)
625 return -EINVAL;
626
627 if (!groups)
628 return 0;
629
630 for (i = 0; groups[i]; i++) {
631 error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
632 if (error)
633 break;
634 }
635
636 return error;
637 }
638 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);
639