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