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: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
b4fd63f426
commit
fa9156ae59
3 changed files with 142 additions and 125 deletions
|
@ -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)
|
||||||
|
|
144
block/ioctl.c
144
block/ioctl.c
|
@ -16,143 +16,45 @@
|
||||||
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)
|
if (p.pno <= 0)
|
||||||
return -EINVAL;
|
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 (op == BLKPG_DEL_PARTITION)
|
||||||
|
return bdev_del_partition(bdev, p.pno);
|
||||||
|
|
||||||
/* overlap? */
|
start = p.start >> SECTOR_SHIFT;
|
||||||
disk_part_iter_init(&piter, disk,
|
length = p.length >> SECTOR_SHIFT;
|
||||||
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 */
|
/* check for fit in a hd_struct */
|
||||||
part = add_partition(disk, partno, start, length,
|
if (sizeof(sector_t) < sizeof(long long)) {
|
||||||
ADDPART_FLAG_NONE, NULL);
|
long pstart = start, plength = length;
|
||||||
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));
|
if (pstart != start || plength != length || pstart < 0 ||
|
||||||
disk_put_part(part);
|
plength < 0 || p.pno > 65535)
|
||||||
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 */
|
|
||||||
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)
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
part = disk_get_part(disk, partno);
|
|
||||||
if (!part)
|
|
||||||
return -ENXIO;
|
|
||||||
bdevp = bdget(part_devt(part));
|
|
||||||
if (!bdevp) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
/* overlap? */
|
|
||||||
disk_part_iter_init(&piter, disk,
|
|
||||||
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:
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case BLKPG_ADD_PARTITION:
|
||||||
|
/* check if partition is aligned to blocksize */
|
||||||
|
if (p.start & (bdev_logical_block_size(bdev) - 1))
|
||||||
|
return -EINVAL;
|
||||||
|
return bdev_add_partition(bdev, p.pno, start, length);
|
||||||
|
case BLKPG_RESIZE_PARTITION:
|
||||||
|
return bdev_resize_partition(bdev, p.pno, start, length);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blkpg_ioctl(struct block_device *bdev,
|
static int blkpg_ioctl(struct block_device *bdev,
|
||||||
|
|
|
@ -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,
|
||||||
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;
|
||||||
|
|
Loading…
Reference in a new issue