2013-11-24 09:54:58 -05:00
/*
* fs / kernfs / symlink . c - kernfs symlink 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 14:54:35 -05:00
# include <linux/fs.h>
# include <linux/gfp.h>
# include <linux/namei.h>
# include "kernfs-internal.h"
/**
* kernfs_create_link - create a symlink
* @ parent : directory to create the symlink in
* @ name : name of the symlink
* @ target : target node for the symlink to point to
*
* Returns the created node on success , ERR_PTR ( ) value on error .
*/
2013-12-11 14:11:53 -05:00
struct kernfs_node * kernfs_create_link ( struct kernfs_node * parent ,
const char * name ,
struct kernfs_node * target )
2013-11-28 14:54:35 -05:00
{
2013-12-11 14:11:53 -05:00
struct kernfs_node * kn ;
2013-12-11 14:11:55 -05:00
struct kernfs_addrm_cxt acxt ;
2013-11-28 14:54:35 -05:00
int error ;
2013-12-11 14:11:58 -05:00
kn = kernfs_new_node ( kernfs_root ( parent ) , name , S_IFLNK | S_IRWXUGO ,
KERNFS_LINK ) ;
2013-12-11 14:11:53 -05:00
if ( ! kn )
2013-11-28 14:54:35 -05:00
return ERR_PTR ( - ENOMEM ) ;
2013-11-29 17:19:09 -05:00
if ( kernfs_ns_enabled ( parent ) )
2013-12-11 14:11:54 -05:00
kn - > ns = target - > ns ;
kn - > symlink . target_kn = target ;
2013-11-28 14:54:35 -05:00
kernfs_get ( target ) ; /* ref owned by symlink */
2013-12-11 14:11:58 -05:00
kernfs_addrm_start ( & acxt ) ;
error = kernfs_add_one ( & acxt , kn , parent ) ;
kernfs_addrm_finish ( & acxt ) ;
2013-11-28 14:54:35 -05:00
if ( ! error )
2013-12-11 14:11:53 -05:00
return kn ;
2013-11-28 14:54:35 -05:00
2013-12-11 14:11:53 -05:00
kernfs_put ( kn ) ;
2013-11-28 14:54:35 -05:00
return ERR_PTR ( error ) ;
}
2013-12-11 14:11:58 -05:00
static int kernfs_get_target_path ( struct kernfs_node * parent ,
struct kernfs_node * target , char * path )
2013-11-28 14:54:35 -05:00
{
2013-12-11 14:11:53 -05:00
struct kernfs_node * base , * kn ;
2013-11-28 14:54:35 -05:00
char * s = path ;
int len = 0 ;
/* go up to the root, stop at the base */
2013-12-11 14:11:53 -05:00
base = parent ;
2013-12-11 14:11:54 -05:00
while ( base - > parent ) {
kn = target - > parent ;
while ( kn - > parent & & base ! = kn )
kn = kn - > parent ;
2013-11-28 14:54:35 -05:00
2013-12-11 14:11:53 -05:00
if ( base = = kn )
2013-11-28 14:54:35 -05:00
break ;
strcpy ( s , " ../ " ) ;
s + = 3 ;
2013-12-11 14:11:54 -05:00
base = base - > parent ;
2013-11-28 14:54:35 -05:00
}
/* determine end of target string for reverse fillup */
2013-12-11 14:11:53 -05:00
kn = target ;
2013-12-11 14:11:54 -05:00
while ( kn - > parent & & kn ! = base ) {
len + = strlen ( kn - > name ) + 1 ;
kn = kn - > parent ;
2013-11-28 14:54:35 -05:00
}
/* check limits */
if ( len < 2 )
return - EINVAL ;
len - - ;
if ( ( s - path ) + len > PATH_MAX )
return - ENAMETOOLONG ;
/* reverse fillup of target string from target to base */
2013-12-11 14:11:53 -05:00
kn = target ;
2013-12-11 14:11:54 -05:00
while ( kn - > parent & & kn ! = base ) {
int slen = strlen ( kn - > name ) ;
2013-11-28 14:54:35 -05:00
len - = slen ;
2013-12-11 14:11:54 -05:00
strncpy ( s + len , kn - > name , slen ) ;
2013-11-28 14:54:35 -05:00
if ( len )
s [ - - len ] = ' / ' ;
2013-12-11 14:11:54 -05:00
kn = kn - > parent ;
2013-11-28 14:54:35 -05:00
}
return 0 ;
}
2013-12-11 14:11:58 -05:00
static int kernfs_getlink ( struct dentry * dentry , char * path )
2013-11-28 14:54:35 -05:00
{
2013-12-11 14:11:53 -05:00
struct kernfs_node * kn = dentry - > d_fsdata ;
2013-12-11 14:11:54 -05:00
struct kernfs_node * parent = kn - > parent ;
struct kernfs_node * target = kn - > symlink . target_kn ;
2013-11-28 14:54:35 -05:00
int error ;
2013-12-11 14:11:57 -05:00
mutex_lock ( & kernfs_mutex ) ;
2013-12-11 14:11:58 -05:00
error = kernfs_get_target_path ( parent , target , path ) ;
2013-12-11 14:11:57 -05:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 14:54:35 -05:00
return error ;
}
2013-12-11 14:11:58 -05:00
static void * kernfs_iop_follow_link ( struct dentry * dentry , struct nameidata * nd )
2013-11-28 14:54:35 -05:00
{
int error = - ENOMEM ;
unsigned long page = get_zeroed_page ( GFP_KERNEL ) ;
if ( page ) {
2013-12-11 14:11:58 -05:00
error = kernfs_getlink ( dentry , ( char * ) page ) ;
2013-11-28 14:54:35 -05:00
if ( error < 0 )
free_page ( ( unsigned long ) page ) ;
}
nd_set_link ( nd , error ? ERR_PTR ( error ) : ( char * ) page ) ;
return NULL ;
}
2013-12-11 14:11:58 -05:00
static void kernfs_iop_put_link ( struct dentry * dentry , struct nameidata * nd ,
void * cookie )
2013-11-28 14:54:35 -05:00
{
char * page = nd_get_link ( nd ) ;
if ( ! IS_ERR ( page ) )
free_page ( ( unsigned long ) page ) ;
}
2013-12-11 14:11:57 -05:00
const struct inode_operations kernfs_symlink_iops = {
2013-12-11 14:11:58 -05:00
. setxattr = kernfs_iop_setxattr ,
. removexattr = kernfs_iop_removexattr ,
. getxattr = kernfs_iop_getxattr ,
. listxattr = kernfs_iop_listxattr ,
2013-11-28 14:54:35 -05:00
. readlink = generic_readlink ,
2013-12-11 14:11:58 -05:00
. follow_link = kernfs_iop_follow_link ,
. put_link = kernfs_iop_put_link ,
. setattr = kernfs_iop_setattr ,
. getattr = kernfs_iop_getattr ,
. permission = kernfs_iop_permission ,
2013-11-28 14:54:35 -05:00
} ;