2005-04-17 02:20:36 +04: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 11:09:12 +04:00
# include <linux/namei.h>
2005-04-17 02:20:36 +04:00
# include <linux/err.h>
# include "sysfs.h"
2007-10-31 17:38:04 +03:00
static void remove_files ( struct sysfs_dirent * dir_sd , struct kobject * kobj ,
2007-06-13 23:27:22 +04:00
const struct attribute_group * grp )
2005-04-17 02:20:36 +04:00
{
struct attribute * const * attr ;
2007-10-31 17:38:04 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2007-10-31 17:38:04 +03:00
for ( i = 0 , attr = grp - > attrs ; * attr ; i + + , attr + + )
2010-03-30 22:31:26 +04:00
sysfs_hash_and_remove ( dir_sd , NULL , ( * attr ) - > name ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-31 17:38:04 +03:00
static int create_files ( struct sysfs_dirent * dir_sd , struct kobject * kobj ,
2008-03-21 04:47:52 +03:00
const struct attribute_group * grp , int update )
2005-04-17 02:20:36 +04:00
{
struct attribute * const * attr ;
2007-10-31 17:38:04 +03:00
int error = 0 , i ;
2005-04-17 02:20:36 +04:00
2008-03-21 04:47:52 +03:00
for ( i = 0 , attr = grp - > attrs ; * attr & & ! error ; i + + , attr + + ) {
mode_t mode = 0 ;
/* in update mode, we're changing the permissions or
* visibility . Do this by first removing then
* re - adding ( if required ) the file */
if ( update )
2010-03-30 22:31:26 +04:00
sysfs_hash_and_remove ( dir_sd , NULL , ( * attr ) - > name ) ;
2008-03-21 04:47:52 +03:00
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 ;
}
2005-04-17 02:20:36 +04:00
if ( error )
2007-10-31 17:38:04 +03:00
remove_files ( dir_sd , kobj , grp ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2008-03-21 04:47:52 +03:00
static int internal_create_group ( struct kobject * kobj , int update ,
const struct attribute_group * grp )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sd ;
2005-04-17 02:20:36 +04:00
int error ;
2008-03-21 04:47:52 +03:00
BUG_ON ( ! kobj | | ( ! update & & ! kobj - > sd ) ) ;
/* Updates may happen before the object has been instantiated */
if ( unlikely ( update & & ! kobj - > sd ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( grp - > name ) {
2007-06-13 23:27:22 +04:00
error = sysfs_create_subdir ( kobj , grp - > name , & sd ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return error ;
} else
2007-06-13 23:27:22 +04:00
sd = kobj - > sd ;
sysfs_get ( sd ) ;
2008-03-21 04:47:52 +03:00
error = create_files ( sd , kobj , grp , update ) ;
2007-06-13 23:27:22 +04:00
if ( error ) {
2005-04-17 02:20:36 +04:00
if ( grp - > name )
2007-06-13 23:27:22 +04:00
sysfs_remove_subdir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
sysfs_put ( sd ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2008-03-21 04:47:52 +03: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 ) ;
}
/**
* sysfs_update_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 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-17 02:20:36 +04:00
void sysfs_remove_group ( struct kobject * kobj ,
const struct attribute_group * grp )
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * dir_sd = kobj - > sd ;
struct sysfs_dirent * sd ;
2005-04-17 02:20:36 +04:00
2007-04-26 11:12:05 +04:00
if ( grp - > name ) {
2010-03-30 22:31:26 +04:00
sd = sysfs_get_dirent ( dir_sd , NULL , grp - > name ) ;
2008-02-07 19:58:54 +03:00
if ( ! sd ) {
2008-07-26 06:45:41 +04:00
WARN ( ! sd , KERN_WARNING " sysfs group %p not found for "
2008-02-07 19:58:54 +03:00
" kobject '%s' \n " , grp , kobject_name ( kobj ) ) ;
return ;
}
2007-06-13 23:27:22 +04:00
} else
sd = sysfs_get ( dir_sd ) ;
2005-04-17 02:20:36 +04:00
2007-10-31 17:38:04 +03:00
remove_files ( sd , kobj , grp ) ;
2005-04-17 02:20:36 +04:00
if ( grp - > name )
2007-06-13 23:27:22 +04:00
sysfs_remove_subdir ( sd ) ;
sysfs_put ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2010-09-26 01:34:22 +04: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 23:46:07 +03:00
dir_sd = sysfs_get_dirent ( kobj - > sd , NULL , grp - > name ) ;
2010-09-26 01:34:22 +04: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 23:46:07 +03:00
dir_sd = sysfs_get_dirent ( kobj - > sd , NULL , grp - > name ) ;
2010-09-26 01:34:22 +04: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 ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL_GPL ( sysfs_create_group ) ;
2008-03-21 04:47:52 +03:00
EXPORT_SYMBOL_GPL ( sysfs_update_group ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL_GPL ( sysfs_remove_group ) ;