blob: 6ecd46c967c8bc3073cc3389ec65d32524d23e9d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/unlink.c
3 *
4 * nfs sillydelete handling
5 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
7
8#include <linux/slab.h>
9#include <linux/string.h>
10#include <linux/dcache.h>
11#include <linux/sunrpc/sched.h>
12#include <linux/sunrpc/clnt.h>
13#include <linux/nfs_fs.h>
14
15
16struct nfs_unlinkdata {
Trond Myklebust565277f2007-10-15 18:17:53 -040017 struct hlist_node list;
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040018 struct nfs_removeargs args;
19 struct nfs_removeres res;
20 struct inode *dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 struct rpc_cred *cred;
Linus Torvalds1da177e2005-04-16 15:20:36 -070022};
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024/**
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040025 * nfs_free_unlinkdata - release data from a sillydelete operation.
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 * @data: pointer to unlink structure.
27 */
28static void
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040029nfs_free_unlinkdata(struct nfs_unlinkdata *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070030{
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040031 iput(data->dir);
32 put_rpccred(data->cred);
33 kfree(data->args.name.name);
34 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -070035}
36
37#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
38/**
39 * nfs_copy_dname - copy dentry name to data structure
40 * @dentry: pointer to dentry
41 * @data: nfs_unlinkdata
42 */
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040043static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
45 char *str;
46 int len = dentry->d_name.len;
47
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040048 str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 if (!str)
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040050 return -ENOMEM;
51 data->args.name.len = len;
52 data->args.name.name = str;
53 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054}
55
Trond Myklebust565277f2007-10-15 18:17:53 -040056static void nfs_free_dname(struct nfs_unlinkdata *data)
57{
58 kfree(data->args.name.name);
59 data->args.name.name = NULL;
60 data->args.name.len = 0;
61}
62
63static void nfs_dec_sillycount(struct inode *dir)
64{
65 struct nfs_inode *nfsi = NFS_I(dir);
66 if (atomic_dec_return(&nfsi->silly_count) == 1)
67 wake_up(&nfsi->waitqueue);
68}
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070/**
71 * nfs_async_unlink_init - Initialize the RPC info
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040072 * task: rpc_task of the sillydelete
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 */
Trond Myklebust4ce70ad2006-01-03 09:55:05 +010074static void nfs_async_unlink_init(struct rpc_task *task, void *calldata)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040076 struct nfs_unlinkdata *data = calldata;
77 struct inode *dir = data->dir;
78 struct rpc_message msg = {
79 .rpc_argp = &data->args,
80 .rpc_resp = &data->res,
81 .rpc_cred = data->cred,
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040084 NFS_PROTO(dir)->unlink_setup(&msg, dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 rpc_call_setup(task, &msg, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086}
87
88/**
89 * nfs_async_unlink_done - Sillydelete post-processing
90 * @task: rpc_task of the sillydelete
91 *
92 * Do the directory attribute update.
93 */
Trond Myklebust963d8fe2006-01-03 09:55:04 +010094static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040096 struct nfs_unlinkdata *data = calldata;
97 struct inode *dir = data->dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
Trond Myklebuste4eff1a2007-07-14 15:39:58 -040099 if (!NFS_PROTO(dir)->unlink_done(task, dir))
100 rpc_restart_call(task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
103/**
104 * nfs_async_unlink_release - Release the sillydelete data.
105 * @task: rpc_task of the sillydelete
106 *
107 * We need to call nfs_put_unlinkdata as a 'tk_release' task since the
108 * rpc_task would be freed too.
109 */
Trond Myklebust963d8fe2006-01-03 09:55:04 +0100110static void nfs_async_unlink_release(void *calldata)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Trond Myklebust963d8fe2006-01-03 09:55:04 +0100112 struct nfs_unlinkdata *data = calldata;
Trond Myklebust565277f2007-10-15 18:17:53 -0400113
114 nfs_dec_sillycount(data->dir);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400115 nfs_free_unlinkdata(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
Trond Myklebust963d8fe2006-01-03 09:55:04 +0100118static const struct rpc_call_ops nfs_unlink_ops = {
Trond Myklebust4ce70ad2006-01-03 09:55:05 +0100119 .rpc_call_prepare = nfs_async_unlink_init,
Trond Myklebust963d8fe2006-01-03 09:55:04 +0100120 .rpc_call_done = nfs_async_unlink_done,
121 .rpc_release = nfs_async_unlink_release,
122};
123
Trond Myklebust565277f2007-10-15 18:17:53 -0400124static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400125{
126 struct rpc_task *task;
Trond Myklebust565277f2007-10-15 18:17:53 -0400127 struct dentry *alias;
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400128
Trond Myklebust565277f2007-10-15 18:17:53 -0400129 alias = d_lookup(parent, &data->args.name);
130 if (alias != NULL) {
131 int ret = 0;
132 /*
133 * Hey, we raced with lookup... See if we need to transfer
134 * the sillyrename information to the aliased dentry.
135 */
136 nfs_free_dname(data);
137 spin_lock(&alias->d_lock);
138 if (!(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
139 alias->d_fsdata = data;
140 alias->d_flags ^= DCACHE_NFSFS_RENAMED;
141 ret = 1;
142 }
143 spin_unlock(&alias->d_lock);
144 nfs_dec_sillycount(dir);
145 dput(alias);
146 return ret;
147 }
148 data->dir = igrab(dir);
149 if (!data->dir) {
150 nfs_dec_sillycount(dir);
151 return 0;
152 }
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400153 data->args.fh = NFS_FH(dir);
154 nfs_fattr_init(&data->res.dir_attr);
155
156 task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data);
157 if (!IS_ERR(task))
158 rpc_put_task(task);
159 return 1;
Trond Myklebust565277f2007-10-15 18:17:53 -0400160}
161
162static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
163{
164 struct dentry *parent;
165 struct inode *dir;
166 int ret = 0;
167
168
169 parent = dget_parent(dentry);
170 if (parent == NULL)
171 goto out_free;
172 dir = parent->d_inode;
173 if (nfs_copy_dname(dentry, data) == 0)
174 goto out_dput;
175 /* Non-exclusive lock protects against concurrent lookup() calls */
176 spin_lock(&dir->i_lock);
177 if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
178 /* Deferred delete */
179 hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
180 spin_unlock(&dir->i_lock);
181 ret = 1;
182 goto out_dput;
183 }
184 spin_unlock(&dir->i_lock);
185 ret = nfs_do_call_unlink(parent, dir, data);
186out_dput:
187 dput(parent);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400188out_free:
Trond Myklebust565277f2007-10-15 18:17:53 -0400189 return ret;
190}
191
192void nfs_block_sillyrename(struct dentry *dentry)
193{
194 struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
195
196 wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1);
197}
198
199void nfs_unblock_sillyrename(struct dentry *dentry)
200{
201 struct inode *dir = dentry->d_inode;
202 struct nfs_inode *nfsi = NFS_I(dir);
203 struct nfs_unlinkdata *data;
204
205 atomic_inc(&nfsi->silly_count);
206 spin_lock(&dir->i_lock);
207 while (!hlist_empty(&nfsi->silly_list)) {
208 if (!atomic_inc_not_zero(&nfsi->silly_count))
209 break;
210 data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list);
211 hlist_del(&data->list);
212 spin_unlock(&dir->i_lock);
213 if (nfs_do_call_unlink(dentry, dir, data) == 0)
214 nfs_free_unlinkdata(data);
215 spin_lock(&dir->i_lock);
216 }
217 spin_unlock(&dir->i_lock);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400218}
219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220/**
221 * nfs_async_unlink - asynchronous unlinking of a file
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400222 * @dir: parent directory of dentry
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 * @dentry: dentry to unlink
224 */
225int
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400226nfs_async_unlink(struct inode *dir, struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400228 struct nfs_unlinkdata *data;
229 int status = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
Eric Sesterhennbd647542006-03-20 13:44:10 -0500231 data = kzalloc(sizeof(*data), GFP_KERNEL);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400232 if (data == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400235 data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 if (IS_ERR(data->cred)) {
237 status = PTR_ERR(data->cred);
238 goto out_free;
239 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400241 status = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 spin_lock(&dentry->d_lock);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400243 if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
244 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 dentry->d_flags |= DCACHE_NFSFS_RENAMED;
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400246 dentry->d_fsdata = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 spin_unlock(&dentry->d_lock);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400248 return 0;
249out_unlock:
250 spin_unlock(&dentry->d_lock);
251 put_rpccred(data->cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252out_free:
253 kfree(data);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400254out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 return status;
256}
257
258/**
259 * nfs_complete_unlink - Initialize completion of the sillydelete
260 * @dentry: dentry to delete
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400261 * @inode: inode
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 *
263 * Since we're most likely to be called by dentry_iput(), we
264 * only use the dentry to find the sillydelete. We then copy the name
265 * into the qstr.
266 */
267void
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400268nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400270 struct nfs_unlinkdata *data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 spin_lock(&dentry->d_lock);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400273 if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
274 dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
275 data = dentry->d_fsdata;
276 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 spin_unlock(&dentry->d_lock);
Trond Myklebuste4eff1a2007-07-14 15:39:58 -0400278
279 if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
280 nfs_free_unlinkdata(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}