patch-2.1.110 linux/drivers/block/loop.c
Next file: linux/drivers/char/Config.in
Previous file: linux/drivers/acorn/scsi/queue.c
Back to the patch index
Back to the overall index
- Lines: 319
- Date:
Mon Jul 20 17:22:35 1998
- Orig file:
v2.1.109/linux/drivers/block/loop.c
- Orig date:
Wed Jun 24 22:54:04 1998
diff -u --recursive --new-file v2.1.109/linux/drivers/block/loop.c linux/drivers/block/loop.c
@@ -14,6 +14,8 @@
* 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
*/
#include <linux/module.h>
@@ -55,6 +57,14 @@
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
*/
@@ -187,6 +197,7 @@
struct loop_device *lo;
struct buffer_head *bh;
struct request *current_request;
+ int block_present;
repeat:
INIT_REQUEST;
@@ -226,50 +237,70 @@
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);
+ 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) {
- printk("loop: block %d not present\n", block);
- goto error_out_lock;
+
+ /* 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;
+ }
+ }
+
}
}
- 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_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)) {
+
+ 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)) {
+ printk(KERN_ERR "loop: transfer error block %d\n", block);
brelse(bh);
goto error_out_lock;
}
- }
- 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);
+
+ if (current_request->cmd == WRITE) {
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ }
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;
@@ -289,6 +320,57 @@
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;
+
+ 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 = ++event;
+ }
+
+ if (file->f_op->write == NULL) {
+ printk(KERN_WARNING "loop: cannot create block - no write file op\n");
+ return FALSE;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
+
+ set_fs(old_fs);
+
+ if (retval < 0) {
+ printk(KERN_WARNING "loop: cannot create block - FS write failed: code %d\n",
+ retval);
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
{
struct file *file;
@@ -309,7 +391,7 @@
error = -EINVAL;
inode = file->f_dentry->d_inode;
if (!inode) {
- printk("loop_set_fd: NULL inode?!?\n");
+ printk(KERN_ERR "loop_set_fd: NULL inode?!?\n");
goto out_putf;
}
@@ -317,10 +399,36 @@
error = blkdev_open(inode, file);
lo->lo_device = inode->i_rdev;
lo->lo_flags = 0;
+
+ /* Backed by a block device - don't need to hold onto
+ a file structure */
+ lo->lo_backing_file = NULL;
} else if (S_ISREG(inode->i_mode)) {
+
+ /* Backed by a regular file - we need to hold onto
+ a file structure for this file. We'll use it to
+ write to blocks that are not already present in
+ a sparse file. We create a new file structure
+ based on the one passed to us via 'arg'. This is
+ to avoid changing the file structure that the
+ caller is using */
+
lo->lo_device = inode->i_dev;
lo->lo_flags = LO_FLAGS_DO_BMAP;
- error = 0;
+
+ error = -ENFILE;
+ lo->lo_backing_file = get_empty_filp();
+ if (lo->lo_backing_file) {
+ lo->lo_backing_file->f_mode = file->f_mode;
+ lo->lo_backing_file->f_pos = file->f_pos;
+ lo->lo_backing_file->f_flags = file->f_flags;
+ lo->lo_backing_file->f_owner = file->f_owner;
+ lo->lo_backing_file->f_dentry = file->f_dentry;
+ lo->lo_backing_file->f_op = file->f_op;
+ lo->lo_backing_file->private_data = file->private_data;
+
+ error = 0;
+ }
}
if (error)
goto out_putf;
@@ -358,7 +466,14 @@
if (S_ISBLK(dentry->d_inode->i_mode))
blkdev_release (dentry->d_inode);
lo->lo_dentry = NULL;
- dput(dentry);
+
+ if (lo->lo_backing_file != NULL) {
+ fput(lo->lo_backing_file);
+ lo->lo_backing_file = NULL;
+ } else {
+ dput(dentry);
+ }
+
lo->lo_device = 0;
lo->lo_encrypt_type = 0;
lo->lo_offset = 0;
@@ -470,7 +585,7 @@
if (!inode)
return -EINVAL;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_ioctl: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_ioctl: pseudo-major != %d\n", MAJOR_NR);
return -ENODEV;
}
dev = MINOR(inode->i_rdev);
@@ -506,7 +621,7 @@
if (!inode)
return -EINVAL;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_open: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_open: pseudo-major != %d\n", MAJOR_NR);
return -ENODEV;
}
dev = MINOR(inode->i_rdev);
@@ -526,7 +641,7 @@
if (!inode)
return 0;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_release: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_release: pseudo-major != %d\n", MAJOR_NR);
return 0;
}
dev = MINOR(inode->i_rdev);
@@ -535,7 +650,7 @@
fsync_dev(inode->i_rdev);
lo = &loop_dev[dev];
if (lo->lo_refcnt <= 0)
- printk("lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
+ printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
else {
lo->lo_refcnt--;
MOD_DEC_USE_COUNT;
@@ -567,17 +682,17 @@
int i;
if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
- printk("Unable to get major number %d for loop device\n",
+ printk(KERN_WARNING "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);
+ printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR);
#ifdef DES_AVAILABLE
- printk("loop: DES encryption available\n");
+ printk(KERN_INFO "loop: DES encryption available\n");
#endif
#ifdef IDEA_AVAILABLE
- printk("loop: IDEA encryption available\n");
+ printk(KERN_INFO "loop: IDEA encryption available\n");
#endif
#endif
@@ -598,6 +713,6 @@
void
cleanup_module( void ) {
if (unregister_blkdev(MAJOR_NR, "loop") != 0)
- printk("loop: cleanup_module failed\n");
+ printk(KERN_WARNING "loop: cleanup_module failed\n");
}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov