2007-10-22 03:42:19 +04:00
/*
* Copyright ( C ) Neil Brown 2002
* Copyright ( C ) Christoph Hellwig 2007
*
* This file contains the code mapping from inodes to NFS file handles ,
* and for mapping back from file handles to dentries .
*
* For details on why we do all the strange and hairy things in here
2009-10-27 21:41:35 +03:00
* take a look at Documentation / filesystems / nfs / Exporting .
2007-10-22 03:42:19 +04:00
*/
2007-07-17 15:04:28 +04:00
# include <linux/exportfs.h>
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/module.h>
2007-07-17 15:04:30 +04:00
# include <linux/mount.h>
2005-04-17 02:20:36 +04:00
# include <linux/namei.h>
2008-11-14 02:39:22 +03:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
2007-07-17 15:04:31 +04:00
# define dprintk(fmt, args...) do{}while(0)
2005-04-17 02:20:36 +04:00
2012-06-26 21:58:53 +04:00
static int get_name ( const struct path * path , char * name , struct dentry * child ) ;
2007-07-17 15:04:31 +04:00
2007-10-22 03:42:19 +04:00
static int exportfs_get_name ( struct vfsmount * mnt , struct dentry * dir ,
char * name , struct dentry * child )
2007-07-17 15:04:31 +04:00
{
2007-10-22 03:42:17 +04:00
const struct export_operations * nop = dir - > d_sb - > s_export_op ;
2012-06-26 21:58:53 +04:00
struct path path = { . mnt = mnt , . dentry = dir } ;
2007-07-17 15:04:31 +04:00
if ( nop - > get_name )
return nop - > get_name ( dir , name , child ) ;
else
2012-06-26 21:58:53 +04:00
return get_name ( & path , name , child ) ;
2007-07-17 15:04:31 +04:00
}
2005-04-17 02:20:36 +04:00
2007-07-17 15:04:32 +04:00
/*
* Check if the dentry or any of it ' s aliases is acceptable .
*/
2006-01-19 04:43:52 +03:00
static struct dentry *
find_acceptable_alias ( struct dentry * result ,
int ( * acceptable ) ( void * context , struct dentry * dentry ) ,
void * context )
{
struct dentry * dentry , * toput = NULL ;
2011-01-07 09:50:06 +03:00
struct inode * inode ;
2012-06-09 21:51:19 +04:00
struct hlist_node * p ;
2006-01-19 04:43:52 +03:00
2007-07-17 15:04:32 +04:00
if ( acceptable ( context , result ) )
return result ;
2011-01-07 09:50:06 +03:00
inode = result - > d_inode ;
spin_lock ( & inode - > i_lock ) ;
2012-06-09 21:51:19 +04:00
hlist_for_each_entry ( dentry , p , & inode - > i_dentry , d_alias ) {
2011-01-07 09:49:43 +03:00
dget ( dentry ) ;
2011-01-07 09:50:06 +03:00
spin_unlock ( & inode - > i_lock ) ;
2006-01-19 04:43:52 +03:00
if ( toput )
dput ( toput ) ;
if ( dentry ! = result & & acceptable ( context , dentry ) ) {
dput ( result ) ;
return dentry ;
}
2011-01-07 09:50:06 +03:00
spin_lock ( & inode - > i_lock ) ;
2006-01-19 04:43:52 +03:00
toput = dentry ;
}
2011-01-07 09:50:06 +03:00
spin_unlock ( & inode - > i_lock ) ;
2006-01-19 04:43:52 +03:00
if ( toput )
dput ( toput ) ;
return NULL ;
}
2007-07-17 15:04:32 +04:00
/*
* Find root of a disconnected subtree and return a reference to it .
*/
static struct dentry *
find_disconnected_root ( struct dentry * dentry )
{
dget ( dentry ) ;
2010-10-13 19:56:37 +04:00
while ( ! IS_ROOT ( dentry ) ) {
struct dentry * parent = dget_parent ( dentry ) ;
if ( ! ( parent - > d_flags & DCACHE_DISCONNECTED ) ) {
dput ( parent ) ;
break ;
}
2007-07-17 15:04:32 +04:00
dput ( dentry ) ;
dentry = parent ;
}
return dentry ;
}
2007-07-17 15:04:33 +04:00
/*
* Make sure target_dir is fully connected to the dentry tree .
2005-04-17 02:20:36 +04:00
*
2007-07-17 15:04:33 +04:00
* It may already be , as the flag isn ' t always updated when connection happens .
2005-04-17 02:20:36 +04:00
*/
2007-07-17 15:04:33 +04:00
static int
2008-08-11 20:39:47 +04:00
reconnect_path ( struct vfsmount * mnt , struct dentry * target_dir , char * nbuf )
2005-04-17 02:20:36 +04:00
{
2007-07-17 15:04:33 +04:00
int noprogress = 0 ;
int err = - ESTALE ;
2005-04-17 02:20:36 +04:00
/*
2007-07-17 15:04:33 +04:00
* It is possible that a confused file system might not let us complete
2005-04-17 02:20:36 +04:00
* 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
*/
while ( target_dir - > d_flags & DCACHE_DISCONNECTED & & noprogress + + < 10 ) {
2007-07-17 15:04:32 +04:00
struct dentry * pd = find_disconnected_root ( target_dir ) ;
2005-04-17 02:20:36 +04:00
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 ;
2007-10-22 03:42:19 +04:00
} else if ( pd = = mnt - > mnt_sb - > s_root ) {
2005-04-17 02:20:36 +04:00
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 {
2007-07-17 15:04:31 +04:00
/*
* We have hit the top of a disconnected path , try to
* find parent and connect .
*
* 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
*
* Getting the parent can ' t be supported generically ,
* the locking is too icky .
*
* Instead we just return EACCES . If server reboots
* or inodes get flushed , you lose
2005-04-17 02:20:36 +04:00
*/
2007-07-17 15:04:31 +04:00
struct dentry * ppd = ERR_PTR ( - EACCES ) ;
2005-04-17 02:20:36 +04:00
struct dentry * npd ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & pd - > d_inode - > i_mutex ) ;
2007-10-22 03:42:19 +04:00
if ( mnt - > mnt_sb - > s_export_op - > get_parent )
ppd = mnt - > mnt_sb - > s_export_op - > get_parent ( pd ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & pd - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( ppd ) ) {
err = PTR_ERR ( ppd ) ;
2007-07-17 15:04:33 +04:00
dprintk ( " %s: get_parent of %ld failed, err %d \n " ,
2008-04-30 11:55:09 +04:00
__func__ , pd - > d_inode - > i_ino , err ) ;
2005-04-17 02:20:36 +04:00
dput ( pd ) ;
break ;
}
2007-07-17 15:04:33 +04:00
2008-04-30 11:55:09 +04:00
dprintk ( " %s: find name of %lu in %lu \n " , __func__ ,
2007-07-17 15:04:33 +04:00
pd - > d_inode - > i_ino , ppd - > d_inode - > i_ino ) ;
2007-10-22 03:42:19 +04:00
err = exportfs_get_name ( mnt , ppd , nbuf , pd ) ;
2005-04-17 02:20:36 +04:00
if ( err ) {
dput ( ppd ) ;
dput ( pd ) ;
if ( err = = - ENOENT )
/* some race between get_parent and
* get_name ? just try again
*/
continue ;
break ;
}
2008-04-30 11:55:09 +04:00
dprintk ( " %s: found name: %s \n " , __func__ , nbuf ) ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & ppd - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
npd = lookup_one_len ( nbuf , ppd , strlen ( nbuf ) ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & ppd - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( npd ) ) {
err = PTR_ERR ( npd ) ;
2007-07-17 15:04:33 +04:00
dprintk ( " %s: lookup failed: %d \n " ,
2008-04-30 11:55:09 +04:00
__func__ , err ) ;
2005-04-17 02:20:36 +04:00
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
2008-04-30 11:55:09 +04:00
printk ( " %s: npd != pd \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
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 ;
2007-07-17 15:04:33 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-07-17 15:04:33 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
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 12:13:46 +04:00
loff_t pos , u64 ino , unsigned int d_type )
2005-04-17 02:20:36 +04: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 .
*/
2012-06-26 21:58:53 +04:00
static int get_name ( const struct path * path , char * name , struct dentry * child )
2005-04-17 02:20:36 +04:00
{
2008-11-14 02:39:22 +03:00
const struct cred * cred = current_cred ( ) ;
2012-06-26 21:58:53 +04:00
struct inode * dir = path - > dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
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 . . .
*/
2012-06-26 21:58:53 +04:00
file = dentry_open ( path , O_RDONLY , cred ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2008-08-24 15:29:52 +04:00
if ( buffer . found ) {
error = 0 ;
break ;
}
2005-04-17 02:20:36 +04:00
if ( error < 0 )
break ;
error = - ENOENT ;
if ( old_seq = = buffer . sequence )
break ;
}
out_close :
fput ( file ) ;
out :
return error ;
}
/**
* export_encode_fh - default export_operations - > encode_fh function
2012-04-02 22:34:06 +04:00
* @ inode : the object to encode
2005-04-17 02:20:36 +04:00
* @ fh : where to store the file handle fragment
* @ max_len : maximum length to store there
2012-04-02 22:34:06 +04:00
* @ parent : parent directory inode , if wanted
2005-04-17 02:20:36 +04:00
*
* 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 .
*/
2012-04-02 22:34:06 +04:00
static int export_encode_fh ( struct inode * inode , struct fid * fid ,
int * max_len , struct inode * parent )
2005-04-17 02:20:36 +04:00
{
int len = * max_len ;
2007-10-22 03:42:03 +04:00
int type = FILEID_INO32_GEN ;
2011-01-29 16:13:25 +03:00
2012-04-02 22:34:06 +04:00
if ( parent & & ( len < 4 ) ) {
2011-01-29 16:13:25 +03:00
* max_len = 4 ;
return 255 ;
} else if ( len < 2 ) {
* max_len = 2 ;
2005-04-17 02:20:36 +04:00
return 255 ;
2011-01-29 16:13:25 +03:00
}
2005-04-17 02:20:36 +04:00
len = 2 ;
2007-10-22 03:42:03 +04:00
fid - > i32 . ino = inode - > i_ino ;
fid - > i32 . gen = inode - > i_generation ;
2012-04-02 22:34:06 +04:00
if ( parent ) {
2007-10-22 03:42:03 +04:00
fid - > i32 . parent_ino = parent - > i_ino ;
fid - > i32 . parent_gen = parent - > i_generation ;
2005-04-17 02:20:36 +04:00
len = 4 ;
2007-10-22 03:42:03 +04:00
type = FILEID_INO32_GEN_PARENT ;
2005-04-17 02:20:36 +04:00
}
* max_len = len ;
return type ;
}
2007-10-22 03:42:03 +04:00
int exportfs_encode_fh ( struct dentry * dentry , struct fid * fid , int * max_len ,
2007-07-17 15:04:30 +04:00
int connectable )
{
2007-10-22 03:42:17 +04:00
const struct export_operations * nop = dentry - > d_sb - > s_export_op ;
2007-07-17 15:04:31 +04:00
int error ;
2012-04-02 22:34:06 +04:00
struct dentry * p = NULL ;
struct inode * inode = dentry - > d_inode , * parent = NULL ;
2007-07-17 15:04:30 +04:00
2012-04-02 22:34:06 +04:00
if ( connectable & & ! S_ISDIR ( inode - > i_mode ) ) {
p = dget_parent ( dentry ) ;
/*
* note that while p might ' ve ceased to be our parent already ,
* it ' s still pinned by and still positive .
*/
parent = p - > d_inode ;
}
2007-07-17 15:04:31 +04:00
if ( nop - > encode_fh )
2012-04-02 22:34:06 +04:00
error = nop - > encode_fh ( inode , fid - > raw , max_len , parent ) ;
2007-07-17 15:04:31 +04:00
else
2012-04-02 22:34:06 +04:00
error = export_encode_fh ( inode , fid , max_len , parent ) ;
dput ( p ) ;
2007-07-17 15:04:31 +04:00
return error ;
2007-07-17 15:04:30 +04:00
}
EXPORT_SYMBOL_GPL ( exportfs_encode_fh ) ;
2007-10-22 03:42:03 +04:00
struct dentry * exportfs_decode_fh ( struct vfsmount * mnt , struct fid * fid ,
int fh_len , int fileid_type ,
int ( * acceptable ) ( void * , struct dentry * ) , void * context )
2007-07-17 15:04:30 +04:00
{
2007-10-22 03:42:17 +04:00
const struct export_operations * nop = mnt - > mnt_sb - > s_export_op ;
2007-10-22 03:42:05 +04:00
struct dentry * result , * alias ;
2008-08-11 20:39:47 +04:00
char nbuf [ NAME_MAX + 1 ] ;
2007-10-22 03:42:05 +04:00
int err ;
2007-07-17 15:04:30 +04:00
2007-10-22 03:42:05 +04:00
/*
* Try to get any dentry for the given file handle from the filesystem .
*/
2011-01-29 16:13:26 +03:00
if ( ! nop | | ! nop - > fh_to_dentry )
return ERR_PTR ( - ESTALE ) ;
2007-10-22 03:42:05 +04:00
result = nop - > fh_to_dentry ( mnt - > mnt_sb , fid , fh_len , fileid_type ) ;
2008-12-09 02:24:18 +03:00
if ( ! result )
result = ERR_PTR ( - ESTALE ) ;
2007-10-22 03:42:05 +04:00
if ( IS_ERR ( result ) )
return result ;
if ( S_ISDIR ( result - > d_inode - > i_mode ) ) {
/*
* This request is for a directory .
*
* On the positive side there is only one dentry for each
* directory inode . On the negative side this implies that we
* to ensure our dentry is connected all the way up to the
* filesystem root .
*/
if ( result - > d_flags & DCACHE_DISCONNECTED ) {
2008-08-11 20:39:47 +04:00
err = reconnect_path ( mnt , result , nbuf ) ;
2007-10-22 03:42:05 +04:00
if ( err )
goto err_result ;
}
if ( ! acceptable ( context , result ) ) {
err = - EACCES ;
goto err_result ;
}
return result ;
2007-07-17 15:04:31 +04:00
} else {
2007-10-22 03:42:05 +04:00
/*
* It ' s not a directory . Life is a little more complicated .
*/
struct dentry * target_dir , * nresult ;
/*
* See if either the dentry we just got from the filesystem
* or any alias for it is acceptable . This is always true
* if this filesystem is exported without the subtreecheck
* option . If the filesystem is exported with the subtree
* check option there ' s a fair chance we need to look at
* the parent directory in the file handle and make sure
* it ' s connected to the filesystem root .
*/
alias = find_acceptable_alias ( result , acceptable , context ) ;
if ( alias )
return alias ;
/*
* Try to extract a dentry for the parent directory from the
* file handle . If this fails we ' ll have to give up .
*/
err = - ESTALE ;
if ( ! nop - > fh_to_parent )
goto err_result ;
target_dir = nop - > fh_to_parent ( mnt - > mnt_sb , fid ,
fh_len , fileid_type ) ;
2008-12-09 02:24:18 +03:00
if ( ! target_dir )
goto err_result ;
2007-10-22 03:42:05 +04:00
err = PTR_ERR ( target_dir ) ;
if ( IS_ERR ( target_dir ) )
goto err_result ;
/*
* And as usual we need to make sure the parent directory is
* connected to the filesystem root . The VFS really doesn ' t
* like disconnected directories . .
*/
2008-08-11 20:39:47 +04:00
err = reconnect_path ( mnt , target_dir , nbuf ) ;
2007-10-22 03:42:05 +04:00
if ( err ) {
dput ( target_dir ) ;
goto err_result ;
}
/*
* Now that we ' ve got both a well - connected parent and a
* dentry for the inode we ' re after , make sure that our
* inode is actually connected to the parent .
*/
2007-10-22 03:42:19 +04:00
err = exportfs_get_name ( mnt , target_dir , nbuf , result ) ;
2007-10-22 03:42:05 +04:00
if ( ! err ) {
mutex_lock ( & target_dir - > d_inode - > i_mutex ) ;
nresult = lookup_one_len ( nbuf , target_dir ,
strlen ( nbuf ) ) ;
mutex_unlock ( & target_dir - > d_inode - > i_mutex ) ;
if ( ! IS_ERR ( nresult ) ) {
if ( nresult - > d_inode ) {
dput ( result ) ;
result = nresult ;
} else
dput ( nresult ) ;
}
}
/*
* At this point we are done with the parent , but it ' s pinned
* by the child dentry anyway .
*/
dput ( target_dir ) ;
/*
* And finally make sure the dentry is actually acceptable
* to NFSD .
*/
alias = find_acceptable_alias ( result , acceptable , context ) ;
if ( ! alias ) {
err = - EACCES ;
goto err_result ;
}
return alias ;
2007-07-17 15:04:31 +04:00
}
2007-10-22 03:42:05 +04:00
err_result :
dput ( result ) ;
return ERR_PTR ( err ) ;
2007-07-17 15:04:30 +04:00
}
EXPORT_SYMBOL_GPL ( exportfs_decode_fh ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;