| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * fs/sysfs/group.c - Operations for adding/removing multiple files at once. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2003 Patrick Mochel | 
					
						
							|  |  |  |  * Copyright (c) 2003 Open Source Development Lab | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is released undert the GPL v2.  | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kobject.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/dcache.h>
 | 
					
						
							| 
									
										
										
										
											2005-06-23 00:09:12 -07:00
										 |  |  | #include <linux/namei.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include "sysfs.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-31 09:38:04 -05:00
										 |  |  | static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 			 const struct attribute_group *grp) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct attribute *const* attr; | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 	struct bin_attribute *const* bin_attr; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 	if (grp->attrs) | 
					
						
							|  |  |  | 		for (attr = grp->attrs; *attr; attr++) | 
					
						
							|  |  |  | 			sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); | 
					
						
							|  |  |  | 	if (grp->bin_attrs) | 
					
						
							|  |  |  | 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) | 
					
						
							|  |  |  | 			sysfs_remove_bin_file(kobj, *bin_attr); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-31 09:38:04 -05:00
										 |  |  | static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 			const struct attribute_group *grp, int update) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct attribute *const* attr; | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 	struct bin_attribute *const* bin_attr; | 
					
						
							| 
									
										
										
										
											2007-10-31 09:38:04 -05:00
										 |  |  | 	int error = 0, i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 	if (grp->attrs) { | 
					
						
							|  |  |  | 		for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { | 
					
						
							|  |  |  | 			umode_t mode = 0; | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * In update mode, we're changing the permissions or | 
					
						
							|  |  |  | 			 * visibility.  Do this by first removing then | 
					
						
							|  |  |  | 			 * re-adding (if required) the file. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (update) | 
					
						
							|  |  |  | 				sysfs_hash_and_remove(dir_sd, NULL, | 
					
						
							|  |  |  | 						      (*attr)->name); | 
					
						
							|  |  |  | 			if (grp->is_visible) { | 
					
						
							|  |  |  | 				mode = grp->is_visible(kobj, *attr, i); | 
					
						
							|  |  |  | 				if (!mode) | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			error = sysfs_add_file_mode(dir_sd, *attr, | 
					
						
							|  |  |  | 						    SYSFS_KOBJ_ATTR, | 
					
						
							|  |  |  | 						    (*attr)->mode | mode); | 
					
						
							|  |  |  | 			if (unlikely(error)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (error) { | 
					
						
							|  |  |  | 			remove_files(dir_sd, kobj, grp); | 
					
						
							|  |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (grp->bin_attrs) { | 
					
						
							|  |  |  | 		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { | 
					
						
							|  |  |  | 			if (update) | 
					
						
							|  |  |  | 				sysfs_remove_bin_file(kobj, *bin_attr); | 
					
						
							|  |  |  | 			error = sysfs_create_bin_file(kobj, *bin_attr); | 
					
						
							|  |  |  | 			if (error) | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | 		if (error) | 
					
						
							|  |  |  | 			remove_files(dir_sd, kobj, grp); | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:55 -07:00
										 |  |  | exit: | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | static int internal_create_group(struct kobject *kobj, int update, | 
					
						
							|  |  |  | 				 const struct attribute_group *grp) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 	struct sysfs_dirent *sd; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 	BUG_ON(!kobj || (!update && !kobj->sd)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Updates may happen before the object has been instantiated */ | 
					
						
							|  |  |  | 	if (unlikely(update && !kobj->sd)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2013-07-14 16:05:56 -07:00
										 |  |  | 	if (!grp->attrs && !grp->bin_attrs) { | 
					
						
							|  |  |  | 		WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", | 
					
						
							| 
									
										
										
										
											2012-04-03 09:59:48 +02:00
										 |  |  | 			kobj->name, grp->name ? "" : grp->name); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (grp->name) { | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 		error = sysfs_create_subdir(kobj, grp->name, &sd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (error) | 
					
						
							|  |  |  | 			return error; | 
					
						
							|  |  |  | 	} else | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 		sd = kobj->sd; | 
					
						
							|  |  |  | 	sysfs_get(sd); | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | 	error = create_files(sd, kobj, grp, update); | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 	if (error) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (grp->name) | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 			sysfs_remove_subdir(sd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 	sysfs_put(sd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * sysfs_create_group - given a directory kobject, create an attribute group | 
					
						
							|  |  |  |  * @kobj:	The kobject to create the group on | 
					
						
							|  |  |  |  * @grp:	The attribute group to create | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function creates a group for the first time.  It will explicitly | 
					
						
							|  |  |  |  * warn and error if any of the attribute files being created already exist. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success or error. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int sysfs_create_group(struct kobject *kobj, | 
					
						
							|  |  |  | 		       const struct attribute_group *grp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return internal_create_group(kobj, 0, grp); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2011-05-07 17:18:20 -04:00
										 |  |  |  * sysfs_update_group - given a directory kobject, update an attribute group | 
					
						
							|  |  |  |  * @kobj:	The kobject to update the group on | 
					
						
							|  |  |  |  * @grp:	The attribute group to update | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This function updates an attribute group.  Unlike | 
					
						
							|  |  |  |  * sysfs_create_group(), it will explicitly not warn or error if any | 
					
						
							|  |  |  |  * of the attribute files being created already exist.  Furthermore, | 
					
						
							|  |  |  |  * if the visibility of the files has changed through the is_visible() | 
					
						
							|  |  |  |  * callback, it will update the permissions and add or remove the | 
					
						
							|  |  |  |  * relevant files. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The primary use for this function is to call it after making a change | 
					
						
							|  |  |  |  * that affects group visibility. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success or error. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int sysfs_update_group(struct kobject *kobj, | 
					
						
							|  |  |  | 		       const struct attribute_group *grp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return internal_create_group(kobj, 1, grp); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | void sysfs_remove_group(struct kobject * kobj,  | 
					
						
							|  |  |  | 			const struct attribute_group * grp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 	struct sysfs_dirent *dir_sd = kobj->sd; | 
					
						
							|  |  |  | 	struct sysfs_dirent *sd; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-26 00:12:05 -07:00
										 |  |  | 	if (grp->name) { | 
					
						
							| 
									
										
										
										
											2010-03-30 11:31:26 -07:00
										 |  |  | 		sd = sysfs_get_dirent(dir_sd, NULL, grp->name); | 
					
						
							| 
									
										
										
										
											2008-02-07 11:58:54 -05:00
										 |  |  | 		if (!sd) { | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:41 -07:00
										 |  |  | 			WARN(!sd, KERN_WARNING "sysfs group %p not found for " | 
					
						
							| 
									
										
										
										
											2008-02-07 11:58:54 -05:00
										 |  |  | 				"kobject '%s'\n", grp, kobject_name(kobj)); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 	} else | 
					
						
							|  |  |  | 		sd = sysfs_get(dir_sd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-31 09:38:04 -05:00
										 |  |  | 	remove_files(sd, kobj, grp); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (grp->name) | 
					
						
							| 
									
										
										
										
											2007-06-14 04:27:22 +09:00
										 |  |  | 		sysfs_remove_subdir(sd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sysfs_put(sd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-25 23:34:22 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * sysfs_merge_group - merge files into a pre-existing attribute group. | 
					
						
							|  |  |  |  * @kobj:	The kobject containing the group. | 
					
						
							|  |  |  |  * @grp:	The files to create and the attribute group they belong to. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function returns an error if the group doesn't exist or any of the | 
					
						
							|  |  |  |  * files already exist in that group, in which case none of the new files | 
					
						
							|  |  |  |  * are created. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int sysfs_merge_group(struct kobject *kobj, | 
					
						
							|  |  |  | 		       const struct attribute_group *grp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sysfs_dirent *dir_sd; | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 	struct attribute *const *attr; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-15 15:46:07 -05:00
										 |  |  | 	dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); | 
					
						
							| 
									
										
										
										
											2010-09-25 23:34:22 +02:00
										 |  |  | 	if (!dir_sd) | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) | 
					
						
							|  |  |  | 		error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); | 
					
						
							|  |  |  | 	if (error) { | 
					
						
							|  |  |  | 		while (--i >= 0) | 
					
						
							|  |  |  | 			sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sysfs_put(dir_sd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(sysfs_merge_group); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * sysfs_unmerge_group - remove files from a pre-existing attribute group. | 
					
						
							|  |  |  |  * @kobj:	The kobject containing the group. | 
					
						
							|  |  |  |  * @grp:	The files to remove and the attribute group they belong to. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void sysfs_unmerge_group(struct kobject *kobj, | 
					
						
							|  |  |  | 		       const struct attribute_group *grp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sysfs_dirent *dir_sd; | 
					
						
							|  |  |  | 	struct attribute *const *attr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-15 15:46:07 -05:00
										 |  |  | 	dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); | 
					
						
							| 
									
										
										
										
											2010-09-25 23:34:22 +02:00
										 |  |  | 	if (dir_sd) { | 
					
						
							|  |  |  | 		for (attr = grp->attrs; *attr; ++attr) | 
					
						
							|  |  |  | 			sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); | 
					
						
							|  |  |  | 		sysfs_put(dir_sd); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(sysfs_unmerge_group); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-25 21:51:13 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * sysfs_add_link_to_group - add a symlink to an attribute group. | 
					
						
							|  |  |  |  * @kobj:	The kobject containing the group. | 
					
						
							|  |  |  |  * @group_name:	The name of the group. | 
					
						
							|  |  |  |  * @target:	The target kobject of the symlink to create. | 
					
						
							|  |  |  |  * @link_name:	The name of the symlink to create. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, | 
					
						
							|  |  |  | 			    struct kobject *target, const char *link_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sysfs_dirent *dir_sd; | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); | 
					
						
							|  |  |  | 	if (!dir_sd) | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = sysfs_create_link_sd(dir_sd, target, link_name); | 
					
						
							|  |  |  | 	sysfs_put(dir_sd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * sysfs_remove_link_from_group - remove a symlink from an attribute group. | 
					
						
							|  |  |  |  * @kobj:	The kobject containing the group. | 
					
						
							|  |  |  |  * @group_name:	The name of the group. | 
					
						
							|  |  |  |  * @link_name:	The name of the symlink to remove. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, | 
					
						
							|  |  |  | 				  const char *link_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sysfs_dirent *dir_sd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); | 
					
						
							|  |  |  | 	if (dir_sd) { | 
					
						
							|  |  |  | 		sysfs_hash_and_remove(dir_sd, NULL, link_name); | 
					
						
							|  |  |  | 		sysfs_put(dir_sd); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(sysfs_create_group); | 
					
						
							| 
									
										
										
										
											2008-03-20 20:47:52 -05:00
										 |  |  | EXPORT_SYMBOL_GPL(sysfs_update_group); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | EXPORT_SYMBOL_GPL(sysfs_remove_group); |