A RAM Disk device driver for the Linux Kernel which allocates a chunk of memory and presents it as a block device.
- /*
- * A RAM Disk device driver for the Linux Kernel which allocates a chunk of
- * memory and presents it as a block device.
- *
- * Based on sbd.c by Pat Patterson at blog.superpat.com
- * Also uses file I/O functions based on some other drivers
- */
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/kernel.h> /* printk() */
- #include <linux/slab.h> /* kmalloc() */
- #include <linux/fs.h> /* everything... */
- #include <linux/errno.h> /* error codes */
- #include <linux/timer.h>
- #include <linux/types.h> /* size_t */
- #include <linux/vmalloc.h> /* vmalloc() */
- #include <linux/fcntl.h> /* O_ACCMODE */
- #include <linux/hdreg.h> /* HDIO_GETGEO */
- #include <linux/kdev_t.h>
- #include <linux/vmalloc.h>
- #include <linux/genhd.h>
- #include <linux/blkdev.h>
- #include <linux/buffer_head.h> /* invalidate_bdev */
- #include <linux/bio.h>
- #include <linux/crypto.h>
- MODULE_LICENSE("GPL");
- #define PREFIX KERN_DEBUG "##NEWMOD: "
- /* minor number and partition management */
- #define NEWMOD_MINORS 16
- #define MINOR_SHIFT 4
- #define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
- /* Default crypto key */
- #define DEFAULT_KEY "1234567890123456"
- #define DEFAULT_KEY_LEN 16
- /* Data file for loading/unloading data */
- #define DATA_FILE "/newmod_data"
- /* device major number */
- static int newmod_major = 0;
- module_param(newmod_major, int, 0);
- /* sector size (in bytes) */
- static int logical_block_size = 512;
- module_param(logical_block_size, int, 0);
- /* number of sectors (1024 * 512 = 524288 = ~524 kB*/
- static int nsectors = 1024;
- module_param(nsectors, int, 0);
- /* We can tweak our hardware sector size, but the kernel talks to us
- * in terms of small sectors, always. */
- #define KERNEL_SECTOR_SIZE 512
- /* Our request queue */
- static struct request_queue *Queue;
- /* the device data */
- static struct newmod_device {
- unsigned long size; /* device size in sectors */
- u8 *data; /* the data array */
- spinlock_t lock; /* for mutual exclusion */
- struct gendisk *gd; /* the gendisk structure */
- } Device;
- /******************************** CRITICAL NOTE ********************************
- *
- * The crypto key must be 16 bytes long. Any shorter and crypto_cipher_setkey
- * Will return error. Keylen must be 16 bytes.
- *
- ******************************************************************************/
- struct crypto_cipher *tfm;
- static char *key = DEFAULT_KEY;
- module_param(key, charp, 0);
- static int keylen = DEFAULT_KEY_LEN;
- module_param(keylen, int, 0);
- /* Handle an I/O request */
- static void newmod_transfer(struct newmod_device *dev, sector_t sector,
- unsigned long nsect, char *buffer, int write) {
- /* nsect is the number of sectors we're reading/writing,
- * sector is the sector we're starting from. */
- unsigned long offset = sector * logical_block_size;
- unsigned long nbytes = nsect * logical_block_size;
- int i, err;
- /* check that we're reading within bound */
- if ((offset + nbytes) > dev->size) {
- printk(PREFIX "Transfer: too far. offset: %ld. nbytes: %ld.\n",
- offset, nbytes);
- return;
- }
- /* returns an error code or 0 on success */
- err = crypto_cipher_setkey(tfm, key, keylen);
- printk(PREFIX "setting key returned <%d>\n", err);
- /*
- * If we're writing, copy data from buffer into device data.
- * If we're reading, copy device data into buffer.
- *
- * To use the crypto function calls, just copy one block at
- * a time into the dextination from the source, until the
- * number of transfered bytes has reached the desired amount
- */
- if (write) {
- printk(PREFIX "Transfer: Writing %lu bytes.\n", nbytes);
- // memcpy(dev->data + offset, buffer, nbytes);
- for (i = 0; i < nbytes; i += crypto_cipher_blocksize(tfm)) {
- /* Using tfm, encrypt data from source and put it in
- * destination. The crypto function encrypts data
- * in blocks that aren't 1 byte each.
- * crypto_cipher_encrypt_one returns nothing. */
- crypto_cipher_encrypt_one(
- tfm, /* crypto info */
- dev->data + offset + i, /* destination */
- buffer + i /* source */
- );
- }
- } else {
- printk(PREFIX "Transfer: Reading %lu bytes.\n", nbytes);
- // memcpy(buffer, dev->data + offset, nbytes);
- for (i = 0; i < nbytes; i += crypto_cipher_blocksize(tfm)) {
- crypto_cipher_decrypt_one(
- tfm, /* crypto data */
- buffer + i, /* destination */
- dev->data + offset + i /* source */
- );
- }
- }
- printk(PREFIX "Transfer: done.\n");
- /* debug code */
- /* {
- unsigned long len;
- u8 *dst;
- u8 *src;
- len = nbytes;
- if (write) {
- dst = dev->data + offset;
- src = buffer;
- } else {
- dst = buffer;
- src = dev->data + offset;
- }
- src = dev->data + offset;
- dst = buffer;
- printk("Transfer: dev->data data:\n");
- len = nbytes;
- while(len--)
- printk("%u", (unsigned)*src++);
- printk("Transfer: buffer data:\n");
- len = nbytes;
- while(len--)
- printk("%u", (unsigned)*dst++);
- printk("transferred.\n"
- }
- */
- /* print the data that was transferred, and from where */
- // printk(PREFIX "Data in device:\n");
- // for (i = 0; i < nbytes; i++)
- // printk(PREFIX " Byte %d: %c\n", i, (unsigned char)(dev->data + offset + i));
- // printk(PREFIX "Data in buffer:\n");
- // for (i = 0; i < nbytes; i++)
- // printk(PREFIX " Byte %d: %c\n", i, (unsigned char)(buffer + i));
- }
- /* handle a request */
- static void newmod_request(struct request_queue *q) {
- struct request *req;
- printk(PREFIX "request: Handling requests\n");
- /* fetch the request at the top of queue */
- req = blk_fetch_request(q);
- /* iterate through every request in the queue */
- while (req != NULL) {
- /* if we have no request or a request of the wrong type,
- * skip it and continue */
- if ((req == NULL) || (req->cmd_type != REQ_TYPE_FS)) {
- printk(PREFIX "request: Received non-fs request\n");
- __blk_end_request_all(req, -EIO);
- req = blk_fetch_request(q);
- continue;
- }
- /* call my data transfer function */
- newmod_transfer(&Device, /* my device data */
- blk_rq_pos(req), /* request sector */
- blk_rq_cur_sectors(req),/* number of sectors */
- req->buffer, /* buffer to fill */
- rq_data_dir(req)); /* read/write? */
- printk(PREFIX "request: did a transfer.\n");
- /* End current request, then fetch the next one.
- * __blk_end_request_cur returns 0 on success */
- if (!__blk_end_request_cur(req, 0))
- req = blk_fetch_request(q);
- }
- printk(PREFIX "request: finished all the requests.\n");
- }
- /*
- * The HDIO_GETGEO ioctl is handled in blkdev_ioctl(), which
- * calls this. We need to implement getgeo, since we can't
- * use tools such as fdisk to partition the drive otherwise.
- */
- int newmod_getgeo(struct block_device * block_device,
- struct hd_geometry * geo) {
- long size;
- printk(PREFIX "getgeo: Making stuff up.\n");
- /* make stuff up */
- size = Device.size * (logical_block_size * KERNEL_SECTOR_SIZE);
- geo->cylinders = (size & ~0x3f) >> 6;
- geo->heads = 4;
- geo->sectors = 16;
- geo->start = 0;
- return 0;
- }
- /* define the file operations */
- static struct block_device_operations newmod_ops = {
- .owner = THIS_MODULE,
- .getgeo = newmod_getgeo
- };
- /* Initialize the device and announce it to the kernel */
- static int __init newmod_init(void)
- {
- /* variables for file I/O */
- mm_segment_t oldfs;
- struct file *filp = NULL;
- unsigned long long offset = 0;
- ssize_t read_size;
- int ret;
- printk(PREFIX "init: start\n");
- /* register the block device with the value given */
- newmod_major = register_blkdev(newmod_major, "newmod");
- /* register_blkdev() returns negative on failure, whether the input is
- * zero or a requested major number. */
- if (newmod_major < 0) {
- printk(PREFIX "INIT: failed to register blockdev\n");
- return -EBUSY;
- }
- /* setup the device. There is only one. */
- memset(&Device, 0, sizeof(struct newmod_device));
- Device.size = nsectors * logical_block_size;
- Device.data = vmalloc(Device.size);
- memset(Device.data, 0, Device.size);
- /* check allocation */
- if (Device.data == NULL) {
- printk(PREFIX "Init: Failed to allocate Device.data\n");
- unregister_blkdev(newmod_major, "newmod");
- return -ENOMEM;
- }
- printk(PREFIX "Init: device size is %ld.\n", Device.size);
- /* Now copy the device data from a file. If it doesn't exist, create
- * it. */
- oldfs = get_fs();
- set_fs(get_ds());
- filp = filp_open(DATA_FILE, O_RDONLY | O_CREAT, S_IRWXUGO);
- printk(PREFIX "init: attempted to open <%s>.\n", DATA_FILE);
- /* IS_ERR checks nor NULL and also error codes. If we failed to read
- * the data from the previous run of this module, we can just blow it
- * all away and start over with fresh data. It's not that bad */
- if (IS_ERR(filp)) {
- printk(PREFIX "Init: Failed to open file <%s> for reading.\n",
- DATA_FILE);
- set_fs(oldfs);
- } else {
- /* read as many bytes from the file as will fit in our data buffer */
- read_size = vfs_read(filp, Device.data, Device.size, &offset);
- printk(PREFIX "Init: Read from file returned %d. offset: %llu.\n",
- read_size, offset);
- /* close the file after we're done */
- set_fs(oldfs);
- ret = filp_close(filp, 0);
- printk(PREFIX "Init: Closed file, returned %d.\n", ret);
- }
- /* initialize spinlock. Can't find the documentation. */
- spin_lock_init(&Device.lock);
- /* Prepare the request queue for use with this block device. The
Learn More :
Chunk
Disk
Driver
Kernel
Linux