]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/fs/nfs/unlink.c | |
3 | * | |
4 | * nfs sillydelete handling | |
5 | * | |
6 | * NOTE: we rely on holding the BKL for list manipulation protection. | |
7 | */ | |
8 | ||
9 | #include <linux/slab.h> | |
10 | #include <linux/string.h> | |
11 | #include <linux/dcache.h> | |
12 | #include <linux/sunrpc/sched.h> | |
13 | #include <linux/sunrpc/clnt.h> | |
14 | #include <linux/nfs_fs.h> | |
15 | ||
16 | ||
17 | struct nfs_unlinkdata { | |
18 | struct nfs_unlinkdata *next; | |
19 | struct dentry *dir, *dentry; | |
20 | struct qstr name; | |
21 | struct rpc_task task; | |
22 | struct rpc_cred *cred; | |
23 | unsigned int count; | |
24 | }; | |
25 | ||
26 | static struct nfs_unlinkdata *nfs_deletes; | |
27 | static RPC_WAITQ(nfs_delete_queue, "nfs_delete_queue"); | |
28 | ||
29 | /** | |
30 | * nfs_detach_unlinkdata - Remove asynchronous unlink from global list | |
31 | * @data: pointer to descriptor | |
32 | */ | |
33 | static inline void | |
34 | nfs_detach_unlinkdata(struct nfs_unlinkdata *data) | |
35 | { | |
36 | struct nfs_unlinkdata **q; | |
37 | ||
38 | for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { | |
39 | if (*q == data) { | |
40 | *q = data->next; | |
41 | break; | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | /** | |
47 | * nfs_put_unlinkdata - release data from a sillydelete operation. | |
48 | * @data: pointer to unlink structure. | |
49 | */ | |
50 | static void | |
51 | nfs_put_unlinkdata(struct nfs_unlinkdata *data) | |
52 | { | |
53 | if (--data->count == 0) { | |
54 | nfs_detach_unlinkdata(data); | |
f99d49ad | 55 | kfree(data->name.name); |
1da177e4 LT |
56 | kfree(data); |
57 | } | |
58 | } | |
59 | ||
60 | #define NAME_ALLOC_LEN(len) ((len+16) & ~15) | |
61 | /** | |
62 | * nfs_copy_dname - copy dentry name to data structure | |
63 | * @dentry: pointer to dentry | |
64 | * @data: nfs_unlinkdata | |
65 | */ | |
66 | static inline void | |
67 | nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) | |
68 | { | |
69 | char *str; | |
70 | int len = dentry->d_name.len; | |
71 | ||
72 | str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); | |
73 | if (!str) | |
74 | return; | |
75 | memcpy(str, dentry->d_name.name, len); | |
76 | if (!data->name.len) { | |
77 | data->name.len = len; | |
78 | data->name.name = str; | |
79 | } else | |
80 | kfree(str); | |
81 | } | |
82 | ||
83 | /** | |
84 | * nfs_async_unlink_init - Initialize the RPC info | |
85 | * @task: rpc_task of the sillydelete | |
86 | * | |
87 | * We delay initializing RPC info until after the call to dentry_iput() | |
88 | * in order to minimize races against rename(). | |
89 | */ | |
4ce70ada | 90 | static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) |
1da177e4 | 91 | { |
4ce70ada | 92 | struct nfs_unlinkdata *data = calldata; |
1da177e4 LT |
93 | struct dentry *dir = data->dir; |
94 | struct rpc_message msg = { | |
95 | .rpc_cred = data->cred, | |
96 | }; | |
97 | int status = -ENOENT; | |
98 | ||
99 | if (!data->name.len) | |
100 | goto out_err; | |
101 | ||
102 | status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); | |
103 | if (status < 0) | |
104 | goto out_err; | |
105 | nfs_begin_data_update(dir->d_inode); | |
106 | rpc_call_setup(task, &msg, 0); | |
107 | return; | |
108 | out_err: | |
109 | rpc_exit(task, status); | |
110 | } | |
111 | ||
112 | /** | |
113 | * nfs_async_unlink_done - Sillydelete post-processing | |
114 | * @task: rpc_task of the sillydelete | |
115 | * | |
116 | * Do the directory attribute update. | |
117 | */ | |
963d8fe5 | 118 | static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) |
1da177e4 | 119 | { |
963d8fe5 | 120 | struct nfs_unlinkdata *data = calldata; |
1da177e4 LT |
121 | struct dentry *dir = data->dir; |
122 | struct inode *dir_i; | |
123 | ||
124 | if (!dir) | |
125 | return; | |
126 | dir_i = dir->d_inode; | |
127 | nfs_end_data_update(dir_i); | |
128 | if (NFS_PROTO(dir_i)->unlink_done(dir, task)) | |
129 | return; | |
130 | put_rpccred(data->cred); | |
131 | data->cred = NULL; | |
132 | dput(dir); | |
133 | } | |
134 | ||
135 | /** | |
136 | * nfs_async_unlink_release - Release the sillydelete data. | |
137 | * @task: rpc_task of the sillydelete | |
138 | * | |
139 | * We need to call nfs_put_unlinkdata as a 'tk_release' task since the | |
140 | * rpc_task would be freed too. | |
141 | */ | |
963d8fe5 | 142 | static void nfs_async_unlink_release(void *calldata) |
1da177e4 | 143 | { |
963d8fe5 | 144 | struct nfs_unlinkdata *data = calldata; |
1da177e4 LT |
145 | nfs_put_unlinkdata(data); |
146 | } | |
147 | ||
963d8fe5 | 148 | static const struct rpc_call_ops nfs_unlink_ops = { |
4ce70ada | 149 | .rpc_call_prepare = nfs_async_unlink_init, |
963d8fe5 TM |
150 | .rpc_call_done = nfs_async_unlink_done, |
151 | .rpc_release = nfs_async_unlink_release, | |
152 | }; | |
153 | ||
1da177e4 LT |
154 | /** |
155 | * nfs_async_unlink - asynchronous unlinking of a file | |
156 | * @dentry: dentry to unlink | |
157 | */ | |
158 | int | |
159 | nfs_async_unlink(struct dentry *dentry) | |
160 | { | |
161 | struct dentry *dir = dentry->d_parent; | |
162 | struct nfs_unlinkdata *data; | |
1da177e4 LT |
163 | struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); |
164 | int status = -ENOMEM; | |
165 | ||
bd647545 | 166 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
1da177e4 LT |
167 | if (!data) |
168 | goto out; | |
1da177e4 LT |
169 | |
170 | data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); | |
171 | if (IS_ERR(data->cred)) { | |
172 | status = PTR_ERR(data->cred); | |
173 | goto out_free; | |
174 | } | |
175 | data->dir = dget(dir); | |
176 | data->dentry = dentry; | |
177 | ||
178 | data->next = nfs_deletes; | |
179 | nfs_deletes = data; | |
180 | data->count = 1; | |
181 | ||
4ce70ada | 182 | rpc_init_task(&data->task, clnt, RPC_TASK_ASYNC, &nfs_unlink_ops, data); |
1da177e4 LT |
183 | |
184 | spin_lock(&dentry->d_lock); | |
185 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | |
186 | spin_unlock(&dentry->d_lock); | |
187 | ||
4ce70ada | 188 | rpc_sleep_on(&nfs_delete_queue, &data->task, NULL, NULL); |
1da177e4 LT |
189 | status = 0; |
190 | out: | |
191 | return status; | |
192 | out_free: | |
193 | kfree(data); | |
194 | return status; | |
195 | } | |
196 | ||
197 | /** | |
198 | * nfs_complete_unlink - Initialize completion of the sillydelete | |
199 | * @dentry: dentry to delete | |
200 | * | |
201 | * Since we're most likely to be called by dentry_iput(), we | |
202 | * only use the dentry to find the sillydelete. We then copy the name | |
203 | * into the qstr. | |
204 | */ | |
205 | void | |
206 | nfs_complete_unlink(struct dentry *dentry) | |
207 | { | |
208 | struct nfs_unlinkdata *data; | |
209 | ||
210 | for(data = nfs_deletes; data != NULL; data = data->next) { | |
211 | if (dentry == data->dentry) | |
212 | break; | |
213 | } | |
214 | if (!data) | |
215 | return; | |
216 | data->count++; | |
217 | nfs_copy_dname(dentry, data); | |
218 | spin_lock(&dentry->d_lock); | |
219 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | |
220 | spin_unlock(&dentry->d_lock); | |
221 | rpc_wake_up_task(&data->task); | |
222 | nfs_put_unlinkdata(data); | |
223 | } |