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>
2007-10-20 06:59:18 +04:00
# include <linux/sched.h>
# include <linux/wait.h>
2010-09-18 01:31:30 +04:00
# include <linux/namei.h>
2005-04-17 02:20:36 +04:00
2007-11-08 12:05:04 +03:00
# include "internal.h"
2009-04-01 17:22:24 +04:00
# include "nfs4_fs.h"
2010-09-18 01:31:30 +04:00
# include "iostat.h"
# include "delegation.h"
2007-11-08 12:05:04 +03:00
2005-04-17 02:20:36 +04:00
struct nfs_unlinkdata {
2007-10-16 02:17:53 +04:00
struct hlist_node list ;
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 ;
2010-04-17 00:22:50 +04:00
struct nfs_fattr dir_attr ;
2005-04-17 02:20:36 +04:00
} ;
/**
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
}
2007-10-16 02:17:53 +04:00
static void nfs_free_dname ( struct nfs_unlinkdata * data )
{
kfree ( data - > args . name . name ) ;
data - > args . name . name = NULL ;
data - > args . name . len = 0 ;
}
static void nfs_dec_sillycount ( struct inode * dir )
{
struct nfs_inode * nfsi = NFS_I ( dir ) ;
if ( atomic_dec_return ( & nfsi - > silly_count ) = = 1 )
wake_up ( & nfsi - > waitqueue ) ;
}
2005-04-17 02:20:36 +04:00
/**
* 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 ) )
2009-12-07 17:00:24 +03:00
nfs_restart_rpc ( task , NFS_SERVER ( dir ) - > nfs_client ) ;
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 ;
2008-07-28 02:03:19 +04:00
struct super_block * sb = data - > dir - > i_sb ;
2007-10-16 02:17:53 +04:00
nfs_dec_sillycount ( data - > dir ) ;
2007-07-14 23:39:58 +04:00
nfs_free_unlinkdata ( data ) ;
2008-07-28 02:19:01 +04:00
nfs_sb_deactive ( sb ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-01 17:22:24 +04:00
# if defined(CONFIG_NFS_V4_1)
void nfs_unlink_prepare ( struct rpc_task * task , void * calldata )
{
struct nfs_unlinkdata * data = calldata ;
struct nfs_server * server = NFS_SERVER ( data - > dir ) ;
2010-06-16 17:52:26 +04:00
if ( nfs4_setup_sequence ( server , & data - > args . seq_args ,
2009-04-01 17:22:24 +04:00
& data - > res . seq_res , 1 , task ) )
return ;
rpc_call_start ( task ) ;
}
# endif /* CONFIG_NFS_V4_1 */
2006-01-03 11:55:04 +03:00
static const struct rpc_call_ops nfs_unlink_ops = {
. rpc_call_done = nfs_async_unlink_done ,
. rpc_release = nfs_async_unlink_release ,
2009-04-01 17:22:24 +04:00
# if defined(CONFIG_NFS_V4_1)
. rpc_call_prepare = nfs_unlink_prepare ,
# endif /* CONFIG_NFS_V4_1 */
2006-01-03 11:55:04 +03:00
} ;
2007-10-16 02:17:53 +04:00
static int nfs_do_call_unlink ( struct dentry * parent , struct inode * dir , struct nfs_unlinkdata * data )
2007-07-14 23:39:58 +04:00
{
2007-07-14 23:40:01 +04:00
struct rpc_message msg = {
. rpc_argp = & data - > args ,
. rpc_resp = & data - > res ,
. rpc_cred = data - > cred ,
} ;
2007-07-14 23:39:59 +04:00
struct rpc_task_setup task_setup_data = {
2007-07-14 23:40:01 +04:00
. rpc_message = & msg ,
2007-07-14 23:39:59 +04:00
. callback_ops = & nfs_unlink_ops ,
. callback_data = data ,
2008-07-28 02:19:01 +04:00
. workqueue = nfsiod_workqueue ,
2007-07-14 23:39:59 +04:00
. flags = RPC_TASK_ASYNC ,
} ;
2007-07-14 23:39:58 +04:00
struct rpc_task * task ;
2007-10-16 02:17:53 +04:00
struct dentry * alias ;
alias = d_lookup ( parent , & data - > args . name ) ;
if ( alias ! = NULL ) {
int ret = 0 ;
2011-03-16 12:44:14 +03:00
void * devname_garbage = NULL ;
2008-01-29 03:42:59 +03:00
2007-10-16 02:17:53 +04:00
/*
* Hey , we raced with lookup . . . See if we need to transfer
* the sillyrename information to the aliased dentry .
*/
nfs_free_dname ( data ) ;
spin_lock ( & alias - > d_lock ) ;
2008-01-29 03:42:59 +03:00
if ( alias - > d_inode ! = NULL & &
! ( alias - > d_flags & DCACHE_NFSFS_RENAMED ) ) {
2011-03-16 12:44:14 +03:00
devname_garbage = alias - > d_fsdata ;
2007-10-16 02:17:53 +04:00
alias - > d_fsdata = data ;
2008-01-27 01:37:47 +03:00
alias - > d_flags | = DCACHE_NFSFS_RENAMED ;
2007-10-16 02:17:53 +04:00
ret = 1 ;
}
spin_unlock ( & alias - > d_lock ) ;
nfs_dec_sillycount ( dir ) ;
dput ( alias ) ;
2011-03-16 12:44:14 +03:00
/*
* If we ' d displaced old cached devname , free it . At that
* point dentry is definitely not a root , so we won ' t need
* that anymore .
*/
if ( devname_garbage )
kfree ( devname_garbage ) ;
2007-10-16 02:17:53 +04:00
return ret ;
}
data - > dir = igrab ( dir ) ;
if ( ! data - > dir ) {
nfs_dec_sillycount ( dir ) ;
return 0 ;
}
2008-07-28 02:19:01 +04:00
nfs_sb_active ( dir - > i_sb ) ;
2007-10-16 02:17:53 +04:00
data - > args . fh = NFS_FH ( dir ) ;
2010-04-17 00:22:50 +04:00
nfs_fattr_init ( data - > res . dir_attr ) ;
2007-10-16 02:17:53 +04:00
2007-07-14 23:40:01 +04:00
NFS_PROTO ( dir ) - > unlink_setup ( & msg , dir ) ;
2007-07-14 23:39:59 +04:00
2007-07-14 23:40:01 +04:00
task_setup_data . rpc_client = NFS_CLIENT ( dir ) ;
2007-07-14 23:39:59 +04:00
task = rpc_run_task ( & task_setup_data ) ;
2007-10-16 02:17:53 +04:00
if ( ! IS_ERR ( task ) )
2011-02-21 22:05:41 +03:00
rpc_put_task_async ( task ) ;
2007-10-16 02:17:53 +04:00
return 1 ;
}
static int nfs_call_unlink ( struct dentry * dentry , struct nfs_unlinkdata * data )
{
2007-07-14 23:39:58 +04:00
struct dentry * parent ;
struct inode * dir ;
2007-10-16 02:17:53 +04:00
int ret = 0 ;
2007-07-14 23:39:58 +04:00
parent = dget_parent ( dentry ) ;
if ( parent = = NULL )
goto out_free ;
2007-10-16 02:17:53 +04:00
dir = parent - > d_inode ;
2007-10-21 20:02:22 +04:00
if ( nfs_copy_dname ( dentry , data ) ! = 0 )
2007-10-16 02:17:53 +04:00
goto out_dput ;
/* Non-exclusive lock protects against concurrent lookup() calls */
spin_lock ( & dir - > i_lock ) ;
if ( atomic_inc_not_zero ( & NFS_I ( dir ) - > silly_count ) = = 0 ) {
/* Deferred delete */
hlist_add_head ( & data - > list , & NFS_I ( dir ) - > silly_list ) ;
spin_unlock ( & dir - > i_lock ) ;
ret = 1 ;
goto out_dput ;
}
spin_unlock ( & dir - > i_lock ) ;
ret = nfs_do_call_unlink ( parent , dir , data ) ;
out_dput :
2007-07-14 23:39:58 +04:00
dput ( parent ) ;
2007-10-16 02:17:53 +04:00
out_free :
return ret ;
}
2007-07-14 23:39:58 +04:00
2007-10-16 02:17:53 +04:00
void nfs_block_sillyrename ( struct dentry * dentry )
{
struct nfs_inode * nfsi = NFS_I ( dentry - > d_inode ) ;
2007-07-14 23:39:58 +04:00
2007-10-16 02:17:53 +04:00
wait_event ( nfsi - > waitqueue , atomic_cmpxchg ( & nfsi - > silly_count , 1 , 0 ) = = 1 ) ;
}
void nfs_unblock_sillyrename ( struct dentry * dentry )
{
struct inode * dir = dentry - > d_inode ;
struct nfs_inode * nfsi = NFS_I ( dir ) ;
struct nfs_unlinkdata * data ;
atomic_inc ( & nfsi - > silly_count ) ;
spin_lock ( & dir - > i_lock ) ;
while ( ! hlist_empty ( & nfsi - > silly_list ) ) {
if ( ! atomic_inc_not_zero ( & nfsi - > silly_count ) )
break ;
data = hlist_entry ( nfsi - > silly_list . first , struct nfs_unlinkdata , list ) ;
hlist_del ( & data - > list ) ;
spin_unlock ( & dir - > i_lock ) ;
if ( nfs_do_call_unlink ( dentry , dir , data ) = = 0 )
nfs_free_unlinkdata ( data ) ;
spin_lock ( & dir - > i_lock ) ;
}
spin_unlock ( & dir - > i_lock ) ;
2007-07-14 23:39:58 +04:00
}
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
*/
2010-09-18 01:31:30 +04:00
static 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 ;
2011-03-16 12:44:14 +03:00
void * devname_garbage = NULL ;
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 ;
2008-03-12 19:25:28 +03:00
data - > cred = rpc_lookup_cred ( ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( data - > cred ) ) {
status = PTR_ERR ( data - > cred ) ;
goto out_free ;
}
2010-04-17 00:22:50 +04:00
data - > res . dir_attr = & data - > dir_attr ;
2005-04-17 02:20:36 +04:00
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 ;
2011-03-16 12:44:14 +03:00
devname_garbage = dentry - > d_fsdata ;
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 ) ;
2011-03-16 12:44:14 +03:00
/*
* If we ' d displaced old cached devname , free it . At that
* point dentry is definitely not a root , so we won ' t need
* that anymore .
*/
if ( devname_garbage )
kfree ( devname_garbage ) ;
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 ;
2011-03-16 12:44:14 +03:00
dentry - > d_fsdata = NULL ;
2007-07-14 23:39:58 +04:00
}
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
}
2010-09-18 01:31:30 +04:00
2010-09-18 01:31:57 +04:00
/* Cancel a queued async unlink. Called when a sillyrename run fails. */
static void
nfs_cancel_async_unlink ( struct dentry * dentry )
{
spin_lock ( & dentry - > d_lock ) ;
if ( dentry - > d_flags & DCACHE_NFSFS_RENAMED ) {
struct nfs_unlinkdata * data = dentry - > d_fsdata ;
dentry - > d_flags & = ~ DCACHE_NFSFS_RENAMED ;
2011-03-16 12:44:14 +03:00
dentry - > d_fsdata = NULL ;
2010-09-18 01:31:57 +04:00
spin_unlock ( & dentry - > d_lock ) ;
nfs_free_unlinkdata ( data ) ;
return ;
}
spin_unlock ( & dentry - > d_lock ) ;
}
struct nfs_renamedata {
struct nfs_renameargs args ;
struct nfs_renameres res ;
struct rpc_cred * cred ;
struct inode * old_dir ;
struct dentry * old_dentry ;
struct nfs_fattr old_fattr ;
struct inode * new_dir ;
struct dentry * new_dentry ;
struct nfs_fattr new_fattr ;
} ;
/**
* nfs_async_rename_done - Sillyrename post - processing
* @ task : rpc_task of the sillyrename
* @ calldata : nfs_renamedata for the sillyrename
*
* Do the directory attribute updates and the d_move
*/
static void nfs_async_rename_done ( struct rpc_task * task , void * calldata )
{
struct nfs_renamedata * data = calldata ;
struct inode * old_dir = data - > old_dir ;
struct inode * new_dir = data - > new_dir ;
if ( ! NFS_PROTO ( old_dir ) - > rename_done ( task , old_dir , new_dir ) ) {
nfs_restart_rpc ( task , NFS_SERVER ( old_dir ) - > nfs_client ) ;
return ;
}
if ( task - > tk_status ! = 0 ) {
nfs_cancel_async_unlink ( data - > old_dentry ) ;
return ;
}
nfs_set_verifier ( data - > old_dentry , nfs_save_change_attribute ( old_dir ) ) ;
d_move ( data - > old_dentry , data - > new_dentry ) ;
}
/**
* nfs_async_rename_release - Release the sillyrename data .
* @ calldata : the struct nfs_renamedata to be released
*/
static void nfs_async_rename_release ( void * calldata )
{
struct nfs_renamedata * data = calldata ;
struct super_block * sb = data - > old_dir - > i_sb ;
if ( data - > old_dentry - > d_inode )
nfs_mark_for_revalidate ( data - > old_dentry - > d_inode ) ;
dput ( data - > old_dentry ) ;
dput ( data - > new_dentry ) ;
iput ( data - > old_dir ) ;
iput ( data - > new_dir ) ;
nfs_sb_deactive ( sb ) ;
put_rpccred ( data - > cred ) ;
kfree ( data ) ;
}
# if defined(CONFIG_NFS_V4_1)
static void nfs_rename_prepare ( struct rpc_task * task , void * calldata )
{
struct nfs_renamedata * data = calldata ;
struct nfs_server * server = NFS_SERVER ( data - > old_dir ) ;
if ( nfs4_setup_sequence ( server , & data - > args . seq_args ,
& data - > res . seq_res , 1 , task ) )
return ;
rpc_call_start ( task ) ;
}
# endif /* CONFIG_NFS_V4_1 */
static const struct rpc_call_ops nfs_rename_ops = {
. rpc_call_done = nfs_async_rename_done ,
. rpc_release = nfs_async_rename_release ,
# if defined(CONFIG_NFS_V4_1)
. rpc_call_prepare = nfs_rename_prepare ,
# endif /* CONFIG_NFS_V4_1 */
} ;
/**
* nfs_async_rename - perform an asynchronous rename operation
* @ old_dir : directory that currently holds the dentry to be renamed
* @ new_dir : target directory for the rename
* @ old_dentry : original dentry to be renamed
* @ new_dentry : dentry to which the old_dentry should be renamed
*
* It ' s expected that valid references to the dentries and inodes are held
*/
static struct rpc_task *
nfs_async_rename ( struct inode * old_dir , struct inode * new_dir ,
struct dentry * old_dentry , struct dentry * new_dentry )
{
struct nfs_renamedata * data ;
struct rpc_message msg = { } ;
struct rpc_task_setup task_setup_data = {
. rpc_message = & msg ,
. callback_ops = & nfs_rename_ops ,
. workqueue = nfsiod_workqueue ,
. rpc_client = NFS_CLIENT ( old_dir ) ,
. flags = RPC_TASK_ASYNC ,
} ;
2010-09-24 17:17:01 +04:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2010-09-18 01:31:57 +04:00
if ( data = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2010-12-21 18:52:24 +03:00
task_setup_data . callback_data = data ;
2010-09-18 01:31:57 +04:00
data - > cred = rpc_lookup_cred ( ) ;
if ( IS_ERR ( data - > cred ) ) {
2010-09-22 00:52:40 +04:00
struct rpc_task * task = ERR_CAST ( data - > cred ) ;
2010-09-18 01:31:57 +04:00
kfree ( data ) ;
return task ;
}
msg . rpc_argp = & data - > args ;
msg . rpc_resp = & data - > res ;
msg . rpc_cred = data - > cred ;
/* set up nfs_renamedata */
data - > old_dir = old_dir ;
2010-10-27 19:00:08 +04:00
ihold ( old_dir ) ;
2010-09-18 01:31:57 +04:00
data - > new_dir = new_dir ;
2010-10-27 19:00:08 +04:00
ihold ( new_dir ) ;
2010-09-18 01:31:57 +04:00
data - > old_dentry = dget ( old_dentry ) ;
data - > new_dentry = dget ( new_dentry ) ;
nfs_fattr_init ( & data - > old_fattr ) ;
nfs_fattr_init ( & data - > new_fattr ) ;
/* set up nfs_renameargs */
data - > args . old_dir = NFS_FH ( old_dir ) ;
data - > args . old_name = & old_dentry - > d_name ;
data - > args . new_dir = NFS_FH ( new_dir ) ;
data - > args . new_name = & new_dentry - > d_name ;
/* set up nfs_renameres */
data - > res . old_fattr = & data - > old_fattr ;
data - > res . new_fattr = & data - > new_fattr ;
nfs_sb_active ( old_dir - > i_sb ) ;
NFS_PROTO ( data - > old_dir ) - > rename_setup ( & msg , old_dir ) ;
2010-09-22 00:52:40 +04:00
return rpc_run_task ( & task_setup_data ) ;
2010-09-18 01:31:57 +04:00
}
2010-09-18 01:31:30 +04:00
/**
* nfs_sillyrename - Perform a silly - rename of a dentry
* @ dir : inode of directory that contains dentry
* @ dentry : dentry to be sillyrenamed
*
* NFSv2 / 3 is stateless and the server doesn ' t know when the client is
* holding a file open . To prevent application problems when a file is
* unlinked while it ' s still open , the client performs a " silly-rename " .
* That is , it renames the file to a hidden file in the same directory ,
* and only performs the unlink once the last reference to it is put .
*
* The final cleanup is done during dentry_iput .
*/
int
nfs_sillyrename ( struct inode * dir , struct dentry * dentry )
{
static unsigned int sillycounter ;
const int fileidsize = sizeof ( NFS_FILEID ( dentry - > d_inode ) ) * 2 ;
const int countersize = sizeof ( sillycounter ) * 2 ;
const int slen = sizeof ( " .nfs " ) + fileidsize + countersize - 1 ;
char silly [ slen + 1 ] ;
struct dentry * sdentry ;
2010-09-18 01:31:57 +04:00
struct rpc_task * task ;
2010-09-18 01:31:30 +04:00
int error = - EIO ;
dfprintk ( VFS , " NFS: silly-rename(%s/%s, ct=%d) \n " ,
dentry - > d_parent - > d_name . name , dentry - > d_name . name ,
2011-01-07 09:49:32 +03:00
dentry - > d_count ) ;
2010-09-18 01:31:30 +04:00
nfs_inc_stats ( dir , NFSIOS_SILLYRENAME ) ;
/*
* We don ' t allow a dentry to be silly - renamed twice .
*/
error = - EBUSY ;
if ( dentry - > d_flags & DCACHE_NFSFS_RENAMED )
goto out ;
sprintf ( silly , " .nfs%*.*Lx " ,
fileidsize , fileidsize ,
( unsigned long long ) NFS_FILEID ( dentry - > d_inode ) ) ;
/* Return delegation in anticipation of the rename */
nfs_inode_return_delegation ( dentry - > d_inode ) ;
sdentry = NULL ;
do {
char * suffix = silly + slen - countersize ;
dput ( sdentry ) ;
sillycounter + + ;
sprintf ( suffix , " %*.*x " , countersize , countersize , sillycounter ) ;
dfprintk ( VFS , " NFS: trying to rename %s to %s \n " ,
dentry - > d_name . name , silly ) ;
sdentry = lookup_one_len ( silly , dentry - > d_parent , slen ) ;
/*
* N . B . Better to return EBUSY here . . . it could be
* dangerous to delete the file while it ' s in use .
*/
if ( IS_ERR ( sdentry ) )
goto out ;
} while ( sdentry - > d_inode ! = NULL ) ; /* need negative lookup */
2010-09-18 01:31:57 +04:00
/* queue unlink first. Can't do this from rpc_release as it
* has to allocate memory
*/
error = nfs_async_unlink ( dir , dentry ) ;
if ( error )
goto out_dput ;
/* run the rename task, undo unlink if it fails */
task = nfs_async_rename ( dir , dir , dentry , sdentry ) ;
if ( IS_ERR ( task ) ) {
error = - EBUSY ;
nfs_cancel_async_unlink ( dentry ) ;
goto out_dput ;
2010-09-18 01:31:30 +04:00
}
2010-09-18 01:31:57 +04:00
/* wait for the RPC task to complete, unless a SIGKILL intervenes */
error = rpc_wait_for_completion_task ( task ) ;
if ( error = = 0 )
error = task - > tk_status ;
rpc_put_task ( task ) ;
out_dput :
2010-09-18 01:31:30 +04:00
dput ( sdentry ) ;
out :
return error ;
}