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