2013-11-24 18:54:58 +04:00
/*
* fs / kernfs / dir . c - kernfs directory implementation
*
* Copyright ( c ) 2001 - 3 Patrick Mochel
* Copyright ( c ) 2007 SUSE Linux Products GmbH
* Copyright ( c ) 2007 , 2013 Tejun Heo < tj @ kernel . org >
*
* This file is released under the GPLv2 .
*/
2013-11-28 23:54:33 +04:00
# include <linux/fs.h>
# include <linux/namei.h>
# include <linux/idr.h>
# include <linux/slab.h>
# include <linux/security.h>
# include <linux/hash.h>
# include "kernfs-internal.h"
2013-12-11 23:11:57 +04:00
DEFINE_MUTEX ( kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
# define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
2013-11-28 23:54:33 +04:00
/**
* sysfs_name_hash
* @ name : Null terminated string to hash
* @ ns : Namespace tag to hash
*
* Returns 31 bit hash of ns + name ( so it fits in an off_t )
*/
static unsigned int sysfs_name_hash ( const char * name , const void * ns )
{
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 char * name ,
2013-12-11 23:11:53 +04:00
const void * ns , const struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:54 +04:00
if ( hash ! = kn - > hash )
return hash - kn - > hash ;
if ( ns ! = kn - > ns )
return ns - kn - > ns ;
return strcmp ( name , kn - > name ) ;
2013-11-28 23:54:33 +04:00
}
2013-12-11 23:11:53 +04:00
static int sysfs_sd_compare ( const struct kernfs_node * left ,
const struct kernfs_node * right )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:54 +04:00
return sysfs_name_compare ( left - > hash , left - > name , left - > ns , right ) ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_link_sibling - link kernfs_node into sibling rbtree
* @ kn : kernfs_node of interest
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Link @ kn into its sibling rbtree which starts from
2013-12-11 23:11:54 +04:00
* @ kn - > parent - > dir . children .
2013-11-28 23:54:33 +04:00
*
* Locking :
2013-12-11 23:11:57 +04:00
* mutex_lock ( kernfs_mutex )
2013-11-28 23:54:33 +04:00
*
* RETURNS :
* 0 on susccess - EEXIST on failure .
*/
2013-12-11 23:11:53 +04:00
static int sysfs_link_sibling ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:54 +04:00
struct rb_node * * node = & kn - > parent - > dir . children . rb_node ;
2013-11-28 23:54:33 +04:00
struct rb_node * parent = NULL ;
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( kn ) = = KERNFS_DIR )
2013-12-11 23:11:54 +04:00
kn - > parent - > dir . subdirs + + ;
2013-11-28 23:54:33 +04:00
while ( * node ) {
2013-12-11 23:11:53 +04:00
struct kernfs_node * pos ;
2013-11-28 23:54:33 +04:00
int result ;
2013-12-11 23:11:53 +04:00
pos = rb_to_kn ( * node ) ;
2013-11-28 23:54:33 +04:00
parent = * node ;
2013-12-11 23:11:53 +04:00
result = sysfs_sd_compare ( kn , pos ) ;
2013-11-28 23:54:33 +04:00
if ( result < 0 )
2013-12-11 23:11:54 +04:00
node = & pos - > rb . rb_left ;
2013-11-28 23:54:33 +04:00
else if ( result > 0 )
2013-12-11 23:11:54 +04:00
node = & pos - > rb . rb_right ;
2013-11-28 23:54:33 +04:00
else
return - EEXIST ;
}
/* add new node and rebalance the tree */
2013-12-11 23:11:54 +04:00
rb_link_node ( & kn - > rb , parent , node ) ;
rb_insert_color ( & kn - > rb , & kn - > parent - > dir . children ) ;
2013-11-28 23:54:33 +04:00
return 0 ;
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_unlink_sibling - unlink kernfs_node from sibling rbtree
* @ kn : kernfs_node of interest
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Unlink @ kn from its sibling rbtree which starts from
2013-12-11 23:11:54 +04:00
* kn - > parent - > dir . children .
2013-11-28 23:54:33 +04:00
*
* Locking :
2013-12-11 23:11:57 +04:00
* mutex_lock ( kernfs_mutex )
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
static void sysfs_unlink_sibling ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( kn ) = = KERNFS_DIR )
2013-12-11 23:11:54 +04:00
kn - > parent - > dir . subdirs - - ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
rb_erase ( & kn - > rb , & kn - > parent - > dir . children ) ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_get_active - get an active reference to kernfs_node
* @ kn : kernfs_node to get an active reference to
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Get an active reference of @ kn . This function is noop if @ kn
2013-11-28 23:54:33 +04:00
* is NULL .
*
* RETURNS :
2013-12-11 23:11:53 +04:00
* Pointer to @ kn on success , NULL on failure .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
struct kernfs_node * sysfs_get_active ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
if ( unlikely ( ! kn ) )
2013-11-28 23:54:33 +04:00
return NULL ;
2013-12-11 23:11:54 +04:00
if ( ! atomic_inc_unless_negative ( & kn - > active ) )
2013-11-28 23:54:33 +04:00
return NULL ;
2013-12-11 23:11:56 +04:00
if ( kn - > flags & KERNFS_LOCKDEP )
2013-12-11 23:11:53 +04:00
rwsem_acquire_read ( & kn - > dep_map , 0 , 1 , _RET_IP_ ) ;
return kn ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_put_active - put an active reference to kernfs_node
* @ kn : kernfs_node to put an active reference to
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Put an active reference to @ kn . This function is noop if @ kn
2013-11-28 23:54:33 +04:00
* is NULL .
*/
2013-12-11 23:11:53 +04:00
void sysfs_put_active ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
int v ;
2013-12-11 23:11:53 +04:00
if ( unlikely ( ! kn ) )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:56 +04:00
if ( kn - > flags & KERNFS_LOCKDEP )
2013-12-11 23:11:53 +04:00
rwsem_release ( & kn - > dep_map , 1 , _RET_IP_ ) ;
2013-12-11 23:11:54 +04:00
v = atomic_dec_return ( & kn - > active ) ;
2013-12-11 23:11:56 +04:00
if ( likely ( v ! = KN_DEACTIVATED_BIAS ) )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:53 +04:00
/*
* atomic_dec_return ( ) is a mb ( ) , we ' ll always see the updated
* kn - > u . completion .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
complete ( kn - > u . completion ) ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_deactivate - deactivate kernfs_node
* @ kn : kernfs_node to deactivate
2013-11-28 23:54:33 +04:00
*
* Deny new active references and drain existing ones .
*/
2013-12-11 23:11:53 +04:00
static void sysfs_deactivate ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
DECLARE_COMPLETION_ONSTACK ( wait ) ;
int v ;
2013-12-11 23:11:56 +04:00
BUG_ON ( ! ( kn - > flags & KERNFS_REMOVED ) ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:56 +04:00
if ( ! ( kernfs_type ( kn ) & KERNFS_ACTIVE_REF ) )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:53 +04:00
kn - > u . completion = ( void * ) & wait ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
rwsem_acquire ( & kn - > dep_map , 0 , 0 , _RET_IP_ ) ;
2013-11-28 23:54:33 +04:00
/* atomic_add_return() is a mb(), put_active() will always see
2013-12-11 23:11:53 +04:00
* the updated kn - > u . completion .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:56 +04:00
v = atomic_add_return ( KN_DEACTIVATED_BIAS , & kn - > active ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:56 +04:00
if ( v ! = KN_DEACTIVATED_BIAS ) {
2013-12-11 23:11:53 +04:00
lock_contended ( & kn - > dep_map , _RET_IP_ ) ;
2013-11-28 23:54:33 +04:00
wait_for_completion ( & wait ) ;
}
2013-12-11 23:11:53 +04:00
lock_acquired ( & kn - > dep_map , _RET_IP_ ) ;
rwsem_release ( & kn - > dep_map , 1 , _RET_IP_ ) ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* kernfs_get - get a reference count on a kernfs_node
* @ kn : the target kernfs_node
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
void kernfs_get ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
if ( kn ) {
2013-12-11 23:11:54 +04:00
WARN_ON ( ! atomic_read ( & kn - > count ) ) ;
atomic_inc ( & kn - > count ) ;
2013-11-28 23:54:33 +04:00
}
}
EXPORT_SYMBOL_GPL ( kernfs_get ) ;
/**
2013-12-11 23:11:53 +04:00
* kernfs_put - put a reference count on a kernfs_node
* @ kn : the target kernfs_node
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Put a reference count of @ kn and destroy it if it reached zero .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
void kernfs_put ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2013-11-28 23:54:40 +04:00
struct kernfs_root * root ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
if ( ! kn | | ! atomic_dec_and_test ( & kn - > count ) )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:53 +04:00
root = kernfs_root ( kn ) ;
2013-11-28 23:54:33 +04:00
repeat :
/* Moving/renaming is always done while holding reference.
2013-12-11 23:11:54 +04:00
* kn - > parent won ' t change beneath us .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:54 +04:00
parent = kn - > parent ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:56 +04:00
WARN ( ! ( kn - > flags & KERNFS_REMOVED ) ,
2013-11-28 23:54:33 +04:00
" sysfs: free using entry: %s/%s \n " ,
2013-12-11 23:11:54 +04:00
parent ? parent - > name : " " , kn - > name ) ;
2013-12-11 23:11:53 +04:00
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( kn ) = = KERNFS_LINK )
2013-12-11 23:11:54 +04:00
kernfs_put ( kn - > symlink . target_kn ) ;
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( kn ) & KERNFS_COPY_NAME )
2013-12-11 23:11:54 +04:00
kfree ( kn - > name ) ;
if ( kn - > iattr ) {
if ( kn - > iattr - > ia_secdata )
security_release_secctx ( kn - > iattr - > ia_secdata ,
kn - > iattr - > ia_secdata_len ) ;
simple_xattrs_free ( & kn - > iattr - > xattrs ) ;
2013-11-24 02:40:02 +04:00
}
2013-12-11 23:11:54 +04:00
kfree ( kn - > iattr ) ;
ida_simple_remove ( & root - > ino_ida , kn - > ino ) ;
2013-12-11 23:11:57 +04:00
kmem_cache_free ( kernfs_node_cache , kn ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
kn = parent ;
if ( kn ) {
2013-12-11 23:11:54 +04:00
if ( atomic_dec_and_test ( & kn - > count ) )
2013-11-28 23:54:40 +04:00
goto repeat ;
} else {
2013-12-11 23:11:53 +04:00
/* just released the root kn, free @root too */
2013-11-28 23:54:41 +04:00
ida_destroy ( & root - > ino_ida ) ;
2013-11-28 23:54:40 +04:00
kfree ( root ) ;
}
2013-11-28 23:54:33 +04:00
}
EXPORT_SYMBOL_GPL ( kernfs_put ) ;
static int sysfs_dentry_delete ( const struct dentry * dentry )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn = dentry - > d_fsdata ;
2013-12-11 23:11:56 +04:00
return ! ( kn & & ! ( kn - > flags & KERNFS_REMOVED ) ) ;
2013-11-28 23:54:33 +04:00
}
static int sysfs_dentry_revalidate ( struct dentry * dentry , unsigned int flags )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
if ( flags & LOOKUP_RCU )
return - ECHILD ;
2013-12-11 23:11:53 +04:00
kn = dentry - > d_fsdata ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
/* The sysfs dirent has been deleted */
2013-12-11 23:11:56 +04:00
if ( kn - > flags & KERNFS_REMOVED )
2013-11-28 23:54:33 +04:00
goto out_bad ;
/* The sysfs dirent has been moved? */
2013-12-11 23:11:54 +04:00
if ( dentry - > d_parent - > d_fsdata ! = kn - > parent )
2013-11-28 23:54:33 +04:00
goto out_bad ;
/* The sysfs dirent has been renamed */
2013-12-11 23:11:54 +04:00
if ( strcmp ( dentry - > d_name . name , kn - > name ) ! = 0 )
2013-11-28 23:54:33 +04:00
goto out_bad ;
/* The sysfs dirent has been moved to a different namespace */
2013-12-11 23:11:54 +04:00
if ( kn - > parent & & kernfs_ns_enabled ( kn - > parent ) & &
2013-12-11 23:11:55 +04:00
kernfs_info ( dentry - > d_sb ) - > ns ! = kn - > ns )
2013-11-28 23:54:33 +04:00
goto out_bad ;
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
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 .
*
* 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 .
*/
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
/* 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 ( check_submounts_and_drop ( dentry ) ! = 0 )
goto out_valid ;
return 0 ;
}
static void sysfs_dentry_release ( struct dentry * dentry )
{
kernfs_put ( dentry - > d_fsdata ) ;
}
2013-12-11 23:11:57 +04:00
const struct dentry_operations kernfs_dops = {
2013-11-28 23:54:33 +04:00
. d_revalidate = sysfs_dentry_revalidate ,
. d_delete = sysfs_dentry_delete ,
. d_release = sysfs_dentry_release ,
} ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * sysfs_new_dirent ( struct kernfs_root * root ,
const char * name , umode_t mode , int type )
2013-11-28 23:54:33 +04:00
{
char * dup_name = NULL ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:41 +04:00
int ret ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:56 +04:00
if ( type & KERNFS_COPY_NAME ) {
2013-11-28 23:54:33 +04:00
name = dup_name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! name )
return NULL ;
}
2013-12-11 23:11:57 +04:00
kn = kmem_cache_zalloc ( kernfs_node_cache , GFP_KERNEL ) ;
2013-12-11 23:11:53 +04:00
if ( ! kn )
2013-11-28 23:54:33 +04:00
goto err_out1 ;
2013-11-28 23:54:41 +04:00
ret = ida_simple_get ( & root - > ino_ida , 1 , 0 , GFP_KERNEL ) ;
if ( ret < 0 )
2013-11-28 23:54:33 +04:00
goto err_out2 ;
2013-12-11 23:11:54 +04:00
kn - > ino = ret ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
atomic_set ( & kn - > count , 1 ) ;
atomic_set ( & kn - > active , 0 ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
kn - > name = name ;
kn - > mode = mode ;
2013-12-11 23:11:56 +04:00
kn - > flags = type | KERNFS_REMOVED ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
return kn ;
2013-11-28 23:54:33 +04:00
err_out2 :
2013-12-11 23:11:57 +04:00
kmem_cache_free ( kernfs_node_cache , kn ) ;
2013-11-28 23:54:33 +04:00
err_out1 :
kfree ( dup_name ) ;
return NULL ;
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_addrm_start - prepare for kernfs_node add / remove
2013-12-11 23:11:55 +04:00
* @ acxt : pointer to kernfs_addrm_cxt to be used
2013-11-28 23:54:33 +04:00
*
* This function is called when the caller is about to add or remove
2013-12-11 23:11:57 +04:00
* kernfs_node . This function acquires kernfs_mutex . @ acxt is used
* to keep and pass context to other addrm functions .
2013-11-28 23:54:33 +04:00
*
* LOCKING :
2013-12-11 23:11:57 +04:00
* Kernel thread context ( may sleep ) . kernfs_mutex is locked on
2013-11-28 23:54:33 +04:00
* return .
*/
2013-12-11 23:11:55 +04:00
void sysfs_addrm_start ( struct kernfs_addrm_cxt * acxt )
2013-12-11 23:11:57 +04:00
__acquires ( kernfs_mutex )
2013-11-28 23:54:33 +04:00
{
memset ( acxt , 0 , sizeof ( * acxt ) ) ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_add_one - add kernfs_node to parent without warning
2013-11-28 23:54:33 +04:00
* @ acxt : addrm context to use
2013-12-11 23:11:53 +04:00
* @ kn : kernfs_node to be added
* @ parent : the parent kernfs_node to add @ kn to
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:54 +04:00
* Get @ parent and set @ kn - > parent to it and increment nlink of the
* parent inode if @ kn is a directory and link into the children list
* of the parent .
2013-11-28 23:54:33 +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 ( ) .
*
* RETURNS :
* 0 on success , - EEXIST if entry with the given name already
* exists .
*/
2013-12-11 23:11:55 +04:00
int sysfs_add_one ( struct kernfs_addrm_cxt * acxt , struct kernfs_node * kn ,
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
bool has_ns = kernfs_ns_enabled ( parent ) ;
2013-12-11 23:11:55 +04:00
struct kernfs_iattrs * ps_iattr ;
2013-11-28 23:54:33 +04:00
int ret ;
2013-12-11 23:11:54 +04:00
if ( has_ns ! = ( bool ) kn - > ns ) {
2013-11-28 23:54:33 +04:00
WARN ( 1 , KERN_WARNING " sysfs: ns %s in '%s' for '%s' \n " ,
2013-12-11 23:11:54 +04:00
has_ns ? " required " : " invalid " , parent - > name , kn - > name ) ;
2013-11-28 23:54:33 +04:00
return - EINVAL ;
}
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( parent ) ! = KERNFS_DIR )
2013-11-28 23:54:33 +04:00
return - EINVAL ;
2013-12-11 23:11:54 +04:00
kn - > hash = sysfs_name_hash ( kn - > name , kn - > ns ) ;
kn - > parent = parent ;
2013-12-11 23:11:53 +04:00
kernfs_get ( parent ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
ret = sysfs_link_sibling ( kn ) ;
2013-11-28 23:54:33 +04:00
if ( ret )
return ret ;
/* Update timestamps on the parent */
2013-12-11 23:11:54 +04:00
ps_iattr = parent - > iattr ;
2013-11-28 23:54:33 +04:00
if ( ps_iattr ) {
struct iattr * ps_iattrs = & ps_iattr - > ia_iattr ;
ps_iattrs - > ia_ctime = ps_iattrs - > ia_mtime = CURRENT_TIME ;
}
/* Mark the entry added into directory tree */
2013-12-11 23:11:56 +04:00
kn - > flags & = ~ KERNFS_REMOVED ;
2013-11-28 23:54:33 +04:00
return 0 ;
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_remove_one - remove kernfs_node from parent
2013-11-28 23:54:33 +04:00
* @ acxt : addrm context to use
2013-12-11 23:11:53 +04:00
* @ kn : kernfs_node to be removed
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Mark @ kn removed and drop nlink of parent inode if @ kn is a
* directory . @ kn is unlinked from the children list .
2013-11-28 23:54:33 +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 ( ) .
*/
2013-12-11 23:11:55 +04:00
static void sysfs_remove_one ( struct kernfs_addrm_cxt * acxt ,
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:55 +04:00
struct kernfs_iattrs * ps_iattr ;
2013-11-28 23:54:33 +04:00
/*
* Removal can be called multiple times on the same node . Only the
* first invocation is effective and puts the base ref .
*/
2013-12-11 23:11:56 +04:00
if ( kn - > flags & KERNFS_REMOVED )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:54 +04:00
if ( kn - > parent ) {
2013-12-11 23:11:53 +04:00
sysfs_unlink_sibling ( kn ) ;
2013-11-28 23:54:33 +04:00
2013-11-28 23:54:40 +04:00
/* Update timestamps on the parent */
2013-12-11 23:11:54 +04:00
ps_iattr = kn - > parent - > iattr ;
2013-11-28 23:54:40 +04:00
if ( ps_iattr ) {
ps_iattr - > ia_iattr . ia_ctime = CURRENT_TIME ;
ps_iattr - > ia_iattr . ia_mtime = CURRENT_TIME ;
}
2013-11-28 23:54:33 +04:00
}
2013-12-11 23:11:56 +04:00
kn - > flags | = KERNFS_REMOVED ;
2013-12-11 23:11:53 +04:00
kn - > u . removed_list = acxt - > removed ;
acxt - > removed = kn ;
2013-11-28 23:54:33 +04:00
}
/**
2013-12-11 23:11:53 +04:00
* sysfs_addrm_finish - finish up kernfs_node add / remove
2013-11-28 23:54:33 +04:00
* @ acxt : addrm context to finish up
*
2013-12-11 23:11:53 +04:00
* Finish up kernfs_node add / remove . Resources acquired by
* sysfs_addrm_start ( ) are released and removed kernfs_nodes are
2013-11-28 23:54:33 +04:00
* cleaned up .
*
* LOCKING :
2013-12-11 23:11:57 +04:00
* kernfs_mutex is released .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:55 +04:00
void sysfs_addrm_finish ( struct kernfs_addrm_cxt * acxt )
2013-12-11 23:11:57 +04:00
__releases ( kernfs_mutex )
2013-11-28 23:54:33 +04:00
{
/* release resources acquired by sysfs_addrm_start() */
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
/* kill removed kernfs_nodes */
2013-11-28 23:54:33 +04:00
while ( acxt - > removed ) {
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn = acxt - > removed ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
acxt - > removed = kn - > u . removed_list ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
sysfs_deactivate ( kn ) ;
sysfs_unmap_bin_file ( kn ) ;
kernfs_put ( kn ) ;
2013-11-28 23:54:33 +04:00
}
}
/**
2013-12-11 23:11:53 +04:00
* kernfs_find_ns - find kernfs_node with the given name
* @ parent : kernfs_node to search under
2013-11-28 23:54:33 +04:00
* @ name : name to look for
* @ ns : the namespace tag to use
*
2013-12-11 23:11:53 +04:00
* Look for kernfs_node with name @ name under @ parent . Returns pointer to
* the found kernfs_node on success , % NULL on failure .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
static struct kernfs_node * kernfs_find_ns ( struct kernfs_node * parent ,
const unsigned char * name ,
const void * ns )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:54 +04:00
struct rb_node * node = parent - > dir . children . rb_node ;
2013-11-30 02:19:09 +04:00
bool has_ns = kernfs_ns_enabled ( parent ) ;
2013-11-28 23:54:33 +04:00
unsigned int hash ;
2013-12-11 23:11:57 +04:00
lockdep_assert_held ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
if ( has_ns ! = ( bool ) ns ) {
WARN ( 1 , KERN_WARNING " sysfs: ns %s in '%s' for '%s' \n " ,
2013-12-11 23:11:54 +04:00
has_ns ? " required " : " invalid " , parent - > name , name ) ;
2013-11-28 23:54:33 +04:00
return NULL ;
}
hash = sysfs_name_hash ( name , ns ) ;
while ( node ) {
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
int result ;
2013-12-11 23:11:53 +04:00
kn = rb_to_kn ( node ) ;
result = sysfs_name_compare ( hash , name , ns , kn ) ;
2013-11-28 23:54:33 +04:00
if ( result < 0 )
node = node - > rb_left ;
else if ( result > 0 )
node = node - > rb_right ;
else
2013-12-11 23:11:53 +04:00
return kn ;
2013-11-28 23:54:33 +04:00
}
return NULL ;
}
/**
2013-12-11 23:11:53 +04:00
* kernfs_find_and_get_ns - find and get kernfs_node with the given name
* @ parent : kernfs_node to search under
2013-11-28 23:54:33 +04:00
* @ name : name to look for
* @ ns : the namespace tag to use
*
2013-12-11 23:11:53 +04:00
* Look for kernfs_node with name @ name under @ parent and get a reference
2013-11-28 23:54:33 +04:00
* if found . This function may sleep and returns pointer to the found
2013-12-11 23:11:53 +04:00
* kernfs_node on success , % NULL on failure .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
struct kernfs_node * kernfs_find_and_get_ns ( struct kernfs_node * parent ,
const char * name , const void * ns )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-12-11 23:11:53 +04:00
kn = kernfs_find_ns ( parent , name , ns ) ;
kernfs_get ( kn ) ;
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
return kn ;
2013-11-28 23:54:33 +04:00
}
EXPORT_SYMBOL_GPL ( kernfs_find_and_get_ns ) ;
2013-11-28 23:54:40 +04:00
/**
* kernfs_create_root - create a new kernfs hierarchy
* @ priv : opaque data associated with the new directory
*
* Returns the root of the new hierarchy on success , ERR_PTR ( ) value on
* failure .
*/
struct kernfs_root * kernfs_create_root ( void * priv )
{
struct kernfs_root * root ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:40 +04:00
root = kzalloc ( sizeof ( * root ) , GFP_KERNEL ) ;
if ( ! root )
return ERR_PTR ( - ENOMEM ) ;
2013-11-28 23:54:41 +04:00
ida_init ( & root - > ino_ida ) ;
2013-12-11 23:11:56 +04:00
kn = sysfs_new_dirent ( root , " " , S_IFDIR | S_IRUGO | S_IXUGO , KERNFS_DIR ) ;
2013-12-11 23:11:53 +04:00
if ( ! kn ) {
2013-11-28 23:54:41 +04:00
ida_destroy ( & root - > ino_ida ) ;
2013-11-28 23:54:40 +04:00
kfree ( root ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2013-12-11 23:11:56 +04:00
kn - > flags & = ~ KERNFS_REMOVED ;
2013-12-11 23:11:53 +04:00
kn - > priv = priv ;
2013-12-11 23:11:54 +04:00
kn - > dir . root = root ;
2013-11-28 23:54:40 +04:00
2013-12-11 23:11:53 +04:00
root - > kn = kn ;
2013-11-28 23:54:40 +04:00
return root ;
}
/**
* kernfs_destroy_root - destroy a kernfs hierarchy
* @ root : root of the hierarchy to destroy
*
* Destroy the hierarchy anchored at @ root by removing all existing
* directories and destroying @ root .
*/
void kernfs_destroy_root ( struct kernfs_root * root )
{
2013-12-11 23:11:53 +04:00
kernfs_remove ( root - > kn ) ; /* will also free @root */
2013-11-28 23:54:40 +04:00
}
2013-11-28 23:54:33 +04:00
/**
* kernfs_create_dir_ns - create a directory
* @ parent : parent in which to create a new directory
* @ name : name of the new directory
* @ priv : opaque data associated with the new directory
* @ ns : optional namespace tag of the directory
*
* Returns the created node on success , ERR_PTR ( ) value on failure .
*/
2013-12-11 23:11:53 +04:00
struct kernfs_node * kernfs_create_dir_ns ( struct kernfs_node * parent ,
const char * name , void * priv ,
const void * ns )
2013-11-28 23:54:33 +04:00
{
umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ;
2013-12-11 23:11:55 +04:00
struct kernfs_addrm_cxt acxt ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
int rc ;
/* allocate */
2013-12-11 23:11:56 +04:00
kn = sysfs_new_dirent ( kernfs_root ( parent ) , name , mode , KERNFS_DIR ) ;
2013-12-11 23:11:53 +04:00
if ( ! kn )
2013-11-28 23:54:33 +04:00
return ERR_PTR ( - ENOMEM ) ;
2013-12-11 23:11:54 +04:00
kn - > dir . root = parent - > dir . root ;
kn - > ns = ns ;
2013-12-11 23:11:53 +04:00
kn - > priv = priv ;
2013-11-28 23:54:33 +04:00
/* link in */
sysfs_addrm_start ( & acxt ) ;
2013-12-11 23:11:53 +04:00
rc = sysfs_add_one ( & acxt , kn , parent ) ;
2013-11-28 23:54:33 +04:00
sysfs_addrm_finish ( & acxt ) ;
if ( ! rc )
2013-12-11 23:11:53 +04:00
return kn ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
kernfs_put ( kn ) ;
2013-11-28 23:54:33 +04:00
return ERR_PTR ( rc ) ;
}
static struct dentry * sysfs_lookup ( struct inode * dir , struct dentry * dentry ,
unsigned int flags )
{
struct dentry * ret = NULL ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent = dentry - > d_parent - > d_fsdata ;
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
struct inode * inode ;
const void * ns = NULL ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
if ( kernfs_ns_enabled ( parent ) )
2013-12-11 23:11:55 +04:00
ns = kernfs_info ( dir - > i_sb ) - > ns ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
kn = kernfs_find_ns ( parent , dentry - > d_name . name , ns ) ;
2013-11-28 23:54:33 +04:00
/* no such entry */
2013-12-11 23:11:53 +04:00
if ( ! kn ) {
2013-11-28 23:54:33 +04:00
ret = ERR_PTR ( - ENOENT ) ;
goto out_unlock ;
}
2013-12-11 23:11:53 +04:00
kernfs_get ( kn ) ;
dentry - > d_fsdata = kn ;
2013-11-28 23:54:33 +04:00
/* attach dentry and inode */
2013-12-11 23:11:53 +04:00
inode = sysfs_get_inode ( dir - > i_sb , kn ) ;
2013-11-28 23:54:33 +04:00
if ( ! inode ) {
ret = ERR_PTR ( - ENOMEM ) ;
goto out_unlock ;
}
/* instantiate and hash dentry */
ret = d_materialise_unique ( dentry , inode ) ;
out_unlock :
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
return ret ;
}
2013-12-11 23:11:57 +04:00
const struct inode_operations kernfs_dir_iops = {
2013-11-28 23:54:33 +04:00
. lookup = sysfs_lookup ,
. permission = sysfs_permission ,
. setattr = sysfs_setattr ,
. getattr = sysfs_getattr ,
. setxattr = sysfs_setxattr ,
2013-11-24 02:40:02 +04:00
. removexattr = sysfs_removexattr ,
. getxattr = sysfs_getxattr ,
. listxattr = sysfs_listxattr ,
2013-11-28 23:54:33 +04:00
} ;
2013-12-11 23:11:53 +04:00
static struct kernfs_node * sysfs_leftmost_descendant ( struct kernfs_node * pos )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * last ;
2013-11-28 23:54:33 +04:00
while ( true ) {
struct rb_node * rbn ;
last = pos ;
2013-12-11 23:11:56 +04:00
if ( kernfs_type ( pos ) ! = KERNFS_DIR )
2013-11-28 23:54:33 +04:00
break ;
2013-12-11 23:11:54 +04:00
rbn = rb_first ( & pos - > dir . children ) ;
2013-11-28 23:54:33 +04:00
if ( ! rbn )
break ;
2013-12-11 23:11:53 +04:00
pos = rb_to_kn ( rbn ) ;
2013-11-28 23:54:33 +04:00
}
return last ;
}
/**
* sysfs_next_descendant_post - find the next descendant for post - order walk
* @ pos : the current position ( % NULL to initiate traversal )
2013-12-11 23:11:53 +04:00
* @ root : kernfs_node whose descendants to walk
2013-11-28 23:54:33 +04:00
*
* Find the next descendant to visit for post - order traversal of @ root ' s
* descendants . @ root is included in the iteration and the last node to be
* visited .
*/
2013-12-11 23:11:53 +04:00
static struct kernfs_node * sysfs_next_descendant_post ( struct kernfs_node * pos ,
struct kernfs_node * root )
2013-11-28 23:54:33 +04:00
{
struct rb_node * rbn ;
2013-12-11 23:11:57 +04:00
lockdep_assert_held ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
/* if first iteration, visit leftmost descendant which may be root */
if ( ! pos )
return sysfs_leftmost_descendant ( root ) ;
/* if we visited @root, we're done */
if ( pos = = root )
return NULL ;
/* if there's an unvisited sibling, visit its leftmost descendant */
2013-12-11 23:11:54 +04:00
rbn = rb_next ( & pos - > rb ) ;
2013-11-28 23:54:33 +04:00
if ( rbn )
2013-12-11 23:11:53 +04:00
return sysfs_leftmost_descendant ( rb_to_kn ( rbn ) ) ;
2013-11-28 23:54:33 +04:00
/* no sibling left, visit parent */
2013-12-11 23:11:54 +04:00
return pos - > parent ;
2013-11-28 23:54:33 +04:00
}
2013-12-11 23:11:55 +04:00
static void __kernfs_remove ( struct kernfs_addrm_cxt * acxt ,
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * pos , * next ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
if ( ! kn )
2013-11-28 23:54:33 +04:00
return ;
2013-12-11 23:11:54 +04:00
pr_debug ( " sysfs %s: removing \n " , kn - > name ) ;
2013-11-28 23:54:33 +04:00
next = NULL ;
do {
pos = next ;
2013-12-11 23:11:53 +04:00
next = sysfs_next_descendant_post ( pos , kn ) ;
2013-11-28 23:54:33 +04:00
if ( pos )
sysfs_remove_one ( acxt , pos ) ;
} while ( next ) ;
}
/**
2013-12-11 23:11:53 +04:00
* kernfs_remove - remove a kernfs_node recursively
* @ kn : the kernfs_node to remove
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Remove @ kn along with all its subdirectories and files .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
void kernfs_remove ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:55 +04:00
struct kernfs_addrm_cxt acxt ;
2013-11-28 23:54:33 +04:00
sysfs_addrm_start ( & acxt ) ;
2013-12-11 23:11:53 +04:00
__kernfs_remove ( & acxt , kn ) ;
2013-11-28 23:54:33 +04:00
sysfs_addrm_finish ( & acxt ) ;
}
/**
2013-12-11 23:11:53 +04:00
* kernfs_remove_by_name_ns - find a kernfs_node by name and remove it
* @ parent : parent of the target
* @ name : name of the kernfs_node to remove
* @ ns : namespace tag of the kernfs_node to remove
2013-11-28 23:54:33 +04:00
*
2013-12-11 23:11:53 +04:00
* Look for the kernfs_node with @ name and @ ns under @ parent and remove it .
* Returns 0 on success , - ENOENT if such entry doesn ' t exist .
2013-11-28 23:54:33 +04:00
*/
2013-12-11 23:11:53 +04:00
int kernfs_remove_by_name_ns ( struct kernfs_node * parent , const char * name ,
2013-11-28 23:54:33 +04:00
const void * ns )
{
2013-12-11 23:11:55 +04:00
struct kernfs_addrm_cxt acxt ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
if ( ! parent ) {
2013-11-28 23:54:33 +04:00
WARN ( 1 , KERN_WARNING " sysfs: can not remove '%s', no directory \n " ,
name ) ;
return - ENOENT ;
}
sysfs_addrm_start ( & acxt ) ;
2013-12-11 23:11:53 +04:00
kn = kernfs_find_ns ( parent , name , ns ) ;
if ( kn )
__kernfs_remove ( & acxt , kn ) ;
2013-11-28 23:54:33 +04:00
sysfs_addrm_finish ( & acxt ) ;
2013-12-11 23:11:53 +04:00
if ( kn )
2013-11-28 23:54:33 +04:00
return 0 ;
else
return - ENOENT ;
}
/**
* kernfs_rename_ns - move and rename a kernfs_node
2013-12-11 23:11:53 +04:00
* @ kn : target node
2013-11-28 23:54:33 +04:00
* @ new_parent : new parent to put @ sd under
* @ new_name : new name
* @ new_ns : new namespace tag
*/
2013-12-11 23:11:53 +04:00
int kernfs_rename_ns ( struct kernfs_node * kn , struct kernfs_node * new_parent ,
2013-11-28 23:54:33 +04:00
const char * new_name , const void * new_ns )
{
int error ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
error = 0 ;
2013-12-11 23:11:54 +04:00
if ( ( kn - > parent = = new_parent ) & & ( kn - > ns = = new_ns ) & &
( strcmp ( kn - > name , new_name ) = = 0 ) )
2013-11-28 23:54:33 +04:00
goto out ; /* nothing to rename */
error = - EEXIST ;
if ( kernfs_find_ns ( new_parent , new_name , new_ns ) )
goto out ;
2013-12-11 23:11:53 +04:00
/* rename kernfs_node */
2013-12-11 23:11:54 +04:00
if ( strcmp ( kn - > name , new_name ) ! = 0 ) {
2013-11-28 23:54:33 +04:00
error = - ENOMEM ;
new_name = kstrdup ( new_name , GFP_KERNEL ) ;
if ( ! new_name )
goto out ;
2013-12-11 23:11:54 +04:00
kfree ( kn - > name ) ;
kn - > name = new_name ;
2013-11-28 23:54:33 +04:00
}
/*
* Move to the appropriate place in the appropriate directories rbtree .
*/
2013-12-11 23:11:53 +04:00
sysfs_unlink_sibling ( kn ) ;
2013-11-28 23:54:33 +04:00
kernfs_get ( new_parent ) ;
2013-12-11 23:11:54 +04:00
kernfs_put ( kn - > parent ) ;
kn - > ns = new_ns ;
kn - > hash = sysfs_name_hash ( kn - > name , kn - > ns ) ;
kn - > parent = new_parent ;
2013-12-11 23:11:53 +04:00
sysfs_link_sibling ( kn ) ;
2013-11-28 23:54:33 +04:00
error = 0 ;
out :
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
return error ;
}
/* Relationship between s_mode and the DT_xxx types */
2013-12-11 23:11:53 +04:00
static inline unsigned char dt_type ( struct kernfs_node * kn )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:54 +04:00
return ( kn - > mode > > 12 ) & 15 ;
2013-11-28 23:54:33 +04:00
}
static int sysfs_dir_release ( struct inode * inode , struct file * filp )
{
kernfs_put ( filp - > private_data ) ;
return 0 ;
}
2013-12-11 23:11:53 +04:00
static struct kernfs_node * sysfs_dir_pos ( const void * ns ,
struct kernfs_node * parent , loff_t hash , struct kernfs_node * pos )
2013-11-28 23:54:33 +04:00
{
if ( pos ) {
2013-12-11 23:11:56 +04:00
int valid = ! ( pos - > flags & KERNFS_REMOVED ) & &
2013-12-11 23:11:54 +04:00
pos - > parent = = parent & & hash = = pos - > hash ;
2013-11-28 23:54:33 +04:00
kernfs_put ( pos ) ;
if ( ! valid )
pos = NULL ;
}
if ( ! pos & & ( hash > 1 ) & & ( hash < INT_MAX ) ) {
2013-12-11 23:11:54 +04:00
struct rb_node * node = parent - > dir . children . rb_node ;
2013-11-28 23:54:33 +04:00
while ( node ) {
2013-12-11 23:11:53 +04:00
pos = rb_to_kn ( node ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
if ( hash < pos - > hash )
2013-11-28 23:54:33 +04:00
node = node - > rb_left ;
2013-12-11 23:11:54 +04:00
else if ( hash > pos - > hash )
2013-11-28 23:54:33 +04:00
node = node - > rb_right ;
else
break ;
}
}
/* Skip over entries in the wrong namespace */
2013-12-11 23:11:54 +04:00
while ( pos & & pos - > ns ! = ns ) {
struct rb_node * node = rb_next ( & pos - > rb ) ;
2013-11-28 23:54:33 +04:00
if ( ! node )
pos = NULL ;
else
2013-12-11 23:11:53 +04:00
pos = rb_to_kn ( node ) ;
2013-11-28 23:54:33 +04:00
}
return pos ;
}
2013-12-11 23:11:53 +04:00
static struct kernfs_node * sysfs_dir_next_pos ( const void * ns ,
struct kernfs_node * parent , ino_t ino , struct kernfs_node * pos )
2013-11-28 23:54:33 +04:00
{
2013-12-11 23:11:53 +04:00
pos = sysfs_dir_pos ( ns , parent , ino , pos ) ;
2013-11-28 23:54:33 +04:00
if ( pos )
do {
2013-12-11 23:11:54 +04:00
struct rb_node * node = rb_next ( & pos - > rb ) ;
2013-11-28 23:54:33 +04:00
if ( ! node )
pos = NULL ;
else
2013-12-11 23:11:53 +04:00
pos = rb_to_kn ( node ) ;
2013-12-11 23:11:54 +04:00
} while ( pos & & pos - > ns ! = ns ) ;
2013-11-28 23:54:33 +04:00
return pos ;
}
static int sysfs_readdir ( struct file * file , struct dir_context * ctx )
{
struct dentry * dentry = file - > f_path . dentry ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent = dentry - > d_fsdata ;
struct kernfs_node * pos = file - > private_data ;
2013-11-28 23:54:33 +04:00
const void * ns = NULL ;
if ( ! dir_emit_dots ( file , ctx ) )
return 0 ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
if ( kernfs_ns_enabled ( parent ) )
2013-12-11 23:11:55 +04:00
ns = kernfs_info ( dentry - > d_sb ) - > ns ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:53 +04:00
for ( pos = sysfs_dir_pos ( ns , parent , ctx - > pos , pos ) ;
2013-11-28 23:54:33 +04:00
pos ;
2013-12-11 23:11:53 +04:00
pos = sysfs_dir_next_pos ( ns , parent , ctx - > pos , pos ) ) {
2013-12-11 23:11:54 +04:00
const char * name = pos - > name ;
2013-11-28 23:54:33 +04:00
unsigned int type = dt_type ( pos ) ;
int len = strlen ( name ) ;
2013-12-11 23:11:54 +04:00
ino_t ino = pos - > ino ;
2013-11-28 23:54:33 +04:00
2013-12-11 23:11:54 +04:00
ctx - > pos = pos - > hash ;
2013-11-28 23:54:33 +04:00
file - > private_data = pos ;
kernfs_get ( pos ) ;
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
if ( ! dir_emit ( ctx , name , len , ino , type ) )
return 0 ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
}
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:33 +04:00
file - > private_data = NULL ;
ctx - > pos = INT_MAX ;
return 0 ;
}
static loff_t sysfs_dir_llseek ( struct file * file , loff_t offset , int whence )
{
struct inode * inode = file_inode ( file ) ;
loff_t ret ;
mutex_lock ( & inode - > i_mutex ) ;
ret = generic_file_llseek ( file , offset , whence ) ;
mutex_unlock ( & inode - > i_mutex ) ;
return ret ;
}
2013-12-11 23:11:57 +04:00
const struct file_operations kernfs_dir_fops = {
2013-11-28 23:54:33 +04:00
. read = generic_read_dir ,
. iterate = sysfs_readdir ,
. release = sysfs_dir_release ,
. llseek = sysfs_dir_llseek ,
} ;