1c59ede7bSRandy.Dunlap #include <linux/capability.h> 21da177e4SLinus Torvalds #include <linux/blkdev.h> 31da177e4SLinus Torvalds #include <linux/blkpg.h> 4a885c8c4SChristoph Hellwig #include <linux/hdreg.h> 51da177e4SLinus Torvalds #include <linux/backing-dev.h> 61da177e4SLinus Torvalds #include <linux/buffer_head.h> 71da177e4SLinus Torvalds #include <linux/smp_lock.h> 82056a782SJens Axboe #include <linux/blktrace_api.h> 91da177e4SLinus Torvalds #include <asm/uaccess.h> 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) 121da177e4SLinus Torvalds { 131da177e4SLinus Torvalds struct block_device *bdevp; 141da177e4SLinus Torvalds struct gendisk *disk; 15e71bf0d0STejun Heo struct hd_struct *part; 161da177e4SLinus Torvalds struct blkpg_ioctl_arg a; 171da177e4SLinus Torvalds struct blkpg_partition p; 18e71bf0d0STejun Heo struct disk_part_iter piter; 191da177e4SLinus Torvalds long long start, length; 20cf771cb5STejun Heo int partno; 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 231da177e4SLinus Torvalds return -EACCES; 241da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) 251da177e4SLinus Torvalds return -EFAULT; 261da177e4SLinus Torvalds if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) 271da177e4SLinus Torvalds return -EFAULT; 281da177e4SLinus Torvalds disk = bdev->bd_disk; 291da177e4SLinus Torvalds if (bdev != bdev->bd_contains) 301da177e4SLinus Torvalds return -EINVAL; 31cf771cb5STejun Heo partno = p.pno; 32540eed56STejun Heo if (partno <= 0) 331da177e4SLinus Torvalds return -EINVAL; 341da177e4SLinus Torvalds switch (a.op) { 351da177e4SLinus Torvalds case BLKPG_ADD_PARTITION: 361da177e4SLinus Torvalds start = p.start >> 9; 371da177e4SLinus Torvalds length = p.length >> 9; 381da177e4SLinus Torvalds /* check for fit in a hd_struct */ 391da177e4SLinus Torvalds if (sizeof(sector_t) == sizeof(long) && 401da177e4SLinus Torvalds sizeof(long long) > sizeof(long)) { 411da177e4SLinus Torvalds long pstart = start, plength = length; 421da177e4SLinus Torvalds if (pstart != start || plength != length 431da177e4SLinus Torvalds || pstart < 0 || plength < 0) 441da177e4SLinus Torvalds return -EINVAL; 451da177e4SLinus Torvalds } 4688e34126STejun Heo 47c039e313SArjan van de Ven mutex_lock(&bdev->bd_mutex); 4888e34126STejun Heo 491da177e4SLinus Torvalds /* overlap? */ 50e71bf0d0STejun Heo disk_part_iter_init(&piter, disk, 51e71bf0d0STejun Heo DISK_PITER_INCL_EMPTY); 52e71bf0d0STejun Heo while ((part = disk_part_iter_next(&piter))) { 53e71bf0d0STejun Heo if (!(start + length <= part->start_sect || 54e71bf0d0STejun Heo start >= part->start_sect + part->nr_sects)) { 55e71bf0d0STejun Heo disk_part_iter_exit(&piter); 56c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 571da177e4SLinus Torvalds return -EBUSY; 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds } 60e71bf0d0STejun Heo disk_part_iter_exit(&piter); 61e71bf0d0STejun Heo 621da177e4SLinus Torvalds /* all seems OK */ 63ba32929aSTejun Heo part = add_partition(disk, partno, start, length, 64cf771cb5STejun Heo ADDPART_FLAG_NONE); 65c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 66ba32929aSTejun Heo return IS_ERR(part) ? PTR_ERR(part) : 0; 671da177e4SLinus Torvalds case BLKPG_DEL_PARTITION: 68e71bf0d0STejun Heo part = disk_get_part(disk, partno); 69e71bf0d0STejun Heo if (!part) 701da177e4SLinus Torvalds return -ENXIO; 71e71bf0d0STejun Heo 72e71bf0d0STejun Heo bdevp = bdget(part_devt(part)); 73e71bf0d0STejun Heo disk_put_part(part); 741da177e4SLinus Torvalds if (!bdevp) 751da177e4SLinus Torvalds return -ENOMEM; 76e71bf0d0STejun Heo 772e7b651dSPeter Zijlstra mutex_lock(&bdevp->bd_mutex); 781da177e4SLinus Torvalds if (bdevp->bd_openers) { 79c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 801da177e4SLinus Torvalds bdput(bdevp); 811da177e4SLinus Torvalds return -EBUSY; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds /* all seems OK */ 841da177e4SLinus Torvalds fsync_bdev(bdevp); 85f98393a6SPeter Zijlstra invalidate_bdev(bdevp); 861da177e4SLinus Torvalds 876d740cd5SPeter Zijlstra mutex_lock_nested(&bdev->bd_mutex, 1); 88cf771cb5STejun Heo delete_partition(disk, partno); 89c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 90c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 911da177e4SLinus Torvalds bdput(bdevp); 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds return 0; 941da177e4SLinus Torvalds default: 951da177e4SLinus Torvalds return -EINVAL; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds static int blkdev_reread_part(struct block_device *bdev) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds struct gendisk *disk = bdev->bd_disk; 1021da177e4SLinus Torvalds int res; 1031da177e4SLinus Torvalds 104b5d0b9dfSTejun Heo if (!disk_partitionable(disk) || bdev != bdev->bd_contains) 1051da177e4SLinus Torvalds return -EINVAL; 1061da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 1071da177e4SLinus Torvalds return -EACCES; 108c039e313SArjan van de Ven if (!mutex_trylock(&bdev->bd_mutex)) 1091da177e4SLinus Torvalds return -EBUSY; 1101da177e4SLinus Torvalds res = rescan_partitions(disk, bdev); 111c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 1121da177e4SLinus Torvalds return res; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 115d30a2605SDavid Woodhouse static void blk_ioc_discard_endio(struct bio *bio, int err) 116d30a2605SDavid Woodhouse { 117d30a2605SDavid Woodhouse if (err) { 118d30a2605SDavid Woodhouse if (err == -EOPNOTSUPP) 119d30a2605SDavid Woodhouse set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); 120d30a2605SDavid Woodhouse clear_bit(BIO_UPTODATE, &bio->bi_flags); 121d30a2605SDavid Woodhouse } 122d30a2605SDavid Woodhouse complete(bio->bi_private); 123d30a2605SDavid Woodhouse } 124d30a2605SDavid Woodhouse 125d30a2605SDavid Woodhouse static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, 126d30a2605SDavid Woodhouse uint64_t len) 127d30a2605SDavid Woodhouse { 128d30a2605SDavid Woodhouse struct request_queue *q = bdev_get_queue(bdev); 129d30a2605SDavid Woodhouse int ret = 0; 130d30a2605SDavid Woodhouse 131d30a2605SDavid Woodhouse if (start & 511) 132d30a2605SDavid Woodhouse return -EINVAL; 133d30a2605SDavid Woodhouse if (len & 511) 134d30a2605SDavid Woodhouse return -EINVAL; 135d30a2605SDavid Woodhouse start >>= 9; 136d30a2605SDavid Woodhouse len >>= 9; 137d30a2605SDavid Woodhouse 138d30a2605SDavid Woodhouse if (start + len > (bdev->bd_inode->i_size >> 9)) 139d30a2605SDavid Woodhouse return -EINVAL; 140d30a2605SDavid Woodhouse 141d30a2605SDavid Woodhouse if (!q->prepare_discard_fn) 142d30a2605SDavid Woodhouse return -EOPNOTSUPP; 143d30a2605SDavid Woodhouse 144d30a2605SDavid Woodhouse while (len && !ret) { 145d30a2605SDavid Woodhouse DECLARE_COMPLETION_ONSTACK(wait); 146d30a2605SDavid Woodhouse struct bio *bio; 147d30a2605SDavid Woodhouse 148d30a2605SDavid Woodhouse bio = bio_alloc(GFP_KERNEL, 0); 149d30a2605SDavid Woodhouse 150d30a2605SDavid Woodhouse bio->bi_end_io = blk_ioc_discard_endio; 151d30a2605SDavid Woodhouse bio->bi_bdev = bdev; 152d30a2605SDavid Woodhouse bio->bi_private = &wait; 153d30a2605SDavid Woodhouse bio->bi_sector = start; 154d30a2605SDavid Woodhouse 155d30a2605SDavid Woodhouse if (len > q->max_hw_sectors) { 156d30a2605SDavid Woodhouse bio->bi_size = q->max_hw_sectors << 9; 157d30a2605SDavid Woodhouse len -= q->max_hw_sectors; 158d30a2605SDavid Woodhouse start += q->max_hw_sectors; 159d30a2605SDavid Woodhouse } else { 160d30a2605SDavid Woodhouse bio->bi_size = len << 9; 161d30a2605SDavid Woodhouse len = 0; 162d30a2605SDavid Woodhouse } 163e17fc0a1SDavid Woodhouse submit_bio(DISCARD_NOBARRIER, bio); 164d30a2605SDavid Woodhouse 165d30a2605SDavid Woodhouse wait_for_completion(&wait); 166d30a2605SDavid Woodhouse 167d30a2605SDavid Woodhouse if (bio_flagged(bio, BIO_EOPNOTSUPP)) 168d30a2605SDavid Woodhouse ret = -EOPNOTSUPP; 169d30a2605SDavid Woodhouse else if (!bio_flagged(bio, BIO_UPTODATE)) 170d30a2605SDavid Woodhouse ret = -EIO; 171d30a2605SDavid Woodhouse bio_put(bio); 172d30a2605SDavid Woodhouse } 173d30a2605SDavid Woodhouse return ret; 174d30a2605SDavid Woodhouse } 175d30a2605SDavid Woodhouse 1761da177e4SLinus Torvalds static int put_ushort(unsigned long arg, unsigned short val) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds return put_user(val, (unsigned short __user *)arg); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds static int put_int(unsigned long arg, int val) 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds return put_user(val, (int __user *)arg); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static int put_long(unsigned long arg, long val) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds return put_user(val, (long __user *)arg); 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds static int put_ulong(unsigned long arg, unsigned long val) 1921da177e4SLinus Torvalds { 1931da177e4SLinus Torvalds return put_user(val, (unsigned long __user *)arg); 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds static int put_u64(unsigned long arg, u64 val) 1971da177e4SLinus Torvalds { 1981da177e4SLinus Torvalds return put_user(val, (u64 __user *)arg); 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 201633a08b8SAl Viro int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode, 202633a08b8SAl Viro unsigned cmd, unsigned long arg) 203633a08b8SAl Viro { 204633a08b8SAl Viro struct gendisk *disk = bdev->bd_disk; 205633a08b8SAl Viro int ret; 206d4430d62SAl Viro 207d4430d62SAl Viro if (disk->fops->ioctl) 208d4430d62SAl Viro return disk->fops->ioctl(bdev, mode, cmd, arg); 209d4430d62SAl Viro 210d4430d62SAl Viro if (disk->fops->locked_ioctl) { 211d4430d62SAl Viro lock_kernel(); 212d4430d62SAl Viro ret = disk->fops->locked_ioctl(bdev, mode, cmd, arg); 213633a08b8SAl Viro unlock_kernel(); 214633a08b8SAl Viro return ret; 215633a08b8SAl Viro } 216633a08b8SAl Viro 217633a08b8SAl Viro return -ENOTTY; 218633a08b8SAl Viro } 219633a08b8SAl Viro /* 220633a08b8SAl Viro * For the record: _GPL here is only because somebody decided to slap it 221633a08b8SAl Viro * on the previous export. Sheer idiocy, since it wasn't copyrightable 222633a08b8SAl Viro * at all and could be open-coded without any exports by anybody who cares. 223633a08b8SAl Viro */ 224633a08b8SAl Viro EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl); 225633a08b8SAl Viro 226f58c4c0aSArnd Bergmann /* 227f58c4c0aSArnd Bergmann * always keep this in sync with compat_blkdev_ioctl() and 228f58c4c0aSArnd Bergmann * compat_blkdev_locked_ioctl() 229f58c4c0aSArnd Bergmann */ 23056b26addSAl Viro int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, 231bb93e3a5SArnd Bergmann unsigned long arg) 232bb93e3a5SArnd Bergmann { 233bb93e3a5SArnd Bergmann struct gendisk *disk = bdev->bd_disk; 23445048d09SAl Viro struct backing_dev_info *bdi; 23545048d09SAl Viro loff_t size; 236bb93e3a5SArnd Bergmann int ret, n; 237bb93e3a5SArnd Bergmann 238bb93e3a5SArnd Bergmann switch(cmd) { 2391da177e4SLinus Torvalds case BLKFLSBUF: 2401da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2411da177e4SLinus Torvalds return -EACCES; 242bb93e3a5SArnd Bergmann 243e436fdaeSAl Viro ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 2441da177e4SLinus Torvalds /* -EINVAL to handle old uncorrected drivers */ 2451da177e4SLinus Torvalds if (ret != -EINVAL && ret != -ENOTTY) 2461da177e4SLinus Torvalds return ret; 247bb93e3a5SArnd Bergmann 248bb93e3a5SArnd Bergmann lock_kernel(); 2491da177e4SLinus Torvalds fsync_bdev(bdev); 250f98393a6SPeter Zijlstra invalidate_bdev(bdev); 251bb93e3a5SArnd Bergmann unlock_kernel(); 2521da177e4SLinus Torvalds return 0; 253bb93e3a5SArnd Bergmann 2541da177e4SLinus Torvalds case BLKROSET: 255e436fdaeSAl Viro ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 2561da177e4SLinus Torvalds /* -EINVAL to handle old uncorrected drivers */ 2571da177e4SLinus Torvalds if (ret != -EINVAL && ret != -ENOTTY) 2581da177e4SLinus Torvalds return ret; 2591da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2601da177e4SLinus Torvalds return -EACCES; 2611da177e4SLinus Torvalds if (get_user(n, (int __user *)(arg))) 2621da177e4SLinus Torvalds return -EFAULT; 263bb93e3a5SArnd Bergmann lock_kernel(); 2641da177e4SLinus Torvalds set_device_ro(bdev, n); 265bb93e3a5SArnd Bergmann unlock_kernel(); 2661da177e4SLinus Torvalds return 0; 267d30a2605SDavid Woodhouse 268d30a2605SDavid Woodhouse case BLKDISCARD: { 269d30a2605SDavid Woodhouse uint64_t range[2]; 270d30a2605SDavid Woodhouse 271e436fdaeSAl Viro if (!(mode & FMODE_WRITE)) 272d30a2605SDavid Woodhouse return -EBADF; 273d30a2605SDavid Woodhouse 274d30a2605SDavid Woodhouse if (copy_from_user(range, (void __user *)arg, sizeof(range))) 275d30a2605SDavid Woodhouse return -EFAULT; 276d30a2605SDavid Woodhouse 277d30a2605SDavid Woodhouse return blk_ioctl_discard(bdev, range[0], range[1]); 278d30a2605SDavid Woodhouse } 279d30a2605SDavid Woodhouse 280a885c8c4SChristoph Hellwig case HDIO_GETGEO: { 281a885c8c4SChristoph Hellwig struct hd_geometry geo; 282a885c8c4SChristoph Hellwig 283a885c8c4SChristoph Hellwig if (!arg) 284a885c8c4SChristoph Hellwig return -EINVAL; 285a885c8c4SChristoph Hellwig if (!disk->fops->getgeo) 286a885c8c4SChristoph Hellwig return -ENOTTY; 287a885c8c4SChristoph Hellwig 288a885c8c4SChristoph Hellwig /* 289a885c8c4SChristoph Hellwig * We need to set the startsect first, the driver may 290a885c8c4SChristoph Hellwig * want to override it. 291a885c8c4SChristoph Hellwig */ 292a885c8c4SChristoph Hellwig geo.start = get_start_sect(bdev); 293a885c8c4SChristoph Hellwig ret = disk->fops->getgeo(bdev, &geo); 294a885c8c4SChristoph Hellwig if (ret) 295a885c8c4SChristoph Hellwig return ret; 296a885c8c4SChristoph Hellwig if (copy_to_user((struct hd_geometry __user *)arg, &geo, 297a885c8c4SChristoph Hellwig sizeof(geo))) 298a885c8c4SChristoph Hellwig return -EFAULT; 299a885c8c4SChristoph Hellwig return 0; 300a885c8c4SChristoph Hellwig } 30145048d09SAl Viro case BLKRAGET: 30245048d09SAl Viro case BLKFRAGET: 30345048d09SAl Viro if (!arg) 30445048d09SAl Viro return -EINVAL; 30545048d09SAl Viro bdi = blk_get_backing_dev_info(bdev); 30645048d09SAl Viro if (bdi == NULL) 30745048d09SAl Viro return -ENOTTY; 30845048d09SAl Viro return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); 30945048d09SAl Viro case BLKROGET: 31045048d09SAl Viro return put_int(arg, bdev_read_only(bdev) != 0); 31145048d09SAl Viro case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ 31245048d09SAl Viro return put_int(arg, block_size(bdev)); 31345048d09SAl Viro case BLKSSZGET: /* get block device hardware sector size */ 314*e1defc4fSMartin K. Petersen return put_int(arg, bdev_logical_block_size(bdev)); 31545048d09SAl Viro case BLKSECTGET: 31645048d09SAl Viro return put_ushort(arg, bdev_get_queue(bdev)->max_sectors); 31745048d09SAl Viro case BLKRASET: 31845048d09SAl Viro case BLKFRASET: 31945048d09SAl Viro if(!capable(CAP_SYS_ADMIN)) 32045048d09SAl Viro return -EACCES; 32145048d09SAl Viro bdi = blk_get_backing_dev_info(bdev); 32245048d09SAl Viro if (bdi == NULL) 32345048d09SAl Viro return -ENOTTY; 32445048d09SAl Viro bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; 32545048d09SAl Viro return 0; 32645048d09SAl Viro case BLKBSZSET: 32745048d09SAl Viro /* set the logical block size */ 32845048d09SAl Viro if (!capable(CAP_SYS_ADMIN)) 32945048d09SAl Viro return -EACCES; 33045048d09SAl Viro if (!arg) 33145048d09SAl Viro return -EINVAL; 33245048d09SAl Viro if (get_user(n, (int __user *) arg)) 33345048d09SAl Viro return -EFAULT; 3346af3a56eSAl Viro if (!(mode & FMODE_EXCL) && bd_claim(bdev, &bdev) < 0) 33545048d09SAl Viro return -EBUSY; 33645048d09SAl Viro ret = set_blocksize(bdev, n); 3376af3a56eSAl Viro if (!(mode & FMODE_EXCL)) 33845048d09SAl Viro bd_release(bdev); 339bb93e3a5SArnd Bergmann return ret; 34045048d09SAl Viro case BLKPG: 34145048d09SAl Viro lock_kernel(); 34245048d09SAl Viro ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); 34345048d09SAl Viro unlock_kernel(); 34445048d09SAl Viro break; 34545048d09SAl Viro case BLKRRPART: 34645048d09SAl Viro lock_kernel(); 34745048d09SAl Viro ret = blkdev_reread_part(bdev); 34845048d09SAl Viro unlock_kernel(); 34945048d09SAl Viro break; 35045048d09SAl Viro case BLKGETSIZE: 35145048d09SAl Viro size = bdev->bd_inode->i_size; 35245048d09SAl Viro if ((size >> 9) > ~0UL) 35345048d09SAl Viro return -EFBIG; 35445048d09SAl Viro return put_ulong(arg, size >> 9); 35545048d09SAl Viro case BLKGETSIZE64: 35645048d09SAl Viro return put_u64(arg, bdev->bd_inode->i_size); 35745048d09SAl Viro case BLKTRACESTART: 35845048d09SAl Viro case BLKTRACESTOP: 35945048d09SAl Viro case BLKTRACESETUP: 36045048d09SAl Viro case BLKTRACETEARDOWN: 36145048d09SAl Viro lock_kernel(); 36245048d09SAl Viro ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg); 36345048d09SAl Viro unlock_kernel(); 36445048d09SAl Viro break; 36545048d09SAl Viro default: 366e436fdaeSAl Viro ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 3671da177e4SLinus Torvalds } 36845048d09SAl Viro return ret; 36945048d09SAl Viro } 37068f66febSStephen Tweedie EXPORT_SYMBOL_GPL(blkdev_ioctl); 371