Blame view

block/ioctl.c 8.5 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;
04ebd4aee   Abdel Benamrouche   block/ioctl.c and...
21
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
  
  	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...
32
  	partno = p.pno;
540eed563   Tejun Heo   block: make parti...
33
  	if (partno <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
45
46
  		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...
47

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

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

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

6d740cd5b   Peter Zijlstra   [PATCH] lockdep: ...
87
  			mutex_lock_nested(&bdev->bd_mutex, 1);
cf771cb5a   Tejun Heo   block: make varia...
88
  			delete_partition(disk, partno);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
89
90
  			mutex_unlock(&bdev->bd_mutex);
  			mutex_unlock(&bdevp->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
97
98
99
100
101
102
  			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 ...
103
  	if (!disk_partitionable(disk) || bdev != bdev->bd_contains)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
  		return -EINVAL;
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
107
  	if (!mutex_trylock(&bdev->bd_mutex))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  		return -EBUSY;
  	res = rescan_partitions(disk, bdev);
c039e3134   Arjan van de Ven   [PATCH] sem2mutex...
110
  	mutex_unlock(&bdev->bd_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
  	return res;
  }
d30a2605b   David Woodhouse   Add BLKDISCARD io...
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
162
  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...
163
  		submit_bio(DISCARD_NOBARRIER, bio);
d30a2605b   David Woodhouse   Add BLKDISCARD io...
164
165
166
167
168
169
170
171
172
173
174
  
  		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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  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);
  }
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
199
200
  static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev,
  				unsigned cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  	struct backing_dev_info *bdi;
  	int ret, n;
  
  	switch (cmd) {
  	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;
  		bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
  		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;
  		if (bd_claim(bdev, file) < 0)
  			return -EBUSY;
  		ret = set_blocksize(bdev, n);
  		bd_release(bdev);
  		return ret;
  	case BLKPG:
  		return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
  	case BLKRRPART:
  		return blkdev_reread_part(bdev);
  	case BLKGETSIZE:
  		if ((bdev->bd_inode->i_size >> 9) > ~0UL)
  			return -EFBIG;
  		return put_ulong(arg, bdev->bd_inode->i_size >> 9);
  	case BLKGETSIZE64:
  		return put_u64(arg, bdev->bd_inode->i_size);
2056a782f   Jens Axboe   [PATCH] Block que...
254
255
256
257
258
  	case BLKTRACESTART:
  	case BLKTRACESTOP:
  	case BLKTRACESETUP:
  	case BLKTRACETEARDOWN:
  		return blk_trace_ioctl(bdev, cmd, (char __user *) arg);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
259
260
261
  	}
  	return -ENOIOCTLCMD;
  }
7006f6eca   Alasdair G Kergon   [PATCH] dm: expor...
262
263
  int blkdev_driver_ioctl(struct inode *inode, struct file *file,
  			struct gendisk *disk, unsigned cmd, unsigned long arg)
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  {
  	int ret;
  	if (disk->fops->unlocked_ioctl)
  		return disk->fops->unlocked_ioctl(file, cmd, arg);
  
  	if (disk->fops->ioctl) {
  		lock_kernel();
  		ret = disk->fops->ioctl(inode, file, cmd, arg);
  		unlock_kernel();
  		return ret;
  	}
  
  	return -ENOTTY;
  }
7006f6eca   Alasdair G Kergon   [PATCH] dm: expor...
278
  EXPORT_SYMBOL_GPL(blkdev_driver_ioctl);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
279

f58c4c0a1   Arnd Bergmann   compat_ioctl: mov...
280
281
282
283
  /*
   * always keep this in sync with compat_blkdev_ioctl() and
   * compat_blkdev_locked_ioctl()
   */
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
284
285
286
287
288
289
290
291
  int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
  			unsigned long arg)
  {
  	struct block_device *bdev = inode->i_bdev;
  	struct gendisk *disk = bdev->bd_disk;
  	int ret, n;
  
  	switch(cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
  	case BLKFLSBUF:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
295
296
297
298
299
300
301
  
  		ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
  		/* -EINVAL to handle old uncorrected drivers */
  		if (ret != -EINVAL && ret != -ENOTTY)
  			return ret;
  
  		lock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  		fsync_bdev(bdev);
f98393a64   Peter Zijlstra   mm: remove destro...
303
  		invalidate_bdev(bdev);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
304
  		unlock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  		return 0;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
306

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	case BLKROSET:
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
308
309
310
311
  		ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
  		/* -EINVAL to handle old uncorrected drivers */
  		if (ret != -EINVAL && ret != -ENOTTY)
  			return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		if (get_user(n, (int __user *)(arg)))
  			return -EFAULT;
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
316
  		lock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  		set_device_ro(bdev, n);
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
318
  		unlock_kernel();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  		return 0;
d30a2605b   David Woodhouse   Add BLKDISCARD io...
320
321
322
323
324
325
326
327
328
329
330
331
  
  	case BLKDISCARD: {
  		uint64_t range[2];
  
  		if (!(file->f_mode & FMODE_WRITE))
  			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...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  	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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	}
bb93e3a52   Arnd Bergmann   [PATCH] block: ad...
354
355
356
357
358
359
360
361
  
  	lock_kernel();
  	ret = blkdev_locked_ioctl(file, bdev, cmd, arg);
  	unlock_kernel();
  	if (ret != -ENOIOCTLCMD)
  		return ret;
  
  	return blkdev_driver_ioctl(inode, file, disk, cmd, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  }
68f66feb3   Stephen Tweedie   [PATCH] Fix root ...
363
  EXPORT_SYMBOL_GPL(blkdev_ioctl);