1c59ede7bSRandy.Dunlap #include <linux/capability.h> 23a65dfe8SJens Axboe #include <linux/blkdev.h> 33a65dfe8SJens Axboe #include <linux/blkpg.h> 4a885c8c4SChristoph Hellwig #include <linux/hdreg.h> 53a65dfe8SJens Axboe #include <linux/backing-dev.h> 63a65dfe8SJens Axboe #include <linux/buffer_head.h> 73a65dfe8SJens Axboe #include <linux/smp_lock.h> 82056a782SJens Axboe #include <linux/blktrace_api.h> 93a65dfe8SJens Axboe #include <asm/uaccess.h> 103a65dfe8SJens Axboe 113a65dfe8SJens Axboe static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) 123a65dfe8SJens Axboe { 133a65dfe8SJens Axboe struct block_device *bdevp; 143a65dfe8SJens Axboe struct gendisk *disk; 153a65dfe8SJens Axboe struct blkpg_ioctl_arg a; 163a65dfe8SJens Axboe struct blkpg_partition p; 173a65dfe8SJens Axboe long long start, length; 183a65dfe8SJens Axboe int part; 193a65dfe8SJens Axboe int i; 2004ebd4aeSAbdel Benamrouche int err; 213a65dfe8SJens Axboe 223a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 233a65dfe8SJens Axboe return -EACCES; 243a65dfe8SJens Axboe if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) 253a65dfe8SJens Axboe return -EFAULT; 263a65dfe8SJens Axboe if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) 273a65dfe8SJens Axboe return -EFAULT; 283a65dfe8SJens Axboe disk = bdev->bd_disk; 293a65dfe8SJens Axboe if (bdev != bdev->bd_contains) 303a65dfe8SJens Axboe return -EINVAL; 313a65dfe8SJens Axboe part = p.pno; 323a65dfe8SJens Axboe if (part <= 0 || part >= disk->minors) 333a65dfe8SJens Axboe return -EINVAL; 343a65dfe8SJens Axboe switch (a.op) { 353a65dfe8SJens Axboe case BLKPG_ADD_PARTITION: 363a65dfe8SJens Axboe start = p.start >> 9; 373a65dfe8SJens Axboe length = p.length >> 9; 383a65dfe8SJens Axboe /* check for fit in a hd_struct */ 393a65dfe8SJens Axboe if (sizeof(sector_t) == sizeof(long) && 403a65dfe8SJens Axboe sizeof(long long) > sizeof(long)) { 413a65dfe8SJens Axboe long pstart = start, plength = length; 423a65dfe8SJens Axboe if (pstart != start || plength != length 433a65dfe8SJens Axboe || pstart < 0 || plength < 0) 443a65dfe8SJens Axboe return -EINVAL; 453a65dfe8SJens Axboe } 463a65dfe8SJens Axboe /* partition number in use? */ 47c039e313SArjan van de Ven mutex_lock(&bdev->bd_mutex); 483a65dfe8SJens Axboe if (disk->part[part - 1]) { 49c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 503a65dfe8SJens Axboe return -EBUSY; 513a65dfe8SJens Axboe } 523a65dfe8SJens Axboe /* overlap? */ 533a65dfe8SJens Axboe for (i = 0; i < disk->minors - 1; i++) { 543a65dfe8SJens Axboe struct hd_struct *s = disk->part[i]; 553a65dfe8SJens Axboe 563a65dfe8SJens Axboe if (!s) 573a65dfe8SJens Axboe continue; 583a65dfe8SJens Axboe if (!(start+length <= s->start_sect || 593a65dfe8SJens Axboe start >= s->start_sect + s->nr_sects)) { 60c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 613a65dfe8SJens Axboe return -EBUSY; 623a65dfe8SJens Axboe } 633a65dfe8SJens Axboe } 643a65dfe8SJens Axboe /* all seems OK */ 6504ebd4aeSAbdel Benamrouche err = add_partition(disk, part, start, length, ADDPART_FLAG_NONE); 66c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 6704ebd4aeSAbdel Benamrouche return err; 683a65dfe8SJens Axboe case BLKPG_DEL_PARTITION: 693a65dfe8SJens Axboe if (!disk->part[part-1]) 703a65dfe8SJens Axboe return -ENXIO; 713a65dfe8SJens Axboe if (disk->part[part - 1]->nr_sects == 0) 723a65dfe8SJens Axboe return -ENXIO; 733a65dfe8SJens Axboe bdevp = bdget_disk(disk, part); 743a65dfe8SJens Axboe if (!bdevp) 753a65dfe8SJens Axboe return -ENOMEM; 762e7b651dSPeter Zijlstra mutex_lock(&bdevp->bd_mutex); 773a65dfe8SJens Axboe if (bdevp->bd_openers) { 78c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 793a65dfe8SJens Axboe bdput(bdevp); 803a65dfe8SJens Axboe return -EBUSY; 813a65dfe8SJens Axboe } 823a65dfe8SJens Axboe /* all seems OK */ 833a65dfe8SJens Axboe fsync_bdev(bdevp); 84f98393a6SPeter Zijlstra invalidate_bdev(bdevp); 853a65dfe8SJens Axboe 866d740cd5SPeter Zijlstra mutex_lock_nested(&bdev->bd_mutex, 1); 873a65dfe8SJens Axboe delete_partition(disk, part); 88c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 89c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 903a65dfe8SJens Axboe bdput(bdevp); 913a65dfe8SJens Axboe 923a65dfe8SJens Axboe return 0; 933a65dfe8SJens Axboe default: 943a65dfe8SJens Axboe return -EINVAL; 953a65dfe8SJens Axboe } 963a65dfe8SJens Axboe } 973a65dfe8SJens Axboe 983a65dfe8SJens Axboe static int blkdev_reread_part(struct block_device *bdev) 993a65dfe8SJens Axboe { 1003a65dfe8SJens Axboe struct gendisk *disk = bdev->bd_disk; 1013a65dfe8SJens Axboe int res; 1023a65dfe8SJens Axboe 1033a65dfe8SJens Axboe if (disk->minors == 1 || bdev != bdev->bd_contains) 1043a65dfe8SJens Axboe return -EINVAL; 1053a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 1063a65dfe8SJens Axboe return -EACCES; 107c039e313SArjan van de Ven if (!mutex_trylock(&bdev->bd_mutex)) 1083a65dfe8SJens Axboe return -EBUSY; 1093a65dfe8SJens Axboe res = rescan_partitions(disk, bdev); 110c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 1113a65dfe8SJens Axboe return res; 1123a65dfe8SJens Axboe } 1133a65dfe8SJens Axboe 114*d30a2605SDavid Woodhouse static void blk_ioc_discard_endio(struct bio *bio, int err) 115*d30a2605SDavid Woodhouse { 116*d30a2605SDavid Woodhouse if (err) { 117*d30a2605SDavid Woodhouse if (err == -EOPNOTSUPP) 118*d30a2605SDavid Woodhouse set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); 119*d30a2605SDavid Woodhouse clear_bit(BIO_UPTODATE, &bio->bi_flags); 120*d30a2605SDavid Woodhouse } 121*d30a2605SDavid Woodhouse complete(bio->bi_private); 122*d30a2605SDavid Woodhouse } 123*d30a2605SDavid Woodhouse 124*d30a2605SDavid Woodhouse static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, 125*d30a2605SDavid Woodhouse uint64_t len) 126*d30a2605SDavid Woodhouse { 127*d30a2605SDavid Woodhouse struct request_queue *q = bdev_get_queue(bdev); 128*d30a2605SDavid Woodhouse int ret = 0; 129*d30a2605SDavid Woodhouse 130*d30a2605SDavid Woodhouse if (start & 511) 131*d30a2605SDavid Woodhouse return -EINVAL; 132*d30a2605SDavid Woodhouse if (len & 511) 133*d30a2605SDavid Woodhouse return -EINVAL; 134*d30a2605SDavid Woodhouse start >>= 9; 135*d30a2605SDavid Woodhouse len >>= 9; 136*d30a2605SDavid Woodhouse 137*d30a2605SDavid Woodhouse if (start + len > (bdev->bd_inode->i_size >> 9)) 138*d30a2605SDavid Woodhouse return -EINVAL; 139*d30a2605SDavid Woodhouse 140*d30a2605SDavid Woodhouse if (!q->prepare_discard_fn) 141*d30a2605SDavid Woodhouse return -EOPNOTSUPP; 142*d30a2605SDavid Woodhouse 143*d30a2605SDavid Woodhouse while (len && !ret) { 144*d30a2605SDavid Woodhouse DECLARE_COMPLETION_ONSTACK(wait); 145*d30a2605SDavid Woodhouse struct bio *bio; 146*d30a2605SDavid Woodhouse 147*d30a2605SDavid Woodhouse bio = bio_alloc(GFP_KERNEL, 0); 148*d30a2605SDavid Woodhouse if (!bio) 149*d30a2605SDavid Woodhouse return -ENOMEM; 150*d30a2605SDavid Woodhouse 151*d30a2605SDavid Woodhouse bio->bi_end_io = blk_ioc_discard_endio; 152*d30a2605SDavid Woodhouse bio->bi_bdev = bdev; 153*d30a2605SDavid Woodhouse bio->bi_private = &wait; 154*d30a2605SDavid Woodhouse bio->bi_sector = start; 155*d30a2605SDavid Woodhouse 156*d30a2605SDavid Woodhouse if (len > q->max_hw_sectors) { 157*d30a2605SDavid Woodhouse bio->bi_size = q->max_hw_sectors << 9; 158*d30a2605SDavid Woodhouse len -= q->max_hw_sectors; 159*d30a2605SDavid Woodhouse start += q->max_hw_sectors; 160*d30a2605SDavid Woodhouse } else { 161*d30a2605SDavid Woodhouse bio->bi_size = len << 9; 162*d30a2605SDavid Woodhouse len = 0; 163*d30a2605SDavid Woodhouse } 164*d30a2605SDavid Woodhouse submit_bio(WRITE_DISCARD, bio); 165*d30a2605SDavid Woodhouse 166*d30a2605SDavid Woodhouse wait_for_completion(&wait); 167*d30a2605SDavid Woodhouse 168*d30a2605SDavid Woodhouse if (bio_flagged(bio, BIO_EOPNOTSUPP)) 169*d30a2605SDavid Woodhouse ret = -EOPNOTSUPP; 170*d30a2605SDavid Woodhouse else if (!bio_flagged(bio, BIO_UPTODATE)) 171*d30a2605SDavid Woodhouse ret = -EIO; 172*d30a2605SDavid Woodhouse bio_put(bio); 173*d30a2605SDavid Woodhouse } 174*d30a2605SDavid Woodhouse return ret; 175*d30a2605SDavid Woodhouse } 176*d30a2605SDavid Woodhouse 1773a65dfe8SJens Axboe static int put_ushort(unsigned long arg, unsigned short val) 1783a65dfe8SJens Axboe { 1793a65dfe8SJens Axboe return put_user(val, (unsigned short __user *)arg); 1803a65dfe8SJens Axboe } 1813a65dfe8SJens Axboe 1823a65dfe8SJens Axboe static int put_int(unsigned long arg, int val) 1833a65dfe8SJens Axboe { 1843a65dfe8SJens Axboe return put_user(val, (int __user *)arg); 1853a65dfe8SJens Axboe } 1863a65dfe8SJens Axboe 1873a65dfe8SJens Axboe static int put_long(unsigned long arg, long val) 1883a65dfe8SJens Axboe { 1893a65dfe8SJens Axboe return put_user(val, (long __user *)arg); 1903a65dfe8SJens Axboe } 1913a65dfe8SJens Axboe 1923a65dfe8SJens Axboe static int put_ulong(unsigned long arg, unsigned long val) 1933a65dfe8SJens Axboe { 1943a65dfe8SJens Axboe return put_user(val, (unsigned long __user *)arg); 1953a65dfe8SJens Axboe } 1963a65dfe8SJens Axboe 1973a65dfe8SJens Axboe static int put_u64(unsigned long arg, u64 val) 1983a65dfe8SJens Axboe { 1993a65dfe8SJens Axboe return put_user(val, (u64 __user *)arg); 2003a65dfe8SJens Axboe } 2013a65dfe8SJens Axboe 2023a65dfe8SJens Axboe static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, 2033a65dfe8SJens Axboe unsigned cmd, unsigned long arg) 2043a65dfe8SJens Axboe { 2053a65dfe8SJens Axboe struct backing_dev_info *bdi; 2063a65dfe8SJens Axboe int ret, n; 2073a65dfe8SJens Axboe 2083a65dfe8SJens Axboe switch (cmd) { 2093a65dfe8SJens Axboe case BLKRAGET: 2103a65dfe8SJens Axboe case BLKFRAGET: 2113a65dfe8SJens Axboe if (!arg) 2123a65dfe8SJens Axboe return -EINVAL; 2133a65dfe8SJens Axboe bdi = blk_get_backing_dev_info(bdev); 2143a65dfe8SJens Axboe if (bdi == NULL) 2153a65dfe8SJens Axboe return -ENOTTY; 2163a65dfe8SJens Axboe return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); 2173a65dfe8SJens Axboe case BLKROGET: 2183a65dfe8SJens Axboe return put_int(arg, bdev_read_only(bdev) != 0); 2193a65dfe8SJens Axboe case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ 2203a65dfe8SJens Axboe return put_int(arg, block_size(bdev)); 2213a65dfe8SJens Axboe case BLKSSZGET: /* get block device hardware sector size */ 2223a65dfe8SJens Axboe return put_int(arg, bdev_hardsect_size(bdev)); 2233a65dfe8SJens Axboe case BLKSECTGET: 2243a65dfe8SJens Axboe return put_ushort(arg, bdev_get_queue(bdev)->max_sectors); 2253a65dfe8SJens Axboe case BLKRASET: 2263a65dfe8SJens Axboe case BLKFRASET: 2273a65dfe8SJens Axboe if(!capable(CAP_SYS_ADMIN)) 2283a65dfe8SJens Axboe return -EACCES; 2293a65dfe8SJens Axboe bdi = blk_get_backing_dev_info(bdev); 2303a65dfe8SJens Axboe if (bdi == NULL) 2313a65dfe8SJens Axboe return -ENOTTY; 2323a65dfe8SJens Axboe bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; 2333a65dfe8SJens Axboe return 0; 2343a65dfe8SJens Axboe case BLKBSZSET: 2353a65dfe8SJens Axboe /* set the logical block size */ 2363a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 2373a65dfe8SJens Axboe return -EACCES; 2383a65dfe8SJens Axboe if (!arg) 2393a65dfe8SJens Axboe return -EINVAL; 2403a65dfe8SJens Axboe if (get_user(n, (int __user *) arg)) 2413a65dfe8SJens Axboe return -EFAULT; 2423a65dfe8SJens Axboe if (bd_claim(bdev, file) < 0) 2433a65dfe8SJens Axboe return -EBUSY; 2443a65dfe8SJens Axboe ret = set_blocksize(bdev, n); 2453a65dfe8SJens Axboe bd_release(bdev); 2463a65dfe8SJens Axboe return ret; 2473a65dfe8SJens Axboe case BLKPG: 2483a65dfe8SJens Axboe return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); 2493a65dfe8SJens Axboe case BLKRRPART: 2503a65dfe8SJens Axboe return blkdev_reread_part(bdev); 2513a65dfe8SJens Axboe case BLKGETSIZE: 2523a65dfe8SJens Axboe if ((bdev->bd_inode->i_size >> 9) > ~0UL) 2533a65dfe8SJens Axboe return -EFBIG; 2543a65dfe8SJens Axboe return put_ulong(arg, bdev->bd_inode->i_size >> 9); 2553a65dfe8SJens Axboe case BLKGETSIZE64: 2563a65dfe8SJens Axboe return put_u64(arg, bdev->bd_inode->i_size); 2572056a782SJens Axboe case BLKTRACESTART: 2582056a782SJens Axboe case BLKTRACESTOP: 2592056a782SJens Axboe case BLKTRACESETUP: 2602056a782SJens Axboe case BLKTRACETEARDOWN: 2612056a782SJens Axboe return blk_trace_ioctl(bdev, cmd, (char __user *) arg); 2623a65dfe8SJens Axboe } 2633a65dfe8SJens Axboe return -ENOIOCTLCMD; 2643a65dfe8SJens Axboe } 2653a65dfe8SJens Axboe 2667006f6ecSAlasdair G Kergon int blkdev_driver_ioctl(struct inode *inode, struct file *file, 2673a65dfe8SJens Axboe struct gendisk *disk, unsigned cmd, unsigned long arg) 2683a65dfe8SJens Axboe { 2693a65dfe8SJens Axboe int ret; 2703a65dfe8SJens Axboe if (disk->fops->unlocked_ioctl) 2713a65dfe8SJens Axboe return disk->fops->unlocked_ioctl(file, cmd, arg); 2723a65dfe8SJens Axboe 2733a65dfe8SJens Axboe if (disk->fops->ioctl) { 2743a65dfe8SJens Axboe lock_kernel(); 2753a65dfe8SJens Axboe ret = disk->fops->ioctl(inode, file, cmd, arg); 2763a65dfe8SJens Axboe unlock_kernel(); 2773a65dfe8SJens Axboe return ret; 2783a65dfe8SJens Axboe } 2793a65dfe8SJens Axboe 2803a65dfe8SJens Axboe return -ENOTTY; 2813a65dfe8SJens Axboe } 2827006f6ecSAlasdair G Kergon EXPORT_SYMBOL_GPL(blkdev_driver_ioctl); 2833a65dfe8SJens Axboe 284f58c4c0aSArnd Bergmann /* 285f58c4c0aSArnd Bergmann * always keep this in sync with compat_blkdev_ioctl() and 286f58c4c0aSArnd Bergmann * compat_blkdev_locked_ioctl() 287f58c4c0aSArnd Bergmann */ 2883a65dfe8SJens Axboe int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, 2893a65dfe8SJens Axboe unsigned long arg) 2903a65dfe8SJens Axboe { 2913a65dfe8SJens Axboe struct block_device *bdev = inode->i_bdev; 2923a65dfe8SJens Axboe struct gendisk *disk = bdev->bd_disk; 2933a65dfe8SJens Axboe int ret, n; 2943a65dfe8SJens Axboe 2953a65dfe8SJens Axboe switch(cmd) { 2963a65dfe8SJens Axboe case BLKFLSBUF: 2973a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 2983a65dfe8SJens Axboe return -EACCES; 2993a65dfe8SJens Axboe 3003a65dfe8SJens Axboe ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 3013a65dfe8SJens Axboe /* -EINVAL to handle old uncorrected drivers */ 3023a65dfe8SJens Axboe if (ret != -EINVAL && ret != -ENOTTY) 3033a65dfe8SJens Axboe return ret; 3043a65dfe8SJens Axboe 3053a65dfe8SJens Axboe lock_kernel(); 3063a65dfe8SJens Axboe fsync_bdev(bdev); 307f98393a6SPeter Zijlstra invalidate_bdev(bdev); 3083a65dfe8SJens Axboe unlock_kernel(); 3093a65dfe8SJens Axboe return 0; 3103a65dfe8SJens Axboe 3113a65dfe8SJens Axboe case BLKROSET: 3123a65dfe8SJens Axboe ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 3133a65dfe8SJens Axboe /* -EINVAL to handle old uncorrected drivers */ 3143a65dfe8SJens Axboe if (ret != -EINVAL && ret != -ENOTTY) 3153a65dfe8SJens Axboe return ret; 3163a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 3173a65dfe8SJens Axboe return -EACCES; 3183a65dfe8SJens Axboe if (get_user(n, (int __user *)(arg))) 3193a65dfe8SJens Axboe return -EFAULT; 3203a65dfe8SJens Axboe lock_kernel(); 3213a65dfe8SJens Axboe set_device_ro(bdev, n); 3223a65dfe8SJens Axboe unlock_kernel(); 3233a65dfe8SJens Axboe return 0; 324*d30a2605SDavid Woodhouse 325*d30a2605SDavid Woodhouse case BLKDISCARD: { 326*d30a2605SDavid Woodhouse uint64_t range[2]; 327*d30a2605SDavid Woodhouse 328*d30a2605SDavid Woodhouse if (!(file->f_mode & FMODE_WRITE)) 329*d30a2605SDavid Woodhouse return -EBADF; 330*d30a2605SDavid Woodhouse 331*d30a2605SDavid Woodhouse if (copy_from_user(range, (void __user *)arg, sizeof(range))) 332*d30a2605SDavid Woodhouse return -EFAULT; 333*d30a2605SDavid Woodhouse 334*d30a2605SDavid Woodhouse return blk_ioctl_discard(bdev, range[0], range[1]); 335*d30a2605SDavid Woodhouse } 336*d30a2605SDavid Woodhouse 337a885c8c4SChristoph Hellwig case HDIO_GETGEO: { 338a885c8c4SChristoph Hellwig struct hd_geometry geo; 339a885c8c4SChristoph Hellwig 340a885c8c4SChristoph Hellwig if (!arg) 341a885c8c4SChristoph Hellwig return -EINVAL; 342a885c8c4SChristoph Hellwig if (!disk->fops->getgeo) 343a885c8c4SChristoph Hellwig return -ENOTTY; 344a885c8c4SChristoph Hellwig 345a885c8c4SChristoph Hellwig /* 346a885c8c4SChristoph Hellwig * We need to set the startsect first, the driver may 347a885c8c4SChristoph Hellwig * want to override it. 348a885c8c4SChristoph Hellwig */ 349a885c8c4SChristoph Hellwig geo.start = get_start_sect(bdev); 350a885c8c4SChristoph Hellwig ret = disk->fops->getgeo(bdev, &geo); 351a885c8c4SChristoph Hellwig if (ret) 352a885c8c4SChristoph Hellwig return ret; 353a885c8c4SChristoph Hellwig if (copy_to_user((struct hd_geometry __user *)arg, &geo, 354a885c8c4SChristoph Hellwig sizeof(geo))) 355a885c8c4SChristoph Hellwig return -EFAULT; 356a885c8c4SChristoph Hellwig return 0; 357a885c8c4SChristoph Hellwig } 3583a65dfe8SJens Axboe } 3593a65dfe8SJens Axboe 3603a65dfe8SJens Axboe lock_kernel(); 3613a65dfe8SJens Axboe ret = blkdev_locked_ioctl(file, bdev, cmd, arg); 3623a65dfe8SJens Axboe unlock_kernel(); 3633a65dfe8SJens Axboe if (ret != -ENOIOCTLCMD) 3643a65dfe8SJens Axboe return ret; 3653a65dfe8SJens Axboe 3663a65dfe8SJens Axboe return blkdev_driver_ioctl(inode, file, disk, cmd, arg); 3673a65dfe8SJens Axboe } 3683a65dfe8SJens Axboe EXPORT_SYMBOL_GPL(blkdev_ioctl); 369