1619daeeeSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (c) 2003 Patrick Mochel
61da177e4SLinus Torvalds * Copyright (c) 2003 Open Source Development Lab
79e2a47edSGreg Kroah-Hartman * Copyright (c) 2013 Greg Kroah-Hartman
89e2a47edSGreg Kroah-Hartman * Copyright (c) 2013 The Linux Foundation
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include <linux/kobject.h>
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/dcache.h>
145f45f1a7SChristoph Hellwig #include <linux/namei.h>
151da177e4SLinus Torvalds #include <linux/err.h>
16303a4276SChristian Brauner #include <linux/fs.h>
171da177e4SLinus Torvalds #include "sysfs.h"
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds
remove_files(struct kernfs_node * parent,const struct attribute_group * grp)209f70a401SRobert ABEL static void remove_files(struct kernfs_node *parent,
211da177e4SLinus Torvalds const struct attribute_group *grp)
221da177e4SLinus Torvalds {
231da177e4SLinus Torvalds struct attribute *const *attr;
246ab9cea1SGreg Kroah-Hartman struct bin_attribute *const *bin_attr;
251da177e4SLinus Torvalds
266ab9cea1SGreg Kroah-Hartman if (grp->attrs)
276ab9cea1SGreg Kroah-Hartman for (attr = grp->attrs; *attr; attr++)
28324a56e1STejun Heo kernfs_remove_by_name(parent, (*attr)->name);
296ab9cea1SGreg Kroah-Hartman if (grp->bin_attrs)
306ab9cea1SGreg Kroah-Hartman for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
319f70a401SRobert ABEL kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds
create_files(struct kernfs_node * parent,struct kobject * kobj,kuid_t uid,kgid_t gid,const struct attribute_group * grp,int update)34324a56e1STejun Heo static int create_files(struct kernfs_node *parent, struct kobject *kobj,
355f81880dSDmitry Torokhov kuid_t uid, kgid_t gid,
360f423895SJames Bottomley const struct attribute_group *grp, int update)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds struct attribute *const *attr;
396ab9cea1SGreg Kroah-Hartman struct bin_attribute *const *bin_attr;
40d4acd722SJames Bottomley int error = 0, i;
411da177e4SLinus Torvalds
426ab9cea1SGreg Kroah-Hartman if (grp->attrs) {
430f423895SJames Bottomley for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
44da4759c7SGuenter Roeck umode_t mode = (*attr)->mode;
450f423895SJames Bottomley
466ab9cea1SGreg Kroah-Hartman /*
476ab9cea1SGreg Kroah-Hartman * In update mode, we're changing the permissions or
480f423895SJames Bottomley * visibility. Do this by first removing then
496ab9cea1SGreg Kroah-Hartman * re-adding (if required) the file.
506ab9cea1SGreg Kroah-Hartman */
510f423895SJames Bottomley if (update)
52324a56e1STejun Heo kernfs_remove_by_name(parent, (*attr)->name);
530f423895SJames Bottomley if (grp->is_visible) {
540f423895SJames Bottomley mode = grp->is_visible(kobj, *attr, i);
550f423895SJames Bottomley if (!mode)
560f423895SJames Bottomley continue;
570f423895SJames Bottomley }
58d8bf8c92SVivien Didelot
59d8bf8c92SVivien Didelot WARN(mode & ~(SYSFS_PREALLOC | 0664),
60d8bf8c92SVivien Didelot "Attribute %s: Invalid permissions 0%o\n",
61d8bf8c92SVivien Didelot (*attr)->name, mode);
62d8bf8c92SVivien Didelot
63d8bf8c92SVivien Didelot mode &= SYSFS_PREALLOC | 0664;
645cf3bb0dSChristoph Hellwig error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,
655cf3bb0dSChristoph Hellwig gid, NULL);
660f423895SJames Bottomley if (unlikely(error))
670f423895SJames Bottomley break;
680f423895SJames Bottomley }
696ab9cea1SGreg Kroah-Hartman if (error) {
709f70a401SRobert ABEL remove_files(parent, grp);
716ab9cea1SGreg Kroah-Hartman goto exit;
726ab9cea1SGreg Kroah-Hartman }
736ab9cea1SGreg Kroah-Hartman }
746ab9cea1SGreg Kroah-Hartman
756ab9cea1SGreg Kroah-Hartman if (grp->bin_attrs) {
767f5028cfSEmilio López for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
777f5028cfSEmilio López umode_t mode = (*bin_attr)->attr.mode;
787f5028cfSEmilio López
796ab9cea1SGreg Kroah-Hartman if (update)
80aabaf4c2SCody P Schafer kernfs_remove_by_name(parent,
81aabaf4c2SCody P Schafer (*bin_attr)->attr.name);
827f5028cfSEmilio López if (grp->is_bin_visible) {
837f5028cfSEmilio López mode = grp->is_bin_visible(kobj, *bin_attr, i);
847f5028cfSEmilio López if (!mode)
857f5028cfSEmilio López continue;
867f5028cfSEmilio López }
877f5028cfSEmilio López
887f5028cfSEmilio López WARN(mode & ~(SYSFS_PREALLOC | 0664),
897f5028cfSEmilio López "Attribute %s: Invalid permissions 0%o\n",
907f5028cfSEmilio López (*bin_attr)->attr.name, mode);
917f5028cfSEmilio López
927f5028cfSEmilio López mode &= SYSFS_PREALLOC | 0664;
935cf3bb0dSChristoph Hellwig error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,
945cf3bb0dSChristoph Hellwig mode, uid, gid,
955cf3bb0dSChristoph Hellwig NULL);
966ab9cea1SGreg Kroah-Hartman if (error)
976ab9cea1SGreg Kroah-Hartman break;
986ab9cea1SGreg Kroah-Hartman }
991da177e4SLinus Torvalds if (error)
1009f70a401SRobert ABEL remove_files(parent, grp);
1016ab9cea1SGreg Kroah-Hartman }
1026ab9cea1SGreg Kroah-Hartman exit:
1031da177e4SLinus Torvalds return error;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds
1061da177e4SLinus Torvalds
internal_create_group(struct kobject * kobj,int update,const struct attribute_group * grp)1070f423895SJames Bottomley static int internal_create_group(struct kobject *kobj, int update,
1081da177e4SLinus Torvalds const struct attribute_group *grp)
1091da177e4SLinus Torvalds {
110324a56e1STejun Heo struct kernfs_node *kn;
1115f81880dSDmitry Torokhov kuid_t uid;
1125f81880dSDmitry Torokhov kgid_t gid;
1131da177e4SLinus Torvalds int error;
1141da177e4SLinus Torvalds
115de96e9feSGreg Kroah-Hartman if (WARN_ON(!kobj || (!update && !kobj->sd)))
116de96e9feSGreg Kroah-Hartman return -EINVAL;
1170f423895SJames Bottomley
1180f423895SJames Bottomley /* Updates may happen before the object has been instantiated */
1190f423895SJames Bottomley if (unlikely(update && !kobj->sd))
1200f423895SJames Bottomley return -EINVAL;
121*a91845b9SMiquel Raynal
122388a8c35SOliver Schinagl if (!grp->attrs && !grp->bin_attrs) {
123*a91845b9SMiquel Raynal pr_debug("sysfs: (bin_)attrs not set by subsystem for group: %s/%s, skipping\n",
124adf305f7SJavi Merino kobj->name, grp->name ?: "");
125*a91845b9SMiquel Raynal return 0;
1265631f2c1SBruno Prémont }
127*a91845b9SMiquel Raynal
1285f81880dSDmitry Torokhov kobject_get_ownership(kobj, &uid, &gid);
1291da177e4SLinus Torvalds if (grp->name) {
130c855cf27SRajat Jain if (update) {
131c855cf27SRajat Jain kn = kernfs_find_and_get(kobj->sd, grp->name);
132c855cf27SRajat Jain if (!kn) {
133c855cf27SRajat Jain pr_warn("Can't update unknown attr grp name: %s/%s\n",
134c855cf27SRajat Jain kobj->name, grp->name);
135c855cf27SRajat Jain return -EINVAL;
136c855cf27SRajat Jain }
137c855cf27SRajat Jain } else {
1385f81880dSDmitry Torokhov kn = kernfs_create_dir_ns(kobj->sd, grp->name,
1395f81880dSDmitry Torokhov S_IRWXU | S_IRUGO | S_IXUGO,
1405f81880dSDmitry Torokhov uid, gid, kobj, NULL);
141324a56e1STejun Heo if (IS_ERR(kn)) {
142324a56e1STejun Heo if (PTR_ERR(kn) == -EEXIST)
14393b2b8e4STejun Heo sysfs_warn_dup(kobj->sd, grp->name);
144324a56e1STejun Heo return PTR_ERR(kn);
14593b2b8e4STejun Heo }
146c855cf27SRajat Jain }
1474981e013SMiquel Raynal } else {
148324a56e1STejun Heo kn = kobj->sd;
1494981e013SMiquel Raynal }
1504981e013SMiquel Raynal
151324a56e1STejun Heo kernfs_get(kn);
1525f81880dSDmitry Torokhov error = create_files(kn, kobj, uid, gid, grp, update);
153608e266aSTejun Heo if (error) {
1541da177e4SLinus Torvalds if (grp->name)
155324a56e1STejun Heo kernfs_remove(kn);
1561da177e4SLinus Torvalds }
157324a56e1STejun Heo kernfs_put(kn);
158c855cf27SRajat Jain
159c855cf27SRajat Jain if (grp->name && update)
160c855cf27SRajat Jain kernfs_put(kn);
161c855cf27SRajat Jain
1621da177e4SLinus Torvalds return error;
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds
1650f423895SJames Bottomley /**
1660f423895SJames Bottomley * sysfs_create_group - given a directory kobject, create an attribute group
1670f423895SJames Bottomley * @kobj: The kobject to create the group on
1680f423895SJames Bottomley * @grp: The attribute group to create
1690f423895SJames Bottomley *
1700f423895SJames Bottomley * This function creates a group for the first time. It will explicitly
1710f423895SJames Bottomley * warn and error if any of the attribute files being created already exist.
1720f423895SJames Bottomley *
173ed1dc8a8SAntonio Ospite * Returns 0 on success or error code on failure.
1740f423895SJames Bottomley */
sysfs_create_group(struct kobject * kobj,const struct attribute_group * grp)1750f423895SJames Bottomley int sysfs_create_group(struct kobject *kobj,
1760f423895SJames Bottomley const struct attribute_group *grp)
1770f423895SJames Bottomley {
1780f423895SJames Bottomley return internal_create_group(kobj, 0, grp);
1790f423895SJames Bottomley }
180d363bc53SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_create_group);
1810f423895SJames Bottomley
internal_create_groups(struct kobject * kobj,int update,const struct attribute_group ** groups)182aac1f7f9SJiri Olsa static int internal_create_groups(struct kobject *kobj, int update,
183aac1f7f9SJiri Olsa const struct attribute_group **groups)
184aac1f7f9SJiri Olsa {
185aac1f7f9SJiri Olsa int error = 0;
186aac1f7f9SJiri Olsa int i;
187aac1f7f9SJiri Olsa
188aac1f7f9SJiri Olsa if (!groups)
189aac1f7f9SJiri Olsa return 0;
190aac1f7f9SJiri Olsa
191aac1f7f9SJiri Olsa for (i = 0; groups[i]; i++) {
192aac1f7f9SJiri Olsa error = internal_create_group(kobj, update, groups[i]);
193aac1f7f9SJiri Olsa if (error) {
194aac1f7f9SJiri Olsa while (--i >= 0)
195aac1f7f9SJiri Olsa sysfs_remove_group(kobj, groups[i]);
196aac1f7f9SJiri Olsa break;
197aac1f7f9SJiri Olsa }
198aac1f7f9SJiri Olsa }
199aac1f7f9SJiri Olsa return error;
200aac1f7f9SJiri Olsa }
201aac1f7f9SJiri Olsa
2020f423895SJames Bottomley /**
2033e9b2baeSGreg Kroah-Hartman * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
2043e9b2baeSGreg Kroah-Hartman * @kobj: The kobject to create the group on
2053e9b2baeSGreg Kroah-Hartman * @groups: The attribute groups to create, NULL terminated
2063e9b2baeSGreg Kroah-Hartman *
2073e9b2baeSGreg Kroah-Hartman * This function creates a bunch of attribute groups. If an error occurs when
2083e9b2baeSGreg Kroah-Hartman * creating a group, all previously created groups will be removed, unwinding
2093e9b2baeSGreg Kroah-Hartman * everything back to the original state when this function was called.
2103e9b2baeSGreg Kroah-Hartman * It will explicitly warn and error if any of the attribute files being
2113e9b2baeSGreg Kroah-Hartman * created already exist.
2123e9b2baeSGreg Kroah-Hartman *
213ed1dc8a8SAntonio Ospite * Returns 0 on success or error code from sysfs_create_group on failure.
2143e9b2baeSGreg Kroah-Hartman */
sysfs_create_groups(struct kobject * kobj,const struct attribute_group ** groups)2153e9b2baeSGreg Kroah-Hartman int sysfs_create_groups(struct kobject *kobj,
2163e9b2baeSGreg Kroah-Hartman const struct attribute_group **groups)
2173e9b2baeSGreg Kroah-Hartman {
218aac1f7f9SJiri Olsa return internal_create_groups(kobj, 0, groups);
2193e9b2baeSGreg Kroah-Hartman }
2203e9b2baeSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_create_groups);
2213e9b2baeSGreg Kroah-Hartman
2223e9b2baeSGreg Kroah-Hartman /**
223aac1f7f9SJiri Olsa * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups
224aac1f7f9SJiri Olsa * @kobj: The kobject to update the group on
225aac1f7f9SJiri Olsa * @groups: The attribute groups to update, NULL terminated
226aac1f7f9SJiri Olsa *
227aac1f7f9SJiri Olsa * This function update a bunch of attribute groups. If an error occurs when
228aac1f7f9SJiri Olsa * updating a group, all previously updated groups will be removed together
229aac1f7f9SJiri Olsa * with already existing (not updated) attributes.
230aac1f7f9SJiri Olsa *
231aac1f7f9SJiri Olsa * Returns 0 on success or error code from sysfs_update_group on failure.
232aac1f7f9SJiri Olsa */
sysfs_update_groups(struct kobject * kobj,const struct attribute_group ** groups)233aac1f7f9SJiri Olsa int sysfs_update_groups(struct kobject *kobj,
234aac1f7f9SJiri Olsa const struct attribute_group **groups)
235aac1f7f9SJiri Olsa {
236aac1f7f9SJiri Olsa return internal_create_groups(kobj, 1, groups);
237aac1f7f9SJiri Olsa }
238aac1f7f9SJiri Olsa EXPORT_SYMBOL_GPL(sysfs_update_groups);
239aac1f7f9SJiri Olsa
240aac1f7f9SJiri Olsa /**
2411f8e1cdaSRobert P. J. Day * sysfs_update_group - given a directory kobject, update an attribute group
2421f8e1cdaSRobert P. J. Day * @kobj: The kobject to update the group on
2431f8e1cdaSRobert P. J. Day * @grp: The attribute group to update
2440f423895SJames Bottomley *
2450f423895SJames Bottomley * This function updates an attribute group. Unlike
2460f423895SJames Bottomley * sysfs_create_group(), it will explicitly not warn or error if any
2470f423895SJames Bottomley * of the attribute files being created already exist. Furthermore,
2480f423895SJames Bottomley * if the visibility of the files has changed through the is_visible()
2490f423895SJames Bottomley * callback, it will update the permissions and add or remove the
250c855cf27SRajat Jain * relevant files. Changing a group's name (subdirectory name under
251c855cf27SRajat Jain * kobj's directory in sysfs) is not allowed.
2520f423895SJames Bottomley *
2530f423895SJames Bottomley * The primary use for this function is to call it after making a change
2540f423895SJames Bottomley * that affects group visibility.
2550f423895SJames Bottomley *
256ed1dc8a8SAntonio Ospite * Returns 0 on success or error code on failure.
2570f423895SJames Bottomley */
sysfs_update_group(struct kobject * kobj,const struct attribute_group * grp)2580f423895SJames Bottomley int sysfs_update_group(struct kobject *kobj,
2590f423895SJames Bottomley const struct attribute_group *grp)
2600f423895SJames Bottomley {
2610f423895SJames Bottomley return internal_create_group(kobj, 1, grp);
2620f423895SJames Bottomley }
263d363bc53SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_update_group);
2640f423895SJames Bottomley
265f9ae443bSGreg Kroah-Hartman /**
266f9ae443bSGreg Kroah-Hartman * sysfs_remove_group: remove a group from a kobject
267f9ae443bSGreg Kroah-Hartman * @kobj: kobject to remove the group from
268f9ae443bSGreg Kroah-Hartman * @grp: group to remove
269f9ae443bSGreg Kroah-Hartman *
270f9ae443bSGreg Kroah-Hartman * This function removes a group of attributes from a kobject. The attributes
271f9ae443bSGreg Kroah-Hartman * previously have to have been created for this group, otherwise it will fail.
272f9ae443bSGreg Kroah-Hartman */
sysfs_remove_group(struct kobject * kobj,const struct attribute_group * grp)2731da177e4SLinus Torvalds void sysfs_remove_group(struct kobject *kobj,
2741da177e4SLinus Torvalds const struct attribute_group *grp)
2751da177e4SLinus Torvalds {
276324a56e1STejun Heo struct kernfs_node *parent = kobj->sd;
277324a56e1STejun Heo struct kernfs_node *kn;
2781da177e4SLinus Torvalds
279057f6c01SJames Morris if (grp->name) {
280324a56e1STejun Heo kn = kernfs_find_and_get(parent, grp->name);
281324a56e1STejun Heo if (!kn) {
282324a56e1STejun Heo WARN(!kn, KERN_WARNING
28378618d39SJohannes Thumshirn "sysfs group '%s' not found for kobject '%s'\n",
28478618d39SJohannes Thumshirn grp->name, kobject_name(kobj));
285969affd2SGreg Kroah-Hartman return;
286969affd2SGreg Kroah-Hartman }
287ccf73cf3STejun Heo } else {
288324a56e1STejun Heo kn = parent;
289324a56e1STejun Heo kernfs_get(kn);
290ccf73cf3STejun Heo }
2911da177e4SLinus Torvalds
2929f70a401SRobert ABEL remove_files(kn, grp);
2931da177e4SLinus Torvalds if (grp->name)
294324a56e1STejun Heo kernfs_remove(kn);
295608e266aSTejun Heo
296324a56e1STejun Heo kernfs_put(kn);
2971da177e4SLinus Torvalds }
298d363bc53SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_remove_group);
2991da177e4SLinus Torvalds
30069d44ffbSAlan Stern /**
3013e9b2baeSGreg Kroah-Hartman * sysfs_remove_groups - remove a list of groups
3023e9b2baeSGreg Kroah-Hartman *
303f9ae443bSGreg Kroah-Hartman * @kobj: The kobject for the groups to be removed from
304f9ae443bSGreg Kroah-Hartman * @groups: NULL terminated list of groups to be removed
3053e9b2baeSGreg Kroah-Hartman *
30609239ed4SGreg Kroah-Hartman * If groups is not NULL, remove the specified groups from the kobject.
3073e9b2baeSGreg Kroah-Hartman */
sysfs_remove_groups(struct kobject * kobj,const struct attribute_group ** groups)3083e9b2baeSGreg Kroah-Hartman void sysfs_remove_groups(struct kobject *kobj,
3093e9b2baeSGreg Kroah-Hartman const struct attribute_group **groups)
3103e9b2baeSGreg Kroah-Hartman {
3113e9b2baeSGreg Kroah-Hartman int i;
3123e9b2baeSGreg Kroah-Hartman
3133e9b2baeSGreg Kroah-Hartman if (!groups)
3143e9b2baeSGreg Kroah-Hartman return;
3153e9b2baeSGreg Kroah-Hartman for (i = 0; groups[i]; i++)
3163e9b2baeSGreg Kroah-Hartman sysfs_remove_group(kobj, groups[i]);
3173e9b2baeSGreg Kroah-Hartman }
3183e9b2baeSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_remove_groups);
3193e9b2baeSGreg Kroah-Hartman
3203e9b2baeSGreg Kroah-Hartman /**
32169d44ffbSAlan Stern * sysfs_merge_group - merge files into a pre-existing attribute group.
32269d44ffbSAlan Stern * @kobj: The kobject containing the group.
32369d44ffbSAlan Stern * @grp: The files to create and the attribute group they belong to.
32469d44ffbSAlan Stern *
32569d44ffbSAlan Stern * This function returns an error if the group doesn't exist or any of the
32669d44ffbSAlan Stern * files already exist in that group, in which case none of the new files
32769d44ffbSAlan Stern * are created.
32869d44ffbSAlan Stern */
sysfs_merge_group(struct kobject * kobj,const struct attribute_group * grp)32969d44ffbSAlan Stern int sysfs_merge_group(struct kobject *kobj,
33069d44ffbSAlan Stern const struct attribute_group *grp)
33169d44ffbSAlan Stern {
332324a56e1STejun Heo struct kernfs_node *parent;
3335f81880dSDmitry Torokhov kuid_t uid;
3345f81880dSDmitry Torokhov kgid_t gid;
33569d44ffbSAlan Stern int error = 0;
33669d44ffbSAlan Stern struct attribute *const *attr;
33769d44ffbSAlan Stern int i;
33869d44ffbSAlan Stern
339324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, grp->name);
340324a56e1STejun Heo if (!parent)
34169d44ffbSAlan Stern return -ENOENT;
34269d44ffbSAlan Stern
3435f81880dSDmitry Torokhov kobject_get_ownership(kobj, &uid, &gid);
3445f81880dSDmitry Torokhov
34569d44ffbSAlan Stern for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
3465cf3bb0dSChristoph Hellwig error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
3475cf3bb0dSChristoph Hellwig uid, gid, NULL);
34869d44ffbSAlan Stern if (error) {
34969d44ffbSAlan Stern while (--i >= 0)
350324a56e1STejun Heo kernfs_remove_by_name(parent, (*--attr)->name);
35169d44ffbSAlan Stern }
352324a56e1STejun Heo kernfs_put(parent);
35369d44ffbSAlan Stern
35469d44ffbSAlan Stern return error;
35569d44ffbSAlan Stern }
35669d44ffbSAlan Stern EXPORT_SYMBOL_GPL(sysfs_merge_group);
35769d44ffbSAlan Stern
35869d44ffbSAlan Stern /**
35969d44ffbSAlan Stern * sysfs_unmerge_group - remove files from a pre-existing attribute group.
36069d44ffbSAlan Stern * @kobj: The kobject containing the group.
36169d44ffbSAlan Stern * @grp: The files to remove and the attribute group they belong to.
36269d44ffbSAlan Stern */
sysfs_unmerge_group(struct kobject * kobj,const struct attribute_group * grp)36369d44ffbSAlan Stern void sysfs_unmerge_group(struct kobject *kobj,
36469d44ffbSAlan Stern const struct attribute_group *grp)
36569d44ffbSAlan Stern {
366324a56e1STejun Heo struct kernfs_node *parent;
36769d44ffbSAlan Stern struct attribute *const *attr;
36869d44ffbSAlan Stern
369324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, grp->name);
370324a56e1STejun Heo if (parent) {
37169d44ffbSAlan Stern for (attr = grp->attrs; *attr; ++attr)
372324a56e1STejun Heo kernfs_remove_by_name(parent, (*attr)->name);
373324a56e1STejun Heo kernfs_put(parent);
37469d44ffbSAlan Stern }
37569d44ffbSAlan Stern }
37669d44ffbSAlan Stern EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
37769d44ffbSAlan Stern
3780bb8f3d6SRafael J. Wysocki /**
3790bb8f3d6SRafael J. Wysocki * sysfs_add_link_to_group - add a symlink to an attribute group.
3800bb8f3d6SRafael J. Wysocki * @kobj: The kobject containing the group.
3810bb8f3d6SRafael J. Wysocki * @group_name: The name of the group.
3820bb8f3d6SRafael J. Wysocki * @target: The target kobject of the symlink to create.
3830bb8f3d6SRafael J. Wysocki * @link_name: The name of the symlink to create.
3840bb8f3d6SRafael J. Wysocki */
sysfs_add_link_to_group(struct kobject * kobj,const char * group_name,struct kobject * target,const char * link_name)3850bb8f3d6SRafael J. Wysocki int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
3860bb8f3d6SRafael J. Wysocki struct kobject *target, const char *link_name)
3870bb8f3d6SRafael J. Wysocki {
388324a56e1STejun Heo struct kernfs_node *parent;
3890bb8f3d6SRafael J. Wysocki int error = 0;
3900bb8f3d6SRafael J. Wysocki
391324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, group_name);
392324a56e1STejun Heo if (!parent)
3930bb8f3d6SRafael J. Wysocki return -ENOENT;
3940bb8f3d6SRafael J. Wysocki
395324a56e1STejun Heo error = sysfs_create_link_sd(parent, target, link_name);
396324a56e1STejun Heo kernfs_put(parent);
3970bb8f3d6SRafael J. Wysocki
3980bb8f3d6SRafael J. Wysocki return error;
3990bb8f3d6SRafael J. Wysocki }
4000bb8f3d6SRafael J. Wysocki EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
4010bb8f3d6SRafael J. Wysocki
4020bb8f3d6SRafael J. Wysocki /**
4030bb8f3d6SRafael J. Wysocki * sysfs_remove_link_from_group - remove a symlink from an attribute group.
4040bb8f3d6SRafael J. Wysocki * @kobj: The kobject containing the group.
4050bb8f3d6SRafael J. Wysocki * @group_name: The name of the group.
4060bb8f3d6SRafael J. Wysocki * @link_name: The name of the symlink to remove.
4070bb8f3d6SRafael J. Wysocki */
sysfs_remove_link_from_group(struct kobject * kobj,const char * group_name,const char * link_name)4080bb8f3d6SRafael J. Wysocki void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
4090bb8f3d6SRafael J. Wysocki const char *link_name)
4100bb8f3d6SRafael J. Wysocki {
411324a56e1STejun Heo struct kernfs_node *parent;
4120bb8f3d6SRafael J. Wysocki
413324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, group_name);
414324a56e1STejun Heo if (parent) {
415324a56e1STejun Heo kernfs_remove_by_name(parent, link_name);
416324a56e1STejun Heo kernfs_put(parent);
4170bb8f3d6SRafael J. Wysocki }
4180bb8f3d6SRafael J. Wysocki }
4190bb8f3d6SRafael J. Wysocki EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
42037c1c04cSJarkko Sakkinen
42137c1c04cSJarkko Sakkinen /**
4229255782fSSourabh Jain * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
4239255782fSSourabh Jain * to a group or an attribute
4249255782fSSourabh Jain * @kobj: The kobject containing the group.
4259255782fSSourabh Jain * @target_kobj: The target kobject.
4269255782fSSourabh Jain * @target_name: The name of the target group or attribute.
4279255782fSSourabh Jain * @symlink_name: The name of the symlink file (target_name will be
4289255782fSSourabh Jain * considered if symlink_name is NULL).
4299255782fSSourabh Jain */
compat_only_sysfs_link_entry_to_kobj(struct kobject * kobj,struct kobject * target_kobj,const char * target_name,const char * symlink_name)4309255782fSSourabh Jain int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
4319255782fSSourabh Jain struct kobject *target_kobj,
4329255782fSSourabh Jain const char *target_name,
4339255782fSSourabh Jain const char *symlink_name)
4349255782fSSourabh Jain {
43537c1c04cSJarkko Sakkinen struct kernfs_node *target;
43637c1c04cSJarkko Sakkinen struct kernfs_node *entry;
43737c1c04cSJarkko Sakkinen struct kernfs_node *link;
43837c1c04cSJarkko Sakkinen
43937c1c04cSJarkko Sakkinen /*
44037c1c04cSJarkko Sakkinen * We don't own @target_kobj and it may be removed at any time.
44137c1c04cSJarkko Sakkinen * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
44237c1c04cSJarkko Sakkinen * for details.
44337c1c04cSJarkko Sakkinen */
44437c1c04cSJarkko Sakkinen spin_lock(&sysfs_symlink_target_lock);
44537c1c04cSJarkko Sakkinen target = target_kobj->sd;
44637c1c04cSJarkko Sakkinen if (target)
44737c1c04cSJarkko Sakkinen kernfs_get(target);
44837c1c04cSJarkko Sakkinen spin_unlock(&sysfs_symlink_target_lock);
44937c1c04cSJarkko Sakkinen if (!target)
45037c1c04cSJarkko Sakkinen return -ENOENT;
45137c1c04cSJarkko Sakkinen
452d3ce1979SGeert Uytterhoeven entry = kernfs_find_and_get(target, target_name);
45337c1c04cSJarkko Sakkinen if (!entry) {
45437c1c04cSJarkko Sakkinen kernfs_put(target);
45537c1c04cSJarkko Sakkinen return -ENOENT;
45637c1c04cSJarkko Sakkinen }
45737c1c04cSJarkko Sakkinen
4589255782fSSourabh Jain if (!symlink_name)
4599255782fSSourabh Jain symlink_name = target_name;
4609255782fSSourabh Jain
4619255782fSSourabh Jain link = kernfs_create_link(kobj->sd, symlink_name, entry);
46245586c70SMasahiro Yamada if (PTR_ERR(link) == -EEXIST)
4639255782fSSourabh Jain sysfs_warn_dup(kobj->sd, symlink_name);
46437c1c04cSJarkko Sakkinen
46537c1c04cSJarkko Sakkinen kernfs_put(entry);
46637c1c04cSJarkko Sakkinen kernfs_put(target);
46790b3d2f6SVasyl Gomonovych return PTR_ERR_OR_ZERO(link);
46837c1c04cSJarkko Sakkinen }
4699255782fSSourabh Jain EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
470303a4276SChristian Brauner
sysfs_group_attrs_change_owner(struct kernfs_node * grp_kn,const struct attribute_group * grp,struct iattr * newattrs)471303a4276SChristian Brauner static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,
472303a4276SChristian Brauner const struct attribute_group *grp,
473303a4276SChristian Brauner struct iattr *newattrs)
474303a4276SChristian Brauner {
475303a4276SChristian Brauner struct kernfs_node *kn;
476303a4276SChristian Brauner int error;
477303a4276SChristian Brauner
478303a4276SChristian Brauner if (grp->attrs) {
479303a4276SChristian Brauner struct attribute *const *attr;
480303a4276SChristian Brauner
481303a4276SChristian Brauner for (attr = grp->attrs; *attr; attr++) {
482303a4276SChristian Brauner kn = kernfs_find_and_get(grp_kn, (*attr)->name);
483303a4276SChristian Brauner if (!kn)
484303a4276SChristian Brauner return -ENOENT;
485303a4276SChristian Brauner
486303a4276SChristian Brauner error = kernfs_setattr(kn, newattrs);
487303a4276SChristian Brauner kernfs_put(kn);
488303a4276SChristian Brauner if (error)
489303a4276SChristian Brauner return error;
490303a4276SChristian Brauner }
491303a4276SChristian Brauner }
492303a4276SChristian Brauner
493303a4276SChristian Brauner if (grp->bin_attrs) {
494303a4276SChristian Brauner struct bin_attribute *const *bin_attr;
495303a4276SChristian Brauner
496303a4276SChristian Brauner for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
497303a4276SChristian Brauner kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
498303a4276SChristian Brauner if (!kn)
499303a4276SChristian Brauner return -ENOENT;
500303a4276SChristian Brauner
501303a4276SChristian Brauner error = kernfs_setattr(kn, newattrs);
502303a4276SChristian Brauner kernfs_put(kn);
503303a4276SChristian Brauner if (error)
504303a4276SChristian Brauner return error;
505303a4276SChristian Brauner }
506303a4276SChristian Brauner }
507303a4276SChristian Brauner
508303a4276SChristian Brauner return 0;
509303a4276SChristian Brauner }
510303a4276SChristian Brauner
511303a4276SChristian Brauner /**
512303a4276SChristian Brauner * sysfs_group_change_owner - change owner of an attribute group.
513303a4276SChristian Brauner * @kobj: The kobject containing the group.
514303a4276SChristian Brauner * @grp: The attribute group.
515303a4276SChristian Brauner * @kuid: new owner's kuid
516303a4276SChristian Brauner * @kgid: new owner's kgid
517303a4276SChristian Brauner *
518303a4276SChristian Brauner * Returns 0 on success or error code on failure.
519303a4276SChristian Brauner */
sysfs_group_change_owner(struct kobject * kobj,const struct attribute_group * grp,kuid_t kuid,kgid_t kgid)520303a4276SChristian Brauner int sysfs_group_change_owner(struct kobject *kobj,
521303a4276SChristian Brauner const struct attribute_group *grp, kuid_t kuid,
522303a4276SChristian Brauner kgid_t kgid)
523303a4276SChristian Brauner {
524303a4276SChristian Brauner struct kernfs_node *grp_kn;
525303a4276SChristian Brauner int error;
526303a4276SChristian Brauner struct iattr newattrs = {
527303a4276SChristian Brauner .ia_valid = ATTR_UID | ATTR_GID,
528303a4276SChristian Brauner .ia_uid = kuid,
529303a4276SChristian Brauner .ia_gid = kgid,
530303a4276SChristian Brauner };
531303a4276SChristian Brauner
532303a4276SChristian Brauner if (!kobj->state_in_sysfs)
533303a4276SChristian Brauner return -EINVAL;
534303a4276SChristian Brauner
535303a4276SChristian Brauner if (grp->name) {
536303a4276SChristian Brauner grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
537303a4276SChristian Brauner } else {
538303a4276SChristian Brauner kernfs_get(kobj->sd);
539303a4276SChristian Brauner grp_kn = kobj->sd;
540303a4276SChristian Brauner }
541303a4276SChristian Brauner if (!grp_kn)
542303a4276SChristian Brauner return -ENOENT;
543303a4276SChristian Brauner
544303a4276SChristian Brauner error = kernfs_setattr(grp_kn, &newattrs);
545303a4276SChristian Brauner if (!error)
546303a4276SChristian Brauner error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
547303a4276SChristian Brauner
548303a4276SChristian Brauner kernfs_put(grp_kn);
549303a4276SChristian Brauner
550303a4276SChristian Brauner return error;
551303a4276SChristian Brauner }
552303a4276SChristian Brauner EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
553303a4276SChristian Brauner
554303a4276SChristian Brauner /**
555303a4276SChristian Brauner * sysfs_groups_change_owner - change owner of a set of attribute groups.
556303a4276SChristian Brauner * @kobj: The kobject containing the groups.
557303a4276SChristian Brauner * @groups: The attribute groups.
558303a4276SChristian Brauner * @kuid: new owner's kuid
559303a4276SChristian Brauner * @kgid: new owner's kgid
560303a4276SChristian Brauner *
561303a4276SChristian Brauner * Returns 0 on success or error code on failure.
562303a4276SChristian Brauner */
sysfs_groups_change_owner(struct kobject * kobj,const struct attribute_group ** groups,kuid_t kuid,kgid_t kgid)563303a4276SChristian Brauner int sysfs_groups_change_owner(struct kobject *kobj,
564303a4276SChristian Brauner const struct attribute_group **groups,
565303a4276SChristian Brauner kuid_t kuid, kgid_t kgid)
566303a4276SChristian Brauner {
567303a4276SChristian Brauner int error = 0, i;
568303a4276SChristian Brauner
569303a4276SChristian Brauner if (!kobj->state_in_sysfs)
570303a4276SChristian Brauner return -EINVAL;
571303a4276SChristian Brauner
572303a4276SChristian Brauner if (!groups)
573303a4276SChristian Brauner return 0;
574303a4276SChristian Brauner
575303a4276SChristian Brauner for (i = 0; groups[i]; i++) {
576303a4276SChristian Brauner error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
577303a4276SChristian Brauner if (error)
578303a4276SChristian Brauner break;
579303a4276SChristian Brauner }
580303a4276SChristian Brauner
581303a4276SChristian Brauner return error;
582303a4276SChristian Brauner }
583303a4276SChristian Brauner EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);
584