162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/capability.h> 362306a36Sopenharmony_ci#include <linux/compat.h> 462306a36Sopenharmony_ci#include <linux/blkdev.h> 562306a36Sopenharmony_ci#include <linux/export.h> 662306a36Sopenharmony_ci#include <linux/gfp.h> 762306a36Sopenharmony_ci#include <linux/blkpg.h> 862306a36Sopenharmony_ci#include <linux/hdreg.h> 962306a36Sopenharmony_ci#include <linux/backing-dev.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/blktrace_api.h> 1262306a36Sopenharmony_ci#include <linux/pr.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include "blk.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int blkpg_do_ioctl(struct block_device *bdev, 1762306a36Sopenharmony_ci struct blkpg_partition __user *upart, int op) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 2062306a36Sopenharmony_ci struct blkpg_partition p; 2162306a36Sopenharmony_ci sector_t start, length; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 2462306a36Sopenharmony_ci return -EACCES; 2562306a36Sopenharmony_ci if (copy_from_user(&p, upart, sizeof(struct blkpg_partition))) 2662306a36Sopenharmony_ci return -EFAULT; 2762306a36Sopenharmony_ci if (bdev_is_partition(bdev)) 2862306a36Sopenharmony_ci return -EINVAL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (p.pno <= 0) 3162306a36Sopenharmony_ci return -EINVAL; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (op == BLKPG_DEL_PARTITION) 3462306a36Sopenharmony_ci return bdev_del_partition(disk, p.pno); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (p.start < 0 || p.length <= 0 || p.start + p.length < 0) 3762306a36Sopenharmony_ci return -EINVAL; 3862306a36Sopenharmony_ci /* Check that the partition is aligned to the block size */ 3962306a36Sopenharmony_ci if (!IS_ALIGNED(p.start | p.length, bdev_logical_block_size(bdev))) 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci start = p.start >> SECTOR_SHIFT; 4362306a36Sopenharmony_ci length = p.length >> SECTOR_SHIFT; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci switch (op) { 4662306a36Sopenharmony_ci case BLKPG_ADD_PARTITION: 4762306a36Sopenharmony_ci return bdev_add_partition(disk, p.pno, start, length); 4862306a36Sopenharmony_ci case BLKPG_RESIZE_PARTITION: 4962306a36Sopenharmony_ci return bdev_resize_partition(disk, p.pno, start, length); 5062306a36Sopenharmony_ci default: 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int blkpg_ioctl(struct block_device *bdev, 5662306a36Sopenharmony_ci struct blkpg_ioctl_arg __user *arg) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct blkpg_partition __user *udata; 5962306a36Sopenharmony_ci int op; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (get_user(op, &arg->op) || get_user(udata, &arg->data)) 6262306a36Sopenharmony_ci return -EFAULT; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return blkpg_do_ioctl(bdev, udata, op); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 6862306a36Sopenharmony_cistruct compat_blkpg_ioctl_arg { 6962306a36Sopenharmony_ci compat_int_t op; 7062306a36Sopenharmony_ci compat_int_t flags; 7162306a36Sopenharmony_ci compat_int_t datalen; 7262306a36Sopenharmony_ci compat_caddr_t data; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int compat_blkpg_ioctl(struct block_device *bdev, 7662306a36Sopenharmony_ci struct compat_blkpg_ioctl_arg __user *arg) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci compat_caddr_t udata; 7962306a36Sopenharmony_ci int op; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (get_user(op, &arg->op) || get_user(udata, &arg->data)) 8262306a36Sopenharmony_ci return -EFAULT; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return blkpg_do_ioctl(bdev, compat_ptr(udata), op); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci#endif 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, 8962306a36Sopenharmony_ci unsigned long arg) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci uint64_t range[2]; 9262306a36Sopenharmony_ci uint64_t start, len; 9362306a36Sopenharmony_ci struct inode *inode = bdev->bd_inode; 9462306a36Sopenharmony_ci int err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!(mode & BLK_OPEN_WRITE)) 9762306a36Sopenharmony_ci return -EBADF; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!bdev_max_discard_sectors(bdev)) 10062306a36Sopenharmony_ci return -EOPNOTSUPP; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (copy_from_user(range, (void __user *)arg, sizeof(range))) 10362306a36Sopenharmony_ci return -EFAULT; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci start = range[0]; 10662306a36Sopenharmony_ci len = range[1]; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (start & 511) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci if (len & 511) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (start + len > bdev_nr_bytes(bdev)) 11462306a36Sopenharmony_ci return -EINVAL; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 11762306a36Sopenharmony_ci err = truncate_bdev_range(bdev, mode, start, start + len - 1); 11862306a36Sopenharmony_ci if (err) 11962306a36Sopenharmony_ci goto fail; 12062306a36Sopenharmony_ci err = blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL); 12162306a36Sopenharmony_cifail: 12262306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 12362306a36Sopenharmony_ci return err; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int blk_ioctl_secure_erase(struct block_device *bdev, blk_mode_t mode, 12762306a36Sopenharmony_ci void __user *argp) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci uint64_t start, len; 13062306a36Sopenharmony_ci uint64_t range[2]; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!(mode & BLK_OPEN_WRITE)) 13462306a36Sopenharmony_ci return -EBADF; 13562306a36Sopenharmony_ci if (!bdev_max_secure_erase_sectors(bdev)) 13662306a36Sopenharmony_ci return -EOPNOTSUPP; 13762306a36Sopenharmony_ci if (copy_from_user(range, argp, sizeof(range))) 13862306a36Sopenharmony_ci return -EFAULT; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci start = range[0]; 14162306a36Sopenharmony_ci len = range[1]; 14262306a36Sopenharmony_ci if ((start & 511) || (len & 511)) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci if (start + len > bdev_nr_bytes(bdev)) 14562306a36Sopenharmony_ci return -EINVAL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci filemap_invalidate_lock(bdev->bd_inode->i_mapping); 14862306a36Sopenharmony_ci err = truncate_bdev_range(bdev, mode, start, start + len - 1); 14962306a36Sopenharmony_ci if (!err) 15062306a36Sopenharmony_ci err = blkdev_issue_secure_erase(bdev, start >> 9, len >> 9, 15162306a36Sopenharmony_ci GFP_KERNEL); 15262306a36Sopenharmony_ci filemap_invalidate_unlock(bdev->bd_inode->i_mapping); 15362306a36Sopenharmony_ci return err; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int blk_ioctl_zeroout(struct block_device *bdev, blk_mode_t mode, 15862306a36Sopenharmony_ci unsigned long arg) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci uint64_t range[2]; 16162306a36Sopenharmony_ci uint64_t start, end, len; 16262306a36Sopenharmony_ci struct inode *inode = bdev->bd_inode; 16362306a36Sopenharmony_ci int err; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!(mode & BLK_OPEN_WRITE)) 16662306a36Sopenharmony_ci return -EBADF; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (copy_from_user(range, (void __user *)arg, sizeof(range))) 16962306a36Sopenharmony_ci return -EFAULT; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci start = range[0]; 17262306a36Sopenharmony_ci len = range[1]; 17362306a36Sopenharmony_ci end = start + len - 1; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (start & 511) 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci if (len & 511) 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci if (end >= (uint64_t)bdev_nr_bytes(bdev)) 18062306a36Sopenharmony_ci return -EINVAL; 18162306a36Sopenharmony_ci if (end < start) 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Invalidate the page cache, including dirty pages */ 18562306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 18662306a36Sopenharmony_ci err = truncate_bdev_range(bdev, mode, start, end); 18762306a36Sopenharmony_ci if (err) 18862306a36Sopenharmony_ci goto fail; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci err = blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL, 19162306a36Sopenharmony_ci BLKDEV_ZERO_NOUNMAP); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cifail: 19462306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int put_ushort(unsigned short __user *argp, unsigned short val) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return put_user(val, argp); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int put_int(int __user *argp, int val) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return put_user(val, argp); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int put_uint(unsigned int __user *argp, unsigned int val) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return put_user(val, argp); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int put_long(long __user *argp, long val) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci return put_user(val, argp); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int put_ulong(unsigned long __user *argp, unsigned long val) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci return put_user(val, argp); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int put_u64(u64 __user *argp, u64 val) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return put_user(val, argp); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 22962306a36Sopenharmony_cistatic int compat_put_long(compat_long_t __user *argp, long val) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return put_user(val, argp); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int compat_put_ulong(compat_ulong_t __user *argp, compat_ulong_t val) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return put_user(val, argp); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci#endif 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * This is the equivalent of compat_ptr_ioctl(), to be used by block 24362306a36Sopenharmony_ci * drivers that implement only commands that are completely compatible 24462306a36Sopenharmony_ci * between 32-bit and 64-bit user space 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ciint blkdev_compat_ptr_ioctl(struct block_device *bdev, blk_mode_t mode, 24762306a36Sopenharmony_ci unsigned cmd, unsigned long arg) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (disk->fops->ioctl) 25262306a36Sopenharmony_ci return disk->fops->ioctl(bdev, mode, cmd, 25362306a36Sopenharmony_ci (unsigned long)compat_ptr(arg)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return -ENOIOCTLCMD; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ciEXPORT_SYMBOL(blkdev_compat_ptr_ioctl); 25862306a36Sopenharmony_ci#endif 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic bool blkdev_pr_allowed(struct block_device *bdev, blk_mode_t mode) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci /* no sense to make reservations for partitions */ 26362306a36Sopenharmony_ci if (bdev_is_partition(bdev)) 26462306a36Sopenharmony_ci return false; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) 26762306a36Sopenharmony_ci return true; 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Only allow unprivileged reservations if the file descriptor is open 27062306a36Sopenharmony_ci * for writing. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci return mode & BLK_OPEN_WRITE; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int blkdev_pr_register(struct block_device *bdev, blk_mode_t mode, 27662306a36Sopenharmony_ci struct pr_registration __user *arg) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; 27962306a36Sopenharmony_ci struct pr_registration reg; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!blkdev_pr_allowed(bdev, mode)) 28262306a36Sopenharmony_ci return -EPERM; 28362306a36Sopenharmony_ci if (!ops || !ops->pr_register) 28462306a36Sopenharmony_ci return -EOPNOTSUPP; 28562306a36Sopenharmony_ci if (copy_from_user(®, arg, sizeof(reg))) 28662306a36Sopenharmony_ci return -EFAULT; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (reg.flags & ~PR_FL_IGNORE_KEY) 28962306a36Sopenharmony_ci return -EOPNOTSUPP; 29062306a36Sopenharmony_ci return ops->pr_register(bdev, reg.old_key, reg.new_key, reg.flags); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int blkdev_pr_reserve(struct block_device *bdev, blk_mode_t mode, 29462306a36Sopenharmony_ci struct pr_reservation __user *arg) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; 29762306a36Sopenharmony_ci struct pr_reservation rsv; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!blkdev_pr_allowed(bdev, mode)) 30062306a36Sopenharmony_ci return -EPERM; 30162306a36Sopenharmony_ci if (!ops || !ops->pr_reserve) 30262306a36Sopenharmony_ci return -EOPNOTSUPP; 30362306a36Sopenharmony_ci if (copy_from_user(&rsv, arg, sizeof(rsv))) 30462306a36Sopenharmony_ci return -EFAULT; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (rsv.flags & ~PR_FL_IGNORE_KEY) 30762306a36Sopenharmony_ci return -EOPNOTSUPP; 30862306a36Sopenharmony_ci return ops->pr_reserve(bdev, rsv.key, rsv.type, rsv.flags); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int blkdev_pr_release(struct block_device *bdev, blk_mode_t mode, 31262306a36Sopenharmony_ci struct pr_reservation __user *arg) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; 31562306a36Sopenharmony_ci struct pr_reservation rsv; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!blkdev_pr_allowed(bdev, mode)) 31862306a36Sopenharmony_ci return -EPERM; 31962306a36Sopenharmony_ci if (!ops || !ops->pr_release) 32062306a36Sopenharmony_ci return -EOPNOTSUPP; 32162306a36Sopenharmony_ci if (copy_from_user(&rsv, arg, sizeof(rsv))) 32262306a36Sopenharmony_ci return -EFAULT; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (rsv.flags) 32562306a36Sopenharmony_ci return -EOPNOTSUPP; 32662306a36Sopenharmony_ci return ops->pr_release(bdev, rsv.key, rsv.type); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int blkdev_pr_preempt(struct block_device *bdev, blk_mode_t mode, 33062306a36Sopenharmony_ci struct pr_preempt __user *arg, bool abort) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; 33362306a36Sopenharmony_ci struct pr_preempt p; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!blkdev_pr_allowed(bdev, mode)) 33662306a36Sopenharmony_ci return -EPERM; 33762306a36Sopenharmony_ci if (!ops || !ops->pr_preempt) 33862306a36Sopenharmony_ci return -EOPNOTSUPP; 33962306a36Sopenharmony_ci if (copy_from_user(&p, arg, sizeof(p))) 34062306a36Sopenharmony_ci return -EFAULT; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (p.flags) 34362306a36Sopenharmony_ci return -EOPNOTSUPP; 34462306a36Sopenharmony_ci return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int blkdev_pr_clear(struct block_device *bdev, blk_mode_t mode, 34862306a36Sopenharmony_ci struct pr_clear __user *arg) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; 35162306a36Sopenharmony_ci struct pr_clear c; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!blkdev_pr_allowed(bdev, mode)) 35462306a36Sopenharmony_ci return -EPERM; 35562306a36Sopenharmony_ci if (!ops || !ops->pr_clear) 35662306a36Sopenharmony_ci return -EOPNOTSUPP; 35762306a36Sopenharmony_ci if (copy_from_user(&c, arg, sizeof(c))) 35862306a36Sopenharmony_ci return -EFAULT; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (c.flags) 36162306a36Sopenharmony_ci return -EOPNOTSUPP; 36262306a36Sopenharmony_ci return ops->pr_clear(bdev, c.key); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int blkdev_flushbuf(struct block_device *bdev, unsigned cmd, 36662306a36Sopenharmony_ci unsigned long arg) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 36962306a36Sopenharmony_ci return -EACCES; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mutex_lock(&bdev->bd_holder_lock); 37262306a36Sopenharmony_ci if (bdev->bd_holder_ops && bdev->bd_holder_ops->sync) 37362306a36Sopenharmony_ci bdev->bd_holder_ops->sync(bdev); 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci sync_blockdev(bdev); 37662306a36Sopenharmony_ci mutex_unlock(&bdev->bd_holder_lock); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci invalidate_bdev(bdev); 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int blkdev_roset(struct block_device *bdev, unsigned cmd, 38362306a36Sopenharmony_ci unsigned long arg) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci int ret, n; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 38862306a36Sopenharmony_ci return -EACCES; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (get_user(n, (int __user *)arg)) 39162306a36Sopenharmony_ci return -EFAULT; 39262306a36Sopenharmony_ci if (bdev->bd_disk->fops->set_read_only) { 39362306a36Sopenharmony_ci ret = bdev->bd_disk->fops->set_read_only(bdev, n); 39462306a36Sopenharmony_ci if (ret) 39562306a36Sopenharmony_ci return ret; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci bdev->bd_read_only = n; 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int blkdev_getgeo(struct block_device *bdev, 40262306a36Sopenharmony_ci struct hd_geometry __user *argp) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 40562306a36Sopenharmony_ci struct hd_geometry geo; 40662306a36Sopenharmony_ci int ret; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!argp) 40962306a36Sopenharmony_ci return -EINVAL; 41062306a36Sopenharmony_ci if (!disk->fops->getgeo) 41162306a36Sopenharmony_ci return -ENOTTY; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * We need to set the startsect first, the driver may 41562306a36Sopenharmony_ci * want to override it. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci memset(&geo, 0, sizeof(geo)); 41862306a36Sopenharmony_ci geo.start = get_start_sect(bdev); 41962306a36Sopenharmony_ci ret = disk->fops->getgeo(bdev, &geo); 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci if (copy_to_user(argp, &geo, sizeof(geo))) 42362306a36Sopenharmony_ci return -EFAULT; 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 42862306a36Sopenharmony_cistruct compat_hd_geometry { 42962306a36Sopenharmony_ci unsigned char heads; 43062306a36Sopenharmony_ci unsigned char sectors; 43162306a36Sopenharmony_ci unsigned short cylinders; 43262306a36Sopenharmony_ci u32 start; 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int compat_hdio_getgeo(struct block_device *bdev, 43662306a36Sopenharmony_ci struct compat_hd_geometry __user *ugeo) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 43962306a36Sopenharmony_ci struct hd_geometry geo; 44062306a36Sopenharmony_ci int ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!ugeo) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci if (!disk->fops->getgeo) 44562306a36Sopenharmony_ci return -ENOTTY; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci memset(&geo, 0, sizeof(geo)); 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * We need to set the startsect first, the driver may 45062306a36Sopenharmony_ci * want to override it. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci geo.start = get_start_sect(bdev); 45362306a36Sopenharmony_ci ret = disk->fops->getgeo(bdev, &geo); 45462306a36Sopenharmony_ci if (ret) 45562306a36Sopenharmony_ci return ret; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = copy_to_user(ugeo, &geo, 4); 45862306a36Sopenharmony_ci ret |= put_user(geo.start, &ugeo->start); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci ret = -EFAULT; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci#endif 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* set the logical block size */ 46762306a36Sopenharmony_cistatic int blkdev_bszset(struct block_device *bdev, blk_mode_t mode, 46862306a36Sopenharmony_ci int __user *argp) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci int ret, n; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 47362306a36Sopenharmony_ci return -EACCES; 47462306a36Sopenharmony_ci if (!argp) 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci if (get_user(n, argp)) 47762306a36Sopenharmony_ci return -EFAULT; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (mode & BLK_OPEN_EXCL) 48062306a36Sopenharmony_ci return set_blocksize(bdev, n); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (IS_ERR(blkdev_get_by_dev(bdev->bd_dev, mode, &bdev, NULL))) 48362306a36Sopenharmony_ci return -EBUSY; 48462306a36Sopenharmony_ci ret = set_blocksize(bdev, n); 48562306a36Sopenharmony_ci blkdev_put(bdev, &bdev); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* 49162306a36Sopenharmony_ci * Common commands that are handled the same way on native and compat 49262306a36Sopenharmony_ci * user space. Note the separate arg/argp parameters that are needed 49362306a36Sopenharmony_ci * to deal with the compat_ptr() conversion. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistatic int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode, 49662306a36Sopenharmony_ci unsigned int cmd, unsigned long arg, 49762306a36Sopenharmony_ci void __user *argp) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci unsigned int max_sectors; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (cmd) { 50262306a36Sopenharmony_ci case BLKFLSBUF: 50362306a36Sopenharmony_ci return blkdev_flushbuf(bdev, cmd, arg); 50462306a36Sopenharmony_ci case BLKROSET: 50562306a36Sopenharmony_ci return blkdev_roset(bdev, cmd, arg); 50662306a36Sopenharmony_ci case BLKDISCARD: 50762306a36Sopenharmony_ci return blk_ioctl_discard(bdev, mode, arg); 50862306a36Sopenharmony_ci case BLKSECDISCARD: 50962306a36Sopenharmony_ci return blk_ioctl_secure_erase(bdev, mode, argp); 51062306a36Sopenharmony_ci case BLKZEROOUT: 51162306a36Sopenharmony_ci return blk_ioctl_zeroout(bdev, mode, arg); 51262306a36Sopenharmony_ci case BLKGETDISKSEQ: 51362306a36Sopenharmony_ci return put_u64(argp, bdev->bd_disk->diskseq); 51462306a36Sopenharmony_ci case BLKREPORTZONE: 51562306a36Sopenharmony_ci return blkdev_report_zones_ioctl(bdev, cmd, arg); 51662306a36Sopenharmony_ci case BLKRESETZONE: 51762306a36Sopenharmony_ci case BLKOPENZONE: 51862306a36Sopenharmony_ci case BLKCLOSEZONE: 51962306a36Sopenharmony_ci case BLKFINISHZONE: 52062306a36Sopenharmony_ci return blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg); 52162306a36Sopenharmony_ci case BLKGETZONESZ: 52262306a36Sopenharmony_ci return put_uint(argp, bdev_zone_sectors(bdev)); 52362306a36Sopenharmony_ci case BLKGETNRZONES: 52462306a36Sopenharmony_ci return put_uint(argp, bdev_nr_zones(bdev)); 52562306a36Sopenharmony_ci case BLKROGET: 52662306a36Sopenharmony_ci return put_int(argp, bdev_read_only(bdev) != 0); 52762306a36Sopenharmony_ci case BLKSSZGET: /* get block device logical block size */ 52862306a36Sopenharmony_ci return put_int(argp, bdev_logical_block_size(bdev)); 52962306a36Sopenharmony_ci case BLKPBSZGET: /* get block device physical block size */ 53062306a36Sopenharmony_ci return put_uint(argp, bdev_physical_block_size(bdev)); 53162306a36Sopenharmony_ci case BLKIOMIN: 53262306a36Sopenharmony_ci return put_uint(argp, bdev_io_min(bdev)); 53362306a36Sopenharmony_ci case BLKIOOPT: 53462306a36Sopenharmony_ci return put_uint(argp, bdev_io_opt(bdev)); 53562306a36Sopenharmony_ci case BLKALIGNOFF: 53662306a36Sopenharmony_ci return put_int(argp, bdev_alignment_offset(bdev)); 53762306a36Sopenharmony_ci case BLKDISCARDZEROES: 53862306a36Sopenharmony_ci return put_uint(argp, 0); 53962306a36Sopenharmony_ci case BLKSECTGET: 54062306a36Sopenharmony_ci max_sectors = min_t(unsigned int, USHRT_MAX, 54162306a36Sopenharmony_ci queue_max_sectors(bdev_get_queue(bdev))); 54262306a36Sopenharmony_ci return put_ushort(argp, max_sectors); 54362306a36Sopenharmony_ci case BLKROTATIONAL: 54462306a36Sopenharmony_ci return put_ushort(argp, !bdev_nonrot(bdev)); 54562306a36Sopenharmony_ci case BLKRASET: 54662306a36Sopenharmony_ci case BLKFRASET: 54762306a36Sopenharmony_ci if(!capable(CAP_SYS_ADMIN)) 54862306a36Sopenharmony_ci return -EACCES; 54962306a36Sopenharmony_ci bdev->bd_disk->bdi->ra_pages = (arg * 512) / PAGE_SIZE; 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci case BLKRRPART: 55262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 55362306a36Sopenharmony_ci return -EACCES; 55462306a36Sopenharmony_ci if (bdev_is_partition(bdev)) 55562306a36Sopenharmony_ci return -EINVAL; 55662306a36Sopenharmony_ci return disk_scan_partitions(bdev->bd_disk, mode); 55762306a36Sopenharmony_ci case BLKTRACESTART: 55862306a36Sopenharmony_ci case BLKTRACESTOP: 55962306a36Sopenharmony_ci case BLKTRACETEARDOWN: 56062306a36Sopenharmony_ci return blk_trace_ioctl(bdev, cmd, argp); 56162306a36Sopenharmony_ci case IOC_PR_REGISTER: 56262306a36Sopenharmony_ci return blkdev_pr_register(bdev, mode, argp); 56362306a36Sopenharmony_ci case IOC_PR_RESERVE: 56462306a36Sopenharmony_ci return blkdev_pr_reserve(bdev, mode, argp); 56562306a36Sopenharmony_ci case IOC_PR_RELEASE: 56662306a36Sopenharmony_ci return blkdev_pr_release(bdev, mode, argp); 56762306a36Sopenharmony_ci case IOC_PR_PREEMPT: 56862306a36Sopenharmony_ci return blkdev_pr_preempt(bdev, mode, argp, false); 56962306a36Sopenharmony_ci case IOC_PR_PREEMPT_ABORT: 57062306a36Sopenharmony_ci return blkdev_pr_preempt(bdev, mode, argp, true); 57162306a36Sopenharmony_ci case IOC_PR_CLEAR: 57262306a36Sopenharmony_ci return blkdev_pr_clear(bdev, mode, argp); 57362306a36Sopenharmony_ci default: 57462306a36Sopenharmony_ci return -ENOIOCTLCMD; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* 57962306a36Sopenharmony_ci * Always keep this in sync with compat_blkdev_ioctl() 58062306a36Sopenharmony_ci * to handle all incompatible commands in both functions. 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * New commands must be compatible and go into blkdev_common_ioctl 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_cilong blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct block_device *bdev = I_BDEV(file->f_mapping->host); 58762306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 58862306a36Sopenharmony_ci blk_mode_t mode = file_to_blk_mode(file); 58962306a36Sopenharmony_ci int ret; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci switch (cmd) { 59262306a36Sopenharmony_ci /* These need separate implementations for the data structure */ 59362306a36Sopenharmony_ci case HDIO_GETGEO: 59462306a36Sopenharmony_ci return blkdev_getgeo(bdev, argp); 59562306a36Sopenharmony_ci case BLKPG: 59662306a36Sopenharmony_ci return blkpg_ioctl(bdev, argp); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Compat mode returns 32-bit data instead of 'long' */ 59962306a36Sopenharmony_ci case BLKRAGET: 60062306a36Sopenharmony_ci case BLKFRAGET: 60162306a36Sopenharmony_ci if (!argp) 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci return put_long(argp, 60462306a36Sopenharmony_ci (bdev->bd_disk->bdi->ra_pages * PAGE_SIZE) / 512); 60562306a36Sopenharmony_ci case BLKGETSIZE: 60662306a36Sopenharmony_ci if (bdev_nr_sectors(bdev) > ~0UL) 60762306a36Sopenharmony_ci return -EFBIG; 60862306a36Sopenharmony_ci return put_ulong(argp, bdev_nr_sectors(bdev)); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* The data is compatible, but the command number is different */ 61162306a36Sopenharmony_ci case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ 61262306a36Sopenharmony_ci return put_int(argp, block_size(bdev)); 61362306a36Sopenharmony_ci case BLKBSZSET: 61462306a36Sopenharmony_ci return blkdev_bszset(bdev, mode, argp); 61562306a36Sopenharmony_ci case BLKGETSIZE64: 61662306a36Sopenharmony_ci return put_u64(argp, bdev_nr_bytes(bdev)); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* Incompatible alignment on i386 */ 61962306a36Sopenharmony_ci case BLKTRACESETUP: 62062306a36Sopenharmony_ci return blk_trace_ioctl(bdev, cmd, argp); 62162306a36Sopenharmony_ci default: 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); 62662306a36Sopenharmony_ci if (ret != -ENOIOCTLCMD) 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (!bdev->bd_disk->fops->ioctl) 63062306a36Sopenharmony_ci return -ENOTTY; 63162306a36Sopenharmony_ci return bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci#define BLKBSZGET_32 _IOR(0x12, 112, int) 63762306a36Sopenharmony_ci#define BLKBSZSET_32 _IOW(0x12, 113, int) 63862306a36Sopenharmony_ci#define BLKGETSIZE64_32 _IOR(0x12, 114, int) 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/* Most of the generic ioctls are handled in the normal fallback path. 64162306a36Sopenharmony_ci This assumes the blkdev's low level compat_ioctl always returns 64262306a36Sopenharmony_ci ENOIOCTLCMD for unknown ioctls. */ 64362306a36Sopenharmony_cilong compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci int ret; 64662306a36Sopenharmony_ci void __user *argp = compat_ptr(arg); 64762306a36Sopenharmony_ci struct block_device *bdev = I_BDEV(file->f_mapping->host); 64862306a36Sopenharmony_ci struct gendisk *disk = bdev->bd_disk; 64962306a36Sopenharmony_ci blk_mode_t mode = file_to_blk_mode(file); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci switch (cmd) { 65262306a36Sopenharmony_ci /* These need separate implementations for the data structure */ 65362306a36Sopenharmony_ci case HDIO_GETGEO: 65462306a36Sopenharmony_ci return compat_hdio_getgeo(bdev, argp); 65562306a36Sopenharmony_ci case BLKPG: 65662306a36Sopenharmony_ci return compat_blkpg_ioctl(bdev, argp); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* Compat mode returns 32-bit data instead of 'long' */ 65962306a36Sopenharmony_ci case BLKRAGET: 66062306a36Sopenharmony_ci case BLKFRAGET: 66162306a36Sopenharmony_ci if (!argp) 66262306a36Sopenharmony_ci return -EINVAL; 66362306a36Sopenharmony_ci return compat_put_long(argp, 66462306a36Sopenharmony_ci (bdev->bd_disk->bdi->ra_pages * PAGE_SIZE) / 512); 66562306a36Sopenharmony_ci case BLKGETSIZE: 66662306a36Sopenharmony_ci if (bdev_nr_sectors(bdev) > ~(compat_ulong_t)0) 66762306a36Sopenharmony_ci return -EFBIG; 66862306a36Sopenharmony_ci return compat_put_ulong(argp, bdev_nr_sectors(bdev)); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* The data is compatible, but the command number is different */ 67162306a36Sopenharmony_ci case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */ 67262306a36Sopenharmony_ci return put_int(argp, bdev_logical_block_size(bdev)); 67362306a36Sopenharmony_ci case BLKBSZSET_32: 67462306a36Sopenharmony_ci return blkdev_bszset(bdev, mode, argp); 67562306a36Sopenharmony_ci case BLKGETSIZE64_32: 67662306a36Sopenharmony_ci return put_u64(argp, bdev_nr_bytes(bdev)); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Incompatible alignment on i386 */ 67962306a36Sopenharmony_ci case BLKTRACESETUP32: 68062306a36Sopenharmony_ci return blk_trace_ioctl(bdev, cmd, argp); 68162306a36Sopenharmony_ci default: 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); 68662306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl) 68762306a36Sopenharmony_ci ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return ret; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci#endif 692