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