aboutsummaryrefslogtreecommitdiff
path: root/fs/nfs/unlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r--fs/nfs/unlink.c151
1 files changed, 79 insertions, 72 deletions
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index 4f9319a2e56..de54129336c 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -14,20 +14,14 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/namei.h>
+#include <linux/fsnotify.h>
#include "internal.h"
#include "nfs4_fs.h"
#include "iostat.h"
#include "delegation.h"
-struct nfs_unlinkdata {
- struct hlist_node list;
- struct nfs_removeargs args;
- struct nfs_removeres res;
- struct inode *dir;
- struct rpc_cred *cred;
- struct nfs_fattr dir_attr;
-};
+#include "nfstrace.h"
/**
* nfs_free_unlinkdata - release data from a sillydelete operation.
@@ -86,6 +80,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
struct nfs_unlinkdata *data = calldata;
struct inode *dir = data->dir;
+ trace_nfs_sillyrename_unlink(data, task->tk_status);
if (!NFS_PROTO(dir)->unlink_done(task, dir))
rpc_restart_call_prepare(task);
}
@@ -107,25 +102,16 @@ static void nfs_async_unlink_release(void *calldata)
nfs_sb_deactive(sb);
}
-#if defined(CONFIG_NFS_V4_1)
-void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
+static void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
{
struct nfs_unlinkdata *data = calldata;
- struct nfs_server *server = NFS_SERVER(data->dir);
-
- if (nfs4_setup_sequence(server, &data->args.seq_args,
- &data->res.seq_res, 1, task))
- return;
- rpc_call_start(task);
+ NFS_PROTO(data->dir)->unlink_rpc_prepare(task, data);
}
-#endif /* CONFIG_NFS_V4_1 */
static const struct rpc_call_ops nfs_unlink_ops = {
.rpc_call_done = nfs_async_unlink_done,
.rpc_release = nfs_async_unlink_release,
-#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_unlink_prepare,
-#endif /* CONFIG_NFS_V4_1 */
};
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
@@ -222,6 +208,13 @@ out_free:
return ret;
}
+void nfs_wait_on_sillyrename(struct dentry *dentry)
+{
+ struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
+
+ wait_event(nfsi->waitqueue, atomic_read(&nfsi->silly_count) <= 1);
+}
+
void nfs_block_sillyrename(struct dentry *dentry)
{
struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
@@ -286,8 +279,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
* point dentry is definitely not a root, so we won't need
* that anymore.
*/
- if (devname_garbage)
- kfree(devname_garbage);
+ kfree(devname_garbage);
return 0;
out_unlock:
spin_unlock(&dentry->d_lock);
@@ -341,18 +333,6 @@ nfs_cancel_async_unlink(struct dentry *dentry)
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
@@ -366,20 +346,16 @@ static void nfs_async_rename_done(struct rpc_task *task, void *calldata)
struct inode *old_dir = data->old_dir;
struct inode *new_dir = data->new_dir;
struct dentry *old_dentry = data->old_dentry;
- struct dentry *new_dentry = data->new_dentry;
+ trace_nfs_sillyrename_rename(old_dir, old_dentry,
+ new_dir, data->new_dentry, task->tk_status);
if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) {
rpc_restart_call_prepare(task);
return;
}
- if (task->tk_status != 0) {
- nfs_cancel_async_unlink(old_dentry);
- return;
- }
-
- d_drop(old_dentry);
- d_drop(new_dentry);
+ if (data->complete)
+ data->complete(task, data);
}
/**
@@ -403,25 +379,16 @@ static void nfs_async_rename_release(void *calldata)
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);
+ NFS_PROTO(data->old_dir)->rename_rpc_prepare(task, data);
}
-#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 */
};
/**
@@ -433,9 +400,10 @@ static const struct rpc_call_ops nfs_rename_ops = {
*
* It's expected that valid references to the dentries and inodes are held
*/
-static struct rpc_task *
+struct rpc_task *
nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
- struct dentry *old_dentry, struct dentry *new_dentry)
+ struct dentry *old_dentry, struct dentry *new_dentry,
+ void (*complete)(struct rpc_task *, struct nfs_renamedata *))
{
struct nfs_renamedata *data;
struct rpc_message msg = { };
@@ -472,6 +440,7 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
data->new_dentry = dget(new_dentry);
nfs_fattr_init(&data->old_fattr);
nfs_fattr_init(&data->new_fattr);
+ data->complete = complete;
/* set up nfs_renameargs */
data->args.old_dir = NFS_FH(old_dir);
@@ -490,6 +459,35 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
return rpc_run_task(&task_setup_data);
}
+/*
+ * Perform tasks needed when a sillyrename is done such as cancelling the
+ * queued async unlink if it failed.
+ */
+static void
+nfs_complete_sillyrename(struct rpc_task *task, struct nfs_renamedata *data)
+{
+ struct dentry *dentry = data->old_dentry;
+
+ if (task->tk_status != 0) {
+ nfs_cancel_async_unlink(dentry);
+ return;
+ }
+
+ /*
+ * vfs_unlink and the like do not issue this when a file is
+ * sillyrenamed, so do it here.
+ */
+ fsnotify_nameremove(dentry, 0);
+}
+
+#define SILLYNAME_PREFIX ".nfs"
+#define SILLYNAME_PREFIX_LEN ((unsigned)sizeof(SILLYNAME_PREFIX) - 1)
+#define SILLYNAME_FILEID_LEN ((unsigned)sizeof(u64) << 1)
+#define SILLYNAME_COUNTER_LEN ((unsigned)sizeof(unsigned int) << 1)
+#define SILLYNAME_LEN (SILLYNAME_PREFIX_LEN + \
+ SILLYNAME_FILEID_LEN + \
+ SILLYNAME_COUNTER_LEN)
+
/**
* nfs_sillyrename - Perform a silly-rename of a dentry
* @dir: inode of directory that contains dentry
@@ -515,43 +513,39 @@ 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];
+ unsigned char silly[SILLYNAME_LEN + 1];
+ unsigned long long fileid;
struct dentry *sdentry;
struct rpc_task *task;
- int error = -EIO;
+ int error = -EBUSY;
- dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name,
- dentry->d_count);
+ dfprintk(VFS, "NFS: silly-rename(%pd2, ct=%d)\n",
+ dentry, d_count(dentry));
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));
+ fileid = NFS_FILEID(dentry->d_inode);
/* Return delegation in anticipation of the rename */
- nfs_inode_return_delegation(dentry->d_inode);
+ NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);
sdentry = NULL;
do {
- char *suffix = silly + slen - countersize;
-
+ int slen;
dput(sdentry);
sillycounter++;
- sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
+ slen = scnprintf(silly, sizeof(silly),
+ SILLYNAME_PREFIX "%0*llx%0*x",
+ SILLYNAME_FILEID_LEN, fileid,
+ SILLYNAME_COUNTER_LEN, sillycounter);
- dfprintk(VFS, "NFS: trying to rename %s to %s\n",
- dentry->d_name.name, silly);
+ dfprintk(VFS, "NFS: trying to rename %pd to %s\n",
+ dentry, silly);
sdentry = lookup_one_len(silly, dentry->d_parent, slen);
/*
@@ -578,7 +572,8 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
}
/* run the rename task, undo unlink if it fails */
- task = nfs_async_rename(dir, dir, dentry, sdentry);
+ task = nfs_async_rename(dir, dir, dentry, sdentry,
+ nfs_complete_sillyrename);
if (IS_ERR(task)) {
error = -EBUSY;
nfs_cancel_async_unlink(dentry);
@@ -589,6 +584,18 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
error = rpc_wait_for_completion_task(task);
if (error == 0)
error = task->tk_status;
+ switch (error) {
+ case 0:
+ /* The rename succeeded */
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ d_move(dentry, sdentry);
+ break;
+ case -ERESTARTSYS:
+ /* The result of the rename is unknown. Play it safe by
+ * forcing a new lookup */
+ d_drop(dentry);
+ d_drop(sdentry);
+ }
rpc_put_task(task);
out_dput:
dput(sdentry);