OSDN Git Service

MAINTAINERS: add entry for redpine wireless driver
[uclinux-h8/linux.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 "sysfs.h"
17
18
19 static void remove_files(struct kernfs_node *parent,
20                          const struct attribute_group *grp)
21 {
22         struct attribute *const *attr;
23         struct bin_attribute *const *bin_attr;
24
25         if (grp->attrs)
26                 for (attr = grp->attrs; *attr; attr++)
27                         kernfs_remove_by_name(parent, (*attr)->name);
28         if (grp->bin_attrs)
29                 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
30                         kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
31 }
32
33 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
34                         kuid_t uid, kgid_t gid,
35                         const struct attribute_group *grp, int update)
36 {
37         struct attribute *const *attr;
38         struct bin_attribute *const *bin_attr;
39         int error = 0, i;
40
41         if (grp->attrs) {
42                 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
43                         umode_t mode = (*attr)->mode;
44
45                         /*
46                          * In update mode, we're changing the permissions or
47                          * visibility.  Do this by first removing then
48                          * re-adding (if required) the file.
49                          */
50                         if (update)
51                                 kernfs_remove_by_name(parent, (*attr)->name);
52                         if (grp->is_visible) {
53                                 mode = grp->is_visible(kobj, *attr, i);
54                                 if (!mode)
55                                         continue;
56                         }
57
58                         WARN(mode & ~(SYSFS_PREALLOC | 0664),
59                              "Attribute %s: Invalid permissions 0%o\n",
60                              (*attr)->name, mode);
61
62                         mode &= SYSFS_PREALLOC | 0664;
63                         error = sysfs_add_file_mode_ns(parent, *attr, false,
64                                                        mode, uid, gid, NULL);
65                         if (unlikely(error))
66                                 break;
67                 }
68                 if (error) {
69                         remove_files(parent, grp);
70                         goto exit;
71                 }
72         }
73
74         if (grp->bin_attrs) {
75                 for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
76                         umode_t mode = (*bin_attr)->attr.mode;
77
78                         if (update)
79                                 kernfs_remove_by_name(parent,
80                                                 (*bin_attr)->attr.name);
81                         if (grp->is_bin_visible) {
82                                 mode = grp->is_bin_visible(kobj, *bin_attr, i);
83                                 if (!mode)
84                                         continue;
85                         }
86
87                         WARN(mode & ~(SYSFS_PREALLOC | 0664),
88                              "Attribute %s: Invalid permissions 0%o\n",
89                              (*bin_attr)->attr.name, mode);
90
91                         mode &= SYSFS_PREALLOC | 0664;
92                         error = sysfs_add_file_mode_ns(parent,
93                                         &(*bin_attr)->attr, true,
94                                         mode,
95                                         uid, gid, 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         BUG_ON(!kobj || (!update && !kobj->sd));
116
117         /* Updates may happen before the object has been instantiated */
118         if (unlikely(update && !kobj->sd))
119                 return -EINVAL;
120         if (!grp->attrs && !grp->bin_attrs) {
121                 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
122                         kobj->name, grp->name ?: "");
123                 return -EINVAL;
124         }
125         kobject_get_ownership(kobj, &uid, &gid);
126         if (grp->name) {
127                 if (update) {
128                         kn = kernfs_find_and_get(kobj->sd, grp->name);
129                         if (!kn) {
130                                 pr_warn("Can't update unknown attr grp name: %s/%s\n",
131                                         kobj->name, grp->name);
132                                 return -EINVAL;
133                         }
134                 } else {
135                         kn = kernfs_create_dir_ns(kobj->sd, grp->name,
136                                                   S_IRWXU | S_IRUGO | S_IXUGO,
137                                                   uid, gid, kobj, NULL);
138                         if (IS_ERR(kn)) {
139                                 if (PTR_ERR(kn) == -EEXIST)
140                                         sysfs_warn_dup(kobj->sd, grp->name);
141                                 return PTR_ERR(kn);
142                         }
143                 }
144         } else
145                 kn = kobj->sd;
146         kernfs_get(kn);
147         error = create_files(kn, kobj, uid, gid, grp, update);
148         if (error) {
149                 if (grp->name)
150                         kernfs_remove(kn);
151         }
152         kernfs_put(kn);
153
154         if (grp->name && update)
155                 kernfs_put(kn);
156
157         return error;
158 }
159
160 /**
161  * sysfs_create_group - given a directory kobject, create an attribute group
162  * @kobj:       The kobject to create the group on
163  * @grp:        The attribute group to create
164  *
165  * This function creates a group for the first time.  It will explicitly
166  * warn and error if any of the attribute files being created already exist.
167  *
168  * Returns 0 on success or error code on failure.
169  */
170 int sysfs_create_group(struct kobject *kobj,
171                        const struct attribute_group *grp)
172 {
173         return internal_create_group(kobj, 0, grp);
174 }
175 EXPORT_SYMBOL_GPL(sysfs_create_group);
176
177 /**
178  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
179  * @kobj:       The kobject to create the group on
180  * @groups:     The attribute groups to create, NULL terminated
181  *
182  * This function creates a bunch of attribute groups.  If an error occurs when
183  * creating a group, all previously created groups will be removed, unwinding
184  * everything back to the original state when this function was called.
185  * It will explicitly warn and error if any of the attribute files being
186  * created already exist.
187  *
188  * Returns 0 on success or error code from sysfs_create_group on failure.
189  */
190 int sysfs_create_groups(struct kobject *kobj,
191                         const struct attribute_group **groups)
192 {
193         int error = 0;
194         int i;
195
196         if (!groups)
197                 return 0;
198
199         for (i = 0; groups[i]; i++) {
200                 error = sysfs_create_group(kobj, groups[i]);
201                 if (error) {
202                         while (--i >= 0)
203                                 sysfs_remove_group(kobj, groups[i]);
204                         break;
205                 }
206         }
207         return error;
208 }
209 EXPORT_SYMBOL_GPL(sysfs_create_groups);
210
211 /**
212  * sysfs_update_group - given a directory kobject, update an attribute group
213  * @kobj:       The kobject to update the group on
214  * @grp:        The attribute group to update
215  *
216  * This function updates an attribute group.  Unlike
217  * sysfs_create_group(), it will explicitly not warn or error if any
218  * of the attribute files being created already exist.  Furthermore,
219  * if the visibility of the files has changed through the is_visible()
220  * callback, it will update the permissions and add or remove the
221  * relevant files. Changing a group's name (subdirectory name under
222  * kobj's directory in sysfs) is not allowed.
223  *
224  * The primary use for this function is to call it after making a change
225  * that affects group visibility.
226  *
227  * Returns 0 on success or error code on failure.
228  */
229 int sysfs_update_group(struct kobject *kobj,
230                        const struct attribute_group *grp)
231 {
232         return internal_create_group(kobj, 1, grp);
233 }
234 EXPORT_SYMBOL_GPL(sysfs_update_group);
235
236 /**
237  * sysfs_remove_group: remove a group from a kobject
238  * @kobj:       kobject to remove the group from
239  * @grp:        group to remove
240  *
241  * This function removes a group of attributes from a kobject.  The attributes
242  * previously have to have been created for this group, otherwise it will fail.
243  */
244 void sysfs_remove_group(struct kobject *kobj,
245                         const struct attribute_group *grp)
246 {
247         struct kernfs_node *parent = kobj->sd;
248         struct kernfs_node *kn;
249
250         if (grp->name) {
251                 kn = kernfs_find_and_get(parent, grp->name);
252                 if (!kn) {
253                         WARN(!kn, KERN_WARNING
254                              "sysfs group '%s' not found for kobject '%s'\n",
255                              grp->name, kobject_name(kobj));
256                         return;
257                 }
258         } else {
259                 kn = parent;
260                 kernfs_get(kn);
261         }
262
263         remove_files(kn, grp);
264         if (grp->name)
265                 kernfs_remove(kn);
266
267         kernfs_put(kn);
268 }
269 EXPORT_SYMBOL_GPL(sysfs_remove_group);
270
271 /**
272  * sysfs_remove_groups - remove a list of groups
273  *
274  * @kobj:       The kobject for the groups to be removed from
275  * @groups:     NULL terminated list of groups to be removed
276  *
277  * If groups is not NULL, remove the specified groups from the kobject.
278  */
279 void sysfs_remove_groups(struct kobject *kobj,
280                          const struct attribute_group **groups)
281 {
282         int i;
283
284         if (!groups)
285                 return;
286         for (i = 0; groups[i]; i++)
287                 sysfs_remove_group(kobj, groups[i]);
288 }
289 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
290
291 /**
292  * sysfs_merge_group - merge files into a pre-existing attribute group.
293  * @kobj:       The kobject containing the group.
294  * @grp:        The files to create and the attribute group they belong to.
295  *
296  * This function returns an error if the group doesn't exist or any of the
297  * files already exist in that group, in which case none of the new files
298  * are created.
299  */
300 int sysfs_merge_group(struct kobject *kobj,
301                        const struct attribute_group *grp)
302 {
303         struct kernfs_node *parent;
304         kuid_t uid;
305         kgid_t gid;
306         int error = 0;
307         struct attribute *const *attr;
308         int i;
309
310         parent = kernfs_find_and_get(kobj->sd, grp->name);
311         if (!parent)
312                 return -ENOENT;
313
314         kobject_get_ownership(kobj, &uid, &gid);
315
316         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
317                 error = sysfs_add_file_mode_ns(parent, *attr, false,
318                                                (*attr)->mode, uid, gid, NULL);
319         if (error) {
320                 while (--i >= 0)
321                         kernfs_remove_by_name(parent, (*--attr)->name);
322         }
323         kernfs_put(parent);
324
325         return error;
326 }
327 EXPORT_SYMBOL_GPL(sysfs_merge_group);
328
329 /**
330  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
331  * @kobj:       The kobject containing the group.
332  * @grp:        The files to remove and the attribute group they belong to.
333  */
334 void sysfs_unmerge_group(struct kobject *kobj,
335                        const struct attribute_group *grp)
336 {
337         struct kernfs_node *parent;
338         struct attribute *const *attr;
339
340         parent = kernfs_find_and_get(kobj->sd, grp->name);
341         if (parent) {
342                 for (attr = grp->attrs; *attr; ++attr)
343                         kernfs_remove_by_name(parent, (*attr)->name);
344                 kernfs_put(parent);
345         }
346 }
347 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
348
349 /**
350  * sysfs_add_link_to_group - add a symlink to an attribute group.
351  * @kobj:       The kobject containing the group.
352  * @group_name: The name of the group.
353  * @target:     The target kobject of the symlink to create.
354  * @link_name:  The name of the symlink to create.
355  */
356 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
357                             struct kobject *target, const char *link_name)
358 {
359         struct kernfs_node *parent;
360         int error = 0;
361
362         parent = kernfs_find_and_get(kobj->sd, group_name);
363         if (!parent)
364                 return -ENOENT;
365
366         error = sysfs_create_link_sd(parent, target, link_name);
367         kernfs_put(parent);
368
369         return error;
370 }
371 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
372
373 /**
374  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
375  * @kobj:       The kobject containing the group.
376  * @group_name: The name of the group.
377  * @link_name:  The name of the symlink to remove.
378  */
379 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
380                                   const char *link_name)
381 {
382         struct kernfs_node *parent;
383
384         parent = kernfs_find_and_get(kobj->sd, group_name);
385         if (parent) {
386                 kernfs_remove_by_name(parent, link_name);
387                 kernfs_put(parent);
388         }
389 }
390 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
391
392 /**
393  * __compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
394  * to a group or an attribute
395  * @kobj:               The kobject containing the group.
396  * @target_kobj:        The target kobject.
397  * @target_name:        The name of the target group or attribute.
398  */
399 int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
400                                       struct kobject *target_kobj,
401                                       const char *target_name)
402 {
403         struct kernfs_node *target;
404         struct kernfs_node *entry;
405         struct kernfs_node *link;
406
407         /*
408          * We don't own @target_kobj and it may be removed at any time.
409          * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
410          * for details.
411          */
412         spin_lock(&sysfs_symlink_target_lock);
413         target = target_kobj->sd;
414         if (target)
415                 kernfs_get(target);
416         spin_unlock(&sysfs_symlink_target_lock);
417         if (!target)
418                 return -ENOENT;
419
420         entry = kernfs_find_and_get(target_kobj->sd, target_name);
421         if (!entry) {
422                 kernfs_put(target);
423                 return -ENOENT;
424         }
425
426         link = kernfs_create_link(kobj->sd, target_name, entry);
427         if (IS_ERR(link) && PTR_ERR(link) == -EEXIST)
428                 sysfs_warn_dup(kobj->sd, target_name);
429
430         kernfs_put(entry);
431         kernfs_put(target);
432         return PTR_ERR_OR_ZERO(link);
433 }
434 EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);