Blame view

block/ioctl.c 8.62 KB
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
1
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
  #include <linux/blkdev.h>
  #include <linux/blkpg.h>
a885c8c43   Christoph Hellwig   [PATCH] Add block...
4
  #include <linux/hdreg.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
  #include <linux/backing-dev.h>
  #include <linux/buffer_head.h>
  #include <linux/smp_lock.h>
2056a782f   Jens Axboe   [PATCH] Block que...
8
  #include <linux/blktrace_api.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
  #include <asm/uaccess.h>
  
  static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
  {
  	struct block_device *bdevp;
  	struct gendisk *disk;
e71bf0d0e   Tejun Heo   block: fix disk->...
15
  	struct hd_struct *part;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
  	struct blkpg_ioctl_arg a;
  	struct blkpg_partition p;
e71bf0d0e   Tejun Heo   block: fix disk->...
18
  	struct disk_part_iter piter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  	long long start, length;
cf771cb5a   Tejun Heo   block: make varia...
20
  	int partno;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
28
29
30
  
  	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...
31
  	partno = p.pno;
540eed563   Tejun Heo   block: make parti...
32
  	if (partno <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
43
44
45
  		return -EINVAL;
  	switch (a.op) {
  		case BLKPG_ADD_PARTITION:
  			start = p.start >> 9;
  			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;
  			}
88e341261   Tejun Heo   block: update add...
46

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  			/* overlap? */
e71bf0d0e   Tejun Heo   block: fix disk->...
50
51
52
53
54
55
  			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...
56
  					mutex_unlock(&bdev->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
  					return -EBUSY;
  				}
  			}
e71bf0d0e   Tejun Heo   block: fix disk->...
60
  			disk_part_iter_exit(&piter);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  			/* all seems OK */
ba32929a9   Tejun Heo   block: make add_p...
62
63
  			part = add_partition(disk, partno, start, length,
  					     ADDPART_FLAG_NONE);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
64
  			mutex_unlock(&bdev->bd_mutex);
ba32929a9   Tejun Heo   block: make add_p...
65
  			return IS_ERR(part) ? PTR_ERR(part) : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  		case BLKPG_DEL_PARTITION:
e71bf0d0e   Tejun Heo   block: fix disk->...
67
68
  			part = disk_get_part(disk, partno);
  			if (!part)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  				return -ENXIO;
e71bf0d0e   Tejun Heo   block: fix disk->...
70
71
72
  
  			bdevp = bdget(part_devt(part));
  			disk_put_part(part);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  			if (!bdevp)
  				return -ENOMEM;
e71bf0d0e   Tejun Heo   block: fix disk->...
75

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

6d740cd5b   Peter Zijlstra   [PATCH] lockdep: ...
86
  			mutex_lock_nested(&bdev->bd_mutex, 1);
cf771cb5a   Tejun Heo   block: make varia...
87
  			delete_partition(disk, partno);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
88
89
  			mutex_unlock(&bdev->bd_mutex);
  			mutex_unlock(&bdevp->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
98
99
100
101
  			bdput(bdevp);
  
  			return 0;
  		default:
  			return -EINVAL;
  	}
  }
  
  static int blkdev_reread_part(struct block_device *bdev)
  {
  	struct gendisk *disk = bdev->bd_disk;
  	int res;
b5d0b9df0   Tejun Heo   block: introduce ...
102
  	if (!disk_partitionable(disk) || bdev != bdev->bd_contains)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
  		return -EINVAL;
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
106
  	if (!mutex_trylock(&bdev->bd_mutex))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  		return -EBUSY;
  	res = rescan_partitions(disk, bdev);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
109
  	mutex_unlock(&bdev->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
  	return res;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
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
152
153
154
155
156
157
158
159
160
161
  static void blk_ioc_discard_endio(struct bio *bio, int err)
  {
  	if (err) {
  		if (err == -EOPNOTSUPP)
  			set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
  		clear_bit(BIO_UPTODATE, &bio->bi_flags);
  	}
  	complete(bio->bi_private);
  }
  
  static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
  			     uint64_t len)
  {
  	struct request_queue *q = bdev_get_queue(bdev);
  	int ret = 0;
  
  	if (start & 511)
  		return -EINVAL;
  	if (len & 511)
  		return -EINVAL;
  	start >>= 9;
  	len >>= 9;
  
  	if (start + len > (bdev->bd_inode->i_size >> 9))
  		return -EINVAL;
  
  	if (!q->prepare_discard_fn)
  		return -EOPNOTSUPP;
  
  	while (len && !ret) {
  		DECLARE_COMPLETION_ONSTACK(wait);
  		struct bio *bio;
  
  		bio = bio_alloc(GFP_KERNEL, 0);
  		if (!bio)
  			return -ENOMEM;
  
  		bio->bi_end_io = blk_ioc_discard_endio;
  		bio->bi_bdev = bdev;
  		bio->bi_private = &wait;
  		bio->bi_sector = start;
  
  		if (len > q->max_hw_sectors) {
  			bio->bi_size = q->max_hw_sectors << 9;
  			len -= q->max_hw_sectors;
  			start += q->max_hw_sectors;
  		} else {
  			bio->bi_size = len << 9;
  			len = 0;
  		}
e17fc0a1c   David Woodhouse   Allow elevators t...
162
  		submit_bio(DISCARD_NOBARRIER, bio);
d30a2605b   David Woodhouse   Add BLKDISCARD io...
163
164
165
166
167
168
169
170
171
172
173
  
  		wait_for_completion(&wait);
  
  		if (bio_flagged(bio, BIO_EOPNOTSUPP))
  			ret = -EOPNOTSUPP;
  		else if (!bio_flagged(bio, BIO_UPTODATE))
  			ret = -EIO;
  		bio_put(bio);
  	}
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  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);
  }
  
  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...
