Line data Source code
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 41 : 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 41 : if (grp->attrs)
27 102 : for (attr = grp->attrs; *attr; attr++)
28 204 : kernfs_remove_by_name(parent, (*attr)->name);
29 41 : if (grp->bin_attrs)
30 0 : for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
31 0 : kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
32 41 : }
33 :
34 674 : 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 674 : int error = 0, i;
41 :
42 674 : if (grp->attrs) {
43 1594 : for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
44 1594 : 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 1594 : if (update)
52 0 : kernfs_remove_by_name(parent, (*attr)->name);
53 1594 : if (grp->is_visible) {
54 34 : mode = grp->is_visible(kobj, *attr, i);
55 34 : if (!mode)
56 9 : continue;
57 : }
58 :
59 1585 : WARN(mode & ~(SYSFS_PREALLOC | 0664),
60 : "Attribute %s: Invalid permissions 0%o\n",
61 : (*attr)->name, mode);
62 :
63 1585 : mode &= SYSFS_PREALLOC | 0664;
64 1585 : error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,
65 : gid, NULL);
66 1585 : if (unlikely(error))
67 : break;
68 : }
69 674 : if (error) {
70 0 : remove_files(parent, grp);
71 0 : goto exit;
72 : }
73 : }
74 :
75 674 : if (grp->bin_attrs) {
76 8 : for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
77 8 : umode_t mode = (*bin_attr)->attr.mode;
78 :
79 8 : if (update)
80 0 : kernfs_remove_by_name(parent,
81 : (*bin_attr)->attr.name);
82 8 : if (grp->is_bin_visible) {
83 0 : mode = grp->is_bin_visible(kobj, *bin_attr, i);
84 0 : if (!mode)
85 0 : continue;
86 : }
87 :
88 8 : WARN(mode & ~(SYSFS_PREALLOC | 0664),
89 : "Attribute %s: Invalid permissions 0%o\n",
90 : (*bin_attr)->attr.name, mode);
91 :
92 8 : mode &= SYSFS_PREALLOC | 0664;
93 8 : error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,
94 : mode, uid, gid,
95 : NULL);
96 8 : if (error)
97 : break;
98 : }
99 1 : if (error)
100 0 : remove_files(parent, grp);
101 : }
102 : exit:
103 674 : return error;
104 : }
105 :
106 :
107 674 : 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 674 : if (WARN_ON(!kobj || (!update && !kobj->sd)))
116 : return -EINVAL;
117 :
118 : /* Updates may happen before the object has been instantiated */
119 674 : if (unlikely(update && !kobj->sd))
120 : return -EINVAL;
121 :
122 674 : 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 674 : kobject_get_ownership(kobj, &uid, &gid);
129 674 : if (grp->name) {
130 593 : if (update) {
131 0 : kn = kernfs_find_and_get(kobj->sd, grp->name);
132 0 : if (!kn) {
133 0 : pr_warn("Can't update unknown attr grp name: %s/%s\n",
134 : kobj->name, grp->name);
135 0 : return -EINVAL;
136 : }
137 : } else {
138 593 : kn = kernfs_create_dir_ns(kobj->sd, grp->name,
139 : S_IRWXU | S_IRUGO | S_IXUGO,
140 : uid, gid, kobj, NULL);
141 593 : if (IS_ERR(kn)) {
142 0 : if (PTR_ERR(kn) == -EEXIST)
143 0 : sysfs_warn_dup(kobj->sd, grp->name);
144 0 : return PTR_ERR(kn);
145 : }
146 : }
147 : } else {
148 81 : kn = kobj->sd;
149 : }
150 :
151 674 : kernfs_get(kn);
152 674 : error = create_files(kn, kobj, uid, gid, grp, update);
153 674 : if (error) {
154 0 : if (grp->name)
155 0 : kernfs_remove(kn);
156 : }
157 674 : kernfs_put(kn);
158 :
159 674 : if (grp->name && update)
160 0 : 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 648 : int sysfs_create_group(struct kobject *kobj,
176 : const struct attribute_group *grp)
177 : {
178 648 : return internal_create_group(kobj, 0, grp);
179 : }
180 : EXPORT_SYMBOL_GPL(sysfs_create_group);
181 :
182 1848 : static int internal_create_groups(struct kobject *kobj, int update,
183 : const struct attribute_group **groups)
184 : {
185 1848 : int error = 0;
186 : int i;
187 :
188 1848 : if (!groups)
189 : return 0;
190 :
191 26 : for (i = 0; groups[i]; i++) {
192 26 : error = internal_create_group(kobj, update, groups[i]);
193 26 : if (error) {
194 0 : while (--i >= 0)
195 0 : 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 1848 : int sysfs_create_groups(struct kobject *kobj,
216 : const struct attribute_group **groups)
217 : {
218 1848 : 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 0 : int sysfs_update_groups(struct kobject *kobj,
234 : const struct attribute_group **groups)
235 : {
236 0 : 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 0 : int sysfs_update_group(struct kobject *kobj,
259 : const struct attribute_group *grp)
260 : {
261 0 : 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 41 : void sysfs_remove_group(struct kobject *kobj,
274 : const struct attribute_group *grp)
275 : {
276 41 : struct kernfs_node *parent = kobj->sd;
277 : struct kernfs_node *kn;
278 :
279 41 : if (grp->name) {
280 72 : kn = kernfs_find_and_get(parent, grp->name);
281 36 : if (!kn) {
282 0 : WARN(!kn, KERN_WARNING
283 : "sysfs group '%s' not found for kobject '%s'\n",
284 : grp->name, kobject_name(kobj));
285 0 : return;
286 : }
287 : } else {
288 5 : kn = parent;
289 5 : kernfs_get(kn);
290 : }
291 :
292 41 : remove_files(kn, grp);
293 41 : if (grp->name)
294 36 : kernfs_remove(kn);
295 :
296 41 : 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 38 : void sysfs_remove_groups(struct kobject *kobj,
309 : const struct attribute_group **groups)
310 : {
311 : int i;
312 :
313 38 : if (!groups)
314 : return;
315 5 : for (i = 0; groups[i]; i++)
316 5 : 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 541 : 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 541 : int error = 0;
336 : struct attribute *const *attr;
337 : int i;
338 :
339 1082 : parent = kernfs_find_and_get(kobj->sd, grp->name);
340 541 : if (!parent)
341 : return -ENOENT;
342 :
343 541 : kobject_get_ownership(kobj, &uid, &gid);
344 :
345 3242 : for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
346 2701 : error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
347 : uid, gid, NULL);
348 541 : if (error) {
349 0 : while (--i >= 0)
350 0 : kernfs_remove_by_name(parent, (*--attr)->name);
351 : }
352 541 : kernfs_put(parent);
353 :
354 541 : 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 25 : 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 50 : parent = kernfs_find_and_get(kobj->sd, grp->name);
370 25 : if (parent) {
371 110 : for (attr = grp->attrs; *attr; ++attr)
372 170 : kernfs_remove_by_name(parent, (*attr)->name);
373 25 : kernfs_put(parent);
374 : }
375 25 : }
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 0 : 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 0 : int error = 0;
390 :
391 0 : parent = kernfs_find_and_get(kobj->sd, group_name);
392 0 : if (!parent)
393 : return -ENOENT;
394 :
395 0 : error = sysfs_create_link_sd(parent, target, link_name);
396 0 : kernfs_put(parent);
397 :
398 0 : 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 0 : 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 0 : parent = kernfs_find_and_get(kobj->sd, group_name);
414 0 : if (parent) {
415 0 : kernfs_remove_by_name(parent, link_name);
416 0 : kernfs_put(parent);
417 : }
418 0 : }
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 0 : 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 0 : spin_lock(&sysfs_symlink_target_lock);
445 0 : target = target_kobj->sd;
446 0 : if (target)
447 0 : kernfs_get(target);
448 0 : spin_unlock(&sysfs_symlink_target_lock);
449 0 : if (!target)
450 : return -ENOENT;
451 :
452 0 : entry = kernfs_find_and_get(target, target_name);
453 0 : if (!entry) {
454 0 : kernfs_put(target);
455 0 : return -ENOENT;
456 : }
457 :
458 0 : if (!symlink_name)
459 0 : symlink_name = target_name;
460 :
461 0 : link = kernfs_create_link(kobj->sd, symlink_name, entry);
462 0 : if (PTR_ERR(link) == -EEXIST)
463 0 : sysfs_warn_dup(kobj->sd, symlink_name);
464 :
465 0 : kernfs_put(entry);
466 0 : kernfs_put(target);
467 : return PTR_ERR_OR_ZERO(link);
468 : }
469 : EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
470 :
471 0 : 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 0 : if (grp->attrs) {
479 : struct attribute *const *attr;
480 :
481 0 : for (attr = grp->attrs; *attr; attr++) {
482 0 : kn = kernfs_find_and_get(grp_kn, (*attr)->name);
483 0 : if (!kn)
484 : return -ENOENT;
485 :
486 0 : error = kernfs_setattr(kn, newattrs);
487 0 : kernfs_put(kn);
488 0 : if (error)
489 : return error;
490 : }
491 : }
492 :
493 0 : if (grp->bin_attrs) {
494 : struct bin_attribute *const *bin_attr;
495 :
496 0 : for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
497 0 : kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
498 0 : if (!kn)
499 : return -ENOENT;
500 :
501 0 : error = kernfs_setattr(kn, newattrs);
502 0 : kernfs_put(kn);
503 0 : 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 0 : 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 0 : struct iattr newattrs = {
527 : .ia_valid = ATTR_UID | ATTR_GID,
528 : .ia_uid = kuid,
529 : .ia_gid = kgid,
530 : };
531 :
532 0 : if (!kobj->state_in_sysfs)
533 : return -EINVAL;
534 :
535 0 : if (grp->name) {
536 0 : grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
537 : } else {
538 0 : kernfs_get(kobj->sd);
539 0 : grp_kn = kobj->sd;
540 : }
541 0 : if (!grp_kn)
542 : return -ENOENT;
543 :
544 0 : error = kernfs_setattr(grp_kn, &newattrs);
545 0 : if (!error)
546 0 : error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
547 :
548 0 : kernfs_put(grp_kn);
549 :
550 0 : 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 0 : int sysfs_groups_change_owner(struct kobject *kobj,
564 : const struct attribute_group **groups,
565 : kuid_t kuid, kgid_t kgid)
566 : {
567 0 : int error = 0, i;
568 :
569 0 : if (!kobj->state_in_sysfs)
570 : return -EINVAL;
571 :
572 0 : if (!groups)
573 : return 0;
574 :
575 0 : for (i = 0; groups[i]; i++) {
576 0 : error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
577 0 : if (error)
578 : break;
579 : }
580 :
581 : return error;
582 : }
583 : EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);
|