diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-05-28 11:36:16 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-06-17 20:47:38 +0200 |
commit | bda013fbafabd0d0826441cb40c93a46d4dc2301 (patch) | |
tree | 7420201add2b847d726d539ad11a3af995f0f441 /ncr-data.c | |
parent | e1e9463cca0bd41f56897c14014f714e48afe543 (diff) | |
download | kernel-crypto-bda013fbafabd0d0826441cb40c93a46d4dc2301.tar.gz kernel-crypto-bda013fbafabd0d0826441cb40c93a46d4dc2301.tar.xz kernel-crypto-bda013fbafabd0d0826441cb40c93a46d4dc2301.zip |
Separated data functionality to ncr-data.c.
Diffstat (limited to 'ncr-data.c')
-rw-r--r-- | ncr-data.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/ncr-data.c b/ncr-data.c new file mode 100644 index 00000000000..ea10f8b10ee --- /dev/null +++ b/ncr-data.c @@ -0,0 +1,256 @@ +/* + * New driver for /dev/crypto device (aka CryptoDev) + + * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org> + * + * This file is part of linux cryptodev. + * + * cryptodev 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 3 of the License, or + * (at your option) any later version. + * + * cryptodev 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crypto.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <linux/random.h> +#include "cryptodev.h" +#include <asm/uaccess.h> +#include <asm/ioctl.h> +#include <linux/scatterlist.h> +#include "ncr.h" +#include "ncr_int.h" + +#define err() printk(KERN_DEBUG"ncr: %s: %d\n", __func__, __LINE__) + +static void _ncr_data_item_put( struct data_item* item); + +void ncr_data_list_deinit(struct ncr_lists *lst) +{ + if(lst) { + struct data_item * item, *tmp; + + down(&lst->data_sem); + + list_for_each_entry_safe(item, tmp, &lst->data_list, list) { + list_del(&item->list); + _ncr_data_item_put( item); /* decrement ref count */ + } + up(&lst->data_sem); + + } +} + +/* must be called with data semaphore down + */ +static ncr_data_t _ncr_data_get_new_desc( struct ncr_lists* lst) +{ +struct data_item* item; +int mx = 0; + + list_for_each_entry(item, &lst->data_list, list) { + mx = max(mx, item->desc); + } + mx++; + + return mx; +} + +/* returns the data item corresponding to desc */ +static struct data_item* ncr_data_item_get( struct ncr_lists* lst, ncr_data_t desc) +{ +struct data_item* item; + + down(&lst->data_sem); + list_for_each_entry(item, &lst->data_list, list) { + if (item->desc == desc) { + atomic_inc(&item->refcnt); + up(&lst->data_sem); + return item; + } + } + up(&lst->data_sem); + + err(); + return NULL; +} + +static void* data_alloc(unsigned int uid, size_t size) +{ + /* FIXME: enforce a maximum memory limit per user */ + if (size > 64*1024) { + err(); + return NULL; + } + return kmalloc(size, GFP_KERNEL); +} + +static void data_free(struct data_item * data) +{ + /* FIXME: enforce a maximum memory limit per user */ + kfree(data->data); +} + +static void _ncr_data_item_put( struct data_item* item) +{ + if (atomic_dec_and_test(&item->refcnt)) { + data_free(item); + kfree(item); + } +} + +int ncr_data_new(unsigned int uid, struct ncr_lists* lst, void __user* arg) +{ + struct ncr_data_init_st init; + struct data_item* data; + + copy_from_user( &init, arg, sizeof(init)); + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + err(); + return -ENOMEM; + } + + memset(data, 0, sizeof(*data)); + + data->flags = init.flags; + atomic_set(&data->refcnt, 1); + + data->data = data_alloc(uid, init.max_object_size); + if (data->data == NULL) { + kfree(data); + err(); + return -ENOMEM; + } + data->max_data_size = init.max_object_size; + + down(&lst->data_sem); + + data->desc = _ncr_data_get_new_desc(lst); + data->uid = uid; + + if (init.initial_data != NULL) { + copy_from_user(data->data, init.initial_data, init.initial_data_size); + data->data_size = init.initial_data_size; + } + + list_add(&data->list, &lst->data_list); + + up(&lst->data_sem); + + init.desc = data->desc; + copy_to_user(arg, &init, sizeof(init)); + + return 0; +} + + +int ncr_data_deinit(struct ncr_lists* lst, void __user* arg) +{ + ncr_data_t desc; + struct data_item * item, *tmp; + + copy_from_user( &desc, arg, sizeof(desc)); + + down(&lst->data_sem); + + list_for_each_entry_safe(item, tmp, &lst->data_list, list) { + if(item->desc == desc) { + list_del(&item->list); + _ncr_data_item_put( item); /* decrement ref count */ + break; + } + } + + up(&lst->data_sem); + + return 0; +} + +int ncr_data_get(struct ncr_lists* lst, void __user* arg) +{ + struct ncr_data_st get; + struct data_item * data; + size_t len; + + copy_from_user( &get, arg, sizeof(get)); + + data = ncr_data_item_get( lst, get.desc); + + if (data == NULL) { + err(); + return -EINVAL; + } + + if (!(data->flags & NCR_DATA_FLAG_EXPORTABLE)) { + err(); + return -EPERM; + } + + len = min(get.data_size, data->data_size); + + /* update length */ + get.data_size = len; + copy_to_user(arg, &get, sizeof(get)); + + if (len > 0) + copy_to_user(get.data, data->data, len); + + _ncr_data_item_put( data); + + return 0; +} + +int ncr_data_set(struct ncr_lists* lst, void __user* arg) +{ + struct ncr_data_st get; + struct data_item * data; + int ret; + + copy_from_user( &get, arg, sizeof(get)); + + data = ncr_data_item_get( lst, get.desc); + + if (data == NULL) { + err(); + return -EINVAL; + } + + if ((get.data_size > data->max_data_size) || + (get.data == NULL && get.data_size != 0)) { + err(); + ret = -EINVAL; + goto cleanup; + } + + if (!get.append_flag) { + if (get.data != NULL) + copy_from_user(data->data, get.data, get.data_size); + data->data_size = get.data_size; + } else { + if (get.data_size+data->data_size > data->max_data_size) { + err(); + ret = -EINVAL; + goto cleanup; + } + if (get.data != NULL) + copy_from_user(&data->data[data->data_size], get.data, get.data_size); + data->data_size += get.data_size; + } + ret = 0; + +cleanup: + _ncr_data_item_put( data); + + return ret; +} |