unlink.c 7.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 *  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>
14 15
#include <linux/sched.h>
#include <linux/wait.h>
L
Linus Torvalds 已提交
16

17
#include "internal.h"
18
#include "nfs4_fs.h"
19

L
Linus Torvalds 已提交
20
struct nfs_unlinkdata {
T
Trond Myklebust 已提交
21
	struct hlist_node list;
22 23 24
	struct nfs_removeargs args;
	struct nfs_removeres res;
	struct inode *dir;
L
Linus Torvalds 已提交
25 26 27 28
	struct rpc_cred	*cred;
};

/**
29
 * nfs_free_unlinkdata - release data from a sillydelete operation.
L
Linus Torvalds 已提交
30 31 32
 * @data: pointer to unlink structure.
 */
static void
33
nfs_free_unlinkdata(struct nfs_unlinkdata *data)
L
Linus Torvalds 已提交
34
{
35 36 37 38
	iput(data->dir);
	put_rpccred(data->cred);
	kfree(data->args.name.name);
	kfree(data);
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46
}

#define NAME_ALLOC_LEN(len)	((len+16) & ~15)
/**
 * nfs_copy_dname - copy dentry name to data structure
 * @dentry: pointer to dentry
 * @data: nfs_unlinkdata
 */
47
static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
L
Linus Torvalds 已提交
48 49 50 51
{
	char		*str;
	int		len = dentry->d_name.len;

52
	str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL);
L
Linus Torvalds 已提交
53
	if (!str)
54 55 56 57
		return -ENOMEM;
	data->args.name.len = len;
	data->args.name.name = str;
	return 0;
L
Linus Torvalds 已提交
58 59
}

T
Trond Myklebust 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73
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);
}

L
Linus Torvalds 已提交
74 75 76 77 78 79
/**
 * nfs_async_unlink_done - Sillydelete post-processing
 * @task: rpc_task of the sillydelete
 *
 * Do the directory attribute update.
 */
80
static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
L
Linus Torvalds 已提交
81
{
82 83
	struct nfs_unlinkdata *data = calldata;
	struct inode *dir = data->dir;
84
	struct nfs_removeres *res = task->tk_msg.rpc_resp;
85 86

	if (!NFS_PROTO(dir)->unlink_done(task, dir))
87 88
		nfs4_restart_rpc(task, NFS_SERVER(dir)->nfs_client,
				 &res->seq_res);
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97
}

/**
 * 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.
 */
98
static void nfs_async_unlink_release(void *calldata)
L
Linus Torvalds 已提交
99
{
100
	struct nfs_unlinkdata	*data = calldata;
101
	struct super_block *sb = data->dir->i_sb;
T
Trond Myklebust 已提交
102 103

	nfs_dec_sillycount(data->dir);
104
	nfs_free_unlinkdata(data);
105
	nfs_sb_deactive(sb);
L
Linus Torvalds 已提交
106 107
}

108 109 110 111 112 113 114 115 116 117 118 119 120
#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);

	if (nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
				&data->res.seq_res, 1, task))
		return;
	rpc_call_start(task);
}
#endif /* CONFIG_NFS_V4_1 */

121 122 123
static const struct rpc_call_ops nfs_unlink_ops = {
	.rpc_call_done = nfs_async_unlink_done,
	.rpc_release = nfs_async_unlink_release,
124 125 126
#if defined(CONFIG_NFS_V4_1)
	.rpc_call_prepare = nfs_unlink_prepare,
#endif /* CONFIG_NFS_V4_1 */
127 128
};

