2005-04-17 02:20:36 +04:00
/*
2007-09-20 12:31:38 +04:00
* fs / sysfs / dir . c - sysfs core and dir operation implementation
*
* Copyright ( c ) 2001 - 3 Patrick Mochel
* Copyright ( c ) 2007 SUSE Linux Products GmbH
* Copyright ( c ) 2007 Tejun Heo < teheo @ suse . de >
*
* This file is released under the GPLv2 .
*
* Please see Documentation / filesystems / sysfs . txt for more information .
2005-04-17 02:20:36 +04:00
*/
# undef DEBUG
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/module.h>
# include <linux/kobject.h>
2005-06-23 11:09:12 +04:00
# include <linux/namei.h>
2007-06-13 22:45:13 +04:00
# include <linux/idr.h>
2007-06-13 22:45:18 +04:00
# include <linux/completion.h>
2007-07-26 18:53:53 +04:00
# include <linux/mutex.h>
2008-03-14 05:41:52 +03:00
# include <linux/slab.h>
2009-11-04 13:50:06 +03:00
# include <linux/security.h>
2011-12-19 08:05:43 +04:00
# include <linux/hash.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2007-06-13 23:27:23 +04:00
DEFINE_MUTEX ( sysfs_mutex ) ;
2007-10-17 10:30:25 +04:00
DEFINE_SPINLOCK ( sysfs_assoc_lock ) ;
2005-04-17 02:20:36 +04:00
2011-12-19 08:05:43 +04:00
# define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
2007-10-17 10:30:25 +04:00
static DEFINE_SPINLOCK ( sysfs_ino_lock ) ;
2007-06-13 22:45:13 +04:00
static DEFINE_IDA ( sysfs_ino_ida ) ;
2007-06-13 22:45:18 +04:00
/**
2011-12-19 08:05:43 +04:00
* sysfs_name_hash
* @ ns : Namespace tag to hash
* @ name : Null terminated string to hash
*
* Returns 31 bit hash of ns + name ( so it fits in an off_t )
*/
static unsigned int sysfs_name_hash ( const void * ns , const char * name )
{
unsigned long hash = init_name_hash ( ) ;
unsigned int len = strlen ( name ) ;
while ( len - - )
hash = partial_name_hash ( * name + + , hash ) ;
hash = ( end_name_hash ( hash ) ^ hash_ptr ( ( void * ) ns , 31 ) ) ;
hash & = 0x7fffffffU ;
/* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
if ( hash < 1 )
hash + = 2 ;
if ( hash > = INT_MAX )
hash = INT_MAX - 1 ;
return hash ;
}
static int sysfs_name_compare ( unsigned int hash , const void * ns ,
const char * name , const struct sysfs_dirent * sd )
{
if ( hash ! = sd - > s_hash )
return hash - sd - > s_hash ;
if ( ns ! = sd - > s_ns )
return ns - sd - > s_ns ;
return strcmp ( name , sd - > s_name ) ;
}
static int sysfs_sd_compare ( const struct sysfs_dirent * left ,
const struct sysfs_dirent * right )
{
return sysfs_name_compare ( left - > s_hash , left - > s_ns , left - > s_name ,
right ) ;
}
/**
* sysfs_link_subling - link sysfs_dirent into sibling rbtree
2007-06-13 22:45:18 +04:00
* @ sd : sysfs_dirent of interest
*
2011-12-19 08:05:43 +04:00
* Link @ sd into its sibling rbtree which starts from
2007-09-20 11:05:12 +04:00
* sd - > s_parent - > s_dir . children .
2007-06-13 22:45:18 +04:00
*
* Locking :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2011-12-19 08:05:43 +04:00
*
* RETURNS :
* 0 on susccess - EEXIST on failure .
2007-06-13 22:45:18 +04:00
*/
2011-12-19 08:05:43 +04:00
static int sysfs_link_sibling ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2011-12-19 08:05:43 +04:00
struct rb_node * * node = & sd - > s_parent - > s_dir . children . rb_node ;
struct rb_node * parent = NULL ;
2011-07-26 01:55:57 +04:00
2012-03-09 01:03:10 +04:00
if ( sysfs_type ( sd ) = = SYSFS_DIR )
sd - > s_parent - > s_dir . subdirs + + ;
2011-12-19 08:05:43 +04:00
while ( * node ) {
struct sysfs_dirent * pos ;
int result ;
pos = to_sysfs_dirent ( * node ) ;
parent = * node ;
result = sysfs_sd_compare ( sd , pos ) ;
if ( result < 0 )
node = & pos - > s_rb . rb_left ;
else if ( result > 0 )
node = & pos - > s_rb . rb_right ;
else
return - EEXIST ;
2011-07-26 01:55:57 +04:00
}
2011-12-19 08:05:43 +04:00
/* add new node and rebalance the tree */
rb_link_node ( & sd - > s_rb , parent , node ) ;
rb_insert_color ( & sd - > s_rb , & sd - > s_parent - > s_dir . children ) ;
return 0 ;
2007-06-13 22:45:18 +04:00
}
/**
2011-12-19 08:05:43 +04:00
* sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree
2007-06-13 22:45:18 +04:00
* @ sd : sysfs_dirent of interest
*
2011-12-19 08:05:43 +04:00
* Unlink @ sd from its sibling rbtree which starts from
2007-09-20 11:05:12 +04:00
* sd - > s_parent - > s_dir . children .
2007-06-13 22:45:18 +04:00
*
* Locking :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2007-06-13 22:45:18 +04:00
*/
2007-08-02 16:38:03 +04:00
static void sysfs_unlink_sibling ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2012-03-09 01:03:10 +04:00
if ( sysfs_type ( sd ) = = SYSFS_DIR )
sd - > s_parent - > s_dir . subdirs - - ;
2011-12-19 08:05:43 +04:00
rb_erase ( & sd - > s_rb , & sd - > s_parent - > s_dir . children ) ;
2007-06-13 22:45:18 +04:00
}
2012-05-14 21:30:03 +04:00
# ifdef CONFIG_DEBUG_LOCK_ALLOC
/* Test for attributes that want to ignore lockdep for read-locking */
static bool ignore_lockdep ( struct sysfs_dirent * sd )
{
return sysfs_type ( sd ) = = SYSFS_KOBJ_ATTR & &
sd - > s_attr . attr - > ignore_lockdep ;
}
# else
static inline bool ignore_lockdep ( struct sysfs_dirent * sd )
{
return true ;
}
# endif
2007-06-13 22:45:18 +04:00
/**
* sysfs_get_active - get an active reference to sysfs_dirent
* @ sd : sysfs_dirent to get an active reference to
*
* Get an active reference of @ sd . This function is noop if @ sd
* is NULL .
*
* RETURNS :
* Pointer to @ sd on success , NULL on failure .
*/
2010-02-12 02:18:38 +03:00
struct sysfs_dirent * sysfs_get_active ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
if ( unlikely ( ! sd ) )
return NULL ;
while ( 1 ) {
int v , t ;
v = atomic_read ( & sd - > s_active ) ;
if ( unlikely ( v < 0 ) )
return NULL ;
t = atomic_cmpxchg ( & sd - > s_active , v , v + 1 ) ;
2012-05-14 21:30:03 +04:00
if ( likely ( t = = v ) )
break ;
2007-06-13 22:45:18 +04:00
if ( t < 0 )
return NULL ;
cpu_relax ( ) ;
2007-06-13 22:45:18 +04:00
}
2012-05-14 21:30:03 +04:00
if ( likely ( ! ignore_lockdep ( sd ) ) )
rwsem_acquire_read ( & sd - > dep_map , 0 , 1 , _RET_IP_ ) ;
return sd ;
2007-06-13 22:45:18 +04:00
}
/**
* sysfs_put_active - put an active reference to sysfs_dirent
* @ sd : sysfs_dirent to put an active reference to
*
* Put an active reference to @ sd . This function is noop if @ sd
* is NULL .
*/
2010-02-12 02:18:38 +03:00
void sysfs_put_active ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
int v ;
if ( unlikely ( ! sd ) )
return ;
2012-05-14 21:30:03 +04:00
if ( likely ( ! ignore_lockdep ( sd ) ) )
rwsem_release ( & sd - > dep_map , 1 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
v = atomic_dec_return ( & sd - > s_active ) ;
if ( likely ( v ! = SD_DEACTIVATED_BIAS ) )
return ;
/* atomic_dec_return() is a mb(), we'll always see the updated
2011-07-22 04:01:12 +04:00
* sd - > u . completion .
2007-06-13 22:45:18 +04:00
*/
2011-07-22 04:01:12 +04:00
complete ( sd - > u . completion ) ;
2007-06-13 22:45:18 +04:00
}
/**
* sysfs_deactivate - deactivate sysfs_dirent
* @ sd : sysfs_dirent to deactivate
*
2007-06-13 22:45:18 +04:00
* Deny new active references and drain existing ones .
2007-06-13 22:45:18 +04:00
*/
2007-06-13 23:27:24 +04:00
static void sysfs_deactivate ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
DECLARE_COMPLETION_ONSTACK ( wait ) ;
int v ;
2007-06-13 22:45:18 +04:00
2011-07-22 04:01:12 +04:00
BUG_ON ( ! ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ) ;
2010-02-12 02:20:00 +03:00
if ( ! ( sysfs_type ( sd ) & SYSFS_ACTIVE_REF ) )
return ;
2011-07-22 04:01:12 +04:00
sd - > u . completion = ( void * ) & wait ;
2007-06-13 22:45:18 +04:00
2010-01-03 00:37:12 +03:00
rwsem_acquire ( & sd - > dep_map , 0 , 0 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
/* atomic_add_return() is a mb(), put_active() will always see
2011-07-22 04:01:12 +04:00
* the updated sd - > u . completion .
2007-06-13 22:45:18 +04:00
*/
2007-06-13 22:45:18 +04:00
v = atomic_add_return ( SD_DEACTIVATED_BIAS , & sd - > s_active ) ;
2010-01-03 00:37:12 +03:00
if ( v ! = SD_DEACTIVATED_BIAS ) {
lock_contended ( & sd - > dep_map , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
wait_for_completion ( & wait ) ;
2010-01-03 00:37:12 +03:00
}
2007-06-13 22:45:18 +04:00
2010-01-03 00:37:12 +03:00
lock_acquired ( & sd - > dep_map , _RET_IP_ ) ;
rwsem_release ( & sd - > dep_map , 1 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
}
2011-12-19 08:08:16 +04:00
static int sysfs_alloc_ino ( unsigned int * pino )
2007-06-13 22:45:13 +04:00
{
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 ;
}
2011-12-19 08:08:16 +04:00
static void sysfs_free_ino ( unsigned int ino )
2007-06-13 22:45:13 +04:00
{
spin_lock ( & sysfs_ino_lock ) ;
ida_remove ( & sysfs_ino_ida , ino ) ;
spin_unlock ( & sysfs_ino_lock ) ;
}
2007-06-13 22:45:13 +04:00
void release_sysfs_dirent ( struct sysfs_dirent * sd )
{
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * parent_sd ;
repeat :
2007-06-13 23:27:23 +04:00
/* Moving/renaming is always done while holding reference.
* sd - > s_parent won ' t change beneath us .
*/
2007-06-13 22:45:14 +04:00
parent_sd = sd - > s_parent ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) = = SYSFS_KOBJ_LINK )
2007-09-20 11:05:11 +04:00
sysfs_put ( sd - > s_symlink . target_sd ) ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) & SYSFS_COPY_NAME )
2007-06-13 22:45:15 +04:00
kfree ( sd - > s_name ) ;
2009-11-04 13:50:06 +03:00
if ( sd - > s_iattr & & sd - > s_iattr - > ia_secdata )
security_release_secctx ( sd - > s_iattr - > ia_secdata ,
sd - > s_iattr - > ia_secdata_len ) ;
2007-06-13 22:45:13 +04:00
kfree ( sd - > s_iattr ) ;
2007-06-13 22:45:13 +04:00
sysfs_free_ino ( sd - > s_ino ) ;
2007-06-13 22:45:13 +04:00
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
2007-06-13 22:45:14 +04:00
sd = parent_sd ;
if ( sd & & atomic_dec_and_test ( & sd - > s_count ) )
goto repeat ;
2007-06-13 22:45:13 +04:00
}
2011-01-07 09:49:23 +03:00
static int sysfs_dentry_delete ( const struct dentry * dentry )
2009-11-08 10:27:01 +03:00
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
return ! ! ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ;
}
static int sysfs_dentry_revalidate ( struct dentry * dentry , struct nameidata * nd )
{
2011-01-07 09:49:57 +03:00
struct sysfs_dirent * sd ;
2009-11-08 10:27:01 +03:00
int is_dir ;
2011-01-07 09:49:57 +03:00
if ( nd - > flags & LOOKUP_RCU )
return - ECHILD ;
sd = dentry - > d_fsdata ;
2009-11-08 10:27:01 +03:00
mutex_lock ( & sysfs_mutex ) ;
/* The sysfs dirent has been deleted */
if ( sd - > s_flags & SYSFS_FLAG_REMOVED )
goto out_bad ;
2009-11-21 03:08:56 +03:00
/* The sysfs dirent has been moved? */
if ( dentry - > d_parent - > d_fsdata ! = sd - > s_parent )
goto out_bad ;
/* The sysfs dirent has been renamed */
if ( strcmp ( dentry - > d_name . name , sd - > s_name ) ! = 0 )
goto out_bad ;
2009-11-08 10:27:01 +03:00
mutex_unlock ( & sysfs_mutex ) ;
out_valid :
return 1 ;
out_bad :
/* Remove the dentry from the dcache hashes.
* If this is a deleted dentry we use d_drop instead of d_delete
* so sysfs doesn ' t need to cope with negative dentries .
2009-11-21 03:08:56 +03:00
*
* If this is a dentry that has simply been renamed we
* use d_drop to remove it from the dcache lookup on its
* old parent . If this dentry persists later when a lookup
* is performed at its new name the dentry will be readded
* to the dcache hashes .
2009-11-08 10:27:01 +03:00
*/
is_dir = ( sysfs_type ( sd ) = = SYSFS_DIR ) ;
mutex_unlock ( & sysfs_mutex ) ;
if ( is_dir ) {
/* If we have submounts we must allow the vfs caches
* to lie about the state of the filesystem to prevent
* leaks and other nasty things .
*/
if ( have_submounts ( dentry ) )
goto out_valid ;
shrink_dcache_parent ( dentry ) ;
}
d_drop ( dentry ) ;
return 0 ;
}
2009-11-08 10:27:00 +03:00
static void sysfs_dentry_iput ( struct dentry * dentry , struct inode * inode )
2005-04-17 02:20:36 +04:00
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
2007-08-20 16:36:30 +04:00
sysfs_put ( sd ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
}
2009-02-20 09:01:46 +03:00
static const struct dentry_operations sysfs_dentry_ops = {
2009-11-08 10:27:01 +03:00
. d_revalidate = sysfs_dentry_revalidate ,
. d_delete = sysfs_dentry_delete ,
2009-11-08 10:27:00 +03:00
. d_iput = sysfs_dentry_iput ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * sysfs_new_dirent ( const char * name , umode_t mode , int type )
2005-04-17 02:20:36 +04:00
{
2007-06-13 22:45:15 +04:00
char * dup_name = NULL ;
2007-07-14 06:03:35 +04:00
struct sysfs_dirent * sd ;
2007-06-13 22:45:15 +04:00
if ( type & SYSFS_COPY_NAME ) {
name = dup_name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! name )
2007-07-14 06:03:35 +04:00
return NULL ;
2007-06-13 22:45:15 +04:00
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:03 +03:00
sd = kmem_cache_zalloc ( sysfs_dir_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! sd )
2007-07-14 06:03:35 +04:00
goto err_out1 ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:15 +04:00
if ( sysfs_alloc_ino ( & sd - > s_ino ) )
2007-07-14 06:03:35 +04:00
goto err_out2 ;
2007-06-13 22:45:13 +04:00
2005-04-17 02:20:36 +04:00
atomic_set ( & sd - > s_count , 1 ) ;
2007-06-13 22:45:18 +04:00
atomic_set ( & sd - > s_active , 0 ) ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:15 +04:00
sd - > s_name = name ;
2007-06-13 22:45:14 +04:00
sd - > s_mode = mode ;
2007-06-13 23:27:21 +04:00
sd - > s_flags = type ;
2005-04-17 02:20:36 +04:00
return sd ;
2007-06-13 22:45:15 +04:00
2007-07-14 06:03:35 +04:00
err_out2 :
2007-06-13 22:45:15 +04:00
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
2007-07-14 06:03:35 +04:00
err_out1 :
kfree ( dup_name ) ;
2007-06-13 22:45:15 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:23 +04:00
/**
2007-06-13 23:27:24 +04:00
* sysfs_addrm_start - prepare for sysfs_dirent add / remove
* @ acxt : pointer to sysfs_addrm_cxt to be used
* @ parent_sd : parent sysfs_dirent
2007-06-13 23:27:23 +04:00
*
2007-06-13 23:27:24 +04:00
* This function is called when the caller is about to add or
* remove sysfs_dirent under @ parent_sd . This function acquires
2009-11-21 03:08:55 +03:00
* sysfs_mutex . @ acxt is used to keep and pass context to
2007-06-13 23:27:24 +04:00
* other addrm functions .
2007-06-13 23:27:23 +04:00
*
* LOCKING :
2007-06-13 23:27:24 +04:00
* Kernel thread context ( may sleep ) . sysfs_mutex is locked on
2009-11-21 03:08:55 +03:00
* return .
2007-06-13 23:27:23 +04:00
*/
2007-06-13 23:27:24 +04:00
void sysfs_addrm_start ( struct sysfs_addrm_cxt * acxt ,
struct sysfs_dirent * parent_sd )
2007-01-24 22:35:52 +03:00
{
2007-06-13 23:27:24 +04:00
memset ( acxt , 0 , sizeof ( * acxt ) ) ;
acxt - > parent_sd = parent_sd ;
mutex_lock ( & sysfs_mutex ) ;
}
/**
2008-06-10 13:09:08 +04:00
* __sysfs_add_one - add sysfs_dirent to parent without warning
2007-06-13 23:27:24 +04:00
* @ acxt : addrm context to use
* @ sd : sysfs_dirent to be added
*
* Get @ acxt - > parent_sd and set sd - > s_parent to it and increment
2007-09-20 11:05:09 +04:00
* nlink of parent inode if @ sd is a directory and link into the
* children list of the parent .
2007-06-13 23:27:24 +04:00
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
2007-08-02 16:38:03 +04:00
*
* RETURNS :
* 0 on success , - EEXIST if entry with the given name already
* exists .
2007-06-13 23:27:24 +04:00
*/
2008-06-10 13:09:08 +04:00
int __sysfs_add_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
2007-06-13 23:27:24 +04:00
{
2009-11-21 03:08:51 +03:00
struct sysfs_inode_attrs * ps_iattr ;
2011-12-19 08:05:43 +04:00
int ret ;
2009-11-21 03:08:51 +03:00
2011-10-13 02:02:43 +04:00
if ( ! ! sysfs_ns_type ( acxt - > parent_sd ) ! = ! ! sd - > s_ns ) {
WARN ( 1 , KERN_WARNING " sysfs: ns %s in '%s' for '%s' \n " ,
sysfs_ns_type ( acxt - > parent_sd ) ? " required " : " invalid " ,
acxt - > parent_sd - > s_name , sd - > s_name ) ;
return - EINVAL ;
}
2011-12-19 08:05:43 +04:00
sd - > s_hash = sysfs_name_hash ( sd - > s_ns , sd - > s_name ) ;
2007-06-13 23:27:24 +04:00
sd - > s_parent = sysfs_get ( acxt - > parent_sd ) ;
2011-12-19 08:05:43 +04:00
ret = sysfs_link_sibling ( sd ) ;
if ( ret )
return ret ;
2007-08-02 16:38:03 +04:00
2009-11-21 03:08:51 +03:00
/* Update timestamps on the parent */
ps_iattr = acxt - > parent_sd - > s_iattr ;
if ( ps_iattr ) {
struct iattr * ps_iattrs = & ps_iattr - > ia_iattr ;
ps_iattrs - > ia_ctime = ps_iattrs - > ia_mtime = CURRENT_TIME ;
}
2007-08-02 16:38:03 +04:00
return 0 ;
2007-06-13 23:27:24 +04:00
}
2009-02-12 20:56:59 +03:00
/**
* sysfs_pathname - return full path to sysfs dirent
* @ sd : sysfs_dirent whose path we want
* @ path : caller allocated buffer
*
* Gives the name " / " to the sysfs_root entry ; any path returned
* is relative to wherever sysfs is mounted .
*
* XXX : does no error checking on @ path size
*/
static char * sysfs_pathname ( struct sysfs_dirent * sd , char * path )
{
if ( sd - > s_parent ) {
sysfs_pathname ( sd - > s_parent , path ) ;
strcat ( path , " / " ) ;
}
strcat ( path , sd - > s_name ) ;
return path ;
}
2008-06-10 13:09:08 +04:00
/**
* sysfs_add_one - add sysfs_dirent to parent
* @ acxt : addrm context to use
* @ sd : sysfs_dirent to be added
*
* Get @ acxt - > parent_sd and set sd - > s_parent to it and increment
* nlink of parent inode if @ sd is a directory and link into the
* children list of the parent .
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
*
* RETURNS :
* 0 on success , - EEXIST if entry with the given name already
* exists .
*/
int sysfs_add_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
{
int ret ;
ret = __sysfs_add_one ( acxt , sd ) ;
2009-02-12 20:56:59 +03:00
if ( ret = = - EEXIST ) {
char * path = kzalloc ( PATH_MAX , GFP_KERNEL ) ;
WARN ( 1 , KERN_WARNING
" sysfs: cannot create duplicate filename '%s' \n " ,
( path = = NULL ) ? sd - > s_name :
strcat ( strcat ( sysfs_pathname ( acxt - > parent_sd , path ) , " / " ) ,
sd - > s_name ) ) ;
kfree ( path ) ;
}
2008-06-10 13:09:08 +04:00
return ret ;
}
2007-06-13 23:27:24 +04:00
/**
* sysfs_remove_one - remove sysfs_dirent from parent
* @ acxt : addrm context to use
2008-01-08 20:11:24 +03:00
* @ sd : sysfs_dirent to be removed
2007-06-13 23:27:24 +04:00
*
* Mark @ sd removed and drop nlink of parent inode if @ sd is a
2007-09-20 11:05:09 +04:00
* directory . @ sd is unlinked from the children list .
2007-06-13 23:27:24 +04:00
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
*/
void sysfs_remove_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
{
2009-11-21 03:08:51 +03:00
struct sysfs_inode_attrs * ps_iattr ;
2007-08-02 16:38:03 +04:00
BUG_ON ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ;
sysfs_unlink_sibling ( sd ) ;
2007-06-13 23:27:24 +04:00
2009-11-21 03:08:51 +03:00
/* Update timestamps on the parent */
ps_iattr = acxt - > parent_sd - > s_iattr ;
if ( ps_iattr ) {
struct iattr * ps_iattrs = & ps_iattr - > ia_iattr ;
ps_iattrs - > ia_ctime = ps_iattrs - > ia_mtime = CURRENT_TIME ;
}
2007-06-13 23:27:24 +04:00
sd - > s_flags | = SYSFS_FLAG_REMOVED ;
2011-07-22 04:01:12 +04:00
sd - > u . removed_list = acxt - > removed ;
2007-06-13 23:27:24 +04:00
acxt - > removed = sd ;
2007-06-13 23:27:24 +04:00
}
2007-06-13 23:27:24 +04:00
/**
* sysfs_addrm_finish - finish up sysfs_dirent add / remove
* @ acxt : addrm context to finish up
*
* Finish up sysfs_dirent add / remove . Resources acquired by
* sysfs_addrm_start ( ) are released and removed sysfs_dirents are
2009-11-21 03:08:55 +03:00
* cleaned up .
2007-06-13 23:27:24 +04:00
*
* LOCKING :
2009-11-21 03:08:55 +03:00
* sysfs_mutex is released .
2007-06-13 23:27:24 +04:00
*/
2007-08-02 16:38:03 +04:00
void sysfs_addrm_finish ( struct sysfs_addrm_cxt * acxt )
2007-06-13 23:27:24 +04:00
{
/* release resources acquired by sysfs_addrm_start() */
mutex_unlock ( & sysfs_mutex ) ;
/* kill removed sysfs_dirents */
while ( acxt - > removed ) {
struct sysfs_dirent * sd = acxt - > removed ;
2011-07-22 04:01:12 +04:00
acxt - > removed = sd - > u . removed_list ;
2007-06-13 23:27:24 +04:00
sysfs_deactivate ( sd ) ;
2009-03-04 22:57:20 +03:00
unmap_bin_file ( sd ) ;
2007-06-13 23:27:24 +04:00
sysfs_put ( sd ) ;
2007-06-13 22:45:14 +04:00
}
2007-01-24 22:35:52 +03:00
}
2007-06-13 23:27:22 +04:00
/**
* sysfs_find_dirent - find sysfs_dirent with the given name
* @ parent_sd : sysfs_dirent to search under
* @ name : name to look for
*
* Look for sysfs_dirent with name @ name under @ parent_sd .
2006-03-09 17:10:14 +03:00
*
2007-06-13 23:27:22 +04:00
* LOCKING :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2006-03-09 17:10:14 +03:00
*
2007-06-13 23:27:22 +04:00
* RETURNS :
* Pointer to sysfs_dirent if found , NULL if not .
2006-03-09 17:10:14 +03:00
*/
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sysfs_find_dirent ( struct sysfs_dirent * parent_sd ,
2010-03-30 22:31:26 +04:00
const void * ns ,
2007-06-13 23:27:22 +04:00
const unsigned char * name )
2006-03-09 17:10:14 +03:00
{
2011-12-19 08:05:43 +04:00
struct rb_node * node = parent_sd - > s_dir . children . rb_node ;
unsigned int hash ;
2011-07-26 01:55:57 +04:00
2011-10-13 02:02:43 +04:00
if ( ! ! sysfs_ns_type ( parent_sd ) ! = ! ! ns ) {
WARN ( 1 , KERN_WARNING " sysfs: ns %s in '%s' for '%s' \n " ,
sysfs_ns_type ( parent_sd ) ? " required " : " invalid " ,
parent_sd - > s_name , name ) ;
return NULL ;
}
2011-12-19 08:05:43 +04:00
hash = sysfs_name_hash ( ns , name ) ;
while ( node ) {
struct sysfs_dirent * sd ;
int result ;
sd = to_sysfs_dirent ( node ) ;
result = sysfs_name_compare ( hash , ns , name , sd ) ;
if ( result < 0 )
node = node - > rb_left ;
else if ( result > 0 )
node = node - > rb_right ;
else
return sd ;
2010-03-30 22:31:26 +04:00
}
2011-12-19 08:05:43 +04:00
return NULL ;
2007-06-13 23:27:22 +04:00
}
2006-03-09 17:10:14 +03:00
2007-06-13 23:27:22 +04:00
/**
* sysfs_get_dirent - find and get sysfs_dirent with the given name
* @ parent_sd : sysfs_dirent to search under
* @ name : name to look for
*
* Look for sysfs_dirent with name @ name under @ parent_sd and get
* it if found .
*
* LOCKING :
2007-06-13 23:27:23 +04:00
* Kernel thread context ( may sleep ) . Grabs sysfs_mutex .
2007-06-13 23:27:22 +04:00
*
* RETURNS :
* Pointer to sysfs_dirent if found , NULL if not .
*/
struct sysfs_dirent * sysfs_get_dirent ( struct sysfs_dirent * parent_sd ,
2010-03-30 22:31:26 +04:00
const void * ns ,
2007-06-13 23:27:22 +04:00
const unsigned char * name )
{
struct sysfs_dirent * sd ;
2007-06-13 23:27:23 +04:00
mutex_lock ( & sysfs_mutex ) ;
2010-03-30 22:31:26 +04:00
sd = sysfs_find_dirent ( parent_sd , ns , name ) ;
2007-06-13 23:27:22 +04:00
sysfs_get ( sd ) ;
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-06-13 23:27:22 +04:00
return sd ;
2006-03-09 17:10:14 +03:00
}
2008-07-16 02:58:04 +04:00
EXPORT_SYMBOL_GPL ( sysfs_get_dirent ) ;
2006-03-09 17:10:14 +03:00
2007-06-13 23:27:22 +04:00
static int create_dir ( struct kobject * kobj , struct sysfs_dirent * parent_sd ,
2010-03-30 22:31:26 +04:00
enum kobj_ns_type type , const void * ns , const char * name ,
struct sysfs_dirent * * p_sd )
2005-04-17 02:20:36 +04:00
{
umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ;
2007-06-13 23:27:25 +04:00
struct sysfs_addrm_cxt acxt ;
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * sd ;
2007-08-02 16:38:03 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:17 +04:00
/* allocate */
2007-06-13 22:45:15 +04:00
sd = sysfs_new_dirent ( name , mode , SYSFS_DIR ) ;
2007-06-13 22:45:14 +04:00
if ( ! sd )
2007-06-13 23:27:25 +04:00
return - ENOMEM ;
2010-03-30 22:31:26 +04:00
sd - > s_flags | = ( type < < SYSFS_NS_TYPE_SHIFT ) ;
sd - > s_ns = ns ;
2007-09-20 11:05:11 +04:00
sd - > s_dir . kobj = kobj ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:17 +04:00
/* link in */
2007-06-13 23:27:25 +04:00
sysfs_addrm_start ( & acxt , parent_sd ) ;
2007-08-02 16:38:03 +04:00
rc = sysfs_add_one ( & acxt , sd ) ;
sysfs_addrm_finish ( & acxt ) ;
2007-07-18 11:38:11 +04:00
2007-08-02 16:38:03 +04:00
if ( rc = = 0 )
* p_sd = sd ;
else
2007-07-18 11:38:11 +04:00
sysfs_put ( sd ) ;
2007-06-13 22:45:14 +04:00
2007-08-02 16:38:03 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
int sysfs_create_subdir ( struct kobject * kobj , const char * name ,
struct sysfs_dirent * * p_sd )
2005-04-17 02:20:36 +04:00
{
2010-03-30 22:31:26 +04:00
return create_dir ( kobj , kobj - > sd ,
KOBJ_NS_TYPE_NONE , NULL , name , p_sd ) ;
}
2010-05-04 01:23:15 +04:00
/**
* sysfs_read_ns_type : return associated ns_type
* @ kobj : the kobject being queried
*
* Each kobject can be tagged with exactly one namespace type
* ( i . e . network or user ) . Return the ns_type associated with
* this object if any
*/
2010-03-30 22:31:26 +04:00
static enum kobj_ns_type sysfs_read_ns_type ( struct kobject * kobj )
{
const struct kobj_ns_type_operations * ops ;
enum kobj_ns_type type ;
ops = kobj_child_ns_ops ( kobj ) ;
if ( ! ops )
return KOBJ_NS_TYPE_NONE ;
type = ops - > type ;
BUG_ON ( type < = KOBJ_NS_TYPE_NONE ) ;
BUG_ON ( type > = KOBJ_NS_TYPES ) ;
BUG_ON ( ! kobj_ns_type_registered ( type ) ) ;
return type ;
2005-04-17 02:20:36 +04:00
}
/**
* sysfs_create_dir - create a directory for an object .
* @ kobj : object we ' re creating directory for .
*/
2007-07-31 14:15:08 +04:00
int sysfs_create_dir ( struct kobject * kobj )
2005-04-17 02:20:36 +04:00
{
2010-03-30 22:31:26 +04:00
enum kobj_ns_type type ;
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * parent_sd , * sd ;
2010-03-30 22:31:26 +04:00
const void * ns = NULL ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
BUG_ON ( ! kobj ) ;
2007-07-31 14:15:08 +04:00
if ( kobj - > parent )
2007-06-13 23:27:22 +04:00
parent_sd = kobj - > parent - > sd ;
2005-04-17 02:20:36 +04:00
else
2007-08-20 16:36:30 +04:00
parent_sd = & sysfs_root ;
2005-04-17 02:20:36 +04:00
2012-04-07 00:41:06 +04:00
if ( ! parent_sd )
return - ENOENT ;
2010-03-30 22:31:26 +04:00
if ( sysfs_ns_type ( parent_sd ) )
ns = kobj - > ktype - > namespace ( kobj ) ;
type = sysfs_read_ns_type ( kobj ) ;
error = create_dir ( kobj , parent_sd , type , ns , kobject_name ( kobj ) , & sd ) ;
2005-04-17 02:20:36 +04:00
if ( ! error )
2007-06-13 23:27:22 +04:00
kobj - > sd = sd ;
2005-04-17 02:20:36 +04:00
return error ;
}
static struct dentry * sysfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
2007-07-31 14:15:08 +04:00
struct dentry * ret = NULL ;
2010-03-30 22:31:26 +04:00
struct dentry * parent = dentry - > d_parent ;
struct sysfs_dirent * parent_sd = parent - > d_fsdata ;
2007-08-02 16:38:02 +04:00
struct sysfs_dirent * sd ;
2007-06-13 22:45:17 +04:00
struct inode * inode ;
2010-03-30 22:31:26 +04:00
enum kobj_ns_type type ;
const void * ns ;
2005-04-17 02:20:36 +04:00
2007-07-31 14:15:08 +04:00
mutex_lock ( & sysfs_mutex ) ;
2010-03-30 22:31:26 +04:00
type = sysfs_ns_type ( parent_sd ) ;
ns = sysfs_info ( dir - > i_sb ) - > ns [ type ] ;
sd = sysfs_find_dirent ( parent_sd , ns , dentry - > d_name . name ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:17 +04:00
/* no such entry */
2008-01-16 06:06:14 +03:00
if ( ! sd ) {
ret = ERR_PTR ( - ENOENT ) ;
2007-07-31 14:15:08 +04:00
goto out_unlock ;
2008-01-16 06:06:14 +03:00
}
2007-06-13 22:45:17 +04:00
/* attach dentry and inode */
2010-02-13 06:22:27 +03:00
inode = sysfs_get_inode ( dir - > i_sb , sd ) ;
2007-07-31 14:15:08 +04:00
if ( ! inode ) {
ret = ERR_PTR ( - ENOMEM ) ;
goto out_unlock ;
}
2007-06-13 23:27:23 +04:00
2007-09-20 11:05:11 +04:00
/* instantiate and hash dentry */
2009-11-21 03:08:56 +03:00
ret = d_find_alias ( inode ) ;
if ( ! ret ) {
2011-01-07 09:49:55 +03:00
d_set_d_op ( dentry , & sysfs_dentry_ops ) ;
2009-11-21 03:08:56 +03:00
dentry - > d_fsdata = sysfs_get ( sd ) ;
d_add ( dentry , inode ) ;
} else {
d_move ( ret , dentry ) ;
iput ( inode ) ;
}
2007-06-13 22:45:17 +04:00
2007-07-31 14:15:08 +04:00
out_unlock :
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-07-31 14:15:08 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:40 +03:00
const struct inode_operations sysfs_dir_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = sysfs_lookup ,
2009-11-21 03:08:53 +03:00
. permission = sysfs_permission ,
2005-05-31 09:09:14 +04:00
. setattr = sysfs_setattr ,
2009-11-21 03:08:53 +03:00
. getattr = sysfs_getattr ,
2009-09-09 22:25:37 +04:00
. setxattr = sysfs_setxattr ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-13 23:27:22 +04:00
static void remove_dir ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:24 +04:00
struct sysfs_addrm_cxt acxt ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:24 +04:00
sysfs_addrm_start ( & acxt , sd - > s_parent ) ;
sysfs_remove_one ( & acxt , sd ) ;
sysfs_addrm_finish ( & acxt ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
void sysfs_remove_subdir ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
remove_dir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
static void __sysfs_remove_dir ( struct sysfs_dirent * dir_sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:24 +04:00
struct sysfs_addrm_cxt acxt ;
2011-07-26 01:57:03 +04:00
struct rb_node * pos ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
if ( ! dir_sd )
2005-04-17 02:20:36 +04:00
return ;
2007-06-13 23:27:22 +04:00
pr_debug ( " sysfs %s: removing dir \n " , dir_sd - > s_name ) ;
2007-06-13 23:27:24 +04:00
sysfs_addrm_start ( & acxt , dir_sd ) ;
2011-12-19 08:05:43 +04:00
pos = rb_first ( & dir_sd - > s_dir . children ) ;
2011-07-26 01:57:03 +04:00
while ( pos ) {
2011-12-19 08:05:43 +04:00
struct sysfs_dirent * sd = to_sysfs_dirent ( pos ) ;
2011-07-26 01:57:03 +04:00
pos = rb_next ( pos ) ;
2007-08-20 16:36:30 +04:00
if ( sysfs_type ( sd ) ! = SYSFS_DIR )
2007-06-13 23:27:24 +04:00
sysfs_remove_one ( & acxt , sd ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:24 +04:00
sysfs_addrm_finish ( & acxt ) ;
2007-06-13 22:45:16 +04:00
2007-06-13 23:27:22 +04:00
remove_dir ( dir_sd ) ;
2007-01-24 22:35:52 +03: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-13 23:27:22 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
2007-06-13 22:45:15 +04:00
2007-06-13 23:27:23 +04:00
spin_lock ( & sysfs_assoc_lock ) ;
2007-06-13 23:27:22 +04:00
kobj - > sd = NULL ;
2007-06-13 23:27:23 +04:00
spin_unlock ( & sysfs_assoc_lock ) ;
2007-06-13 22:45:15 +04:00
2007-06-13 23:27:22 +04:00
__sysfs_remove_dir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-21 03:08:57 +03:00
int sysfs_rename ( struct sysfs_dirent * sd ,
2010-03-30 22:31:26 +04:00
struct sysfs_dirent * new_parent_sd , const void * new_ns ,
const char * new_name )
2005-04-17 02:20:36 +04:00
{
2007-06-13 22:45:14 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2009-11-21 03:08:56 +03:00
mutex_lock ( & sysfs_mutex ) ;
2007-08-20 16:36:30 +04:00
2007-08-20 16:36:31 +04:00
error = 0 ;
2010-03-30 22:31:26 +04:00
if ( ( sd - > s_parent = = new_parent_sd ) & & ( sd - > s_ns = = new_ns ) & &
2009-11-21 03:08:57 +03:00
( strcmp ( sd - > s_name , new_name ) = = 0 ) )
2007-08-20 16:36:31 +04:00
goto out ; /* nothing to rename */
error = - EEXIST ;
2010-03-30 22:31:26 +04:00
if ( sysfs_find_dirent ( new_parent_sd , new_ns , new_name ) )
2009-11-21 03:08:56 +03:00
goto out ;
2007-06-13 22:45:14 +04:00
2008-07-04 05:05:28 +04:00
/* rename sysfs_dirent */
2009-11-21 03:08:57 +03:00
if ( strcmp ( sd - > s_name , new_name ) ! = 0 ) {
error = - ENOMEM ;
2012-05-03 00:56:14 +04:00
new_name = kstrdup ( new_name , GFP_KERNEL ) ;
2009-11-21 03:08:57 +03:00
if ( ! new_name )
goto out ;
2012-05-03 00:56:14 +04:00
kfree ( sd - > s_name ) ;
2009-11-21 03:08:57 +03:00
sd - > s_name = new_name ;
}
2007-06-13 22:45:15 +04:00
2011-11-01 18:06:17 +04:00
/* Move to the appropriate place in the appropriate directories rbtree. */
sysfs_unlink_sibling ( sd ) ;
sysfs_get ( new_parent_sd ) ;
sysfs_put ( sd - > s_parent ) ;
2010-03-30 22:31:26 +04:00
sd - > s_ns = new_ns ;
2012-04-04 23:06:20 +04:00
sd - > s_hash = sysfs_name_hash ( sd - > s_ns , sd - > s_name ) ;
2011-11-01 18:06:17 +04:00
sd - > s_parent = new_parent_sd ;
sysfs_link_sibling ( sd ) ;
2007-06-13 22:45:15 +04:00
2007-06-13 22:45:14 +04:00
error = 0 ;
2009-11-21 03:08:56 +03:00
out :
2007-08-20 16:36:31 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-11-21 03:08:57 +03:00
int sysfs_rename_dir ( struct kobject * kobj , const char * new_name )
{
2010-03-30 22:31:26 +04:00
struct sysfs_dirent * parent_sd = kobj - > sd - > s_parent ;
const void * new_ns = NULL ;
if ( sysfs_ns_type ( parent_sd ) )
new_ns = kobj - > ktype - > namespace ( kobj ) ;
return sysfs_rename ( kobj - > sd , parent_sd , new_ns , new_name ) ;
2009-11-21 03:08:57 +03:00
}
2007-06-13 23:27:25 +04:00
int sysfs_move_dir ( struct kobject * kobj , struct kobject * new_parent_kobj )
2006-11-20 19:07:51 +03:00
{
2007-06-13 23:27:25 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
struct sysfs_dirent * new_parent_sd ;
2010-03-30 22:31:26 +04:00
const void * new_ns = NULL ;
2006-11-20 19:07:51 +03:00
2007-06-13 23:27:25 +04:00
BUG_ON ( ! sd - > s_parent ) ;
2010-03-30 22:31:26 +04:00
if ( sysfs_ns_type ( sd - > s_parent ) )
new_ns = kobj - > ktype - > namespace ( kobj ) ;
2009-11-21 03:08:57 +03:00
new_parent_sd = new_parent_kobj & & new_parent_kobj - > sd ?
2009-10-06 17:33:35 +04:00
new_parent_kobj - > sd : & sysfs_root ;
2007-06-13 23:27:25 +04:00
2010-03-30 22:31:26 +04:00
return sysfs_rename ( sd , new_parent_sd , new_ns , sd - > s_name ) ;
2006-11-20 19:07:51 +03:00
}
2005-04-17 02:20:36 +04:00
/* 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 ;
}
2010-01-02 01:43:53 +03:00
static int sysfs_dir_release ( struct inode * inode , struct file * filp )
{
sysfs_put ( filp - > private_data ) ;
return 0 ;
}
2010-03-30 22:31:26 +04:00
static struct sysfs_dirent * sysfs_dir_pos ( const void * ns ,
2011-12-19 08:05:43 +04:00
struct sysfs_dirent * parent_sd , loff_t hash , struct sysfs_dirent * pos )
2010-01-02 01:43:53 +03:00
{
if ( pos ) {
int valid = ! ( pos - > s_flags & SYSFS_FLAG_REMOVED ) & &
pos - > s_parent = = parent_sd & &
2011-12-19 08:05:43 +04:00
hash = = pos - > s_hash ;
2010-01-02 01:43:53 +03:00
sysfs_put ( pos ) ;
2010-03-30 22:31:26 +04:00
if ( ! valid )
pos = NULL ;
2010-01-02 01:43:53 +03:00
}
2011-12-19 08:05:43 +04:00
if ( ! pos & & ( hash > 1 ) & & ( hash < INT_MAX ) ) {
struct rb_node * node = parent_sd - > s_dir . children . rb_node ;
while ( node ) {
pos = to_sysfs_dirent ( node ) ;
if ( hash < pos - > s_hash )
node = node - > rb_left ;
else if ( hash > pos - > s_hash )
node = node - > rb_right ;
else
2011-07-26 01:57:03 +04:00
break ;
}
}
2011-12-19 08:05:43 +04:00
/* Skip over entries in the wrong namespace */
2011-10-25 16:38:41 +04:00
while ( pos & & pos - > s_ns ! = ns ) {
2011-12-19 08:05:43 +04:00
struct rb_node * node = rb_next ( & pos - > s_rb ) ;
if ( ! node )
2011-07-26 01:57:03 +04:00
pos = NULL ;
else
2011-12-19 08:05:43 +04:00
pos = to_sysfs_dirent ( node ) ;
2010-01-02 01:43:53 +03:00
}
return pos ;
}
2010-03-30 22:31:26 +04:00
static struct sysfs_dirent * sysfs_dir_next_pos ( const void * ns ,
struct sysfs_dirent * parent_sd , ino_t ino , struct sysfs_dirent * pos )
2010-01-02 01:43:53 +03:00
{
2010-03-30 22:31:26 +04:00
pos = sysfs_dir_pos ( ns , parent_sd , ino , pos ) ;
2011-07-26 01:57:03 +04:00
if ( pos ) do {
2011-12-19 08:05:43 +04:00
struct rb_node * node = rb_next ( & pos - > s_rb ) ;
if ( ! node )
2011-07-26 01:57:03 +04:00
pos = NULL ;
else
2011-12-19 08:05:43 +04:00
pos = to_sysfs_dirent ( node ) ;
2011-10-25 16:38:41 +04:00
} while ( pos & & pos - > s_ns ! = ns ) ;
2010-01-02 01:43:53 +03:00
return pos ;
}
2005-04-17 02:20:36 +04:00
static int sysfs_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
2006-12-08 13:36:36 +03:00
struct dentry * dentry = filp - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct sysfs_dirent * parent_sd = dentry - > d_fsdata ;
2010-01-02 01:43:53 +03:00
struct sysfs_dirent * pos = filp - > private_data ;
2010-03-30 22:31:26 +04:00
enum kobj_ns_type type ;
const void * ns ;
2005-04-17 02:20:36 +04:00
ino_t ino ;
2010-03-30 22:31:26 +04:00
type = sysfs_ns_type ( parent_sd ) ;
ns = sysfs_info ( dentry - > d_sb ) - > ns [ type ] ;
2007-08-20 16:36:30 +04:00
if ( filp - > f_pos = = 0 ) {
ino = parent_sd - > s_ino ;
if ( filldir ( dirent , " . " , 1 , filp - > f_pos , ino , DT_DIR ) = = 0 )
2005-04-17 02:20:36 +04:00
filp - > f_pos + + ;
2007-08-20 16:36:30 +04:00
}
if ( filp - > f_pos = = 1 ) {
if ( parent_sd - > s_parent )
ino = parent_sd - > s_parent - > s_ino ;
else
ino = parent_sd - > s_ino ;
if ( filldir ( dirent , " .. " , 2 , filp - > f_pos , ino , DT_DIR ) = = 0 )
2005-04-17 02:20:36 +04:00
filp - > f_pos + + ;
2007-08-20 16:36:30 +04:00
}
2010-01-02 01:43:53 +03:00
mutex_lock ( & sysfs_mutex ) ;
2010-03-30 22:31:26 +04:00
for ( pos = sysfs_dir_pos ( ns , parent_sd , filp - > f_pos , pos ) ;
2010-01-02 01:43:53 +03:00
pos ;
2010-03-30 22:31:26 +04:00
pos = sysfs_dir_next_pos ( ns , parent_sd , filp - > f_pos , pos ) ) {
2010-01-02 01:43:53 +03:00
const char * name ;
unsigned int type ;
int len , ret ;
name = pos - > s_name ;
len = strlen ( name ) ;
ino = pos - > s_ino ;
type = dt_type ( pos ) ;
2011-12-19 08:05:43 +04:00
filp - > f_pos = pos - > s_hash ;
2010-01-02 01:43:53 +03:00
filp - > private_data = sysfs_get ( pos ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2010-01-02 01:43:53 +03:00
ret = filldir ( dirent , name , len , filp - > f_pos , ino , type ) ;
mutex_lock ( & sysfs_mutex ) ;
if ( ret < 0 )
break ;
}
mutex_unlock ( & sysfs_mutex ) ;
if ( ( filp - > f_pos > 1 ) & & ! pos ) { /* EOF */
filp - > f_pos = INT_MAX ;
filp - > private_data = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-08-20 16:36:30 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-20 16:36:30 +04:00
2006-03-28 13:56:42 +04:00
const struct file_operations sysfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
. readdir = sysfs_readdir ,
2010-01-02 01:43:53 +03:00
. release = sysfs_dir_release ,
2008-09-03 23:53:01 +04:00
. llseek = generic_file_llseek ,
2005-04-17 02:20:36 +04:00
} ;