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 ;
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 ) ;
2014-10-27 02:19:16 +03:00
hlist_for_each_entry ( dentry , & inode - > i_dentry , d_u . 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 ;
}
2013-10-17 05:09:30 +04:00
static bool dentry_connected ( struct dentry * dentry )
{
dget ( dentry ) ;
while ( dentry - > d_flags & DCACHE_DISCONNECTED ) {
struct dentry * parent = dget_parent ( dentry ) ;
dput ( dentry ) ;
if ( IS_ROOT ( dentry ) ) {
dput ( parent ) ;
return false ;
}
dentry = parent ;
}
dput ( dentry ) ;
return true ;
}
2013-09-10 00:15:13 +04:00
static void clear_disconnected ( struct dentry * dentry )
{
dget ( dentry ) ;
while ( dentry - > d_flags & DCACHE_DISCONNECTED ) {
struct dentry * parent = dget_parent ( dentry ) ;
WARN_ON_ONCE ( IS_ROOT ( dentry ) ) ;
spin_lock ( & dentry - > d_lock ) ;
dentry - > d_flags & = ~ DCACHE_DISCONNECTED ;
spin_unlock ( & dentry - > d_lock ) ;
dput ( dentry ) ;
dentry = parent ;
}
dput ( dentry ) ;
}
2013-10-17 19:13:00 +04:00
/*
* Reconnect a directory dentry with its parent .
*
* This can return a dentry , or NULL , or an error .
*
* In the first case the returned dentry is the parent of the given
* dentry , and may itself need to be reconnected to its parent .
*
* In the NULL case , a concurrent VFS operation has either renamed or
* removed this directory . The concurrent operation has reconnected our
* dentry , so we no longer need to .
*/
static struct dentry * reconnect_one ( struct vfsmount * mnt ,
struct dentry * dentry , char * nbuf )
{
struct dentry * parent ;
struct dentry * tmp ;
int err ;
parent = ERR_PTR ( - EACCES ) ;
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
if ( mnt - > mnt_sb - > s_export_op - > get_parent )
parent = mnt - > mnt_sb - > s_export_op - > get_parent ( dentry ) ;
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
if ( IS_ERR ( parent ) ) {
dprintk ( " %s: get_parent of %ld failed, err %d \n " ,
__func__ , dentry - > d_inode - > i_ino , PTR_ERR ( parent ) ) ;
return parent ;
}
dprintk ( " %s: find name of %lu in %lu \n " , __func__ ,
dentry - > d_inode - > i_ino , parent - > d_inode - > i_ino ) ;
err = exportfs_get_name ( mnt , parent , nbuf , dentry ) ;
if ( err = = - ENOENT )
goto out_reconnected ;
if ( err )
goto out_err ;
dprintk ( " %s: found name: %s \n " , __func__ , nbuf ) ;
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
tmp = lookup_one_len ( nbuf , parent , strlen ( nbuf ) ) ;
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
if ( IS_ERR ( tmp ) ) {
dprintk ( " %s: lookup failed: %d \n " , __func__ , PTR_ERR ( tmp ) ) ;
goto out_err ;
}
if ( tmp ! = dentry ) {
dput ( tmp ) ;
goto out_reconnected ;
}
dput ( tmp ) ;
if ( IS_ROOT ( dentry ) ) {
err = - ESTALE ;
goto out_err ;
}
return parent ;
out_err :
dput ( parent ) ;
return ERR_PTR ( err ) ;
out_reconnected :
dput ( parent ) ;
/*
* Someone must have renamed our entry into another parent , in
* which case it has been reconnected by the rename .
*
* Or someone removed it entirely , in which case filehandle
* lookup will succeed but the directory is now IS_DEAD and
* subsequent operations on it will fail .
*
* Alternatively , maybe there was no race at all , and the
* filesystem is just corrupt and gave us a parent that doesn ' t
* actually contain any entry pointing to this inode . So ,
* double check that this worked and return - ESTALE if not :
*/
if ( ! dentry_connected ( dentry ) )
return ERR_PTR ( - ESTALE ) ;
return NULL ;
}
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
*
2013-10-23 04:59:19 +04:00
* On successful return , DCACHE_DISCONNECTED will be cleared on
* target_dir , and target_dir - > d_parent - > . . . - > d_parent will reach the
* root of the filesystem .
*
* Whenever DCACHE_DISCONNECTED is unset , target_dir is fully connected .
* But the converse is not true : target_dir may have DCACHE_DISCONNECTED
* set but already be connected . In that case we ' ll verify the
* connection to root and then clear the flag .
*
* Note that target_dir could be removed by a concurrent operation . In
* that case reconnect_path may still succeed with target_dir fully
* connected , but further operations using the filehandle will fail when
* necessary ( due to S_DEAD being set on the directory ) .
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
{
2013-10-18 05:34:21 +04:00
struct dentry * dentry , * parent ;
2005-04-17 02:20:36 +04:00
2013-10-18 05:34:21 +04:00
dentry = dget ( target_dir ) ;
2005-04-17 02:20:36 +04:00
2013-10-18 05:34:21 +04:00
while ( dentry - > d_flags & DCACHE_DISCONNECTED ) {
2013-10-18 05:42:35 +04:00
BUG_ON ( dentry = = mnt - > mnt_sb - > s_root ) ;
2013-10-16 23:48:53 +04:00
2013-10-18 05:34:21 +04:00
if ( IS_ROOT ( dentry ) )
parent = reconnect_one ( mnt , dentry , nbuf ) ;
else
parent = dget_parent ( dentry ) ;
if ( ! parent )
2013-09-10 00:15:13 +04:00
break ;
2013-10-18 05:42:35 +04:00
dput ( dentry ) ;
2013-10-18 05:34:21 +04:00
if ( IS_ERR ( parent ) )
return PTR_ERR ( parent ) ;
dentry = parent ;
2005-04-17 02:20:36 +04:00
}
2013-10-18 05:34:21 +04:00
dput ( dentry ) ;
2013-10-17 05:09:30 +04:00
clear_disconnected ( target_dir ) ;
2007-07-17 15:04:33 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
struct getdents_callback {
2013-05-15 21:52:59 +04:00
struct dir_context ctx ;
2005-04-17 02:20:36 +04:00
char * name ; /* name that was found. It already points to a
buffer NAME_MAX + 1 is size */
2013-09-10 19:41:12 +04:00
u64 ino ; /* the inum we are looking for */
2005-04-17 02:20:36 +04:00
int found ; /* inode matched? */
int sequence ; /* sequence counter */
} ;
/*
* A rather strange filldir function to capture
* the name matching the specified inode number .
*/
2014-10-30 19:37:34 +03:00
static int filldir_one ( struct dir_context * ctx , 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
{
2014-10-30 19:37:34 +03:00
struct getdents_callback * buf =
container_of ( ctx , struct getdents_callback , ctx ) ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
buf - > sequence + + ;
2013-09-07 00:55:36 +04:00
if ( buf - > ino = = ino & & len < = NAME_MAX ) {
2005-04-17 02:20:36 +04:00
memcpy ( buf - > name , name , len ) ;
buf - > name [ len ] = ' \0 ' ;
buf - > found = 1 ;
result = - 1 ;
}
return result ;
}
/**
* get_name - default export_operations - > get_name function
2014-06-05 03:11:15 +04:00
* @ path : the directory in which to find a name
2005-04-17 02:20:36 +04:00
* @ 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 ;
2013-09-10 19:41:12 +04:00
struct kstat stat ;
struct path child_path = {
. mnt = path - > mnt ,
. dentry = child ,
} ;
2013-05-23 06:22:04 +04:00
struct getdents_callback buffer = {
. ctx . actor = filldir_one ,
. name = name ,
} ;
2005-04-17 02:20:36 +04:00
error = - ENOTDIR ;
if ( ! dir | | ! S_ISDIR ( dir - > i_mode ) )
goto out ;
error = - EINVAL ;
if ( ! dir - > i_fop )
goto out ;
2013-09-10 19:41:12 +04:00
/*
* inode - > i_ino is unsigned long , kstat - > ino is u64 , so the
* former would be insufficient on 32 - bit hosts when the
* filesystem supports 64 - bit inode numbers . So we need to
* actually call - > getattr , not just read i_ino :
*/
error = vfs_getattr_nosec ( & child_path , & stat ) ;
if ( error )
return error ;
buffer . ino = stat . ino ;
2005-04-17 02:20:36 +04:00
/*
* 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 ;
2013-05-23 05:44:23 +04:00
if ( ! file - > f_op - > iterate )
2005-04-17 02:20:36 +04:00
goto out_close ;
buffer . sequence = 0 ;
while ( 1 ) {
int old_seq = buffer . sequence ;
2013-05-15 21:52:59 +04:00
error = iterate_dir ( file , & buffer . ctx ) ;
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
2014-06-05 03:11:15 +04:00
* @ fid : where to store the file handle fragment
2005-04-17 02:20:36 +04:00
* @ 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 ;
2012-08-29 18:10:10 +04:00
return FILEID_INVALID ;
2011-01-29 16:13:25 +03:00
} else if ( len < 2 ) {
* max_len = 2 ;
2012-08-29 18:10:10 +04:00
return FILEID_INVALID ;
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 ;
}
2012-12-18 04:05:08 +04:00
int exportfs_encode_inode_fh ( struct inode * inode , struct fid * fid ,
int * max_len , struct inode * parent )
{
const struct export_operations * nop = inode - > i_sb - > s_export_op ;
if ( nop & & nop - > encode_fh )
return nop - > encode_fh ( inode , fid - > raw , max_len , parent ) ;
return export_encode_fh ( inode , fid , max_len , parent ) ;
}
EXPORT_SYMBOL_GPL ( exportfs_encode_inode_fh ) ;
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-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 ;
}
2012-12-18 04:05:08 +04:00
error = exportfs_encode_inode_fh ( inode , fid , max_len , parent ) ;
2012-04-02 22:34:06 +04:00
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 ;
VFS: (Scripted) Convert S_ISLNK/DIR/REG(dentry->d_inode) to d_is_*(dentry)
Convert the following where appropriate:
(1) S_ISLNK(dentry->d_inode) to d_is_symlink(dentry).
(2) S_ISREG(dentry->d_inode) to d_is_reg(dentry).
(3) S_ISDIR(dentry->d_inode) to d_is_dir(dentry). This is actually more
complicated than it appears as some calls should be converted to
d_can_lookup() instead. The difference is whether the directory in
question is a real dir with a ->lookup op or whether it's a fake dir with
a ->d_automount op.
In some circumstances, we can subsume checks for dentry->d_inode not being
NULL into this, provided we the code isn't in a filesystem that expects
d_inode to be NULL if the dirent really *is* negative (ie. if we're going to
use d_inode() rather than d_backing_inode() to get the inode pointer).
Note that the dentry type field may be set to something other than
DCACHE_MISS_TYPE when d_inode is NULL in the case of unionmount, where the VFS
manages the fall-through from a negative dentry to a lower layer. In such a
case, the dentry type of the negative union dentry is set to the same as the
type of the lower dentry.
However, if you know d_inode is not NULL at the call site, then you can use
the d_is_xxx() functions even in a filesystem.
There is one further complication: a 0,0 chardev dentry may be labelled
DCACHE_WHITEOUT_TYPE rather than DCACHE_SPECIAL_TYPE. Strictly, this was
intended for special directory entry types that don't have attached inodes.
The following perl+coccinelle script was used:
use strict;
my @callers;
open($fd, 'git grep -l \'S_IS[A-Z].*->d_inode\' |') ||
die "Can't grep for S_ISDIR and co. callers";
@callers = <$fd>;
close($fd);
unless (@callers) {
print "No matches\n";
exit(0);
}
my @cocci = (
'@@',
'expression E;',
'@@',
'',
'- S_ISLNK(E->d_inode->i_mode)',
'+ d_is_symlink(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISDIR(E->d_inode->i_mode)',
'+ d_is_dir(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISREG(E->d_inode->i_mode)',
'+ d_is_reg(E)' );
my $coccifile = "tmp.sp.cocci";
open($fd, ">$coccifile") || die $coccifile;
print($fd "$_\n") || die $coccifile foreach (@cocci);
close($fd);
foreach my $file (@callers) {
chomp $file;
print "Processing ", $file, "\n";
system("spatch", "--sp-file", $coccifile, $file, "--in-place", "--no-show-diff") == 0 ||
die "spatch failed";
}
[AV: overlayfs parts skipped]
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2015-01-29 15:02:35 +03:00
if ( d_is_dir ( result ) ) {
2007-10-22 03:42:05 +04:00
/*
* 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 " ) ;