loop device: sparse files, and loop on loop bug (w/fix)

Tornaria Gonzalo (gtornari@varela.reu.edu.uy)
Fri, 9 Aug 1996 3:9:07


It seems the loop device can't deal with sparse files. I don't
know if that could/should work, but in any case we should check and
at least not allow it. I don't know how to do either (how can I
determine whether a file has holes or not? I thought of comparing
st_blocks<<9 and st_size, but don't like it too much). I think it
would be very useful if it worked with sparse files (e.g dosemu
hdimages are generally sparse, and I want them to stay sparse).

---------------
numenor:~# dd if=/dev/zero of=a bs=1k seek=360 count=0
0+0 records in
0+0 records out
numenor:~# cat a > b
numenor:~# du a b
0 a
363 b
numenor:~# losetup /dev/loop0 a
numenor:~# losetup /dev/loop1 b
numenor:~# mkfs /dev/loop1 360
120 inodes
360 blocks
Firstdatazone=8 (8)
Zonesize=1024
Maxsize=268966912

numenor:~# mkfs /dev/loop0 360
120 inodes
360 blocks
Firstdatazone=8 (8)
Zonesize=1024
Maxsize=268966912

loop: block 0 not present
end_request: I/O error, dev 07:00, sector 0
mkfs.minix: unable to clear boot sector
loop: block 8 not present
end_request: I/O error, dev 07:00, sector 16
---------------

Another one: you can do "losetup /dev/loop0 /dev/loop0", but
then you can't "losetup -d /dev/loop0". And the same thing with more
than one loop device ("losetup /dev/loop0 /dev/loop1;losetup
/dev/loop1 /dev/loop0"). I tried to solve this by returning -ENXIO
when opening a loop device not configured, but of course, I need to
open it to configure (via ioctl) :-)

Well, I can live with this (Linux: a thousand ways to shot
yourself on a foot if you have permission ;-) A solution would be to
not allow looping a loop that is not configured, but what if you
have two loop modules? (that would be silly, but..)

Now, last one:

---------------
numenor:~# losetup /dev/loop0 b
numenor:~# losetup /dev/loop1 /dev/loop0
numenor:~# mount /dev/loop1 /mnt
---------------

The mount process hungs (i.e. no kill -9). I found this to be
the same problem with reentrancy that I found in do_sbpcd_request
(but this time in do_lo_request). So it was easy to solve it
(exactly the same hack that I did for sbpcd :-) I know that you
don't normally loop a loop, but special cases should work too (no
matter how useless). This seems a general omision, I think, the fact
that the do_*_request are generally not reentrant!
Here's a patch:


--- drivers/block/loop.c.old Thu Aug 8 03:01:14 1996
+++ drivers/block/loop.c Thu Aug 8 05:29:25 1996
@@ -12,6 +12,9 @@
* Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May
1994
*
* Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ *
+ * Hacked out a bug when using loop on another loop device.
+ * Gonzalo Tornaria (GTL), 8/8/1996
*/

#include <linux/module.h>
@@ -175,18 +178,40 @@
loop_sizes[lo->lo_number] = size;
}

+/*
+ * Special end_request for loop to solve double loop bug. (GTL)
+ *
+ * This is similar to what I found in sbpcd.
+ * Here we don't sleep, but we request some blocks from the same
device.
+ * When we reenter do_lo_request() we haven't yet finished the first
+ * request, so we start over with it, then we request some blocks from
+ * the same device ... ad infinitum.
+ *
+ * May be all this block device request stuff needs a global change?
+ *
+ * see also drivers/cdrom/sbpcd.c (it's the same fix)
+ */
+static inline void loop_end_request(struct request *req, int uptodate)
{
+ req->next=CURRENT;
+ CURRENT=req;
+ end_request(uptodate);
+}
+
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 *req;

repeat:
INIT_REQUEST;
- if (MINOR(CURRENT->rq_dev) >= MAX_LOOP)
+ req=CURRENT; /* take out our request so to avoid */
+ CURRENT=req->next; /* reentrancy problems GTL */
+ if (MINOR(req->rq_dev) >= MAX_LOOP)
goto error_out;
- lo = &loop_dev[MINOR(CURRENT->rq_dev)];
+ lo = &loop_dev[MINOR(req->rq_dev)];
if (!lo->lo_inode || !lo->transfer)
goto error_out;

@@ -197,14 +222,14 @@
blksize = BLOCK_SIZE;
}

- dest_addr = CURRENT->buffer;
+ dest_addr = req->buffer;

if (blksize < 512) {
- block = CURRENT->sector * (512/blksize);
+ block = req->sector * (512/blksize);
offset = 0;
} else {
- block = CURRENT->sector / (blksize >> 9);
- offset = (CURRENT->sector % (blksize >> 9)) << 9;
+ block = req->sector / (blksize >> 9);
+ offset = (req->sector % (blksize >> 9)) << 9;
}
block += lo->lo_offset / blksize;
offset += lo->lo_offset % blksize;
@@ -212,13 +237,13 @@
block++;
offset -= blksize;
}
- len = CURRENT->current_nr_sectors << 9;
+ len = req->current_nr_sectors << 9;

- if (CURRENT->cmd == WRITE) {
+ if (req->cmd == WRITE) {
if (lo->lo_flags & LO_FLAGS_READ_ONLY)
goto error_out;
- } else if (CURRENT->cmd != READ) {
- printk("unknown loop device command (%d)?!?",
CURRENT->cmd);
+ } else if (req->cmd != READ) {
+ printk("unknown loop device command (%d)?!?",
req->cmd);
goto error_out;
}
while (len > 0) {
@@ -237,7 +262,7 @@
block, blksize);
goto error_out;
}
- if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) ||
+ if (!buffer_uptodate(bh) && ((req->cmd == READ) ||
(offset || (len < blksize)))) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
@@ -250,13 +275,13 @@
if (size > len)
size = len;

- if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data +
offset,
+ if ((lo->transfer)(lo, req->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) {
+ if (req->cmd == WRITE) {
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
}
@@ -266,10 +291,10 @@
offset = 0;
block++;
}
- end_request(1);
+ loop_end_request(req, 1);
goto repeat;
error_out:
- end_request(0);
+ loop_end_request(req, 0);
goto repeat;
}
-------------

Saludos,
Gonzalo

PS: sorry, some 5 lines got wrapped. I can't do nothing from here.
My normal conection (tornaria@cmat.edu.uy) is down and this is a
stupid bbs software (flame the guy in X-Mailer:). If someone has
problems with that I can resend the patch once my conection is up,
or put it into my home page.

--
GM/CS/S d? a-- C++(+++) UL++(+++)>++++ P++>+++ L+++>++++ E---
W-(+) N+(++) w--- O M-(--) V-(--) PGP(--) b++(+++) G++ e>++++