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 ;
}
2015-11-17 18:20:54 +03:00
static const char * kernfs_iop_get_link ( struct dentry * dentry ,
2015-12-29 23:58:39 +03:00
struct inode * inode ,
struct delayed_call * done )
2013-11-28 23:54:35 +04:00
{
2015-12-29 23:58:39 +03:00
char * body ;
int error ;
2015-11-17 18:20:54 +03:00
if ( ! dentry )
return ERR_PTR ( - ECHILD ) ;
2015-12-29 23:58:39 +03:00
body = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! body )
2015-05-02 20:32:22 +03:00
return ERR_PTR ( - ENOMEM ) ;
2015-12-29 23:58:39 +03:00
error = kernfs_getlink ( dentry , body ) ;
2015-05-02 20:32:22 +03:00
if ( unlikely ( error < 0 ) ) {
2015-12-29 23:58:39 +03:00
kfree ( body ) ;
2015-05-02 20:32:22 +03:00
return ERR_PTR ( error ) ;
2013-11-28 23:54:35 +04:00
}
2015-12-29 23:58:39 +03:00
set_delayed_call ( done , kfree_link , body ) ;
return body ;
2013-11-28 23:54:35 +04:00
}
2013-12-11 23:11:57 +04:00
const struct inode_operations kernfs_symlink_iops = {
2013-12-11 23:11:58 +04:00
. listxattr = kernfs_iop_listxattr ,
2013-11-28 23:54:35 +04:00
. readlink = generic_readlink ,
2015-11-17 18:20:54 +03:00
. get_link = kernfs_iop_get_link ,
2013-12-11 23:11:58 +04:00
. setattr = kernfs_iop_setattr ,
. getattr = kernfs_iop_getattr ,
. permission = kernfs_iop_permission ,
2013-11-28 23:54:35 +04:00
} ;