NFS: Fix a race in sillyrename
[linux-2.6.git] / fs / nfs / unlink.c
1 /*
2  *  linux/fs/nfs/unlink.c
3  *
4  * nfs sillydelete handling
5  *
6  */
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
16 struct nfs_unlinkdata {
17         struct hlist_node list;
18         struct nfs_removeargs args;
19         struct nfs_removeres res;
20         struct inode *dir;
21         struct rpc_cred *cred;
22 };
23
24 /**
25  * nfs_free_unlinkdata - release data from a sillydelete operation.
26  * @data: pointer to unlink structure.
27  */
28 static void
29 nfs_free_unlinkdata(struct nfs_unlinkdata *data)
30 {
31         iput(data->dir);
32         put_rpccred(data->cred);
33         kfree(data->args.name.name);
34         kfree(data);
35 }
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  */
43 static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
44 {
45         char            *str;
46         int             len = dentry->d_name.len;
47
48         str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL);
49         if (!str)
50                 return -ENOMEM;
51         data->args.name.len = len;
52         data->args.name.name = str;
53         return 0;
54 }
55
56 static 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
63 static 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
70 /**
71  * nfs_async_unlink_init - Initialize the RPC info
72  * task: rpc_task of the sillydelete
73  */
74 static void nfs_async_unlink_init(struct rpc_task *task, void *calldata)
75 {
76         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,
82         };
83
84         NFS_PROTO(dir)->unlink_setup(&msg, dir);
85         rpc_call_setup(task, &msg, 0);
86 }
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  */
94 static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
95 {
96         struct nfs_unlinkdata *data = calldata;
97         struct inode *dir = data->dir;
98
99         if (!NFS_PROTO(dir)->unlink_done(task, dir))
100                 rpc_restart_call(task);
101 }
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  */
110 static void nfs_async_unlink_release(void *calldata)
111 {
112         struct nfs_unlinkdata   *data = calldata;
113
114         nfs_dec_sillycount(data->dir);
115         nfs_free_unlinkdata(data);
116 }
117
118 static const struct rpc_call_ops nfs_unlink_ops = {
119         .rpc_call_prepare = nfs_async_unlink_init,
120         .rpc_call_done = nfs_async_unlink_done,
121         .rpc_release = nfs_async_unlink_release,
122 };
123
124 static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
125 {
126         struct rpc_task *task;
127         struct dentry *alias;
128
129         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         }
153         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;
160 }
161
162 static 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);
186 out_dput:
187         dput(parent);
188 out_free:
189         return ret;
190 }
191
192 void 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
199 void 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);
218 }
219
220 /**
221  * nfs_async_unlink - asynchronous unlinking of a file
222  * @dir: parent directory of dentry
223  * @dentry: dentry to unlink
224  */
225 int
226 nfs_async_unlink(struct inode *dir, struct dentry *dentry)
227 {
228         struct nfs_unlinkdata *data;
229         int status = -ENOMEM;
230
231         data = kzalloc(sizeof(*data), GFP_KERNEL);
232         if (data == NULL)
233                 goto out;
234
235         data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
236         if (IS_ERR(data->cred)) {
237                 status = PTR_ERR(data->cred);
238                 goto out_free;
239         }
240
241         status = -EBUSY;
242         spin_lock(&dentry->d_lock);
243         if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
244                 goto out_unlock;
245         dentry->d_flags |= DCACHE_NFSFS_RENAMED;
246         dentry->d_fsdata = data;
247         spin_unlock(&dentry->d_lock);
248         return 0;
249 out_unlock:
250         spin_unlock(&dentry->d_lock);
251         put_rpccred(data->cred);
252 out_free:
253         kfree(data);
254 out:
255         return status;
256 }
257
258 /**
259  * nfs_complete_unlink - Initialize completion of the sillydelete
260  * @dentry: dentry to delete
261  * @inode: inode
262  *
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  */
267 void
268 nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
269 {
270         struct nfs_unlinkdata   *data = NULL;
271
272         spin_lock(&dentry->d_lock);
273         if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
274                 dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
275                 data = dentry->d_fsdata;
276         }
277         spin_unlock(&dentry->d_lock);
278
279         if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
280                 nfs_free_unlinkdata(data);
281 }