dm-crypt.c

来自「linux 内核源代码」· C语言 代码 · 共 1,109 行 · 第 1/2 页

C
1,109
字号
	 * The block layer might modify the bvec array, so always	 * copy the required bvecs because we need the original	 * one in order to decrypt the whole bio data *afterwards*.	 */	clone = bio_alloc_bioset(GFP_NOIO, bio_segments(base_bio), cc->bs);	if (unlikely(!clone)) {		crypt_dec_pending(io, -ENOMEM);		return;	}	clone_init(io, clone);	clone->bi_idx = 0;	clone->bi_vcnt = bio_segments(base_bio);	clone->bi_size = base_bio->bi_size;	clone->bi_sector = cc->start + sector;	memcpy(clone->bi_io_vec, bio_iovec(base_bio),	       sizeof(struct bio_vec) * clone->bi_vcnt);	generic_make_request(clone);}static void process_write(struct dm_crypt_io *io){	struct crypt_config *cc = io->target->private;	struct bio *base_bio = io->base_bio;	struct bio *clone;	struct convert_context ctx;	unsigned remaining = base_bio->bi_size;	sector_t sector = base_bio->bi_sector - io->target->begin;	atomic_inc(&io->pending);	crypt_convert_init(cc, &ctx, NULL, base_bio, sector, 1);	/*	 * The allocated buffers can be smaller than the whole bio,	 * so repeat the whole process until all the data can be handled.	 */	while (remaining) {		clone = crypt_alloc_buffer(io, remaining);		if (unlikely(!clone)) {			crypt_dec_pending(io, -ENOMEM);			return;		}		ctx.bio_out = clone;		ctx.idx_out = 0;		if (unlikely(crypt_convert(cc, &ctx) < 0)) {			crypt_free_buffer_pages(cc, clone);			bio_put(clone);			crypt_dec_pending(io, -EIO);			return;		}		/* crypt_convert should have filled the clone bio */		BUG_ON(ctx.idx_out < clone->bi_vcnt);		clone->bi_sector = cc->start + sector;		remaining -= clone->bi_size;		sector += bio_sectors(clone);		/* Grab another reference to the io struct		 * before we kick off the request */		if (remaining)			atomic_inc(&io->pending);		generic_make_request(clone);		/* Do not reference clone after this - it		 * may be gone already. */		/* out of memory -> run queues */		if (remaining)			congestion_wait(WRITE, HZ/100);	}}static void process_read_endio(struct dm_crypt_io *io){	struct crypt_config *cc = io->target->private;	struct convert_context ctx;	crypt_convert_init(cc, &ctx, io->base_bio, io->base_bio,			   io->base_bio->bi_sector - io->target->begin, 0);	crypt_dec_pending(io, crypt_convert(cc, &ctx));}static void kcryptd_do_work(struct work_struct *work){	struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);	if (bio_data_dir(io->base_bio) == READ)		process_read(io);}static void kcryptd_do_crypt(struct work_struct *work){	struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);	if (bio_data_dir(io->base_bio) == READ)		process_read_endio(io);	else		process_write(io);}/* * Decode key from its hex representation */static int crypt_decode_key(u8 *key, char *hex, unsigned int size){	char buffer[3];	char *endp;	unsigned 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, unsigned int size){	unsigned int i;	for (i = 0; i < size; i++) {		sprintf(hex, "%02x", *key);		hex += 2;		key++;	}}static int crypt_set_key(struct crypt_config *cc, char *key){	unsigned key_size = strlen(key) >> 1;	if (cc->key_size && cc->key_size != key_size)		return -EINVAL;	cc->key_size = key_size; /* initial settings */	if ((!key_size && strcmp(key, "-")) ||	   (key_size && crypt_decode_key(cc->key, key, key_size) < 0))		return -EINVAL;	set_bit(DM_CRYPT_KEY_VALID, &cc->flags);	return 0;}static int crypt_wipe_key(struct crypt_config *cc){	clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);	memset(&cc->key, 0, cc->key_size * sizeof(u8));	return 0;}/* * Construct an encryption mapping: * <cipher> <key> <iv_offset> <dev_path> <start> */static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv){	struct crypt_config *cc;	struct crypto_blkcipher *tfm;	char *tmp;	char *cipher;	char *chainmode;	char *ivmode;	char *ivopts;	unsigned int key_size;	unsigned long long tmpll;	if (argc != 5) {		ti->error = "Not enough arguments";		return -EINVAL;	}	tmp = argv[0];	cipher = strsep(&tmp, "-");	chainmode = strsep(&tmp, "-");	ivopts = strsep(&tmp, "-");	ivmode = strsep(&ivopts, ":");	if (tmp)		DMWARN("Unexpected additional cipher options");	key_size = strlen(argv[1]) >> 1; 	cc = kzalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);	if (cc == NULL) {		ti->error =			"Cannot allocate transparent encryption context";		return -ENOMEM;	} 	if (crypt_set_key(cc, argv[1])) {		ti->error = "Error decoding key";		goto bad_cipher;	}	/* Compatiblity mode for old dm-crypt cipher strings */	if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {		chainmode = "cbc";		ivmode = "plain";	}	if (strcmp(chainmode, "ecb") && !ivmode) {		ti->error = "This chaining mode requires an IV mechanism";		goto bad_cipher;	}	if (snprintf(cc->cipher, CRYPTO_MAX_ALG_NAME, "%s(%s)",		     chainmode, cipher) >= CRYPTO_MAX_ALG_NAME) {		ti->error = "Chain mode + cipher name is too long";		goto bad_cipher;	}	tfm = crypto_alloc_blkcipher(cc->cipher, 0, CRYPTO_ALG_ASYNC);	if (IS_ERR(tfm)) {		ti->error = "Error allocating crypto tfm";		goto bad_cipher;	}	strcpy(cc->cipher, cipher);	strcpy(cc->chainmode, chainmode);	cc->tfm = tfm;	/*	 * Choose ivmode. Valid modes: "plain", "essiv:<esshash>", "benbi".	 * See comments at iv code	 */	if (ivmode == NULL)		cc->iv_gen_ops = NULL;	else if (strcmp(ivmode, "plain") == 0)		cc->iv_gen_ops = &crypt_iv_plain_ops;	else if (strcmp(ivmode, "essiv") == 0)		cc->iv_gen_ops = &crypt_iv_essiv_ops;	else if (strcmp(ivmode, "benbi") == 0)		cc->iv_gen_ops = &crypt_iv_benbi_ops;	else if (strcmp(ivmode, "null") == 0)		cc->iv_gen_ops = &crypt_iv_null_ops;	else {		ti->error = "Invalid IV mode";		goto bad_ivmode;	}	if (cc->iv_gen_ops && cc->iv_gen_ops->ctr &&	    cc->iv_gen_ops->ctr(cc, ti, ivopts) < 0)		goto bad_ivmode;	cc->iv_size = crypto_blkcipher_ivsize(tfm);	if (cc->iv_size)		/* at least a 64 bit sector number should fit in our buffer */		cc->iv_size = max(cc->iv_size,				  (unsigned int)(sizeof(u64) / sizeof(u8)));	else {		if (cc->iv_gen_ops) {			DMWARN("Selected cipher does not support IVs");			if (cc->iv_gen_ops->dtr)				cc->iv_gen_ops->dtr(cc);			cc->iv_gen_ops = NULL;		}	}	cc->io_pool = mempool_create_slab_pool(MIN_IOS, _crypt_io_pool);	if (!cc->io_pool) {		ti->error = "Cannot allocate crypt io mempool";		goto bad_slab_pool;	}	cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0);	if (!cc->page_pool) {		ti->error = "Cannot allocate page mempool";		goto bad_page_pool;	}	cc->bs = bioset_create(MIN_IOS, MIN_IOS);	if (!cc->bs) {		ti->error = "Cannot allocate crypt bioset";		goto bad_bs;	}	if (crypto_blkcipher_setkey(tfm, cc->key, key_size) < 0) {		ti->error = "Error setting key";		goto bad_device;	}	if (sscanf(argv[2], "%llu", &tmpll) != 1) {		ti->error = "Invalid iv_offset sector";		goto bad_device;	}	cc->iv_offset = tmpll;	if (sscanf(argv[4], "%llu", &tmpll) != 1) {		ti->error = "Invalid device sector";		goto bad_device;	}	cc->start = tmpll;	if (dm_get_device(ti, argv[3], cc->start, ti->len,			  dm_table_get_mode(ti->table), &cc->dev)) {		ti->error = "Device lookup failed";		goto bad_device;	}	if (ivmode && cc->iv_gen_ops) {		if (ivopts)			*(ivopts - 1) = ':';		cc->iv_mode = kmalloc(strlen(ivmode) + 1, GFP_KERNEL);		if (!cc->iv_mode) {			ti->error = "Error kmallocing iv_mode string";			goto bad_ivmode_string;		}		strcpy(cc->iv_mode, ivmode);	} else		cc->iv_mode = NULL;	cc->io_queue = create_singlethread_workqueue("kcryptd_io");	if (!cc->io_queue) {		ti->error = "Couldn't create kcryptd io queue";		goto bad_io_queue;	}	cc->crypt_queue = create_singlethread_workqueue("kcryptd");	if (!cc->crypt_queue) {		ti->error = "Couldn't create kcryptd queue";		goto bad_crypt_queue;	}	ti->private = cc;	return 0;bad_crypt_queue:	destroy_workqueue(cc->io_queue);bad_io_queue:	kfree(cc->iv_mode);bad_ivmode_string:	dm_put_device(ti, cc->dev);bad_device:	bioset_free(cc->bs);bad_bs:	mempool_destroy(cc->page_pool);bad_page_pool:	mempool_destroy(cc->io_pool);bad_slab_pool:	if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)		cc->iv_gen_ops->dtr(cc);bad_ivmode:	crypto_free_blkcipher(tfm);bad_cipher:	/* Must zero key material before freeing */	memset(cc, 0, sizeof(*cc) + cc->key_size * sizeof(u8));	kfree(cc);	return -EINVAL;}static void crypt_dtr(struct dm_target *ti){	struct crypt_config *cc = (struct crypt_config *) ti->private;	destroy_workqueue(cc->io_queue);	destroy_workqueue(cc->crypt_queue);	bioset_free(cc->bs);	mempool_destroy(cc->page_pool);	mempool_destroy(cc->io_pool);	kfree(cc->iv_mode);	if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)		cc->iv_gen_ops->dtr(cc);	crypto_free_blkcipher(cc->tfm);	dm_put_device(ti, cc->dev);	/* Must zero key material before freeing */	memset(cc, 0, sizeof(*cc) + cc->key_size * sizeof(u8));	kfree(cc);}static int crypt_map(struct dm_target *ti, struct bio *bio,		     union map_info *map_context){	struct crypt_config *cc = ti->private;	struct dm_crypt_io *io;	io = mempool_alloc(cc->io_pool, GFP_NOIO);	io->target = ti;	io->base_bio = bio;	io->error = 0;	atomic_set(&io->pending, 0);	if (bio_data_dir(io->base_bio) == READ)		kcryptd_queue_io(io);	else		kcryptd_queue_crypt(io);	return DM_MAPIO_SUBMITTED;}static int crypt_status(struct dm_target *ti, status_type_t type,			char *result, unsigned int maxlen){	struct crypt_config *cc = (struct crypt_config *) ti->private;	unsigned int sz = 0;	switch (type) {	case STATUSTYPE_INFO:		result[0] = '\0';		break;	case STATUSTYPE_TABLE:		if (cc->iv_mode)			DMEMIT("%s-%s-%s ", cc->cipher, cc->chainmode,			       cc->iv_mode);		else			DMEMIT("%s-%s ", cc->cipher, cc->chainmode);		if (cc->key_size > 0) {			if ((maxlen - sz) < ((cc->key_size << 1) + 1))				return -ENOMEM;			crypt_encode_key(result + sz, cc->key, cc->key_size);			sz += cc->key_size << 1;		} else {			if (sz >= maxlen)				return -ENOMEM;			result[sz++] = '-';		}		DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,				cc->dev->name, (unsigned long long)cc->start);		break;	}	return 0;}static void crypt_postsuspend(struct dm_target *ti){	struct crypt_config *cc = ti->private;	set_bit(DM_CRYPT_SUSPENDED, &cc->flags);}static int crypt_preresume(struct dm_target *ti){	struct crypt_config *cc = ti->private;	if (!test_bit(DM_CRYPT_KEY_VALID, &cc->flags)) {		DMERR("aborting resume - crypt key is not set.");		return -EAGAIN;	}	return 0;}static void crypt_resume(struct dm_target *ti){	struct crypt_config *cc = ti->private;	clear_bit(DM_CRYPT_SUSPENDED, &cc->flags);}/* Message interface *	key set <key> *	key wipe */static int crypt_message(struct dm_target *ti, unsigned argc, char **argv){	struct crypt_config *cc = ti->private;	if (argc < 2)		goto error;	if (!strnicmp(argv[0], MESG_STR("key"))) {		if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {			DMWARN("not suspended during key manipulation.");			return -EINVAL;		}		if (argc == 3 && !strnicmp(argv[1], MESG_STR("set")))			return crypt_set_key(cc, argv[2]);		if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe")))			return crypt_wipe_key(cc);	}error:	DMWARN("unrecognised message received.");	return -EINVAL;}static struct target_type crypt_target = {	.name   = "crypt",	.version= {1, 5, 0},	.module = THIS_MODULE,	.ctr    = crypt_ctr,	.dtr    = crypt_dtr,	.map    = crypt_map,	.status = crypt_status,	.postsuspend = crypt_postsuspend,	.preresume = crypt_preresume,	.resume = crypt_resume,	.message = crypt_message,};static int __init dm_crypt_init(void){	int r;	_crypt_io_pool = KMEM_CACHE(dm_crypt_io, 0);	if (!_crypt_io_pool)		return -ENOMEM;	r = dm_register_target(&crypt_target);	if (r < 0) {		DMERR("register failed %d", r);		kmem_cache_destroy(_crypt_io_pool);	}	return r;}static void __exit dm_crypt_exit(void){	int r = dm_unregister_target(&crypt_target);	if (r < 0)		DMERR("unregister failed %d", r);	kmem_cache_destroy(_crypt_io_pool);}module_init(dm_crypt_init);module_exit(dm_crypt_exit);MODULE_AUTHOR("Christophe Saout <christophe@saout.de>");MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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