Merge tag 'probes-fixes-v6.5-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel...
[platform/kernel/linux-starfive.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, mode, uid,
65                                                        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_bin_file_mode_ns(parent, *bin_attr,
94                                                            mode, uid, gid,
95                                                            NULL);
96                         if (error)
97                                 break;
98                 }
99                 if (error)
100                         remove_files(parent, grp);
101         }
102 exit:
103         return error;
104 }
105
106
107 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         if (WARN_ON(!kobj || (!update && !kobj->sd)))
116                 return -EINVAL;
117
118         /* Updates may happen before the object has been instantiated */
119         if (unlikely(update && !kobj->sd))
120                 return -EINVAL;
121
122         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         kobject_get_ownership(kobj, &uid, &gid);
129         if (grp->name) {
130                 if (update) {
131                         kn = kernfs_find_and_get(kobj->sd, grp->name);
132                         if (!kn) {
133                                 pr_warn("Can't update unknown attr grp name: %s/%s\n",
134                                         kobj->name, grp->name);
135                                 return -EINVAL;
136                         }
137                 } else {
138                         kn = kernfs_create_dir_ns(kobj->sd, grp->name,
139                                                   S_IRWXU | S_IRUGO | S_IXUGO,
140                                                   uid, gid, kobj, NULL);
141                         if (IS_ERR(kn)) {
142                                 if (PTR_ERR(kn) == -EEXIST)
143                                         sysfs_warn_dup(kobj->sd, grp->name);
144                                 return PTR_ERR(kn);
145                         }
146                 }
147         } else {
148                 kn = kobj->sd;
149         }
150
151         kernfs_get(kn);
152         error = create_files(kn, kobj, uid, gid, grp, update);
153         if (error) {
154                 if (grp->name)
155                         kernfs_remove(kn);
156         }
157         kernfs_put(kn);
158
159         if (grp->name && update)
160                 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 int sysfs_create_group(struct kobject *kobj,
176                        const struct attribute_group *grp)
177 {
178         return internal_create_group(kobj, 0, grp);
179 }
180 EXPORT_SYMBOL_GPL(sysfs_create_group);
181
182 static int internal_create_groups(struct kobject *kobj, int update,
183                                   const struct attribute_group **groups)
184 {
185         int error = 0;
186         int i;
187
188         if (!groups)
189                 return 0;
190
191         for (i = 0; groups[i]; i++) {
192                 error = internal_create_group(kobj, update, groups[i]);
193                 if (error) {
194                         while (--i >= 0)
195                                 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 int sysfs_create_groups(struct kobject *kobj,
216                         const struct attribute_group **groups)
217 {
218         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 int sysfs_update_groups(struct kobject *kobj,
234                         const struct attribute_group **groups)
235 {
236         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 int sysfs_update_group(struct kobject *kobj,
259                        const struct attribute_group *grp)
260 {
261         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 void sysfs_remove_group(struct kobject *kobj,
274                         const struct attribute_group *grp)
275 {
276         struct kernfs_node *parent = kobj->sd;
277         struct kernfs_node *kn;
278
279         if (grp->name) {
280                 kn = kernfs_find_and_get(parent, grp->name);
281                 if (!kn) {
282                         WARN(!kn, KERN_WARNING
283                              "sysfs group '%s' not found for kobject '%s'\n",
284                              grp->name, kobject_name(kobj));
285                         return;
286                 }
287         } else {
288                 kn = parent;
289                 kernfs_get(kn);
290         }
291
292         remove_files(kn, grp);
293         if (grp->name)
294                 kernfs_remove(kn);
295
296         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 void sysfs_remove_groups(struct kobject *kobj,
309                          const struct attribute_group **groups)
310 {
311         int i;
312
313         if (!groups)
314                 return;
315         for (i = 0; groups[i]; i++)
316                 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 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         int error = 0;
336         struct attribute *const *attr;
337         int i;
338
339         parent = kernfs_find_and_get(kobj->sd, grp->name);
340         if (!parent)
341                 return -ENOENT;
342
343         kobject_get_ownership(kobj, &uid, &gid);
344
345         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
346                 error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
347                                                uid, gid, NULL);
348         if (error) {
349                 while (--i >= 0)
350                         kernfs_remove_by_name(parent, (*--attr)->name);
351         }
352         kernfs_put(parent);
353
354         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 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         parent = kernfs_find_and_get(kobj->sd, grp->name);
370         if (parent) {
371                 for (attr = grp->attrs; *attr; ++attr)
372                         kernfs_remove_by_name(parent, (*attr)->name);
373                 kernfs_put(parent);
374         }
375 }
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 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         int error = 0;
390
391         parent = kernfs_find_and_get(kobj->sd, group_name);
392         if (!parent)
393                 return -ENOENT;
394
395         error = sysfs_create_link_sd(parent, target, link_name);
396         kernfs_put(parent);
397
398         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 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         parent = kernfs_find_and_get(kobj->sd, group_name);
414         if (parent) {
415                 kernfs_remove_by_name(parent, link_name);
416                 kernfs_put(parent);
417         }
418 }
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 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         spin_lock(&sysfs_symlink_target_lock);
445         target = target_kobj->sd;
446         if (target)
447                 kernfs_get(target);
448         spin_unlock(&sysfs_symlink_target_lock);
449         if (!target)
450                 return -ENOENT;
451
452         entry = kernfs_find_and_get(target, target_name);
453         if (!entry) {
454                 kernfs_put(target);
455                 return -ENOENT;
456         }
457
458         if (!symlink_name)
459                 symlink_name = target_name;
460
461         link = kernfs_create_link(kobj->sd, symlink_name, entry);
462         if (PTR_ERR(link) == -EEXIST)
463                 sysfs_warn_dup(kobj->sd, symlink_name);
464
465         kernfs_put(entry);
466         kernfs_put(target);
467         return PTR_ERR_OR_ZERO(link);
468 }
469 EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
470
471 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         if (grp->attrs) {
479                 struct attribute *const *attr;
480
481                 for (attr = grp->attrs; *attr; attr++) {
482                         kn = kernfs_find_and_get(grp_kn, (*attr)->name);
483                         if (!kn)
484                                 return -ENOENT;
485
486                         error = kernfs_setattr(kn, newattrs);
487                         kernfs_put(kn);
488                         if (error)
489                                 return error;
490                 }
491         }
492
493         if (grp->bin_attrs) {
494                 struct bin_attribute *const *bin_attr;
495
496                 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
497                         kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
498                         if (!kn)
499                                 return -ENOENT;
500
501                         error = kernfs_setattr(kn, newattrs);
502                         kernfs_put(kn);
503                         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 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         struct iattr newattrs = {
527                 .ia_valid = ATTR_UID | ATTR_GID,
528                 .ia_uid = kuid,
529                 .ia_gid = kgid,
530         };
531
532         if (!kobj->state_in_sysfs)
533                 return -EINVAL;
534
535         if (grp->name) {
536                 grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
537         } else {
538                 kernfs_get(kobj->sd);
539                 grp_kn = kobj->sd;
540         }
541         if (!grp_kn)
542                 return -ENOENT;
543
544         error = kernfs_setattr(grp_kn, &newattrs);
545         if (!error)
546                 error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
547
548         kernfs_put(grp_kn);
549
550         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 int sysfs_groups_change_owner(struct kobject *kobj,
564                               const struct attribute_group **groups,
565                               kuid_t kuid, kgid_t kgid)
566 {
567         int error = 0, i;
568
569         if (!kobj->state_in_sysfs)
570                 return -EINVAL;
571
572         if (!groups)
573                 return 0;
574
575         for (i = 0; groups[i]; i++) {
576                 error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
577                 if (error)
578                         break;
579         }
580
581         return error;
582 }
583 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);