patch-2.1.122 linux/drivers/block/loop.c

Next file: linux/drivers/block/via82c586.c
Previous file: linux/drivers/block/ide.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.121/linux/drivers/block/loop.c linux/drivers/block/loop.c
@@ -15,30 +15,27 @@
  * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997
  *
  * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998
+ *
+ * Loadable modules and other fixes by AK, 1998
+ * 
+ * Still To Fix:
+ * - Advisory locking is ignored here. 
+ * - Should use an own CAP_* category instead of CAP_SYS_ADMIN 
  */
 
 #include <linux/module.h>
-
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/major.h>
+
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_BLK_DEV_LOOP_DES
-# /*nodep*/ include <linux/des.h>
-#endif
-
-#ifdef CONFIG_BLK_DEV_LOOP_IDEA
-# /*nodep*/ include <linux/idea.h>
-#endif
-
-#include <linux/loop.h>		/* must follow des.h */
+#include <linux/loop.h>		
 
 #define MAJOR_NR LOOP_MAJOR
 
@@ -63,7 +60,6 @@
    backing file (can happen if the backing file is sparse) */
 static int create_missing_block(struct loop_device *lo, int block, int blksize);
 
-
 /*
  * Transfer functions
  */
@@ -97,80 +93,38 @@
 	return 0;
 }
 
-#ifdef DES_AVAILABLE
-static int transfer_des(struct loop_device *lo, int cmd, char *raw_buf,
-		  char *loop_buf, int size)
+static int none_status(struct loop_device *lo, struct loop_info *info)
 {
-	unsigned long tmp[2];
-	unsigned long x0,x1,p0,p1;
+	return 0; 
+} 
 
