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