Merge tag 'media/v4.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-microblaze.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                 kn = kernfs_create_dir_ns(kobj->sd, grp->name,
128                                           S_IRWXU | S_IRUGO | S_IXUGO,
129                                           uid, gid, kobj, NULL);
130                 if (IS_ERR(kn)) {
131                         if (PTR_ERR(kn) == -EEXIST)
132                                 sysfs_warn_dup(kobj->sd, grp->name);
133                         return PTR_ERR(kn);
134                 }
135         } else
136                 kn = kobj->sd;
137         kernfs_get(kn);
138         error = create_files(kn, kobj, uid, gid, grp, update);
139         if (error) {
140                 if (grp->name)
141                         kernfs_remove(kn);
142         }
143         kernfs_put(kn);
144         return error;
145 }
146
147 /**
148  * sysfs_create_group - given a directory kobject, create an attribute group
149  * @kobj:       The kobject to create the group on
150  * @grp:        The attribute group to create
151  *
152  * This function creates a group for the first time.  It will explicitly
153  * warn and error if any of the attribute files being created already exist.
154  *
155  * Returns 0 on success or error code on failure.
156  */
157 int sysfs_create_group(struct kobject *kobj,
158                        const struct attribute_group *grp)
159 {
160         return internal_create_group(kobj, 0, grp);
161 }
162 EXPORT_SYMBOL_GPL(sysfs_create_group);
163
164 /**
165  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
166  * @kobj:       The kobject to create the group on
167  * @groups:     The attribute groups to create, NULL terminated
168  *
169  * This function creates a bunch of attribute groups.  If an error occurs when
170  * creating a group, all previously created groups will be removed, unwinding
171  * everything back to the original state when this function was called.
172  * It will explicitly warn and error if any of the attribute files being
173  * created already exist.
174  *
175  * Returns 0 on success or error code from sysfs_create_group on failure.
176  */
177 int sysfs_create_groups(struct kobject *kobj,
178                         const struct attribute_group **groups)
179 {
180         int error = 0;
181         int i;
182
183         if (!groups)
184                 return 0;
185
186         for (i = 0; groups[i]; i++) {
187                 error = sysfs_create_group(kobj, groups[i]);
188                 if (error) {
189                         while (--i >= 0)
190                                 sysfs_remove_group(kobj, groups[i]);
191                         break;
192                 }
193         }
194         return error;
195 }
196 EXPORT_SYMBOL_GPL(sysfs_create_groups);
197
198 /**
199  * sysfs_update_group - given a directory kobject, update an attribute group
200  * @kobj:       The kobject to update the group on
201  * @grp:        The attribute group to update
202  *
203  * This function updates an attribute group.  Unlike
204  * sysfs_create_group(), it will explicitly not warn or error if any
205  * of the attribute files being created already exist.  Furthermore,
206  * if the visibility of the files has changed through the is_visible()
207  * callback, it will update the permissions and add or remove the
208  * relevant files.
209  *
210  * The primary use for this function is to call it after making a change
211  * that affects group visibility.
212  *
213  * Returns 0 on success or error code on failure.
214  */
215 int sysfs_update_group(struct kobject *kobj,
216                        const struct attribute_group *grp)
217 {
218         return internal_create_group(kobj, 1, grp);
219 }
220 EXPORT_SYMBOL_GPL(sysfs_update_group);
221
222 /**
223  * sysfs_remove_group: remove a group from a kobject
224  * @kobj:       kobject to remove the group from
225  * @grp:        group to remove
226  *
227  * This function removes a group of attributes from a kobject.  The attributes
228  * previously have to have been created for this group, otherwise it will fail.
229  */
230 void sysfs_remove_group(struct kobject *kobj,
231                         const struct attribute_group *grp)
232 {
233         struct kernfs_node *parent = kobj->sd;
234         struct kernfs_node *kn;
235
236         if (grp->name) {
237                 kn = kernfs_find_and_get(parent, grp->name);
238                 if (!kn) {
239                         WARN(!kn, KERN_WARNING
240                              "sysfs group '%s' not found for kobject '%s'\n",
241                              grp->name, kobject_name(kobj));
242                         return;
243                 }
244         } else {
245                 kn = parent;
246                 kernfs_get(kn);
247         }
248
249         remove_files(kn, grp);
250         if (grp->name)
251                 kernfs_remove(kn);
252
253         kernfs_put(kn);
254 }
255 EXPORT_SYMBOL_GPL(sysfs_remove_group);
256
257 /**
258  * sysfs_remove_groups - remove a list of groups
259  *
260  * @kobj:       The kobject for the groups to be removed from
261  * @groups:     NULL terminated list of groups to be removed
262  *
263  * If groups is not NULL, remove the specified groups from the kobject.
264  */
265 void sysfs_remove_groups(struct kobject *kobj,
266                          const struct attribute_group **groups)
267 {
268         int i;
269
270         if (!groups)
271                 return;
272         for (i = 0; groups[i]; i++)
273                 sysfs_remove_group(kobj, groups[i]);
274 }
275 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
276
277 /**
278  * sysfs_merge_group - merge files into a pre-existing attribute group.
279  * @kobj:       The kobject containing the group.
280  * @grp:        The files to create and the attribute group they belong to.
281  *
282  * This function returns an error if the group doesn't exist or any of the
283  * files already exist in that group, in which case none of the new files
284  * are created.
285  */
286 int sysfs_merge_group(struct kobject *kobj,
287                        const struct attribute_group *grp)
288 {
289         struct kernfs_node *parent;
290         kuid_t uid;
291         kgid_t gid;
292         int error = 0;
293         struct attribute *const *attr;
294         int i;
295
296         parent = kernfs_find_and_get(kobj->sd, grp->name);
297         if (!parent)
298                 return -ENOENT;
299
300         kobject_get_ownership(kobj, &uid, &gid);
301
302         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
303                 error = sysfs_add_file_mode_ns(parent, *attr, false,
304                                                (*attr)->mode, uid, gid, NULL);
305         if (error) {
306                 while (--i >= 0)
307                         kernfs_remove_by_name(parent, (*--attr)->name);
308         }
309         kernfs_put(parent);
310
311         return error;
312 }
313 EXPORT_SYMBOL_GPL(sysfs_merge_group);
314
315 /**
316  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
317  * @kobj:       The kobject containing the group.
318  * @grp:        The files to remove and the attribute group they belong to.
319  */
320 void sysfs_unmerge_group(struct kobject *kobj,
321                        const struct attribute_group *grp)
322 {
323         struct kernfs_node *parent;
324         struct attribute *const *attr;
325
326         parent = kernfs_find_and_get(kobj->sd, grp->name);
327         if (parent) {
328                 for (attr = grp->attrs; *attr; ++attr)
329                         kernfs_remove_by_name(parent, (*attr)->name);
330                 kernfs_put(parent);
331         }
332 }
333 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
334
335 /**
336  * sysfs_add_link_to_group - add a symlink to an attribute group.
337  * @kobj:       The kobject containing the group.
338  * @group_name: The name of the group.
339  * @target:     The target kobject of the symlink to create.
340  * @link_name:  The name of the symlink to create.
341  */
342 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
343                             struct kobject *target, const char *link_name)
344 {
345         struct kernfs_node *parent;
346         int error = 0;
347
348         parent = kernfs_find_and_get(kobj->sd, group_name);
349         if (!parent)
350                 return -ENOENT;
351
352         error = sysfs_create_link_sd(parent, target, link_name);
353         kernfs_put(parent);
354
355         return error;
356 }
357 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
358
359 /**
360  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
361  * @kobj:       The kobject containing the group.
362  * @group_name: The name of the group.
363  * @link_name:  The name of the symlink to remove.
364  */
365 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
366                                   const char *link_name)
367 {
368         struct kernfs_node *parent;
369
370         parent = kernfs_find_and_get(kobj->sd, group_name);
371         if (parent) {
372                 kernfs_remove_by_name(parent, link_name);
373                 kernfs_put(parent);
374         }
375 }
376 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
377
378 /**
379  * __compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
380  * to a group or an attribute
381  * @kobj:               The kobject containing the group.
382  * @target_kobj:        The target kobject.
383  * @target_name:        The name of the target group or attribute.
384  */
385 int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
386                                       struct kobject *target_kobj,
387                                       const char *target_name)
388 {
389         struct kernfs_node *target;
390         struct kernfs_node *entry;
391         struct kernfs_node *link;
392
393         /*
394          * We don't own @target_kobj and it may be removed at any time.
395          * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
396          * for details.
397          */
398         spin_lock(&sysfs_symlink_target_lock);
399         target = target_kobj->sd;
400         if (target)
401                 kernfs_get(target);
402         spin_unlock(&sysfs_symlink_target_lock);
403         if (!target)
404                 return -ENOENT;
405
406         entry = kernfs_find_and_get(target_kobj->sd, target_name);
407         if (!entry) {
408                 kernfs_put(target);
409                 return -ENOENT;
410         }
411
412         link = kernfs_create_link(kobj->sd, target_name, entry);
413         if (IS_ERR(link) && PTR_ERR(link) == -EEXIST)
414                 sysfs_warn_dup(kobj->sd, target_name);
415
416         kernfs_put(entry);
417         kernfs_put(target);
418         return PTR_ERR_OR_ZERO(link);
419 }
420 EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);