2005-04-16 15:20:36 -07:00
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/module.h>
# include <linux/smp_lock.h>
# include <linux/namei.h>
struct export_operations export_op_default ;
# define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
# define dprintk(fmt, args...) do{}while(0)
2006-01-18 17:43:52 -08:00
static struct dentry *
find_acceptable_alias ( struct dentry * result ,
int ( * acceptable ) ( void * context , struct dentry * dentry ) ,
void * context )
{
struct dentry * dentry , * toput = NULL ;
spin_lock ( & dcache_lock ) ;
list_for_each_entry ( dentry , & result - > d_inode - > i_dentry , d_alias ) {
dget_locked ( dentry ) ;
spin_unlock ( & dcache_lock ) ;
if ( toput )
dput ( toput ) ;
if ( dentry ! = result & & acceptable ( context , dentry ) ) {
dput ( result ) ;
return dentry ;
}
spin_lock ( & dcache_lock ) ;
toput = dentry ;
}
spin_unlock ( & dcache_lock ) ;
if ( toput )
dput ( toput ) ;
return NULL ;
}
2005-04-16 15:20:36 -07:00
/**
* find_exported_dentry - helper routine to implement export_operations - > decode_fh
* @ sb : The & super_block identifying the filesystem
* @ obj : An opaque identifier of the object to be found - passed to
* get_inode
* @ parent : An optional opqaue identifier of the parent of the object .
* @ acceptable : A function used to test possible & dentries to see if they are
* acceptable
* @ context : A parameter to @ acceptable so that it knows on what basis to
* judge .
*
* find_exported_dentry is the central helper routine to enable file systems
* to provide the decode_fh ( ) export_operation . It ' s main task is to take
* an & inode , find or create an appropriate & dentry structure , and possibly
* splice this into the dcache in the correct place .
*
* The decode_fh ( ) operation provided by the filesystem should call
* find_exported_dentry ( ) with the same parameters that it received except
* that instead of the file handle fragment , pointers to opaque identifiers
* for the object and optionally its parent are passed . The default decode_fh
* routine passes one pointer to the start of the filehandle fragment , and
* one 8 bytes into the fragment . It is expected that most filesystems will
* take this approach , though the offset to the parent identifier may well be
* different .
*
* find_exported_dentry ( ) will call get_dentry to get an dentry pointer from
* the file system . If any & dentry in the d_alias list is acceptable , it will
* be returned . Otherwise find_exported_dentry ( ) will attempt to splice a new
* & dentry into the dcache using get_name ( ) and get_parent ( ) to find the
* appropriate place .
*/
struct dentry *
find_exported_dentry ( struct super_block * sb , void * obj , void * parent ,
int ( * acceptable ) ( void * context , struct dentry * de ) ,
void * context )
{
struct dentry * result = NULL ;
struct dentry * target_dir ;
int err ;
struct export_operations * nops = sb - > s_export_op ;
2006-01-18 17:43:52 -08:00
struct dentry * alias ;
2005-04-16 15:20:36 -07:00
int noprogress ;
char nbuf [ NAME_MAX + 1 ] ;
/*
* Attempt to find the inode .
*/
result = CALL ( sb - > s_export_op , get_dentry ) ( sb , obj ) ;
err = - ESTALE ;
if ( result = = NULL )
goto err_out ;
if ( IS_ERR ( result ) ) {
err = PTR_ERR ( result ) ;
goto err_out ;
}
if ( S_ISDIR ( result - > d_inode - > i_mode ) & &
( result - > d_flags & DCACHE_DISCONNECTED ) ) {
/* it is an unconnected directory, we must connect it */
;
} else {
if ( acceptable ( context , result ) )
return result ;
if ( S_ISDIR ( result - > d_inode - > i_mode ) ) {
2006-05-20 14:59:56 -07:00
err = - EACCES ;
2005-04-16 15:20:36 -07:00
goto err_result ;
}
2006-01-18 17:43:52 -08:00
alias = find_acceptable_alias ( result , acceptable , context ) ;
if ( alias )
return alias ;
2005-04-16 15:20:36 -07:00
}
/* It's a directory, or we are required to confirm the file's
* location in the tree based on the parent information
*/
dprintk ( " find_exported_dentry: need to look harder for %s/%d \n " , sb - > s_id , * ( int * ) obj ) ;
if ( S_ISDIR ( result - > d_inode - > i_mode ) )
target_dir = dget ( result ) ;
else {
if ( parent = = NULL )
goto err_result ;
target_dir = CALL ( sb - > s_export_op , get_dentry ) ( sb , parent ) ;
if ( IS_ERR ( target_dir ) )
err = PTR_ERR ( target_dir ) ;
if ( target_dir = = NULL | | IS_ERR ( target_dir ) )
goto err_result ;
}
/*
* Now we need to make sure that target_dir is properly connected .
* It may already be , as the flag isn ' t always updated when connection
* happens .
* So , we walk up parent links until we find a connected directory ,
* or we run out of directories . Then we find the parent , find
* the name of the child in that parent , and do a lookup .
* This should connect the child into the parent
* We then repeat .
*/
/* it is possible that a confused file system might not let us complete
* the path to the root . For example , if get_parent returns a directory
* in which we cannot find a name for the child . While this implies a
* very sick filesystem we don ' t want it to cause knfsd to spin . Hence
* the noprogress counter . If we go through the loop 10 times ( 2 is
* probably enough ) without getting anywhere , we just give up
*/
noprogress = 0 ;
while ( target_dir - > d_flags & DCACHE_DISCONNECTED & & noprogress + + < 10 ) {
struct dentry * pd = target_dir ;
dget ( pd ) ;
spin_lock ( & pd - > d_lock ) ;
while ( ! IS_ROOT ( pd ) & &
( pd - > d_parent - > d_flags & DCACHE_DISCONNECTED ) ) {
struct dentry * parent = pd - > d_parent ;
dget ( parent ) ;
spin_unlock ( & pd - > d_lock ) ;
dput ( pd ) ;
pd = parent ;
spin_lock ( & pd - > d_lock ) ;
}
spin_unlock ( & pd - > d_lock ) ;
if ( ! IS_ROOT ( pd ) ) {
/* must have found a connected parent - great */
spin_lock ( & pd - > d_lock ) ;
pd - > d_flags & = ~ DCACHE_DISCONNECTED ;
spin_unlock ( & pd - > d_lock ) ;
noprogress = 0 ;
} else if ( pd = = sb - > s_root ) {
printk ( KERN_ERR " export: Eeek filesystem root is not connected, impossible \n " ) ;
spin_lock ( & pd - > d_lock ) ;
pd - > d_flags & = ~ DCACHE_DISCONNECTED ;
spin_unlock ( & pd - > d_lock ) ;
noprogress = 0 ;
} else {
/* we have hit the top of a disconnected path. Try
* to find parent and connect
* note : racing with some other process renaming a
* directory isn ' t much of a problem here . If someone
* renames the directory , it will end up properly
* connected , which is what we want
*/
struct dentry * ppd ;
struct dentry * npd ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & pd - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
ppd = CALL ( nops , get_parent ) ( pd ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & pd - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( ppd ) ) {
err = PTR_ERR ( ppd ) ;
dprintk ( " find_exported_dentry: get_parent of %ld failed, err %d \n " ,
pd - > d_inode - > i_ino , err ) ;
dput ( pd ) ;
break ;
}
dprintk ( " find_exported_dentry: find name of %lu in %lu \n " , pd - > d_inode - > i_ino , ppd - > d_inode - > i_ino ) ;
err = CALL ( nops , get_name ) ( ppd , nbuf , pd ) ;
if ( err ) {
dput ( ppd ) ;
dput ( pd ) ;
if ( err = = - ENOENT )
/* some race between get_parent and
* get_name ? just try again
*/
continue ;
break ;
}
dprintk ( " find_exported_dentry: found name: %s \n " , nbuf ) ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & ppd - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
npd = lookup_one_len ( nbuf , ppd , strlen ( nbuf ) ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & ppd - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( npd ) ) {
err = PTR_ERR ( npd ) ;
dprintk ( " find_exported_dentry: lookup failed: %d \n " , err ) ;
dput ( ppd ) ;
dput ( pd ) ;
break ;
}
/* we didn't really want npd, we really wanted
* a side - effect of the lookup .
* hopefully , npd = = pd , though it isn ' t really
* a problem if it isn ' t
*/
if ( npd = = pd )
noprogress = 0 ;
else
printk ( " find_exported_dentry: npd != pd \n " ) ;
dput ( npd ) ;
dput ( ppd ) ;
if ( IS_ROOT ( pd ) ) {
/* something went wrong, we have to give up */
dput ( pd ) ;
break ;
}
}
dput ( pd ) ;
}
if ( target_dir - > d_flags & DCACHE_DISCONNECTED ) {
/* something went wrong - oh-well */
if ( ! err )
err = - ESTALE ;
goto err_target ;
}
/* if we weren't after a directory, have one more step to go */
if ( result ! = target_dir ) {
struct dentry * nresult ;
err = CALL ( nops , get_name ) ( target_dir , nbuf , result ) ;
if ( ! err ) {
2006-01-09 15:59:24 -08:00
mutex_lock ( & target_dir - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
nresult = lookup_one_len ( nbuf , target_dir , strlen ( nbuf ) ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & target_dir - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( ! IS_ERR ( nresult ) ) {
if ( nresult - > d_inode ) {
dput ( result ) ;
result = nresult ;
} else
dput ( nresult ) ;
}
}
}
dput ( target_dir ) ;
/* now result is properly connected, it is our best bet */
if ( acceptable ( context , result ) )
return result ;
2006-01-18 17:43:52 -08:00
alias = find_acceptable_alias ( result , acceptable , context ) ;
if ( alias )
return alias ;
2005-04-16 15:20:36 -07:00
/* drat - I just cannot find anything acceptable */
dput ( result ) ;
/* It might be justifiable to return ESTALE here,
* but the filehandle at - least looks reasonable good
* and it just be a permission problem , so returning
* - EACCESS is safer
*/
return ERR_PTR ( - EACCES ) ;
err_target :
dput ( target_dir ) ;
err_result :
dput ( result ) ;
err_out :
return ERR_PTR ( err ) ;
}
static struct dentry * get_parent ( struct dentry * child )
{
/* get_parent cannot be supported generically, the locking
* is too icky .
* instead , we just return EACCES . If server reboots or inodes
* get flushed , you lose
*/
return ERR_PTR ( - EACCES ) ;
}
struct getdents_callback {
char * name ; /* name that was found. It already points to a
buffer NAME_MAX + 1 is size */
unsigned long ino ; /* the inum we are looking for */
int found ; /* inode matched? */
int sequence ; /* sequence counter */
} ;
/*
* A rather strange filldir function to capture
* the name matching the specified inode number .
*/
static int filldir_one ( void * __buf , const char * name , int len ,
2006-10-03 01:13:46 -07:00
loff_t pos , u64 ino , unsigned int d_type )
2005-04-16 15:20:36 -07:00
{
struct getdents_callback * buf = __buf ;
int result = 0 ;
buf - > sequence + + ;
if ( buf - > ino = = ino ) {
memcpy ( buf - > name , name , len ) ;
buf - > name [ len ] = ' \0 ' ;
buf - > found = 1 ;
result = - 1 ;
}
return result ;
}
/**
* get_name - default export_operations - > get_name function
* @ dentry : the directory in which to find a name
* @ name : a pointer to a % NAME_MAX + 1 char buffer to store the name
* @ child : the dentry for the child directory .
*
* calls readdir on the parent until it finds an entry with
* the same inode number as the child , and returns that .
*/
static int get_name ( struct dentry * dentry , char * name ,
struct dentry * child )
{
struct inode * dir = dentry - > d_inode ;
int error ;
struct file * file ;
struct getdents_callback buffer ;
error = - ENOTDIR ;
if ( ! dir | | ! S_ISDIR ( dir - > i_mode ) )
goto out ;
error = - EINVAL ;
if ( ! dir - > i_fop )
goto out ;
/*
* Open the directory . . .
*/
file = dentry_open ( dget ( dentry ) , NULL , O_RDONLY ) ;
error = PTR_ERR ( file ) ;
if ( IS_ERR ( file ) )
goto out ;
error = - EINVAL ;
if ( ! file - > f_op - > readdir )
goto out_close ;
buffer . name = name ;
buffer . ino = child - > d_inode - > i_ino ;
buffer . found = 0 ;
buffer . sequence = 0 ;
while ( 1 ) {
int old_seq = buffer . sequence ;
error = vfs_readdir ( file , filldir_one , & buffer ) ;
if ( error < 0 )
break ;
error = 0 ;
if ( buffer . found )
break ;
error = - ENOENT ;
if ( old_seq = = buffer . sequence )
break ;
}
out_close :
fput ( file ) ;
out :
return error ;
}
static struct dentry * export_iget ( struct super_block * sb , unsigned long ino , __u32 generation )
{
/* iget isn't really right if the inode is currently unallocated!!
* This should really all be done inside each filesystem
*
* ext2fs ' read_inode has been strengthed to return a bad_inode if
* the inode had been deleted .
*
* Currently we don ' t know the generation for parent directory , so
* a generation of 0 means " accept any "
*/
struct inode * inode ;
struct dentry * result ;
if ( ino = = 0 )
return ERR_PTR ( - ESTALE ) ;
inode = iget ( sb , ino ) ;
if ( inode = = NULL )
return ERR_PTR ( - ENOMEM ) ;
if ( is_bad_inode ( inode )
| | ( generation & & inode - > i_generation ! = generation )
) {
/* we didn't find the right inode.. */
dprintk ( " fh_verify: Inode %lu, Bad count: %d %d or version %u %u \n " ,
inode - > i_ino ,
inode - > i_nlink , atomic_read ( & inode - > i_count ) ,
inode - > i_generation ,
generation ) ;
iput ( inode ) ;
return ERR_PTR ( - ESTALE ) ;
}
/* now to find a dentry.
* If possible , get a well - connected one
*/
result = d_alloc_anon ( inode ) ;
if ( ! result ) {
iput ( inode ) ;
return ERR_PTR ( - ENOMEM ) ;
}
return result ;
}
static struct dentry * get_object ( struct super_block * sb , void * vobjp )
{
__u32 * objp = vobjp ;
unsigned long ino = objp [ 0 ] ;
__u32 generation = objp [ 1 ] ;
return export_iget ( sb , ino , generation ) ;
}
/**
* export_encode_fh - default export_operations - > encode_fh function
* @ dentry : the dentry to encode
* @ fh : where to store the file handle fragment
* @ max_len : maximum length to store there
* @ connectable : whether to store parent information
*
* This default encode_fh function assumes that the 32 inode number
* is suitable for locating an inode , and that the generation number
* can be used to check that it is still valid . It places them in the
* filehandle fragment where export_decode_fh expects to find them .
*/
static int export_encode_fh ( struct dentry * dentry , __u32 * fh , int * max_len ,
int connectable )
{
struct inode * inode = dentry - > d_inode ;
int len = * max_len ;
int type = 1 ;
if ( len < 2 | | ( connectable & & len < 4 ) )
return 255 ;
len = 2 ;
fh [ 0 ] = inode - > i_ino ;
fh [ 1 ] = inode - > i_generation ;
if ( connectable & & ! S_ISDIR ( inode - > i_mode ) ) {
struct inode * parent ;
spin_lock ( & dentry - > d_lock ) ;
parent = dentry - > d_parent - > d_inode ;
fh [ 2 ] = parent - > i_ino ;
fh [ 3 ] = parent - > i_generation ;
spin_unlock ( & dentry - > d_lock ) ;
len = 4 ;
type = 2 ;
}
* max_len = len ;
return type ;
}
/**
* export_decode_fh - default export_operations - > decode_fh function
* @ sb : The superblock
* @ fh : pointer to the file handle fragment
* @ fh_len : length of file handle fragment
* @ acceptable : function for testing acceptability of dentrys
* @ context : context for @ acceptable
*
* This is the default decode_fh ( ) function .
* a fileid_type of 1 indicates that the filehandlefragment
* just contains an object identifier understood by get_dentry .
* a fileid_type of 2 says that there is also a directory
* identifier 8 bytes in to the filehandlefragement .
*/
static struct dentry * export_decode_fh ( struct super_block * sb , __u32 * fh , int fh_len ,
int fileid_type ,
int ( * acceptable ) ( void * context , struct dentry * de ) ,
void * context )
{
__u32 parent [ 2 ] ;
parent [ 0 ] = parent [ 1 ] = 0 ;
if ( fh_len < 2 | | fileid_type > 2 )
return NULL ;
if ( fileid_type = = 2 ) {
if ( fh_len > 2 ) parent [ 0 ] = fh [ 2 ] ;
if ( fh_len > 3 ) parent [ 1 ] = fh [ 3 ] ;
}
return find_exported_dentry ( sb , fh , parent ,
acceptable , context ) ;
}
struct export_operations export_op_default = {
. decode_fh = export_decode_fh ,
. encode_fh = export_encode_fh ,
. get_name = get_name ,
. get_parent = get_parent ,
. get_dentry = get_object ,
} ;
EXPORT_SYMBOL ( export_op_default ) ;
EXPORT_SYMBOL ( find_exported_dentry ) ;
MODULE_LICENSE ( " GPL " ) ;