Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/block/ioctl.c
15109 views
1
#include <linux/capability.h>
2
#include <linux/blkdev.h>
3
#include <linux/gfp.h>
4
#include <linux/blkpg.h>
5
#include <linux/hdreg.h>
6
#include <linux/backing-dev.h>
7
#include <linux/buffer_head.h>
8
#include <linux/blktrace_api.h>
9
#include <asm/uaccess.h>
10
11
static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
12
{
13
struct block_device *bdevp;
14
struct gendisk *disk;
15
struct hd_struct *part;
16
struct blkpg_ioctl_arg a;
17
struct blkpg_partition p;
18
struct disk_part_iter piter;
19
long long start, length;
20
int partno;
21
22
if (!capable(CAP_SYS_ADMIN))
23
return -EACCES;
24
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
25
return -EFAULT;
26
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
27
return -EFAULT;
28
disk = bdev->bd_disk;
29
if (bdev != bdev->bd_contains)
30
return -EINVAL;
31
partno = p.pno;
32
if (partno <= 0)
33
return -EINVAL;
34
switch (a.op) {
35
case BLKPG_ADD_PARTITION:
36
start = p.start >> 9;
37
length = p.length >> 9;
38
/* check for fit in a hd_struct */
39
if (sizeof(sector_t) == sizeof(long) &&
40
sizeof(long long) > sizeof(long)) {
41
long pstart = start, plength = length;
42
if (pstart != start || plength != length
43
|| pstart < 0 || plength < 0)
44
return -EINVAL;
45
}
46
47
mutex_lock(&bdev->bd_mutex);
48
49
/* overlap? */
50
disk_part_iter_init(&piter, disk,
51
DISK_PITER_INCL_EMPTY);
52
while ((part = disk_part_iter_next(&piter))) {
53
if (!(start + length <= part->start_sect ||
54
start >= part->start_sect + part->nr_sects)) {
55
disk_part_iter_exit(&piter);
56
mutex_unlock(&bdev->bd_mutex);
57
return -EBUSY;
58
}
59
}
60
disk_part_iter_exit(&piter);
61
62
/* all seems OK */
63
part = add_partition(disk, partno, start, length,
64
ADDPART_FLAG_NONE, NULL);
65
mutex_unlock(&bdev->bd_mutex);
66
return IS_ERR(part) ? PTR_ERR(part) : 0;
67
case BLKPG_DEL_PARTITION:
68
part = disk_get_part(disk, partno);
69
if (!part)
70
return -ENXIO;
71
72
bdevp = bdget(part_devt(part));
73
disk_put_part(part);
74
if (!bdevp)
75
return -ENOMEM;
76
77
mutex_lock(&bdevp->bd_mutex);
78
if (bdevp->bd_openers) {
79
mutex_unlock(&bdevp->bd_mutex);
80
bdput(bdevp);
81
return -EBUSY;
82
}
83
/* all seems OK */
84
fsync_bdev(bdevp);
85
invalidate_bdev(bdevp);
86
87
mutex_lock_nested(&bdev->bd_mutex, 1);
88
delete_partition(disk, partno);
89
mutex_unlock(&bdev->bd_mutex);
90
mutex_unlock(&bdevp->bd_mutex);
91
bdput(bdevp);
92
93
return 0;
94
default:
95
return -EINVAL;
96
}
97
}
98
99
static int blkdev_reread_part(struct block_device *bdev)
100
{
101
struct gendisk *disk = bdev->bd_disk;
102
int res;
103
104
if (!disk_partitionable(disk) || bdev != bdev->bd_contains)
105
return -EINVAL;
106
if (!capable(CAP_SYS_ADMIN))
107
return -EACCES;
108
if (!mutex_trylock(&bdev->bd_mutex))
109
return -EBUSY;
110
res = rescan_partitions(disk, bdev);
111
mutex_unlock(&bdev->bd_mutex);
112
return res;
113
}
114
115
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
116
uint64_t len, int secure)
117
{
118
unsigned long flags = 0;
119
120
if (start & 511)
121
return -EINVAL;
122
if (len & 511)
123
return -EINVAL;
124
start >>= 9;
125
len >>= 9;
126
127
if (start + len > (i_size_read(bdev->bd_inode) >> 9))
128
return -EINVAL;
129
if (secure)
130
flags |= BLKDEV_DISCARD_SECURE;
131
return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
132
}
133
134
static int put_ushort(unsigned long arg, unsigned short val)
135
{
136
return put_user(val, (unsigned short __user *)arg);
137
}
138
139
static int put_int(unsigned long arg, int val)
140
{
141
return put_user(val, (int __user *)arg);
142
}
143
144
static int put_uint(unsigned long arg, unsigned int val)
145
{
146
return put_user(val, (unsigned int __user *)arg);
147
}
148
149
static int put_long(unsigned long arg, long val)
150
{
151
return put_user(val, (long __user *)arg);
152
}
153
154
static int put_ulong(unsigned long arg, unsigned long val)
155
{
156
return put_user(val, (unsigned long __user *)arg);
157
}
158
159
static int put_u64(unsigned long arg, u64 val)
160
{
161
return put_user(val, (u64 __user *)arg);
162
}
163
164
int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
165
unsigned cmd, unsigned long arg)
166
{
167
struct gendisk *disk = bdev->bd_disk;
168
169
if (disk->fops->ioctl)
170
return disk->fops->ioctl(bdev, mode, cmd, arg);
171
172
return -ENOTTY;
173
}
174
/*
175
* For the record: _GPL here is only because somebody decided to slap it
176
* on the previous export. Sheer idiocy, since it wasn't copyrightable
177
* at all and could be open-coded without any exports by anybody who cares.
178
*/
179
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
180
181
/*
182
* always keep this in sync with compat_blkdev_ioctl()
183
*/
184
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
185
unsigned long arg)
186
{
187
struct gendisk *disk = bdev->bd_disk;
188
struct backing_dev_info *bdi;
189
loff_t size;
190
int ret, n;
191
192
switch(cmd) {
193
case BLKFLSBUF:
194
if (!capable(CAP_SYS_ADMIN))
195
return -EACCES;
196
197
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
198
/* -EINVAL to handle old uncorrected drivers */
199
if (ret != -EINVAL && ret != -ENOTTY)
200
return ret;
201
202
fsync_bdev(bdev);
203
invalidate_bdev(bdev);
204
return 0;
205
206
case BLKROSET:
207
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
208
/* -EINVAL to handle old uncorrected drivers */
209
if (ret != -EINVAL && ret != -ENOTTY)
210
return ret;
211
if (!capable(CAP_SYS_ADMIN))
212
return -EACCES;
213
if (get_user(n, (int __user *)(arg)))
214
return -EFAULT;
215
set_device_ro(bdev, n);
216
return 0;
217
218
case BLKDISCARD:
219
case BLKSECDISCARD: {
220
uint64_t range[2];
221
222
if (!(mode & FMODE_WRITE))
223
return -EBADF;
224
225
if (copy_from_user(range, (void __user *)arg, sizeof(range)))
226
return -EFAULT;
227
228
return blk_ioctl_discard(bdev, range[0], range[1],
229
cmd == BLKSECDISCARD);
230
}
231
232
case HDIO_GETGEO: {
233
struct hd_geometry geo;
234
235
if (!arg)
236
return -EINVAL;
237
if (!disk->fops->getgeo)
238
return -ENOTTY;
239
240
/*
241
* We need to set the startsect first, the driver may
242
* want to override it.
243
*/
244
memset(&geo, 0, sizeof(geo));
245
geo.start = get_start_sect(bdev);
246
ret = disk->fops->getgeo(bdev, &geo);
247
if (ret)
248
return ret;
249
if (copy_to_user((struct hd_geometry __user *)arg, &geo,
250
sizeof(geo)))
251
return -EFAULT;
252
return 0;
253
}
254
case BLKRAGET:
255
case BLKFRAGET:
256
if (!arg)
257
return -EINVAL;
258
bdi = blk_get_backing_dev_info(bdev);
259
if (bdi == NULL)
260
return -ENOTTY;
261
return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
262
case BLKROGET:
263
return put_int(arg, bdev_read_only(bdev) != 0);
264
case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */
265
return put_int(arg, block_size(bdev));
266
case BLKSSZGET: /* get block device logical block size */
267
return put_int(arg, bdev_logical_block_size(bdev));
268
case BLKPBSZGET: /* get block device physical block size */
269
return put_uint(arg, bdev_physical_block_size(bdev));
270
case BLKIOMIN:
271
return put_uint(arg, bdev_io_min(bdev));
272
case BLKIOOPT:
273
return put_uint(arg, bdev_io_opt(bdev));
274
case BLKALIGNOFF:
275
return put_int(arg, bdev_alignment_offset(bdev));
276
case BLKDISCARDZEROES:
277
return put_uint(arg, bdev_discard_zeroes_data(bdev));
278
case BLKSECTGET:
279
return put_ushort(arg, queue_max_sectors(bdev_get_queue(bdev)));
280
case BLKRASET:
281
case BLKFRASET:
282
if(!capable(CAP_SYS_ADMIN))
283
return -EACCES;
284
bdi = blk_get_backing_dev_info(bdev);
285
if (bdi == NULL)
286
return -ENOTTY;
287
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
288
return 0;
289
case BLKBSZSET:
290
/* set the logical block size */
291
if (!capable(CAP_SYS_ADMIN))
292
return -EACCES;
293
if (!arg)
294
return -EINVAL;
295
if (get_user(n, (int __user *) arg))
296
return -EFAULT;
297
if (!(mode & FMODE_EXCL)) {
298
bdgrab(bdev);
299
if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
300
return -EBUSY;
301
}
302
ret = set_blocksize(bdev, n);
303
if (!(mode & FMODE_EXCL))
304
blkdev_put(bdev, mode | FMODE_EXCL);
305
return ret;
306
case BLKPG:
307
ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
308
break;
309
case BLKRRPART:
310
ret = blkdev_reread_part(bdev);
311
break;
312
case BLKGETSIZE:
313
size = i_size_read(bdev->bd_inode);
314
if ((size >> 9) > ~0UL)
315
return -EFBIG;
316
return put_ulong(arg, size >> 9);
317
case BLKGETSIZE64:
318
return put_u64(arg, i_size_read(bdev->bd_inode));
319
case BLKTRACESTART:
320
case BLKTRACESTOP:
321
case BLKTRACESETUP:
322
case BLKTRACETEARDOWN:
323
ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
324
break;
325
default:
326
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
327
}
328
return ret;
329
}
330
EXPORT_SYMBOL_GPL(blkdev_ioctl);
331
332