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