unlink.c 4.4 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 {
17 18 19
	struct nfs_removeargs args;
	struct nfs_removeres res;
	struct inode *dir;
L
Linus Torvalds 已提交
20 21 22 23
	struct rpc_cred	*cred;
};

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

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

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

/**
 * nfs_async_unlink_init - Initialize the RPC info
57
 * task: rpc_task of the sillydelete
L
Linus Torvalds 已提交
58
 */
T
Trond Myklebust 已提交
59
static void nfs_async_unlink_init(struct rpc_task *task, void *calldata)
L
Linus Torvalds 已提交
60
{
61 62 63 64 65 66
	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 已提交
67 68
	};

69 70
	nfs_begin_data_update(dir);
	NFS_PROTO(dir)->unlink_setup(&msg, dir);
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78 79
	rpc_call_setup(task, &msg, 0);
}

/**
 * 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 84 85 86 87 88
	struct nfs_unlinkdata *data = calldata;
	struct inode *dir = data->dir;

	if (!NFS_PROTO(dir)->unlink_done(task, dir))
		rpc_restart_call(task);
	else
		nfs_end_data_update(dir);
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
	nfs_free_unlinkdata(data);
L
Linus Torvalds 已提交
102 103
}

104
static const struct rpc_call_ops nfs_unlink_ops = {
T
Trond Myklebust 已提交
105
	.rpc_call_prepare = nfs_async_unlink_init,
106 107 108 109
	.rpc_call_done = nfs_async_unlink_done,
	.rpc_release = nfs_async_unlink_release,
};

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
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;
}

L
Linus Torvalds 已提交
139 140
/**
 * nfs_async_unlink - asynchronous unlinking of a file
141
 * @dir: parent directory of dentry
L
Linus Torvalds 已提交
142 143 144
 * @dentry: dentry to unlink
 */
int
145
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
L
Linus Torvalds 已提交
146
{
147 148
	struct nfs_unlinkdata *data;
	int status = -ENOMEM;
L
Linus Torvalds 已提交
149

150
	data = kzalloc(sizeof(*data), GFP_KERNEL);
151
	if (data == NULL)
L
Linus Torvalds 已提交
152 153
		goto out;

154
	data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
L
Linus Torvalds 已提交
155 156 157 158 159
	if (IS_ERR(data->cred)) {
		status = PTR_ERR(data->cred);
		goto out_free;
	}

160
	status = -EBUSY;
L
Linus Torvalds 已提交
161
	spin_lock(&dentry->d_lock);
162 163
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out_unlock;
L
Linus Torvalds 已提交
164
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
165
	dentry->d_fsdata = data;
L
Linus Torvalds 已提交
166
	spin_unlock(&dentry->d_lock);
167 168 169 170
	return 0;
out_unlock:
	spin_unlock(&dentry->d_lock);
	put_rpccred(data->cred);
L
Linus Torvalds 已提交
171 172
out_free:
	kfree(data);
173
out:
L
Linus Torvalds 已提交
174 175 176 177 178 179
	return status;
}

/**
 * nfs_complete_unlink - Initialize completion of the sillydelete
 * @dentry: dentry to delete
180
 * @inode: inode
L
Linus Torvalds 已提交
181 182 183 184 185 186
 *
 * 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
187
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
L
Linus Torvalds 已提交
188
{
189
	struct nfs_unlinkdata	*data = NULL;
L
Linus Torvalds 已提交
190 191

	spin_lock(&dentry->d_lock);
192 193 194 195
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		data = dentry->d_fsdata;
	}
L
Linus Torvalds 已提交
196
	spin_unlock(&dentry->d_lock);
197 198 199

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