diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/pohmelfs/crypto.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/staging/pohmelfs/crypto.c')
-rw-r--r-- | drivers/staging/pohmelfs/crypto.c | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/drivers/staging/pohmelfs/crypto.c b/drivers/staging/pohmelfs/crypto.c new file mode 100644 index 00000000000..ad92771dce5 --- /dev/null +++ b/drivers/staging/pohmelfs/crypto.c | |||
@@ -0,0 +1,878 @@ | |||
1 | /* | ||
2 | * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/crypto.h> | ||
17 | #include <linux/highmem.h> | ||
18 | #include <linux/kthread.h> | ||
19 | #include <linux/pagemap.h> | ||
20 | #include <linux/scatterlist.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include "netfs.h" | ||
24 | |||
25 | static struct crypto_hash *pohmelfs_init_hash(struct pohmelfs_sb *psb) | ||
26 | { | ||
27 | int err; | ||
28 | struct crypto_hash *hash; | ||
29 | |||
30 | hash = crypto_alloc_hash(psb->hash_string, 0, CRYPTO_ALG_ASYNC); | ||
31 | if (IS_ERR(hash)) { | ||
32 | err = PTR_ERR(hash); | ||
33 | dprintk("%s: idx: %u: failed to allocate hash '%s', err: %d.\n", | ||
34 | __func__, psb->idx, psb->hash_string, err); | ||
35 | goto err_out_exit; | ||
36 | } | ||
37 | |||
38 | psb->crypto_attached_size = crypto_hash_digestsize(hash); | ||
39 | |||
40 | if (!psb->hash_keysize) | ||
41 | return hash; | ||
42 | |||
43 | err = crypto_hash_setkey(hash, psb->hash_key, psb->hash_keysize); | ||
44 | if (err) { | ||
45 | dprintk("%s: idx: %u: failed to set key for hash '%s', err: %d.\n", | ||
46 | __func__, psb->idx, psb->hash_string, err); | ||
47 | goto err_out_free; | ||
48 | } | ||
49 | |||
50 | return hash; | ||
51 | |||
52 | err_out_free: | ||
53 | crypto_free_hash(hash); | ||
54 | err_out_exit: | ||
55 | return ERR_PTR(err); | ||
56 | } | ||
57 | |||
58 | static struct crypto_ablkcipher *pohmelfs_init_cipher(struct pohmelfs_sb *psb) | ||
59 | { | ||
60 | int err = -EINVAL; | ||
61 | struct crypto_ablkcipher *cipher; | ||
62 | |||
63 | if (!psb->cipher_keysize) | ||
64 | goto err_out_exit; | ||
65 | |||
66 | cipher = crypto_alloc_ablkcipher(psb->cipher_string, 0, 0); | ||
67 | if (IS_ERR(cipher)) { | ||
68 | err = PTR_ERR(cipher); | ||
69 | dprintk("%s: idx: %u: failed to allocate cipher '%s', err: %d.\n", | ||
70 | __func__, psb->idx, psb->cipher_string, err); | ||
71 | goto err_out_exit; | ||
72 | } | ||
73 | |||
74 | crypto_ablkcipher_clear_flags(cipher, ~0); | ||
75 | |||
76 | err = crypto_ablkcipher_setkey(cipher, psb->cipher_key, psb->cipher_keysize); | ||
77 | if (err) { | ||
78 | dprintk("%s: idx: %u: failed to set key for cipher '%s', err: %d.\n", | ||
79 | __func__, psb->idx, psb->cipher_string, err); | ||
80 | goto err_out_free; | ||
81 | } | ||
82 | |||
83 | return cipher; | ||
84 | |||
85 | err_out_free: | ||
86 | crypto_free_ablkcipher(cipher); | ||
87 | err_out_exit: | ||
88 | return ERR_PTR(err); | ||
89 | } | ||
90 | |||
91 | int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) | ||
92 | { | ||
93 | int err; | ||
94 | |||
95 | e->page_num = 0; | ||
96 | |||
97 | e->size = PAGE_SIZE; | ||
98 | e->data = kmalloc(e->size, GFP_KERNEL); | ||
99 | if (!e->data) { | ||
100 | err = -ENOMEM; | ||
101 | goto err_out_exit; | ||
102 | } | ||
103 | |||
104 | if (psb->hash_string) { | ||
105 | e->hash = pohmelfs_init_hash(psb); | ||
106 | if (IS_ERR(e->hash)) { | ||
107 | err = PTR_ERR(e->hash); | ||
108 | e->hash = NULL; | ||
109 | goto err_out_free; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if (psb->cipher_string) { | ||
114 | e->cipher = pohmelfs_init_cipher(psb); | ||
115 | if (IS_ERR(e->cipher)) { | ||
116 | err = PTR_ERR(e->cipher); | ||
117 | e->cipher = NULL; | ||
118 | goto err_out_free_hash; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | return 0; | ||
123 | |||
124 | err_out_free_hash: | ||
125 | crypto_free_hash(e->hash); | ||
126 | err_out_free: | ||
127 | kfree(e->data); | ||
128 | err_out_exit: | ||
129 | return err; | ||
130 | } | ||
131 | |||
132 | void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e) | ||
133 | { | ||
134 | crypto_free_hash(e->hash); | ||
135 | crypto_free_ablkcipher(e->cipher); | ||
136 | kfree(e->data); | ||
137 | } | ||
138 | |||
139 | static void pohmelfs_crypto_complete(struct crypto_async_request *req, int err) | ||
140 | { | ||
141 | struct pohmelfs_crypto_completion *c = req->data; | ||
142 | |||
143 | if (err == -EINPROGRESS) | ||
144 | return; | ||
145 | |||
146 | dprintk("%s: req: %p, err: %d.\n", __func__, req, err); | ||
147 | c->error = err; | ||
148 | complete(&c->complete); | ||
149 | } | ||
150 | |||
151 | static int pohmelfs_crypto_process(struct ablkcipher_request *req, | ||
152 | struct scatterlist *sg_dst, struct scatterlist *sg_src, | ||
153 | void *iv, int enc, unsigned long timeout) | ||
154 | { | ||
155 | struct pohmelfs_crypto_completion complete; | ||
156 | int err; | ||
157 | |||
158 | init_completion(&complete.complete); | ||
159 | complete.error = -EINPROGRESS; | ||
160 | |||
161 | ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, | ||
162 | pohmelfs_crypto_complete, &complete); | ||
163 | |||
164 | ablkcipher_request_set_crypt(req, sg_src, sg_dst, sg_src->length, iv); | ||
165 | |||
166 | if (enc) | ||
167 | err = crypto_ablkcipher_encrypt(req); | ||
168 | else | ||
169 | err = crypto_ablkcipher_decrypt(req); | ||
170 | |||
171 | switch (err) { | ||
172 | case -EINPROGRESS: | ||
173 | case -EBUSY: | ||
174 | err = wait_for_completion_interruptible_timeout(&complete.complete, | ||
175 | timeout); | ||
176 | if (!err) | ||
177 | err = -ETIMEDOUT; | ||
178 | else if (err > 0) | ||
179 | err = complete.error; | ||
180 | break; | ||
181 | default: | ||
182 | break; | ||
183 | } | ||
184 | |||
185 | return err; | ||
186 | } | ||
187 | |||
188 | int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 cmd_iv, | ||
189 | void *data, struct page *page, unsigned int size) | ||
190 | { | ||
191 | int err; | ||
192 | struct scatterlist sg; | ||
193 | |||
194 | if (!e->cipher && !e->hash) | ||
195 | return 0; | ||
196 | |||
197 | dprintk("%s: eng: %p, iv: %llx, data: %p, page: %p/%lu, size: %u.\n", | ||
198 | __func__, e, cmd_iv, data, page, (page) ? page->index : 0, size); | ||
199 | |||
200 | if (data) { | ||
201 | sg_init_one(&sg, data, size); | ||
202 | } else { | ||
203 | sg_init_table(&sg, 1); | ||
204 | sg_set_page(&sg, page, size, 0); | ||
205 | } | ||
206 | |||
207 | if (e->cipher) { | ||
208 | struct ablkcipher_request *req = e->data + crypto_hash_digestsize(e->hash); | ||
209 | u8 iv[32]; | ||
210 | |||
211 | memset(iv, 0, sizeof(iv)); | ||
212 | memcpy(iv, &cmd_iv, sizeof(cmd_iv)); | ||
213 | |||
214 | ablkcipher_request_set_tfm(req, e->cipher); | ||
215 | |||
216 | err = pohmelfs_crypto_process(req, &sg, &sg, iv, 0, e->timeout); | ||
217 | if (err) | ||
218 | goto err_out_exit; | ||
219 | } | ||
220 | |||
221 | if (e->hash) { | ||
222 | struct hash_desc desc; | ||
223 | void *dst = e->data + e->size/2; | ||
224 | |||
225 | desc.tfm = e->hash; | ||
226 | desc.flags = 0; | ||
227 | |||
228 | err = crypto_hash_init(&desc); | ||
229 | if (err) | ||
230 | goto err_out_exit; | ||
231 | |||
232 | err = crypto_hash_update(&desc, &sg, size); | ||
233 | if (err) | ||
234 | goto err_out_exit; | ||
235 | |||
236 | err = crypto_hash_final(&desc, dst); | ||
237 | if (err) | ||
238 | goto err_out_exit; | ||
239 | |||
240 | err = !!memcmp(dst, e->data, crypto_hash_digestsize(e->hash)); | ||
241 | |||
242 | if (err) { | ||
243 | #ifdef CONFIG_POHMELFS_DEBUG | ||
244 | unsigned int i; | ||
245 | unsigned char *recv = e->data, *calc = dst; | ||
246 | |||
247 | dprintk("%s: eng: %p, hash: %p, cipher: %p: iv : %llx, hash mismatch (recv/calc): ", | ||
248 | __func__, e, e->hash, e->cipher, cmd_iv); | ||
249 | for (i = 0; i < crypto_hash_digestsize(e->hash); ++i) { | ||
250 | #if 0 | ||
251 | dprintka("%02x ", recv[i]); | ||
252 | if (recv[i] != calc[i]) { | ||
253 | dprintka("| calc byte: %02x.\n", calc[i]); | ||
254 | break; | ||
255 | } | ||
256 | #else | ||
257 | dprintka("%02x/%02x ", recv[i], calc[i]); | ||
258 | #endif | ||
259 | } | ||
260 | dprintk("\n"); | ||
261 | #endif | ||
262 | goto err_out_exit; | ||
263 | } else { | ||
264 | dprintk("%s: eng: %p, hash: %p, cipher: %p: hashes matched.\n", | ||
265 | __func__, e, e->hash, e->cipher); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | dprintk("%s: eng: %p, size: %u, hash: %p, cipher: %p: completed.\n", | ||
270 | __func__, e, e->size, e->hash, e->cipher); | ||
271 | |||
272 | return 0; | ||
273 | |||
274 | err_out_exit: | ||
275 | dprintk("%s: eng: %p, hash: %p, cipher: %p: err: %d.\n", | ||
276 | __func__, e, e->hash, e->cipher, err); | ||
277 | return err; | ||
278 | } | ||
279 | |||
280 | static int pohmelfs_trans_iter(struct netfs_trans *t, struct pohmelfs_crypto_engine *e, | ||
281 | int (*iterator) (struct pohmelfs_crypto_engine *e, | ||
282 | struct scatterlist *dst, | ||
283 | struct scatterlist *src)) | ||
284 | { | ||
285 | void *data = t->iovec.iov_base + sizeof(struct netfs_cmd) + t->psb->crypto_attached_size; | ||
286 | unsigned int size = t->iovec.iov_len - sizeof(struct netfs_cmd) - t->psb->crypto_attached_size; | ||
287 | struct netfs_cmd *cmd = data; | ||
288 | unsigned int sz, pages = t->attached_pages, i, csize, cmd_cmd, dpage_idx; | ||
289 | struct scatterlist sg_src, sg_dst; | ||
290 | int err; | ||
291 | |||
292 | while (size) { | ||
293 | cmd = data; | ||
294 | cmd_cmd = __be16_to_cpu(cmd->cmd); | ||
295 | csize = __be32_to_cpu(cmd->size); | ||
296 | cmd->iv = __cpu_to_be64(e->iv); | ||
297 | |||
298 | if (cmd_cmd == NETFS_READ_PAGES || cmd_cmd == NETFS_READ_PAGE) | ||
299 | csize = __be16_to_cpu(cmd->ext); | ||
300 | |||
301 | sz = csize + __be16_to_cpu(cmd->cpad) + sizeof(struct netfs_cmd); | ||
302 | |||
303 | dprintk("%s: size: %u, sz: %u, cmd_size: %u, cmd_cpad: %u.\n", | ||
304 | __func__, size, sz, __be32_to_cpu(cmd->size), __be16_to_cpu(cmd->cpad)); | ||
305 | |||
306 | data += sz; | ||
307 | size -= sz; | ||
308 | |||
309 | sg_init_one(&sg_src, cmd->data, sz - sizeof(struct netfs_cmd)); | ||
310 | sg_init_one(&sg_dst, cmd->data, sz - sizeof(struct netfs_cmd)); | ||
311 | |||
312 | err = iterator(e, &sg_dst, &sg_src); | ||
313 | if (err) | ||
314 | return err; | ||
315 | } | ||
316 | |||
317 | if (!pages) | ||
318 | return 0; | ||
319 | |||
320 | dpage_idx = 0; | ||
321 | for (i = 0; i < t->page_num; ++i) { | ||
322 | struct page *page = t->pages[i]; | ||
323 | struct page *dpage = e->pages[dpage_idx]; | ||
324 | |||
325 | if (!page) | ||
326 | continue; | ||
327 | |||
328 | sg_init_table(&sg_src, 1); | ||
329 | sg_init_table(&sg_dst, 1); | ||
330 | sg_set_page(&sg_src, page, page_private(page), 0); | ||
331 | sg_set_page(&sg_dst, dpage, page_private(page), 0); | ||
332 | |||
333 | err = iterator(e, &sg_dst, &sg_src); | ||
334 | if (err) | ||
335 | return err; | ||
336 | |||
337 | pages--; | ||
338 | if (!pages) | ||
339 | break; | ||
340 | dpage_idx++; | ||
341 | } | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int pohmelfs_encrypt_iterator(struct pohmelfs_crypto_engine *e, | ||
347 | struct scatterlist *sg_dst, struct scatterlist *sg_src) | ||
348 | { | ||
349 | struct ablkcipher_request *req = e->data; | ||
350 | u8 iv[32]; | ||
351 | |||
352 | memset(iv, 0, sizeof(iv)); | ||
353 | |||
354 | memcpy(iv, &e->iv, sizeof(e->iv)); | ||
355 | |||
356 | return pohmelfs_crypto_process(req, sg_dst, sg_src, iv, 1, e->timeout); | ||
357 | } | ||
358 | |||
359 | static int pohmelfs_encrypt(struct pohmelfs_crypto_thread *tc) | ||
360 | { | ||
361 | struct netfs_trans *t = tc->trans; | ||
362 | struct pohmelfs_crypto_engine *e = &tc->eng; | ||
363 | struct ablkcipher_request *req = e->data; | ||
364 | |||
365 | memset(req, 0, sizeof(struct ablkcipher_request)); | ||
366 | ablkcipher_request_set_tfm(req, e->cipher); | ||
367 | |||
368 | e->iv = pohmelfs_gen_iv(t); | ||
369 | |||
370 | return pohmelfs_trans_iter(t, e, pohmelfs_encrypt_iterator); | ||
371 | } | ||
372 | |||
373 | static int pohmelfs_hash_iterator(struct pohmelfs_crypto_engine *e, | ||
374 | struct scatterlist *sg_dst, struct scatterlist *sg_src) | ||
375 | { | ||
376 | return crypto_hash_update(e->data, sg_src, sg_src->length); | ||
377 | } | ||
378 | |||
379 | static int pohmelfs_hash(struct pohmelfs_crypto_thread *tc) | ||
380 | { | ||
381 | struct pohmelfs_crypto_engine *e = &tc->eng; | ||
382 | struct hash_desc *desc = e->data; | ||
383 | unsigned char *dst = tc->trans->iovec.iov_base + sizeof(struct netfs_cmd); | ||
384 | int err; | ||
385 | |||
386 | desc->tfm = e->hash; | ||
387 | desc->flags = 0; | ||
388 | |||
389 | err = crypto_hash_init(desc); | ||
390 | if (err) | ||
391 | return err; | ||
392 | |||
393 | err = pohmelfs_trans_iter(tc->trans, e, pohmelfs_hash_iterator); | ||
394 | if (err) | ||
395 | return err; | ||
396 | |||
397 | err = crypto_hash_final(desc, dst); | ||
398 | if (err) | ||
399 | return err; | ||
400 | |||
401 | { | ||
402 | unsigned int i; | ||
403 | dprintk("%s: ", __func__); | ||
404 | for (i = 0; i < tc->psb->crypto_attached_size; ++i) | ||
405 | dprintka("%02x ", dst[i]); | ||
406 | dprintka("\n"); | ||
407 | } | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static void pohmelfs_crypto_pages_free(struct pohmelfs_crypto_engine *e) | ||
413 | { | ||
414 | unsigned int i; | ||
415 | |||
416 | for (i = 0; i < e->page_num; ++i) | ||
417 | __free_page(e->pages[i]); | ||
418 | kfree(e->pages); | ||
419 | } | ||
420 | |||
421 | static int pohmelfs_crypto_pages_alloc(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) | ||
422 | { | ||
423 | unsigned int i; | ||
424 | |||
425 | e->pages = kmalloc(psb->trans_max_pages * sizeof(struct page *), GFP_KERNEL); | ||
426 | if (!e->pages) | ||
427 | return -ENOMEM; | ||
428 | |||
429 | for (i = 0; i < psb->trans_max_pages; ++i) { | ||
430 | e->pages[i] = alloc_page(GFP_KERNEL); | ||
431 | if (!e->pages[i]) | ||
432 | break; | ||
433 | } | ||
434 | |||
435 | e->page_num = i; | ||
436 | if (!e->page_num) | ||
437 | goto err_out_free; | ||
438 | |||
439 | return 0; | ||
440 | |||
441 | err_out_free: | ||
442 | kfree(e->pages); | ||
443 | return -ENOMEM; | ||
444 | } | ||
445 | |||
446 | static void pohmelfs_sys_crypto_exit_one(struct pohmelfs_crypto_thread *t) | ||
447 | { | ||
448 | struct pohmelfs_sb *psb = t->psb; | ||
449 | |||
450 | if (t->thread) | ||
451 | kthread_stop(t->thread); | ||
452 | |||
453 | mutex_lock(&psb->crypto_thread_lock); | ||
454 | list_del(&t->thread_entry); | ||
455 | psb->crypto_thread_num--; | ||
456 | mutex_unlock(&psb->crypto_thread_lock); | ||
457 | |||
458 | pohmelfs_crypto_engine_exit(&t->eng); | ||
459 | pohmelfs_crypto_pages_free(&t->eng); | ||
460 | kfree(t); | ||
461 | } | ||
462 | |||
463 | static int pohmelfs_crypto_finish(struct netfs_trans *t, struct pohmelfs_sb *psb, int err) | ||
464 | { | ||
465 | struct netfs_cmd *cmd = t->iovec.iov_base; | ||
466 | netfs_convert_cmd(cmd); | ||
467 | |||
468 | if (likely(!err)) | ||
469 | err = netfs_trans_finish_send(t, psb); | ||
470 | |||
471 | t->result = err; | ||
472 | netfs_trans_put(t); | ||
473 | |||
474 | return err; | ||
475 | } | ||
476 | |||
477 | void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th) | ||
478 | { | ||
479 | struct pohmelfs_sb *psb = th->psb; | ||
480 | |||
481 | th->page = NULL; | ||
482 | th->trans = NULL; | ||
483 | |||
484 | mutex_lock(&psb->crypto_thread_lock); | ||
485 | list_move_tail(&th->thread_entry, &psb->crypto_ready_list); | ||
486 | mutex_unlock(&psb->crypto_thread_lock); | ||
487 | wake_up(&psb->wait); | ||
488 | } | ||
489 | |||
490 | static int pohmelfs_crypto_thread_trans(struct pohmelfs_crypto_thread *t) | ||
491 | { | ||
492 | struct netfs_trans *trans; | ||
493 | int err = 0; | ||
494 | |||
495 | trans = t->trans; | ||
496 | trans->eng = NULL; | ||
497 | |||
498 | if (t->eng.hash) { | ||
499 | err = pohmelfs_hash(t); | ||
500 | if (err) | ||
501 | goto out_complete; | ||
502 | } | ||
503 | |||
504 | if (t->eng.cipher) { | ||
505 | err = pohmelfs_encrypt(t); | ||
506 | if (err) | ||
507 | goto out_complete; | ||
508 | trans->eng = &t->eng; | ||
509 | } | ||
510 | |||
511 | out_complete: | ||
512 | t->page = NULL; | ||
513 | t->trans = NULL; | ||
514 | |||
515 | if (!trans->eng) | ||
516 | pohmelfs_crypto_thread_make_ready(t); | ||
517 | |||
518 | pohmelfs_crypto_finish(trans, t->psb, err); | ||
519 | return err; | ||
520 | } | ||
521 | |||
522 | static int pohmelfs_crypto_thread_page(struct pohmelfs_crypto_thread *t) | ||
523 | { | ||
524 | struct pohmelfs_crypto_engine *e = &t->eng; | ||
525 | struct page *page = t->page; | ||
526 | int err; | ||
527 | |||
528 | WARN_ON(!PageChecked(page)); | ||
529 | |||
530 | err = pohmelfs_crypto_process_input_data(e, e->iv, NULL, page, t->size); | ||
531 | if (!err) | ||
532 | SetPageUptodate(page); | ||
533 | else | ||
534 | SetPageError(page); | ||
535 | unlock_page(page); | ||
536 | page_cache_release(page); | ||
537 | |||
538 | pohmelfs_crypto_thread_make_ready(t); | ||
539 | |||
540 | return err; | ||
541 | } | ||
542 | |||
543 | static int pohmelfs_crypto_thread_func(void *data) | ||
544 | { | ||
545 | struct pohmelfs_crypto_thread *t = data; | ||
546 | |||
547 | while (!kthread_should_stop()) { | ||
548 | wait_event_interruptible(t->wait, kthread_should_stop() || | ||
549 | t->trans || t->page); | ||
550 | |||
551 | if (kthread_should_stop()) | ||
552 | break; | ||
553 | |||
554 | if (!t->trans && !t->page) | ||
555 | continue; | ||
556 | |||
557 | dprintk("%s: thread: %p, trans: %p, page: %p.\n", | ||
558 | __func__, t, t->trans, t->page); | ||
559 | |||
560 | if (t->trans) | ||
561 | pohmelfs_crypto_thread_trans(t); | ||
562 | else if (t->page) | ||
563 | pohmelfs_crypto_thread_page(t); | ||
564 | } | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static void pohmelfs_crypto_flush(struct pohmelfs_sb *psb, struct list_head *head) | ||
570 | { | ||
571 | while (!list_empty(head)) { | ||
572 | struct pohmelfs_crypto_thread *t = NULL; | ||
573 | |||
574 | mutex_lock(&psb->crypto_thread_lock); | ||
575 | if (!list_empty(head)) { | ||
576 | t = list_first_entry(head, struct pohmelfs_crypto_thread, thread_entry); | ||
577 | list_del_init(&t->thread_entry); | ||
578 | } | ||
579 | mutex_unlock(&psb->crypto_thread_lock); | ||
580 | |||
581 | if (t) | ||
582 | pohmelfs_sys_crypto_exit_one(t); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | static void pohmelfs_sys_crypto_exit(struct pohmelfs_sb *psb) | ||
587 | { | ||
588 | while (!list_empty(&psb->crypto_active_list) || !list_empty(&psb->crypto_ready_list)) { | ||
589 | dprintk("%s: crypto_thread_num: %u.\n", __func__, psb->crypto_thread_num); | ||
590 | pohmelfs_crypto_flush(psb, &psb->crypto_active_list); | ||
591 | pohmelfs_crypto_flush(psb, &psb->crypto_ready_list); | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static int pohmelfs_sys_crypto_init(struct pohmelfs_sb *psb) | ||
596 | { | ||
597 | unsigned int i; | ||
598 | struct pohmelfs_crypto_thread *t; | ||
599 | struct pohmelfs_config *c; | ||
600 | struct netfs_state *st; | ||
601 | int err; | ||
602 | |||
603 | list_for_each_entry(c, &psb->state_list, config_entry) { | ||
604 | st = &c->state; | ||
605 | |||
606 | err = pohmelfs_crypto_engine_init(&st->eng, psb); | ||
607 | if (err) | ||
608 | goto err_out_exit; | ||
609 | |||
610 | dprintk("%s: st: %p, eng: %p, hash: %p, cipher: %p.\n", | ||
611 | __func__, st, &st->eng, &st->eng.hash, &st->eng.cipher); | ||
612 | } | ||
613 | |||
614 | for (i = 0; i < psb->crypto_thread_num; ++i) { | ||
615 | err = -ENOMEM; | ||
616 | t = kzalloc(sizeof(struct pohmelfs_crypto_thread), GFP_KERNEL); | ||
617 | if (!t) | ||
618 | goto err_out_free_state_engines; | ||
619 | |||
620 | init_waitqueue_head(&t->wait); | ||
621 | |||
622 | t->psb = psb; | ||
623 | t->trans = NULL; | ||
624 | t->eng.thread = t; | ||
625 | |||
626 | err = pohmelfs_crypto_engine_init(&t->eng, psb); | ||
627 | if (err) | ||
628 | goto err_out_free_state_engines; | ||
629 | |||
630 | err = pohmelfs_crypto_pages_alloc(&t->eng, psb); | ||
631 | if (err) | ||
632 | goto err_out_free; | ||
633 | |||
634 | t->thread = kthread_run(pohmelfs_crypto_thread_func, t, | ||
635 | "pohmelfs-crypto-%d-%d", psb->idx, i); | ||
636 | if (IS_ERR(t->thread)) { | ||
637 | err = PTR_ERR(t->thread); | ||
638 | t->thread = NULL; | ||
639 | goto err_out_free; | ||
640 | } | ||
641 | |||
642 | if (t->eng.cipher) | ||
643 | psb->crypto_align_size = crypto_ablkcipher_blocksize(t->eng.cipher); | ||
644 | |||
645 | mutex_lock(&psb->crypto_thread_lock); | ||
646 | list_add_tail(&t->thread_entry, &psb->crypto_ready_list); | ||
647 | mutex_unlock(&psb->crypto_thread_lock); | ||
648 | } | ||
649 | |||
650 | psb->crypto_thread_num = i; | ||
651 | return 0; | ||
652 | |||
653 | err_out_free: | ||
654 | pohmelfs_sys_crypto_exit_one(t); | ||
655 | err_out_free_state_engines: | ||
656 | list_for_each_entry(c, &psb->state_list, config_entry) { | ||
657 | st = &c->state; | ||
658 | pohmelfs_crypto_engine_exit(&st->eng); | ||
659 | } | ||
660 | err_out_exit: | ||
661 | pohmelfs_sys_crypto_exit(psb); | ||
662 | return err; | ||
663 | } | ||
664 | |||
665 | void pohmelfs_crypto_exit(struct pohmelfs_sb *psb) | ||
666 | { | ||
667 | pohmelfs_sys_crypto_exit(psb); | ||
668 | |||
669 | kfree(psb->hash_string); | ||
670 | kfree(psb->cipher_string); | ||
671 | } | ||
672 | |||
673 | static int pohmelfs_crypt_init_complete(struct page **pages, unsigned int page_num, | ||
674 | void *private, int err) | ||
675 | { | ||
676 | struct pohmelfs_sb *psb = private; | ||
677 | |||
678 | psb->flags = -err; | ||
679 | dprintk("%s: err: %d.\n", __func__, err); | ||
680 | |||
681 | wake_up(&psb->wait); | ||
682 | |||
683 | return err; | ||
684 | } | ||
685 | |||
686 | static int pohmelfs_crypto_init_handshake(struct pohmelfs_sb *psb) | ||
687 | { | ||
688 | struct netfs_trans *t; | ||
689 | struct netfs_crypto_capabilities *cap; | ||
690 | struct netfs_cmd *cmd; | ||
691 | char *str; | ||
692 | int err = -ENOMEM, size; | ||
693 | |||
694 | size = sizeof(struct netfs_crypto_capabilities) + | ||
695 | psb->cipher_strlen + psb->hash_strlen + 2; /* 0 bytes */ | ||
696 | |||
697 | t = netfs_trans_alloc(psb, size, 0, 0); | ||
698 | if (!t) | ||
699 | goto err_out_exit; | ||
700 | |||
701 | t->complete = pohmelfs_crypt_init_complete; | ||
702 | t->private = psb; | ||
703 | |||
704 | cmd = netfs_trans_current(t); | ||
705 | cap = (struct netfs_crypto_capabilities *)(cmd + 1); | ||
706 | str = (char *)(cap + 1); | ||
707 | |||
708 | cmd->cmd = NETFS_CAPABILITIES; | ||
709 | cmd->id = POHMELFS_CRYPTO_CAPABILITIES; | ||
710 | cmd->size = size; | ||
711 | cmd->start = 0; | ||
712 | cmd->ext = 0; | ||
713 | cmd->csize = 0; | ||
714 | |||
715 | netfs_convert_cmd(cmd); | ||
716 | netfs_trans_update(cmd, t, size); | ||
717 | |||
718 | cap->hash_strlen = psb->hash_strlen; | ||
719 | if (cap->hash_strlen) { | ||
720 | sprintf(str, "%s", psb->hash_string); | ||
721 | str += cap->hash_strlen; | ||
722 | } | ||
723 | |||
724 | cap->cipher_strlen = psb->cipher_strlen; | ||
725 | cap->cipher_keysize = psb->cipher_keysize; | ||
726 | if (cap->cipher_strlen) | ||
727 | sprintf(str, "%s", psb->cipher_string); | ||
728 | |||
729 | netfs_convert_crypto_capabilities(cap); | ||
730 | |||
731 | psb->flags = ~0; | ||
732 | err = netfs_trans_finish(t, psb); | ||
733 | if (err) | ||
734 | goto err_out_exit; | ||
735 | |||
736 | err = wait_event_interruptible_timeout(psb->wait, (psb->flags != ~0), | ||
737 | psb->wait_on_page_timeout); | ||
738 | if (!err) | ||
739 | err = -ETIMEDOUT; | ||
740 | else if (err > 0) | ||
741 | err = -psb->flags; | ||
742 | |||
743 | if (!err) | ||
744 | psb->perform_crypto = 1; | ||
745 | psb->flags = 0; | ||
746 | |||
747 | /* | ||
748 | * At this point NETFS_CAPABILITIES response command | ||
749 | * should setup superblock in a way, which is acceptable | ||
750 | * for both client and server, so if server refuses connection, | ||
751 | * it will send error in transaction response. | ||
752 | */ | ||
753 | |||
754 | if (err) | ||
755 | goto err_out_exit; | ||
756 | |||
757 | return 0; | ||
758 | |||
759 | err_out_exit: | ||
760 | return err; | ||
761 | } | ||
762 | |||
763 | int pohmelfs_crypto_init(struct pohmelfs_sb *psb) | ||
764 | { | ||
765 | int err; | ||
766 | |||
767 | if (!psb->cipher_string && !psb->hash_string) | ||
768 | return 0; | ||
769 | |||
770 | err = pohmelfs_crypto_init_handshake(psb); | ||
771 | if (err) | ||
772 | return err; | ||
773 | |||
774 | err = pohmelfs_sys_crypto_init(psb); | ||
775 | if (err) | ||
776 | return err; | ||
777 | |||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int pohmelfs_crypto_thread_get(struct pohmelfs_sb *psb, | ||
782 | int (*action)(struct pohmelfs_crypto_thread *t, void *data), void *data) | ||
783 | { | ||
784 | struct pohmelfs_crypto_thread *t = NULL; | ||
785 | int err; | ||
786 | |||
787 | while (!t) { | ||
788 | err = wait_event_interruptible_timeout(psb->wait, | ||
789 | !list_empty(&psb->crypto_ready_list), | ||
790 | psb->wait_on_page_timeout); | ||
791 | |||
792 | t = NULL; | ||
793 | err = 0; | ||
794 | mutex_lock(&psb->crypto_thread_lock); | ||
795 | if (!list_empty(&psb->crypto_ready_list)) { | ||
796 | t = list_entry(psb->crypto_ready_list.prev, | ||
797 | struct pohmelfs_crypto_thread, | ||
798 | thread_entry); | ||
799 | |||
800 | list_move_tail(&t->thread_entry, | ||
801 | &psb->crypto_active_list); | ||
802 | |||
803 | action(t, data); | ||
804 | wake_up(&t->wait); | ||
805 | |||
806 | } | ||
807 | mutex_unlock(&psb->crypto_thread_lock); | ||
808 | } | ||
809 | |||
810 | return err; | ||
811 | } | ||
812 | |||
813 | static int pohmelfs_trans_crypt_action(struct pohmelfs_crypto_thread *t, void *data) | ||
814 | { | ||
815 | struct netfs_trans *trans = data; | ||
816 | |||
817 | netfs_trans_get(trans); | ||
818 | t->trans = trans; | ||
819 | |||
820 | dprintk("%s: t: %p, gen: %u, thread: %p.\n", __func__, trans, trans->gen, t); | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | int pohmelfs_trans_crypt(struct netfs_trans *trans, struct pohmelfs_sb *psb) | ||
825 | { | ||
826 | if ((!psb->hash_string && !psb->cipher_string) || !psb->perform_crypto) { | ||
827 | netfs_trans_get(trans); | ||
828 | return pohmelfs_crypto_finish(trans, psb, 0); | ||
829 | } | ||
830 | |||
831 | return pohmelfs_crypto_thread_get(psb, pohmelfs_trans_crypt_action, trans); | ||
832 | } | ||
833 | |||
834 | struct pohmelfs_crypto_input_action_data { | ||
835 | struct page *page; | ||
836 | struct pohmelfs_crypto_engine *e; | ||
837 | u64 iv; | ||
838 | unsigned int size; | ||
839 | }; | ||
840 | |||
841 | static int pohmelfs_crypt_input_page_action(struct pohmelfs_crypto_thread *t, void *data) | ||
842 | { | ||
843 | struct pohmelfs_crypto_input_action_data *act = data; | ||
844 | |||
845 | memcpy(t->eng.data, act->e->data, t->psb->crypto_attached_size); | ||
846 | |||
847 | t->size = act->size; | ||
848 | t->eng.iv = act->iv; | ||
849 | |||
850 | t->page = act->page; | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e, | ||
855 | struct page *page, unsigned int size, u64 iv) | ||
856 | { | ||
857 | struct inode *inode = page->mapping->host; | ||
858 | struct pohmelfs_crypto_input_action_data act; | ||
859 | int err = -ENOENT; | ||
860 | |||
861 | act.page = page; | ||
862 | act.e = e; | ||
863 | act.size = size; | ||
864 | act.iv = iv; | ||
865 | |||
866 | err = pohmelfs_crypto_thread_get(POHMELFS_SB(inode->i_sb), | ||
867 | pohmelfs_crypt_input_page_action, &act); | ||
868 | if (err) | ||
869 | goto err_out_exit; | ||
870 | |||
871 | return 0; | ||
872 | |||
873 | err_out_exit: | ||
874 | SetPageUptodate(page); | ||
875 | page_cache_release(page); | ||
876 | |||
877 | return err; | ||
878 | } | ||