732 lines
16 KiB
C
732 lines
16 KiB
C
/*
|
|
* 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/bio.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/dst.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/slab.h>
|
|
|
|
/*
|
|
* Tricky bastard, but IV can be more complex with time...
|
|
*/
|
|
static inline u64 dst_gen_iv(struct dst_trans *t)
|
|
{
|
|
return t->gen;
|
|
}
|
|
|
|
/*
|
|
* Crypto machinery: hash/cipher support for the given crypto controls.
|
|
*/
|
|
static struct crypto_hash *dst_init_hash(struct dst_crypto_ctl *ctl, u8 *key)
|
|
{
|
|
int err;
|
|
struct crypto_hash *hash;
|
|
|
|
hash = crypto_alloc_hash(ctl->hash_algo, 0, CRYPTO_ALG_ASYNC);
|
|
if (IS_ERR(hash)) {
|
|
err = PTR_ERR(hash);
|
|
dprintk("%s: failed to allocate hash '%s', err: %d.\n",
|
|
__func__, ctl->hash_algo, err);
|
|
goto err_out_exit;
|
|
}
|
|
|
|
ctl->crypto_attached_size = crypto_hash_digestsize(hash);
|
|
|
|
if (!ctl->hash_keysize)
|
|
return hash;
|
|
|
|
err = crypto_hash_setkey(hash, key, ctl->hash_keysize);
|
|
if (err) {
|
|
dprintk("%s: failed to set key for hash '%s', err: %d.\n",
|
|
__func__, ctl->hash_algo, err);
|
|
goto err_out_free;
|
|
}
|
|
|
|
return hash;
|
|
|
|
err_out_free:
|
|
crypto_free_hash(hash);
|
|
err_out_exit:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static struct crypto_ablkcipher *dst_init_cipher(struct dst_crypto_ctl *ctl, u8 *key)
|
|
{
|
|
int err = -EINVAL;
|
|
struct crypto_ablkcipher *cipher;
|
|
|
|
if (!ctl->cipher_keysize)
|
|
goto err_out_exit;
|
|
|
|
cipher = crypto_alloc_ablkcipher(ctl->cipher_algo, 0, 0);
|
|
if (IS_ERR(cipher)) {
|
|
err = PTR_ERR(cipher);
|
|
dprintk("%s: failed to allocate cipher '%s', err: %d.\n",
|
|
__func__, ctl->cipher_algo, err);
|
|
goto err_out_exit;
|
|
}
|
|
|
|
crypto_ablkcipher_clear_flags(cipher, ~0);
|
|
|
|
err = crypto_ablkcipher_setkey(cipher, key, ctl->cipher_keysize);
|
|
if (err) {
|
|
dprintk("%s: failed to set key for cipher '%s', err: %d.\n",
|
|
__func__, ctl->cipher_algo, err);
|
|
goto err_out_free;
|
|
}
|
|
|
|
return cipher;
|
|
|
|
err_out_free:
|
|
crypto_free_ablkcipher(cipher);
|
|
err_out_exit:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
/*
|
|
* Crypto engine has a pool of pages to encrypt data into before sending
|
|
* it over the network. This pool is freed/allocated here.
|
|
*/
|
|
static void dst_crypto_pages_free(struct dst_crypto_engine *e)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i=0; i<e->page_num; ++i)
|
|
__free_page(e->pages[i]);
|
|
kfree(e->pages);
|
|
}
|
|
|
|
static int dst_crypto_pages_alloc(struct dst_crypto_engine *e, int num)
|
|
{
|
|
int i;
|
|
|
|
e->pages = kmalloc(num * sizeof(struct page **), GFP_KERNEL);
|
|
if (!e->pages)
|
|
return -ENOMEM;
|
|
|
|
for (i=0; i<num; ++i) {
|
|
e->pages[i] = alloc_page(GFP_KERNEL);
|
|
if (!e->pages[i])
|
|
goto err_out_free_pages;
|
|
}
|
|
|
|
e->page_num = num;
|
|
return 0;
|
|
|
|
err_out_free_pages:
|
|
while (--i >= 0)
|
|
__free_page(e->pages[i]);
|
|
|
|
kfree(e->pages);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*
|
|
* Initialize crypto engine for given node.
|
|
* Setup cipher/hash, keys, pool of threads and private data.
|
|
*/
|
|
static int dst_crypto_engine_init(struct dst_crypto_engine *e, struct dst_node *n)
|
|
{
|
|
int err;
|
|
struct dst_crypto_ctl *ctl = &n->crypto;
|
|
|
|
err = dst_crypto_pages_alloc(e, n->max_pages);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
e->size = PAGE_SIZE;
|
|
e->data = kmalloc(e->size, GFP_KERNEL);
|
|
if (!e->data) {
|
|
err = -ENOMEM;
|
|
goto err_out_free_pages;
|
|
}
|
|
|
|
if (ctl->hash_algo[0]) {
|
|
e->hash = dst_init_hash(ctl, n->hash_key);
|
|
if (IS_ERR(e->hash)) {
|
|
err = PTR_ERR(e->hash);
|
|
e->hash = NULL;
|
|
goto err_out_free;
|
|
}
|
|
}
|
|
|
|
if (ctl->cipher_algo[0]) {
|
|
e->cipher = dst_init_cipher(ctl, n->cipher_key);
|
|
if (IS_ERR(e->cipher)) {
|
|
err = PTR_ERR(e->cipher);
|
|
e->cipher = NULL;
|
|
goto err_out_free_hash;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_free_hash:
|
|
crypto_free_hash(e->hash);
|
|
err_out_free:
|
|
kfree(e->data);
|
|
err_out_free_pages:
|
|
dst_crypto_pages_free(e);
|
|
err_out_exit:
|
|
return err;
|
|
}
|
|
|
|
static void dst_crypto_engine_exit(struct dst_crypto_engine *e)
|
|
{
|
|
if (e->hash)
|
|
crypto_free_hash(e->hash);
|
|
if (e->cipher)
|
|
crypto_free_ablkcipher(e->cipher);
|
|
dst_crypto_pages_free(e);
|
|
kfree(e->data);
|
|
}
|
|
|
|
/*
|
|
* Waiting for cipher processing to be completed.
|
|
*/
|
|
struct dst_crypto_completion
|
|
{
|
|
struct completion complete;
|
|
int error;
|
|
};
|
|
|
|
static void dst_crypto_complete(struct crypto_async_request *req, int err)
|
|
{
|
|
struct dst_crypto_completion *c = req->data;
|
|
|
|
if (err == -EINPROGRESS)
|
|
return;
|
|
|
|
dprintk("%s: req: %p, err: %d.\n", __func__, req, err);
|
|
c->error = err;
|
|
complete(&c->complete);
|
|
}
|
|
|
|
static int dst_crypto_process(struct ablkcipher_request *req,
|
|
struct scatterlist *sg_dst, struct scatterlist *sg_src,
|
|
void *iv, int enc, unsigned long timeout)
|
|
{
|
|
struct dst_crypto_completion c;
|
|
int err;
|
|
|
|
init_completion(&c.complete);
|
|
c.error = -EINPROGRESS;
|
|
|
|
ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
dst_crypto_complete, &c);
|
|
|
|
ablkcipher_request_set_crypt(req, sg_src, sg_dst, sg_src->length, iv);
|
|
|
|
if (enc)
|
|
err = crypto_ablkcipher_encrypt(req);
|
|
else
|
|
err = crypto_ablkcipher_decrypt(req);
|
|
|
|
switch (err) {
|
|
case -EINPROGRESS:
|
|
case -EBUSY:
|
|
err = wait_for_completion_interruptible_timeout(&c.complete,
|
|
timeout);
|
|
if (!err)
|
|
err = -ETIMEDOUT;
|
|
else
|
|
err = c.error;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* DST uses generic iteration approach for data crypto processing.
|
|
* Single block IO request is switched into array of scatterlists,
|
|
* which are submitted to the crypto processing iterator.
|
|
*
|
|
* Input and output iterator initialization are different, since
|
|
* in output case we can not encrypt data in-place and need a
|
|
* temporary storage, which is then being sent to the remote peer.
|
|
*/
|
|
static int dst_trans_iter_out(struct bio *bio, struct dst_crypto_engine *e,
|
|
int (* iterator) (struct dst_crypto_engine *e,
|
|
struct scatterlist *dst,
|
|
struct scatterlist *src))
|
|
{
|
|
struct bio_vec *bv;
|
|
int err, i;
|
|
|
|
sg_init_table(e->src, bio->bi_vcnt);
|
|
sg_init_table(e->dst, bio->bi_vcnt);
|
|
|
|
bio_for_each_segment(bv, bio, i) {
|
|
sg_set_page(&e->src[i], bv->bv_page, bv->bv_len, bv->bv_offset);
|
|
sg_set_page(&e->dst[i], e->pages[i], bv->bv_len, bv->bv_offset);
|
|
|
|
err = iterator(e, &e->dst[i], &e->src[i]);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dst_trans_iter_in(struct bio *bio, struct dst_crypto_engine *e,
|
|
int (* iterator) (struct dst_crypto_engine *e,
|
|
struct scatterlist *dst,
|
|
struct scatterlist *src))
|
|
{
|
|
struct bio_vec *bv;
|
|
int err, i;
|
|
|
|
sg_init_table(e->src, bio->bi_vcnt);
|
|
sg_init_table(e->dst, bio->bi_vcnt);
|
|
|
|
bio_for_each_segment(bv, bio, i) {
|
|
sg_set_page(&e->src[i], bv->bv_page, bv->bv_len, bv->bv_offset);
|
|
sg_set_page(&e->dst[i], bv->bv_page, bv->bv_len, bv->bv_offset);
|
|
|
|
err = iterator(e, &e->dst[i], &e->src[i]);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dst_crypt_iterator(struct dst_crypto_engine *e,
|
|
struct scatterlist *sg_dst, struct scatterlist *sg_src)
|
|
{
|
|
struct ablkcipher_request *req = e->data;
|
|
u8 iv[32];
|
|
|
|
memset(iv, 0, sizeof(iv));
|
|
|
|
memcpy(iv, &e->iv, sizeof(e->iv));
|
|
|
|
return dst_crypto_process(req, sg_dst, sg_src, iv, e->enc, e->timeout);
|
|
}
|
|
|
|
static int dst_crypt(struct dst_crypto_engine *e, struct bio *bio)
|
|
{
|
|
struct ablkcipher_request *req = e->data;
|
|
|
|
memset(req, 0, sizeof(struct ablkcipher_request));
|
|
ablkcipher_request_set_tfm(req, e->cipher);
|
|
|
|
if (e->enc)
|
|
return dst_trans_iter_out(bio, e, dst_crypt_iterator);
|
|
else
|
|
return dst_trans_iter_in(bio, e, dst_crypt_iterator);
|
|
}
|
|
|
|
static int dst_hash_iterator(struct dst_crypto_engine *e,
|
|
struct scatterlist *sg_dst, struct scatterlist *sg_src)
|
|
{
|
|
return crypto_hash_update(e->data, sg_src, sg_src->length);
|
|
}
|
|
|
|
static int dst_hash(struct dst_crypto_engine *e, struct bio *bio, void *dst)
|
|
{
|
|
struct hash_desc *desc = e->data;
|
|
int err;
|
|
|
|
desc->tfm = e->hash;
|
|
desc->flags = 0;
|
|
|
|
err = crypto_hash_init(desc);
|
|
if (err)
|
|
return err;
|
|
|
|
err = dst_trans_iter_in(bio, e, dst_hash_iterator);
|
|
if (err)
|
|
return err;
|
|
|
|
err = crypto_hash_final(desc, dst);
|
|
if (err)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize/cleanup a crypto thread. The only thing it should
|
|
* do is to allocate a pool of pages as temporary storage.
|
|
* And to setup cipher and/or hash.
|
|
*/
|
|
static void *dst_crypto_thread_init(void *data)
|
|
{
|
|
struct dst_node *n = data;
|
|
struct dst_crypto_engine *e;
|
|
int err = -ENOMEM;
|
|
|
|
e = kzalloc(sizeof(struct dst_crypto_engine), GFP_KERNEL);
|
|
if (!e)
|
|
goto err_out_exit;
|
|
e->src = kcalloc(2 * n->max_pages, sizeof(struct scatterlist),
|
|
GFP_KERNEL);
|
|
if (!e->src)
|
|
goto err_out_free;
|
|
|
|
e->dst = e->src + n->max_pages;
|
|
|
|
err = dst_crypto_engine_init(e, n);
|
|
if (err)
|
|
goto err_out_free_all;
|
|
|
|
return e;
|
|
|
|
err_out_free_all:
|
|
kfree(e->src);
|
|
err_out_free:
|
|
kfree(e);
|
|
err_out_exit:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static void dst_crypto_thread_cleanup(void *private)
|
|
{
|
|
struct dst_crypto_engine *e = private;
|
|
|
|
dst_crypto_engine_exit(e);
|
|
kfree(e->src);
|
|
kfree(e);
|
|
}
|
|
|
|
/*
|
|
* Initialize crypto engine for given node: store keys, create pool
|
|
* of threads, initialize each one.
|
|
*
|
|
* Each thread has unique ID, but 0 and 1 are reserved for receiving and accepting
|
|
* threads (if export node), so IDs could start from 2, but starting them
|
|
* from 10 allows easily understand what this thread is for.
|
|
*/
|
|
int dst_node_crypto_init(struct dst_node *n, struct dst_crypto_ctl *ctl)
|
|
{
|
|
void *key = (ctl + 1);
|
|
int err = -ENOMEM, i;
|
|
char name[32];
|
|
|
|
if (ctl->hash_keysize) {
|
|
n->hash_key = kmalloc(ctl->hash_keysize, GFP_KERNEL);
|
|
if (!n->hash_key)
|
|
goto err_out_exit;
|
|
memcpy(n->hash_key, key, ctl->hash_keysize);
|
|
}
|
|
|
|
if (ctl->cipher_keysize) {
|
|
n->cipher_key = kmalloc(ctl->cipher_keysize, GFP_KERNEL);
|
|
if (!n->cipher_key)
|
|
goto err_out_free_hash;
|
|
memcpy(n->cipher_key, key, ctl->cipher_keysize);
|
|
}
|
|
memcpy(&n->crypto, ctl, sizeof(struct dst_crypto_ctl));
|
|
|
|
for (i=0; i<ctl->thread_num; ++i) {
|
|
snprintf(name, sizeof(name), "%s-crypto-%d", n->name, i);
|
|
/* Unique ids... */
|
|
err = thread_pool_add_worker(n->pool, name, i+10,
|
|
dst_crypto_thread_init, dst_crypto_thread_cleanup, n);
|
|
if (err)
|
|
goto err_out_free_threads;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_free_threads:
|
|
while (--i >= 0)
|
|
thread_pool_del_worker_id(n->pool, i+10);
|
|
|
|
if (ctl->cipher_keysize)
|
|
kfree(n->cipher_key);
|
|
ctl->cipher_keysize = 0;
|
|
err_out_free_hash:
|
|
if (ctl->hash_keysize)
|
|
kfree(n->hash_key);
|
|
ctl->hash_keysize = 0;
|
|
err_out_exit:
|
|
return err;
|
|
}
|
|
|
|
void dst_node_crypto_exit(struct dst_node *n)
|
|
{
|
|
struct dst_crypto_ctl *ctl = &n->crypto;
|
|
|
|
if (ctl->cipher_algo[0] || ctl->hash_algo[0]) {
|
|
kfree(n->hash_key);
|
|
kfree(n->cipher_key);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Thrad pool setup callback. Just stores a transaction in private data.
|
|
*/
|
|
static int dst_trans_crypto_setup(void *crypto_engine, void *trans)
|
|
{
|
|
struct dst_crypto_engine *e = crypto_engine;
|
|
|
|
e->private = trans;
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static void dst_dump_bio(struct bio *bio)
|
|
{
|
|
u8 *p;
|
|
struct bio_vec *bv;
|
|
int i;
|
|
|
|
bio_for_each_segment(bv, bio, i) {
|
|
dprintk("%s: %llu/%u: size: %u, offset: %u, data: ",
|
|
__func__, bio->bi_sector, bio->bi_size,
|
|
bv->bv_len, bv->bv_offset);
|
|
|
|
p = kmap(bv->bv_page) + bv->bv_offset;
|
|
for (i=0; i<bv->bv_len; ++i)
|
|
printk("%02x ", p[i]);
|
|
kunmap(bv->bv_page);
|
|
printk("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Encrypt/hash data and send it to the network.
|
|
*/
|
|
static int dst_crypto_process_sending(struct dst_crypto_engine *e,
|
|
struct bio *bio, u8 *hash)
|
|
{
|
|
int err;
|
|
|
|
if (e->cipher) {
|
|
err = dst_crypt(e, bio);
|
|
if (err)
|
|
goto err_out_exit;
|
|
}
|
|
|
|
if (e->hash) {
|
|
err = dst_hash(e, bio, hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
#ifdef CONFIG_DST_DEBUG
|
|
{
|
|
unsigned int i;
|
|
|
|
/* dst_dump_bio(bio); */
|
|
|
|
printk(KERN_DEBUG "%s: bio: %llu/%u, rw: %lu, hash: ",
|
|
__func__, (u64)bio->bi_sector,
|
|
bio->bi_size, bio_data_dir(bio));
|
|
for (i=0; i<crypto_hash_digestsize(e->hash); ++i)
|
|
printk("%02x ", hash[i]);
|
|
printk("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Check if received data is valid. Decipher if it is.
|
|
*/
|
|
static int dst_crypto_process_receiving(struct dst_crypto_engine *e,
|
|
struct bio *bio, u8 *hash, u8 *recv_hash)
|
|
{
|
|
int err;
|
|
|
|
if (e->hash) {
|
|
int mismatch;
|
|
|
|
err = dst_hash(e, bio, hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
mismatch = !!memcmp(recv_hash, hash,
|
|
crypto_hash_digestsize(e->hash));
|
|
#ifdef CONFIG_DST_DEBUG
|
|
/* dst_dump_bio(bio); */
|
|
|
|
printk(KERN_DEBUG "%s: bio: %llu/%u, rw: %lu, hash mismatch: %d",
|
|
__func__, (u64)bio->bi_sector, bio->bi_size,
|
|
bio_data_dir(bio), mismatch);
|
|
if (mismatch) {
|
|
unsigned int i;
|
|
|
|
printk(", recv/calc: ");
|
|
for (i=0; i<crypto_hash_digestsize(e->hash); ++i) {
|
|
printk("%02x/%02x ", recv_hash[i], hash[i]);
|
|
}
|
|
}
|
|
printk("\n");
|
|
#endif
|
|
err = -1;
|
|
if (mismatch)
|
|
goto err_out_exit;
|
|
}
|
|
|
|
if (e->cipher) {
|
|
err = dst_crypt(e, bio);
|
|
if (err)
|
|
goto err_out_exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Thread pool callback to encrypt data and send it to the netowork.
|
|
*/
|
|
static int dst_trans_crypto_action(void *crypto_engine, void *schedule_data)
|
|
{
|
|
struct dst_crypto_engine *e = crypto_engine;
|
|
struct dst_trans *t = schedule_data;
|
|
struct bio *bio = t->bio;
|
|
int err;
|
|
|
|
dprintk("%s: t: %p, gen: %llu, cipher: %p, hash: %p.\n",
|
|
__func__, t, t->gen, e->cipher, e->hash);
|
|
|
|
e->enc = t->enc;
|
|
e->iv = dst_gen_iv(t);
|
|
|
|
if (bio_data_dir(bio) == WRITE) {
|
|
err = dst_crypto_process_sending(e, bio, t->cmd.hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
if (e->hash) {
|
|
t->cmd.csize = crypto_hash_digestsize(e->hash);
|
|
t->cmd.size += t->cmd.csize;
|
|
}
|
|
|
|
return dst_trans_send(t);
|
|
} else {
|
|
u8 *hash = e->data + e->size/2;
|
|
|
|
err = dst_crypto_process_receiving(e, bio, hash, t->cmd.hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
dst_trans_remove(t);
|
|
dst_trans_put(t);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
t->error = err;
|
|
dst_trans_put(t);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Schedule crypto processing for given transaction.
|
|
*/
|
|
int dst_trans_crypto(struct dst_trans *t)
|
|
{
|
|
struct dst_node *n = t->n;
|
|
int err;
|
|
|
|
err = thread_pool_schedule(n->pool,
|
|
dst_trans_crypto_setup, dst_trans_crypto_action,
|
|
t, MAX_SCHEDULE_TIMEOUT);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
dst_trans_put(t);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Crypto machinery for the export node.
|
|
*/
|
|
static int dst_export_crypto_setup(void *crypto_engine, void *bio)
|
|
{
|
|
struct dst_crypto_engine *e = crypto_engine;
|
|
|
|
e->private = bio;
|
|
return 0;
|
|
}
|
|
|
|
static int dst_export_crypto_action(void *crypto_engine, void *schedule_data)
|
|
{
|
|
struct dst_crypto_engine *e = crypto_engine;
|
|
struct bio *bio = schedule_data;
|
|
struct dst_export_priv *p = bio->bi_private;
|
|
int err;
|
|
|
|
dprintk("%s: e: %p, data: %p, bio: %llu/%u, dir: %lu.\n", __func__,
|
|
e, e->data, (u64)bio->bi_sector, bio->bi_size, bio_data_dir(bio));
|
|
|
|
e->enc = (bio_data_dir(bio) == READ);
|
|
e->iv = p->cmd.id;
|
|
|
|
if (bio_data_dir(bio) == WRITE) {
|
|
u8 *hash = e->data + e->size/2;
|
|
|
|
err = dst_crypto_process_receiving(e, bio, hash, p->cmd.hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
generic_make_request(bio);
|
|
} else {
|
|
err = dst_crypto_process_sending(e, bio, p->cmd.hash);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
if (e->hash) {
|
|
p->cmd.csize = crypto_hash_digestsize(e->hash);
|
|
p->cmd.size += p->cmd.csize;
|
|
}
|
|
|
|
err = dst_export_send_bio(bio);
|
|
}
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
bio_put(bio);
|
|
return err;
|
|
}
|
|
|
|
int dst_export_crypto(struct dst_node *n, struct bio *bio)
|
|
{
|
|
int err;
|
|
|
|
err = thread_pool_schedule(n->pool,
|
|
dst_export_crypto_setup, dst_export_crypto_action,
|
|
bio, MAX_SCHEDULE_TIMEOUT);
|
|
if (err)
|
|
goto err_out_exit;
|
|
|
|
return 0;
|
|
|
|
err_out_exit:
|
|
bio_put(bio);
|
|
return err;
|
|
}
|