Created
January 19, 2014 23:55
-
-
Save RavuAlHemio/8512684 to your computer and use it in GitHub Desktop.
secvault kernel module
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/fs.h> | |
#include <linux/device.h> | |
#include <linux/cdev.h> | |
#include <linux/semaphore.h> | |
#include <linux/slab.h> | |
#include <linux/uaccess.h> | |
#include <linux/thread_info.h> | |
#include <linux/sched.h> | |
#include "../svinterface.h" | |
/* | |
CONSTANT DECLARATIONS | |
*/ | |
/** Maximum number of vaults to supply. */ | |
#define MAX_NUM_VAULTS 4 | |
/** Maximum size of one vault. */ | |
#define MAX_VAULT_SIZE 1048576 | |
/** Name format (%zu) of the vaults themselves. */ | |
#define DEVNAME_VAULT "sv_data%zu" | |
/* | |
MODULE METADATA | |
*/ | |
MODULE_LICENSE("Dual MIT/GPL"); /* quite possibly the most liberal combination */ | |
MODULE_AUTHOR("Ondrej Hosek <[email protected]>"); | |
MODULE_DESCRIPTION("Secure Vault"); | |
MODULE_VERSION("0.0.1"); | |
/* | |
MODULE PARAMETERS | |
*/ | |
static int debug; | |
module_param(debug, bool, 0); | |
MODULE_PARM_DESC(debug, "Log debugging messages to dmesg."); | |
/* | |
FUNCTION PROTOTYPES | |
*/ | |
static int io_succeed(struct inode *n, struct file *f); | |
static long svctl_ioctl(struct file *f, unsigned int num, unsigned long param); | |
static int secvault_open(struct inode *n, struct file *f); | |
ssize_t secvault_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); | |
ssize_t secvault_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos); | |
loff_t secvault_llseek(struct file *f, loff_t offset, int whence); | |
static int take_secvault_offline(size_t vaultnum); | |
static int take_secvault_online(size_t vaultnum, size_t size, char key[SECVAULT_KEY_LEN]); | |
/* | |
STRUCTURES | |
*/ | |
/** File operations for the controller device. */ | |
struct file_operations svctl_fops = { | |
.owner = THIS_MODULE, | |
.open = io_succeed, | |
.release = io_succeed, | |
.unlocked_ioctl = svctl_ioctl | |
}; | |
/** File operations for the vault devices. */ | |
struct file_operations secvault_fops = { | |
.owner = THIS_MODULE, | |
.open = secvault_open, | |
.release = io_succeed, | |
.read = secvault_read, | |
.write = secvault_write, | |
.llseek = secvault_llseek | |
}; | |
/** Metadata holder for the controller device. */ | |
struct svctl_dev | |
{ | |
/** Character device info. */ | |
struct cdev cdev; | |
/** Kernel-created device file in sysfs and devfs. */ | |
struct device *kdevice; | |
/** 1 if the controller device is registered, 0 if not; any other value is junk */ | |
char registered; | |
/** Semaphore for control access. */ | |
struct semaphore sema; | |
}; | |
/** Metadata holder for a vault device. */ | |
struct secvault_dev | |
{ | |
/** Character device info. */ | |
struct cdev cdev; | |
/** Kernel-created device file in sysfs and devfs. */ | |
struct device *kdevice; | |
/** 1 if the vault device is registered, 0 if not; any other value is junk */ | |
char registered; | |
/** 1 if this vault is currently online, 0 if not; any other value is junk. */ | |
char online; | |
/** Semaphore for vault access. */ | |
struct semaphore sema; | |
/** Vault key. */ | |
char key[SECVAULT_KEY_LEN]; | |
/** Current size of the vault. */ | |
size_t size; | |
/** Pointer to vault contents. */ | |
char *contents; | |
/** Owner of the vault. */ | |
uid_t owner; | |
}; | |
/* | |
MODULE SCOPE VARIABLES | |
*/ | |
/** First device number used. */ | |
dev_t devnumber1; | |
/** The control device. */ | |
struct svctl_dev ctldev; | |
/** The individual vault devices. */ | |
struct secvault_dev vaultdevs[MAX_NUM_VAULTS]; | |
/** Device class for secvault in sysfs. */ | |
struct class *secvault_class; | |
/* | |
UTILITY FUNCTIONS | |
*/ | |
/** | |
* printk if debugging is on. | |
* @param fmt Format string. | |
* @return Number of bytes printed. | |
*/ | |
static __attribute__((format(printf, 1, 2))) int dprintk(const char *fmt, ...) | |
{ | |
va_list args; | |
int r; | |
if (debug == 0) | |
return 0; | |
va_start(args, fmt); | |
r = vprintk(fmt, args); | |
va_end(args); | |
return r; | |
} | |
/** | |
* Naïve implementation of memcpy. | |
* @param dest Destination pointer. | |
* @param src Source pointer. | |
* @param n Number of bytes to copy from src to dest. | |
* @return dest | |
*/ | |
static inline void *mymemcpy(void *dest, const void *src, size_t n) | |
{ | |
__u8 *d = (__u8 *)dest; | |
const __u8 *s = (const __u8 *)src; | |
size_t i; | |
for (i = 0; i < n; ++i) | |
{ | |
d[i] = s[i]; | |
} | |
return dest; | |
} | |
/** | |
* Naïve implementation of memset. | |
* @param s Pointer to memory to set. | |
* @param c Character to set to. Truncated to 8 bits. | |
* @param n Number of bytes to set. | |
* @return s | |
*/ | |
static inline void *mymemset(void *s, int c, size_t n) | |
{ | |
__u8 *d = (__u8 *)s; | |
__u8 b = (__u8)c; | |
size_t i; | |
for (i = 0; i < n; ++i) | |
{ | |
d[i] = b; /* ZIIIIM! */ | |
} | |
return s; | |
} | |
/** | |
* Convert device number to vault ID. | |
* @param devnum Device number. | |
* @return Ordinal vault ID. | |
*/ | |
static inline size_t device_to_vault(dev_t devnum) | |
{ | |
return devnum - devnumber1 - 1; /* skip the control device */ | |
} | |
/* | |
GENERAL DEVICE I/O | |
*/ | |
/** | |
* open() and release() call that does nothing and returns successfully. | |
*/ | |
static int io_succeed(struct inode *n, struct file *f) | |
{ | |
/* nothing to see here, move along */ | |
return 0; | |
} | |
/* | |
VAULT DEVICE I/O | |
*/ | |
/** | |
* Open a vault (storing its ID in the file structure). | |
*/ | |
static int secvault_open(struct inode *n, struct file *f) | |
{ | |
size_t vaultnum = device_to_vault(n->i_rdev); | |
struct secvault_dev *v; | |
dprintk( | |
KERN_INFO "%s: Opening vault device 0x%x; devnumber1 is 0x%x; effective number is 0x%zx.", | |
__func__, | |
n->i_rdev, | |
devnumber1, | |
vaultnum | |
); | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
printk(KERN_ERR "%s: Vault %zu out of bounds.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online != 1) | |
{ | |
dprintk( | |
KERN_ERR "%s: Vault %zu is not online.", | |
__func__, | |
vaultnum | |
); | |
return -ENODEV; | |
} | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
if ( | |
v->owner != current_uid() && | |
v->owner != current_euid() && | |
!capable(CAP_DAC_OVERRIDE) | |
) | |
{ | |
/* access denied */ | |
dprintk( | |
KERN_INFO "%s: Access denied to UID %u EUID %u to vault %zu with owner %u.", | |
__func__, | |
current_uid(), | |
current_euid(), | |
vaultnum, | |
v->owner | |
); | |
up(&v->sema); | |
return -EPERM; | |
} | |
up(&v->sema); | |
f->private_data = (void *)vaultnum; | |
return 0; | |
} | |
/** | |
* Read from a vault. | |
*/ | |
ssize_t secvault_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) | |
{ | |
size_t vaultnum = (size_t)filp->private_data; | |
size_t vaultsize, i, endpos, bytesread = 0; | |
struct secvault_dev *v; | |
char *kernbuf; | |
/* sanity checking */ | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
printk(KERN_ERR "%s: Vault %zu out of bounds.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online != 1) | |
{ | |
dprintk(KERN_ERR "%s: Vault %zu unavailable.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
/* check position */ | |
vaultsize = v->size; | |
if (*f_pos >= vaultsize) | |
{ | |
/* reached EOF */ | |
up(&v->sema); | |
return 0; | |
} | |
/* allocate buffer */ | |
endpos = *f_pos + count; | |
kernbuf = kmalloc(count, GFP_KERNEL); | |
if (IS_ERR(kernbuf)) | |
{ | |
printk(KERN_ERR "%s: Cannot allocate buffer.", __func__); | |
up(&v->sema); | |
return -ENOMEM; | |
} | |
for (i = *f_pos; i < vaultsize && i < endpos; ++i) | |
{ | |
/* decryption routine */ | |
kernbuf[i] = v->contents[i] ^ v->key[i % 10]; | |
++bytesread; | |
} | |
/* copy buffer to userspace */ | |
if (copy_to_user( | |
buf, | |
kernbuf, | |
bytesread | |
) != 0) | |
{ | |
printk(KERN_ERR "%s: Failed to copy bytes.", __func__); | |
kfree(kernbuf); | |
up(&v->sema); | |
return -ENOMEM; | |
} | |
kfree(kernbuf); | |
up(&v->sema); | |
*f_pos += bytesread; | |
return bytesread; | |
} | |
/** | |
* Write to a vault. | |
*/ | |
ssize_t secvault_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) | |
{ | |
size_t vaultnum = (size_t)filp->private_data; | |
size_t vaultsize, i, endpos, byteswritten = 0; | |
struct secvault_dev *v; | |
char *kernbuf; | |
/* sanity checking */ | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
printk(KERN_ERR "%s: Vault %zu out of bounds.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online != 1) | |
{ | |
dprintk(KERN_ERR "%s: Vault %zu unavailable.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
/* check position */ | |
vaultsize = v->size; | |
if (*f_pos >= vaultsize) | |
{ | |
/* no space left */ | |
up(&v->sema); | |
return -ENOSPC; | |
} | |
/* allocate buffer */ | |
endpos = *f_pos + count; | |
kernbuf = kmalloc(count, GFP_KERNEL); | |
if (IS_ERR(kernbuf)) | |
{ | |
printk(KERN_ERR "%s: Cannot allocate buffer.", __func__); | |
up(&v->sema); | |
return -ENOMEM; | |
} | |
/* copy buffer from userspace */ | |
if (copy_from_user( | |
kernbuf, | |
buf, | |
count | |
) != 0) | |
{ | |
printk(KERN_ERR "%s: Failed to copy bytes.", __func__); | |
kfree(kernbuf); | |
up(&v->sema); | |
return -ENOMEM; | |
} | |
for (i = *f_pos; i < vaultsize && i < endpos; ++i) | |
{ | |
/* encryption routine */ | |
v->contents[i] = kernbuf[i] ^ v->key[i % 10]; | |
++byteswritten; | |
} | |
kfree(kernbuf); | |
up(&v->sema); | |
*f_pos += byteswritten; | |
return byteswritten; | |
} | |
/** | |
* Seek within a vault. Code mostly stolen from default_llseek. | |
*/ | |
loff_t secvault_llseek(struct file *f, loff_t offset, int whence) | |
{ | |
size_t vaultnum = (size_t)f->private_data; | |
struct secvault_dev *v; | |
loff_t newoff = -1; | |
/* sanity checking */ | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
printk(KERN_ERR "%s: Vault %zu out of bounds.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online != 1) | |
{ | |
dprintk(KERN_ERR "%s: Vault %zu unavailable.", __func__, vaultnum); | |
return -ENODEV; | |
} | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
switch (whence) | |
{ | |
case SEEK_SET: | |
newoff = offset; | |
break; | |
case SEEK_END: | |
newoff = offset + v->size; | |
break; | |
case SEEK_CUR: | |
newoff = offset + f->f_pos; | |
break; | |
#ifdef SEEK_DATA | |
case SEEK_DATA: | |
if (offset > v->size) | |
{ | |
up(&v->sema); | |
return -ENXIO; | |
} | |
newoff = offset; | |
break; | |
#endif | |
#ifdef SEEK_HOLE | |
case SEEK_HOLE: | |
if (offset > v->size) | |
{ | |
up(&v->sema); | |
return -ENXIO; | |
} | |
newoff = v->size; | |
break; | |
#endif | |
} | |
if (newoff >= 0) | |
{ | |
if (newoff != f->f_pos) | |
{ | |
f->f_pos = newoff; | |
f->f_version = 0; | |
} | |
up(&v->sema); | |
return newoff; | |
} | |
up(&v->sema); | |
return -EINVAL; | |
} | |
/* | |
CONTROL DEVICE I/O | |
*/ | |
/** | |
* ioctl on control file. | |
*/ | |
static long svctl_ioctl(struct file *f, unsigned int num, unsigned long param) | |
{ | |
switch (num) | |
{ | |
case SECVAULT_IOC_CREATE: | |
{ | |
struct secvault_iocs_create paramstruct; | |
if (copy_from_user( | |
¶mstruct, | |
(const void __user *)param, | |
sizeof(paramstruct) | |
) != 0) | |
{ | |
printk( | |
KERN_WARNING "%s: Failed to copy bytes from ioctl param.", | |
__func__ | |
); | |
return -EFAULT; | |
} | |
return take_secvault_online(paramstruct.vaultnum, paramstruct.size, paramstruct.key); | |
} | |
case SECVAULT_IOC_CLEAR: | |
{ | |
size_t vaultnum = (size_t)param; | |
struct secvault_dev *v; | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Tried to clear vault %zu; the highest vault number is %d.", | |
__func__, | |
vaultnum, | |
MAX_NUM_VAULTS-1 | |
); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online != 1) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Vault %zu is currently not online.\n", | |
__func__, | |
vaultnum | |
); | |
return -ENODEV; | |
} | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
mymemset(v->contents, 0x00, v->size); | |
up(&v->sema); | |
return 0; | |
} | |
case SECVAULT_IOC_REMOVE: | |
return take_secvault_offline((size_t)param); | |
default: | |
dprintk( | |
KERN_WARNING "%s: Unknown ioctl %u on control file.", | |
__func__, | |
num | |
); | |
return -ENOIOCTLCMD; | |
} | |
return -ENOIOCTLCMD; | |
} | |
/* | |
VAULT MANAGEMENT | |
*/ | |
/** | |
* Take the specified secvault offline. | |
* @param vaultnum Number of the vault to take online. | |
* @return Zero on success, negative number on error. | |
*/ | |
static int take_secvault_offline(size_t vaultnum) | |
{ | |
struct secvault_dev *v; | |
if (down_interruptible(&ctldev.sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Tried to take vault %zu offline; the highest vault number is %d.", | |
__func__, | |
vaultnum, | |
MAX_NUM_VAULTS-1 | |
); | |
up(&ctldev.sema); | |
return -ENODEV; | |
} | |
v = &vaultdevs[vaultnum]; | |
if (v->online) | |
{ | |
if (down_interruptible(&v->sema) == -EINTR) | |
{ | |
up(&ctldev.sema); | |
return -ERESTARTSYS; | |
} | |
} | |
/* alright, let's do this */ | |
v->online = 0; | |
if (v->kdevice != NULL) | |
{ | |
dprintk(KERN_DEBUG "%s: destroying device for vault %zu.", __func__, vaultnum); | |
device_destroy(secvault_class, devnumber1+1+vaultnum); | |
v->kdevice = NULL; | |
} | |
if (v->registered == 1) | |
{ | |
dprintk(KERN_DEBUG "%s: deleting device for vault %zu.", __func__, vaultnum); | |
cdev_del(&v->cdev); | |
v->registered = 0; | |
} | |
if (v->contents != NULL) | |
{ | |
/* zero out memory before freeing */ | |
(void)mymemset(v->contents, 0x00, v->size); | |
} | |
#ifdef DATA_USING_KMALLOC | |
kfree(v->contents); | |
#else | |
vfree(v->contents); | |
#endif | |
dprintk(KERN_DEBUG "%s: freeing space of vault %zu.", __func__, vaultnum); | |
v->contents = NULL; | |
v->size = 0; | |
mymemset(v->key, 0x00, SECVAULT_KEY_LEN); | |
dprintk(KERN_INFO "%s: Vault %zu taken offline.", __func__, vaultnum); | |
up(&ctldev.sema); | |
return 0; | |
} | |
/** | |
* Take the specified secvault online. | |
* @param vaultnum Number of the vault to take online. | |
* @param size Size, in bytes, of the new vault. | |
* @param key Key to access vault. | |
* @return Zero on success, negative number on error. | |
*/ | |
static int take_secvault_online(size_t vaultnum, size_t size, char key[SECVAULT_KEY_LEN]) | |
{ | |
dev_t mydevnumber; | |
struct secvault_dev *v; | |
if (down_interruptible(&ctldev.sema) == -EINTR) | |
{ | |
return -ERESTARTSYS; | |
} | |
if (vaultnum >= MAX_NUM_VAULTS) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Tried to take vault %zu online; the highest vault number is %d.", | |
__func__, | |
vaultnum, | |
MAX_NUM_VAULTS-1 | |
); | |
up(&ctldev.sema); | |
return -ENODEV; | |
} | |
if (vaultdevs[vaultnum].online == 1) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Tried to take vault %zu online; it already is.", | |
__func__, | |
vaultnum | |
); | |
up(&ctldev.sema); | |
return -EEXIST; | |
} | |
else if (vaultdevs[vaultnum].online != 0) | |
{ | |
dprintk( | |
KERN_ERR "%s: Vault %zu online state (%hhu) unknown.", | |
__func__, | |
vaultnum, | |
vaultdevs[vaultnum].online | |
); | |
up(&ctldev.sema); | |
return -EBUSY; | |
} | |
#if MAX_VAULT_SIZE != 0 | |
if (size > MAX_VAULT_SIZE) | |
{ | |
dprintk( | |
KERN_WARNING "%s: Tried to create vault %zu bytes long; the maximum allowed size is %d bytes.", | |
__func__, | |
size, | |
MAX_VAULT_SIZE | |
); | |
up(&ctldev.sema); | |
return -EFBIG; | |
} | |
#endif | |
/* okay, sanity checking's done; create the vault. */ | |
v = &vaultdevs[vaultnum]; | |
/* init semaphore */ | |
sema_init(&v->sema, 0); | |
/* copy key */ | |
(void)mymemcpy(v->key, key, SECVAULT_KEY_LEN); | |
/* copy size */ | |
v->size = size; | |
/* set owner */ | |
v->owner = current_uid(); | |
/* allocate contents */ | |
#ifdef DATA_USING_KMALLOC | |
v->contents = kmalloc(v->size, GFP_KERNEL); | |
#else | |
v->contents = vmalloc(v->size); | |
#endif | |
dprintk(KERN_DEBUG "%s: allocating space for vault %zu.", __func__, vaultnum); | |
if (IS_ERR(v->contents)) | |
{ | |
printk( | |
KERN_ERR "%s: Failed to allocate %zu bytes for vault %zu.", | |
__func__, | |
v->size, | |
vaultnum | |
); | |
up(&ctldev.sema); | |
up(&v->sema); | |
take_secvault_offline(vaultnum); | |
return -ENOMEM; | |
} | |
mydevnumber = devnumber1 + 1 + vaultnum; | |
/* prepare the vault device */ | |
dprintk(KERN_DEBUG "%s: adding device for vault %zu.", __func__, vaultnum); | |
cdev_init(&v->cdev, &secvault_fops); | |
if (cdev_add(&v->cdev, mydevnumber, 1) != 0) | |
{ | |
printk( | |
KERN_ERR "%s: Failed to add vault %zu device to system.", | |
__func__, | |
vaultnum | |
); | |
up(&ctldev.sema); | |
up(&v->sema); | |
take_secvault_offline(vaultnum); | |
return -ENODEV; | |
} | |
v->registered = 1; | |
/* create the ctl device file */ | |
dprintk(KERN_DEBUG "%s: creating device for vault %zu.", __func__, vaultnum); | |
v->kdevice = device_create( | |
secvault_class, /* class */ | |
NULL, /* parent */ | |
mydevnumber, /* device number */ | |
NULL, /* additional data for callbacks */ | |
DEVNAME_VAULT, /* name (format) */ | |
vaultnum | |
); | |
if (v->kdevice == NULL) | |
{ | |
printk( | |
KERN_ERR "%s: Failed to create vault %zu device file.", | |
__func__, | |
vaultnum | |
); | |
up(&ctldev.sema); | |
up(&v->sema); | |
take_secvault_offline(vaultnum); | |
return -ENODEV; | |
} | |
dprintk(KERN_INFO "%s: Vault %zu taken online.", __func__, vaultnum); | |
v->online = 1; | |
up(&ctldev.sema); | |
up(&v->sema); | |
return 0; | |
} | |
/* | |
MODULE HOUSEKEEPING | |
*/ | |
/** Exit the module. */ | |
static void secvault_mod_exit(void) | |
{ | |
size_t i; | |
for (i = 0; i < MAX_NUM_VAULTS; ++i) | |
{ | |
while (take_secvault_offline(i) == -ERESTARTSYS) | |
; | |
} | |
if (ctldev.kdevice != NULL) | |
{ | |
device_destroy(secvault_class, devnumber1); | |
ctldev.kdevice = NULL; | |
} | |
if (ctldev.registered == 1) | |
{ | |
cdev_del(&ctldev.cdev); | |
ctldev.registered = 0; | |
} | |
if (devnumber1 != 0) | |
{ | |
unregister_chrdev_region(devnumber1, MAX_NUM_VAULTS+1); | |
devnumber1 = 0; | |
} | |
if (secvault_class != NULL) | |
{ | |
class_destroy(secvault_class); | |
secvault_class = NULL; | |
} | |
} | |
/** | |
* Initialize the secvault module. | |
* @return Zero on success, nonzero on failure (with negative error number). | |
*/ | |
static int secvault_mod_init(void) | |
{ | |
int ret = 0; | |
size_t i = 0; | |
/* clear variables. */ | |
devnumber1 = 0; | |
memset(&ctldev, 0x00, sizeof(ctldev)); | |
for (i = 0; i < MAX_NUM_VAULTS; ++i) | |
{ | |
memset(&vaultdevs[i], 0x00, sizeof(struct secvault_dev)); | |
} | |
secvault_class = NULL; | |
/* register device class */ | |
secvault_class = class_create(THIS_MODULE, "secvault"); | |
if (IS_ERR(secvault_class)) | |
{ | |
printk(KERN_ERR "%s: Failed to register secvault device class.", __func__); | |
ret = -ENODEV; | |
goto kablooie; | |
} | |
/* obtain major/minor device numbers */ | |
if (alloc_chrdev_region(&devnumber1, 0, MAX_NUM_VAULTS+1, "secvault") != 0) | |
{ | |
printk(KERN_ERR "%s: Failed to allocate character device numbers.", __func__); | |
ret = -ENODEV; | |
goto kablooie; | |
} | |
/* create ctl device semaphore */ | |
sema_init(&ctldev.sema, 0); | |
/* prepare the ctl device */ | |
cdev_init(&ctldev.cdev, &svctl_fops); | |
if (cdev_add(&ctldev.cdev, devnumber1, 1) != 0) | |
{ | |
printk(KERN_ERR "%s: Failed to add controller device to system.", __func__); | |
ret = -ENODEV; | |
goto kablooie; | |
} | |
ctldev.registered = 1; | |
/* create the ctl device file */ | |
ctldev.kdevice = device_create( | |
secvault_class, /* class */ | |
NULL, /* parent */ | |
devnumber1, /* device number */ | |
NULL, /* additional data for callbacks */ | |
SECVAULT_DEVNAME_CTL /* name (format) */ | |
); | |
if (ctldev.kdevice == NULL) | |
{ | |
printk(KERN_ERR "%s: Failed to create controller device file.", __func__); | |
ret = -ENODEV; | |
goto kablooie; | |
} | |
/* unlock the ctl device semaphore */ | |
up(&ctldev.sema); | |
dprintk(KERN_INFO "%s: Ready.", __func__); | |
return ret; | |
kablooie: | |
/* do standard teardown, which is relatively robust */ | |
secvault_mod_exit(); | |
return ret; | |
} | |
/* | |
ENTRY POINT DECLARATIONS | |
*/ | |
module_init(secvault_mod_init); | |
module_exit(secvault_mod_exit); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment