| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | #include <linux/capability.h> |
|---|
| 3 | +#include <linux/compat.h> |
|---|
| 2 | 4 | #include <linux/blkdev.h> |
|---|
| 3 | 5 | #include <linux/export.h> |
|---|
| 4 | 6 | #include <linux/gfp.h> |
|---|
| .. | .. |
|---|
| 9 | 11 | #include <linux/blktrace_api.h> |
|---|
| 10 | 12 | #include <linux/pr.h> |
|---|
| 11 | 13 | #include <linux/uaccess.h> |
|---|
| 14 | +#include "blk.h" |
|---|
| 12 | 15 | |
|---|
| 13 | | -static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) |
|---|
| 16 | +static int blkpg_do_ioctl(struct block_device *bdev, |
|---|
| 17 | + struct blkpg_partition __user *upart, int op) |
|---|
| 14 | 18 | { |
|---|
| 15 | | - struct block_device *bdevp; |
|---|
| 16 | | - struct gendisk *disk; |
|---|
| 17 | | - struct hd_struct *part, *lpart; |
|---|
| 18 | | - struct blkpg_ioctl_arg a; |
|---|
| 19 | 19 | struct blkpg_partition p; |
|---|
| 20 | | - struct disk_part_iter piter; |
|---|
| 21 | 20 | long long start, length; |
|---|
| 22 | | - int partno; |
|---|
| 23 | 21 | |
|---|
| 24 | 22 | if (!capable(CAP_SYS_ADMIN)) |
|---|
| 25 | 23 | return -EACCES; |
|---|
| 26 | | - if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) |
|---|
| 24 | + if (copy_from_user(&p, upart, sizeof(struct blkpg_partition))) |
|---|
| 27 | 25 | return -EFAULT; |
|---|
| 28 | | - if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) |
|---|
| 29 | | - return -EFAULT; |
|---|
| 30 | | - disk = bdev->bd_disk; |
|---|
| 31 | | - if (bdev != bdev->bd_contains) |
|---|
| 26 | + if (bdev_is_partition(bdev)) |
|---|
| 32 | 27 | return -EINVAL; |
|---|
| 33 | | - partno = p.pno; |
|---|
| 34 | | - if (partno <= 0) |
|---|
| 28 | + |
|---|
| 29 | + if (p.pno <= 0) |
|---|
| 35 | 30 | return -EINVAL; |
|---|
| 36 | | - switch (a.op) { |
|---|
| 37 | | - case BLKPG_ADD_PARTITION: |
|---|
| 38 | | - start = p.start >> 9; |
|---|
| 39 | | - length = p.length >> 9; |
|---|
| 40 | | - /* check for fit in a hd_struct */ |
|---|
| 41 | | - if (sizeof(sector_t) == sizeof(long) && |
|---|
| 42 | | - sizeof(long long) > sizeof(long)) { |
|---|
| 43 | | - long pstart = start, plength = length; |
|---|
| 44 | | - if (pstart != start || plength != length |
|---|
| 45 | | - || pstart < 0 || plength < 0 || partno > 65535) |
|---|
| 46 | | - return -EINVAL; |
|---|
| 47 | | - } |
|---|
| 48 | | - /* check if partition is aligned to blocksize */ |
|---|
| 49 | | - if (p.start & (bdev_logical_block_size(bdev) - 1)) |
|---|
| 50 | | - return -EINVAL; |
|---|
| 51 | 31 | |
|---|
| 52 | | - mutex_lock(&bdev->bd_mutex); |
|---|
| 32 | + if (op == BLKPG_DEL_PARTITION) |
|---|
| 33 | + return bdev_del_partition(bdev, p.pno); |
|---|
| 53 | 34 | |
|---|
| 54 | | - /* overlap? */ |
|---|
| 55 | | - disk_part_iter_init(&piter, disk, |
|---|
| 56 | | - DISK_PITER_INCL_EMPTY); |
|---|
| 57 | | - while ((part = disk_part_iter_next(&piter))) { |
|---|
| 58 | | - if (!(start + length <= part->start_sect || |
|---|
| 59 | | - start >= part->start_sect + part->nr_sects)) { |
|---|
| 60 | | - disk_part_iter_exit(&piter); |
|---|
| 61 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 62 | | - return -EBUSY; |
|---|
| 63 | | - } |
|---|
| 64 | | - } |
|---|
| 65 | | - disk_part_iter_exit(&piter); |
|---|
| 35 | + start = p.start >> SECTOR_SHIFT; |
|---|
| 36 | + length = p.length >> SECTOR_SHIFT; |
|---|
| 66 | 37 | |
|---|
| 67 | | - /* all seems OK */ |
|---|
| 68 | | - part = add_partition(disk, partno, start, length, |
|---|
| 69 | | - ADDPART_FLAG_NONE, NULL); |
|---|
| 70 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 71 | | - return PTR_ERR_OR_ZERO(part); |
|---|
| 72 | | - case BLKPG_DEL_PARTITION: |
|---|
| 73 | | - part = disk_get_part(disk, partno); |
|---|
| 74 | | - if (!part) |
|---|
| 75 | | - return -ENXIO; |
|---|
| 38 | + /* check for fit in a hd_struct */ |
|---|
| 39 | + if (sizeof(sector_t) < sizeof(long long)) { |
|---|
| 40 | + long pstart = start, plength = length; |
|---|
| 76 | 41 | |
|---|
| 77 | | - bdevp = bdget(part_devt(part)); |
|---|
| 78 | | - disk_put_part(part); |
|---|
| 79 | | - if (!bdevp) |
|---|
| 80 | | - return -ENOMEM; |
|---|
| 81 | | - |
|---|
| 82 | | - mutex_lock(&bdevp->bd_mutex); |
|---|
| 83 | | - if (bdevp->bd_openers) { |
|---|
| 84 | | - mutex_unlock(&bdevp->bd_mutex); |
|---|
| 85 | | - bdput(bdevp); |
|---|
| 86 | | - return -EBUSY; |
|---|
| 87 | | - } |
|---|
| 88 | | - /* all seems OK */ |
|---|
| 89 | | - fsync_bdev(bdevp); |
|---|
| 90 | | - invalidate_bdev(bdevp); |
|---|
| 91 | | - |
|---|
| 92 | | - mutex_lock_nested(&bdev->bd_mutex, 1); |
|---|
| 93 | | - delete_partition(disk, partno); |
|---|
| 94 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 95 | | - mutex_unlock(&bdevp->bd_mutex); |
|---|
| 96 | | - bdput(bdevp); |
|---|
| 97 | | - |
|---|
| 98 | | - return 0; |
|---|
| 99 | | - case BLKPG_RESIZE_PARTITION: |
|---|
| 100 | | - start = p.start >> 9; |
|---|
| 101 | | - /* new length of partition in bytes */ |
|---|
| 102 | | - length = p.length >> 9; |
|---|
| 103 | | - /* check for fit in a hd_struct */ |
|---|
| 104 | | - if (sizeof(sector_t) == sizeof(long) && |
|---|
| 105 | | - sizeof(long long) > sizeof(long)) { |
|---|
| 106 | | - long pstart = start, plength = length; |
|---|
| 107 | | - if (pstart != start || plength != length |
|---|
| 108 | | - || pstart < 0 || plength < 0) |
|---|
| 109 | | - return -EINVAL; |
|---|
| 110 | | - } |
|---|
| 111 | | - part = disk_get_part(disk, partno); |
|---|
| 112 | | - if (!part) |
|---|
| 113 | | - return -ENXIO; |
|---|
| 114 | | - bdevp = bdget(part_devt(part)); |
|---|
| 115 | | - if (!bdevp) { |
|---|
| 116 | | - disk_put_part(part); |
|---|
| 117 | | - return -ENOMEM; |
|---|
| 118 | | - } |
|---|
| 119 | | - mutex_lock(&bdevp->bd_mutex); |
|---|
| 120 | | - mutex_lock_nested(&bdev->bd_mutex, 1); |
|---|
| 121 | | - if (start != part->start_sect) { |
|---|
| 122 | | - mutex_unlock(&bdevp->bd_mutex); |
|---|
| 123 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 124 | | - bdput(bdevp); |
|---|
| 125 | | - disk_put_part(part); |
|---|
| 126 | | - return -EINVAL; |
|---|
| 127 | | - } |
|---|
| 128 | | - /* overlap? */ |
|---|
| 129 | | - disk_part_iter_init(&piter, disk, |
|---|
| 130 | | - DISK_PITER_INCL_EMPTY); |
|---|
| 131 | | - while ((lpart = disk_part_iter_next(&piter))) { |
|---|
| 132 | | - if (lpart->partno != partno && |
|---|
| 133 | | - !(start + length <= lpart->start_sect || |
|---|
| 134 | | - start >= lpart->start_sect + lpart->nr_sects) |
|---|
| 135 | | - ) { |
|---|
| 136 | | - disk_part_iter_exit(&piter); |
|---|
| 137 | | - mutex_unlock(&bdevp->bd_mutex); |
|---|
| 138 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 139 | | - bdput(bdevp); |
|---|
| 140 | | - disk_put_part(part); |
|---|
| 141 | | - return -EBUSY; |
|---|
| 142 | | - } |
|---|
| 143 | | - } |
|---|
| 144 | | - disk_part_iter_exit(&piter); |
|---|
| 145 | | - part_nr_sects_write(part, (sector_t)length); |
|---|
| 146 | | - i_size_write(bdevp->bd_inode, p.length); |
|---|
| 147 | | - mutex_unlock(&bdevp->bd_mutex); |
|---|
| 148 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 149 | | - bdput(bdevp); |
|---|
| 150 | | - disk_put_part(part); |
|---|
| 151 | | - return 0; |
|---|
| 152 | | - default: |
|---|
| 42 | + if (pstart != start || plength != length || pstart < 0 || |
|---|
| 43 | + plength < 0 || p.pno > 65535) |
|---|
| 153 | 44 | return -EINVAL; |
|---|
| 45 | + } |
|---|
| 46 | + |
|---|
| 47 | + switch (op) { |
|---|
| 48 | + case BLKPG_ADD_PARTITION: |
|---|
| 49 | + /* check if partition is aligned to blocksize */ |
|---|
| 50 | + if (p.start & (bdev_logical_block_size(bdev) - 1)) |
|---|
| 51 | + return -EINVAL; |
|---|
| 52 | + return bdev_add_partition(bdev, p.pno, start, length); |
|---|
| 53 | + case BLKPG_RESIZE_PARTITION: |
|---|
| 54 | + return bdev_resize_partition(bdev, p.pno, start, length); |
|---|
| 55 | + default: |
|---|
| 56 | + return -EINVAL; |
|---|
| 154 | 57 | } |
|---|
| 155 | 58 | } |
|---|
| 156 | 59 | |
|---|
| 157 | | -/* |
|---|
| 158 | | - * This is an exported API for the block driver, and will not |
|---|
| 159 | | - * acquire bd_mutex. This API should be used in case that |
|---|
| 160 | | - * caller has held bd_mutex already. |
|---|
| 161 | | - */ |
|---|
| 162 | | -int __blkdev_reread_part(struct block_device *bdev) |
|---|
| 60 | +static int blkpg_ioctl(struct block_device *bdev, |
|---|
| 61 | + struct blkpg_ioctl_arg __user *arg) |
|---|
| 163 | 62 | { |
|---|
| 164 | | - struct gendisk *disk = bdev->bd_disk; |
|---|
| 63 | + struct blkpg_partition __user *udata; |
|---|
| 64 | + int op; |
|---|
| 165 | 65 | |
|---|
| 166 | | - if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains) |
|---|
| 66 | + if (get_user(op, &arg->op) || get_user(udata, &arg->data)) |
|---|
| 67 | + return -EFAULT; |
|---|
| 68 | + |
|---|
| 69 | + return blkpg_do_ioctl(bdev, udata, op); |
|---|
| 70 | +} |
|---|
| 71 | + |
|---|
| 72 | +#ifdef CONFIG_COMPAT |
|---|
| 73 | +struct compat_blkpg_ioctl_arg { |
|---|
| 74 | + compat_int_t op; |
|---|
| 75 | + compat_int_t flags; |
|---|
| 76 | + compat_int_t datalen; |
|---|
| 77 | + compat_caddr_t data; |
|---|
| 78 | +}; |
|---|
| 79 | + |
|---|
| 80 | +static int compat_blkpg_ioctl(struct block_device *bdev, |
|---|
| 81 | + struct compat_blkpg_ioctl_arg __user *arg) |
|---|
| 82 | +{ |
|---|
| 83 | + compat_caddr_t udata; |
|---|
| 84 | + int op; |
|---|
| 85 | + |
|---|
| 86 | + if (get_user(op, &arg->op) || get_user(udata, &arg->data)) |
|---|
| 87 | + return -EFAULT; |
|---|
| 88 | + |
|---|
| 89 | + return blkpg_do_ioctl(bdev, compat_ptr(udata), op); |
|---|
| 90 | +} |
|---|
| 91 | +#endif |
|---|
| 92 | + |
|---|
| 93 | +static int blkdev_reread_part(struct block_device *bdev, fmode_t mode) |
|---|
| 94 | +{ |
|---|
| 95 | + struct block_device *tmp; |
|---|
| 96 | + |
|---|
| 97 | + if (!disk_part_scan_enabled(bdev->bd_disk) || bdev_is_partition(bdev)) |
|---|
| 167 | 98 | return -EINVAL; |
|---|
| 168 | 99 | if (!capable(CAP_SYS_ADMIN)) |
|---|
| 169 | 100 | return -EACCES; |
|---|
| 101 | + if (bdev->bd_part_count) |
|---|
| 102 | + return -EBUSY; |
|---|
| 170 | 103 | |
|---|
| 171 | | - lockdep_assert_held(&bdev->bd_mutex); |
|---|
| 104 | + /* |
|---|
| 105 | + * Reopen the device to revalidate the driver state and force a |
|---|
| 106 | + * partition rescan. |
|---|
| 107 | + */ |
|---|
| 108 | + mode &= ~FMODE_EXCL; |
|---|
| 109 | + set_bit(GD_NEED_PART_SCAN, &bdev->bd_disk->state); |
|---|
| 172 | 110 | |
|---|
| 173 | | - return rescan_partitions(disk, bdev); |
|---|
| 111 | + tmp = blkdev_get_by_dev(bdev->bd_dev, mode, NULL); |
|---|
| 112 | + if (IS_ERR(tmp)) |
|---|
| 113 | + return PTR_ERR(tmp); |
|---|
| 114 | + blkdev_put(tmp, mode); |
|---|
| 115 | + return 0; |
|---|
| 174 | 116 | } |
|---|
| 175 | | -EXPORT_SYMBOL(__blkdev_reread_part); |
|---|
| 176 | | - |
|---|
| 177 | | -/* |
|---|
| 178 | | - * This is an exported API for the block driver, and will |
|---|
| 179 | | - * try to acquire bd_mutex. If bd_mutex has been held already |
|---|
| 180 | | - * in current context, please call __blkdev_reread_part(). |
|---|
| 181 | | - * |
|---|
| 182 | | - * Make sure the held locks in current context aren't required |
|---|
| 183 | | - * in open()/close() handler and I/O path for avoiding ABBA deadlock: |
|---|
| 184 | | - * - bd_mutex is held before calling block driver's open/close |
|---|
| 185 | | - * handler |
|---|
| 186 | | - * - reading partition table may submit I/O to the block device |
|---|
| 187 | | - */ |
|---|
| 188 | | -int blkdev_reread_part(struct block_device *bdev) |
|---|
| 189 | | -{ |
|---|
| 190 | | - int res; |
|---|
| 191 | | - |
|---|
| 192 | | - mutex_lock(&bdev->bd_mutex); |
|---|
| 193 | | - res = __blkdev_reread_part(bdev); |
|---|
| 194 | | - mutex_unlock(&bdev->bd_mutex); |
|---|
| 195 | | - |
|---|
| 196 | | - return res; |
|---|
| 197 | | -} |
|---|
| 198 | | -EXPORT_SYMBOL(blkdev_reread_part); |
|---|
| 199 | 117 | |
|---|
| 200 | 118 | static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode, |
|---|
| 201 | 119 | unsigned long arg, unsigned long flags) |
|---|
| .. | .. |
|---|
| 203 | 121 | uint64_t range[2]; |
|---|
| 204 | 122 | uint64_t start, len; |
|---|
| 205 | 123 | struct request_queue *q = bdev_get_queue(bdev); |
|---|
| 206 | | - struct address_space *mapping = bdev->bd_inode->i_mapping; |
|---|
| 207 | | - |
|---|
| 124 | + int err; |
|---|
| 208 | 125 | |
|---|
| 209 | 126 | if (!(mode & FMODE_WRITE)) |
|---|
| 210 | 127 | return -EBADF; |
|---|
| .. | .. |
|---|
| 225 | 142 | |
|---|
| 226 | 143 | if (start + len > i_size_read(bdev->bd_inode)) |
|---|
| 227 | 144 | return -EINVAL; |
|---|
| 228 | | - truncate_inode_pages_range(mapping, start, start + len - 1); |
|---|
| 145 | + |
|---|
| 146 | + err = truncate_bdev_range(bdev, mode, start, start + len - 1); |
|---|
| 147 | + if (err) |
|---|
| 148 | + return err; |
|---|
| 149 | + |
|---|
| 229 | 150 | return blkdev_issue_discard(bdev, start >> 9, len >> 9, |
|---|
| 230 | 151 | GFP_KERNEL, flags); |
|---|
| 231 | 152 | } |
|---|
| .. | .. |
|---|
| 234 | 155 | unsigned long arg) |
|---|
| 235 | 156 | { |
|---|
| 236 | 157 | uint64_t range[2]; |
|---|
| 237 | | - struct address_space *mapping; |
|---|
| 238 | 158 | uint64_t start, end, len; |
|---|
| 159 | + int err; |
|---|
| 239 | 160 | |
|---|
| 240 | 161 | if (!(mode & FMODE_WRITE)) |
|---|
| 241 | 162 | return -EBADF; |
|---|
| .. | .. |
|---|
| 257 | 178 | return -EINVAL; |
|---|
| 258 | 179 | |
|---|
| 259 | 180 | /* Invalidate the page cache, including dirty pages */ |
|---|
| 260 | | - mapping = bdev->bd_inode->i_mapping; |
|---|
| 261 | | - truncate_inode_pages_range(mapping, start, end); |
|---|
| 181 | + err = truncate_bdev_range(bdev, mode, start, end); |
|---|
| 182 | + if (err) |
|---|
| 183 | + return err; |
|---|
| 262 | 184 | |
|---|
| 263 | 185 | return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL, |
|---|
| 264 | 186 | BLKDEV_ZERO_NOUNMAP); |
|---|
| 265 | 187 | } |
|---|
| 266 | 188 | |
|---|
| 267 | | -static int put_ushort(unsigned long arg, unsigned short val) |
|---|
| 189 | +static int put_ushort(unsigned short __user *argp, unsigned short val) |
|---|
| 268 | 190 | { |
|---|
| 269 | | - return put_user(val, (unsigned short __user *)arg); |
|---|
| 191 | + return put_user(val, argp); |
|---|
| 270 | 192 | } |
|---|
| 271 | 193 | |
|---|
| 272 | | -static int put_int(unsigned long arg, int val) |
|---|
| 194 | +static int put_int(int __user *argp, int val) |
|---|
| 273 | 195 | { |
|---|
| 274 | | - return put_user(val, (int __user *)arg); |
|---|
| 196 | + return put_user(val, argp); |
|---|
| 275 | 197 | } |
|---|
| 276 | 198 | |
|---|
| 277 | | -static int put_uint(unsigned long arg, unsigned int val) |
|---|
| 199 | +static int put_uint(unsigned int __user *argp, unsigned int val) |
|---|
| 278 | 200 | { |
|---|
| 279 | | - return put_user(val, (unsigned int __user *)arg); |
|---|
| 201 | + return put_user(val, argp); |
|---|
| 280 | 202 | } |
|---|
| 281 | 203 | |
|---|
| 282 | | -static int put_long(unsigned long arg, long val) |
|---|
| 204 | +static int put_long(long __user *argp, long val) |
|---|
| 283 | 205 | { |
|---|
| 284 | | - return put_user(val, (long __user *)arg); |
|---|
| 206 | + return put_user(val, argp); |
|---|
| 285 | 207 | } |
|---|
| 286 | 208 | |
|---|
| 287 | | -static int put_ulong(unsigned long arg, unsigned long val) |
|---|
| 209 | +static int put_ulong(unsigned long __user *argp, unsigned long val) |
|---|
| 288 | 210 | { |
|---|
| 289 | | - return put_user(val, (unsigned long __user *)arg); |
|---|
| 211 | + return put_user(val, argp); |
|---|
| 290 | 212 | } |
|---|
| 291 | 213 | |
|---|
| 292 | | -static int put_u64(unsigned long arg, u64 val) |
|---|
| 214 | +static int put_u64(u64 __user *argp, u64 val) |
|---|
| 293 | 215 | { |
|---|
| 294 | | - return put_user(val, (u64 __user *)arg); |
|---|
| 216 | + return put_user(val, argp); |
|---|
| 295 | 217 | } |
|---|
| 218 | + |
|---|
| 219 | +#ifdef CONFIG_COMPAT |
|---|
| 220 | +static int compat_put_long(compat_long_t __user *argp, long val) |
|---|
| 221 | +{ |
|---|
| 222 | + return put_user(val, argp); |
|---|
| 223 | +} |
|---|
| 224 | + |
|---|
| 225 | +static int compat_put_ulong(compat_ulong_t __user *argp, compat_ulong_t val) |
|---|
| 226 | +{ |
|---|
| 227 | + return put_user(val, argp); |
|---|
| 228 | +} |
|---|
| 229 | +#endif |
|---|
| 296 | 230 | |
|---|
| 297 | 231 | int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode, |
|---|
| 298 | 232 | unsigned cmd, unsigned long arg) |
|---|
| .. | .. |
|---|
| 310 | 244 | * at all and could be open-coded without any exports by anybody who cares. |
|---|
| 311 | 245 | */ |
|---|
| 312 | 246 | EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl); |
|---|
| 247 | + |
|---|
| 248 | +#ifdef CONFIG_COMPAT |
|---|
| 249 | +/* |
|---|
| 250 | + * This is the equivalent of compat_ptr_ioctl(), to be used by block |
|---|
| 251 | + * drivers that implement only commands that are completely compatible |
|---|
| 252 | + * between 32-bit and 64-bit user space |
|---|
| 253 | + */ |
|---|
| 254 | +int blkdev_compat_ptr_ioctl(struct block_device *bdev, fmode_t mode, |
|---|
| 255 | + unsigned cmd, unsigned long arg) |
|---|
| 256 | +{ |
|---|
| 257 | + struct gendisk *disk = bdev->bd_disk; |
|---|
| 258 | + |
|---|
| 259 | + if (disk->fops->ioctl) |
|---|
| 260 | + return disk->fops->ioctl(bdev, mode, cmd, |
|---|
| 261 | + (unsigned long)compat_ptr(arg)); |
|---|
| 262 | + |
|---|
| 263 | + return -ENOIOCTLCMD; |
|---|
| 264 | +} |
|---|
| 265 | +EXPORT_SYMBOL(blkdev_compat_ptr_ioctl); |
|---|
| 266 | +#endif |
|---|
| 313 | 267 | |
|---|
| 314 | 268 | static int blkdev_pr_register(struct block_device *bdev, |
|---|
| 315 | 269 | struct pr_registration __user *arg) |
|---|
| .. | .. |
|---|
| 481 | 435 | return 0; |
|---|
| 482 | 436 | } |
|---|
| 483 | 437 | |
|---|
| 438 | +#ifdef CONFIG_COMPAT |
|---|
| 439 | +struct compat_hd_geometry { |
|---|
| 440 | + unsigned char heads; |
|---|
| 441 | + unsigned char sectors; |
|---|
| 442 | + unsigned short cylinders; |
|---|
| 443 | + u32 start; |
|---|
| 444 | +}; |
|---|
| 445 | + |
|---|
| 446 | +static int compat_hdio_getgeo(struct block_device *bdev, |
|---|
| 447 | + struct compat_hd_geometry __user *ugeo) |
|---|
| 448 | +{ |
|---|
| 449 | + struct gendisk *disk = bdev->bd_disk; |
|---|
| 450 | + struct hd_geometry geo; |
|---|
| 451 | + int ret; |
|---|
| 452 | + |
|---|
| 453 | + if (!ugeo) |
|---|
| 454 | + return -EINVAL; |
|---|
| 455 | + if (!disk->fops->getgeo) |
|---|
| 456 | + return -ENOTTY; |
|---|
| 457 | + |
|---|
| 458 | + memset(&geo, 0, sizeof(geo)); |
|---|
| 459 | + /* |
|---|
| 460 | + * We need to set the startsect first, the driver may |
|---|
| 461 | + * want to override it. |
|---|
| 462 | + */ |
|---|
| 463 | + geo.start = get_start_sect(bdev); |
|---|
| 464 | + ret = disk->fops->getgeo(bdev, &geo); |
|---|
| 465 | + if (ret) |
|---|
| 466 | + return ret; |
|---|
| 467 | + |
|---|
| 468 | + ret = copy_to_user(ugeo, &geo, 4); |
|---|
| 469 | + ret |= put_user(geo.start, &ugeo->start); |
|---|
| 470 | + if (ret) |
|---|
| 471 | + ret = -EFAULT; |
|---|
| 472 | + |
|---|
| 473 | + return ret; |
|---|
| 474 | +} |
|---|
| 475 | +#endif |
|---|
| 476 | + |
|---|
| 484 | 477 | /* set the logical block size */ |
|---|
| 485 | 478 | static int blkdev_bszset(struct block_device *bdev, fmode_t mode, |
|---|
| 486 | 479 | int __user *argp) |
|---|
| .. | .. |
|---|
| 494 | 487 | if (get_user(n, argp)) |
|---|
| 495 | 488 | return -EFAULT; |
|---|
| 496 | 489 | |
|---|
| 497 | | - if (!(mode & FMODE_EXCL)) { |
|---|
| 498 | | - bdgrab(bdev); |
|---|
| 499 | | - if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0) |
|---|
| 500 | | - return -EBUSY; |
|---|
| 501 | | - } |
|---|
| 490 | + if (mode & FMODE_EXCL) |
|---|
| 491 | + return set_blocksize(bdev, n); |
|---|
| 502 | 492 | |
|---|
| 493 | + if (IS_ERR(blkdev_get_by_dev(bdev->bd_dev, mode | FMODE_EXCL, &bdev))) |
|---|
| 494 | + return -EBUSY; |
|---|
| 503 | 495 | ret = set_blocksize(bdev, n); |
|---|
| 504 | | - if (!(mode & FMODE_EXCL)) |
|---|
| 505 | | - blkdev_put(bdev, mode | FMODE_EXCL); |
|---|
| 496 | + blkdev_put(bdev, mode | FMODE_EXCL); |
|---|
| 497 | + |
|---|
| 506 | 498 | return ret; |
|---|
| 507 | 499 | } |
|---|
| 508 | 500 | |
|---|
| 509 | 501 | /* |
|---|
| 510 | | - * always keep this in sync with compat_blkdev_ioctl() |
|---|
| 502 | + * Common commands that are handled the same way on native and compat |
|---|
| 503 | + * user space. Note the separate arg/argp parameters that are needed |
|---|
| 504 | + * to deal with the compat_ptr() conversion. |
|---|
| 511 | 505 | */ |
|---|
| 512 | | -int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, |
|---|
| 513 | | - unsigned long arg) |
|---|
| 506 | +static int blkdev_common_ioctl(struct block_device *bdev, fmode_t mode, |
|---|
| 507 | + unsigned cmd, unsigned long arg, void __user *argp) |
|---|
| 514 | 508 | { |
|---|
| 515 | | - void __user *argp = (void __user *)arg; |
|---|
| 516 | | - loff_t size; |
|---|
| 517 | 509 | unsigned int max_sectors; |
|---|
| 518 | 510 | |
|---|
| 519 | 511 | switch (cmd) { |
|---|
| .. | .. |
|---|
| 531 | 523 | case BLKREPORTZONE: |
|---|
| 532 | 524 | return blkdev_report_zones_ioctl(bdev, mode, cmd, arg); |
|---|
| 533 | 525 | case BLKRESETZONE: |
|---|
| 534 | | - return blkdev_reset_zones_ioctl(bdev, mode, cmd, arg); |
|---|
| 535 | | - case HDIO_GETGEO: |
|---|
| 536 | | - return blkdev_getgeo(bdev, argp); |
|---|
| 537 | | - case BLKRAGET: |
|---|
| 538 | | - case BLKFRAGET: |
|---|
| 539 | | - if (!arg) |
|---|
| 540 | | - return -EINVAL; |
|---|
| 541 | | - return put_long(arg, (bdev->bd_bdi->ra_pages*PAGE_SIZE) / 512); |
|---|
| 526 | + case BLKOPENZONE: |
|---|
| 527 | + case BLKCLOSEZONE: |
|---|
| 528 | + case BLKFINISHZONE: |
|---|
| 529 | + return blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg); |
|---|
| 530 | + case BLKGETZONESZ: |
|---|
| 531 | + return put_uint(argp, bdev_zone_sectors(bdev)); |
|---|
| 532 | + case BLKGETNRZONES: |
|---|
| 533 | + return put_uint(argp, blkdev_nr_zones(bdev->bd_disk)); |
|---|
| 542 | 534 | case BLKROGET: |
|---|
| 543 | | - return put_int(arg, bdev_read_only(bdev) != 0); |
|---|
| 544 | | - case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ |
|---|
| 545 | | - return put_int(arg, block_size(bdev)); |
|---|
| 535 | + return put_int(argp, bdev_read_only(bdev) != 0); |
|---|
| 546 | 536 | case BLKSSZGET: /* get block device logical block size */ |
|---|
| 547 | | - return put_int(arg, bdev_logical_block_size(bdev)); |
|---|
| 537 | + return put_int(argp, bdev_logical_block_size(bdev)); |
|---|
| 548 | 538 | case BLKPBSZGET: /* get block device physical block size */ |
|---|
| 549 | | - return put_uint(arg, bdev_physical_block_size(bdev)); |
|---|
| 539 | + return put_uint(argp, bdev_physical_block_size(bdev)); |
|---|
| 550 | 540 | case BLKIOMIN: |
|---|
| 551 | | - return put_uint(arg, bdev_io_min(bdev)); |
|---|
| 541 | + return put_uint(argp, bdev_io_min(bdev)); |
|---|
| 552 | 542 | case BLKIOOPT: |
|---|
| 553 | | - return put_uint(arg, bdev_io_opt(bdev)); |
|---|
| 543 | + return put_uint(argp, bdev_io_opt(bdev)); |
|---|
| 554 | 544 | case BLKALIGNOFF: |
|---|
| 555 | | - return put_int(arg, bdev_alignment_offset(bdev)); |
|---|
| 545 | + return put_int(argp, bdev_alignment_offset(bdev)); |
|---|
| 556 | 546 | case BLKDISCARDZEROES: |
|---|
| 557 | | - return put_uint(arg, 0); |
|---|
| 547 | + return put_uint(argp, 0); |
|---|
| 558 | 548 | case BLKSECTGET: |
|---|
| 559 | 549 | max_sectors = min_t(unsigned int, USHRT_MAX, |
|---|
| 560 | 550 | queue_max_sectors(bdev_get_queue(bdev))); |
|---|
| 561 | | - return put_ushort(arg, max_sectors); |
|---|
| 551 | + return put_ushort(argp, max_sectors); |
|---|
| 562 | 552 | case BLKROTATIONAL: |
|---|
| 563 | | - return put_ushort(arg, !blk_queue_nonrot(bdev_get_queue(bdev))); |
|---|
| 553 | + return put_ushort(argp, !blk_queue_nonrot(bdev_get_queue(bdev))); |
|---|
| 564 | 554 | case BLKRASET: |
|---|
| 565 | 555 | case BLKFRASET: |
|---|
| 566 | 556 | if(!capable(CAP_SYS_ADMIN)) |
|---|
| 567 | 557 | return -EACCES; |
|---|
| 568 | 558 | bdev->bd_bdi->ra_pages = (arg * 512) / PAGE_SIZE; |
|---|
| 569 | 559 | return 0; |
|---|
| 570 | | - case BLKBSZSET: |
|---|
| 571 | | - return blkdev_bszset(bdev, mode, argp); |
|---|
| 572 | | - case BLKPG: |
|---|
| 573 | | - return blkpg_ioctl(bdev, argp); |
|---|
| 574 | 560 | case BLKRRPART: |
|---|
| 575 | | - return blkdev_reread_part(bdev); |
|---|
| 576 | | - case BLKGETSIZE: |
|---|
| 577 | | - size = i_size_read(bdev->bd_inode); |
|---|
| 578 | | - if ((size >> 9) > ~0UL) |
|---|
| 579 | | - return -EFBIG; |
|---|
| 580 | | - return put_ulong(arg, size >> 9); |
|---|
| 581 | | - case BLKGETSIZE64: |
|---|
| 582 | | - return put_u64(arg, i_size_read(bdev->bd_inode)); |
|---|
| 561 | + return blkdev_reread_part(bdev, mode); |
|---|
| 583 | 562 | case BLKTRACESTART: |
|---|
| 584 | 563 | case BLKTRACESTOP: |
|---|
| 585 | | - case BLKTRACESETUP: |
|---|
| 586 | 564 | case BLKTRACETEARDOWN: |
|---|
| 587 | 565 | return blk_trace_ioctl(bdev, cmd, argp); |
|---|
| 588 | 566 | case IOC_PR_REGISTER: |
|---|
| .. | .. |
|---|
| 598 | 576 | case IOC_PR_CLEAR: |
|---|
| 599 | 577 | return blkdev_pr_clear(bdev, argp); |
|---|
| 600 | 578 | default: |
|---|
| 601 | | - return __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
|---|
| 579 | + return -ENOIOCTLCMD; |
|---|
| 602 | 580 | } |
|---|
| 603 | 581 | } |
|---|
| 604 | | -EXPORT_SYMBOL_GPL(blkdev_ioctl); |
|---|
| 582 | + |
|---|
| 583 | +/* |
|---|
| 584 | + * Always keep this in sync with compat_blkdev_ioctl() |
|---|
| 585 | + * to handle all incompatible commands in both functions. |
|---|
| 586 | + * |
|---|
| 587 | + * New commands must be compatible and go into blkdev_common_ioctl |
|---|
| 588 | + */ |
|---|
| 589 | +int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, |
|---|
| 590 | + unsigned long arg) |
|---|
| 591 | +{ |
|---|
| 592 | + int ret; |
|---|
| 593 | + loff_t size; |
|---|
| 594 | + void __user *argp = (void __user *)arg; |
|---|
| 595 | + |
|---|
| 596 | + switch (cmd) { |
|---|
| 597 | + /* These need separate implementations for the data structure */ |
|---|
| 598 | + case HDIO_GETGEO: |
|---|
| 599 | + return blkdev_getgeo(bdev, argp); |
|---|
| 600 | + case BLKPG: |
|---|
| 601 | + return blkpg_ioctl(bdev, argp); |
|---|
| 602 | + |
|---|
| 603 | + /* Compat mode returns 32-bit data instead of 'long' */ |
|---|
| 604 | + case BLKRAGET: |
|---|
| 605 | + case BLKFRAGET: |
|---|
| 606 | + if (!argp) |
|---|
| 607 | + return -EINVAL; |
|---|
| 608 | + return put_long(argp, (bdev->bd_bdi->ra_pages*PAGE_SIZE) / 512); |
|---|
| 609 | + case BLKGETSIZE: |
|---|
| 610 | + size = i_size_read(bdev->bd_inode); |
|---|
| 611 | + if ((size >> 9) > ~0UL) |
|---|
| 612 | + return -EFBIG; |
|---|
| 613 | + return put_ulong(argp, size >> 9); |
|---|
| 614 | + |
|---|
| 615 | + /* The data is compatible, but the command number is different */ |
|---|
| 616 | + case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ |
|---|
| 617 | + return put_int(argp, block_size(bdev)); |
|---|
| 618 | + case BLKBSZSET: |
|---|
| 619 | + return blkdev_bszset(bdev, mode, argp); |
|---|
| 620 | + case BLKGETSIZE64: |
|---|
| 621 | + return put_u64(argp, i_size_read(bdev->bd_inode)); |
|---|
| 622 | + |
|---|
| 623 | + /* Incompatible alignment on i386 */ |
|---|
| 624 | + case BLKTRACESETUP: |
|---|
| 625 | + return blk_trace_ioctl(bdev, cmd, argp); |
|---|
| 626 | + default: |
|---|
| 627 | + break; |
|---|
| 628 | + } |
|---|
| 629 | + |
|---|
| 630 | + ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); |
|---|
| 631 | + if (ret == -ENOIOCTLCMD) |
|---|
| 632 | + return __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
|---|
| 633 | + |
|---|
| 634 | + return ret; |
|---|
| 635 | +} |
|---|
| 636 | +EXPORT_SYMBOL_GPL(blkdev_ioctl); /* for /dev/raw */ |
|---|
| 637 | + |
|---|
| 638 | +#ifdef CONFIG_COMPAT |
|---|
| 639 | + |
|---|
| 640 | +#define BLKBSZGET_32 _IOR(0x12, 112, int) |
|---|
| 641 | +#define BLKBSZSET_32 _IOW(0x12, 113, int) |
|---|
| 642 | +#define BLKGETSIZE64_32 _IOR(0x12, 114, int) |
|---|
| 643 | + |
|---|
| 644 | +/* Most of the generic ioctls are handled in the normal fallback path. |
|---|
| 645 | + This assumes the blkdev's low level compat_ioctl always returns |
|---|
| 646 | + ENOIOCTLCMD for unknown ioctls. */ |
|---|
| 647 | +long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) |
|---|
| 648 | +{ |
|---|
| 649 | + int ret; |
|---|
| 650 | + void __user *argp = compat_ptr(arg); |
|---|
| 651 | + struct inode *inode = file->f_mapping->host; |
|---|
| 652 | + struct block_device *bdev = inode->i_bdev; |
|---|
| 653 | + struct gendisk *disk = bdev->bd_disk; |
|---|
| 654 | + fmode_t mode = file->f_mode; |
|---|
| 655 | + loff_t size; |
|---|
| 656 | + |
|---|
| 657 | + /* |
|---|
| 658 | + * O_NDELAY can be altered using fcntl(.., F_SETFL, ..), so we have |
|---|
| 659 | + * to updated it before every ioctl. |
|---|
| 660 | + */ |
|---|
| 661 | + if (file->f_flags & O_NDELAY) |
|---|
| 662 | + mode |= FMODE_NDELAY; |
|---|
| 663 | + else |
|---|
| 664 | + mode &= ~FMODE_NDELAY; |
|---|
| 665 | + |
|---|
| 666 | + switch (cmd) { |
|---|
| 667 | + /* These need separate implementations for the data structure */ |
|---|
| 668 | + case HDIO_GETGEO: |
|---|
| 669 | + return compat_hdio_getgeo(bdev, argp); |
|---|
| 670 | + case BLKPG: |
|---|
| 671 | + return compat_blkpg_ioctl(bdev, argp); |
|---|
| 672 | + |
|---|
| 673 | + /* Compat mode returns 32-bit data instead of 'long' */ |
|---|
| 674 | + case BLKRAGET: |
|---|
| 675 | + case BLKFRAGET: |
|---|
| 676 | + if (!argp) |
|---|
| 677 | + return -EINVAL; |
|---|
| 678 | + return compat_put_long(argp, |
|---|
| 679 | + (bdev->bd_bdi->ra_pages * PAGE_SIZE) / 512); |
|---|
| 680 | + case BLKGETSIZE: |
|---|
| 681 | + size = i_size_read(bdev->bd_inode); |
|---|
| 682 | + if ((size >> 9) > ~(compat_ulong_t)0) |
|---|
| 683 | + return -EFBIG; |
|---|
| 684 | + return compat_put_ulong(argp, size >> 9); |
|---|
| 685 | + |
|---|
| 686 | + /* The data is compatible, but the command number is different */ |
|---|
| 687 | + case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */ |
|---|
| 688 | + return put_int(argp, bdev_logical_block_size(bdev)); |
|---|
| 689 | + case BLKBSZSET_32: |
|---|
| 690 | + return blkdev_bszset(bdev, mode, argp); |
|---|
| 691 | + case BLKGETSIZE64_32: |
|---|
| 692 | + return put_u64(argp, i_size_read(bdev->bd_inode)); |
|---|
| 693 | + |
|---|
| 694 | + /* Incompatible alignment on i386 */ |
|---|
| 695 | + case BLKTRACESETUP32: |
|---|
| 696 | + return blk_trace_ioctl(bdev, cmd, argp); |
|---|
| 697 | + default: |
|---|
| 698 | + break; |
|---|
| 699 | + } |
|---|
| 700 | + |
|---|
| 701 | + ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); |
|---|
| 702 | + if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl) |
|---|
| 703 | + ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg); |
|---|
| 704 | + |
|---|
| 705 | + return ret; |
|---|
| 706 | +} |
|---|
| 707 | +#endif |
|---|