diff options
Diffstat (limited to 'fs/nfs/unlink.c')
-rw-r--r-- | fs/nfs/unlink.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c new file mode 100644 index 000000000000..f732541a3332 --- /dev/null +++ b/fs/nfs/unlink.c | |||
@@ -0,0 +1,227 @@ | |||
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 | } | ||