@ -44,28 +44,122 @@ static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
return strlcpy ( buf , kn - > parent ? kn - > name : " / " , buflen ) ;
}
static char * __must_check kernfs_path_locked ( struct kernfs_node * kn , char * buf ,
size_t buflen )
/* kernfs_node_depth - compute depth from @from to @to */
static size_t kernfs_depth ( struct kernfs_node * from , struct kernfs_node * to )
{
char * p = buf + buflen ;
int len ;
size_t depth = 0 ;
* - - p = ' \0 ' ;
while ( to - > parent & & to ! = from ) {
depth + + ;
to = to - > parent ;
}
return depth ;
}
do {
len = strlen ( kn - > name ) ;
if ( p - buf < len + 1 ) {
buf [ 0 ] = ' \0 ' ;
p = NULL ;
break ;
}
p - = len ;
memcpy ( p , kn - > name , len ) ;
* - - p = ' / ' ;
kn = kn - > parent ;
} while ( kn & & kn - > parent ) ;
static struct kernfs_node * kernfs_common_ancestor ( struct kernfs_node * a ,
struct kernfs_node * b )
{
size_t da , db ;
struct kernfs_root * ra = kernfs_root ( a ) , * rb = kernfs_root ( b ) ;
return p ;
if ( ra ! = rb )
return NULL ;
da = kernfs_depth ( ra - > kn , a ) ;
db = kernfs_depth ( rb - > kn , b ) ;
while ( da > db ) {
a = a - > parent ;
da - - ;
}
while ( db > da ) {
b = b - > parent ;
db - - ;
}
/* worst case b and a will be the same at root */
while ( b ! = a ) {
b = b - > parent ;
a = a - > parent ;
}
return a ;
}
/**
* kernfs_path_from_node_locked - find a pseudo - absolute path to @ kn_to ,
* where kn_from is treated as root of the path .
* @ kn_from : kernfs node which should be treated as root for the path
* @ kn_to : kernfs node to which path is needed
* @ buf : buffer to copy the path into
* @ buflen : size of @ buf
*
* We need to handle couple of scenarios here :
* [ 1 ] when @ kn_from is an ancestor of @ kn_to at some level
* kn_from : / n1 / n2 / n3
* kn_to : / n1 / n2 / n3 / n4 / n5
* result : / n4 / n5
*
* [ 2 ] when @ kn_from is on a different hierarchy and we need to find common
* ancestor between @ kn_from and @ kn_to .
* kn_from : / n1 / n2 / n3 / n4
* kn_to : / n1 / n2 / n5
* result : / . . / . . / n5
* OR
* kn_from : / n1 / n2 / n3 / n4 / n5 [ depth = 5 ]
* kn_to : / n1 / n2 / n3 [ depth = 3 ]
* result : / . . / . .
*
* return value : length of the string . If greater than buflen ,
* then contents of buf are undefined . On error , - 1 is returned .
*/
static int kernfs_path_from_node_locked ( struct kernfs_node * kn_to ,
struct kernfs_node * kn_from ,
char * buf , size_t buflen )
{
struct kernfs_node * kn , * common ;
const char parent_str [ ] = " /.. " ;
size_t depth_from , depth_to , len = 0 , nlen = 0 ;
char * p ;
int i ;
if ( ! kn_from )
kn_from = kernfs_root ( kn_to ) - > kn ;
if ( kn_from = = kn_to )
return strlcpy ( buf , " / " , buflen ) ;
common = kernfs_common_ancestor ( kn_from , kn_to ) ;
if ( WARN_ON ( ! common ) )
return - 1 ;
depth_to = kernfs_depth ( common , kn_to ) ;
depth_from = kernfs_depth ( common , kn_from ) ;
if ( buf )
buf [ 0 ] = ' \0 ' ;
for ( i = 0 ; i < depth_from ; i + + )
len + = strlcpy ( buf + len , parent_str ,
len < buflen ? buflen - len : 0 ) ;
/* Calculate how many bytes we need for the rest */
for ( kn = kn_to ; kn ! = common ; kn = kn - > parent )
nlen + = strlen ( kn - > name ) + 1 ;
if ( len + nlen > = buflen )
return len + nlen ;
p = buf + len + nlen ;
* p = ' \0 ' ;
for ( kn = kn_to ; kn ! = common ; kn = kn - > parent ) {
nlen = strlen ( kn - > name ) ;
p - = nlen ;
memcpy ( p , kn - > name , nlen ) ;
* ( - - p ) = ' / ' ;
}
return len + nlen ;
}
/**
@ -114,6 +208,34 @@ size_t kernfs_path_len(struct kernfs_node *kn)
return len ;
}
/**
* kernfs_path_from_node - build path of node @ to relative to @ from .
* @ from : parent kernfs_node relative to which we need to build the path
* @ to : kernfs_node of interest
* @ buf : buffer to copy @ to ' s path into
* @ buflen : size of @ buf
*
* Builds @ to ' s path relative to @ from in @ buf . @ from and @ to must
* be on the same kernfs - root . If @ from is not parent of @ to , then a relative
* path ( which includes ' . . ' s ) as needed to reach from @ from to @ to is
* returned .
*
* If @ buf isn ' t long enough , the return value will be greater than @ buflen
* and @ buf contents are undefined .
*/
int kernfs_path_from_node ( struct kernfs_node * to , struct kernfs_node * from ,
char * buf , size_t buflen )
{
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & kernfs_rename_lock , flags ) ;
ret = kernfs_path_from_node_locked ( to , from , buf , buflen ) ;
spin_unlock_irqrestore ( & kernfs_rename_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( kernfs_path_from_node ) ;
/**
* kernfs_path - build full path of a given node
* @ kn : kernfs_node of interest
@ -127,13 +249,12 @@ size_t kernfs_path_len(struct kernfs_node *kn)
*/
char * kernfs_path ( struct kernfs_node * kn , char * buf , size_t buflen )
{
unsigned long flags ;
char * p ;
int ret ;
spin_lock_irqsave ( & kernfs_rename_lock , flags ) ;
p = kernfs_path_locked ( kn , buf , buflen ) ;
spin_unlock_irqrestore ( & kernfs_rename_lock , flags ) ;
return p ;
ret = kernfs_path_from_node ( kn , NULL , buf , buflen ) ;
if ( ret < 0 | | ret > = buflen )
return NULL ;
return buf ;
}
EXPORT_SYMBOL_GPL ( kernfs_path ) ;
@ -164,17 +285,25 @@ void pr_cont_kernfs_name(struct kernfs_node *kn)
void pr_cont_kernfs_path ( struct kernfs_node * kn )
{
unsigned long flags ;
char * p ;
int sz ;
spin_lock_irqsave ( & kernfs_rename_lock , flags ) ;
p = kernfs_path_locked ( kn , kernfs_pr_cont_buf ,
sizeof ( kernfs_pr_cont_buf ) ) ;
if ( p )
pr_cont ( " %s " , p ) ;
else
pr_cont ( " <name too long> " ) ;
sz = kernfs_path_from_node_locked ( kn , NULL , kernfs_pr_cont_buf ,
sizeof ( kernfs_pr_cont_buf ) ) ;
if ( sz < 0 ) {
pr_cont ( " (error) " ) ;
goto out ;
}
if ( sz > = sizeof ( kernfs_pr_cont_buf ) ) {
pr_cont ( " (name too long) " ) ;
goto out ;
}
pr_cont ( " %s " , kernfs_pr_cont_buf ) ;
out :
spin_unlock_irqrestore ( & kernfs_rename_lock , flags ) ;
}