2005-04-17 02:20:36 +04:00
/*
* symlink . c - operations for sysfs symlinks .
*/
# include <linux/fs.h>
2002-04-09 23:14:34 +04:00
# include <linux/mount.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/kobject.h>
# include <linux/namei.h>
2006-12-20 12:52:44 +03:00
# include <asm/semaphore.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2007-06-13 22:45:15 +04:00
static int object_depth ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
int depth = 0 ;
2007-06-13 22:45:15 +04:00
for ( ; sd - > s_parent ; sd = sd - > s_parent )
depth + + ;
2005-04-17 02:20:36 +04:00
return depth ;
}
2007-06-13 22:45:15 +04:00
static int object_path_length ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
int length = 1 ;
2007-06-13 22:45:15 +04:00
for ( ; sd - > s_parent ; sd = sd - > s_parent )
length + = strlen ( sd - > s_name ) + 1 ;
2005-04-17 02:20:36 +04:00
return length ;
}
2007-06-13 22:45:15 +04:00
static void fill_object_path ( struct sysfs_dirent * sd , char * buffer , int length )
2005-04-17 02:20:36 +04:00
{
- - length ;
2007-06-13 22:45:15 +04:00
for ( ; sd - > s_parent ; sd = sd - > s_parent ) {
int cur = strlen ( sd - > s_name ) ;
2005-04-17 02:20:36 +04:00
/* back up enough to print this bus id with '/' */
length - = cur ;
2007-06-13 22:45:15 +04:00
strncpy ( buffer + length , sd - > s_name , cur ) ;
2005-04-17 02:20:36 +04:00
* ( buffer + - - length ) = ' / ' ;
}
}
2007-06-13 22:45:15 +04:00
static int sysfs_add_link ( struct sysfs_dirent * parent_sd , const char * name ,
struct sysfs_dirent * target_sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * sd ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:15 +04:00
sd = sysfs_new_dirent ( name , S_IFLNK | S_IRWXUGO , SYSFS_KOBJ_LINK ) ;
2007-06-13 22:45:14 +04:00
if ( ! sd )
2007-06-13 22:45:15 +04:00
return - ENOMEM ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:15 +04:00
sd - > s_elem . symlink . target_sd = target_sd ;
2007-06-13 22:45:15 +04:00
sysfs_attach_dirent ( sd , parent_sd , NULL ) ;
2007-06-13 22:45:14 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* sysfs_create_link - create symlink between two objects .
* @ kobj : object whose directory we ' re creating the link in .
* @ target : object we ' re pointing to .
* @ name : name of the symlink .
*/
2005-04-26 11:31:08 +04:00
int sysfs_create_link ( struct kobject * kobj , struct kobject * target , const char * name )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
struct dentry * dentry = NULL ;
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * parent_sd = NULL ;
struct sysfs_dirent * target_sd = NULL ;
2006-03-09 17:10:14 +03:00
int error = - EEXIST ;
2005-04-17 02:20:36 +04:00
2002-04-09 23:14:34 +04:00
BUG_ON ( ! name ) ;
if ( ! kobj ) {
if ( sysfs_mount & & sysfs_mount - > mnt_sb )
dentry = sysfs_mount - > mnt_sb - > s_root ;
} else
dentry = kobj - > dentry ;
if ( ! dentry )
return - EFAULT ;
2007-06-13 22:45:15 +04:00
parent_sd = dentry - > d_fsdata ;
/* target->dentry can go away beneath us but is protected with
* kobj_sysfs_assoc_lock . Fetch target_sd from it .
*/
spin_lock ( & kobj_sysfs_assoc_lock ) ;
if ( target - > dentry )
target_sd = sysfs_get ( target - > dentry - > d_fsdata ) ;
spin_unlock ( & kobj_sysfs_assoc_lock ) ;
if ( ! target_sd )
return - ENOENT ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:59:24 +03:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2007-06-13 23:27:22 +04:00
if ( ! sysfs_find_dirent ( dentry - > d_fsdata , name ) )
2007-06-13 22:45:15 +04:00
error = sysfs_add_link ( parent_sd , name , target_sd ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2007-06-13 22:45:15 +04:00
if ( error )
sysfs_put ( target_sd ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/**
* sysfs_remove_link - remove symlink in object ' s directory .
* @ kobj : object we ' re acting for .
* @ name : name of the symlink to remove .
*/
2005-04-26 11:31:08 +04:00
void sysfs_remove_link ( struct kobject * kobj , const char * name )
2005-04-17 02:20:36 +04:00
{
sysfs_hash_and_remove ( kobj - > dentry , name ) ;
}
2007-06-13 22:45:15 +04:00
static int sysfs_get_target_path ( struct sysfs_dirent * parent_sd ,
struct sysfs_dirent * target_sd , char * path )
2005-04-17 02:20:36 +04:00
{
char * s ;
int depth , size ;
2007-06-13 22:45:15 +04:00
depth = object_depth ( parent_sd ) ;
size = object_path_length ( target_sd ) + depth * 3 - 1 ;
2005-04-17 02:20:36 +04:00
if ( size > PATH_MAX )
return - ENAMETOOLONG ;
pr_debug ( " %s: depth = %d, size = %d \n " , __FUNCTION__ , depth , size ) ;
for ( s = path ; depth - - ; s + = 3 )
strcpy ( s , " ../ " ) ;
2007-06-13 22:45:15 +04:00
fill_object_path ( target_sd , path , size ) ;
2005-04-17 02:20:36 +04:00
pr_debug ( " %s: path = '%s' \n " , __FUNCTION__ , path ) ;
return 0 ;
}
static int sysfs_getlink ( struct dentry * dentry , char * path )
{
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * sd = dentry - > d_fsdata ;
struct sysfs_dirent * parent_sd = sd - > s_parent ;
struct sysfs_dirent * target_sd = sd - > s_elem . symlink . target_sd ;
int error ;
2005-04-17 02:20:36 +04:00
down_read ( & sysfs_rename_sem ) ;
2007-06-13 22:45:15 +04:00
error = sysfs_get_target_path ( parent_sd , target_sd , path ) ;
2005-04-17 02:20:36 +04:00
up_read ( & sysfs_rename_sem ) ;
2007-06-13 22:45:15 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
2005-08-20 05:02:56 +04:00
static void * sysfs_follow_link ( struct dentry * dentry , struct nameidata * nd )
2005-04-17 02:20:36 +04:00
{
int error = - ENOMEM ;
unsigned long page = get_zeroed_page ( GFP_KERNEL ) ;
if ( page )
error = sysfs_getlink ( dentry , ( char * ) page ) ;
nd_set_link ( nd , error ? ERR_PTR ( error ) : ( char * ) page ) ;
2005-08-20 05:02:56 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2005-08-20 05:02:56 +04:00
static void sysfs_put_link ( struct dentry * dentry , struct nameidata * nd , void * cookie )
2005-04-17 02:20:36 +04:00
{
char * page = nd_get_link ( nd ) ;
if ( ! IS_ERR ( page ) )
free_page ( ( unsigned long ) page ) ;
}
2007-02-12 11:55:40 +03:00
const struct inode_operations sysfs_symlink_inode_operations = {
2005-04-17 02:20:36 +04:00
. readlink = generic_readlink ,
. follow_link = sysfs_follow_link ,
. put_link = sysfs_put_link ,
} ;
EXPORT_SYMBOL_GPL ( sysfs_create_link ) ;
EXPORT_SYMBOL_GPL ( sysfs_remove_link ) ;