2005-09-10 00:04:24 +04:00
/*
* V9FS FID Management
*
2007-10-17 23:31:07 +04:00
* Copyright ( C ) 2007 by Latchesar Ionkov < lucho @ ionkov . net >
2006-03-02 13:54:33 +03:00
* Copyright ( C ) 2005 , 2006 by Eric Van Hensbergen < ericvh @ gmail . com >
2005-09-10 00:04:24 +04:00
*
* This program is free software ; you can redistribute it and / or modify
2006-03-25 14:07:28 +03:00
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
2005-09-10 00:04:24 +04:00
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
2006-10-18 21:55:46 +04:00
# include <linux/sched.h>
2005-09-10 00:04:24 +04:00
# include <linux/idr.h>
2007-07-11 02:57:28 +04:00
# include <net/9p/9p.h>
# include <net/9p/client.h>
2005-09-10 00:04:24 +04:00
# include "v9fs.h"
# include "v9fs_vfs.h"
# include "fid.h"
/**
2007-10-17 23:31:07 +04:00
* v9fs_fid_add - add a fid to a dentry
* @ dentry : dentry that the fid is being added to
2005-09-10 00:04:24 +04:00
* @ fid : fid to add
*
*/
2007-07-11 02:57:28 +04:00
int v9fs_fid_add ( struct dentry * dentry , struct p9_fid * fid )
2005-09-10 00:04:24 +04:00
{
2007-07-11 02:57:28 +04:00
struct v9fs_dentry * dent ;
2005-09-10 00:04:24 +04:00
2007-07-11 02:57:28 +04:00
P9_DPRINTK ( P9_DEBUG_VFS , " fid %d dentry %s \n " ,
fid - > fid , dentry - > d_iname ) ;
2005-09-10 00:04:24 +04:00
2007-07-11 02:57:28 +04:00
dent = dentry - > d_fsdata ;
if ( ! dent ) {
dent = kmalloc ( sizeof ( struct v9fs_dentry ) , GFP_KERNEL ) ;
if ( ! dent )
return - ENOMEM ;
2005-09-10 00:04:24 +04:00
2007-07-11 02:57:28 +04:00
spin_lock_init ( & dent - > lock ) ;
INIT_LIST_HEAD ( & dent - > fidlist ) ;
dentry - > d_fsdata = dent ;
2005-09-10 00:04:24 +04:00
}
2007-07-11 02:57:28 +04:00
spin_lock ( & dent - > lock ) ;
list_add ( & fid - > dlist , & dent - > fidlist ) ;
spin_unlock ( & dent - > lock ) ;
2005-09-10 00:04:24 +04:00
2007-07-11 02:57:28 +04:00
return 0 ;
2005-09-10 00:04:24 +04:00
}
/**
2007-10-17 23:31:07 +04:00
* v9fs_fid_find - retrieve a fid that belongs to the specified uid
2005-09-10 00:04:24 +04:00
* @ dentry : dentry to look for fid in
2007-10-17 23:31:07 +04:00
* @ uid : return fid that belongs to the specified user
* @ any : if non - zero , return any fid associated with the dentry
2005-09-10 00:04:24 +04:00
*
*/
2007-10-17 23:31:07 +04:00
static struct p9_fid * v9fs_fid_find ( struct dentry * dentry , u32 uid , int any )
2005-09-10 00:04:24 +04:00
{
2007-07-11 02:57:28 +04:00
struct v9fs_dentry * dent ;
2007-10-17 23:31:07 +04:00
struct p9_fid * fid , * ret ;
P9_DPRINTK ( P9_DEBUG_VFS , " dentry: %s (%p) uid %d any %d \n " ,
dentry - > d_iname , dentry , uid , any ) ;
dent = ( struct v9fs_dentry * ) dentry - > d_fsdata ;
ret = NULL ;
if ( dent ) {
spin_lock ( & dent - > lock ) ;
list_for_each_entry ( fid , & dent - > fidlist , dlist ) {
if ( any | | fid - > uid = = uid ) {
ret = fid ;
break ;
}
}
spin_unlock ( & dent - > lock ) ;
}
2007-07-11 02:57:28 +04:00
2007-10-17 23:31:07 +04:00
return ret ;
2007-07-11 02:57:28 +04:00
}
2005-09-10 00:04:24 +04:00
2007-01-26 11:57:06 +03:00
/**
2007-10-17 23:31:07 +04:00
* v9fs_fid_lookup - lookup for a fid , try to walk if not found
2007-01-26 11:57:06 +03:00
* @ dentry : dentry to look for fid in
*
2007-10-17 23:31:07 +04:00
* Look for a fid in the specified dentry for the current user .
* If no fid is found , try to create one walking from a fid from the parent
* dentry ( if it has one ) , or the root dentry . If the user haven ' t accessed
* the fs yet , attach now and walk from the root .
2007-01-26 11:57:06 +03:00
*/
2007-10-17 23:31:07 +04:00
struct p9_fid * v9fs_fid_lookup ( struct dentry * dentry )
2007-01-26 11:57:06 +03:00
{
2007-10-17 23:31:07 +04:00
int i , n , l , clone , any , access ;
u32 uid ;
struct p9_fid * fid ;
struct dentry * d , * ds ;
struct v9fs_session_info * v9ses ;
char * * wnames , * uname ;
v9ses = v9fs_inode2v9ses ( dentry - > d_inode ) ;
access = v9ses - > flags & V9FS_ACCESS_MASK ;
switch ( access ) {
case V9FS_ACCESS_SINGLE :
case V9FS_ACCESS_USER :
uid = current - > fsuid ;
any = 0 ;
break ;
case V9FS_ACCESS_ANY :
uid = v9ses - > uid ;
any = 1 ;
break ;
default :
uid = ~ 0 ;
any = 0 ;
break ;
}
2007-01-26 11:57:06 +03:00
2007-10-17 23:31:07 +04:00
fid = v9fs_fid_find ( dentry , uid , any ) ;
if ( fid )
return fid ;
ds = dentry - > d_parent ;
fid = v9fs_fid_find ( ds , uid , any ) ;
if ( ! fid ) { /* walk from the root */
n = 0 ;
for ( ds = dentry ; ! IS_ROOT ( ds ) ; ds = ds - > d_parent )
n + + ;
fid = v9fs_fid_find ( ds , uid , any ) ;
if ( ! fid ) { /* the user is not attached to the fs yet */
if ( access = = V9FS_ACCESS_SINGLE )
return ERR_PTR ( - EPERM ) ;
if ( v9fs_extended ( v9ses ) )
uname = NULL ;
else
uname = v9ses - > uname ;
fid = p9_client_attach ( v9ses - > clnt , NULL , uname , uid ,
v9ses - > aname ) ;
if ( IS_ERR ( fid ) )
return fid ;
v9fs_fid_add ( ds , fid ) ;
}
} else /* walk from the parent */
n = 1 ;
if ( ds = = dentry )
return fid ;
wnames = kmalloc ( sizeof ( char * ) * n , GFP_KERNEL ) ;
if ( ! wnames )
return ERR_PTR ( - ENOMEM ) ;
2008-02-07 04:25:08 +03:00
for ( d = dentry , i = ( n - 1 ) ; i > = 0 ; i - - , d = d - > d_parent )
2007-10-17 23:31:07 +04:00
wnames [ i ] = ( char * ) d - > d_name . name ;
clone = 1 ;
i = 0 ;
while ( i < n ) {
l = min ( n - i , P9_MAXWELEM ) ;
fid = p9_client_walk ( fid , l , & wnames [ i ] , clone ) ;
2008-02-07 04:25:08 +03:00
if ( IS_ERR ( fid ) ) {
2007-10-17 23:31:07 +04:00
kfree ( wnames ) ;
return fid ;
}
i + = l ;
clone = 0 ;
}
2007-01-26 11:57:06 +03:00
2007-10-17 23:31:07 +04:00
kfree ( wnames ) ;
v9fs_fid_add ( dentry , fid ) ;
2007-07-11 02:57:28 +04:00
return fid ;
2007-01-26 11:57:06 +03:00
}
2007-10-17 23:31:07 +04:00
struct p9_fid * v9fs_fid_clone ( struct dentry * dentry )
{
struct p9_fid * fid , * ret ;
fid = v9fs_fid_lookup ( dentry ) ;
if ( IS_ERR ( fid ) )
return fid ;
ret = p9_client_walk ( fid , 0 , NULL , 1 ) ;
return ret ;
}