#include #include #include #include #include #include #include #include #include #include static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) { struct block_device *bdevp; struct gendisk *disk; struct hd_struct *part, *lpart; struct blkpg_ioctl_arg a; struct blkpg_partition p; struct disk_part_iter piter; long long start, length; int partno; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) return -EFAULT; if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) return -EFAULT; disk = bdev->bd_disk; if (bdev != bdev->bd_contains) return -EINVAL; partno = p.pno; if (partno <= 0) return -EINVAL; switch (a.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; } mutex_lock(&bdev->bd_mutex); /* overlap? */ disk_part_iter_init(&piter, disk, 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 */ part = add_partition(disk, partno, start, length, 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 */ 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; } } static int blkdev_reread_part(struct block_device *bdev) { struct gendisk *disk = bdev->bd_disk; int res; if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (!mutex_trylock(&bdev->bd_mutex)) return -EBUSY; res = rescan_partitions(disk, bdev); mutex_unlock(&bdev->bd_mutex); return res; } static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, uint64_t len, int secure) { unsigned long flags = 0; if (start & 511) return -EINVAL; if (len & 511) return -EINVAL; start >>= 9; len >>= 9; if (start + len > (i_size_read(bdev->bd_inode) >> 9)) return -EINVAL; if (secure) flags |= BLKDEV_DISCARD_SECURE; return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags); } static int blk_ioctl_zeroout(struct block_device *bdev, uint64_t start, uint64_t len) { if (start & 511) return -EINVAL; if (len & 511) return -EINVAL; start >>= 9; len >>= 9; if (start + len > (i_size_read(bdev->bd_inode) >> 9)) return -EINVAL; return blkdev_issue_zeroout(bdev, start, len, GFP_KERNEL, false); } static int put_ushort(unsigned long arg, unsigned short val) { return put_use