2018-01-22 18:18:13 +03:00
// SPDX-License-Identifier: GPL-2.0
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
2013-08-22 03:14:11 +04:00
* Copyright ( c ) 2013 Greg Kroah - Hartman
* Copyright ( c ) 2013 The Linux Foundation
2005-04-17 02:20:36 +04:00
*/
# 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>
2020-02-27 06:37:13 +03:00
# include <linux/fs.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2014-05-05 17:17:20 +04:00
static void remove_files ( struct kernfs_node * parent ,
2007-06-13 23:27:22 +04:00
const struct attribute_group * grp )
2005-04-17 02:20:36 +04:00
{
2013-08-22 03:07:29 +04:00
struct attribute * const * attr ;
struct bin_attribute * const * bin_attr ;
2005-04-17 02:20:36 +04:00
2013-07-15 03:05:55 +04:00
if ( grp - > attrs )
for ( attr = grp - > attrs ; * attr ; attr + + )
2013-12-11 23:11:53 +04:00
kernfs_remove_by_name ( parent , ( * attr ) - > name ) ;
2013-07-15 03:05:55 +04:00
if ( grp - > bin_attrs )
for ( bin_attr = grp - > bin_attrs ; * bin_attr ; bin_attr + + )
2014-05-05 17:17:20 +04:00
kernfs_remove_by_name ( parent , ( * bin_attr ) - > attr . name ) ;
2005-04-17 02:20:36 +04:00
}
2013-12-11 23:11:53 +04:00
static int create_files ( struct kernfs_node * parent , struct kobject * kobj ,
2018-07-21 00:56:48 +03:00
kuid_t uid , kgid_t gid ,
2008-03-21 04:47:52 +03:00
const struct attribute_group * grp , int update )
2005-04-17 02:20:36 +04:00
{
2013-08-22 03:07:29 +04:00
struct attribute * const * attr ;
struct bin_attribute * const * bin_attr ;
2007-10-31 17:38:04 +03:00
int error = 0 , i ;
2005-04-17 02:20:36 +04:00
2013-07-15 03:05:55 +04:00
if ( grp - > attrs ) {
for ( i = 0 , attr = grp - > attrs ; * attr & & ! error ; i + + , attr + + ) {
2015-03-12 16:58:26 +03:00
umode_t mode = ( * attr ) - > mode ;
2008-03-21 04:47:52 +03:00
2013-07-15 03:05:55 +04: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 )
2013-12-11 23:11:53 +04:00
kernfs_remove_by_name ( parent , ( * attr ) - > name ) ;
2013-07-15 03:05:55 +04:00
if ( grp - > is_visible ) {
mode = grp - > is_visible ( kobj , * attr , i ) ;
if ( ! mode )
continue ;
}
2015-03-12 16:58:27 +03:00
WARN ( mode & ~ ( SYSFS_PREALLOC | 0664 ) ,
" Attribute %s: Invalid permissions 0%o \n " ,
( * attr ) - > name , mode ) ;
mode & = SYSFS_PREALLOC | 0664 ;
2021-09-13 08:41:12 +03:00
error = sysfs_add_file_mode_ns ( parent , * attr , mode , uid ,
gid , NULL ) ;
2013-07-15 03:05:55 +04:00
if ( unlikely ( error ) )
break ;
}
if ( error ) {
2014-05-05 17:17:20 +04:00
remove_files ( parent , grp ) ;
2013-07-15 03:05:55 +04:00
goto exit ;
}
}
if ( grp - > bin_attrs ) {
2015-09-21 16:38:20 +03:00
for ( i = 0 , bin_attr = grp - > bin_attrs ; * bin_attr ; i + + , bin_attr + + ) {
umode_t mode = ( * bin_attr ) - > attr . mode ;
2013-07-15 03:05:55 +04:00
if ( update )
2014-02-15 02:02:07 +04:00
kernfs_remove_by_name ( parent ,
( * bin_attr ) - > attr . name ) ;
2015-09-21 16:38:20 +03:00
if ( grp - > is_bin_visible ) {
mode = grp - > is_bin_visible ( kobj , * bin_attr , i ) ;
if ( ! mode )
continue ;
}
WARN ( mode & ~ ( SYSFS_PREALLOC | 0664 ) ,
" Attribute %s: Invalid permissions 0%o \n " ,
( * bin_attr ) - > attr . name , mode ) ;
mode & = SYSFS_PREALLOC | 0664 ;
2021-09-13 08:41:12 +03:00
error = sysfs_add_bin_file_mode_ns ( parent , * bin_attr ,
mode , uid , gid ,
NULL ) ;
2013-07-15 03:05:55 +04:00
if ( error )
break ;
2008-03-21 04:47:52 +03:00
}
2013-07-15 03:05:55 +04:00
if ( error )
2014-05-05 17:17:20 +04:00
remove_files ( parent , grp ) ;
2008-03-21 04:47:52 +03:00
}
2013-07-15 03:05:55 +04:00
exit :
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
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2018-07-21 00:56:48 +03:00
kuid_t uid ;
kgid_t gid ;
2005-04-17 02:20:36 +04:00
int error ;
2019-01-03 12:23:47 +03:00
if ( WARN_ON ( ! kobj | | ( ! update & & ! kobj - > sd ) ) )
return - EINVAL ;
2008-03-21 04:47:52 +03:00
/* Updates may happen before the object has been instantiated */
if ( unlikely ( update & & ! kobj - > sd ) )
return - EINVAL ;
2013-07-15 03:05:56 +04:00
if ( ! grp - > attrs & & ! grp - > bin_attrs ) {
WARN ( 1 , " sysfs: (bin_)attrs not set by subsystem for group: %s/%s \n " ,
2015-01-15 19:17:45 +03:00
kobj - > name , grp - > name ? : " " ) ;
2012-04-03 11:59:48 +04:00
return - EINVAL ;
}
2018-07-21 00:56:48 +03:00
kobject_get_ownership ( kobj , & uid , & gid ) ;
2005-04-17 02:20:36 +04:00
if ( grp - > name ) {
2018-06-16 20:49:46 +03:00
if ( update ) {
kn = kernfs_find_and_get ( kobj - > sd , grp - > name ) ;
if ( ! kn ) {
pr_warn ( " Can't update unknown attr grp name: %s/%s \n " ,
kobj - > name , grp - > name ) ;
return - EINVAL ;
}
} else {
2018-08-18 21:44:53 +03:00
kn = kernfs_create_dir_ns ( kobj - > sd , grp - > name ,
S_IRWXU | S_IRUGO | S_IXUGO ,
uid , gid , kobj , NULL ) ;
2018-06-16 20:49:46 +03:00
if ( IS_ERR ( kn ) ) {
if ( PTR_ERR ( kn ) = = - EEXIST )
sysfs_warn_dup ( kobj - > sd , grp - > name ) ;
return PTR_ERR ( kn ) ;
}
2013-11-28 23:54:15 +04:00
}
2005-04-17 02:20:36 +04:00
} else
2013-12-11 23:11:53 +04:00
kn = kobj - > sd ;
kernfs_get ( kn ) ;
2018-07-21 00:56:48 +03:00
error = create_files ( kn , kobj , uid , gid , grp , update ) ;
2007-06-13 23:27:22 +04:00
if ( error ) {
2005-04-17 02:20:36 +04:00
if ( grp - > name )
2013-12-11 23:11:53 +04:00
kernfs_remove ( kn ) ;
2005-04-17 02:20:36 +04:00
}
2013-12-11 23:11:53 +04:00
kernfs_put ( kn ) ;
2018-06-16 20:49:46 +03:00
if ( grp - > name & & update )
kernfs_put ( kn ) ;
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 .
*
2015-04-29 11:55:46 +03:00
* Returns 0 on success or error code on failure .
2008-03-21 04:47:52 +03:00
*/
int sysfs_create_group ( struct kobject * kobj ,
const struct attribute_group * grp )
{
return internal_create_group ( kobj , 0 , grp ) ;
}
2013-08-22 03:04:12 +04:00
EXPORT_SYMBOL_GPL ( sysfs_create_group ) ;
2008-03-21 04:47:52 +03:00
2019-05-12 18:55:10 +03:00
static int internal_create_groups ( struct kobject * kobj , int update ,
const struct attribute_group * * groups )
{
int error = 0 ;
int i ;
if ( ! groups )
return 0 ;
for ( i = 0 ; groups [ i ] ; i + + ) {
error = internal_create_group ( kobj , update , groups [ i ] ) ;
if ( error ) {
while ( - - i > = 0 )
sysfs_remove_group ( kobj , groups [ i ] ) ;
break ;
}
}
return error ;
}
2013-08-22 00:47:50 +04:00
/**
* sysfs_create_groups - given a directory kobject , create a bunch of attribute groups
* @ kobj : The kobject to create the group on
* @ groups : The attribute groups to create , NULL terminated
*
* This function creates a bunch of attribute groups . If an error occurs when
* creating a group , all previously created groups will be removed , unwinding
* everything back to the original state when this function was called .
* It will explicitly warn and error if any of the attribute files being
* created already exist .
*
2015-04-29 11:55:46 +03:00
* Returns 0 on success or error code from sysfs_create_group on failure .
2013-08-22 00:47:50 +04:00
*/
int sysfs_create_groups ( struct kobject * kobj ,
const struct attribute_group * * groups )
{
2019-05-12 18:55:10 +03:00
return internal_create_groups ( kobj , 0 , groups ) ;
2013-08-22 00:47:50 +04:00
}
EXPORT_SYMBOL_GPL ( sysfs_create_groups ) ;
2019-05-12 18:55:10 +03:00
/**
* sysfs_update_groups - given a directory kobject , create a bunch of attribute groups
* @ kobj : The kobject to update the group on
* @ groups : The attribute groups to update , NULL terminated
*
* This function update a bunch of attribute groups . If an error occurs when
* updating a group , all previously updated groups will be removed together
* with already existing ( not updated ) attributes .
*
* Returns 0 on success or error code from sysfs_update_group on failure .
*/
int sysfs_update_groups ( struct kobject * kobj ,
const struct attribute_group * * groups )
{
return internal_create_groups ( kobj , 1 , groups ) ;
}
EXPORT_SYMBOL_GPL ( sysfs_update_groups ) ;
2008-03-21 04:47:52 +03:00
/**
2011-05-08 01: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-21 04:47:52 +03: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
2018-06-16 20:49:46 +03:00
* relevant files . Changing a group ' s name ( subdirectory name under
* kobj ' s directory in sysfs ) is not allowed .
2008-03-21 04:47:52 +03:00
*
* The primary use for this function is to call it after making a change
* that affects group visibility .
*
2015-04-29 11:55:46 +03:00
* Returns 0 on success or error code on failure .
2008-03-21 04:47:52 +03:00
*/
int sysfs_update_group ( struct kobject * kobj ,
const struct attribute_group * grp )
{
return internal_create_group ( kobj , 1 , grp ) ;
}
2013-08-22 03:04:12 +04:00
EXPORT_SYMBOL_GPL ( sysfs_update_group ) ;
2008-03-21 04:47:52 +03:00
2013-08-22 03:12:34 +04:00
/**
* sysfs_remove_group : remove a group from a kobject
* @ kobj : kobject to remove the group from
* @ grp : group to remove
*
* This function removes a group of attributes from a kobject . The attributes
* previously have to have been created for this group , otherwise it will fail .
*/
2013-08-22 03:07:29 +04:00
void sysfs_remove_group ( struct kobject * kobj ,
const struct attribute_group * grp )
2005-04-17 02:20:36 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent = kobj - > sd ;
struct kernfs_node * kn ;
2005-04-17 02:20:36 +04:00
2007-04-26 11:12:05 +04:00
if ( grp - > name ) {
2013-12-11 23:11:53 +04:00
kn = kernfs_find_and_get ( parent , grp - > name ) ;
if ( ! kn ) {
WARN ( ! kn , KERN_WARNING
2016-09-19 11:14:54 +03:00
" sysfs group '%s' not found for kobject '%s' \n " ,
grp - > name , kobject_name ( kobj ) ) ;
2008-02-07 19:58:54 +03:00
return ;
}
2013-11-28 23:54:30 +04:00
} else {
2013-12-11 23:11:53 +04:00
kn = parent ;
kernfs_get ( kn ) ;
2013-11-28 23:54:30 +04:00
}
2005-04-17 02:20:36 +04:00
2014-05-05 17:17:20 +04:00
remove_files ( kn , grp ) ;
2005-04-17 02:20:36 +04:00
if ( grp - > name )
2013-12-11 23:11:53 +04:00
kernfs_remove ( kn ) ;
2007-06-13 23:27:22 +04:00
2013-12-11 23:11:53 +04:00
kernfs_put ( kn ) ;
2005-04-17 02:20:36 +04:00
}
2013-08-22 03:04:12 +04:00
EXPORT_SYMBOL_GPL ( sysfs_remove_group ) ;
2005-04-17 02:20:36 +04:00
2013-08-22 00:47:50 +04:00
/**
* sysfs_remove_groups - remove a list of groups
*
2013-08-22 03:12:34 +04:00
* @ kobj : The kobject for the groups to be removed from
* @ groups : NULL terminated list of groups to be removed
2013-08-22 00:47:50 +04:00
*
2013-08-22 20:23:28 +04:00
* If groups is not NULL , remove the specified groups from the kobject .
2013-08-22 00:47:50 +04:00
*/
void sysfs_remove_groups ( struct kobject * kobj ,
const struct attribute_group * * groups )
{
int i ;
if ( ! groups )
return ;
for ( i = 0 ; groups [ i ] ; i + + )
sysfs_remove_group ( kobj , groups [ i ] ) ;
}
EXPORT_SYMBOL_GPL ( sysfs_remove_groups ) ;
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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2018-07-21 00:56:48 +03:00
kuid_t uid ;
kgid_t gid ;
2010-09-26 01:34:22 +04:00
int error = 0 ;
struct attribute * const * attr ;
int i ;
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , grp - > name ) ;
if ( ! parent )
2010-09-26 01:34:22 +04:00
return - ENOENT ;
2018-07-21 00:56:48 +03:00
kobject_get_ownership ( kobj , & uid , & gid ) ;
2010-09-26 01:34:22 +04:00
for ( ( i = 0 , attr = grp - > attrs ) ; * attr & & ! error ; ( + + i , + + attr ) )
2021-09-13 08:41:12 +03:00
error = sysfs_add_file_mode_ns ( parent , * attr , ( * attr ) - > mode ,
uid , gid , NULL ) ;
2010-09-26 01:34:22 +04:00
if ( error ) {
while ( - - i > = 0 )
2013-12-11 23:11:53 +04:00
kernfs_remove_by_name ( parent , ( * - - attr ) - > name ) ;
2010-09-26 01:34:22 +04:00
}
2013-12-11 23:11:53 +04:00
kernfs_put ( parent ) ;
2010-09-26 01:34:22 +04:00
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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2010-09-26 01:34:22 +04:00
struct attribute * const * attr ;
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , grp - > name ) ;
if ( parent ) {
2010-09-26 01:34:22 +04:00
for ( attr = grp - > attrs ; * attr ; + + attr )
2013-12-11 23:11:53 +04:00
kernfs_remove_by_name ( parent , ( * attr ) - > name ) ;
kernfs_put ( parent ) ;
2010-09-26 01:34:22 +04:00
}
}
EXPORT_SYMBOL_GPL ( sysfs_unmerge_group ) ;
2013-01-26 00:51:13 +04: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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2013-01-26 00:51:13 +04:00
int error = 0 ;
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , group_name ) ;
if ( ! parent )
2013-01-26 00:51:13 +04:00
return - ENOENT ;
2013-12-11 23:11:53 +04:00
error = sysfs_create_link_sd ( parent , target , link_name ) ;
kernfs_put ( parent ) ;
2013-01-26 00:51:13 +04:00
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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2013-01-26 00:51:13 +04:00
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , group_name ) ;
if ( parent ) {
kernfs_remove_by_name ( parent , link_name ) ;
kernfs_put ( parent ) ;
2013-01-26 00:51:13 +04:00
}
}
EXPORT_SYMBOL_GPL ( sysfs_remove_link_from_group ) ;
2015-04-22 19:36:06 +03:00
2019-12-11 19:09:06 +03:00
/**
* compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
* to a group or an attribute
* @ kobj : The kobject containing the group .
* @ target_kobj : The target kobject .
* @ target_name : The name of the target group or attribute .
* @ symlink_name : The name of the symlink file ( target_name will be
* considered if symlink_name is NULL ) .
*/
int compat_only_sysfs_link_entry_to_kobj ( struct kobject * kobj ,
struct kobject * target_kobj ,
const char * target_name ,
const char * symlink_name )
2015-04-22 19:36:06 +03:00
{
struct kernfs_node * target ;
struct kernfs_node * entry ;
struct kernfs_node * link ;
/*
* We don ' t own @ target_kobj and it may be removed at any time .
* Synchronize using sysfs_symlink_target_lock . See sysfs_remove_dir ( )
* for details .
*/
spin_lock ( & sysfs_symlink_target_lock ) ;
target = target_kobj - > sd ;
if ( target )
kernfs_get ( target ) ;
spin_unlock ( & sysfs_symlink_target_lock ) ;
if ( ! target )
return - ENOENT ;
2021-07-14 18:15:59 +03:00
entry = kernfs_find_and_get ( target , target_name ) ;
2015-04-22 19:36:06 +03:00
if ( ! entry ) {
kernfs_put ( target ) ;
return - ENOENT ;
}
2019-12-11 19:09:06 +03:00
if ( ! symlink_name )
symlink_name = target_name ;
link = kernfs_create_link ( kobj - > sd , symlink_name , entry ) ;
2020-02-04 04:37:45 +03:00
if ( PTR_ERR ( link ) = = - EEXIST )
2019-12-11 19:09:06 +03:00
sysfs_warn_dup ( kobj - > sd , symlink_name ) ;
2015-04-22 19:36:06 +03:00
kernfs_put ( entry ) ;
kernfs_put ( target ) ;
2017-11-28 02:10:32 +03:00
return PTR_ERR_OR_ZERO ( link ) ;
2015-04-22 19:36:06 +03:00
}
2019-12-11 19:09:06 +03:00
EXPORT_SYMBOL_GPL ( compat_only_sysfs_link_entry_to_kobj ) ;
2020-02-27 06:37:13 +03:00
static int sysfs_group_attrs_change_owner ( struct kernfs_node * grp_kn ,
const struct attribute_group * grp ,
struct iattr * newattrs )
{
struct kernfs_node * kn ;
int error ;
if ( grp - > attrs ) {
struct attribute * const * attr ;
for ( attr = grp - > attrs ; * attr ; attr + + ) {
kn = kernfs_find_and_get ( grp_kn , ( * attr ) - > name ) ;
if ( ! kn )
return - ENOENT ;
error = kernfs_setattr ( kn , newattrs ) ;
kernfs_put ( kn ) ;
if ( error )
return error ;
}
}
if ( grp - > bin_attrs ) {
struct bin_attribute * const * bin_attr ;
for ( bin_attr = grp - > bin_attrs ; * bin_attr ; bin_attr + + ) {
kn = kernfs_find_and_get ( grp_kn , ( * bin_attr ) - > attr . name ) ;
if ( ! kn )
return - ENOENT ;
error = kernfs_setattr ( kn , newattrs ) ;
kernfs_put ( kn ) ;
if ( error )
return error ;
}
}
return 0 ;
}
/**
* sysfs_group_change_owner - change owner of an attribute group .
* @ kobj : The kobject containing the group .
* @ grp : The attribute group .
* @ kuid : new owner ' s kuid
* @ kgid : new owner ' s kgid
*
* Returns 0 on success or error code on failure .
*/
int sysfs_group_change_owner ( struct kobject * kobj ,
const struct attribute_group * grp , kuid_t kuid ,
kgid_t kgid )
{
struct kernfs_node * grp_kn ;
int error ;
struct iattr newattrs = {
. ia_valid = ATTR_UID | ATTR_GID ,
. ia_uid = kuid ,
. ia_gid = kgid ,
} ;
if ( ! kobj - > state_in_sysfs )
return - EINVAL ;
if ( grp - > name ) {
grp_kn = kernfs_find_and_get ( kobj - > sd , grp - > name ) ;
} else {
kernfs_get ( kobj - > sd ) ;
grp_kn = kobj - > sd ;
}
if ( ! grp_kn )
return - ENOENT ;
error = kernfs_setattr ( grp_kn , & newattrs ) ;
if ( ! error )
error = sysfs_group_attrs_change_owner ( grp_kn , grp , & newattrs ) ;
kernfs_put ( grp_kn ) ;
return error ;
}
EXPORT_SYMBOL_GPL ( sysfs_group_change_owner ) ;
/**
* sysfs_groups_change_owner - change owner of a set of attribute groups .
* @ kobj : The kobject containing the groups .
* @ groups : The attribute groups .
* @ kuid : new owner ' s kuid
* @ kgid : new owner ' s kgid
*
* Returns 0 on success or error code on failure .
*/
int sysfs_groups_change_owner ( struct kobject * kobj ,
const struct attribute_group * * groups ,
kuid_t kuid , kgid_t kgid )
{
int error = 0 , i ;
if ( ! kobj - > state_in_sysfs )
return - EINVAL ;
if ( ! groups )
return 0 ;
for ( i = 0 ; groups [ i ] ; i + + ) {
error = sysfs_group_change_owner ( kobj , groups [ i ] , kuid , kgid ) ;
if ( error )
break ;
}
return error ;
}
EXPORT_SYMBOL_GPL ( sysfs_groups_change_owner ) ;