1*c59ede7bSRandy.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> 81da177e4SLinus Torvalds #include <asm/uaccess.h> 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) 111da177e4SLinus Torvalds { 121da177e4SLinus Torvalds struct block_device *bdevp; 131da177e4SLinus Torvalds struct gendisk *disk; 141da177e4SLinus Torvalds struct blkpg_ioctl_arg a; 151da177e4SLinus Torvalds struct blkpg_partition p; 161da177e4SLinus Torvalds long long start, length; 171da177e4SLinus Torvalds int part; 181da177e4SLinus Torvalds int i; 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 211da177e4SLinus Torvalds return -EACCES; 221da177e4SLinus Torvalds if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) 231da177e4SLinus Torvalds return -EFAULT; 241da177e4SLinus Torvalds if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) 251da177e4SLinus Torvalds return -EFAULT; 261da177e4SLinus Torvalds disk = bdev->bd_disk; 271da177e4SLinus Torvalds if (bdev != bdev->bd_contains) 281da177e4SLinus Torvalds return -EINVAL; 291da177e4SLinus Torvalds part = p.pno; 301da177e4SLinus Torvalds if (part <= 0 || part >= disk->minors) 311da177e4SLinus Torvalds return -EINVAL; 321da177e4SLinus Torvalds switch (a.op) { 331da177e4SLinus Torvalds case BLKPG_ADD_PARTITION: 341da177e4SLinus Torvalds start = p.start >> 9; 351da177e4SLinus Torvalds length = p.length >> 9; 361da177e4SLinus Torvalds /* check for fit in a hd_struct */ 371da177e4SLinus Torvalds if (sizeof(sector_t) == sizeof(long) && 381da177e4SLinus Torvalds sizeof(long long) > sizeof(long)) { 391da177e4SLinus Torvalds long pstart = start, plength = length; 401da177e4SLinus Torvalds if (pstart != start || plength != length 411da177e4SLinus Torvalds || pstart < 0 || plength < 0) 421da177e4SLinus Torvalds return -EINVAL; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds /* partition number in use? */ 451da177e4SLinus Torvalds down(&bdev->bd_sem); 461da177e4SLinus Torvalds if (disk->part[part - 1]) { 471da177e4SLinus Torvalds up(&bdev->bd_sem); 481da177e4SLinus Torvalds return -EBUSY; 491da177e4SLinus Torvalds } 501da177e4SLinus Torvalds /* overlap? */ 511da177e4SLinus Torvalds for (i = 0; i < disk->minors - 1; i++) { 521da177e4SLinus Torvalds struct hd_struct *s = disk->part[i]; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds if (!s) 551da177e4SLinus Torvalds continue; 561da177e4SLinus Torvalds if (!(start+length <= s->start_sect || 571da177e4SLinus Torvalds start >= s->start_sect + s->nr_sects)) { 581da177e4SLinus Torvalds up(&bdev->bd_sem); 591da177e4SLinus Torvalds return -EBUSY; 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds /* all seems OK */ 631da177e4SLinus Torvalds add_partition(disk, part, start, length); 641da177e4SLinus Torvalds up(&bdev->bd_sem); 651da177e4SLinus Torvalds return 0; 661da177e4SLinus Torvalds case BLKPG_DEL_PARTITION: 671da177e4SLinus Torvalds if (!disk->part[part-1]) 681da177e4SLinus Torvalds return -ENXIO; 691da177e4SLinus Torvalds if (disk->part[part - 1]->nr_sects == 0) 701da177e4SLinus Torvalds return -ENXIO; 711da177e4SLinus Torvalds bdevp = bdget_disk(disk, part); 721da177e4SLinus Torvalds if (!bdevp) 731da177e4SLinus Torvalds return -ENOMEM; 741da177e4SLinus Torvalds down(&bdevp->bd_sem); 751da177e4SLinus Torvalds if (bdevp->bd_openers) { 761da177e4SLinus Torvalds up(&bdevp->bd_sem); 771da177e4SLinus Torvalds bdput(bdevp); 781da177e4SLinus Torvalds return -EBUSY; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds /* all seems OK */ 811da177e4SLinus Torvalds fsync_bdev(bdevp); 821da177e4SLinus Torvalds invalidate_bdev(bdevp, 0); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds down(&bdev->bd_sem); 851da177e4SLinus Torvalds delete_partition(disk, part); 861da177e4SLinus Torvalds up(&bdev->bd_sem); 871da177e4SLinus Torvalds up(&bdevp->bd_sem); 881da177e4SLinus Torvalds bdput(bdevp); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds return 0; 911da177e4SLinus Torvalds default: 921da177e4SLinus Torvalds return -EINVAL; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds static int blkdev_reread_part(struct block_device *bdev) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds struct gendisk *disk = bdev->bd_disk; 991da177e4SLinus Torvalds int res; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds if (disk->minors == 1 || bdev != bdev->bd_contains) 1021da177e4SLinus Torvalds return -EINVAL; 1031da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 1041da177e4SLinus Torvalds return -EACCES; 1051da177e4SLinus Torvalds if (down_trylock(&bdev->bd_sem)) 1061da177e4SLinus Torvalds return -EBUSY; 1071da177e4SLinus Torvalds res = rescan_partitions(disk, bdev); 1081da177e4SLinus Torvalds up(&bdev->bd_sem); 1091da177e4SLinus Torvalds return res; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static int put_ushort(unsigned long arg, unsigned short val) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds return put_user(val, (unsigned short __user *)arg); 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds static int put_int(unsigned long arg, int val) 1181da177e4SLinus Torvalds { 1191da177e4SLinus Torvalds return put_user(val, (int __user *)arg); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds static int put_long(unsigned long arg, long val) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds return put_user(val, (long __user *)arg); 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds static int put_ulong(unsigned long arg, unsigned long val) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds return put_user(val, (unsigned long __user *)arg); 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds static int put_u64(unsigned long arg, u64 val) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds return put_user(val, (u64 __user *)arg); 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds 137bb93e3a5SArnd Bergmann static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev, 138bb93e3a5SArnd Bergmann unsigned cmd, unsigned long arg) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds struct backing_dev_info *bdi; 1411da177e4SLinus Torvalds int ret, n; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds switch (cmd) { 1441da177e4SLinus Torvalds case BLKRAGET: 1451da177e4SLinus Torvalds case BLKFRAGET: 1461da177e4SLinus Torvalds if (!arg) 1471da177e4SLinus Torvalds return -EINVAL; 1481da177e4SLinus Torvalds bdi = blk_get_backing_dev_info(bdev); 1491da177e4SLinus Torvalds if (bdi == NULL) 1501da177e4SLinus Torvalds return -ENOTTY; 1511da177e4SLinus Torvalds return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); 1521da177e4SLinus Torvalds case BLKROGET: 1531da177e4SLinus Torvalds return put_int(arg, bdev_read_only(bdev) != 0); 1541da177e4SLinus Torvalds case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ 1551da177e4SLinus Torvalds return put_int(arg, block_size(bdev)); 1561da177e4SLinus Torvalds case BLKSSZGET: /* get block device hardware sector size */ 1571da177e4SLinus Torvalds return put_int(arg, bdev_hardsect_size(bdev)); 1581da177e4SLinus Torvalds case BLKSECTGET: 1591da177e4SLinus Torvalds return put_ushort(arg, bdev_get_queue(bdev)->max_sectors); 1601da177e4SLinus Torvalds case BLKRASET: 1611da177e4SLinus Torvalds case BLKFRASET: 1621da177e4SLinus Torvalds if(!capable(CAP_SYS_ADMIN)) 1631da177e4SLinus Torvalds return -EACCES; 1641da177e4SLinus Torvalds bdi = blk_get_backing_dev_info(bdev); 1651da177e4SLinus Torvalds if (bdi == NULL) 1661da177e4SLinus Torvalds return -ENOTTY; 1671da177e4SLinus Torvalds bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; 1681da177e4SLinus Torvalds return 0; 1691da177e4SLinus Torvalds case BLKBSZSET: 1701da177e4SLinus Torvalds /* set the logical block size */ 1711da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 1721da177e4SLinus Torvalds return -EACCES; 1731da177e4SLinus Torvalds if (!arg) 1741da177e4SLinus Torvalds return -EINVAL; 1751da177e4SLinus Torvalds if (get_user(n, (int __user *) arg)) 1761da177e4SLinus Torvalds return -EFAULT; 1771da177e4SLinus Torvalds if (bd_claim(bdev, file) < 0) 1781da177e4SLinus Torvalds return -EBUSY; 1791da177e4SLinus Torvalds ret = set_blocksize(bdev, n); 1801da177e4SLinus Torvalds bd_release(bdev); 1811da177e4SLinus Torvalds return ret; 1821da177e4SLinus Torvalds case BLKPG: 1831da177e4SLinus Torvalds return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); 1841da177e4SLinus Torvalds case BLKRRPART: 1851da177e4SLinus Torvalds return blkdev_reread_part(bdev); 1861da177e4SLinus Torvalds case BLKGETSIZE: 1871da177e4SLinus Torvalds if ((bdev->bd_inode->i_size >> 9) > ~0UL) 1881da177e4SLinus Torvalds return -EFBIG; 1891da177e4SLinus Torvalds return put_ulong(arg, bdev->bd_inode->i_size >> 9); 1901da177e4SLinus Torvalds case BLKGETSIZE64: 1911da177e4SLinus Torvalds return put_u64(arg, bdev->bd_inode->i_size); 192bb93e3a5SArnd Bergmann } 193bb93e3a5SArnd Bergmann return -ENOIOCTLCMD; 194bb93e3a5SArnd Bergmann } 195bb93e3a5SArnd Bergmann 196bb93e3a5SArnd Bergmann static int blkdev_driver_ioctl(struct inode *inode, struct file *file, 197bb93e3a5SArnd Bergmann struct gendisk *disk, unsigned cmd, unsigned long arg) 198bb93e3a5SArnd Bergmann { 199bb93e3a5SArnd Bergmann int ret; 200bb93e3a5SArnd Bergmann if (disk->fops->unlocked_ioctl) 201bb93e3a5SArnd Bergmann return disk->fops->unlocked_ioctl(file, cmd, arg); 202bb93e3a5SArnd Bergmann 203bb93e3a5SArnd Bergmann if (disk->fops->ioctl) { 204bb93e3a5SArnd Bergmann lock_kernel(); 205bb93e3a5SArnd Bergmann ret = disk->fops->ioctl(inode, file, cmd, arg); 206bb93e3a5SArnd Bergmann unlock_kernel(); 207bb93e3a5SArnd Bergmann return ret; 208bb93e3a5SArnd Bergmann } 209bb93e3a5SArnd Bergmann 210bb93e3a5SArnd Bergmann return -ENOTTY; 211bb93e3a5SArnd Bergmann } 212bb93e3a5SArnd Bergmann 213bb93e3a5SArnd Bergmann int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, 214bb93e3a5SArnd Bergmann unsigned long arg) 215bb93e3a5SArnd Bergmann { 216bb93e3a5SArnd Bergmann struct block_device *bdev = inode->i_bdev; 217bb93e3a5SArnd Bergmann struct gendisk *disk = bdev->bd_disk; 218bb93e3a5SArnd Bergmann int ret, n; 219bb93e3a5SArnd Bergmann 220bb93e3a5SArnd Bergmann switch(cmd) { 2211da177e4SLinus Torvalds case BLKFLSBUF: 2221da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2231da177e4SLinus Torvalds return -EACCES; 224bb93e3a5SArnd Bergmann 225bb93e3a5SArnd Bergmann ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2261da177e4SLinus Torvalds /* -EINVAL to handle old uncorrected drivers */ 2271da177e4SLinus Torvalds if (ret != -EINVAL && ret != -ENOTTY) 2281da177e4SLinus Torvalds return ret; 229bb93e3a5SArnd Bergmann 230bb93e3a5SArnd Bergmann lock_kernel(); 2311da177e4SLinus Torvalds fsync_bdev(bdev); 2321da177e4SLinus Torvalds invalidate_bdev(bdev, 0); 233bb93e3a5SArnd Bergmann unlock_kernel(); 2341da177e4SLinus Torvalds return 0; 235bb93e3a5SArnd Bergmann 2361da177e4SLinus Torvalds case BLKROSET: 237bb93e3a5SArnd Bergmann ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2381da177e4SLinus Torvalds /* -EINVAL to handle old uncorrected drivers */ 2391da177e4SLinus Torvalds if (ret != -EINVAL && ret != -ENOTTY) 2401da177e4SLinus Torvalds return ret; 2411da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2421da177e4SLinus Torvalds return -EACCES; 2431da177e4SLinus Torvalds if (get_user(n, (int __user *)(arg))) 2441da177e4SLinus Torvalds return -EFAULT; 245bb93e3a5SArnd Bergmann lock_kernel(); 2461da177e4SLinus Torvalds set_device_ro(bdev, n); 247bb93e3a5SArnd Bergmann unlock_kernel(); 2481da177e4SLinus Torvalds return 0; 249a885c8c4SChristoph Hellwig case HDIO_GETGEO: { 250a885c8c4SChristoph Hellwig struct hd_geometry geo; 251a885c8c4SChristoph Hellwig 252a885c8c4SChristoph Hellwig if (!arg) 253a885c8c4SChristoph Hellwig return -EINVAL; 254a885c8c4SChristoph Hellwig if (!disk->fops->getgeo) 255a885c8c4SChristoph Hellwig return -ENOTTY; 256a885c8c4SChristoph Hellwig 257a885c8c4SChristoph Hellwig /* 258a885c8c4SChristoph Hellwig * We need to set the startsect first, the driver may 259a885c8c4SChristoph Hellwig * want to override it. 260a885c8c4SChristoph Hellwig */ 261a885c8c4SChristoph Hellwig geo.start = get_start_sect(bdev); 262a885c8c4SChristoph Hellwig ret = disk->fops->getgeo(bdev, &geo); 263a885c8c4SChristoph Hellwig if (ret) 264a885c8c4SChristoph Hellwig return ret; 265a885c8c4SChristoph Hellwig if (copy_to_user((struct hd_geometry __user *)arg, &geo, 266a885c8c4SChristoph Hellwig sizeof(geo))) 267a885c8c4SChristoph Hellwig return -EFAULT; 268a885c8c4SChristoph Hellwig return 0; 269a885c8c4SChristoph Hellwig } 2701da177e4SLinus Torvalds } 271bb93e3a5SArnd Bergmann 272bb93e3a5SArnd Bergmann lock_kernel(); 273bb93e3a5SArnd Bergmann ret = blkdev_locked_ioctl(file, bdev, cmd, arg); 274bb93e3a5SArnd Bergmann unlock_kernel(); 275bb93e3a5SArnd Bergmann if (ret != -ENOIOCTLCMD) 276bb93e3a5SArnd Bergmann return ret; 277bb93e3a5SArnd Bergmann 278bb93e3a5SArnd Bergmann return blkdev_driver_ioctl(inode, file, disk, cmd, arg); 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds /* Most of the generic ioctls are handled in the normal fallback path. 2821da177e4SLinus Torvalds This assumes the blkdev's low level compat_ioctl always returns 2831da177e4SLinus Torvalds ENOIOCTLCMD for unknown ioctls. */ 2841da177e4SLinus Torvalds long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) 2851da177e4SLinus Torvalds { 2861da177e4SLinus Torvalds struct block_device *bdev = file->f_dentry->d_inode->i_bdev; 2871da177e4SLinus Torvalds struct gendisk *disk = bdev->bd_disk; 2881da177e4SLinus Torvalds int ret = -ENOIOCTLCMD; 2891da177e4SLinus Torvalds if (disk->fops->compat_ioctl) { 2901da177e4SLinus Torvalds lock_kernel(); 2911da177e4SLinus Torvalds ret = disk->fops->compat_ioctl(file, cmd, arg); 2921da177e4SLinus Torvalds unlock_kernel(); 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds return ret; 2951da177e4SLinus Torvalds } 29668f66febSStephen Tweedie 29768f66febSStephen Tweedie EXPORT_SYMBOL_GPL(blkdev_ioctl); 298