Merge tag 'docs-for-linus' of git://git.lwn.net/linux-2.6
[firefly-linux-kernel-4.4.55.git] / fs / sysfs / group.c
1 /*
2  * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
3  *
4  * Copyright (c) 2003 Patrick Mochel
5  * Copyright (c) 2003 Open Source Development Lab
6  * Copyright (c) 2013 Greg Kroah-Hartman
7  * Copyright (c) 2013 The Linux Foundation
8  *
9  * This file is released undert the GPL v2.
10  *
11  */
12
13 #include <linux/kobject.h>
14 #include <linux/module.h>
15 #include <linux/dcache.h>
16 #include <linux/namei.h>
17 #include <linux/err.h>
18 #include "sysfs.h"
19
20
21 static void remove_files(struct kernfs_node *parent,
22                          const struct attribute_group *grp)
23 {
24         struct attribute *const *attr;
25         struct bin_attribute *const *bin_attr;
26
27         if (grp->attrs)
28                 for (attr = grp->attrs; *attr; attr++)
29                         kernfs_remove_by_name(parent, (*attr)->name);
30         if (grp->bin_attrs)
31                 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
32                         kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
33 }
34
35 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
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, 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 (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
77                         if (update)
78                                 kernfs_remove_by_name(parent,
79                                                 (*bin_attr)->attr.name);
80                         error = sysfs_add_file_mode_ns(parent,
81                                         &(*bin_attr)->attr, true,
82                                         (*bin_attr)->attr.mode, NULL);
83                         if (error)
84                                 break;
85                 }
86                 if (error)
87                         remove_files(parent, grp);
88         }
89 exit:
90         return error;
91 }
92
93
94 static int internal_create_group(struct kobject *kobj, int update,
95                                  const struct attribute_group *grp)
96 {
97         struct kernfs_node *kn;
98         int error;
99
100         BUG_ON(!kobj || (!update && !kobj->sd));
101
102         /* Updates may happen before the object has been instantiated */
103         if (unlikely(update && !kobj->sd))
104                 return -EINVAL;
105         if (!grp->attrs && !grp->bin_attrs) {
106                 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
107                         kobj->name, grp->name ?: "");
108                 return -EINVAL;
109         }
110         if (grp->name) {
111                 kn = kernfs_create_dir(kobj->sd, grp->name,
112                                        S_IRWXU | S_IRUGO | S_IXUGO, kobj);
113                 if (IS_ERR(kn)) {
114                         if (PTR_ERR(kn) == -EEXIST)
115                                 sysfs_warn_dup(kobj->sd, grp->name);
116                         return PTR_ERR(kn);
117                 }
118         } else
119                 kn = kobj->sd;
120         kernfs_get(kn);
121         error = create_files(kn, kobj, grp, update);
122         if (error) {
123                 if (grp->name)
124                         kernfs_remove(kn);
125         }
126         kernfs_put(kn);
127         return error;
128 }
129
130 /**
131  * sysfs_create_group - given a directory kobject, create an attribute group
132  * @kobj:       The kobject to create the group on
133  * @grp:        The attribute group to create
134  *
135  * This function creates a group for the first time.  It will explicitly
136  * warn and error if any of the attribute files being created already exist.
137  *
138  * Returns 0 on success or error.
139  */
140 int sysfs_create_group(struct kobject *kobj,
141                        const struct attribute_group *grp)
142 {
143         return internal_create_group(kobj, 0, grp);
144 }
145 EXPORT_SYMBOL_GPL(sysfs_create_group);
146
147 /**
148  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
149  * @kobj:       The kobject to create the group on
150  * @groups:     The attribute groups to create, NULL terminated
151  *
152  * This function creates a bunch of attribute groups.  If an error occurs when
153  * creating a group, all previously created groups will be removed, unwinding
154  * everything back to the original state when this function was called.
155  * It will explicitly warn and error if any of the attribute files being
156  * created already exist.
157  *
158  * Returns 0 on success or error code from sysfs_create_group on error.
159  */
160 int sysfs_create_groups(struct kobject *kobj,
161                         const struct attribute_group **groups)
162 {
163         int error = 0;
164         int i;
165
166         if (!groups)
167                 return 0;
168
169         for (i = 0; groups[i]; i++) {
170                 error = sysfs_create_group(kobj, groups[i]);
171                 if (error) {
172                         while (--i >= 0)
173                                 sysfs_remove_group(kobj, groups[i]);
174                         break;
175                 }
176         }
177         return error;
178 }
179 EXPORT_SYMBOL_GPL(sysfs_create_groups);
180
181 /**
182  * sysfs_update_group - given a directory kobject, update an attribute group
183  * @kobj:       The kobject to update the group on
184  * @grp:        The attribute group to update
185  *
186  * This function updates an attribute group.  Unlike
187  * sysfs_create_group(), it will explicitly not warn or error if any
188  * of the attribute files being created already exist.  Furthermore,
189  * if the visibility of the files has changed through the is_visible()
190  * callback, it will update the permissions and add or remove the
191  * relevant files.
192  *
193  * The primary use for this function is to call it after making a change
194  * that affects group visibility.
195  *
196  * Returns 0 on success or error.
197  */
198 int sysfs_update_group(struct kobject *kobj,
199                        const struct attribute_group *grp)
200 {
201         return internal_create_group(kobj, 1, grp);
202 }
203 EXPORT_SYMBOL_GPL(sysfs_update_group);
204
205 /**
206  * sysfs_remove_group: remove a group from a kobject
207  * @kobj:       kobject to remove the group from
208  * @grp:        group to remove
209  *
210  * This function removes a group of attributes from a kobject.  The attributes
211  * previously have to have been created for this group, otherwise it will fail.
212  */
213 void sysfs_remove_group(struct kobject *kobj,
214                         const struct attribute_group *grp)
215 {
216         struct kernfs_node *parent = kobj->sd;
217         struct kernfs_node *kn;
218
219         if (grp->name) {
220                 kn = kernfs_find_and_get(parent, grp->name);
221                 if (!kn) {
222                         WARN(!kn, KERN_WARNING
223                              "sysfs group %p not found for kobject '%s'\n",
224                              grp, kobject_name(kobj));
225                         return;
226                 }
227         } else {
228                 kn = parent;
229                 kernfs_get(kn);
230         }
231
232         remove_files(kn, grp);
233         if (grp->name)
234                 kernfs_remove(kn);
235
236         kernfs_put(kn);
237 }
238 EXPORT_SYMBOL_GPL(sysfs_remove_group);
239
240 /**
241  * sysfs_remove_groups - remove a list of groups
242  *
243  * @kobj:       The kobject for the groups to be removed from
244  * @groups:     NULL terminated list of groups to be removed
245  *
246  * If groups is not NULL, remove the specified groups from the kobject.
247  */
248 void sysfs_remove_groups(struct kobject *kobj,
249                          const struct attribute_group **groups)
250 {
251         int i;
252
253         if (!groups)
254                 return;
255         for (i = 0; groups[i]; i++)
256                 sysfs_remove_group(kobj, groups[i]);
257 }
258 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
259
260 /**
261  * sysfs_merge_group - merge files into a pre-existing attribute group.
262  * @kobj:       The kobject containing the group.
263  * @grp:        The files to create and the attribute group they belong to.
264  *
265  * This function returns an error if the group doesn't exist or any of the
266  * files already exist in that group, in which case none of the new files
267  * are created.
268  */
269 int sysfs_merge_group(struct kobject *kobj,
270                        const struct attribute_group *grp)
271 {
272         struct kernfs_node *parent;
273         int error = 0;
274         struct attribute *const *attr;
275         int i;
276
277         parent = kernfs_find_and_get(kobj->sd, grp->name);
278         if (!parent)
279                 return -ENOENT;
280
281         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
282                 error = sysfs_add_file(parent, *attr, false);
283         if (error) {
284                 while (--i >= 0)
285                         kernfs_remove_by_name(parent, (*--attr)->name);
286         }
287         kernfs_put(parent);
288
289         return error;
290 }
291 EXPORT_SYMBOL_GPL(sysfs_merge_group);
292
293 /**
294  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
295  * @kobj:       The kobject containing the group.
296  * @grp:        The files to remove and the attribute group they belong to.
297  */
298 void sysfs_unmerge_group(struct kobject *kobj,
299                        const struct attribute_group *grp)
300 {
301         struct kernfs_node *parent;
302         struct attribute *const *attr;
303
304         parent = kernfs_find_and_get(kobj->sd, grp->name);
305         if (parent) {
306                 for (attr = grp->attrs; *attr; ++attr)
307                         kernfs_remove_by_name(parent, (*attr)->name);
308                 kernfs_put(parent);
309         }
310 }
311 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
312
313 /**
314  * sysfs_add_link_to_group - add a symlink to an attribute group.
315  * @kobj:       The kobject containing the group.
316  * @group_name: The name of the group.
317  * @target:     The target kobject of the symlink to create.
318  * @link_name:  The name of the symlink to create.
319  */
320 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
321                             struct kobject *target, const char *link_name)
322 {
323         struct kernfs_node *parent;
324         int error = 0;
325
326         parent = kernfs_find_and_get(kobj->sd, group_name);
327         if (!parent)
328                 return -ENOENT;
329
330         error = sysfs_create_link_sd(parent, target, link_name);
331         kernfs_put(parent);
332
333         return error;
334 }
335 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
336
337 /**
338  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
339  * @kobj:       The kobject containing the group.
340  * @group_name: The name of the group.
341  * @link_name:  The name of the symlink to remove.
342  */
343 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
344                                   const char *link_name)
345 {
346         struct kernfs_node *parent;
347
348         parent = kernfs_find_and_get(kobj->sd, group_name);
349         if (parent) {
350                 kernfs_remove_by_name(parent, link_name);
351                 kernfs_put(parent);
352         }
353 }
354 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);