xref: /openbmc/linux/fs/sysfs/group.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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