1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/capability.h> |
3 | #include <linux/compat.h> |
4 | #include <linux/blkdev.h> |
5 | #include <linux/export.h> |
6 | #include <linux/gfp.h> |
7 | #include <linux/blkpg.h> |
8 | #include <linux/hdreg.h> |
9 | #include <linux/backing-dev.h> |
10 | #include <linux/fs.h> |
11 | #include <linux/blktrace_api.h> |
12 | #include <linux/pr.h> |
13 | #include <linux/uaccess.h> |
14 | #include "blk.h" |
15 | |
16 | static int blkpg_do_ioctl(struct block_device *bdev, |
17 | struct blkpg_partition __user *upart, int op) |
18 | { |
19 | struct gendisk *disk = bdev->bd_disk; |
20 | struct blkpg_partition p; |
21 | sector_t start, length, capacity, end; |
22 | |
23 | if (!capable(CAP_SYS_ADMIN)) |
24 | return -EACCES; |
25 | if (copy_from_user(to: &p, from: upart, n: sizeof(struct blkpg_partition))) |
26 | return -EFAULT; |
27 | if (bdev_is_partition(bdev)) |
28 | return -EINVAL; |
29 | |
30 | if (p.pno <= 0) |
31 | return -EINVAL; |
32 | |
33 | if (op == BLKPG_DEL_PARTITION) |
34 | return bdev_del_partition(disk, partno: p.pno); |
35 | |
36 | if (p.start < 0 || p.length <= 0 || p.start + p.length < 0) |
37 | return -EINVAL; |
38 | /* Check that the partition is aligned to the block size */ |
39 | if (!IS_ALIGNED(p.start | p.length, bdev_logical_block_size(bdev))) |
40 | return -EINVAL; |
41 | |
42 | start = p.start >> SECTOR_SHIFT; |
43 | length = p.length >> SECTOR_SHIFT; |
44 | capacity = get_capacity(disk); |
45 | |
46 | if (check_add_overflow(start, length, &end)) |
47 | return -EINVAL; |
48 | |
49 | if (start >= capacity || end > capacity) |
50 | return -EINVAL; |
51 | |
52 | switch (op) { |
53 | case BLKPG_ADD_PARTITION: |
54 | return bdev_add_partition(disk, partno: p.pno, start, length); |
55 | case BLKPG_RESIZE_PARTITION: |
56 | return bdev_resize_partition(disk, partno: p.pno, start, length); |
57 | default: |
58 | return -EINVAL; |
59 | } |
60 | } |
61 | |
62 | static int blkpg_ioctl(struct block_device *bdev, |
63 | struct blkpg_ioctl_arg __user *arg) |
64 | { |
65 | struct blkpg_partition __user *udata; |
66 | int op; |
67 | |
68 | if (get_user(op, &arg->op) || get_user(udata, &arg->data)) |
69 | return -EFAULT; |
70 | |
71 | return blkpg_do_ioctl(bdev, upart: udata, op); |
72 | } |
73 | |
74 | #ifdef CONFIG_COMPAT |
75 | struct compat_blkpg_ioctl_arg { |
76 | compat_int_t op; |
77 | compat_int_t flags; |
78 | compat_int_t datalen; |
79 | compat_caddr_t data; |
80 | }; |
81 | |
82 | static int compat_blkpg_ioctl(struct block_device *bdev, |
83 | struct compat_blkpg_ioctl_arg __user *arg) |
84 | { |
85 | compat_caddr_t udata; |
86 | int op; |
87 | |
88 | if (get_user(op, &arg->op) || get_user(udata, &arg->data)) |
89 | return -EFAULT; |
90 | |
91 | return blkpg_do_ioctl(bdev, upart: compat_ptr(uptr: udata), op); |
92 | } |
93 | #endif |
94 | |
95 | static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, |
96 | unsigned long arg) |
97 | { |
98 | uint64_t range[2]; |
99 | uint64_t start, len, end; |
100 | struct inode *inode = bdev->bd_inode; |
101 | int err; |
102 | |
103 | if (!(mode & BLK_OPEN_WRITE)) |
104 | return -EBADF; |
105 | |
106 | if (!bdev_max_discard_sectors(bdev)) |
107 | return -EOPNOTSUPP; |
108 | |
109 | if (copy_from_user(to: range, from: (void __user *)arg, n: sizeof(range))) |
110 | return -EFAULT; |
111 | |
112 | start = range[0]; |
113 | len = range[1]; |
114 | |
115 | if (start & 511) |
116 | return -EINVAL; |
117 | if (len & 511) |
118 | return -EINVAL; |
119 | |
120 | if (check_add_overflow(start, len, &end) || |
121 | end > bdev_nr_bytes(bdev)) |
122 | return -EINVAL; |
123 | |
124 | filemap_invalidate_lock(mapping: inode->i_mapping); |
125 | err = truncate_bdev_range(bdev, mode, lstart: start, lend: start + len - 1); |
126 | if (err) |
127 | goto fail; |
128 | err = blkdev_issue_discard(bdev, sector: start >> 9, nr_sects: len >> 9, GFP_KERNEL); |
129 | fail: |
130 | filemap_invalidate_unlock(mapping: inode->i_mapping); |
131 | return err; |
132 | } |
133 | |
134 | static int blk_ioctl_secure_erase(struct block_device *bdev, blk_mode_t mode, |
135 | void __user *argp) |
136 | { |
137 | uint64_t start, len; |
138 | uint64_t range[2]; |
139 | int err; |
140 | |
141 | if (!(mode & BLK_OPEN_WRITE)) |
142 | return -EBADF; |
143 | if (!bdev_max_secure_erase_sectors(bdev)) |
144 | return -EOPNOTSUPP; |
145 | if (copy_from_user(to: range, from: argp, n: sizeof(range))) |
146 | return -EFAULT; |
147 | |
148 | start = range[0]; |
149 | len = range[1]; |
150 | if ((start & 511) || (len & 511)) |
151 | return -EINVAL; |
152 | if (start + len > bdev_nr_bytes(bdev)) |
153 | return -EINVAL; |
154 | |
155 | filemap_invalidate_lock(mapping: bdev->bd_inode->i_mapping); |
156 | err = truncate_bdev_range(bdev, mode, lstart: start, lend: start + len - 1); |
157 | if (!err) |
158 | err = blkdev_issue_secure_erase(bdev, sector: start >> 9, nr_sects: len >> 9, |
159 | GFP_KERNEL); |
160 | filemap_invalidate_unlock(mapping: bdev->bd_inode->i_mapping); |
161 | return err; |
162 | } |
163 | |
164 | |
165 | static int blk_ioctl_zeroout(struct block_device *bdev, blk_mode_t mode, |
166 | unsigned long arg) |
167 | { |
168 | uint64_t range[2]; |
169 | uint64_t start, end, len; |
170 | struct inode *inode = bdev->bd_inode; |
171 | int err; |
172 | |
173 | if (!(mode & BLK_OPEN_WRITE)) |
174 | return -EBADF; |
175 | |
176 | if (copy_from_user(to: range, from: (void __user *)arg, n: sizeof(range))) |
177 | return -EFAULT; |
178 | |
179 | start = range[0]; |
180 | len = range[1]; |
181 | end = start + len - 1; |
182 | |
183 | if (start & 511) |
184 | return -EINVAL; |
185 | if (len & 511) |
186 | return -EINVAL; |
187 | if (end >= (uint64_t)bdev_nr_bytes(bdev)) |
188 | return -EINVAL; |
189 | if (end < start) |
190 | return -EINVAL; |
191 | |
192 | /* Invalidate the page cache, including dirty pages */ |
193 | filemap_invalidate_lock(mapping: inode->i_mapping); |
194 | err = truncate_bdev_range(bdev, mode, lstart: start, lend: end); |
195 | if (err) |
196 | goto fail; |
197 | |
198 | err = blkdev_issue_zeroout(bdev, sector: start >> 9, nr_sects: len >> 9, GFP_KERNEL, |
199 | BLKDEV_ZERO_NOUNMAP); |
200 | |
201 | fail: |
202 | filemap_invalidate_unlock(mapping: inode->i_mapping); |
203 | return err; |
204 | } |
205 | |
206 | static int put_ushort(unsigned short __user *argp, unsigned short val) |
207 | { |
208 | return put_user(val, argp); |
209 | } |
210 | |
211 | static int put_int(int __user *argp, int val) |
212 | { |
213 | return put_user(val, argp); |
214 | } |
215 | |
216 | static int put_uint(unsigned int __user *argp, unsigned int val) |
217 | { |
218 | return put_user(val, argp); |
219 | } |
220 | |
221 | static int put_long(long __user *argp, long val) |
222 | { |
223 | return put_user(val, argp); |
224 | } |
225 | |
226 | static int put_ulong(unsigned long __user *argp, unsigned long val) |
227 | { |
228 | return put_user(val, argp); |
229 | } |
230 | |
231 | static int put_u64(u64 __user *argp, u64 val) |
232 | { |
233 | return put_user(val, argp); |
234 | } |
235 | |
236 | #ifdef CONFIG_COMPAT |
237 | static int compat_put_long(compat_long_t __user *argp, long val) |
238 | { |
239 | return put_user(val, argp); |
240 | } |
241 | |
242 | static int compat_put_ulong(compat_ulong_t __user *argp, compat_ulong_t val) |
243 | { |
244 | return put_user(val, argp); |
245 | } |
246 | #endif |
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, blk_mode_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(uptr: arg)); |
262 | |
263 | return -ENOIOCTLCMD; |
264 | } |
265 | EXPORT_SYMBOL(blkdev_compat_ptr_ioctl); |
266 | #endif |
267 | |
268 | static bool blkdev_pr_allowed(struct block_device *bdev, blk_mode_t mode) |
269 | { |
270 | /* no sense to make reservations for partitions */ |
271 | if (bdev_is_partition(bdev)) |
272 | return false; |
273 | |
274 | if (capable(CAP_SYS_ADMIN)) |
275 | return true; |
276 | /* |
277 | * Only allow unprivileged reservations if the file descriptor is open |
278 | * for writing. |
279 | */ |
280 | return mode & BLK_OPEN_WRITE; |
281 | } |
282 | |
283 | static int blkdev_pr_register(struct block_device *bdev, blk_mode_t mode, |
284 | struct pr_registration __user *arg) |
285 | { |
286 | const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; |
287 | struct pr_registration reg; |
288 | |
289 | if (!blkdev_pr_allowed(bdev, mode)) |
290 | return -EPERM; |
291 | if (!ops || !ops->pr_register) |
292 | return -EOPNOTSUPP; |
293 | if (copy_from_user(to: ®, from: arg, n: sizeof(reg))) |
294 | return -EFAULT; |
295 | |
296 | if (reg.flags & ~PR_FL_IGNORE_KEY) |
297 | return -EOPNOTSUPP; |
298 | return ops->pr_register(bdev, reg.old_key, reg.new_key, reg.flags); |
299 | } |
300 | |
301 | static int blkdev_pr_reserve(struct block_device *bdev, blk_mode_t mode, |
302 | struct pr_reservation __user *arg) |
303 | { |
304 | const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; |
305 | struct pr_reservation rsv; |
306 | |
307 | if (!blkdev_pr_allowed(bdev, mode)) |
308 | return -EPERM; |
309 | if (!ops || !ops->pr_reserve) |
310 | return -EOPNOTSUPP; |
311 | if (copy_from_user(to: &rsv, from: arg, n: sizeof(rsv))) |
312 | return -EFAULT; |
313 | |
314 | if (rsv.flags & ~PR_FL_IGNORE_KEY) |
315 | return -EOPNOTSUPP; |
316 | return ops->pr_reserve(bdev, rsv.key, rsv.type, rsv.flags); |
317 | } |
318 | |
319 | static int blkdev_pr_release(struct block_device *bdev, blk_mode_t mode, |
320 | struct pr_reservation __user *arg) |
321 | { |
322 | const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; |
323 | struct pr_reservation rsv; |
324 | |
325 | if (!blkdev_pr_allowed(bdev, mode)) |
326 | return -EPERM; |
327 | if (!ops || !ops->pr_release) |
328 | return -EOPNOTSUPP; |
329 | if (copy_from_user(to: &rsv, from: arg, n: sizeof(rsv))) |
330 | return -EFAULT; |
331 | |
332 | if (rsv.flags) |
333 | return -EOPNOTSUPP; |
334 | return ops->pr_release(bdev, rsv.key, rsv.type); |
335 | } |
336 | |
337 | static int blkdev_pr_preempt(struct block_device *bdev, blk_mode_t mode, |
338 | struct pr_preempt __user *arg, bool abort) |
339 | { |
340 | const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; |
341 | struct pr_preempt p; |
342 | |
343 | if (!blkdev_pr_allowed(bdev, mode)) |
344 | return -EPERM; |
345 | if (!ops || !ops->pr_preempt) |
346 | return -EOPNOTSUPP; |
347 | if (copy_from_user(to: &p, from: arg, n: sizeof(p))) |
348 | return -EFAULT; |
349 | |
350 | if (p.flags) |
351 | return -EOPNOTSUPP; |
352 | return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort); |
353 | } |
354 | |
355 | static int blkdev_pr_clear(struct block_device *bdev, blk_mode_t mode, |
356 | struct pr_clear __user *arg) |
357 | { |
358 | const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops; |
359 | struct pr_clear c; |
360 | |
361 | if (!blkdev_pr_allowed(bdev, mode)) |
362 | return -EPERM; |
363 | if (!ops || !ops->pr_clear) |
364 | return -EOPNOTSUPP; |
365 | if (copy_from_user(to: &c, from: arg, n: sizeof(c))) |
366 | return -EFAULT; |
367 | |
368 | if (c.flags) |
369 | return -EOPNOTSUPP; |
370 | return ops->pr_clear(bdev, c.key); |
371 | } |
372 | |
373 | static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd, |
374 | unsigned long arg) |
375 | { |
376 | if (!capable(CAP_SYS_ADMIN)) |
377 | return -EACCES; |
378 | |
379 | mutex_lock(&bdev->bd_holder_lock); |
380 | if (bdev->bd_holder_ops && bdev->bd_holder_ops->sync) |
381 | bdev->bd_holder_ops->sync(bdev); |
382 | else { |
383 | mutex_unlock(lock: &bdev->bd_holder_lock); |
384 | sync_blockdev(bdev); |
385 | } |
386 | |
387 | invalidate_bdev(bdev); |
388 | return 0; |
389 | } |
390 | |
391 | static int blkdev_roset(struct block_device *bdev, unsigned cmd, |
392 | unsigned long arg) |
393 | { |
394 | int ret, n; |
395 | |
396 | if (!capable(CAP_SYS_ADMIN)) |
397 | return -EACCES; |
398 | |
399 | if (get_user(n, (int __user *)arg)) |
400 | return -EFAULT; |
401 | if (bdev->bd_disk->fops->set_read_only) { |
402 | ret = bdev->bd_disk->fops->set_read_only(bdev, n); |
403 | if (ret) |
404 | return ret; |
405 | } |
406 | bdev->bd_read_only = n; |
407 | return 0; |
408 | } |
409 | |
410 | static int blkdev_getgeo(struct block_device *bdev, |
411 | struct hd_geometry __user *argp) |
412 | { |
413 | struct gendisk *disk = bdev->bd_disk; |
414 | struct hd_geometry geo; |
415 | int ret; |
416 | |
417 | if (!argp) |
418 | return -EINVAL; |
419 | if (!disk->fops->getgeo) |
420 | return -ENOTTY; |
421 | |
422 | /* |
423 | * We need to set the startsect first, the driver may |
424 | * want to override it. |
425 | */ |
426 | memset(&geo, 0, sizeof(geo)); |
427 | geo.start = get_start_sect(bdev); |
428 | ret = disk->fops->getgeo(bdev, &geo); |
429 | if (ret) |
430 | return ret; |
431 | if (copy_to_user(to: argp, from: &geo, n: sizeof(geo))) |
432 | return -EFAULT; |
433 | return 0; |
434 | } |
435 | |
436 | #ifdef CONFIG_COMPAT |
437 | struct compat_hd_geometry { |
438 | unsigned char heads; |
439 | unsigned char sectors; |
440 | unsigned short cylinders; |
441 | u32 start; |
442 | }; |
443 | |
444 | static int compat_hdio_getgeo(struct block_device *bdev, |
445 | struct compat_hd_geometry __user *ugeo) |
446 | { |
447 | struct gendisk *disk = bdev->bd_disk; |
448 | struct hd_geometry geo; |
449 | int ret; |
450 | |
451 | if (!ugeo) |
452 | return -EINVAL; |
453 | if (!disk->fops->getgeo) |
454 | return -ENOTTY; |
455 | |
456 | memset(&geo, 0, sizeof(geo)); |
457 | /* |
458 | * We need to set the startsect first, the driver may |
459 | * want to override it. |
460 | */ |
461 | geo.start = get_start_sect(bdev); |
462 | ret = disk->fops->getgeo(bdev, &geo); |
463 | if (ret) |
464 | return ret; |
465 | |
466 | ret = copy_to_user(to: ugeo, from: &geo, n: 4); |
467 | ret |= put_user(geo.start, &ugeo->start); |
468 | if (ret) |
469 | ret = -EFAULT; |
470 | |
471 | return ret; |
472 | } |
473 | #endif |
474 | |
475 | /* set the logical block size */ |
476 | static int blkdev_bszset(struct block_device *bdev, blk_mode_t mode, |
477 | int __user *argp) |
478 | { |
479 | int ret, n; |
480 | struct file *file; |
481 | |
482 | if (!capable(CAP_SYS_ADMIN)) |
483 | return -EACCES; |
484 | if (!argp) |
485 | return -EINVAL; |
486 | if (get_user(n, argp)) |
487 | return -EFAULT; |
488 | |
489 | if (mode & BLK_OPEN_EXCL) |
490 | return set_blocksize(bdev, size: n); |
491 | |
492 | file = bdev_file_open_by_dev(dev: bdev->bd_dev, mode, holder: &bdev, NULL); |
493 | if (IS_ERR(ptr: file)) |
494 | return -EBUSY; |
495 | ret = set_blocksize(bdev, size: n); |
496 | fput(file); |
497 | return ret; |
498 | } |
499 | |
500 | /* |
501 | * Common commands that are handled the same way on native and compat |
502 | * user space. Note the separate arg/argp parameters that are needed |
503 | * to deal with the compat_ptr() conversion. |
504 | */ |
505 | static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode, |
506 | unsigned int cmd, unsigned long arg, |
507 | void __user *argp) |
508 | { |
509 | unsigned int max_sectors; |
510 | |
511 | switch (cmd) { |
512 | case BLKFLSBUF: |
513 | return blkdev_flushbuf(bdev, cmd, arg); |
514 | case BLKROSET: |
515 | return blkdev_roset(bdev, cmd, arg); |
516 | case BLKDISCARD: |
517 | return blk_ioctl_discard(bdev, mode, arg); |
518 | case BLKSECDISCARD: |
519 | return blk_ioctl_secure_erase(bdev, mode, argp); |
520 | case BLKZEROOUT: |
521 | return blk_ioctl_zeroout(bdev, mode, arg); |
522 | case BLKGETDISKSEQ: |
523 | return put_u64(argp, val: bdev->bd_disk->diskseq); |
524 | case BLKREPORTZONE: |
525 | return blkdev_report_zones_ioctl(bdev, cmd, arg); |
526 | case BLKRESETZONE: |
527 | case BLKOPENZONE: |
528 | case BLKCLOSEZONE: |
529 | case BLKFINISHZONE: |
530 | return blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg); |
531 | case BLKGETZONESZ: |
532 | return put_uint(argp, val: bdev_zone_sectors(bdev)); |
533 | case BLKGETNRZONES: |
534 | return put_uint(argp, val: bdev_nr_zones(bdev)); |
535 | case BLKROGET: |
536 | return put_int(argp, val: bdev_read_only(bdev) != 0); |
537 | case BLKSSZGET: /* get block device logical block size */ |
538 | return put_int(argp, val: bdev_logical_block_size(bdev)); |
539 | case BLKPBSZGET: /* get block device physical block size */ |
540 | return put_uint(argp, val: bdev_physical_block_size(bdev)); |
541 | case BLKIOMIN: |
542 | return put_uint(argp, val: bdev_io_min(bdev)); |
543 | case BLKIOOPT: |
544 | return put_uint(argp, val: bdev_io_opt(bdev)); |
545 | case BLKALIGNOFF: |
546 | return put_int(argp, val: bdev_alignment_offset(bdev)); |
547 | case BLKDISCARDZEROES: |
548 | return put_uint(argp, val: 0); |
549 | case BLKSECTGET: |
550 | max_sectors = min_t(unsigned int, USHRT_MAX, |
551 | queue_max_sectors(bdev_get_queue(bdev))); |
552 | return put_ushort(argp, val: max_sectors); |
553 | case BLKROTATIONAL: |
554 | return put_ushort(argp, val: !bdev_nonrot(bdev)); |
555 | case BLKRASET: |
556 | case BLKFRASET: |
557 | if(!capable(CAP_SYS_ADMIN)) |
558 | return -EACCES; |
559 | bdev->bd_disk->bdi->ra_pages = (arg * 512) / PAGE_SIZE; |
560 | return 0; |
561 | case BLKRRPART: |
562 | if (!capable(CAP_SYS_ADMIN)) |
563 | return -EACCES; |
564 | if (bdev_is_partition(bdev)) |
565 | return -EINVAL; |
566 | return disk_scan_partitions(disk: bdev->bd_disk, |
567 | mode: mode | BLK_OPEN_STRICT_SCAN); |
568 | case BLKTRACESTART: |
569 | case BLKTRACESTOP: |
570 | case BLKTRACETEARDOWN: |
571 | return blk_trace_ioctl(bdev, cmd, argp); |
572 | case IOC_PR_REGISTER: |
573 | return blkdev_pr_register(bdev, mode, arg: argp); |
574 | case IOC_PR_RESERVE: |
575 | return blkdev_pr_reserve(bdev, mode, arg: argp); |
576 | case IOC_PR_RELEASE: |
577 | return blkdev_pr_release(bdev, mode, arg: argp); |
578 | case IOC_PR_PREEMPT: |
579 | return blkdev_pr_preempt(bdev, mode, arg: argp, abort: false); |
580 | case IOC_PR_PREEMPT_ABORT: |
581 | return blkdev_pr_preempt(bdev, mode, arg: argp, abort: true); |
582 | case IOC_PR_CLEAR: |
583 | return blkdev_pr_clear(bdev, mode, arg: argp); |
584 | default: |
585 | return -ENOIOCTLCMD; |
586 | } |
587 | } |
588 | |
589 | /* |
590 | * Always keep this in sync with compat_blkdev_ioctl() |
591 | * to handle all incompatible commands in both functions. |
592 | * |
593 | * New commands must be compatible and go into blkdev_common_ioctl |
594 | */ |
595 | long blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) |
596 | { |
597 | struct block_device *bdev = I_BDEV(inode: file->f_mapping->host); |
598 | void __user *argp = (void __user *)arg; |
599 | blk_mode_t mode = file_to_blk_mode(file); |
600 | int ret; |
601 | |
602 | switch (cmd) { |
603 | /* These need separate implementations for the data structure */ |
604 | case HDIO_GETGEO: |
605 | return blkdev_getgeo(bdev, argp); |
606 | case BLKPG: |
607 | return blkpg_ioctl(bdev, arg: argp); |
608 | |
609 | /* Compat mode returns 32-bit data instead of 'long' */ |
610 | case BLKRAGET: |
611 | case BLKFRAGET: |
612 | if (!argp) |
613 | return -EINVAL; |
614 | return put_long(argp, |
615 | val: (bdev->bd_disk->bdi->ra_pages * PAGE_SIZE) / 512); |
616 | case BLKGETSIZE: |
617 | if (bdev_nr_sectors(bdev) > ~0UL) |
618 | return -EFBIG; |
619 | return put_ulong(argp, val: bdev_nr_sectors(bdev)); |
620 | |
621 | /* The data is compatible, but the command number is different */ |
622 | case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ |
623 | return put_int(argp, val: block_size(bdev)); |
624 | case BLKBSZSET: |
625 | return blkdev_bszset(bdev, mode, argp); |
626 | case BLKGETSIZE64: |
627 | return put_u64(argp, val: bdev_nr_bytes(bdev)); |
628 | |
629 | /* Incompatible alignment on i386 */ |
630 | case BLKTRACESETUP: |
631 | return blk_trace_ioctl(bdev, cmd, argp); |
632 | default: |
633 | break; |
634 | } |
635 | |
636 | ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); |
637 | if (ret != -ENOIOCTLCMD) |
638 | return ret; |
639 | |
640 | if (!bdev->bd_disk->fops->ioctl) |
641 | return -ENOTTY; |
642 | return bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg); |
643 | } |
644 | |
645 | #ifdef CONFIG_COMPAT |
646 | |
647 | #define BLKBSZGET_32 _IOR(0x12, 112, int) |
648 | #define BLKBSZSET_32 _IOW(0x12, 113, int) |
649 | #define BLKGETSIZE64_32 _IOR(0x12, 114, int) |
650 | |
651 | /* Most of the generic ioctls are handled in the normal fallback path. |
652 | This assumes the blkdev's low level compat_ioctl always returns |
653 | ENOIOCTLCMD for unknown ioctls. */ |
654 | long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) |
655 | { |
656 | int ret; |
657 | void __user *argp = compat_ptr(uptr: arg); |
658 | struct block_device *bdev = I_BDEV(inode: file->f_mapping->host); |
659 | struct gendisk *disk = bdev->bd_disk; |
660 | blk_mode_t mode = file_to_blk_mode(file); |
661 | |
662 | switch (cmd) { |
663 | /* These need separate implementations for the data structure */ |
664 | case HDIO_GETGEO: |
665 | return compat_hdio_getgeo(bdev, ugeo: argp); |
666 | case BLKPG: |
667 | return compat_blkpg_ioctl(bdev, arg: argp); |
668 | |
669 | /* Compat mode returns 32-bit data instead of 'long' */ |
670 | case BLKRAGET: |
671 | case BLKFRAGET: |
672 | if (!argp) |
673 | return -EINVAL; |
674 | return compat_put_long(argp, |
675 | val: (bdev->bd_disk->bdi->ra_pages * PAGE_SIZE) / 512); |
676 | case BLKGETSIZE: |
677 | if (bdev_nr_sectors(bdev) > ~(compat_ulong_t)0) |
678 | return -EFBIG; |
679 | return compat_put_ulong(argp, val: bdev_nr_sectors(bdev)); |
680 | |
681 | /* The data is compatible, but the command number is different */ |
682 | case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */ |
683 | return put_int(argp, val: bdev_logical_block_size(bdev)); |
684 | case BLKBSZSET_32: |
685 | return blkdev_bszset(bdev, mode, argp); |
686 | case BLKGETSIZE64_32: |
687 | return put_u64(argp, val: bdev_nr_bytes(bdev)); |
688 | |
689 | /* Incompatible alignment on i386 */ |
690 | case BLKTRACESETUP32: |
691 | return blk_trace_ioctl(bdev, cmd, argp); |
692 | default: |
693 | break; |
694 | } |
695 | |
696 | ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp); |
697 | if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl) |
698 | ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg); |
699 | |
700 | return ret; |
701 | } |
702 | #endif |
703 | |