unlink.c 6.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *  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 {
T
Trond Myklebust 已提交
17
	struct hlist_node list;
18 19 20
	struct nfs_removeargs args;
	struct nfs_removeres res;
	struct inode *dir;
L
Linus Torvalds 已提交
21 22 23 24
	struct rpc_cred	*cred;
};

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

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

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

T
Trond Myklebust 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69
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 已提交
70 71
/**
 * nfs_async_unlink_init - Initialize the RPC info
72
 * task: rpc_task of the sillydelete
L
Linus Torvalds 已提交
73
 */
T
Trond Myklebust 已提交
74
static void nfs_async_unlink_init(struct rpc_task *task, void *calldata)
L
Linus Torvalds 已提交
75
{
76 77 78 79 80 81
	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,
L
Linus Torvalds 已提交
82 83
	};

84
	NFS_PROTO(dir)->unlink_setup(&msg, dir);
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93
	rpc_call_setup(task, &msg, 0);
}

/**
 * nfs_async_unlink_done - Sillydelete post-processing
 * @task: rpc_task of the sillydelete
 *
 * Do the directory attribute update.
 */
94
static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
L
Linus Torvalds 已提交
95
{
96 97 98 99 100
	struct nfs_unlinkdata *data = calldata;
	struct inode *dir = data->dir;

	if (!NFS_PROTO(dir)->unlink_done(task, dir))
		rpc_restart_call(task);
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109
}

/**
 * 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.
 */
110
static void nfs_async_unlink_release(void *calldata)
L
Linus Torvalds 已提交
111
{
112
	struct nfs_unlinkdata	*data = calldata;
T
Trond Myklebust 已提交
113 114

	nfs_dec_sillycount(data->dir);
115
	nfs_free_unlinkdata(data);
L
Linus Torvalds 已提交
116 117
}

118
static const struct rpc_call_ops nfs_unlink_ops = {
T
Trond Myklebust 已提交
119
	.rpc_call_prepare = nfs_async_unlink_init,
120 121 122 123
	.rpc_call_done = nfs_async_unlink_done,
	.rpc_release = nfs_async_unlink_release,
};

T
Trond Myklebust 已提交
124
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
125 126
{
	struct rpc_task *task;
T
Trond Myklebust 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	struct dentry *alias;

	alias = d_lookup(parent, &data->args.name);
	if (alias != NULL) {
		int ret = 0;
		/*
		 * 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);
		if (!(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
			alias->d_fsdata = data;
			alias->d_flags ^= DCACHE_NFSFS_RENAMED;
			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;
	}
	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;
}

static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
{
164 165
	struct dentry *parent;
	struct inode *dir;
T
Trond Myklebust 已提交
166
	int ret = 0;
167 168 169 170 171


	parent = dget_parent(dentry);
	if (parent == NULL)
		goto out_free;
T
Trond Myklebust 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	dir = parent->d_inode;
	if (nfs_copy_dname(dentry, data) == 0)
		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:
187
	dput(parent);
T
Trond Myklebust 已提交
188 189 190
out_free:
	return ret;
}
191

T
Trond Myklebust 已提交
192 193 194
void nfs_block_sillyrename(struct dentry *dentry)
{
	struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
195

T
Trond Myklebust 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	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);
218 219
}

L
Linus Torvalds 已提交
220 221
/**
 * nfs_async_unlink - asynchronous unlinking of a file
222
 * @dir: parent directory of dentry
L
Linus Torvalds 已提交
223 224 225
 * @dentry: dentry to unlink
 */
int
226
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
L
Linus Torvalds 已提交
227
{
228 229
	struct nfs_unlinkdata *data;
	int status = -ENOMEM;
L
Linus Torvalds 已提交
230

231
	data = kzalloc(sizeof(*data), GFP_KERNEL);
232
	if (data == NULL)
L
Linus Torvalds 已提交
233 234
		goto out;

235
	data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
L
Linus Torvalds 已提交
236 237 238 239 240
	if (IS_ERR(data->cred)) {
		status = PTR_ERR(data->cred);
		goto out_free;
	}

241
	status = -EBUSY;
L
Linus Torvalds 已提交
242
	spin_lock(&dentry->d_lock);
243 244
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out_unlock;
L
Linus Torvalds 已提交
245
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
246
	dentry->d_fsdata = data;
L
Linus Torvalds 已提交
247
	spin_unlock(&dentry->d_lock);
248 249 250 251
	return 0;
out_unlock:
	spin_unlock(&dentry->d_lock);
	put_rpccred(data->cred);
L
Linus Torvalds 已提交
252 253
out_free:
	kfree(data);
254
out:
L
Linus Torvalds 已提交
255 256 257 258 259 260
	return status;
}

/**
 * nfs_complete_unlink - Initialize completion of the sillydelete
 * @dentry: dentry to delete
261
 * @inode: inode
L
Linus Torvalds 已提交
262 263 264 265 266 267
 *
 * 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
268
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
L
Linus Torvalds 已提交
269
{
270
	struct nfs_unlinkdata	*data = NULL;
L
Linus Torvalds 已提交
271 272

	spin_lock(&dentry->d_lock);
273 274 275 276
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		data = dentry->d_fsdata;
	}
L
Linus Torvalds 已提交
277
	spin_unlock(&dentry->d_lock);
278 279 280

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