summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorFranz Pletz <franz_pletz@t-online.de>2005-01-04 05:33:21 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-04 05:33:21 -0800
commit577dfb53c790924fe10bd7415ef37a9601605455 (patch)
tree96dab0b3b66a781d6a4df68ff79640e144df1c26 /drivers
parent24498885b08fed64b7f90a6e16732d3dd22f40be (diff)
[PATCH] loop device resursion avoidance
With Andries Brouwer <Andries.Brouwer@cwi.nl> Fix various recursion scenarios wherein it was possible to mount a loop device on itself, either directly or via intermediate loops devices. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/loop.c33
1 files changed, 28 insertions, 5 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 9e1b08b73bfc..3efc2ea7ad64 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -622,10 +622,17 @@ static int loop_change_fd(struct loop_device *lo, struct file *lo_file,
return error;
}
+static inline int is_loop_device(struct file *file)
+{
+ struct inode *i = file->f_mapping->host;
+
+ return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR;
+}
+
static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
struct block_device *bdev, unsigned int arg)
{
- struct file *file;
+ struct file *file, *f;
struct inode *inode;
struct address_space *mapping;
unsigned lo_blocksize;
@@ -636,15 +643,31 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
/* This is safe, since we have a reference from open(). */
__module_get(THIS_MODULE);
- error = -EBUSY;
- if (lo->lo_state != Lo_unbound)
- goto out;
-
error = -EBADF;
file = fget(arg);
if (!file)
goto out;
+ error = -EBUSY;
+ if (lo->lo_state != Lo_unbound)
+ goto out_putf;
+
+ /* Avoid recursion */
+ f = file;
+ while (is_loop_device(f)) {
+ struct loop_device *l;
+
+ if (f->f_mapping->host->i_rdev == lo_file->f_mapping->host->i_rdev)
+ goto out_putf;
+
+ l = f->f_mapping->host->i_bdev->bd_disk->private_data;
+ if (l->lo_state == Lo_unbound) {
+ error = -EINVAL;
+ goto out_putf;
+ }
+ f = l->lo_backing_file;
+ }
+
mapping = file->f_mapping;
inode = mapping->host;