198
199
200
201
202
  int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
  			unsigned cmd, unsigned long arg)
  {
  	struct gendisk *disk = bdev->bd_disk;
  	int ret;
d4430d62f   Al Viro   [PATCH] beginning...
203
204
205
  
  	if (disk->fops->ioctl)
  		return disk->fops->ioctl(bdev, mode, cmd, arg);
633a08b81   Al Viro   [PATCH] introduce...
206

d4430d62f   Al Viro   [PATCH] beginning...
207
  	if (disk->fops->locked_ioctl) {
633a08b81   Al Viro   [PATCH] introduce...
208
  		lock_kernel();
d4430d62f   Al Viro   [PATCH] beginning...
209
  		ret = disk->fops->locked_ioctl(bdev, mode, cmd, arg);
633a08b81   Al Viro   [PATCH] introduce...
210
211
212
213
214
215
216
217
218
219
220
221
  		unlock_kernel();
  		return ret;
  	}
  
  	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);
f58c4c0a1   Arnd Bergmann   compat_ioctl: mov...
222
223
224
225
  /*
   * always keep this in sync with compat_blkdev_ioctl() and
   * compat_blkdev_locked_ioctl()
   */
56b26add0   Al Viro   [PATCH] kill the ...
226
  int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
227
228
  			unsigned long arg)
  {
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
229
  	struct gendisk *disk = bdev->bd_disk;
45048d096   Al Viro   [PATCH] get rid o...
230
231
  	struct backing_dev_info *bdi;
  	loff_t size;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
232
233
234
  	int ret, n;
  
  	switch(cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
  	case BLKFLSBUF:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
238

e436fdae7   Al Viro   [PATCH] get rid o...
239
  		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
240
241
242
243
244
  		/* -EINVAL to handle old uncorrected drivers */
  		if (ret != -EINVAL && ret != -ENOTTY)
  			return ret;
  
  		lock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  		fsync_bdev(bdev);
f98393a64   Peter Zijlstra   mm: remove destro...
246
  		invalidate_bdev(bdev);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
247
  		unlock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  		return 0;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
249

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	case BLKROSET:
e436fdae7   Al Viro   [PATCH] get rid o...
251
  		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
252
253
254
  		/* -EINVAL to handle old uncorrected drivers */
  		if (ret != -EINVAL && ret != -ENOTTY)
  			return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		if (get_user(n, (int __user *)(arg)))
  			return -EFAULT;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
259
  		lock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  		set_device_ro(bdev, n);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
261
  		unlock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
  		return 0;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
263
264
265
  
  	case BLKDISCARD: {
  		uint64_t range[2];
e436fdae7   Al Viro   [PATCH] get rid o...
266
  		if (!(mode & FMODE_WRITE))
d30a2605b   David Woodhouse   Add BLKDISCARD io...
267
268
269
270
271
272
273
  			return -EBADF;
  
  		if (copy_from_user(range, (void __user *)arg, sizeof(range)))
  			return -EFAULT;
  
  		return blk_ioctl_discard(bdev, range[0], range[1]);
  	}
a885c8c43   Christoph Hellwig   [PATCH] Add block...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  	case HDIO_GETGEO: {
  		struct hd_geometry geo;
  
  		if (!arg)
  			return -EINVAL;
  		if (!disk->fops->getgeo)
  			return -ENOTTY;
  
  		/*
  		 * 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;
  		if (copy_to_user((struct hd_geometry __user *)arg, &geo,
  					sizeof(geo)))
  			return -EFAULT;
  		return 0;
  	}
45048d096   Al Viro   [PATCH] get rid o...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  	case BLKRAGET:
  	case BLKFRAGET:
  		if (!arg)
  			return -EINVAL;
  		bdi = blk_get_backing_dev_info(bdev);
  		if (bdi == NULL)
  			return -ENOTTY;
  		return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
  	case BLKROGET:
  		return put_int(arg, bdev_read_only(bdev) != 0);
  	case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */
  		return put_int(arg, block_size(bdev));
  	case BLKSSZGET: /* get block device hardware sector size */
  		return put_int(arg, bdev_hardsect_size(bdev));
  	case BLKSECTGET:
  		return put_ushort(arg, bdev_get_queue(bdev)->max_sectors);
  	case BLKRASET:
  	case BLKFRASET:
  		if(!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		bdi = blk_get_backing_dev_info(bdev);
  		if (bdi == NULL)
  			return -ENOTTY;
45048d096   Al Viro   [PATCH] get rid o...
318
  		bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
45048d096   Al Viro   [PATCH] get rid o...
319
320
321
322
323
324
325
326
327
  		return 0;
  	case BLKBSZSET:
  		/* set the logical block size */
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		if (!arg)
  			return -EINVAL;
  		if (get_user(n, (int __user *) arg))
  			return -EFAULT;
6af3a56e1   Al Viro   [PATCH] get rid o...
328
  		if (!(mode & FMODE_EXCL) && bd_claim(bdev, &bdev) < 0)
45048d096   Al Viro   [PATCH] get rid o...
329
330
  			return -EBUSY;
  		ret = set_blocksize(bdev, n);
6af3a56e1   Al Viro   [PATCH] get rid o...
331
332
  		if (!(mode & FMODE_EXCL))
  			bd_release(bdev);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
333
  		return ret;
45048d096   Al Viro   [PATCH] get rid o...
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
  	case BLKPG:
  		lock_kernel();
  		ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
  		unlock_kernel();
  		break;
  	case BLKRRPART:
  		lock_kernel();
  		ret = blkdev_reread_part(bdev);
  		unlock_kernel();
  		break;
  	case BLKGETSIZE:
  		size = bdev->bd_inode->i_size;
  		if ((size >> 9) > ~0UL)
  			return -EFBIG;
  		return put_ulong(arg, size >> 9);
  	case BLKGETSIZE64:
  		return put_u64(arg, bdev->bd_inode->i_size);
  	case BLKTRACESTART:
  	case BLKTRACESTOP:
  	case BLKTRACESETUP:
  	case BLKTRACETEARDOWN:
  		lock_kernel();
  		ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
  		unlock_kernel();
  		break;
  	default:
  		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
  	}
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  }
68f66feb3   Stephen Tweedie   [PATCH] Fix root ...
364
  EXPORT_SYMBOL_GPL(blkdev_ioctl);