]>
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); | |
55 | if (data->name.name != NULL) | |
56 | kfree(data->name.name); | |
57 | kfree(data); | |
58 | } | |
59 | } | |
60 | ||
61 | #define NAME_ALLOC_LEN(len) ((len+16) & ~15) | |
62 | /** | |
63 | * nfs_copy_dname - copy dentry name to data structure | |
64 | * @dentry: pointer to dentry | |
65 | * @data: nfs_unlinkdata | |
66 | */ | |
67 | static inline void | |
68 | nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) | |
69 | { | |
70 | char *str; | |
71 | int len = dentry->d_name.len; | |
72 | ||
73 | str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); | |
74 | if (!str) | |
75 | return; | |
76 | memcpy(str, dentry->d_name.name, len); | |
77 | if (!data->name.len) { | |
78 | data->name.len = len; | |
79 | data->name.name = str; | |
80 | } else | |
81 | kfree(str); | |
82 | } | |
83 | ||
84 | /** | |
85 | * nfs_async_unlink_init - Initialize the RPC info | |
86 | * @task: rpc_task of the sillydelete | |
87 | * | |
88 | * We delay initializing RPC info until after the call to dentry_iput() | |
89 | * in order to minimize races against rename(). | |
90 | */ | |
91 | static void | |
92 | nfs_async_unlink_init(struct rpc_task *task) | |
93 | { | |
94 | struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; | |
95 | struct dentry *dir = data->dir; | |
96 | struct rpc_message msg = { | |
97 | .rpc_cred = data->cred, | |
98 | }; | |
99 | int status = -ENOENT; | |
100 | ||
101 | if (!data->name.len) | |
102 | goto out_err; | |
103 | ||
104 | status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); | |
105 | if (status < 0) | |
106 | goto out_err; | |
107 | nfs_begin_data_update(dir->d_inode); | |
108 | rpc_call_setup(task, &msg, 0); | |
109 | return; | |
110 | out_err: | |
111 | rpc_exit(task, status); | |
112 | } | |
113 | ||
114 | /** | |
115 | * nfs_async_unlink_done - Sillydelete post-processing | |
116 | * @task: rpc_task of the sillydelete | |
117 | * | |
118 | * Do the directory attribute update. | |
119 | */ | |
120 | static void | |
121 | nfs_async_unlink_done(struct rpc_task *task) | |
122 | { | |
123 | struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; | |
124 | struct dentry *dir = data->dir; | |
125 | struct inode *dir_i; | |
126 | ||
127 | if (!dir) | |
128 | return; | |
129 | dir_i = dir->d_inode; | |
130 | nfs_end_data_update(dir_i); | |
131 | if (NFS_PROTO(dir_i)->unlink_done(dir, task)) | |
132 | return; | |
133 | put_rpccred(data->cred); | |
134 | data->cred = NULL; | |
135 | dput(dir); | |
136 | } | |
137 | ||
138 | /** | |
139 | * nfs_async_unlink_release - Release the sillydelete data. | |
140 | * @task: rpc_task of the sillydelete | |
141 | * | |
142 | * We need to call nfs_put_unlinkdata as a 'tk_release' task since the | |
143 | * rpc_task would be freed too. | |
144 | */ | |
145 | static void | |
146 | nfs_async_unlink_release(struct rpc_task *task) | |
147 | { | |
148 | struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; | |
149 | nfs_put_unlinkdata(data); | |
150 | } | |
151 | ||
152 | /** | |
153 | * nfs_async_unlink - asynchronous unlinking of a file | |
154 | * @dentry: dentry to unlink | |
155 | */ | |
156 | int | |
157 | nfs_async_unlink(struct dentry *dentry) | |
158 | { | |
159 | struct dentry *dir = dentry->d_parent; | |
160 | struct nfs_unlinkdata *data; | |
161 | struct rpc_task *task; | |
162 | struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); | |
163 | int status = -ENOMEM; | |
164 | ||
165 | data = kmalloc(sizeof(*data), GFP_KERNEL); | |
166 | if (!data) | |
167 | goto out; | |
168 | memset(data, 0, sizeof(*data)); | |
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 | ||
182 | task = &data->task; | |
183 | rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC); | |
184 | task->tk_calldata = data; | |
185 | task->tk_action = nfs_async_unlink_init; | |
186 | task->tk_release = nfs_async_unlink_release; | |
187 | ||
188 | spin_lock(&dentry->d_lock); | |
189 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | |
190 | spin_unlock(&dentry->d_lock); | |
191 | ||
192 | rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); | |
193 | status = 0; | |
194 | out: | |
195 | return status; | |
196 | out_free: | |
197 | kfree(data); | |
198 | return status; | |
199 | } | |
200 | ||
201 | /** | |
202 | * nfs_complete_unlink - Initialize completion of the sillydelete | |
203 | * @dentry: dentry to delete | |
204 | * | |
205 | * Since we're most likely to be called by dentry_iput(), we | |
206 | * only use the dentry to find the sillydelete. We then copy the name | |
207 | * into the qstr. | |
208 | */ | |
209 | void | |
210 | nfs_complete_unlink(struct dentry *dentry) | |
211 | { | |
212 | struct nfs_unlinkdata *data; | |
213 | ||
214 | for(data = nfs_deletes; data != NULL; data = data->next) { | |
215 | if (dentry == data->dentry) | |
216 | break; | |
217 | } | |
218 | if (!data) | |
219 | return; | |
220 | data->count++; | |
221 | nfs_copy_dname(dentry, data); | |
222 | spin_lock(&dentry->d_lock); | |
223 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | |
224 | spin_unlock(&dentry->d_lock); | |
225 | rpc_wake_up_task(&data->task); | |
226 | nfs_put_unlinkdata(data); | |
227 | } |