Blame view

block/ioctl.c 16.8 KB
3dcf60bcb   Christoph Hellwig   block: add SPDX t...
1
  // SPDX-License-Identifier: GPL-2.0
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
2
  #include <linux/capability.h>
ee6a129df   Arnd Bergmann   compat_ioctl: blo...
3
  #include <linux/compat.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
  #include <linux/blkdev.h>
d5decd3b9   Paul Gortmaker   block: add export...
5
  #include <linux/export.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
6
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
  #include <linux/blkpg.h>
a885c8c43   Christoph Hellwig   [PATCH] Add block...
8
  #include <linux/hdreg.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/backing-dev.h>
ff01bb483   Al Viro   fs: move code out...
10
  #include <linux/fs.h>
2056a782f   Jens Axboe   [PATCH] Block que...
11
  #include <linux/blktrace_api.h>
bbd3e0643   Christoph Hellwig   block: add an API...
12
  #include <linux/pr.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
13
  #include <linux/uaccess.h>
581e26004   Christoph Hellwig   block: move block...
14
  #include "blk.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15

5fb889f58   Arnd Bergmann   compat_ioctl: blo...
16
17
  static int blkpg_do_ioctl(struct block_device *bdev,
  			  struct blkpg_partition __user *upart, int op)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  	struct blkpg_partition p;
  	long long start, length;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
