patch-1.3.68 linux/drivers/block/loop.c
Next file: linux/drivers/block/loop.h
Previous file: linux/drivers/block/ll_rw_blk.c
Back to the patch index
Back to the overall index
- Lines: 501
- Date:
Thu Feb 22 15:18:43 1996
- Orig file:
v1.3.67/linux/drivers/block/loop.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.67/linux/drivers/block/loop.c linux/drivers/block/loop.c
@@ -0,0 +1,500 @@
+/*
+ * linux/drivers/block/loop.c
+ *
+ * Written by Theodore Ts'o, 3/29/93
+ *
+ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is
+ * permitted under the GNU Public License.
+ *
+ * DES encryption plus some minor changes by Werner Almesberger, 30-MAY-1993
+ *
+ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994
+ *
+ * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ */
+
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include "loop.h"
+
+#ifdef DES_AVAILABLE
+#include "des.h"
+#endif
+
+#define DEFAULT_MAJOR_NR 28
+int loop_major = DEFAULT_MAJOR_NR;
+#define MAJOR_NR loop_major /* not necessarily constant */
+
+#define DEVICE_NAME "loop"
+#define DEVICE_REQUEST do_lo_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+#define TIMEOUT_VALUE (6 * HZ)
+#include <linux/blk.h>
+
+#define MAX_LOOP 8
+static struct loop_device loop_dev[MAX_LOOP];
+static int loop_sizes[MAX_LOOP];
+
+/*
+ * Transfer functions
+ */
+static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ if (cmd == READ)
+ memcpy(loop_buf, raw_buf, size);
+ else
+ memcpy(raw_buf, loop_buf, size);
+ return 0;
+}
+
+static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ char *in, *out, *key;
+ int i, keysize;
+
+ if (cmd == READ) {
+ in = raw_buf;
+ out = loop_buf;
+ } else {
+ in = loop_buf;
+ out = raw_buf;
+ }
+ key = lo->lo_encrypt_key;
+ keysize = lo->lo_encrypt_key_size;
+ for (i=0; i < size; i++)
+ *out++ = *in++ ^ key[(i & 511) % keysize];
+ return 0;
+}
+
+#ifdef DES_AVAILABLE
+static int transfer_des(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ unsigned long tmp[2];
+ unsigned long x0,x1,p0,p1;
+
+ if (size & 7)
+ return -EINVAL;
+ x0 = lo->lo_des_init[0];
+ x1 = lo->lo_des_init[1];
+ while (size) {
+ if (cmd == READ) {
+ tmp[0] = (p0 = ((unsigned long *) raw_buf)[0])^x0;
+ tmp[1] = (p1 = ((unsigned long *) raw_buf)[1])^x1;
+ des_ecb_encrypt((des_cblock *) tmp,(des_cblock *)
+ loop_buf,lo->lo_des_key,DES_ENCRYPT);
+ x0 = p0^((unsigned long *) loop_buf)[0];
+ x1 = p1^((unsigned long *) loop_buf)[1];
+ }
+ else {
+ p0 = ((unsigned long *) loop_buf)[0];
+ p1 = ((unsigned long *) loop_buf)[1];
+ des_ecb_encrypt((des_cblock *) loop_buf,(des_cblock *)
+ raw_buf,lo->lo_des_key,DES_DECRYPT);
+ ((unsigned long *) raw_buf)[0] ^= x0;
+ ((unsigned long *) raw_buf)[1] ^= x1;
+ x0 = p0^((unsigned long *) raw_buf)[0];
+ x1 = p1^((unsigned long *) raw_buf)[1];
+ }
+ size -= 8;
+ raw_buf += 8;
+ loop_buf += 8;
+ }
+ return 0;
+}
+#endif
+
+static transfer_proc_t xfer_funcs[MAX_LOOP] = {
+ transfer_none, /* LO_CRYPT_NONE */
+ transfer_xor, /* LO_CRYPT_XOR */
+#ifdef DES_AVAILABLE
+ transfer_des, /* LO_CRYPT_DES */
+#else
+ NULL, /* LO_CRYPT_DES */
+#endif
+ 0 /* LO_CRYPT_IDEA */
+};
+
+
+#define MAX_DISK_SIZE 1024*1024*1024
+
+
+static void figure_loop_size(struct loop_device *lo)
+{
+ int size;
+
+ if (S_ISREG(lo->lo_inode->i_mode))
+ size = (lo->lo_inode->i_size - lo->lo_offset) / 1024;
+ else {
+ if (blk_size[MAJOR(lo->lo_device)])
+ size = ((blk_size[MAJOR(lo->lo_device)]
+ [MINOR(lo->lo_device)]) -
+ (lo->lo_offset/1024));
+ else
+ size = MAX_DISK_SIZE;
+ }
+ loop_sizes[lo->lo_number] = size;
+}
+
+static void do_lo_request(void)
+{
+ int real_block, block, offset, len, blksize, size;
+ char *dest_addr;
+ struct loop_device *lo;
+ struct buffer_head *bh;
+
+repeat:
+ INIT_REQUEST;
+ if (MINOR(CURRENT->rq_dev) >= MAX_LOOP)
+ goto error_out;
+ lo = &loop_dev[MINOR(CURRENT->rq_dev)];
+ if (!lo->lo_inode || !lo->transfer)
+ goto error_out;
+
+ blksize = BLOCK_SIZE;
+ if (blksize_size[MAJOR(lo->lo_device)]) {
+ blksize = blksize_size[MAJOR(lo->lo_device)][MINOR(lo->lo_device)];
+ if (!blksize)
+ blksize = BLOCK_SIZE;
+ }
+
+ dest_addr = CURRENT->buffer;
+
+ if (blksize < 512) {
+ block = CURRENT->sector * (512/blksize);
+ offset = 0;
+ } else {
+ block = CURRENT->sector / (blksize >> 9);
+ offset = CURRENT->sector % (blksize >> 9);
+ }
+ block += lo->lo_offset / blksize;
+ offset += lo->lo_offset % blksize;
+ if (offset > blksize) {
+ block++;
+ offset -= blksize;
+ }
+ len = CURRENT->current_nr_sectors << 9;
+ if ((CURRENT->cmd != WRITE) && (CURRENT->cmd != READ)) {
+ printk("unknown loop device command (%d)?!?", CURRENT->cmd);
+ goto error_out;
+ }
+ while (len > 0) {
+ real_block = block;
+ if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
+ real_block = bmap(lo->lo_inode, block);
+ if (!real_block) {
+ printk("loop: block %d not present\n", block);
+ goto error_out;
+ }
+ }
+ bh = getblk(lo->lo_device, real_block, blksize);
+ if (!bh) {
+ printk("loop: device %s: getblk(-, %d, %d) returned NULL",
+ kdevname(lo->lo_device),
+ block, blksize);
+ goto error_out;
+ }
+ if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) ||
+ (offset || (len < blksize)))) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ goto error_out;
+ }
+ }
+ size = blksize - offset;
+ if (size > len)
+ size = len;
+ if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset,
+ dest_addr, size)) {
+ printk("loop: transfer error block %d\n", block);
+ brelse(bh);
+ goto error_out;
+ }
+ if (CURRENT->cmd == WRITE) {
+ set_bit(BH_Dirty, &bh->b_state);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_dirty(bh)) {
+ brelse(bh);
+ goto error_out;
+ }
+ }
+ brelse(bh);
+ dest_addr += size;
+ len -= size;
+ offset = 0;
+ block++;
+ }
+ end_request(1);
+ goto repeat;
+error_out:
+ end_request(0);
+ goto repeat;
+}
+
+static int loop_set_fd(struct loop_device *lo, unsigned int arg)
+{
+ struct inode *inode;
+
+ if (arg >= NR_OPEN || !current->files->fd[arg])
+ return -EBADF;
+ if (lo->lo_inode)
+ return -EBUSY;
+ inode = current->files->fd[arg]->f_inode;
+ if (!inode) {
+ printk("loop_set_fd: NULL inode?!?\n");
+ return -EINVAL;
+ }
+ if (S_ISREG(inode->i_mode)) {
+ lo->lo_device = inode->i_dev;
+ lo->lo_flags |= LO_FLAGS_DO_BMAP;
+ } else if (S_ISBLK(inode->i_mode))
+ lo->lo_device = inode->i_rdev;
+ else
+ return -EINVAL;
+ lo->lo_inode = inode;
+ lo->lo_inode->i_count++;
+ lo->transfer = NULL;
+ figure_loop_size(lo);
+ return 0;
+}
+
+static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
+{
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (lo->lo_refcnt > 1)
+ return -EBUSY;
+ lo->lo_inode->i_count--;
+ lo->lo_device = 0;
+ lo->lo_inode = NULL;
+ lo->lo_encrypt_type = 0;
+ lo->lo_offset = 0;
+ lo->lo_encrypt_key_size = 0;
+ memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
+ memset(lo->lo_name, 0, LO_NAME_SIZE);
+ invalidate_buffers(dev);
+ return 0;
+}
+
+static int loop_set_status(struct loop_device *lo, struct loop_info *arg)
+{
+ struct loop_info info;
+
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg)
+ return -EINVAL;
+ memcpy_fromfs(&info, arg, sizeof(info));
+ if (info.lo_encrypt_key_size > LO_KEY_SIZE)
+ return -EINVAL;
+ switch (info.lo_encrypt_type) {
+ case LO_CRYPT_NONE:
+ break;
+ case LO_CRYPT_XOR:
+ if (info.lo_encrypt_key_size < 0)
+ return -EINVAL;
+ break;
+#ifdef DES_AVAILABLE
+ case LO_CRYPT_DES:
+ if (info.lo_encrypt_key_size != 8)
+ return -EINVAL;
+ des_set_key((des_cblock *) lo->lo_encrypt_key,
+ lo->lo_des_key);
+ memcpy(lo->lo_des_init,info.lo_init,8);
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+ lo->lo_offset = info.lo_offset;
+ strncpy(lo->lo_name, info.lo_name, LO_NAME_SIZE);
+ lo->lo_encrypt_type = info.lo_encrypt_type;
+ lo->transfer = xfer_funcs[lo->lo_encrypt_type];
+ lo->lo_encrypt_key_size = info.lo_encrypt_key_size;
+ if (info.lo_encrypt_key_size)
+ memcpy(lo->lo_encrypt_key, info.lo_encrypt_key,
+ info.lo_encrypt_key_size);
+ figure_loop_size(lo);
+ return 0;
+}
+
+static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
+{
+ struct loop_info info;
+
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg)
+ return -EINVAL;
+ memset(&info, 0, sizeof(info));
+ info.lo_number = lo->lo_number;
+ info.lo_device = kdev_t_to_nr(lo->lo_inode->i_dev);
+ info.lo_inode = lo->lo_inode->i_ino;
+ info.lo_rdevice = kdev_t_to_nr(lo->lo_device);
+ info.lo_offset = lo->lo_offset;
+ info.lo_flags = lo->lo_flags;
+ strncpy(info.lo_name, lo->lo_name, LO_NAME_SIZE);
+ info.lo_encrypt_type = lo->lo_encrypt_type;
+ if (lo->lo_encrypt_key_size && suser()) {
+ info.lo_encrypt_key_size = lo->lo_encrypt_key_size;
+ memcpy(info.lo_encrypt_key, lo->lo_encrypt_key,
+ lo->lo_encrypt_key_size);
+ }
+ memcpy_tofs(arg, &info, sizeof(info));
+ return 0;
+}
+
+static int lo_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct loop_device *lo;
+ int dev, err;
+
+ if (!inode)
+ return -EINVAL;
+ if (MAJOR(inode->i_rdev) != loop_major) {
+ printk("lo_ioctl: pseudo-major != %d\n", loop_major);
+ return -ENODEV;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return -ENODEV;
+ lo = &loop_dev[dev];
+ switch (cmd) {
+ case LOOP_SET_FD:
+ return loop_set_fd(lo, arg);
+ case LOOP_CLR_FD:
+ return loop_clr_fd(lo, inode->i_rdev);
+ case LOOP_SET_STATUS:
+ return loop_set_status(lo, (struct loop_info *) arg);
+ case LOOP_GET_STATUS:
+ return loop_get_status(lo, (struct loop_info *) arg);
+ case BLKGETSIZE: /* Return device size */
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ put_fs_long(loop_sizes[lo->lo_number] << 1, (long *) arg);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int lo_open(struct inode *inode, struct file *file)
+{
+ struct loop_device *lo;
+ int dev;
+
+ if (!inode)
+ return -EINVAL;
+ if (MAJOR(inode->i_rdev) != loop_major) {
+ printk("lo_open: pseudo-major != %d\n", loop_major);
+ return -ENODEV;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return -ENODEV;
+ lo = &loop_dev[dev];
+ lo->lo_refcnt++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void lo_release(struct inode *inode, struct file *file)
+{
+ struct loop_device *lo;
+ int dev;
+
+ if (!inode)
+ return;
+ if (MAJOR(inode->i_rdev) != loop_major) {
+ printk("lo_release: pseudo-major != %d\n", loop_major);
+ return;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return;
+ sync_dev(inode->i_rdev);
+ lo = &loop_dev[dev];
+ if (lo->lo_refcnt <= 0)
+ printk("lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
+ else {
+ lo->lo_refcnt--;
+ MOD_DEC_USE_COUNT;
+ }
+
+ return;
+}
+
+static struct file_operations lo_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ lo_ioctl, /* ioctl */
+ NULL, /* mmap */
+ lo_open, /* open */
+ lo_release /* release */
+};
+
+/*
+ * And now the modules code and kernel interface.
+ */
+#ifdef MODULE
+#define loop_init init_module
+#endif
+
+int
+loop_init( void ) {
+ int i = (loop_major ? loop_major : DEFAULT_MAJOR_NR);
+
+ if (register_blkdev(i, "loop", &lo_fops) &&
+ (i = register_blkdev(0, "loop", &lo_fops)) <= 0) {
+ printk("Unable to get major number for loop device\n");
+ return -EIO;
+ }
+ loop_major = i;
+#ifdef MODULE
+ if (i != DEFAULT_MAJOR_NR)
+#endif
+ printk("loop: registered device at major %d\n",loop_major);
+
+ blk_dev[loop_major].request_fn = DEVICE_REQUEST;
+ for (i=0; i < MAX_LOOP; i++) {
+ memset(&loop_dev[i], 0, sizeof(struct loop_device));
+ loop_dev[i].lo_number = i;
+ }
+ memset(&loop_sizes, 0, sizeof(loop_sizes));
+ blk_size[loop_major] = loop_sizes;
+
+ return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module( void ) {
+ if (unregister_blkdev(loop_major, "loop") != 0)
+ printk("loop: cleanup_module failed\n");
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this