2005-04-16 15:20:36 -07:00
/*
* dir . c - Operations for sysfs directories .
*/
# undef DEBUG
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/module.h>
# include <linux/kobject.h>
2005-06-23 00:09:12 -07:00
# include <linux/namei.h>
2007-06-14 03:45:13 +09:00
# include <linux/idr.h>
2006-12-20 10:52:44 +01:00
# include <asm/semaphore.h>
2005-04-16 15:20:36 -07:00
# include "sysfs.h"
DECLARE_RWSEM ( sysfs_rename_sem ) ;
2007-06-11 14:04:01 +09:00
spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED ;
2007-06-14 03:45:15 +09:00
spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:13 +09:00
static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED ;
static DEFINE_IDA ( sysfs_ino_ida ) ;
int sysfs_alloc_ino ( ino_t * pino )
{
int ino , rc ;
retry :
spin_lock ( & sysfs_ino_lock ) ;
rc = ida_get_new_above ( & sysfs_ino_ida , 2 , & ino ) ;
spin_unlock ( & sysfs_ino_lock ) ;
if ( rc = = - EAGAIN ) {
if ( ida_pre_get ( & sysfs_ino_ida , GFP_KERNEL ) )
goto retry ;
rc = - ENOMEM ;
}
* pino = ino ;
return rc ;
}
static void sysfs_free_ino ( ino_t ino )
{
spin_lock ( & sysfs_ino_lock ) ;
ida_remove ( & sysfs_ino_ida , ino ) ;
spin_unlock ( & sysfs_ino_lock ) ;
}
2007-06-14 03:45:13 +09:00
void release_sysfs_dirent ( struct sysfs_dirent * sd )
{
2007-06-14 03:45:14 +09:00
struct sysfs_dirent * parent_sd ;
repeat :
parent_sd = sd - > s_parent ;
2007-06-14 03:45:16 +09:00
/* If @sd is being released after deletion, s_active is write
* locked . If @ sd is cursor for directory walk or being
* released prematurely , s_active has no reader or writer .
*
* sysfs_deactivate ( ) lies to lockdep that s_active is
* unlocked immediately . Lie one more time to cover the
* previous lie .
*/
if ( ! down_write_trylock ( & sd - > s_active ) )
rwsem_acquire ( & sd - > s_active . dep_map ,
SYSFS_S_ACTIVE_DEACTIVATE , 0 , _RET_IP_ ) ;
up_write ( & sd - > s_active ) ;
2007-06-14 03:45:15 +09:00
if ( sd - > s_type & SYSFS_KOBJ_LINK )
2007-06-14 03:45:15 +09:00
sysfs_put ( sd - > s_elem . symlink . target_sd ) ;
2007-06-14 03:45:15 +09:00
if ( sd - > s_type & SYSFS_COPY_NAME )
kfree ( sd - > s_name ) ;
2007-06-14 03:45:13 +09:00
kfree ( sd - > s_iattr ) ;
2007-06-14 03:45:13 +09:00
sysfs_free_ino ( sd - > s_ino ) ;
2007-06-14 03:45:13 +09:00
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
2007-06-14 03:45:14 +09:00
sd = parent_sd ;
if ( sd & & atomic_dec_and_test ( & sd - > s_count ) )
goto repeat ;
2007-06-14 03:45:13 +09:00
}
2005-04-16 15:20:36 -07:00
static void sysfs_d_iput ( struct dentry * dentry , struct inode * inode )
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
if ( sd ) {
2007-06-11 14:04:01 +09:00
/* sd->s_dentry is protected with sysfs_lock. This
* allows sysfs_drop_dentry ( ) to dereference it .
*/
spin_lock ( & sysfs_lock ) ;
/* The dentry might have been deleted or another
* lookup could have happened updating sd - > s_dentry to
* point the new dentry . Ignore if it isn ' t pointing
* to this dentry .
*/
if ( sd - > s_dentry = = dentry )
sd - > s_dentry = NULL ;
spin_unlock ( & sysfs_lock ) ;
2005-04-16 15:20:36 -07:00
sysfs_put ( sd ) ;
}
iput ( inode ) ;
}
static struct dentry_operations sysfs_dentry_ops = {
. d_iput = sysfs_d_iput ,
} ;
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * sysfs_new_dirent ( const char * name , umode_t mode , int type )
2005-04-16 15:20:36 -07:00
{
2007-06-14 03:45:15 +09:00
char * dup_name = NULL ;
struct sysfs_dirent * sd = NULL ;
if ( type & SYSFS_COPY_NAME ) {
name = dup_name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! name )
goto err_out ;
}
2005-04-16 15:20:36 -07:00
2007-02-10 01:45:03 -08:00
sd = kmem_cache_zalloc ( sysfs_dir_cachep , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! sd )
2007-06-14 03:45:15 +09:00
goto err_out ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:15 +09:00
if ( sysfs_alloc_ino ( & sd - > s_ino ) )
goto err_out ;
2007-06-14 03:45:13 +09:00
2005-04-16 15:20:36 -07:00
atomic_set ( & sd - > s_count , 1 ) ;
2006-08-03 19:06:25 +03:00
atomic_set ( & sd - > s_event , 1 ) ;
2007-06-14 03:45:16 +09:00
init_rwsem ( & sd - > s_active ) ;
2005-04-16 15:20:36 -07:00
INIT_LIST_HEAD ( & sd - > s_children ) ;
2007-01-24 12:35:52 -07:00
INIT_LIST_HEAD ( & sd - > s_sibling ) ;
2007-06-14 03:45:14 +09:00
2007-06-14 03:45:15 +09:00
sd - > s_name = name ;
2007-06-14 03:45:14 +09:00
sd - > s_mode = mode ;
sd - > s_type = type ;
2005-04-16 15:20:36 -07:00
return sd ;
2007-06-14 03:45:15 +09:00
err_out :
kfree ( dup_name ) ;
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
return NULL ;
2005-04-16 15:20:36 -07:00
}
2007-06-14 03:45:14 +09:00
void sysfs_attach_dirent ( struct sysfs_dirent * sd ,
struct sysfs_dirent * parent_sd , struct dentry * dentry )
2007-01-24 12:35:52 -07:00
{
2007-06-14 03:45:14 +09:00
if ( dentry ) {
sd - > s_dentry = dentry ;
dentry - > d_fsdata = sysfs_get ( sd ) ;
dentry - > d_op = & sysfs_dentry_ops ;
}
2007-01-24 12:35:52 -07:00
2007-06-14 03:45:14 +09:00
if ( parent_sd ) {
sd - > s_parent = sysfs_get ( parent_sd ) ;
2007-06-14 03:45:14 +09:00
list_add ( & sd - > s_sibling , & parent_sd - > s_children ) ;
2007-06-14 03:45:14 +09:00
}
2007-01-24 12:35:52 -07:00
}
2006-04-02 13:59:55 +02:00
/*
2006-03-09 19:40:14 +05:30
*
* Return - EEXIST if there is already a sysfs element with the same name for
* the same parent .
*
* called with parent inode ' s i_mutex held
*/
int sysfs_dirent_exist ( struct sysfs_dirent * parent_sd ,
const unsigned char * new )
{
struct sysfs_dirent * sd ;
list_for_each_entry ( sd , & parent_sd - > s_children , s_sibling ) {
2007-06-14 03:45:15 +09:00
if ( sd - > s_type ) {
2007-06-14 03:45:15 +09:00
if ( strcmp ( sd - > s_name , new ) )
2006-03-09 19:40:14 +05:30
continue ;
else
return - EEXIST ;
}
}
return 0 ;
}
2005-04-16 15:20:36 -07:00
static int init_dir ( struct inode * inode )
{
inode - > i_op = & sysfs_dir_inode_operations ;
inode - > i_fop = & sysfs_dir_operations ;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
2006-09-30 23:29:04 -07:00
inc_nlink ( inode ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int init_file ( struct inode * inode )
{
inode - > i_size = PAGE_SIZE ;
inode - > i_fop = & sysfs_file_operations ;
return 0 ;
}
static int init_symlink ( struct inode * inode )
{
inode - > i_op = & sysfs_symlink_inode_operations ;
return 0 ;
}
2007-06-14 03:45:14 +09:00
static int create_dir ( struct kobject * kobj , struct dentry * parent ,
const char * name , struct dentry * * p_dentry )
2005-04-16 15:20:36 -07:00
{
int error ;
umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ;
2007-06-14 03:45:14 +09:00
struct dentry * dentry ;
struct sysfs_dirent * sd ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:14 +09:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
dentry = lookup_one_len ( name , parent , strlen ( name ) ) ;
if ( IS_ERR ( dentry ) ) {
error = PTR_ERR ( dentry ) ;
goto out_unlock ;
}
error = - EEXIST ;
if ( sysfs_dirent_exist ( parent - > d_fsdata , name ) )
goto out_dput ;
2007-06-14 03:45:14 +09:00
error = - ENOMEM ;
2007-06-14 03:45:15 +09:00
sd = sysfs_new_dirent ( name , mode , SYSFS_DIR ) ;
2007-06-14 03:45:14 +09:00
if ( ! sd )
2007-06-14 03:45:14 +09:00
goto out_drop ;
2007-06-14 03:45:15 +09:00
sd - > s_elem . dir . kobj = kobj ;
2007-06-14 03:45:14 +09:00
sysfs_attach_dirent ( sd , parent - > d_fsdata , dentry ) ;
2007-06-14 03:45:14 +09:00
error = sysfs_create ( dentry , mode , init_dir ) ;
if ( error )
goto out_sput ;
inc_nlink ( parent - > d_inode ) ;
dentry - > d_op = & sysfs_dentry_ops ;
d_rehash ( dentry ) ;
* p_dentry = dentry ;
error = 0 ;
goto out_dput ;
out_sput :
list_del_init ( & sd - > s_sibling ) ;
sysfs_put ( sd ) ;
out_drop :
d_drop ( dentry ) ;
out_dput :
dput ( dentry ) ;
out_unlock :
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
int sysfs_create_subdir ( struct kobject * k , const char * n , struct dentry * * d )
{
return create_dir ( k , k - > dentry , n , d ) ;
}
/**
* sysfs_create_dir - create a directory for an object .
* @ kobj : object we ' re creating directory for .
2007-01-24 12:35:52 -07:00
* @ shadow_parent : parent parent object .
2005-04-16 15:20:36 -07:00
*/
2007-01-24 12:35:52 -07:00
int sysfs_create_dir ( struct kobject * kobj , struct dentry * shadow_parent )
2005-04-16 15:20:36 -07:00
{
struct dentry * dentry = NULL ;
struct dentry * parent ;
int error = 0 ;
BUG_ON ( ! kobj ) ;
2007-01-24 12:35:52 -07:00
if ( shadow_parent )
parent = shadow_parent ;
else if ( kobj - > parent )
2005-04-16 15:20:36 -07:00
parent = kobj - > parent - > dentry ;
else if ( sysfs_mount & & sysfs_mount - > mnt_sb )
parent = sysfs_mount - > mnt_sb - > s_root ;
else
return - EFAULT ;
error = create_dir ( kobj , parent , kobject_name ( kobj ) , & dentry ) ;
if ( ! error )
kobj - > dentry = dentry ;
return error ;
}
/* attaches attribute's sysfs_dirent to the dentry corresponding to the
* attribute file
*/
static int sysfs_attach_attr ( struct sysfs_dirent * sd , struct dentry * dentry )
{
struct attribute * attr = NULL ;
struct bin_attribute * bin_attr = NULL ;
int ( * init ) ( struct inode * ) = NULL ;
int error = 0 ;
if ( sd - > s_type & SYSFS_KOBJ_BIN_ATTR ) {
2007-06-14 03:45:15 +09:00
bin_attr = sd - > s_elem . bin_attr . bin_attr ;
2005-04-16 15:20:36 -07:00
attr = & bin_attr - > attr ;
} else {
2007-06-14 03:45:15 +09:00
attr = sd - > s_elem . attr . attr ;
2005-04-16 15:20:36 -07:00
init = init_file ;
}
2005-05-31 10:38:12 +05:30
dentry - > d_fsdata = sysfs_get ( sd ) ;
2007-06-11 14:04:01 +09:00
/* protect sd->s_dentry against sysfs_d_iput */
spin_lock ( & sysfs_lock ) ;
2005-05-31 10:38:12 +05:30
sd - > s_dentry = dentry ;
2007-06-11 14:04:01 +09:00
spin_unlock ( & sysfs_lock ) ;
2005-04-16 15:20:36 -07:00
error = sysfs_create ( dentry , ( attr - > mode & S_IALLUGO ) | S_IFREG , init ) ;
2005-05-31 10:38:12 +05:30
if ( error ) {
sysfs_put ( sd ) ;
2005-04-16 15:20:36 -07:00
return error ;
2005-05-31 10:38:12 +05:30
}
2005-04-16 15:20:36 -07:00
if ( bin_attr ) {
dentry - > d_inode - > i_size = bin_attr - > size ;
dentry - > d_inode - > i_fop = & bin_fops ;
}
dentry - > d_op = & sysfs_dentry_ops ;
d_rehash ( dentry ) ;
return 0 ;
}
static int sysfs_attach_link ( struct sysfs_dirent * sd , struct dentry * dentry )
{
int err = 0 ;
2005-05-31 10:38:12 +05:30
dentry - > d_fsdata = sysfs_get ( sd ) ;
2007-06-11 14:04:01 +09:00
/* protect sd->s_dentry against sysfs_d_iput */
spin_lock ( & sysfs_lock ) ;
2005-05-31 10:38:12 +05:30
sd - > s_dentry = dentry ;
2007-06-11 14:04:01 +09:00
spin_unlock ( & sysfs_lock ) ;
2005-04-16 15:20:36 -07:00
err = sysfs_create ( dentry , S_IFLNK | S_IRWXUGO , init_symlink ) ;
if ( ! err ) {
dentry - > d_op = & sysfs_dentry_ops ;
d_rehash ( dentry ) ;
2005-05-31 10:38:12 +05:30
} else
sysfs_put ( sd ) ;
2005-04-16 15:20:36 -07:00
return err ;
}
static struct dentry * sysfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
struct sysfs_dirent * parent_sd = dentry - > d_parent - > d_fsdata ;
struct sysfs_dirent * sd ;
int err = 0 ;
list_for_each_entry ( sd , & parent_sd - > s_children , s_sibling ) {
if ( sd - > s_type & SYSFS_NOT_PINNED ) {
2007-06-14 03:45:15 +09:00
if ( strcmp ( sd - > s_name , dentry - > d_name . name ) )
2005-04-16 15:20:36 -07:00
continue ;
if ( sd - > s_type & SYSFS_KOBJ_LINK )
err = sysfs_attach_link ( sd , dentry ) ;
else
err = sysfs_attach_attr ( sd , dentry ) ;
break ;
}
}
return ERR_PTR ( err ) ;
}
2007-02-12 00:55:40 -08:00
const struct inode_operations sysfs_dir_inode_operations = {
2005-04-16 15:20:36 -07:00
. lookup = sysfs_lookup ,
2005-05-31 10:39:14 +05:30
. setattr = sysfs_setattr ,
2005-04-16 15:20:36 -07:00
} ;
static void remove_dir ( struct dentry * d )
{
struct dentry * parent = dget ( d - > d_parent ) ;
struct sysfs_dirent * sd ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
d_delete ( d ) ;
sd = d - > d_fsdata ;
list_del_init ( & sd - > s_sibling ) ;
if ( d - > d_inode )
simple_rmdir ( parent - > d_inode , d ) ;
pr_debug ( " o %s removing done (%d) \n " , d - > d_name . name ,
atomic_read ( & d - > d_count ) ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
dput ( parent ) ;
2007-06-14 03:45:16 +09:00
sysfs_deactivate ( sd ) ;
sysfs_put ( sd ) ;
2005-04-16 15:20:36 -07:00
}
void sysfs_remove_subdir ( struct dentry * d )
{
remove_dir ( d ) ;
}
2007-01-24 12:35:52 -07:00
static void __sysfs_remove_dir ( struct dentry * dentry )
2005-04-16 15:20:36 -07:00
{
2007-06-14 03:45:16 +09:00
LIST_HEAD ( removed ) ;
2005-04-16 15:20:36 -07:00
struct sysfs_dirent * parent_sd ;
struct sysfs_dirent * sd , * tmp ;
2007-01-24 12:35:52 -07:00
dget ( dentry ) ;
2005-04-16 15:20:36 -07:00
if ( ! dentry )
return ;
pr_debug ( " sysfs %s: removing dir \n " , dentry - > d_name . name ) ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
parent_sd = dentry - > d_fsdata ;
list_for_each_entry_safe ( sd , tmp , & parent_sd - > s_children , s_sibling ) {
2007-06-14 03:45:15 +09:00
if ( ! sd - > s_type | | ! ( sd - > s_type & SYSFS_NOT_PINNED ) )
2005-04-16 15:20:36 -07:00
continue ;
2007-06-14 03:45:16 +09:00
list_move ( & sd - > s_sibling , & removed ) ;
2005-04-16 15:20:36 -07:00
sysfs_drop_dentry ( sd , dentry ) ;
}
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:16 +09:00
list_for_each_entry_safe ( sd , tmp , & removed , s_sibling ) {
list_del_init ( & sd - > s_sibling ) ;
sysfs_deactivate ( sd ) ;
sysfs_put ( sd ) ;
}
2005-04-16 15:20:36 -07:00
remove_dir ( dentry ) ;
/**
* Drop reference from dget ( ) on entrance .
*/
dput ( dentry ) ;
2007-01-24 12:35:52 -07:00
}
/**
* sysfs_remove_dir - remove an object ' s directory .
* @ kobj : object .
*
* The only thing special about this is that we remove any files in
* the directory before we remove the directory , and we ' ve inlined
* what used to be sysfs_rmdir ( ) below , instead of calling separately .
*/
void sysfs_remove_dir ( struct kobject * kobj )
{
2007-06-14 03:45:15 +09:00
struct dentry * d = kobj - > dentry ;
spin_lock ( & kobj_sysfs_assoc_lock ) ;
2006-03-16 15:44:26 -08:00
kobj - > dentry = NULL ;
2007-06-14 03:45:15 +09:00
spin_unlock ( & kobj_sysfs_assoc_lock ) ;
__sysfs_remove_dir ( d ) ;
2005-04-16 15:20:36 -07:00
}
2007-01-24 12:35:52 -07:00
int sysfs_rename_dir ( struct kobject * kobj , struct dentry * new_parent ,
const char * new_name )
2005-04-16 15:20:36 -07:00
{
2007-06-14 03:45:15 +09:00
struct sysfs_dirent * sd = kobj - > dentry - > d_fsdata ;
struct sysfs_dirent * parent_sd = new_parent - > d_fsdata ;
struct dentry * new_dentry ;
char * dup_name ;
2007-06-14 03:45:14 +09:00
int error ;
2005-04-16 15:20:36 -07:00
2007-01-24 12:35:52 -07:00
if ( ! new_parent )
return - EFAULT ;
2005-04-16 15:20:36 -07:00
down_write ( & sysfs_rename_sem ) ;
2007-01-24 12:35:52 -07:00
mutex_lock ( & new_parent - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
2007-01-24 12:35:52 -07:00
new_dentry = lookup_one_len ( new_name , new_parent , strlen ( new_name ) ) ;
2007-06-14 03:45:14 +09:00
if ( IS_ERR ( new_dentry ) ) {
error = PTR_ERR ( new_dentry ) ;
goto out_unlock ;
2005-04-16 15:20:36 -07:00
}
2007-06-14 03:45:14 +09:00
/* By allowing two different directories with the same
* d_parent we allow this routine to move between different
* shadows of the same directory
*/
error = - EINVAL ;
if ( kobj - > dentry - > d_parent - > d_inode ! = new_parent - > d_inode | |
new_dentry - > d_parent - > d_inode ! = new_parent - > d_inode | |
new_dentry = = kobj - > dentry )
goto out_dput ;
error = - EEXIST ;
if ( new_dentry - > d_inode )
goto out_dput ;
2007-06-14 03:45:15 +09:00
/* rename kobject and sysfs_dirent */
error = - ENOMEM ;
new_name = dup_name = kstrdup ( new_name , GFP_KERNEL ) ;
if ( ! new_name )
goto out_drop ;
2007-06-14 03:45:14 +09:00
error = kobject_set_name ( kobj , " %s " , new_name ) ;
if ( error )
2007-06-14 03:45:15 +09:00
goto out_free ;
2007-06-14 03:45:14 +09:00
2007-06-14 03:45:15 +09:00
kfree ( sd - > s_name ) ;
sd - > s_name = new_name ;
/* move under the new parent */
2007-06-14 03:45:14 +09:00
d_add ( new_dentry , NULL ) ;
d_move ( kobj - > dentry , new_dentry ) ;
list_del_init ( & sd - > s_sibling ) ;
list_add ( & sd - > s_sibling , & parent_sd - > s_children ) ;
error = 0 ;
goto out_unlock ;
2007-06-14 03:45:15 +09:00
out_free :
kfree ( dup_name ) ;
2007-06-14 03:45:14 +09:00
out_drop :
d_drop ( new_dentry ) ;
out_dput :
dput ( new_dentry ) ;
out_unlock :
2007-01-24 12:35:52 -07:00
mutex_unlock ( & new_parent - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
up_write ( & sysfs_rename_sem ) ;
return error ;
}
2006-11-20 17:07:51 +01:00
int sysfs_move_dir ( struct kobject * kobj , struct kobject * new_parent )
{
struct dentry * old_parent_dentry , * new_parent_dentry , * new_dentry ;
struct sysfs_dirent * new_parent_sd , * sd ;
int error ;
old_parent_dentry = kobj - > parent ?
kobj - > parent - > dentry : sysfs_mount - > mnt_sb - > s_root ;
2007-01-08 20:16:44 +01:00
new_parent_dentry = new_parent ?
new_parent - > dentry : sysfs_mount - > mnt_sb - > s_root ;
2006-11-20 17:07:51 +01:00
2007-03-06 01:42:03 -08:00
if ( old_parent_dentry - > d_inode = = new_parent_dentry - > d_inode )
return 0 ; /* nothing to move */
2006-11-20 17:07:51 +01:00
again :
mutex_lock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
if ( ! mutex_trylock ( & new_parent_dentry - > d_inode - > i_mutex ) ) {
mutex_unlock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
goto again ;
}
new_parent_sd = new_parent_dentry - > d_fsdata ;
sd = kobj - > dentry - > d_fsdata ;
new_dentry = lookup_one_len ( kobj - > name , new_parent_dentry ,
strlen ( kobj - > name ) ) ;
if ( IS_ERR ( new_dentry ) ) {
error = PTR_ERR ( new_dentry ) ;
goto out ;
} else
error = 0 ;
d_add ( new_dentry , NULL ) ;
d_move ( kobj - > dentry , new_dentry ) ;
dput ( new_dentry ) ;
/* Remove from old parent's list and insert into new parent's list. */
list_del_init ( & sd - > s_sibling ) ;
list_add ( & sd - > s_sibling , & new_parent_sd - > s_children ) ;
out :
mutex_unlock ( & new_parent_dentry - > d_inode - > i_mutex ) ;
mutex_unlock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
return error ;
}
2005-04-16 15:20:36 -07:00
static int sysfs_dir_open ( struct inode * inode , struct file * file )
{
2006-12-08 02:36:36 -08:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
struct sysfs_dirent * parent_sd = dentry - > d_fsdata ;
2007-06-14 03:45:14 +09:00
struct sysfs_dirent * sd ;
2005-04-16 15:20:36 -07:00
2006-01-09 15:59:24 -08:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2007-06-14 03:45:15 +09:00
sd = sysfs_new_dirent ( " _DIR_ " , 0 , 0 ) ;
2007-06-14 03:45:14 +09:00
if ( sd )
sysfs_attach_dirent ( sd , parent_sd , NULL ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
2007-06-14 03:45:14 +09:00
file - > private_data = sd ;
return sd ? 0 : - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
static int sysfs_dir_close ( struct inode * inode , struct file * file )
{
2006-12-08 02:36:36 -08:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
struct sysfs_dirent * cursor = file - > private_data ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
list_del_init ( & cursor - > s_sibling ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
release_sysfs_dirent ( cursor ) ;
return 0 ;
}
/* Relationship between s_mode and the DT_xxx types */
static inline unsigned char dt_type ( struct sysfs_dirent * sd )
{
return ( sd - > s_mode > > 12 ) & 15 ;
}
static int sysfs_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
2006-12-08 02:36:36 -08:00
struct dentry * dentry = filp - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
struct sysfs_dirent * parent_sd = dentry - > d_fsdata ;
struct sysfs_dirent * cursor = filp - > private_data ;
struct list_head * p , * q = & cursor - > s_sibling ;
ino_t ino ;
int i = filp - > f_pos ;
switch ( i ) {
case 0 :
2007-06-11 14:02:45 +09:00
ino = parent_sd - > s_ino ;
2005-04-16 15:20:36 -07:00
if ( filldir ( dirent , " . " , 1 , i , ino , DT_DIR ) < 0 )
break ;
filp - > f_pos + + ;
i + + ;
/* fallthrough */
case 1 :
2007-06-14 03:45:14 +09:00
if ( parent_sd - > s_parent )
ino = parent_sd - > s_parent - > s_ino ;
else
ino = parent_sd - > s_ino ;
2005-04-16 15:20:36 -07:00
if ( filldir ( dirent , " .. " , 2 , i , ino , DT_DIR ) < 0 )
break ;
filp - > f_pos + + ;
i + + ;
/* fallthrough */
default :
2006-06-26 00:24:40 -07:00
if ( filp - > f_pos = = 2 )
list_move ( q , & parent_sd - > s_children ) ;
2005-04-16 15:20:36 -07:00
for ( p = q - > next ; p ! = & parent_sd - > s_children ; p = p - > next ) {
struct sysfs_dirent * next ;
const char * name ;
int len ;
next = list_entry ( p , struct sysfs_dirent ,
s_sibling ) ;
2007-06-14 03:45:15 +09:00
if ( ! next - > s_type )
2005-04-16 15:20:36 -07:00
continue ;
2007-06-14 03:45:15 +09:00
name = next - > s_name ;
2005-04-16 15:20:36 -07:00
len = strlen ( name ) ;
2007-06-11 14:02:45 +09:00
ino = next - > s_ino ;
2005-04-16 15:20:36 -07:00
if ( filldir ( dirent , name , len , filp - > f_pos , ino ,
dt_type ( next ) ) < 0 )
return 0 ;
2006-06-26 00:24:40 -07:00
list_move ( q , p ) ;
2005-04-16 15:20:36 -07:00
p = q ;
filp - > f_pos + + ;
}
}
return 0 ;
}
static loff_t sysfs_dir_lseek ( struct file * file , loff_t offset , int origin )
{
2006-12-08 02:36:36 -08:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-16 15:20:36 -07:00
2006-01-09 15:59:24 -08:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
switch ( origin ) {
case 1 :
offset + = file - > f_pos ;
case 0 :
if ( offset > = 0 )
break ;
default :
2006-12-08 02:36:36 -08:00
mutex_unlock ( & file - > f_path . dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
if ( offset ! = file - > f_pos ) {
file - > f_pos = offset ;
if ( file - > f_pos > = 2 ) {
struct sysfs_dirent * sd = dentry - > d_fsdata ;
struct sysfs_dirent * cursor = file - > private_data ;
struct list_head * p ;
loff_t n = file - > f_pos - 2 ;
list_del ( & cursor - > s_sibling ) ;
p = sd - > s_children . next ;
while ( n & & p ! = & sd - > s_children ) {
struct sysfs_dirent * next ;
next = list_entry ( p , struct sysfs_dirent ,
s_sibling ) ;
2007-06-14 03:45:15 +09:00
if ( next - > s_type )
2005-04-16 15:20:36 -07:00
n - - ;
p = p - > next ;
}
list_add_tail ( & cursor - > s_sibling , p ) ;
}
}
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return offset ;
}
2007-01-24 12:35:52 -07:00
/**
* sysfs_make_shadowed_dir - Setup so a directory can be shadowed
* @ kobj : object we ' re creating shadow of .
*/
int sysfs_make_shadowed_dir ( struct kobject * kobj ,
void * ( * follow_link ) ( struct dentry * , struct nameidata * ) )
{
struct inode * inode ;
struct inode_operations * i_op ;
inode = kobj - > dentry - > d_inode ;
if ( inode - > i_op ! = & sysfs_dir_inode_operations )
return - EINVAL ;
i_op = kmalloc ( sizeof ( * i_op ) , GFP_KERNEL ) ;
if ( ! i_op )
return - ENOMEM ;
memcpy ( i_op , & sysfs_dir_inode_operations , sizeof ( * i_op ) ) ;
i_op - > follow_link = follow_link ;
/* Locking of inode->i_op?
* Since setting i_op is a single word write and they
* are atomic we should be ok here .
*/
inode - > i_op = i_op ;
return 0 ;
}
/**
* sysfs_create_shadow_dir - create a shadow directory for an object .
* @ kobj : object we ' re creating directory for .
*
* sysfs_make_shadowed_dir must already have been called on this
* directory .
*/
struct dentry * sysfs_create_shadow_dir ( struct kobject * kobj )
{
2007-06-14 03:45:14 +09:00
struct dentry * dir = kobj - > dentry ;
struct inode * inode = dir - > d_inode ;
struct dentry * parent = dir - > d_parent ;
struct sysfs_dirent * parent_sd = parent - > d_fsdata ;
struct dentry * shadow ;
2007-01-24 12:35:52 -07:00
struct sysfs_dirent * sd ;
shadow = ERR_PTR ( - EINVAL ) ;
if ( ! sysfs_is_shadowed_inode ( inode ) )
goto out ;
shadow = d_alloc ( parent , & dir - > d_name ) ;
if ( ! shadow )
goto nomem ;
2007-06-14 03:45:15 +09:00
sd = sysfs_new_dirent ( " _SHADOW_ " , inode - > i_mode , SYSFS_DIR ) ;
2007-01-24 12:35:52 -07:00
if ( ! sd )
goto nomem ;
2007-06-14 03:45:15 +09:00
sd - > s_elem . dir . kobj = kobj ;
2007-06-14 03:45:14 +09:00
/* point to parent_sd but don't attach to it */
sd - > s_parent = sysfs_get ( parent_sd ) ;
2007-06-14 03:45:14 +09:00
sysfs_attach_dirent ( sd , NULL , shadow ) ;
2007-01-24 12:35:52 -07:00
d_instantiate ( shadow , igrab ( inode ) ) ;
inc_nlink ( inode ) ;
inc_nlink ( parent - > d_inode ) ;
shadow - > d_op = & sysfs_dentry_ops ;
dget ( shadow ) ; /* Extra count - pin the dentry in core */
out :
return shadow ;
nomem :
dput ( shadow ) ;
shadow = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
/**
* sysfs_remove_shadow_dir - remove an object ' s directory .
* @ shadow : dentry of shadow directory
*
* The only thing special about this is that we remove any files in
* the directory before we remove the directory , and we ' ve inlined
* what used to be sysfs_rmdir ( ) below , instead of calling separately .
*/
void sysfs_remove_shadow_dir ( struct dentry * shadow )
{
__sysfs_remove_dir ( shadow ) ;
}
2006-03-28 01:56:42 -08:00
const struct file_operations sysfs_dir_operations = {
2005-04-16 15:20:36 -07:00
. open = sysfs_dir_open ,
. release = sysfs_dir_close ,
. llseek = sysfs_dir_lseek ,
. read = generic_read_dir ,
. readdir = sysfs_readdir ,
} ;