5fb889f58   Arnd Bergmann   compat_ioctl: blo...
24
  	if (copy_from_user(&p, upart, sizeof(struct blkpg_partition)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  		return -EFAULT;
fa01b1e97   Christoph Hellwig   block: add a bdev...
26
  	if (bdev_is_partition(bdev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  		return -EINVAL;
88e341261   Tejun Heo   block: update add...
28

fa9156ae5   Christoph Hellwig   block: refactor b...
29
30
  	if (p.pno <= 0)
  		return -EINVAL;
e71bf0d0e   Tejun Heo   block: fix disk->...
31

fa9156ae5   Christoph Hellwig   block: refactor b...
32
33
  	if (op == BLKPG_DEL_PARTITION)
  		return bdev_del_partition(bdev, p.pno);
e71bf0d0e   Tejun Heo   block: fix disk->...
34

fa9156ae5   Christoph Hellwig   block: refactor b...
35
36
  	start = p.start >> SECTOR_SHIFT;
  	length = p.length >> SECTOR_SHIFT;
e71bf0d0e   Tejun Heo   block: fix disk->...
37

fa9156ae5   Christoph Hellwig   block: refactor b...
38
39
40
  	/* check for fit in a hd_struct */
  	if (sizeof(sector_t) < sizeof(long long)) {
  		long pstart = start, plength = length;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41

fa9156ae5   Christoph Hellwig   block: refactor b...
42
43
44
45
  		if (pstart != start || plength != length || pstart < 0 ||
  		    plength < 0 || p.pno > 65535)
  			return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

fa9156ae5   Christoph Hellwig   block: refactor b...
47
48
49
50
  	switch (op) {
  	case BLKPG_ADD_PARTITION:
  		/* check if partition is aligned to blocksize */
  		if (p.start & (bdev_logical_block_size(bdev) - 1))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  			return -EINVAL;
fa9156ae5   Christoph Hellwig   block: refactor b...
52
53
54
55
56
  		return bdev_add_partition(bdev, p.pno, start, length);
  	case BLKPG_RESIZE_PARTITION:
  		return bdev_resize_partition(bdev, p.pno, start, length);
  	default:
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
  	}
  }
5fb889f58   Arnd Bergmann   compat_ioctl: blo...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  static int blkpg_ioctl(struct block_device *bdev,
  		       struct blkpg_ioctl_arg __user *arg)
  {
  	struct blkpg_partition __user *udata;
  	int op;
  
  	if (get_user(op, &arg->op) || get_user(udata, &arg->data))
  		return -EFAULT;
  
  	return blkpg_do_ioctl(bdev, udata, op);
  }
  
  #ifdef CONFIG_COMPAT
  struct compat_blkpg_ioctl_arg {
  	compat_int_t op;
  	compat_int_t flags;
  	compat_int_t datalen;
  	compat_caddr_t data;
  };
  
  static int compat_blkpg_ioctl(struct block_device *bdev,
  			      struct compat_blkpg_ioctl_arg __user *arg)
  {
  	compat_caddr_t udata;
  	int op;
  
  	if (get_user(op, &arg->op) || get_user(udata, &arg->data))
  		return -EFAULT;
  
  	return blkpg_do_ioctl(bdev, compat_ptr(udata), op);
  }
  #endif
f0b870df8   Christoph Hellwig   block: remove (__...
91
  static int blkdev_reread_part(struct block_device *bdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  {
f0b870df8   Christoph Hellwig   block: remove (__...
93
  	int ret;
fa01b1e97   Christoph Hellwig   block: add a bdev...
94
  	if (!disk_part_scan_enabled(bdev->bd_disk) || bdev_is_partition(bdev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
  		return -EINVAL;
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
be3241779   Jarod Wilson   block: export blk...
98

b04a5636a   Ming Lei   block: replace tr...
99
  	mutex_lock(&bdev->bd_mutex);
f0b870df8   Christoph Hellwig   block: remove (__...
100
  	ret = bdev_disk_changed(bdev, false);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
101
  	mutex_unlock(&bdev->bd_mutex);
be3241779   Jarod Wilson   block: export blk...
102

f0b870df8   Christoph Hellwig   block: remove (__...
103
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  }
d8e4bb810   Christoph Hellwig   block: cleanup bl...
105
106
  static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode,
  		unsigned long arg, unsigned long flags)
d30a2605b   David Woodhouse   Add BLKDISCARD io...
107
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
108
109
  	uint64_t range[2];
  	uint64_t start, len;
351499a17   Dmitry Monakhov   block: Invalidate...
110
  	struct request_queue *q = bdev_get_queue(bdev);
384d87ef2   Jan Kara   block: Do not dis...
111
  	int err;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
112
113
114
  
  	if (!(mode & FMODE_WRITE))
  		return -EBADF;
351499a17   Dmitry Monakhov   block: Invalidate...
115
116
  	if (!blk_queue_discard(q))
  		return -EOPNOTSUPP;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
117
118
119
120
121
  	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
  		return -EFAULT;
  
  	start = range[0];
  	len = range[1];
8d57a98cc   Adrian Hunter   block: add secure...
122

d30a2605b   David Woodhouse   Add BLKDISCARD io...
123
124
125
126
  	if (start & 511)
  		return -EINVAL;
  	if (len & 511)
  		return -EINVAL;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
127

351499a17   Dmitry Monakhov   block: Invalidate...
128
  	if (start + len > i_size_read(bdev->bd_inode))
d30a2605b   David Woodhouse   Add BLKDISCARD io...
129
  		return -EINVAL;
384d87ef2   Jan Kara   block: Do not dis...
130
131
132
133
  
  	err = truncate_bdev_range(bdev, mode, start, start + len - 1);
  	if (err)
  		return err;
351499a17   Dmitry Monakhov   block: Invalidate...
134
135
  	return blkdev_issue_discard(bdev, start >> 9, len >> 9,
  				    GFP_KERNEL, flags);
d30a2605b   David Woodhouse   Add BLKDISCARD io...
136
  }
d8e4bb810   Christoph Hellwig   block: cleanup bl...
137
138
  static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
  		unsigned long arg)
66ba32dc1   Martin K. Petersen   block: ioctl to z...
139
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
140
  	uint64_t range[2];
22dd6d356   Darrick J. Wong   block: invalidate...
141
  	uint64_t start, end, len;
384d87ef2   Jan Kara   block: Do not dis...
142
  	int err;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
143
144
145
146
147
148
149
150
151
  
  	if (!(mode & FMODE_WRITE))
  		return -EBADF;
  
  	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
  		return -EFAULT;
  
  	start = range[0];
  	len = range[1];
22dd6d356   Darrick J. Wong   block: invalidate...
152
  	end = start + len - 1;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
153

66ba32dc1   Martin K. Petersen   block: ioctl to z...
154
155
156
157
  	if (start & 511)
  		return -EINVAL;
  	if (len & 511)
  		return -EINVAL;
22dd6d356   Darrick J. Wong   block: invalidate...
158
159
160
  	if (end >= (uint64_t)i_size_read(bdev->bd_inode))
  		return -EINVAL;
  	if (end < start)
66ba32dc1   Martin K. Petersen   block: ioctl to z...
161
  		return -EINVAL;
22dd6d356   Darrick J. Wong   block: invalidate...
162
  	/* Invalidate the page cache, including dirty pages */
384d87ef2   Jan Kara   block: Do not dis...
163
164
165
  	err = truncate_bdev_range(bdev, mode, start, end);
  	if (err)
  		return err;
22dd6d356   Darrick J. Wong   block: invalidate...
166
167
  
  	return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL,
ee472d835   Christoph Hellwig   block: add a flag...
168
  			BLKDEV_ZERO_NOUNMAP);
66ba32dc1   Martin K. Petersen   block: ioctl to z...
169
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
170
  static int put_ushort(unsigned short __user *argp, unsigned short val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
172
  	return put_user(val, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
174
  static int put_int(int __user *argp, int val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
176
  	return put_user(val, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
178
  static int put_uint(unsigned int __user *argp, unsigned int val)
ac481c20e   Martin K. Petersen   block: Topology i...
179
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
180
  	return put_user(val, argp);
ac481c20e   Martin K. Petersen   block: Topology i...
181
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
182
  static int put_long(long __user *argp, long val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
184
  	return put_user(val, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
186
  static int put_ulong(unsigned long __user *argp, unsigned long val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
188
  	return put_user(val, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
190
  static int put_u64(u64 __user *argp, u64 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
192
  	return put_user(val, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  }
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
194
  #ifdef CONFIG_COMPAT
c8210a576   Bart Van Assche   block: Fix type o...
195
  static int compat_put_long(compat_long_t __user *argp, long val)
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
196
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
197
  	return put_user(val, argp);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
198
  }
c8210a576   Bart Van Assche   block: Fix type o...
199
  static int compat_put_ulong(compat_ulong_t __user *argp, compat_ulong_t val)
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
200
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
201
  	return put_user(val, argp);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
202
203
  }
  #endif
633a08b81   Al Viro   [PATCH] introduce...
204
205
206
207
  int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
  			unsigned cmd, unsigned long arg)
  {
  	struct gendisk *disk = bdev->bd_disk;
d4430d62f   Al Viro   [PATCH] beginning...
208
209
210
  
  	if (disk->fops->ioctl)
  		return disk->fops->ioctl(bdev, mode, cmd, arg);
633a08b81   Al Viro   [PATCH] introduce...
211

633a08b81   Al Viro   [PATCH] introduce...
212
213
214
215
216
217
218
219
  	return -ENOTTY;
  }
  /*
   * For the record: _GPL here is only because somebody decided to slap it
   * on the previous export.  Sheer idiocy, since it wasn't copyrightable
   * at all and could be open-coded without any exports by anybody who cares.
   */
  EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
ee6a129df   Arnd Bergmann   compat_ioctl: blo...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  #ifdef CONFIG_COMPAT
  /*
   * This is the equivalent of compat_ptr_ioctl(), to be used by block
   * drivers that implement only commands that are completely compatible
   * between 32-bit and 64-bit user space
   */
  int blkdev_compat_ptr_ioctl(struct block_device *bdev, fmode_t mode,
  			unsigned cmd, unsigned long arg)
  {
  	struct gendisk *disk = bdev->bd_disk;
  
  	if (disk->fops->ioctl)
  		return disk->fops->ioctl(bdev, mode, cmd,
  					 (unsigned long)compat_ptr(arg));
  
  	return -ENOIOCTLCMD;
  }
  EXPORT_SYMBOL(blkdev_compat_ptr_ioctl);
  #endif
bbd3e0643   Christoph Hellwig   block: add an API...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  static int blkdev_pr_register(struct block_device *bdev,
  		struct pr_registration __user *arg)
  {
  	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
  	struct pr_registration reg;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (!ops || !ops->pr_register)
  		return -EOPNOTSUPP;
  	if (copy_from_user(&reg, arg, sizeof(reg)))
  		return -EFAULT;
  
  	if (reg.flags & ~PR_FL_IGNORE_KEY)
  		return -EOPNOTSUPP;
  	return ops->pr_register(bdev, reg.old_key, reg.new_key, reg.flags);
  }
  
  static int blkdev_pr_reserve(struct block_device *bdev,
  		struct pr_reservation __user *arg)
  {
  	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
  	struct pr_reservation rsv;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (!ops || !ops->pr_reserve)
  		return -EOPNOTSUPP;
  	if (copy_from_user(&rsv, arg, sizeof(rsv)))
  		return -EFAULT;
  
  	if (rsv.flags & ~PR_FL_IGNORE_KEY)
  		return -EOPNOTSUPP;
  	return ops->pr_reserve(bdev, rsv.key, rsv.type, rsv.flags);
  }
  
  static int blkdev_pr_release(struct block_device *bdev,
  		struct pr_reservation __user *arg)
  {
  	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
  	struct pr_reservation rsv;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (!ops || !ops->pr_release)
  		return -EOPNOTSUPP;
  	if (copy_from_user(&rsv, arg, sizeof(rsv)))
  		return -EFAULT;
  
  	if (rsv.flags)
  		return -EOPNOTSUPP;
  	return ops->pr_release(bdev, rsv.key, rsv.type);
  }
  
  static int blkdev_pr_preempt(struct block_device *bdev,
  		struct pr_preempt __user *arg, bool abort)
  {
  	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
  	struct pr_preempt p;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (!ops || !ops->pr_preempt)
  		return -EOPNOTSUPP;
  	if (copy_from_user(&p, arg, sizeof(p)))
  		return -EFAULT;
  
  	if (p.flags)
  		return -EOPNOTSUPP;
  	return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort);
  }
  
  static int blkdev_pr_clear(struct block_device *bdev,
  		struct pr_clear __user *arg)
  {
  	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
  	struct pr_clear c;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  	if (!ops || !ops->pr_clear)
  		return -EOPNOTSUPP;
  	if (copy_from_user(&c, arg, sizeof(c)))
  		return -EFAULT;
  
  	if (c.flags)
  		return -EOPNOTSUPP;
  	return ops->pr_clear(bdev, c.key);
  }
f58c4c0a1   Arnd Bergmann   compat_ioctl: mov...
328
  /*
07d106d0a   Linus Torvalds   vfs: fix up ENOIO...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
   * Is it an unrecognized ioctl? The correct returns are either
   * ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
   * fallback"). ENOIOCTLCMD gets turned into ENOTTY by the ioctl
   * code before returning.
   *
   * Confused drivers sometimes return EINVAL, which is wrong. It
   * means "I understood the ioctl command, but the parameters to
   * it were wrong".
   *
   * We should aim to just fix the broken drivers, the EINVAL case
   * should go away.
   */
  static inline int is_unrecognized_ioctl(int ret)
  {
  	return	ret == -EINVAL ||
  		ret == -ENOTTY ||
  		ret == -ENOIOCTLCMD;
  }
d8e4bb810   Christoph Hellwig   block: cleanup bl...
347
348
  static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
  		unsigned cmd, unsigned long arg)
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
349
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
350
  	int ret;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
351

d8e4bb810   Christoph Hellwig   block: cleanup bl...
352
353
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
354

d8e4bb810   Christoph Hellwig   block: cleanup bl...
355
356
357
  	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  	if (!is_unrecognized_ioctl(ret))
  		return ret;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
358

d8e4bb810   Christoph Hellwig   block: cleanup bl...
359
360
361
362
  	fsync_bdev(bdev);
  	invalidate_bdev(bdev);
  	return 0;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
363

d8e4bb810   Christoph Hellwig   block: cleanup bl...
364
365
366
367
  static int blkdev_roset(struct block_device *bdev, fmode_t mode,
  		unsigned cmd, unsigned long arg)
  {
  	int ret, n;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
368

bb749b31c   Ilya Dryomov   block: move CAP_S...
369
370
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
371
372
373
  	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  	if (!is_unrecognized_ioctl(ret))
  		return ret;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
374
375
376
377
378
  	if (get_user(n, (int __user *)arg))
  		return -EFAULT;
  	set_device_ro(bdev, n);
  	return 0;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
379

d8e4bb810   Christoph Hellwig   block: cleanup bl...
380
381
382
383
384
385
  static int blkdev_getgeo(struct block_device *bdev,
  		struct hd_geometry __user *argp)
  {
  	struct gendisk *disk = bdev->bd_disk;
  	struct hd_geometry geo;
  	int ret;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
386

d8e4bb810   Christoph Hellwig   block: cleanup bl...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  	if (!argp)
  		return -EINVAL;
  	if (!disk->fops->getgeo)
  		return -ENOTTY;
  
  	/*
  	 * We need to set the startsect first, the driver may
  	 * want to override it.
  	 */
  	memset(&geo, 0, sizeof(geo));
  	geo.start = get_start_sect(bdev);
  	ret = disk->fops->getgeo(bdev, &geo);
  	if (ret)
  		return ret;
  	if (copy_to_user(argp, &geo, sizeof(geo)))
  		return -EFAULT;
  	return 0;
  }
66ba32dc1   Martin K. Petersen   block: ioctl to z...
405

bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
406
407
408
409
410
411
412
  #ifdef CONFIG_COMPAT
  struct compat_hd_geometry {
  	unsigned char heads;
  	unsigned char sectors;
  	unsigned short cylinders;
  	u32 start;
  };
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
413
414
  static int compat_hdio_getgeo(struct block_device *bdev,
  			      struct compat_hd_geometry __user *ugeo)
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
415
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
416
  	struct gendisk *disk = bdev->bd_disk;
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  	struct hd_geometry geo;
  	int ret;
  
  	if (!ugeo)
  		return -EINVAL;
  	if (!disk->fops->getgeo)
  		return -ENOTTY;
  
  	memset(&geo, 0, sizeof(geo));
  	/*
  	 * We need to set the startsect first, the driver may
  	 * want to override it.
  	 */
  	geo.start = get_start_sect(bdev);
  	ret = disk->fops->getgeo(bdev, &geo);
  	if (ret)
  		return ret;
  
  	ret = copy_to_user(ugeo, &geo, 4);
  	ret |= put_user(geo.start, &ugeo->start);
  	if (ret)
  		ret = -EFAULT;
  
  	return ret;
  }
  #endif
d8e4bb810   Christoph Hellwig   block: cleanup bl...
443
444
445
446
447
  /* set the logical block size */
  static int blkdev_bszset(struct block_device *bdev, fmode_t mode,
  		int __user *argp)
  {
  	int ret, n;
66ba32dc1   Martin K. Petersen   block: ioctl to z...
448

d8e4bb810   Christoph Hellwig   block: cleanup bl...
449
450
451
452
453
454
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  	if (!argp)
  		return -EINVAL;
  	if (get_user(n, argp))
  		return -EFAULT;
66ba32dc1   Martin K. Petersen   block: ioctl to z...
455

478162821   Christoph Hellwig   block: cleanup bl...
456
457
  	if (mode & FMODE_EXCL)
  		return set_blocksize(bdev, n);
d30a2605b   David Woodhouse   Add BLKDISCARD io...
458

478162821   Christoph Hellwig   block: cleanup bl...
459
460
  	if (IS_ERR(blkdev_get_by_dev(bdev->bd_dev, mode | FMODE_EXCL, &bdev)))
  		return -EBUSY;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
461
  	ret = set_blocksize(bdev, n);
478162821   Christoph Hellwig   block: cleanup bl...
462
  	blkdev_put(bdev, mode | FMODE_EXCL);
d8e4bb810   Christoph Hellwig   block: cleanup bl...
463
464
  	return ret;
  }
a885c8c43   Christoph Hellwig   [PATCH] Add block...
465

d8e4bb810   Christoph Hellwig   block: cleanup bl...
466
  /*
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
467
468
469
   * Common commands that are handled the same way on native and compat
   * user space. Note the separate arg/argp parameters that are needed
   * to deal with the compat_ptr() conversion.
d8e4bb810   Christoph Hellwig   block: cleanup bl...
470
   */
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
471
472
  static int blkdev_common_ioctl(struct block_device *bdev, fmode_t mode,
  				unsigned cmd, unsigned long arg, void __user *argp)
d8e4bb810   Christoph Hellwig   block: cleanup bl...
473
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  	unsigned int max_sectors;
  
  	switch (cmd) {
  	case BLKFLSBUF:
  		return blkdev_flushbuf(bdev, mode, cmd, arg);
  	case BLKROSET:
  		return blkdev_roset(bdev, mode, cmd, arg);
  	case BLKDISCARD:
  		return blk_ioctl_discard(bdev, mode, arg, 0);
  	case BLKSECDISCARD:
  		return blk_ioctl_discard(bdev, mode, arg,
  				BLKDEV_DISCARD_SECURE);
  	case BLKZEROOUT:
  		return blk_ioctl_zeroout(bdev, mode, arg);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
488
489
490
  	case BLKREPORTZONE:
  		return blkdev_report_zones_ioctl(bdev, mode, cmd, arg);
  	case BLKRESETZONE:
e876df1fe   Ajay Joshi   block: add zone o...
491
492
493
494
  	case BLKOPENZONE:
  	case BLKCLOSEZONE:
  	case BLKFINISHZONE:
  		return blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg);
72cd87576   Damien Le Moal   block: Introduce ...
495
  	case BLKGETZONESZ:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
496
  		return put_uint(argp, bdev_zone_sectors(bdev));
65e4e3eee   Damien Le Moal   block: Introduce ...
497
  	case BLKGETNRZONES:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
498
  		return put_uint(argp, blkdev_nr_zones(bdev->bd_disk));
45048d096   Al Viro   [PATCH] get rid o...
499
  	case BLKROGET:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
500
  		return put_int(argp, bdev_read_only(bdev) != 0);
ac481c20e   Martin K. Petersen   block: Topology i...
501
  	case BLKSSZGET: /* get block device logical block size */
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
502
  		return put_int(argp, bdev_logical_block_size(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
503
  	case BLKPBSZGET: /* get block device physical block size */
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
504
  		return put_uint(argp, bdev_physical_block_size(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
505
  	case BLKIOMIN:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
506
  		return put_uint(argp, bdev_io_min(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
507
  	case BLKIOOPT:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
508
  		return put_uint(argp, bdev_io_opt(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
509
  	case BLKALIGNOFF:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
510
  		return put_int(argp, bdev_alignment_offset(bdev));
98262f276   Martin K. Petersen   block: Allow devi...
511
  	case BLKDISCARDZEROES:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
512
  		return put_uint(argp, 0);
45048d096   Al Viro   [PATCH] get rid o...
513
  	case BLKSECTGET:
63f264965   Akinobu Mita   block: fix BLKSEC...
514
515
  		max_sectors = min_t(unsigned int, USHRT_MAX,
  				    queue_max_sectors(bdev_get_queue(bdev)));
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
516
  		return put_ushort(argp, max_sectors);
ef00f59c9   Martin K. Petersen   block: Add BLKROT...
517
  	case BLKROTATIONAL:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
518
  		return put_ushort(argp, !blk_queue_nonrot(bdev_get_queue(bdev)));
45048d096   Al Viro   [PATCH] get rid o...
519
520
521
522
  	case BLKRASET:
  	case BLKFRASET:
  		if(!capable(CAP_SYS_ADMIN))
  			return -EACCES;
efa7c9f97   Jan Kara   block: Get rid of...
523
  		bdev->bd_bdi->ra_pages = (arg * 512) / PAGE_SIZE;
45048d096   Al Viro   [PATCH] get rid o...
524
  		return 0;
45048d096   Al Viro   [PATCH] get rid o...
525
  	case BLKRRPART:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
526
  		return blkdev_reread_part(bdev);
45048d096   Al Viro   [PATCH] get rid o...
527
528
  	case BLKTRACESTART:
  	case BLKTRACESTOP:
45048d096   Al Viro   [PATCH] get rid o...
529
  	case BLKTRACETEARDOWN:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
530
  		return blk_trace_ioctl(bdev, cmd, argp);
bbd3e0643   Christoph Hellwig   block: add an API...
531
532
533
534
535
536
537
538
539
540
541
542
  	case IOC_PR_REGISTER:
  		return blkdev_pr_register(bdev, argp);
  	case IOC_PR_RESERVE:
  		return blkdev_pr_reserve(bdev, argp);
  	case IOC_PR_RELEASE:
  		return blkdev_pr_release(bdev, argp);
  	case IOC_PR_PREEMPT:
  		return blkdev_pr_preempt(bdev, argp, false);
  	case IOC_PR_PREEMPT_ABORT:
  		return blkdev_pr_preempt(bdev, argp, true);
  	case IOC_PR_CLEAR:
  		return blkdev_pr_clear(bdev, argp);
45048d096   Al Viro   [PATCH] get rid o...
543
  	default:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
544
  		return -ENOIOCTLCMD;
45048d096   Al Viro   [PATCH] get rid o...
545
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
  }
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  
  /*
   * Always keep this in sync with compat_blkdev_ioctl()
   * to handle all incompatible commands in both functions.
   *
   * New commands must be compatible and go into blkdev_common_ioctl
   */
  int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
  			unsigned long arg)
  {
  	int ret;
  	loff_t size;
  	void __user *argp = (void __user *)arg;
  
  	switch (cmd) {
  	/* These need separate implementations for the data structure */
  	case HDIO_GETGEO:
  		return blkdev_getgeo(bdev, argp);
  	case BLKPG:
  		return blkpg_ioctl(bdev, argp);
  
  	/* Compat mode returns 32-bit data instead of 'long' */
  	case BLKRAGET:
  	case BLKFRAGET:
  		if (!argp)
  			return -EINVAL;
  		return put_long(argp, (bdev->bd_bdi->ra_pages*PAGE_SIZE) / 512);
  	case BLKGETSIZE:
  		size = i_size_read(bdev->bd_inode);
  		if ((size >> 9) > ~0UL)
  			return -EFBIG;
  		return put_ulong(argp, size >> 9);
  
  	/* The data is compatible, but the command number is different */
  	case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */
  		return put_int(argp, block_size(bdev));
  	case BLKBSZSET:
  		return blkdev_bszset(bdev, mode, argp);
  	case BLKGETSIZE64:
  		return put_u64(argp, i_size_read(bdev->bd_inode));
  
  	/* Incompatible alignment on i386 */
  	case BLKTRACESETUP:
  		return blk_trace_ioctl(bdev, cmd, argp);
  	default:
  		break;
  	}
  
  	ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp);
  	if (ret == -ENOIOCTLCMD)
  		return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(blkdev_ioctl); /* for /dev/raw */
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
602
603
  
  #ifdef CONFIG_COMPAT
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
604

bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
605
606
607
608
609
610
611
612
613
  #define BLKBSZGET_32		_IOR(0x12, 112, int)
  #define BLKBSZSET_32		_IOW(0x12, 113, int)
  #define BLKGETSIZE64_32		_IOR(0x12, 114, int)
  
  /* Most of the generic ioctls are handled in the normal fallback path.
     This assumes the blkdev's low level compat_ioctl always returns
     ENOIOCTLCMD for unknown ioctls. */
  long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
  {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
614
615
  	int ret;
  	void __user *argp = compat_ptr(arg);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
616
617
618
619
620
  	struct inode *inode = file->f_mapping->host;
  	struct block_device *bdev = inode->i_bdev;
  	struct gendisk *disk = bdev->bd_disk;
  	fmode_t mode = file->f_mode;
  	loff_t size;
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
621
622
623
624
625
626
627
628
629
630
631
  
  	/*
  	 * O_NDELAY can be altered using fcntl(.., F_SETFL, ..), so we have
  	 * to updated it before every ioctl.
  	 */
  	if (file->f_flags & O_NDELAY)
  		mode |= FMODE_NDELAY;
  	else
  		mode &= ~FMODE_NDELAY;
  
  	switch (cmd) {
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
632
  	/* These need separate implementations for the data structure */
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
633
  	case HDIO_GETGEO:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
634
  		return compat_hdio_getgeo(bdev, argp);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
635
  	case BLKPG:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
636
637
638
  		return compat_blkpg_ioctl(bdev, argp);
  
  	/* Compat mode returns 32-bit data instead of 'long' */
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
639
640
  	case BLKRAGET:
  	case BLKFRAGET:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
641
  		if (!argp)
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
642
  			return -EINVAL;
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
643
  		return compat_put_long(argp,
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
644
  			       (bdev->bd_bdi->ra_pages * PAGE_SIZE) / 512);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
645
646
647
648
  	case BLKGETSIZE:
  		size = i_size_read(bdev->bd_inode);
  		if ((size >> 9) > ~0UL)
  			return -EFBIG;
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
649
  		return compat_put_ulong(argp, size >> 9);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
650

9b81648cb   Arnd Bergmann   compat_ioctl: sim...
651
652
653
654
655
  	/* The data is compatible, but the command number is different */
  	case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */
  		return put_int(argp, bdev_logical_block_size(bdev));
  	case BLKBSZSET_32:
  		return blkdev_bszset(bdev, mode, argp);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
656
  	case BLKGETSIZE64_32:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
657
  		return put_u64(argp, i_size_read(bdev->bd_inode));
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
658

9b81648cb   Arnd Bergmann   compat_ioctl: sim...
659
  	/* Incompatible alignment on i386 */
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
660
  	case BLKTRACESETUP32:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
661
  		return blk_trace_ioctl(bdev, cmd, argp);
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
662
  	default:
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
663
  		break;
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
664
  	}
9b81648cb   Arnd Bergmann   compat_ioctl: sim...
665
666
667
668
669
670
  
  	ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp);
  	if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl)
  		ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
  
  	return ret;
bdc1ddad3   Arnd Bergmann   compat_ioctl: blo...
671
672
  }
  #endif