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; 203a65dfe8SJens Axboe 213a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 223a65dfe8SJens Axboe return -EACCES; 233a65dfe8SJens Axboe if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) 243a65dfe8SJens Axboe return -EFAULT; 253a65dfe8SJens Axboe if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) 263a65dfe8SJens Axboe return -EFAULT; 273a65dfe8SJens Axboe disk = bdev->bd_disk; 283a65dfe8SJens Axboe if (bdev != bdev->bd_contains) 293a65dfe8SJens Axboe return -EINVAL; 303a65dfe8SJens Axboe part = p.pno; 313a65dfe8SJens Axboe if (part <= 0 || part >= disk->minors) 323a65dfe8SJens Axboe return -EINVAL; 333a65dfe8SJens Axboe switch (a.op) { 343a65dfe8SJens Axboe case BLKPG_ADD_PARTITION: 353a65dfe8SJens Axboe start = p.start >> 9; 363a65dfe8SJens Axboe length = p.length >> 9; 373a65dfe8SJens Axboe /* check for fit in a hd_struct */ 383a65dfe8SJens Axboe if (sizeof(sector_t) == sizeof(long) && 393a65dfe8SJens Axboe sizeof(long long) > sizeof(long)) { 403a65dfe8SJens Axboe long pstart = start, plength = length; 413a65dfe8SJens Axboe if (pstart != start || plength != length 423a65dfe8SJens Axboe || pstart < 0 || plength < 0) 433a65dfe8SJens Axboe return -EINVAL; 443a65dfe8SJens Axboe } 453a65dfe8SJens Axboe /* partition number in use? */ 46c039e313SArjan van de Ven mutex_lock(&bdev->bd_mutex); 473a65dfe8SJens Axboe if (disk->part[part - 1]) { 48c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 493a65dfe8SJens Axboe return -EBUSY; 503a65dfe8SJens Axboe } 513a65dfe8SJens Axboe /* overlap? */ 523a65dfe8SJens Axboe for (i = 0; i < disk->minors - 1; i++) { 533a65dfe8SJens Axboe struct hd_struct *s = disk->part[i]; 543a65dfe8SJens Axboe 553a65dfe8SJens Axboe if (!s) 563a65dfe8SJens Axboe continue; 573a65dfe8SJens Axboe if (!(start+length <= s->start_sect || 583a65dfe8SJens Axboe start >= s->start_sect + s->nr_sects)) { 59c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 603a65dfe8SJens Axboe return -EBUSY; 613a65dfe8SJens Axboe } 623a65dfe8SJens Axboe } 633a65dfe8SJens Axboe /* all seems OK */ 64d18d7682SFabio Massimo Di Nitto add_partition(disk, part, start, length, ADDPART_FLAG_NONE); 65c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 663a65dfe8SJens Axboe return 0; 673a65dfe8SJens Axboe case BLKPG_DEL_PARTITION: 683a65dfe8SJens Axboe if (!disk->part[part-1]) 693a65dfe8SJens Axboe return -ENXIO; 703a65dfe8SJens Axboe if (disk->part[part - 1]->nr_sects == 0) 713a65dfe8SJens Axboe return -ENXIO; 723a65dfe8SJens Axboe bdevp = bdget_disk(disk, part); 733a65dfe8SJens Axboe if (!bdevp) 743a65dfe8SJens Axboe return -ENOMEM; 752e7b651dSPeter Zijlstra mutex_lock(&bdevp->bd_mutex); 763a65dfe8SJens Axboe if (bdevp->bd_openers) { 77c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 783a65dfe8SJens Axboe bdput(bdevp); 793a65dfe8SJens Axboe return -EBUSY; 803a65dfe8SJens Axboe } 813a65dfe8SJens Axboe /* all seems OK */ 823a65dfe8SJens Axboe fsync_bdev(bdevp); 83*f98393a6SPeter Zijlstra invalidate_bdev(bdevp); 843a65dfe8SJens Axboe 856d740cd5SPeter Zijlstra mutex_lock_nested(&bdev->bd_mutex, 1); 863a65dfe8SJens Axboe delete_partition(disk, part); 87c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 88c039e313SArjan van de Ven mutex_unlock(&bdevp->bd_mutex); 893a65dfe8SJens Axboe bdput(bdevp); 903a65dfe8SJens Axboe 913a65dfe8SJens Axboe return 0; 923a65dfe8SJens Axboe default: 933a65dfe8SJens Axboe return -EINVAL; 943a65dfe8SJens Axboe } 953a65dfe8SJens Axboe } 963a65dfe8SJens Axboe 973a65dfe8SJens Axboe static int blkdev_reread_part(struct block_device *bdev) 983a65dfe8SJens Axboe { 993a65dfe8SJens Axboe struct gendisk *disk = bdev->bd_disk; 1003a65dfe8SJens Axboe int res; 1013a65dfe8SJens Axboe 1023a65dfe8SJens Axboe if (disk->minors == 1 || bdev != bdev->bd_contains) 1033a65dfe8SJens Axboe return -EINVAL; 1043a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 1053a65dfe8SJens Axboe return -EACCES; 106c039e313SArjan van de Ven if (!mutex_trylock(&bdev->bd_mutex)) 1073a65dfe8SJens Axboe return -EBUSY; 1083a65dfe8SJens Axboe res = rescan_partitions(disk, bdev); 109c039e313SArjan van de Ven mutex_unlock(&bdev->bd_mutex); 1103a65dfe8SJens Axboe return res; 1113a65dfe8SJens Axboe } 1123a65dfe8SJens Axboe 1133a65dfe8SJens Axboe static int put_ushort(unsigned long arg, unsigned short val) 1143a65dfe8SJens Axboe { 1153a65dfe8SJens Axboe return put_user(val, (unsigned short __user *)arg); 1163a65dfe8SJens Axboe } 1173a65dfe8SJens Axboe 1183a65dfe8SJens Axboe static int put_int(unsigned long arg, int val) 1193a65dfe8SJens Axboe { 1203a65dfe8SJens Axboe return put_user(val, (int __user *)arg); 1213a65dfe8SJens Axboe } 1223a65dfe8SJens Axboe 1233a65dfe8SJens Axboe static int put_long(unsigned long arg, long val) 1243a65dfe8SJens Axboe { 1253a65dfe8SJens Axboe return put_user(val, (long __user *)arg); 1263a65dfe8SJens Axboe } 1273a65dfe8SJens Axboe 1283a65dfe8SJens Axboe static int put_ulong(unsigned long arg, unsigned long val) 1293a65dfe8SJens Axboe { 1303a65dfe8SJens Axboe return put_user(val, (unsigned long __user *)arg); 1313a65dfe8SJens Axboe } 1323a65dfe8SJens Axboe 1333a65dfe8SJens Axboe static int put_u64(unsigned long arg, u64 val) 1343a65dfe8SJens Axboe { 1353a65dfe8SJens Axboe return put_user(val, (u64 __user *)arg); 1363a65dfe8SJens Axboe } 1373a65dfe8SJens Axboe 1383a65dfe8SJens Axboe static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, 1393a65dfe8SJens Axboe unsigned cmd, unsigned long arg) 1403a65dfe8SJens Axboe { 1413a65dfe8SJens Axboe struct backing_dev_info *bdi; 1423a65dfe8SJens Axboe int ret, n; 1433a65dfe8SJens Axboe 1443a65dfe8SJens Axboe switch (cmd) { 1453a65dfe8SJens Axboe case BLKRAGET: 1463a65dfe8SJens Axboe case BLKFRAGET: 1473a65dfe8SJens Axboe if (!arg) 1483a65dfe8SJens Axboe return -EINVAL; 1493a65dfe8SJens Axboe bdi = blk_get_backing_dev_info(bdev); 1503a65dfe8SJens Axboe if (bdi == NULL) 1513a65dfe8SJens Axboe return -ENOTTY; 1523a65dfe8SJens Axboe return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); 1533a65dfe8SJens Axboe case BLKROGET: 1543a65dfe8SJens Axboe return put_int(arg, bdev_read_only(bdev) != 0); 1553a65dfe8SJens Axboe case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ 1563a65dfe8SJens Axboe return put_int(arg, block_size(bdev)); 1573a65dfe8SJens Axboe case BLKSSZGET: /* get block device hardware sector size */ 1583a65dfe8SJens Axboe return put_int(arg, bdev_hardsect_size(bdev)); 1593a65dfe8SJens Axboe case BLKSECTGET: 1603a65dfe8SJens Axboe return put_ushort(arg, bdev_get_queue(bdev)->max_sectors); 1613a65dfe8SJens Axboe case BLKRASET: 1623a65dfe8SJens Axboe case BLKFRASET: 1633a65dfe8SJens Axboe if(!capable(CAP_SYS_ADMIN)) 1643a65dfe8SJens Axboe return -EACCES; 1653a65dfe8SJens Axboe bdi = blk_get_backing_dev_info(bdev); 1663a65dfe8SJens Axboe if (bdi == NULL) 1673a65dfe8SJens Axboe return -ENOTTY; 1683a65dfe8SJens Axboe bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; 1693a65dfe8SJens Axboe return 0; 1703a65dfe8SJens Axboe case BLKBSZSET: 1713a65dfe8SJens Axboe /* set the logical block size */ 1723a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 1733a65dfe8SJens Axboe return -EACCES; 1743a65dfe8SJens Axboe if (!arg) 1753a65dfe8SJens Axboe return -EINVAL; 1763a65dfe8SJens Axboe if (get_user(n, (int __user *) arg)) 1773a65dfe8SJens Axboe return -EFAULT; 1783a65dfe8SJens Axboe if (bd_claim(bdev, file) < 0) 1793a65dfe8SJens Axboe return -EBUSY; 1803a65dfe8SJens Axboe ret = set_blocksize(bdev, n); 1813a65dfe8SJens Axboe bd_release(bdev); 1823a65dfe8SJens Axboe return ret; 1833a65dfe8SJens Axboe case BLKPG: 1843a65dfe8SJens Axboe return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); 1853a65dfe8SJens Axboe case BLKRRPART: 1863a65dfe8SJens Axboe return blkdev_reread_part(bdev); 1873a65dfe8SJens Axboe case BLKGETSIZE: 1883a65dfe8SJens Axboe if ((bdev->bd_inode->i_size >> 9) > ~0UL) 1893a65dfe8SJens Axboe return -EFBIG; 1903a65dfe8SJens Axboe return put_ulong(arg, bdev->bd_inode->i_size >> 9); 1913a65dfe8SJens Axboe case BLKGETSIZE64: 1923a65dfe8SJens Axboe return put_u64(arg, bdev->bd_inode->i_size); 1932056a782SJens Axboe case BLKTRACESTART: 1942056a782SJens Axboe case BLKTRACESTOP: 1952056a782SJens Axboe case BLKTRACESETUP: 1962056a782SJens Axboe case BLKTRACETEARDOWN: 1972056a782SJens Axboe return blk_trace_ioctl(bdev, cmd, (char __user *) arg); 1983a65dfe8SJens Axboe } 1993a65dfe8SJens Axboe return -ENOIOCTLCMD; 2003a65dfe8SJens Axboe } 2013a65dfe8SJens Axboe 2027006f6ecSAlasdair G Kergon int blkdev_driver_ioctl(struct inode *inode, struct file *file, 2033a65dfe8SJens Axboe struct gendisk *disk, unsigned cmd, unsigned long arg) 2043a65dfe8SJens Axboe { 2053a65dfe8SJens Axboe int ret; 2063a65dfe8SJens Axboe if (disk->fops->unlocked_ioctl) 2073a65dfe8SJens Axboe return disk->fops->unlocked_ioctl(file, cmd, arg); 2083a65dfe8SJens Axboe 2093a65dfe8SJens Axboe if (disk->fops->ioctl) { 2103a65dfe8SJens Axboe lock_kernel(); 2113a65dfe8SJens Axboe ret = disk->fops->ioctl(inode, file, cmd, arg); 2123a65dfe8SJens Axboe unlock_kernel(); 2133a65dfe8SJens Axboe return ret; 2143a65dfe8SJens Axboe } 2153a65dfe8SJens Axboe 2163a65dfe8SJens Axboe return -ENOTTY; 2173a65dfe8SJens Axboe } 2187006f6ecSAlasdair G Kergon EXPORT_SYMBOL_GPL(blkdev_driver_ioctl); 2193a65dfe8SJens Axboe 2203a65dfe8SJens Axboe int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, 2213a65dfe8SJens Axboe unsigned long arg) 2223a65dfe8SJens Axboe { 2233a65dfe8SJens Axboe struct block_device *bdev = inode->i_bdev; 2243a65dfe8SJens Axboe struct gendisk *disk = bdev->bd_disk; 2253a65dfe8SJens Axboe int ret, n; 2263a65dfe8SJens Axboe 2273a65dfe8SJens Axboe switch(cmd) { 2283a65dfe8SJens Axboe case BLKFLSBUF: 2293a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 2303a65dfe8SJens Axboe return -EACCES; 2313a65dfe8SJens Axboe 2323a65dfe8SJens Axboe ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2333a65dfe8SJens Axboe /* -EINVAL to handle old uncorrected drivers */ 2343a65dfe8SJens Axboe if (ret != -EINVAL && ret != -ENOTTY) 2353a65dfe8SJens Axboe return ret; 2363a65dfe8SJens Axboe 2373a65dfe8SJens Axboe lock_kernel(); 2383a65dfe8SJens Axboe fsync_bdev(bdev); 239*f98393a6SPeter Zijlstra invalidate_bdev(bdev); 2403a65dfe8SJens Axboe unlock_kernel(); 2413a65dfe8SJens Axboe return 0; 2423a65dfe8SJens Axboe 2433a65dfe8SJens Axboe case BLKROSET: 2443a65dfe8SJens Axboe ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2453a65dfe8SJens Axboe /* -EINVAL to handle old uncorrected drivers */ 2463a65dfe8SJens Axboe if (ret != -EINVAL && ret != -ENOTTY) 2473a65dfe8SJens Axboe return ret; 2483a65dfe8SJens Axboe if (!capable(CAP_SYS_ADMIN)) 2493a65dfe8SJens Axboe return -EACCES; 2503a65dfe8SJens Axboe if (get_user(n, (int __user *)(arg))) 2513a65dfe8SJens Axboe return -EFAULT; 2523a65dfe8SJens Axboe lock_kernel(); 2533a65dfe8SJens Axboe set_device_ro(bdev, n); 2543a65dfe8SJens Axboe unlock_kernel(); 2553a65dfe8SJens Axboe return 0; 256a885c8c4SChristoph Hellwig case HDIO_GETGEO: { 257a885c8c4SChristoph Hellwig struct hd_geometry geo; 258a885c8c4SChristoph Hellwig 259a885c8c4SChristoph Hellwig if (!arg) 260a885c8c4SChristoph Hellwig return -EINVAL; 261a885c8c4SChristoph Hellwig if (!disk->fops->getgeo) 262a885c8c4SChristoph Hellwig return -ENOTTY; 263a885c8c4SChristoph Hellwig 264a885c8c4SChristoph Hellwig /* 265a885c8c4SChristoph Hellwig * We need to set the startsect first, the driver may 266a885c8c4SChristoph Hellwig * want to override it. 267a885c8c4SChristoph Hellwig */ 268a885c8c4SChristoph Hellwig geo.start = get_start_sect(bdev); 269a885c8c4SChristoph Hellwig ret = disk->fops->getgeo(bdev, &geo); 270a885c8c4SChristoph Hellwig if (ret) 271a885c8c4SChristoph Hellwig return ret; 272a885c8c4SChristoph Hellwig if (copy_to_user((struct hd_geometry __user *)arg, &geo, 273a885c8c4SChristoph Hellwig sizeof(geo))) 274a885c8c4SChristoph Hellwig return -EFAULT; 275a885c8c4SChristoph Hellwig return 0; 276a885c8c4SChristoph Hellwig } 2773a65dfe8SJens Axboe } 2783a65dfe8SJens Axboe 2793a65dfe8SJens Axboe lock_kernel(); 2803a65dfe8SJens Axboe ret = blkdev_locked_ioctl(file, bdev, cmd, arg); 2813a65dfe8SJens Axboe unlock_kernel(); 2823a65dfe8SJens Axboe if (ret != -ENOIOCTLCMD) 2833a65dfe8SJens Axboe return ret; 2843a65dfe8SJens Axboe 2853a65dfe8SJens Axboe return blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2863a65dfe8SJens Axboe } 2873a65dfe8SJens Axboe 2883a65dfe8SJens Axboe /* Most of the generic ioctls are handled in the normal fallback path. 2893a65dfe8SJens Axboe This assumes the blkdev's low level compat_ioctl always returns 2903a65dfe8SJens Axboe ENOIOCTLCMD for unknown ioctls. */ 2913a65dfe8SJens Axboe long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) 2923a65dfe8SJens Axboe { 293c5a20b6cSJosef Sipek struct block_device *bdev = file->f_path.dentry->d_inode->i_bdev; 2943a65dfe8SJens Axboe struct gendisk *disk = bdev->bd_disk; 2953a65dfe8SJens Axboe int ret = -ENOIOCTLCMD; 2963a65dfe8SJens Axboe if (disk->fops->compat_ioctl) { 2973a65dfe8SJens Axboe lock_kernel(); 2983a65dfe8SJens Axboe ret = disk->fops->compat_ioctl(file, cmd, arg); 2993a65dfe8SJens Axboe unlock_kernel(); 3003a65dfe8SJens Axboe } 3013a65dfe8SJens Axboe return ret; 3023a65dfe8SJens Axboe } 3033a65dfe8SJens Axboe 3043a65dfe8SJens Axboe EXPORT_SYMBOL_GPL(blkdev_ioctl); 305