dm-crypt.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 800 行 · 第 1/2 页

C
800
字号
/* * Copyright (C) 2003 Christophe Saout <christophe@saout.de> * * This file is released under the GPL. */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/bio.h>#include <linux/blkdev.h>#include <linux/mempool.h>#include <linux/slab.h>#include <linux/crypto.h>#include <linux/workqueue.h>#include <asm/atomic.h>#include <asm/scatterlist.h>#include "dm.h"#define PFX	"crypt: "/* * per bio private data */struct crypt_io {	struct dm_target *target;	struct bio *bio;	struct bio *first_clone;	struct work_struct work;	atomic_t pending;	int error;};/* * context holding the current state of a multi-part conversion */struct convert_context {	struct bio *bio_in;	struct bio *bio_out;	unsigned int offset_in;	unsigned int offset_out;	int idx_in;	int idx_out;	sector_t sector;	int write;};/* * Crypt: maps a linear range of a block device * and encrypts / decrypts at the same time. */struct crypt_config {	struct dm_dev *dev;	sector_t start;	/*	 * pool for per bio private data and	 * for encryption buffer pages	 */	mempool_t *io_pool;	mempool_t *page_pool;	/*	 * crypto related data	 */	struct crypto_tfm *tfm;	sector_t iv_offset;	int (*iv_generator)(struct crypt_config *cc, u8 *iv, sector_t sector);	int iv_size;	int key_size;	u8 key[0];};#define MIN_IOS        256#define MIN_POOL_PAGES 32#define MIN_BIO_PAGES  8static kmem_cache_t *_crypt_io_pool;/* * Mempool alloc and free functions for the page */static void *mempool_alloc_page(int gfp_mask, void *data){	return alloc_page(gfp_mask);}static void mempool_free_page(void *page, void *data){	__free_page(page);}/* * Different IV generation algorithms */static int crypt_iv_plain(struct crypt_config *cc, u8 *iv, sector_t sector){	*(u32 *)iv = cpu_to_le32(sector & 0xffffffff);	if (cc->iv_size > sizeof(u32) / sizeof(u8))		memset(iv + (sizeof(u32) / sizeof(u8)), 0,		       cc->iv_size - (sizeof(u32) / sizeof(u8)));	return 0;}static inline intcrypt_convert_scatterlist(struct crypt_config *cc, struct scatterlist *out,                          struct scatterlist *in, unsigned int length,                          int write, sector_t sector){	u8 iv[cc->iv_size];	int r;	if (cc->iv_generator) {		r = cc->iv_generator(cc, iv, sector);		if (r < 0)			return r;		if (write)			r = crypto_cipher_encrypt_iv(cc->tfm, out, in, length, iv);		else			r = crypto_cipher_decrypt_iv(cc->tfm, out, in, length, iv);	} else {		if (write)			r = crypto_cipher_encrypt(cc->tfm, out, in, length);		else			r = crypto_cipher_decrypt(cc->tfm, out, in, length);	}	return r;}static voidcrypt_convert_init(struct crypt_config *cc, struct convert_context *ctx,                   struct bio *bio_out, struct bio *bio_in,                   sector_t sector, int write){	ctx->bio_in = bio_in;	ctx->bio_out = bio_out;	ctx->offset_in = 0;	ctx->offset_out = 0;	ctx->idx_in = bio_in ? bio_in->bi_idx : 0;	ctx->idx_out = bio_out ? bio_out->bi_idx : 0;	ctx->sector = sector + cc->iv_offset;	ctx->write = write;}/* * Encrypt / decrypt data from one bio to another one (can be the same one) */static int crypt_convert(struct crypt_config *cc,                         struct convert_context *ctx){	int r = 0;	while(ctx->idx_in < ctx->bio_in->bi_vcnt &&	      ctx->idx_out < ctx->bio_out->bi_vcnt) {		struct bio_vec *bv_in = bio_iovec_idx(ctx->bio_in, ctx->idx_in);		struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out);		struct scatterlist sg_in = {			.page = bv_in->bv_page,			.offset = bv_in->bv_offset + ctx->offset_in,			.length = 1 << SECTOR_SHIFT		};		struct scatterlist sg_out = {			.page = bv_out->bv_page,			.offset = bv_out->bv_offset + ctx->offset_out,			.length = 1 << SECTOR_SHIFT		};		ctx->offset_in += sg_in.length;		if (ctx->offset_in >= bv_in->bv_len) {			ctx->offset_in = 0;			ctx->idx_in++;		}		ctx->offset_out += sg_out.length;		if (ctx->offset_out >= bv_out->bv_len) {			ctx->offset_out = 0;			ctx->idx_out++;		}		r = crypt_convert_scatterlist(cc, &sg_out, &sg_in, sg_in.length,		                              ctx->write, ctx->sector);		if (r < 0)			break;		ctx->sector++;	}	return r;}/* * Generate a new unfragmented bio with the given size * This should never violate the device limitations * May return a smaller bio when running out of pages */static struct bio *crypt_alloc_buffer(struct crypt_config *cc, unsigned int size,                   struct bio *base_bio, int *bio_vec_idx){	struct bio *bio;	int nr_iovecs = dm_div_up(size, PAGE_SIZE);	int gfp_mask = GFP_NOIO | __GFP_HIGHMEM;	int flags = current->flags;	int i;	/*	 * Tell VM to act less aggressively and fail earlier.	 * This is not necessary but increases throughput.	 * FIXME: Is this really intelligent?	 */	current->flags &= ~PF_MEMALLOC;	if (base_bio)		bio = bio_clone(base_bio, GFP_NOIO);	else		bio = bio_alloc(GFP_NOIO, nr_iovecs);	if (!bio) {		if (flags & PF_MEMALLOC)			current->flags |= PF_MEMALLOC;		return NULL;	}	/* if the last bio was not complete, continue where that one ended */	bio->bi_idx = *bio_vec_idx;	bio->bi_vcnt = *bio_vec_idx;	bio->bi_size = 0;	bio->bi_flags &= ~(1 << BIO_SEG_VALID);	/* bio->bi_idx pages have already been allocated */	size -= bio->bi_idx * PAGE_SIZE;	for(i = bio->bi_idx; i < nr_iovecs; i++) {		struct bio_vec *bv = bio_iovec_idx(bio, i);		bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask);		if (!bv->bv_page)			break;		/*		 * if additional pages cannot be allocated without waiting,		 * return a partially allocated bio, the caller will then try		 * to allocate additional bios while submitting this partial bio		 */		if ((i - bio->bi_idx) == (MIN_BIO_PAGES - 1))			gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT;		bv->bv_offset = 0;		if (size > PAGE_SIZE)			bv->bv_len = PAGE_SIZE;		else			bv->bv_len = size;		bio->bi_size += bv->bv_len;		bio->bi_vcnt++;		size -= bv->bv_len;	}	if (flags & PF_MEMALLOC)		current->flags |= PF_MEMALLOC;	if (!bio->bi_size) {		bio_put(bio);		return NULL;	}	/*	 * Remember the last bio_vec allocated to be able	 * to correctly continue after the splitting.	 */	*bio_vec_idx = bio->bi_vcnt;	return bio;}static void crypt_free_buffer_pages(struct crypt_config *cc,                                    struct bio *bio, unsigned int bytes){	unsigned int start, end;	struct bio_vec *bv;	int i;	/*	 * This is ugly, but Jens Axboe thinks that using bi_idx in the	 * endio function is too dangerous at the moment, so I calculate the	 * correct position using bi_vcnt and bi_size.	 * The bv_offset and bv_len fields might already be modified but we	 * know that we always allocated whole pages.	 * A fix to the bi_idx issue in the kernel is in the works, so	 * we will hopefully be able to revert to the cleaner solution soon.	 */	i = bio->bi_vcnt - 1;	bv = bio_iovec_idx(bio, i);	end = (i << PAGE_SHIFT) + (bv->bv_offset + bv->bv_len) - bio->bi_size;	start = end - bytes;	start >>= PAGE_SHIFT;	if (!bio->bi_size)		end = bio->bi_vcnt;	else		end >>= PAGE_SHIFT;	for(i = start; i < end; i++) {		bv = bio_iovec_idx(bio, i);		BUG_ON(!bv->bv_page);		mempool_free(bv->bv_page, cc->page_pool);		bv->bv_page = NULL;	}}/* * One of the bios was finished. Check for completion of * the whole request and correctly clean up the buffer. */static void dec_pending(struct crypt_io *io, int error){	struct crypt_config *cc = (struct crypt_config *) io->target->private;	if (error < 0)		io->error = error;	if (!atomic_dec_and_test(&io->pending))		return;	if (io->first_clone)		bio_put(io->first_clone);	bio_endio(io->bio, io->bio->bi_size, io->error);	mempool_free(io, cc->io_pool);}/* * kcryptd: * * Needed because it would be very unwise to do decryption in an * interrupt context, so bios returning from read requests get * queued here. */static struct workqueue_struct *_kcryptd_workqueue;static void kcryptd_do_work(void *data){	struct crypt_io *io = (struct crypt_io *) data;	struct crypt_config *cc = (struct crypt_config *) io->target->private;	struct convert_context ctx;	int r;	crypt_convert_init(cc, &ctx, io->bio, io->bio,	                   io->bio->bi_sector - io->target->begin, 0);	r = crypt_convert(cc, &ctx);	dec_pending(io, r);}static void kcryptd_queue_io(struct crypt_io *io){	INIT_WORK(&io->work, kcryptd_do_work, io);	queue_work(_kcryptd_workqueue, &io->work);}/* * Decode key from its hex representation */static int crypt_decode_key(u8 *key, char *hex, int size){	char buffer[3];	char *endp;	int i;	buffer[2] = '\0';	for(i = 0; i < size; i++) {		buffer[0] = *hex++;		buffer[1] = *hex++;		key[i] = (u8)simple_strtoul(buffer, &endp, 16);		if (endp != &buffer[2])			return -EINVAL;	}	if (*hex != '\0')		return -EINVAL;	return 0;}/* * Encode key into its hex representation */static void crypt_encode_key(char *hex, u8 *key, int size){	int i;	for(i = 0; i < size; i++) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?