-	if (size & 7)
+static int xor_status(struct loop_device *lo, struct loop_info *info)
+{
+	if (info->lo_encrypt_key_size < 0)
 		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
-
-#ifdef IDEA_AVAILABLE
-
-extern void idea_encrypt_block(idea_key,char *,char *,int);
 
-static int transfer_idea(struct loop_device *lo, int cmd, char *raw_buf,
-		  char *loop_buf, int size)
-{
-  if (cmd==READ) {
-    idea_encrypt_block(lo->lo_idea_en_key,raw_buf,loop_buf,size);
-  }
-  else {
-    idea_encrypt_block(lo->lo_idea_de_key,loop_buf,raw_buf,size);
-  }
-  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
-#ifdef IDEA_AVAILABLE           /* LO_CRYPT_IDEA */
-	transfer_idea
-#else
-	NULL
-#endif
+struct loop_func_table none_funcs = { 
+	number: LO_CRYPT_NONE,
+	transfer: transfer_none,
+	init: none_status
+}; 	
+
+struct loop_func_table xor_funcs = { 
+	number: LO_CRYPT_XOR,
+	transfer: transfer_xor,
+	init: xor_status
+}; 	
+
+/* xfer_funcs[0] is special - its release function is never called */ 
+struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
+	&none_funcs,
+	&xor_funcs  
 };
 
-
 #define MAX_DISK_SIZE 1024*1024*1024
 
-
 static void figure_loop_size(struct loop_device *lo)
 {
 	int	size;
@@ -326,6 +280,7 @@
 	char            zero_buf[1] = { 0 };
 	ssize_t         retval;
 	mm_segment_t	old_fs;
+	struct inode	*inode;
 
 	file = lo->lo_backing_file;
 	if (file == NULL) {
@@ -349,16 +304,19 @@
 		file->f_version = ++event;
 	}
 
-	if (file->f_op->write == NULL) {
-		printk(KERN_WARNING "loop: cannot create block - no write file op\n");
+	if ((file->f_mode & FMODE_WRITE) == 0 ||  file->f_op->write == NULL) {
+		printk(KERN_WARNING "loop: cannot create block - file not writeable\n");
 		return FALSE;
 	}
 
 	old_fs = get_fs();
 	set_fs(get_ds());
 
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem); 
 	retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
-
+	up(&inode->i_sem);
+	
 	set_fs(old_fs);
 
 	if (retval < 0) {
@@ -447,6 +405,7 @@
 	lo->lo_dentry = file->f_dentry;
 	lo->lo_dentry->d_count++;
 	lo->transfer = NULL;
+	lo->ioctl = NULL;
 	figure_loop_size(lo);
 
 out_putf:
@@ -457,6 +416,19 @@
 	return error;
 }
 
+static int loop_release_xfer(struct loop_device *lo)
+{
+	int err = 0; 
+	if (lo->lo_encrypt_type) {
+		if (xfer_funcs[lo->lo_encrypt_type] && 
+		    xfer_funcs[lo->lo_encrypt_type]->release) { 
+			err = xfer_funcs[lo->lo_encrypt_type]->release(lo);
+		}
+		lo->lo_encrypt_type = 0;
+	}
+	return err;
+}
+
 static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
 {
 	struct dentry *dentry = lo->lo_dentry;
@@ -477,6 +449,8 @@
 		dput(dentry);
 	}
 
+	lo->transfer = NULL;
+	lo->ioctl = NULL;
 	lo->lo_device = 0;
 	lo->lo_encrypt_type = 0;
 	lo->lo_offset = 0;
@@ -491,60 +465,44 @@
 
 static int loop_set_status(struct loop_device *lo, struct loop_info *arg)
 {
-	struct loop_info info;
+	struct loop_info info; 
 	int err;
+	unsigned int type;
 
+	if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid && 
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
 	if (!lo->lo_dentry)
 		return -ENXIO;
-	if (!arg)
-		return -EINVAL;
-	err = verify_area(VERIFY_READ, arg, sizeof(info));
-	if (err)
-		return err;
-	copy_from_user(&info, arg, sizeof(info));
+	if (copy_from_user(&info, arg, sizeof (struct loop_info)))
+		return -EFAULT; 
 	if ((unsigned int) 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
-#ifdef IDEA_AVAILABLE
-	case LO_CRYPT_IDEA:
-	  {
-	        uint16 tmpkey[8];
-
-	        if (info.lo_encrypt_key_size != IDEAKEYSIZE)
-		        return -EINVAL;
-                /* create key in lo-> from info.lo_encrypt_key */
-		memcpy(tmpkey,info.lo_encrypt_key,sizeof(tmpkey));
-		en_key_idea(tmpkey,lo->lo_idea_en_key);
-		de_key_idea(lo->lo_idea_en_key,lo->lo_idea_de_key);
-		break;
-	  }
-#endif
-	default:
+	type = info.lo_encrypt_type; 
+	if (type >= MAX_LO_CRYPT || xfer_funcs[type] == NULL)
 		return -EINVAL;
-	}
+	err = loop_release_xfer(lo); 
+	if (err)
+		return err;
+	if (xfer_funcs[type]->init) 
+		err = xfer_funcs[type]->init(lo, &info);
+	if (err)
+		return err;
+
 	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_type = type; 
+
+	lo->transfer = xfer_funcs[type]->transfer;
+	lo->ioctl = xfer_funcs[type]->ioctl;
 	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,
+	lo->lo_init[0] = info.lo_init[0];
+	lo->lo_init[1] = info.lo_init[1];
+	if (info.lo_encrypt_key_size) {
+		memcpy(lo->lo_encrypt_key, info.lo_encrypt_key, 
 		       info.lo_encrypt_key_size);
+		lo->lo_key_owner = current->uid; 
+	}	
 	figure_loop_size(lo);
 	return 0;
 }
@@ -552,15 +510,11 @@
 static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
 {
 	struct loop_info	info;
-	int err;
-	
+
 	if (!lo->lo_dentry)
 		return -ENXIO;
 	if (!arg)
 		return -EINVAL;
-	err = verify_area(VERIFY_WRITE, arg, sizeof(info));
-	if (err)
-		return err;
 	memset(&info, 0, sizeof(info));
 	info.lo_number = lo->lo_number;
 	info.lo_device = kdev_t_to_nr(lo->lo_dentry->d_inode->i_dev);
@@ -575,8 +529,7 @@
 		memcpy(info.lo_encrypt_key, lo->lo_encrypt_key,
 		       lo->lo_encrypt_key_size);
 	}
-	copy_to_user(arg, &info, sizeof(info));
-	return 0;
+	return copy_to_user(arg, &info, sizeof(info)) ? -EFAULT : 0;
 }
 
 static int lo_ioctl(struct inode * inode, struct file * file,
@@ -611,7 +564,7 @@
 			return -EINVAL;
 		return put_user(loop_sizes[lo->lo_number] << 1, (long *) arg);
 	default:
-		return -EINVAL;
+		return lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
 	}
 	return 0;
 }
