2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / unlink . c
*
* nfs sillydelete handling
*
*/
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/dcache.h>
# include <linux/sunrpc/sched.h>
# include <linux/sunrpc/clnt.h>
# include <linux/nfs_fs.h>
struct nfs_unlinkdata {
2007-07-14 23:39:58 +04:00
struct nfs_removeargs args ;
struct nfs_removeres res ;
struct inode * dir ;
2005-04-17 02:20:36 +04:00
struct rpc_cred * cred ;
} ;
/**
2007-07-14 23:39:58 +04:00
* nfs_free_unlinkdata - release data from a sillydelete operation .
2005-04-17 02:20:36 +04:00
* @ data : pointer to unlink structure .
*/
static void
2007-07-14 23:39:58 +04:00
nfs_free_unlinkdata ( struct nfs_unlinkdata * data )
2005-04-17 02:20:36 +04:00
{
2007-07-14 23:39:58 +04:00
iput ( data - > dir ) ;
put_rpccred ( data - > cred ) ;
kfree ( data - > args . name . name ) ;
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
}
# define NAME_ALLOC_LEN(len) ((len+16) & ~15)
/**
* nfs_copy_dname - copy dentry name to data structure
* @ dentry : pointer to dentry
* @ data : nfs_unlinkdata
*/
2007-07-14 23:39:58 +04:00
static int nfs_copy_dname ( struct dentry * dentry , struct nfs_unlinkdata * data )
2005-04-17 02:20:36 +04:00
{
char * str ;
int len = dentry - > d_name . len ;
2007-07-14 23:39:58 +04:00
str = kmemdup ( dentry - > d_name . name , NAME_ALLOC_LEN ( len ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! str )
2007-07-14 23:39:58 +04:00
return - ENOMEM ;
data - > args . name . len = len ;
data - > args . name . name = str ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* nfs_async_unlink_init - Initialize the RPC info
2007-07-14 23:39:58 +04:00
* task : rpc_task of the sillydelete
2005-04-17 02:20:36 +04:00
*/
2006-01-03 11:55:05 +03:00
static void nfs_async_unlink_init ( struct rpc_task * task , void * calldata )
2005-04-17 02:20:36 +04:00
{
2007-07-14 23:39:58 +04:00
struct nfs_unlinkdata * data = calldata ;
struct inode * dir = data - > dir ;
struct rpc_message msg = {
. rpc_argp = & data - > args ,
. rpc_resp = & data - > res ,
. rpc_cred = data - > cred ,
2005-04-17 02:20:36 +04:00
} ;
2007-07-14 23:39:58 +04:00
NFS_PROTO ( dir ) - > unlink_setup ( & msg , dir ) ;
2005-04-17 02:20:36 +04:00
rpc_call_setup ( task , & msg , 0 ) ;
}
/**
* nfs_async_unlink_done - Sillydelete post - processing
* @ task : rpc_task of the sillydelete
*
* Do the directory attribute update .
*/
2006-01-03 11:55:04 +03:00
static void nfs_async_unlink_done ( struct rpc_task * task , void * calldata )
2005-04-17 02:20:36 +04:00
{
2007-07-14 23:39:58 +04:00
struct nfs_unlinkdata * data = calldata ;
struct inode * dir = data - > dir ;
if ( ! NFS_PROTO ( dir ) - > unlink_done ( task , dir ) )
rpc_restart_call ( task ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nfs_async_unlink_release - Release the sillydelete data .
* @ task : rpc_task of the sillydelete
*
* We need to call nfs_put_unlinkdata as a ' tk_release ' task since the
* rpc_task would be freed too .
*/
2006-01-03 11:55:04 +03:00
static void nfs_async_unlink_release ( void * calldata )
2005-04-17 02:20:36 +04:00
{
2006-01-03 11:55:04 +03:00
struct nfs_unlinkdata * data = calldata ;
2007-07-14 23:39:58 +04:00
nfs_free_unlinkdata ( data ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-03 11:55:04 +03:00
static const struct rpc_call_ops nfs_unlink_ops = {
2006-01-03 11:55:05 +03:00
. rpc_call_prepare = nfs_async_unlink_init ,
2006-01-03 11:55:04 +03:00
. rpc_call_done = nfs_async_unlink_done ,
. rpc_release = nfs_async_unlink_release ,
} ;
2007-07-14 23:39:58 +04:00
static int nfs_call_unlink ( struct dentry * dentry , struct nfs_unlinkdata * data )
{
struct rpc_task * task ;
struct dentry * parent ;
struct inode * dir ;
if ( nfs_copy_dname ( dentry , data ) < 0 )
goto out_free ;
parent = dget_parent ( dentry ) ;
if ( parent = = NULL )
goto out_free ;
dir = igrab ( parent - > d_inode ) ;
dput ( parent ) ;
if ( dir = = NULL )
goto out_free ;
data - > dir = dir ;
data - > args . fh = NFS_FH ( dir ) ;
nfs_fattr_init ( & data - > res . dir_attr ) ;
task = rpc_run_task ( NFS_CLIENT ( dir ) , RPC_TASK_ASYNC , & nfs_unlink_ops , data ) ;
if ( ! IS_ERR ( task ) )
rpc_put_task ( task ) ;
return 1 ;
out_free :
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* nfs_async_unlink - asynchronous unlinking of a file
2007-07-14 23:39:58 +04:00
* @ dir : parent directory of dentry
2005-04-17 02:20:36 +04:00
* @ dentry : dentry to unlink
*/
int
2007-07-14 23:39:58 +04:00
nfs_async_unlink ( struct inode * dir , struct dentry * dentry )
2005-04-17 02:20:36 +04:00
{
2007-07-14 23:39:58 +04:00
struct nfs_unlinkdata * data ;
int status = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:10 +03:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2007-07-14 23:39:58 +04:00
if ( data = = NULL )
2005-04-17 02:20:36 +04:00
goto out ;
2007-07-14 23:39:58 +04:00
data - > cred = rpcauth_lookupcred ( NFS_CLIENT ( dir ) - > cl_auth , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( data - > cred ) ) {
status = PTR_ERR ( data - > cred ) ;
goto out_free ;
}
2007-07-14 23:39:58 +04:00
status = - EBUSY ;
2005-04-17 02:20:36 +04:00
spin_lock ( & dentry - > d_lock ) ;
2007-07-14 23:39:58 +04:00
if ( dentry - > d_flags & DCACHE_NFSFS_RENAMED )
goto out_unlock ;
2005-04-17 02:20:36 +04:00
dentry - > d_flags | = DCACHE_NFSFS_RENAMED ;
2007-07-14 23:39:58 +04:00
dentry - > d_fsdata = data ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & dentry - > d_lock ) ;
2007-07-14 23:39:58 +04:00
return 0 ;
out_unlock :
spin_unlock ( & dentry - > d_lock ) ;
put_rpccred ( data - > cred ) ;
2005-04-17 02:20:36 +04:00
out_free :
kfree ( data ) ;
2007-07-14 23:39:58 +04:00
out :
2005-04-17 02:20:36 +04:00
return status ;
}
/**
* nfs_complete_unlink - Initialize completion of the sillydelete
* @ dentry : dentry to delete
2007-07-14 23:39:58 +04:00
* @ inode : inode
2005-04-17 02:20:36 +04:00
*
* Since we ' re most likely to be called by dentry_iput ( ) , we
* only use the dentry to find the sillydelete . We then copy the name
* into the qstr .
*/
void
2007-07-14 23:39:58 +04:00
nfs_complete_unlink ( struct dentry * dentry , struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2007-07-14 23:39:58 +04:00
struct nfs_unlinkdata * data = NULL ;
2005-04-17 02:20:36 +04:00
spin_lock ( & dentry - > d_lock ) ;
2007-07-14 23:39:58 +04:00
if ( dentry - > d_flags & DCACHE_NFSFS_RENAMED ) {
dentry - > d_flags & = ~ DCACHE_NFSFS_RENAMED ;
data = dentry - > d_fsdata ;
}
2005-04-17 02:20:36 +04:00
spin_unlock ( & dentry - > d_lock ) ;
2007-07-14 23:39:58 +04:00
if ( data ! = NULL & & ( NFS_STALE ( inode ) | | ! nfs_call_unlink ( dentry , data ) ) )
nfs_free_unlinkdata ( data ) ;
2005-04-17 02:20:36 +04:00
}