Merge tag 'pci-v5.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / fs / sysfs / group.c
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 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
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
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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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, 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
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  */
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  */
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);