2013-11-24 18:54:58 +04: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 23:54:35 +04: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 23:11:53 +04:00
struct kernfs_node * kernfs_create_link ( struct kernfs_node * parent ,
const char * name ,
struct kernfs_node * target )
2013-11-28 23:54:35 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:35 +04:00
int error ;
2014-01-17 18:58:25 +04:00
kn = kernfs_new_node ( parent , name , S_IFLNK | S_IRWXUGO , KERNFS_LINK ) ;
2013-12-11 23:11:53 +04:00
if ( ! kn )
2013-11-28 23:54:35 +04:00
return ERR_PTR ( - ENOMEM ) ;
2013-11-30 02:19:09 +04:00
if ( kernfs_ns_enabled ( parent ) )
2013-12-11 23:11:54 +04:00
kn - > ns = target - > ns ;
kn - > symlink . target_kn = target ;
2013-11-28 23:54:35 +04:00
kernfs_get ( target ) ; /* ref owned by symlink */
2014-02-03 23:02:58 +04:00
error = kernfs_add_one ( kn ) ;
2013-11-28 23:54:35 +04:00
if ( ! error )
2013-12-11 23:11:53 +04:00
return kn ;
2013-11-28 23:54:35 +04:00
2013-12-11 23:11:53 +04:00
kernfs_put ( kn ) ;
2013-11-28 23:54:35 +04:00
return ERR_PTR ( error ) ;
}
2013-12-11 23:11:58 +04:00
static int kernfs_get_target_path ( struct kernfs_node * parent ,
struct kernfs_node * target , char * path )
2013-11-28 23:54:35 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * base , * kn ;
2013-11-28 23:54:35 +04:00
char * s = path ;
int len = 0 ;
/* go up to the root, stop at the base */
2013-12-11 23:11:53 +04:00
base = parent ;
2013-12-11 23:11:54 +04:00
while ( base - > parent ) {
kn = target - > parent ;
while ( kn - > parent & & base ! = kn )
kn = kn - > parent ;
2013-11-28 23:54:35 +04:00
2013-12-11 23:11:53 +04:00
if ( base = = kn )
2013-11-28 23:54:35 +04:00
break ;
strcpy ( s , " ../ " ) ;
s + = 3 ;
2013-12-11 23:11:54 +04:00
base = base - > parent ;
2013-11-28 23:54:35 +04:00
}
/* determine end of target string for reverse fillup */
2013-12-11 23:11:53 +04:00
kn = target ;
2013-12-11 23:11:54 +04:00
while ( kn - > parent & & kn ! = base ) {
len + = strlen ( kn - > name ) + 1 ;
kn = kn - > parent ;
2013-11-28 23:54:35 +04: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 23:11:53 +04:00
kn = target ;
2013-12-11 23:11:54 +04:00
while ( kn - > parent & & kn ! = base ) {
int slen = strlen ( kn - > name ) ;
2013-11-28 23:54:35 +04:00
len - = slen ;
2013-12-11 23:11:54 +04:00
strncpy ( s + len , kn - > name , slen ) ;
2013-11-28 23:54:35 +04:00
if ( len )
s [ - - len ] = ' / ' ;
2013-12-11 23:11:54 +04:00
kn = kn - > parent ;
2013-11-28 23:54:35 +04:00
}
return 0 ;
}
2013-12-11 23:11:58 +04:00
static int kernfs_getlink ( struct dentry * dentry , char * path )
2013-11-28 23:54:35 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn = dentry - > d_fsdata ;
2013-12-11 23:11:54 +04:00
struct kernfs_node * parent = kn - > parent ;
struct kernfs_node * target = kn - > symlink . target_kn ;
2013-11-28 23:54:35 +04:00
int error ;
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-12-11 23:11:58 +04:00
error = kernfs_get_target_path ( parent , target , path ) ;
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:35 +04:00
return error ;
}
2013-12-11 23:11:58 +04:00
static void * kernfs_iop_follow_link ( struct dentry * dentry , struct nameidata * nd )
2013-11-28 23:54:35 +04:00
{
int error = - ENOMEM ;
unsigned long page = get_zeroed_page ( GFP_KERNEL ) ;
if ( page ) {
2013-12-11 23:11:58 +04:00
error = kernfs_getlink ( dentry , ( char * ) page ) ;
2013-11-28 23:54:35 +04:00
if ( error < 0 )
free_page ( ( unsigned long ) page ) ;
}
nd_set_link ( nd , error ? ERR_PTR ( error ) : ( char * ) page ) ;
return NULL ;
}
2013-12-11 23:11:58 +04:00
static void kernfs_iop_put_link ( struct dentry * dentry , struct nameidata * nd ,
void * cookie )
2013-11-28 23:54:35 +04:00
{
char * page = nd_get_link ( nd ) ;
if ( ! IS_ERR ( page ) )
free_page ( ( unsigned long ) page ) ;
}
2013-12-11 23:11:57 +04:00
const struct inode_operations kernfs_symlink_iops = {
2013-12-11 23:11:58 +04:00
. setxattr = kernfs_iop_setxattr ,
. removexattr = kernfs_iop_removexattr ,
. getxattr = kernfs_iop_getxattr ,
. listxattr = kernfs_iop_listxattr ,
2013-11-28 23:54:35 +04:00
. readlink = generic_readlink ,
2013-12-11 23:11:58 +04: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 23:54:35 +04:00
} ;