@@ -628,8 +581,9 @@
 		return -ENODEV;
 	}
 	dev = MINOR(inode->i_rdev);
-	if (dev >= MAX_LOOP)
+	if (dev >= MAX_LOOP) {
 		return -ENODEV;
+	}
 	lo = &loop_dev[dev];
 	lo->lo_refcnt++;
 	MOD_INC_USE_COUNT;
@@ -639,7 +593,7 @@
 static int lo_release(struct inode *inode, struct file *file)
 {
 	struct loop_device *lo;
-	int	dev;
+	int	dev, err = 0;
 
 	if (!inode)
 		return 0;
@@ -650,15 +604,18 @@
 	dev = MINOR(inode->i_rdev);
 	if (dev >= MAX_LOOP)
 		return 0;
-	fsync_dev(inode->i_rdev);
+	err = fsync_dev(inode->i_rdev);
+	if (err < 0) 
+		return err; 
 	lo = &loop_dev[dev];
 	if (lo->lo_refcnt <= 0)
 		printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
 	else  {
-		lo->lo_refcnt--;
+		if (--lo->lo_refcnt == 0) 
+			err = loop_release_xfer(lo);
 		MOD_DEC_USE_COUNT;
 	}
-	return 0;
+	return err;
 }
 
 static struct file_operations lo_fops = {
@@ -681,8 +638,28 @@
 #define loop_init init_module
 #endif
 
-__initfunc(int
-loop_init( void )) {
+int loop_register_transfer(struct loop_func_table *funcs)
+{
+	if ((unsigned)funcs->number > MAX_LO_CRYPT || xfer_funcs[funcs->number])
+		return -EINVAL;
+	xfer_funcs[funcs->number] = funcs;
+	return 0; 
+}
+
+/* Usage checking must be done by the caller - usually with MOD_INC/DEC_* */  
+int loop_unregister_transfer(int number)
+{
+	if ((unsigned)number >= MAX_LO_CRYPT)
+		return -EINVAL; 
+	xfer_funcs[number] = NULL; 
+	return 0; 
+}
+
+EXPORT_SYMBOL(loop_register_transfer);
+EXPORT_SYMBOL(loop_unregister_transfer);
+
+int __init loop_init(void) 
+{
 	int	i;
 
 	if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
@@ -692,12 +669,6 @@
 	}
 #ifndef MODULE
 	printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR);
-#ifdef DES_AVAILABLE
-	printk(KERN_INFO "loop: DES encryption available\n");
-#endif
-#ifdef IDEA_AVAILABLE
-	printk(KERN_INFO "loop: IDEA encryption available\n");
-#endif
 #endif
 
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
@@ -714,9 +685,9 @@
 }
 
 #ifdef MODULE
-void
-cleanup_module( void ) {
-  if (unregister_blkdev(MAJOR_NR, "loop") != 0)
-    printk(KERN_WARNING "loop: cleanup_module failed\n");
+void cleanup_module(void) 
+{
+	if (unregister_blkdev(MAJOR_NR, "loop") != 0)
+		printk(KERN_WARNING "loop: cannot unregister blkdev\n");
 }
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov