Blame view

block/ioctl.c 15.4 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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
  #include <linux/blkdev.h>
d5decd3b9   Paul Gortmaker   block: add export...
4
  #include <linux/export.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
5
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
  #include <linux/blkpg.h>
a885c8c43   Christoph Hellwig   [PATCH] Add block...
7
  #include <linux/hdreg.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
  #include <linux/backing-dev.h>
ff01bb483   Al Viro   fs: move code out...
9
  #include <linux/fs.h>
2056a782f   Jens Axboe   [PATCH] Block que...
10
  #include <linux/blktrace_api.h>
bbd3e0643   Christoph Hellwig   block: add an API...
11
  #include <linux/pr.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
12
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
  
  static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
  {
  	struct block_device *bdevp;
  	struct gendisk *disk;
c83f6bf98   Vivek Goyal   block: add partit...
18
  	struct hd_struct *part, *lpart;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  	struct blkpg_ioctl_arg a;
  	struct blkpg_partition p;
e71bf0d0e   Tejun Heo   block: fix disk->...
21
  	struct disk_part_iter piter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  	long long start, length;
cf771cb5a   Tejun Heo   block: make varia...
23
  	int partno;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  	if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
  		return -EFAULT;
  	if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
  		return -EFAULT;
  	disk = bdev->bd_disk;
  	if (bdev != bdev->bd_contains)
  		return -EINVAL;
cf771cb5a   Tejun Heo   block: make varia...
34
  	partno = p.pno;
540eed563   Tejun Heo   block: make parti...
35
  	if (partno <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
  		return -EINVAL;
  	switch (a.op) {
  		case BLKPG_ADD_PARTITION:
  			start = p.start >> 9;
  			length = p.length >> 9;
c83f6bf98   Vivek Goyal   block: add partit...
41
42
  			/* check for fit in a hd_struct */
  			if (sizeof(sector_t) == sizeof(long) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  			    sizeof(long long) > sizeof(long)) {
  				long pstart = start, plength = length;
  				if (pstart != start || plength != length
2bd6efad2   Alan Cox   blk: add an upper...
46
  				    || pstart < 0 || plength < 0 || partno > 65535)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
  					return -EINVAL;
  			}
633395b67   Stefan Haberland   block: check part...
49
50
51
  			/* check if partition is aligned to blocksize */
  			if (p.start & (bdev_logical_block_size(bdev) - 1))
  				return -EINVAL;
88e341261   Tejun Heo   block: update add...
52

c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
53
  			mutex_lock(&bdev->bd_mutex);
88e341261   Tejun Heo   block: update add...
54

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  			/* overlap? */
e71bf0d0e   Tejun Heo   block: fix disk->...
56
57
58
59
60
61
  			disk_part_iter_init(&piter, disk,
  					    DISK_PITER_INCL_EMPTY);
  			while ((part = disk_part_iter_next(&piter))) {
  				if (!(start + length <= part->start_sect ||
  				      start >= part->start_sect + part->nr_sects)) {
  					disk_part_iter_exit(&piter);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
62
  					mutex_unlock(&bdev->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
  					return -EBUSY;
  				}
  			}
e71bf0d0e   Tejun Heo   block: fix disk->...
66
  			disk_part_iter_exit(&piter);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  			/* all seems OK */
ba32929a9   Tejun Heo   block: make add_p...
68
  			part = add_partition(disk, partno, start, length,
6d1d8050b   Will Drewry   block, partition:...
69
  					     ADDPART_FLAG_NONE, NULL);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
70
  			mutex_unlock(&bdev->bd_mutex);
c7d1ba417   Duan Jiong   block: replace IS...
71
  			return PTR_ERR_OR_ZERO(part);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  		case BLKPG_DEL_PARTITION:
e71bf0d0e   Tejun Heo   block: fix disk->...
73
74
  			part = disk_get_part(disk, partno);
  			if (!part)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  				return -ENXIO;
e71bf0d0e   Tejun Heo   block: fix disk->...
76
77
78
  
  			bdevp = bdget(part_devt(part));
  			disk_put_part(part);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  			if (!bdevp)
  				return -ENOMEM;
e71bf0d0e   Tejun Heo   block: fix disk->...
81

2e7b651df   Peter Zijlstra   [PATCH] remove th...
82
  			mutex_lock(&bdevp->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  			if (bdevp->bd_openers) {
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
84
  				mutex_unlock(&bdevp->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
  				bdput(bdevp);
  				return -EBUSY;
  			}
  			/* all seems OK */
  			fsync_bdev(bdevp);
f98393a64   Peter Zijlstra   mm: remove destro...
90
  			invalidate_bdev(bdevp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91

6d740cd5b   Peter Zijlstra   [PATCH] lockdep: ...
92
  			mutex_lock_nested(&bdev->bd_mutex, 1);
cf771cb5a   Tejun Heo   block: make varia...
93
  			delete_partition(disk, partno);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
94
95
  			mutex_unlock(&bdev->bd_mutex);
  			mutex_unlock(&bdevp->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
  			bdput(bdevp);
  
  			return 0;
c83f6bf98   Vivek Goyal   block: add partit...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  		case BLKPG_RESIZE_PARTITION:
  			start = p.start >> 9;
  			/* new length of partition in bytes */
  			length = p.length >> 9;
  			/* check for fit in a hd_struct */
  			if (sizeof(sector_t) == sizeof(long) &&
  			    sizeof(long long) > sizeof(long)) {
  				long pstart = start, plength = length;
  				if (pstart != start || plength != length
  				    || pstart < 0 || plength < 0)
  					return -EINVAL;
  			}
  			part = disk_get_part(disk, partno);
  			if (!part)
  				return -ENXIO;
  			bdevp = bdget(part_devt(part));
  			if (!bdevp) {
  				disk_put_part(part);
  				return -ENOMEM;
  			}
  			mutex_lock(&bdevp->bd_mutex);
  			mutex_lock_nested(&bdev->bd_mutex, 1);
  			if (start != part->start_sect) {
  				mutex_unlock(&bdevp->bd_mutex);
  				mutex_unlock(&bdev->bd_mutex);
  				bdput(bdevp);
  				disk_put_part(part);
  				return -EINVAL;
  			}
  			/* overlap? */
  			disk_part_iter_init(&piter, disk,
  					    DISK_PITER_INCL_EMPTY);
  			while ((lpart = disk_part_iter_next(&piter))) {
  				if (lpart->partno != partno &&
  				   !(start + length <= lpart->start_sect ||
  				   start >= lpart->start_sect + lpart->nr_sects)
  				   ) {
  					disk_part_iter_exit(&piter);
  					mutex_unlock(&bdevp->bd_mutex);
  					mutex_unlock(&bdev->bd_mutex);
  					bdput(bdevp);
  					disk_put_part(part);
  					return -EBUSY;
  				}
  			}
  			disk_part_iter_exit(&piter);
  			part_nr_sects_write(part, (sector_t)length);
  			i_size_write(bdevp->bd_inode, p.length);
  			mutex_unlock(&bdevp->bd_mutex);
  			mutex_unlock(&bdev->bd_mutex);
  			bdput(bdevp);
  			disk_put_part(part);
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
  		default:
  			return -EINVAL;
  	}
  }
be3241779   Jarod Wilson   block: export blk...
156
157
158
159
160
161
  /*
   * This is an exported API for the block driver, and will not
   * acquire bd_mutex. This API should be used in case that
   * caller has held bd_mutex already.
   */
  int __blkdev_reread_part(struct block_device *bdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
  {
  	struct gendisk *disk = bdev->bd_disk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164

d27769ec3   Tejun Heo   block: add GENHD_...
165
  	if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
  		return -EINVAL;
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
be3241779   Jarod Wilson   block: export blk...
169
170
171
172
173
174
175
176
177
178
179
  
  	lockdep_assert_held(&bdev->bd_mutex);
  
  	return rescan_partitions(disk, bdev);
  }
  EXPORT_SYMBOL(__blkdev_reread_part);
  
  /*
   * This is an exported API for the block driver, and will
   * try to acquire bd_mutex. If bd_mutex has been held already
   * in current context, please call __blkdev_reread_part().
b04a5636a   Ming Lei   block: replace tr...
180
181
182
183
184
185
   *
   * Make sure the held locks in current context aren't required
   * in open()/close() handler and I/O path for avoiding ABBA deadlock:
   * - bd_mutex is held before calling block driver's open/close
   *   handler
   * - reading partition table may submit I/O to the block device
be3241779   Jarod Wilson   block: export blk...
186
187
188
189
   */
  int blkdev_reread_part(struct block_device *bdev)
  {
  	int res;
b04a5636a   Ming Lei   block: replace tr...
190
  	mutex_lock(&bdev->bd_mutex);
be3241779   Jarod Wilson   block: export blk...
191
  	res = __blkdev_reread_part(bdev);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
192
  	mutex_unlock(&bdev->bd_mutex);
be3241779   Jarod Wilson   block: export blk...
193

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
  	return res;
  }
be3241779   Jarod Wilson   block: export blk...
196
  EXPORT_SYMBOL(blkdev_reread_part);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

d8e4bb810   Christoph Hellwig   block: cleanup bl...
198
199
  static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode,
  		unsigned long arg, unsigned long flags)
d30a2605b   David Woodhouse   Add BLKDISCARD io...
200
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
201
202
  	uint64_t range[2];
  	uint64_t start, len;
351499a17   Dmitry Monakhov   block: Invalidate...
203
204
  	struct request_queue *q = bdev_get_queue(bdev);
  	struct address_space *mapping = bdev->bd_inode->i_mapping;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
205
206
207
  
  	if (!(mode & FMODE_WRITE))
  		return -EBADF;
351499a17   Dmitry Monakhov   block: Invalidate...
208
209
  	if (!blk_queue_discard(q))
  		return -EOPNOTSUPP;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
210
211
212
213
214
  	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
  		return -EFAULT;
  
  	start = range[0];
  	len = range[1];
8d57a98cc   Adrian Hunter   block: add secure...
215

d30a2605b   David Woodhouse   Add BLKDISCARD io...
216
217
218
219
  	if (start & 511)
  		return -EINVAL;
  	if (len & 511)
  		return -EINVAL;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
220

351499a17   Dmitry Monakhov   block: Invalidate...
221
  	if (start + len > i_size_read(bdev->bd_inode))
d30a2605b   David Woodhouse   Add BLKDISCARD io...
222
  		return -EINVAL;
0bd1ed486   Ming Lei   block: pass inclu...
223
  	truncate_inode_pages_range(mapping, start, start + len - 1);
351499a17   Dmitry Monakhov   block: Invalidate...
224
225
  	return blkdev_issue_discard(bdev, start >> 9, len >> 9,
  				    GFP_KERNEL, flags);
d30a2605b   David Woodhouse   Add BLKDISCARD io...
226
  }
d8e4bb810   Christoph Hellwig   block: cleanup bl...
227
228
  static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
  		unsigned long arg)
66ba32dc1   Martin K. Petersen   block: ioctl to z...
229
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
230
  	uint64_t range[2];
22dd6d356   Darrick J. Wong   block: invalidate...
231
232
  	struct address_space *mapping;
  	uint64_t start, end, len;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
233
234
235
236
237
238
239
240
241
  
  	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...
242
  	end = start + len - 1;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
243

66ba32dc1   Martin K. Petersen   block: ioctl to z...
244
245
246
247
  	if (start & 511)
  		return -EINVAL;
  	if (len & 511)
  		return -EINVAL;
22dd6d356   Darrick J. Wong   block: invalidate...
248
249
250
  	if (end >= (uint64_t)i_size_read(bdev->bd_inode))
  		return -EINVAL;
  	if (end < start)
66ba32dc1   Martin K. Petersen   block: ioctl to z...
251
  		return -EINVAL;
22dd6d356   Darrick J. Wong   block: invalidate...
252
253
254
255
256
  	/* Invalidate the page cache, including dirty pages */
  	mapping = bdev->bd_inode->i_mapping;
  	truncate_inode_pages_range(mapping, start, end);
  
  	return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL,
ee472d835   Christoph Hellwig   block: add a flag...
257
  			BLKDEV_ZERO_NOUNMAP);
66ba32dc1   Martin K. Petersen   block: ioctl to z...
258
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
263
264
265
266
267
  static int put_ushort(unsigned long arg, unsigned short val)
  {
  	return put_user(val, (unsigned short __user *)arg);
  }
  
  static int put_int(unsigned long arg, int val)
  {
  	return put_user(val, (int __user *)arg);
  }
ac481c20e   Martin K. Petersen   block: Topology i...
268
269
270
271
  static int put_uint(unsigned long arg, unsigned int val)
  {
  	return put_user(val, (unsigned int __user *)arg);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  static int put_long(unsigned long arg, long val)
  {
  	return put_user(val, (long __user *)arg);
  }
  
  static int put_ulong(unsigned long arg, unsigned long val)
  {
  	return put_user(val, (unsigned long __user *)arg);
  }
  
  static int put_u64(unsigned long arg, u64 val)
  {
  	return put_user(val, (u64 __user *)arg);
  }
633a08b81   Al Viro   [PATCH] introduce...
286
287
288
289
  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...
290
291
292
  
  	if (disk->fops->ioctl)
  		return disk->fops->ioctl(bdev, mode, cmd, arg);
633a08b81   Al Viro   [PATCH] introduce...
293

633a08b81   Al Viro   [PATCH] introduce...
294
295
296
297
298
299
300
301
  	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);
bbd3e0643   Christoph Hellwig   block: add an API...
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
  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...
391
  /*
07d106d0a   Linus Torvalds   vfs: fix up ENOIO...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
   * 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...
410
411
  static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
  		unsigned cmd, unsigned long arg)
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
412
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
413
  	int ret;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
414

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

d8e4bb810   Christoph Hellwig   block: cleanup bl...
418
419
420
  	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  	if (!is_unrecognized_ioctl(ret))
  		return ret;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
421

d8e4bb810   Christoph Hellwig   block: cleanup bl...
422
423
424
425
  	fsync_bdev(bdev);
  	invalidate_bdev(bdev);
  	return 0;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
426

d8e4bb810   Christoph Hellwig   block: cleanup bl...
427
428
429
430
  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...
431

bb749b31c   Ilya Dryomov   block: move CAP_S...
432
433
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
434
435
436
  	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  	if (!is_unrecognized_ioctl(ret))
  		return ret;
d8e4bb810   Christoph Hellwig   block: cleanup bl...
437
438
439
440
441
  	if (get_user(n, (int __user *)arg))
  		return -EFAULT;
  	set_device_ro(bdev, n);
  	return 0;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
442

d8e4bb810   Christoph Hellwig   block: cleanup bl...
443
444
445
446
447
448
  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...
449

d8e4bb810   Christoph Hellwig   block: cleanup bl...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
  	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...
468

d8e4bb810   Christoph Hellwig   block: cleanup bl...
469
470
471
472
473
  /* 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...
474

d8e4bb810   Christoph Hellwig   block: cleanup bl...
475
476
477
478
479
480
  	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...
481

d8e4bb810   Christoph Hellwig   block: cleanup bl...
482
483
484
485
  	if (!(mode & FMODE_EXCL)) {
  		bdgrab(bdev);
  		if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
  			return -EBUSY;
66ba32dc1   Martin K. Petersen   block: ioctl to z...
486
  	}
d30a2605b   David Woodhouse   Add BLKDISCARD io...
487

d8e4bb810   Christoph Hellwig   block: cleanup bl...
488
489
490
491
492
  	ret = set_blocksize(bdev, n);
  	if (!(mode & FMODE_EXCL))
  		blkdev_put(bdev, mode | FMODE_EXCL);
  	return ret;
  }
a885c8c43   Christoph Hellwig   [PATCH] Add block...
493

d8e4bb810   Christoph Hellwig   block: cleanup bl...
494
495
496
497
498
499
  /*
   * always keep this in sync with compat_blkdev_ioctl()
   */
  int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
  			unsigned long arg)
  {
d8e4bb810   Christoph Hellwig   block: cleanup bl...
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  	void __user *argp = (void __user *)arg;
  	loff_t size;
  	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...
516
517
518
519
  	case BLKREPORTZONE:
  		return blkdev_report_zones_ioctl(bdev, mode, cmd, arg);
  	case BLKRESETZONE:
  		return blkdev_reset_zones_ioctl(bdev, mode, cmd, arg);
72cd87576   Damien Le Moal   block: Introduce ...
520
521
  	case BLKGETZONESZ:
  		return put_uint(arg, bdev_zone_sectors(bdev));
65e4e3eee   Damien Le Moal   block: Introduce ...
522
523
  	case BLKGETNRZONES:
  		return put_uint(arg, blkdev_nr_zones(bdev));
d8e4bb810   Christoph Hellwig   block: cleanup bl...
524
525
  	case HDIO_GETGEO:
  		return blkdev_getgeo(bdev, argp);
45048d096   Al Viro   [PATCH] get rid o...
526
527
528
529
  	case BLKRAGET:
  	case BLKFRAGET:
  		if (!arg)
  			return -EINVAL;
efa7c9f97   Jan Kara   block: Get rid of...
530
  		return put_long(arg, (bdev->bd_bdi->ra_pages*PAGE_SIZE) / 512);
45048d096   Al Viro   [PATCH] get rid o...
531
532
  	case BLKROGET:
  		return put_int(arg, bdev_read_only(bdev) != 0);
ac481c20e   Martin K. Petersen   block: Topology i...
533
  	case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */
45048d096   Al Viro   [PATCH] get rid o...
534
  		return put_int(arg, block_size(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
535
  	case BLKSSZGET: /* get block device logical block size */
e1defc4ff   Martin K. Petersen   block: Do away wi...
536
  		return put_int(arg, bdev_logical_block_size(bdev));
ac481c20e   Martin K. Petersen   block: Topology i...
537
538
539
540
541
542
543
544
  	case BLKPBSZGET: /* get block device physical block size */
  		return put_uint(arg, bdev_physical_block_size(bdev));
  	case BLKIOMIN:
  		return put_uint(arg, bdev_io_min(bdev));
  	case BLKIOOPT:
  		return put_uint(arg, bdev_io_opt(bdev));
  	case BLKALIGNOFF:
  		return put_int(arg, bdev_alignment_offset(bdev));
98262f276   Martin K. Petersen   block: Allow devi...
545
  	case BLKDISCARDZEROES:
48920ff2a   Christoph Hellwig   block: remove the...
546
  		return put_uint(arg, 0);
45048d096   Al Viro   [PATCH] get rid o...
547
  	case BLKSECTGET:
63f264965   Akinobu Mita   block: fix BLKSEC...
548
549
550
  		max_sectors = min_t(unsigned int, USHRT_MAX,
  				    queue_max_sectors(bdev_get_queue(bdev)));
  		return put_ushort(arg, max_sectors);
ef00f59c9   Martin K. Petersen   block: Add BLKROT...
551
552
  	case BLKROTATIONAL:
  		return put_ushort(arg, !blk_queue_nonrot(bdev_get_queue(bdev)));
45048d096   Al Viro   [PATCH] get rid o...
553
554
555
556
  	case BLKRASET:
  	case BLKFRASET:
  		if(!capable(CAP_SYS_ADMIN))
  			return -EACCES;
efa7c9f97   Jan Kara   block: Get rid of...
557
  		bdev->bd_bdi->ra_pages = (arg * 512) / PAGE_SIZE;
45048d096   Al Viro   [PATCH] get rid o...
558
559
  		return 0;
  	case BLKBSZSET:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
560
  		return blkdev_bszset(bdev, mode, argp);
45048d096   Al Viro   [PATCH] get rid o...
561
  	case BLKPG:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
562
  		return blkpg_ioctl(bdev, argp);
45048d096   Al Viro   [PATCH] get rid o...
563
  	case BLKRRPART:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
564
  		return blkdev_reread_part(bdev);
45048d096   Al Viro   [PATCH] get rid o...
565
  	case BLKGETSIZE:
77304d2ab   Mike Snitzer   block: read i_siz...
566
  		size = i_size_read(bdev->bd_inode);
45048d096   Al Viro   [PATCH] get rid o...
567
568
569
570
  		if ((size >> 9) > ~0UL)
  			return -EFBIG;
  		return put_ulong(arg, size >> 9);
  	case BLKGETSIZE64:
77304d2ab   Mike Snitzer   block: read i_siz...
571
  		return put_u64(arg, i_size_read(bdev->bd_inode));
45048d096   Al Viro   [PATCH] get rid o...
572
573
574
575
  	case BLKTRACESTART:
  	case BLKTRACESTOP:
  	case BLKTRACESETUP:
  	case BLKTRACETEARDOWN:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
576
  		return blk_trace_ioctl(bdev, cmd, argp);
bbd3e0643   Christoph Hellwig   block: add an API...
577
578
579
580
581
582
583
584
585
586
587
588
  	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...
589
  	default:
d8e4bb810   Christoph Hellwig   block: cleanup bl...
590
  		return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
45048d096   Al Viro   [PATCH] get rid o...
591
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
  }
68f66feb3   Stephen Tweedie   [PATCH] Fix root ...
593
  EXPORT_SYMBOL_GPL(blkdev_ioctl);