diff options
| -rw-r--r-- | drivers/block/genhd.c | 10 | ||||
| -rw-r--r-- | drivers/block/ioctl.c | 47 | ||||
| -rw-r--r-- | fs/block_dev.c | 23 | ||||
| -rw-r--r-- | fs/partitions/check.c | 1 | ||||
| -rw-r--r-- | include/linux/genhd.h | 7 |
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 |
