Commit fa9156ae authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe

block: refactor blkpg_ioctl

Split each sub-command out into a separate helper, and move those helpers
to block/partitions/core.c instead of having a lot of partition
manipulation logic open coded in block/ioctl.c.

Signed-off-by: Christoph Hellwig <hch@lst.de
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent b4fd63f4
...@@ -389,11 +389,13 @@ char *disk_name(struct gendisk *hd, int partno, char *buf); ...@@ -389,11 +389,13 @@ char *disk_name(struct gendisk *hd, int partno, char *buf);
#define ADDPART_FLAG_NONE 0 #define ADDPART_FLAG_NONE 0
#define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_RAID 1
#define ADDPART_FLAG_WHOLEDISK 2 #define ADDPART_FLAG_WHOLEDISK 2
struct hd_struct *__must_check add_partition(struct gendisk *disk, int partno,
sector_t start, sector_t len, int flags,
struct partition_meta_info *info);
void __delete_partition(struct percpu_ref *ref); void __delete_partition(struct percpu_ref *ref);
void delete_partition(struct gendisk *disk, int partno); void delete_partition(struct gendisk *disk, int partno);
int bdev_add_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length);
int bdev_del_partition(struct block_device *bdev, int partno);
int bdev_resize_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length);
int disk_expand_part_tbl(struct gendisk *disk, int target); int disk_expand_part_tbl(struct gendisk *disk, int target);
static inline int hd_ref_init(struct hd_struct *part) static inline int hd_ref_init(struct hd_struct *part)
......
...@@ -16,140 +16,42 @@ ...@@ -16,140 +16,42 @@
static int blkpg_do_ioctl(struct block_device *bdev, static int blkpg_do_ioctl(struct block_device *bdev,
struct blkpg_partition __user *upart, int op) struct blkpg_partition __user *upart, int op)
{ {
struct block_device *bdevp;
struct gendisk *disk;
struct hd_struct *part, *lpart;
struct blkpg_partition p; struct blkpg_partition p;
struct disk_part_iter piter;
long long start, length; long long start, length;
int partno;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EACCES; return -EACCES;
if (copy_from_user(&p, upart, sizeof(struct blkpg_partition))) if (copy_from_user(&p, upart, sizeof(struct blkpg_partition)))
return -EFAULT; return -EFAULT;
disk = bdev->bd_disk;
if (bdev != bdev->bd_contains) if (bdev != bdev->bd_contains)
return -EINVAL; return -EINVAL;
partno = p.pno;
if (partno <= 0)
return -EINVAL;
switch (op) {
case BLKPG_ADD_PARTITION:
start = p.start >> 9;
length = p.length >> 9;
/* check for fit in a hd_struct */
if (sizeof(sector_t) == sizeof(long) &&
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
|| pstart < 0 || plength < 0 || partno > 65535)
return -EINVAL;
}
/* check if partition is aligned to blocksize */
if (p.start & (bdev_logical_block_size(bdev) - 1))
return -EINVAL;
mutex_lock(&bdev->bd_mutex); if (p.pno <= 0)
return -EINVAL;
/* overlap? */ if (op == BLKPG_DEL_PARTITION)
disk_part_iter_init(&piter, disk, return bdev_del_partition(bdev, p.pno);
DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter))) {
if (!(start + length <= part->start_sect ||
start >= part->start_sect + part->nr_sects)) {
disk_part_iter_exit(&piter);
mutex_unlock(&bdev->bd_mutex);
return -EBUSY;
}
}
disk_part_iter_exit(&piter);
/* all seems OK */ start = p.start >> SECTOR_SHIFT;
part = add_partition(disk, partno, start, length, length = p.length >> SECTOR_SHIFT;
ADDPART_FLAG_NONE, NULL);
mutex_unlock(&bdev->bd_mutex);
return PTR_ERR_OR_ZERO(part);
case BLKPG_DEL_PARTITION:
part = disk_get_part(disk, partno);
if (!part)
return -ENXIO;
bdevp = bdget(part_devt(part));
disk_put_part(part);
if (!bdevp)
return -ENOMEM;
mutex_lock(&bdevp->bd_mutex);
if (bdevp->bd_openers) {
mutex_unlock(&bdevp->bd_mutex);
bdput(bdevp);
return -EBUSY;
}
/* all seems OK */
fsync_bdev(bdevp);
invalidate_bdev(bdevp);
mutex_lock_nested(&bdev->bd_mutex, 1);
delete_partition(disk, partno);
mutex_unlock(&bdev->bd_mutex);
mutex_unlock(&bdevp->bd_mutex);
bdput(bdevp);
return 0;
case BLKPG_RESIZE_PARTITION:
start = p.start >> 9;
/* new length of partition in bytes */
length = p.length >> 9;
/* check for fit in a hd_struct */ /* check for fit in a hd_struct */
if (sizeof(sector_t) == sizeof(long) && if (sizeof(sector_t) < sizeof(long long)) {
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length; long pstart = start, plength = length;
if (pstart != start || plength != length
|| pstart < 0 || plength < 0) if (pstart != start || plength != length || pstart < 0 ||
plength < 0 || p.pno > 65535)
return -EINVAL; return -EINVAL;
} }
part = disk_get_part(disk, partno);
if (!part) switch (op) {
return -ENXIO; case BLKPG_ADD_PARTITION:
bdevp = bdget(part_devt(part)); /* check if partition is aligned to blocksize */
if (!bdevp) { if (p.start & (bdev_logical_block_size(bdev) - 1))
disk_put_part(part);
return -ENOMEM;
}
mutex_lock(&bdevp->bd_mutex);
mutex_lock_nested(&bdev->bd_mutex, 1);
if (start != part->start_sect) {
mutex_unlock(&bdevp->bd_mutex);
mutex_unlock(&bdev->bd_mutex);
bdput(bdevp);
disk_put_part(part);
return -EINVAL; return -EINVAL;
} return bdev_add_partition(bdev, p.pno, start, length);
/* overlap? */ case BLKPG_RESIZE_PARTITION:
disk_part_iter_init(&piter, disk, return bdev_resize_partition(bdev, p.pno, start, length);
DISK_PITER_INCL_EMPTY);
while ((lpart = disk_part_iter_next(&piter))) {
if (lpart->partno != partno &&
!(start + length <= lpart->start_sect ||
start >= lpart->start_sect + lpart->nr_sects)
) {
disk_part_iter_exit(&piter);
mutex_unlock(&bdevp->bd_mutex);
mutex_unlock(&bdev->bd_mutex);
bdput(bdevp);
disk_put_part(part);
return -EBUSY;
}
}
disk_part_iter_exit(&piter);
part_nr_sects_write(part, (sector_t)length);
i_size_write(bdevp->bd_inode, p.length);
mutex_unlock(&bdevp->bd_mutex);
mutex_unlock(&bdev->bd_mutex);
bdput(bdevp);
disk_put_part(part);
return 0;
default: default:
return -EINVAL; return -EINVAL;
} }
......
...@@ -335,7 +335,7 @@ static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL); ...@@ -335,7 +335,7 @@ static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
* Must be called either with bd_mutex held, before a disk can be opened or * Must be called either with bd_mutex held, before a disk can be opened or
* after all disk users are gone. * after all disk users are gone.
*/ */
struct hd_struct *add_partition(struct gendisk *disk, int partno, static struct hd_struct *add_partition(struct gendisk *disk, int partno,
sector_t start, sector_t len, int flags, sector_t start, sector_t len, int flags,
struct partition_meta_info *info) struct partition_meta_info *info)
{ {
...@@ -472,6 +472,119 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -472,6 +472,119 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno,
return ERR_PTR(err); return ERR_PTR(err);
} }
static bool partition_overlaps(struct gendisk *disk, sector_t start,
sector_t length, int skip_partno)
{
struct disk_part_iter piter;
struct hd_struct *part;
bool overlap = false;
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter))) {
if (part->partno == skip_partno ||
start >= part->start_sect + part->nr_sects ||
start + length <= part->start_sect)
continue;
overlap = true;
break;
}
disk_part_iter_exit(&piter);
return overlap;
}
int bdev_add_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length)
{
struct hd_struct *part;
mutex_lock(&bdev->bd_mutex);
if (partition_overlaps(bdev->bd_disk, start, length, -1)) {
mutex_unlock(&bdev->bd_mutex);
return -EBUSY;
}
part = add_partition(bdev->bd_disk, partno, start, length,
ADDPART_FLAG_NONE, NULL);
mutex_unlock(&bdev->bd_mutex);
return PTR_ERR_OR_ZERO(part);
}
int bdev_del_partition(struct block_device *bdev, int partno)
{
struct block_device *bdevp;
struct hd_struct *part;
int ret = 0;
part = disk_get_part(bdev->bd_disk, partno);
if (!part)
return -ENXIO;
bdevp = bdget(part_devt(part));
disk_put_part(part);
if (!bdevp)
return -ENOMEM;
mutex_lock(&bdevp->bd_mutex);
ret = -EBUSY;
if (bdevp->bd_openers)
goto out_unlock;
fsync_bdev(bdevp);
invalidate_bdev(bdevp);
mutex_lock_nested(&bdev->bd_mutex, 1);
delete_partition(bdev->bd_disk, partno);
mutex_unlock(&bdev->bd_mutex);
ret = 0;
out_unlock:
mutex_unlock(&bdevp->bd_mutex);
bdput(bdevp);
return ret;
}
int bdev_resize_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length)
{
struct block_device *bdevp;
struct hd_struct *part;
int ret = 0;
part = disk_get_part(bdev->bd_disk, partno);
if (!part)
return -ENXIO;
ret = -ENOMEM;
bdevp = bdget(part_devt(part));
if (!bdevp)
goto out_put_part;
mutex_lock(&bdevp->bd_mutex);
mutex_lock_nested(&bdev->bd_mutex, 1);
ret = -EINVAL;
if (start != part->start_sect)
goto out_unlock;
ret = -EBUSY;
if (partition_overlaps(bdev->bd_disk, start, length, partno))
goto out_unlock;
part_nr_sects_write(part, (sector_t)length);
i_size_write(bdevp->bd_inode, length << SECTOR_SHIFT);
ret = 0;
out_unlock:
mutex_unlock(&bdevp->bd_mutex);
mutex_unlock(&bdev->bd_mutex);
bdput(bdevp);
out_put_part:
disk_put_part(part);
return ret;
}
static bool disk_unlock_native_capacity(struct gendisk *disk) static bool disk_unlock_native_capacity(struct gendisk *disk)
{ {
const struct block_device_operations *bdops = disk->fops; const struct block_device_operations *bdops = disk->fops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment