/* * 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. * * more DES encryption plus IDEA encryption by Nicholas J. Leon, June 20, 1996 * 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 * * Fixed do_loop_request() re-entrancy - Mar 20, 1997 */ #include #include #include #include #include #include #include #include #ifdef CONFIG_BLK_DEV_LOOP_DES # /*nodep*/ include #endif #ifdef CONFIG_BLK_DEV_LOOP_IDEA # /*nodep*/ include #endif #include /* must follow des.h */ #define MAJOR_NR LOOP_MAJOR #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 #define MAX_LOOP 8 static struct loop_device loop_dev[MAX_LOOP]; static int loop_sizes[MAX_LOOP]; static int loop_blksizes[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 #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 }; #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) / BLOCK_SIZE; else { kdev_t lodev = lo->lo_device; if (blk_size[MAJOR(lodev)]) size = blk_size[MAJOR(lodev)][MINOR(lodev)] - lo->lo_offset / BLOCK_SIZE; 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; struct request *current_request; repeat: INIT_REQUEST; current_request=CURRENT; CURRENT=current_request->next; if (MINOR(current_request->rq_dev) >= MAX_LOOP) goto error_out; lo = &loop_dev[MINOR(current_request->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_request->buffer; if (blksize < 512) { block = current_request->sector * (512/blksize); offset = 0; } else { block = current_request->sector / (blksize >> 9); offset = (current_request->sector % (blksize >> 9)) << 9; } block += lo->lo_offset / blksize; offset += lo->lo_offset % blksize; if (offset > blksize) { block++; offset -= blksize; } len = current_request->current_nr_sectors << 9; if (current_request->cmd == WRITE) { if (lo->lo_flags & LO_FLAGS_READ_ONLY) goto error_out; } else if (current_request->cmd != READ) { printk("unknown loop device command (%d)?!?", current_request->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_request->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_request->cmd, bh->b_data + offset, dest_addr, size)) { printk("loop: transfer error block %d\n", block); brelse(bh); goto error_out; } if (current_request->cmd == WRITE) { mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 1); } brelse(bh); dest_addr += size; len -= size; offset = 0; block++; } current_request->next=CURRENT; CURRENT=current_request; end_request(1); goto repeat; error_out: current_request->next=CURRENT; CURRENT=current_request; end_request(0); goto repeat; } static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) { struct file *file; struct inode *inode; if (arg >= NR_OPEN || !(file = current->files->fd[arg])) return -EBADF; if (lo->lo_inode) return -EBUSY; inode = file->f_inode; if (!inode) { printk("loop_set_fd: NULL inode?!?\n"); return -EINVAL; } if (S_ISBLK(inode->i_mode)) { int error = blkdev_open(inode, file); if (error) return error; lo->lo_device = inode->i_rdev; lo->lo_flags = 0; } else if (S_ISREG(inode->i_mode)) { lo->lo_device = inode->i_dev; lo->lo_flags = LO_FLAGS_DO_BMAP; } else return -EINVAL; if (IS_RDONLY (inode) || is_read_only(lo->lo_device)) { lo->lo_flags |= LO_FLAGS_READ_ONLY; set_device_ro(dev, 1); } else { invalidate_inode_pages (inode); set_device_ro(dev, 0); } lo->lo_inode = inode; lo->lo_inode->i_count++; lo->transfer = NULL; figure_loop_size(lo); MOD_INC_USE_COUNT; 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) /* we needed one fd for the ioctl */ return -EBUSY; if (S_ISBLK(lo->lo_inode->i_mode)) blkdev_release (lo->lo_inode); iput(lo->lo_inode); 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); loop_sizes[lo->lo_number] = 0; invalidate_buffers(dev); MOD_DEC_USE_COUNT; return 0; } static int loop_set_status(struct loop_device *lo, struct loop_info *arg) { struct loop_info info; int err; if (!lo->lo_inode) 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 ((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: 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; int err; if (!lo->lo_inode) 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_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); } copy_to_user(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; if (!inode) return -EINVAL; if (MAJOR(inode->i_rdev) != MAJOR_NR) { printk("lo_ioctl: pseudo-major != %d\n", MAJOR_NR); 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, inode->i_rdev, 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; return put_user(loop_sizes[lo->lo_number] << 1, (long *) arg); 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) != MAJOR_NR) { printk("lo_open: pseudo-major != %d\n", MAJOR_NR); 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 int lo_release(struct inode *inode, struct file *file) { struct loop_device *lo; int dev; if (!inode) return 0; if (MAJOR(inode->i_rdev) != MAJOR_NR) { printk("lo_release: pseudo-major != %d\n", MAJOR_NR); return 0; } dev = MINOR(inode->i_rdev); if (dev >= MAX_LOOP) return 0; fsync_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 0; } 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, /* poll */ 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 __initfunc(int loop_init( void )) { int i; if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) { printk("Unable to get major number %d for loop device\n", MAJOR_NR); return -EIO; } #ifndef MODULE printk("loop: registered device at major %d\n", MAJOR_NR); #ifdef DES_AVAILABLE printk("loop: DES encryption available\n"); #endif #ifdef IDEA_AVAILABLE printk("loop: IDEA encryption available\n"); #endif #endif blk_dev[MAJOR_NR].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)); memset(&loop_blksizes, 0, sizeof(loop_blksizes)); blk_size[MAJOR_NR] = loop_sizes; blksize_size[MAJOR_NR] = loop_blksizes; return 0; } #ifdef MODULE void cleanup_module( void ) { if (unregister_blkdev(MAJOR_NR, "loop") != 0) printk("loop: cleanup_module failed\n"); } #endif