T
Trond Myklebust 已提交
129
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
130
{
131 132 133 134 135
	struct rpc_message msg = {
		.rpc_argp = &data->args,
		.rpc_resp = &data->res,
		.rpc_cred = data->cred,
	};
T
Trond Myklebust 已提交
136
	struct rpc_task_setup task_setup_data = {
137
		.rpc_message = &msg,
T
Trond Myklebust 已提交
138 139
		.callback_ops = &nfs_unlink_ops,
		.callback_data = data,
140
		.workqueue = nfsiod_workqueue,
T
Trond Myklebust 已提交
141 142
		.flags = RPC_TASK_ASYNC,
	};
143
	struct rpc_task *task;
T
Trond Myklebust 已提交
144 145 146 147 148
	struct dentry *alias;

	alias = d_lookup(parent, &data->args.name);
	if (alias != NULL) {
		int ret = 0;
149

T
Trond Myklebust 已提交
150 151 152 153 154 155
		/*
		 * 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);
156 157
		if (alias->d_inode != NULL &&
		    !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
T
Trond Myklebust 已提交
158
			alias->d_fsdata = data;
T
Trond Myklebust 已提交
159
			alias->d_flags |= DCACHE_NFSFS_RENAMED;
T
Trond Myklebust 已提交
160 161 162 163 164 165 166 167 168 169 170 171
			ret = 1;
		}
		spin_unlock(&alias->d_lock);
		nfs_dec_sillycount(dir);
		dput(alias);
		return ret;
	}
	data->dir = igrab(dir);
	if (!data->dir) {
		nfs_dec_sillycount(dir);
		return 0;
	}
172
	nfs_sb_active(dir->i_sb);
T
Trond Myklebust 已提交
173 174 175
	data->args.fh = NFS_FH(dir);
	nfs_fattr_init(&data->res.dir_attr);

176
	NFS_PROTO(dir)->unlink_setup(&msg, dir);
T
Trond Myklebust 已提交
177

178
	task_setup_data.rpc_client = NFS_CLIENT(dir);
T
Trond Myklebust 已提交
179
	task = rpc_run_task(&task_setup_data);
T
Trond Myklebust 已提交
180 181 182 183 184 185 186
	if (!IS_ERR(task))
		rpc_put_task(task);
	return 1;
}

static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
{
187 188
	struct dentry *parent;
	struct inode *dir;
T
Trond Myklebust 已提交
189
	int ret = 0;
190 191 192 193 194


	parent = dget_parent(dentry);
	if (parent == NULL)
		goto out_free;
T
Trond Myklebust 已提交
195
	dir = parent->d_inode;
196
	if (nfs_copy_dname(dentry, data) != 0)
T
Trond Myklebust 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209
		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:
210
	dput(parent);
T
Trond Myklebust 已提交
211 212 213
out_free:
	return ret;
}
214

T
Trond Myklebust 已提交
215 216 217
void nfs_block_sillyrename(struct dentry *dentry)
{
	struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
218

T
Trond Myklebust 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	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);
241 242
}

L
Linus Torvalds 已提交
243 244
/**
 * nfs_async_unlink - asynchronous unlinking of a file
245
 * @dir: parent directory of dentry
L
Linus Torvalds 已提交
246 247 248
 * @dentry: dentry to unlink
 */
int
249
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
L
Linus Torvalds 已提交
250
{
251 252
	struct nfs_unlinkdata *data;
	int status = -ENOMEM;
L
Linus Torvalds 已提交
253

254
	data = kzalloc(sizeof(*data), GFP_KERNEL);
255
	if (data == NULL)
L
Linus Torvalds 已提交
256 257
		goto out;

258
	data->cred = rpc_lookup_cred();
L
Linus Torvalds 已提交
259 260 261 262
	if (IS_ERR(data->cred)) {
		status = PTR_ERR(data->cred);
		goto out_free;
	}
A
Andy Adamson 已提交
263
	data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
L
Linus Torvalds 已提交
264

265
	status = -EBUSY;
L
Linus Torvalds 已提交
266
	spin_lock(&dentry->d_lock);
267 268
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out_unlock;
L
Linus Torvalds 已提交
269
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
270
	dentry->d_fsdata = data;
L
Linus Torvalds 已提交
271
	spin_unlock(&dentry->d_lock);
272 273 274 275
	return 0;
out_unlock:
	spin_unlock(&dentry->d_lock);
	put_rpccred(data->cred);
L
Linus Torvalds 已提交
276 277
out_free:
	kfree(data);
278
out:
L
Linus Torvalds 已提交
279 280 281 282 283 284
	return status;
}

/**
 * nfs_complete_unlink - Initialize completion of the sillydelete
 * @dentry: dentry to delete
285
 * @inode: inode
L
Linus Torvalds 已提交
286 287 288 289 290 291
 *
 * 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
292
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
L
Linus Torvalds 已提交
293
{
294
	struct nfs_unlinkdata	*data = NULL;
L
Linus Torvalds 已提交
295 296

	spin_lock(&dentry->d_lock);
297 298 299 300
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		data = dentry->d_fsdata;
	}
L
Linus Torvalds 已提交
301
	spin_unlock(&dentry->d_lock);
302 303 304

	if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
		nfs_free_unlinkdata(data);
L
Linus Torvalds 已提交
305
}