📄 loop.c-2.2.original
字号:
/* * 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 * more DES encryption plus IDEA encryption by Nicholas J. Leon, June 20, 1996 * * 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 - 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 * * Make real block number available to downstream transfer functions, enables * CBC (and relatives) mode encryption requiring unique IVs per data block. * Reed H. Petty, rhp@draper.net * * Still To Fix: * - Advisory locking is ignored here. * - Should use an own CAP_* category instead of CAP_SYS_ADMIN * - Should use the underlying filesystems/devices read function if possible * to support read ahead (and for write) * * WARNING/FIXME: * - The block number as IV passing to low level transfer functions is broken: * it passes the underlying device's block number instead of the * offset. This makes it change for a given block when the file is * moved/restored/copied and also doesn't work over NFS. */ #include <linux/module.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>#include <linux/loop.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 <linux/blk.h>#define MAX_LOOP 8static struct loop_device loop_dev[MAX_LOOP];static int loop_sizes[MAX_LOOP];static int loop_blksizes[MAX_LOOP];#define FALSE 0#define TRUE (!FALSE)/* Forward declaration of function to create missing blocks in the 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 */static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf, char *loop_buf, int size, int real_block){ 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, int real_block){ 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;}static int none_status(struct loop_device *lo, struct loop_info *info){ return 0; } static int xor_status(struct loop_device *lo, struct loop_info *info){ if (info->lo_encrypt_key_size <= 0) return -EINVAL; return 0;}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*1024static void figure_loop_size(struct loop_device *lo){ int size; if (S_ISREG(lo->lo_dentry->d_inode->i_mode)) size = (lo->lo_dentry->d_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; int block_present;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_dentry || !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(KERN_ERR "unknown loop device command (%d)?!?", current_request->cmd); goto error_out; } spin_unlock_irq(&io_request_lock); while (len > 0) { size = blksize - offset; if (size > len) size = len; real_block = block; block_present = TRUE; if (lo->lo_flags & LO_FLAGS_DO_BMAP) { real_block = bmap(lo->lo_dentry->d_inode, block); if (!real_block) { /* The backing file is a sparse file and this block doesn't exist. If reading, return zeros. If writing, force the underlying FS to create the block */ if (current_request->cmd == READ) { memset(dest_addr, 0, size); block_present = FALSE; } else { if (!create_missing_block(lo, block, blksize)) { goto error_out_lock; } real_block = bmap(lo->lo_dentry->d_inode, block); } } } if (block_present) { bh = getblk(lo->lo_device, real_block, blksize); if (!bh) { printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL", kdevname(lo->lo_device), block, blksize); goto error_out_lock; } 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_lock; } } if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset, dest_addr, size, real_block)) { printk(KERN_ERR "loop: transfer error block %d\n", block); brelse(bh); goto error_out_lock; } 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++; } spin_lock_irq(&io_request_lock); current_request->next=CURRENT; CURRENT=current_request; end_request(1); goto repeat;error_out_lock: spin_lock_irq(&io_request_lock);error_out: current_request->next=CURRENT; CURRENT=current_request; end_request(0); goto repeat;}static int create_missing_block(struct loop_device *lo, int block, int blksize){ struct file *file; loff_t new_offset; char zero_buf[1] = { 0 }; ssize_t retval; mm_segment_t old_fs; struct inode *inode; file = lo->lo_backing_file; if (file == NULL) { printk(KERN_WARNING "loop: cannot create block - no backing file\n"); return FALSE; } if (file->f_op == NULL) { printk(KERN_WARNING "loop: cannot create block - no file ops\n"); return FALSE; } new_offset = block * blksize; if (file->f_op->llseek != NULL) { file->f_op->llseek(file, new_offset, 0); } else { /* Do what the default llseek() code would have done */ file->f_pos = new_offset; file->f_reada = 0; file->f_version = ++global_event; } if (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) { printk(KERN_WARNING "loop: cannot create block - FS write failed: code %d\n", (int)retval); return FALSE; } else { return TRUE; }}static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg){ struct file *file; struct inode *inode; int error; MOD_INC_USE_COUNT; error = -EBUSY; if (lo->lo_dentry) goto out; error = -EBADF; file = fget(arg); if (!file) goto out; error = -EINVAL; inode = file->f_dentry->d_inode; if (!inode) { printk(KERN_ERR "loop_set_fd: NULL inode?!?\n"); goto out_putf; } if (S_ISBLK(inode->i_mode)) { error = blkdev_open(inode, file);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -