summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/block/genhd.c10
-rw-r--r--drivers/block/ioctl.c47
-rw-r--r--fs/block_dev.c23
-rw-r--r--fs/partitions/check.c1
-rw-r--r--include/linux/genhd.h7
5 files changed, 71 insertions, 17 deletions
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index ecb2dcdf214d..1cc4655c04c9 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -100,6 +100,8 @@ get_gendisk(dev_t dev, int *part)
read_lock(&gendisk_lock);
if (gendisks[major].get) {
disk = gendisks[major].get(minor);
+ if (disk)
+ get_disk(disk);
read_unlock(&gendisk_lock);
return disk;
}
@@ -109,6 +111,7 @@ get_gendisk(dev_t dev, int *part)
continue;
if (disk->first_minor + disk->minors <= minor)
continue;
+ get_disk(disk);
read_unlock(&gendisk_lock);
*part = minor - disk->first_minor;
return disk;
@@ -244,6 +247,12 @@ struct gendisk *alloc_disk(int minors)
return disk;
}
+struct gendisk *get_disk(struct gendisk *disk)
+{
+ atomic_inc(&disk->disk_dev.refcount);
+ return disk;
+}
+
void put_disk(struct gendisk *disk)
{
if (disk)
@@ -251,4 +260,5 @@ void put_disk(struct gendisk *disk)
}
EXPORT_SYMBOL(alloc_disk);
+EXPORT_SYMBOL(get_disk);
EXPORT_SYMBOL(put_disk);
diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c
index 4af05bc32db2..de2da2b44cad 100644
--- a/drivers/block/ioctl.c
+++ b/drivers/block/ioctl.c
@@ -25,13 +25,17 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
disk = get_gendisk(bdev->bd_dev, &part);
if (!disk)
return -ENXIO;
- if (bdev != bdev->bd_contains)
+ if (bdev != bdev->bd_contains) {
+ put_disk(disk);
return -EINVAL;
+ }
if (part)
BUG();
part = p.pno;
- if (part <= 0 || part >= disk->minors)
+ if (part <= 0 || part >= disk->minors) {
+ put_disk(disk);
return -EINVAL;
+ }
switch (a.op) {
case BLKPG_ADD_PARTITION:
@@ -42,34 +46,46 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
- || pstart < 0 || plength < 0)
+ || pstart < 0 || plength < 0) {
+ put_disk(disk);
return -EINVAL;
+ }
}
/* partition number in use? */
- if (disk->part[part - 1].nr_sects != 0)
+ if (disk->part[part - 1].nr_sects != 0) {
+ put_disk(disk);
return -EBUSY;
+ }
/* overlap? */
for (i = 0; i < disk->minors - 1; i++) {
struct hd_struct *s = &disk->part[i];
if (!(start+length <= s->start_sect ||
- start >= s->start_sect + s->nr_sects))
+ start >= s->start_sect + s->nr_sects)) {
+ put_disk(disk);
return -EBUSY;
+ }
}
/* all seems OK */
add_partition(disk, part, start, length);
+ put_disk(disk);
return 0;
case BLKPG_DEL_PARTITION:
- if (disk->part[part - 1].nr_sects == 0)
+ if (disk->part[part - 1].nr_sects == 0) {
+ put_disk(disk);
return -ENXIO;
+ }
/* partition in use? Incomplete check for now. */
bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part);
- if (!bdevp)
+ if (!bdevp) {
+ put_disk(disk);
return -ENOMEM;
+ }
if (bd_claim(bdevp, &holder) < 0) {
bdput(bdevp);
+ put_disk(disk);
return -EBUSY;
}
@@ -80,8 +96,10 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
delete_partition(disk, part);
bd_release(bdevp);
bdput(bdevp);
+ put_disk(disk);
return 0;
default:
+ put_disk(disk);
return -EINVAL;
}
}
@@ -92,16 +110,25 @@ static int blkdev_reread_part(struct block_device *bdev)
struct gendisk *disk = get_gendisk(bdev->bd_dev, &part);
int res = 0;
- if (!disk || disk->minors == 1 || bdev != bdev->bd_contains)
+ if (!disk)
return -EINVAL;
+ if (disk->minors == 1 || bdev != bdev->bd_contains) {
+ put_disk(disk);
+ return -EINVAL;
+ }
if (part)
BUG();
- if (!capable(CAP_SYS_ADMIN))
+ if (!capable(CAP_SYS_ADMIN)) {
+ put_disk(disk);
return -EACCES;
- if (down_trylock(&bdev->bd_sem))
+ }
+ if (down_trylock(&bdev->bd_sem)) {
+ put_disk(disk);
return -EBUSY;
+ }
res = rescan_partitions(disk, bdev);
up(&bdev->bd_sem);
+ put_disk(disk);
return res;
}
diff --git a/fs/block_dev.c b/fs/block_dev.c
index dff0244e63a6..d029636b07e6 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -542,6 +542,7 @@ int check_disk_change(struct block_device *bdev)
bdops->revalidate(dev);
if (disk && disk->minors > 1)
bdev->bd_invalidated = 1;
+ put_disk(disk);
return 1;
}
@@ -553,7 +554,9 @@ int full_check_disk_change(struct block_device *bdev)
BUG();
down(&bdev->bd_sem);
if (check_disk_change(bdev)) {
- rescan_partitions(get_gendisk(bdev->bd_dev, &n), bdev);
+ struct gendisk *disk = get_gendisk(bdev->bd_dev, &n);
+ rescan_partitions(disk, bdev);
+ put_disk(disk);
res = 1;
}
up(&bdev->bd_sem);
@@ -622,13 +625,18 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
struct block_device *disk;
disk = bdget(MKDEV(g->major, g->first_minor));
ret = -ENOMEM;
- if (!disk)
+ if (!disk) {
+ put_disk(g);
goto out1;
+ }
ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW);
- if (ret)
+ if (ret) {
+ put_disk(g);
goto out1;
+ }
bdev->bd_contains = disk;
}
+ put_disk(g);
}
if (bdev->bd_contains == bdev) {
int part;
@@ -643,8 +651,10 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
if (bdev->bd_op->open) {
ret = bdev->bd_op->open(inode, file);
- if (ret)
+ if (ret) {
+ put_disk(g);
goto out2;
+ }
}
if (!bdev->bd_openers) {
struct backing_dev_info *bdi;
@@ -662,6 +672,7 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
}
if (bdev->bd_invalidated)
rescan_partitions(g, bdev);
+ put_disk(g);
} else {
down(&bdev->bd_contains->bd_sem);
bdev->bd_contains->bd_part_count++;
@@ -673,15 +684,17 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
inode->i_data.backing_dev_info =
bdev->bd_inode->i_data.backing_dev_info =
bdev->bd_contains->bd_inode->i_data.backing_dev_info;
- if (!p->nr_sects) {
+ if (!(g->flags & GENHD_FL_UP) || !p->nr_sects) {
bdev->bd_contains->bd_part_count--;
up(&bdev->bd_contains->bd_sem);
+ put_disk(g);
ret = -ENXIO;
goto out2;
}
bdev->bd_queue = bdev->bd_contains->bd_queue;
bdev->bd_offset = p->start_sect;
bd_set_size(bdev, (loff_t) p->nr_sects << 9);
+ put_disk(g);
}
up(&bdev->bd_contains->bd_sem);
}
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 5fc23d047567..e6ed1a443116 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -616,6 +616,7 @@ char *partition_name(dev_t dev)
dname->name = NULL;
if (hd)
dname->name = disk_name(hd, part, dname->namebuf);
+ put_disk(hd);
if (!dname->name) {
sprintf(dname->namebuf, "[dev %s]", kdevname(to_kdev_t(dev)));
dname->name = dname->namebuf;
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 6b859fad6a8a..030ee2f87891 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -266,6 +266,7 @@ extern void add_partition(struct gendisk *, int, sector_t, sector_t);
extern void delete_partition(struct gendisk *, int);
extern struct gendisk *alloc_disk(int minors);
+extern struct gendisk *get_disk(struct gendisk *disk);
extern void put_disk(struct gendisk *disk);
/* will go away */
@@ -273,9 +274,11 @@ extern void blk_set_probe(int major, struct gendisk *(p)(int));
static inline unsigned int disk_index (kdev_t dev)
{
- int part;
+ int part, res;
struct gendisk *g = get_gendisk(kdev_t_to_nr(dev), &part);
- return g ? (minor(dev) >> g->minor_shift) : 0;
+ res = g ? (minor(dev) >> g->minor_shift) : 0;
+ put_disk(g);
+ return res;
}
#endif