]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - block/compat_ioctl.c
compat_ioctl: move BLKPG handling to block/compat_ioctl.c
[mirror_ubuntu-bionic-kernel.git] / block / compat_ioctl.c
CommitLineData
f58c4c0a
AB
1#include <linux/blkdev.h>
2#include <linux/blkpg.h>
3#include <linux/blktrace_api.h>
4#include <linux/cdrom.h>
5#include <linux/compat.h>
6#include <linux/elevator.h>
7#include <linux/fd.h>
8#include <linux/hdreg.h>
9#include <linux/syscalls.h>
10#include <linux/smp_lock.h>
11#include <linux/types.h>
12#include <linux/uaccess.h>
13
14static int compat_put_ushort(unsigned long arg, unsigned short val)
15{
16 return put_user(val, (unsigned short __user *)compat_ptr(arg));
17}
18
19static int compat_put_int(unsigned long arg, int val)
20{
21 return put_user(val, (compat_int_t __user *)compat_ptr(arg));
22}
23
24static int compat_put_long(unsigned long arg, long val)
25{
26 return put_user(val, (compat_long_t __user *)compat_ptr(arg));
27}
28
29static int compat_put_ulong(unsigned long arg, compat_ulong_t val)
30{
31 return put_user(val, (compat_ulong_t __user *)compat_ptr(arg));
32}
33
34static int compat_put_u64(unsigned long arg, u64 val)
35{
36 return put_user(val, (compat_u64 __user *)compat_ptr(arg));
37}
38
9617db08
AB
39struct compat_hd_geometry {
40 unsigned char heads;
41 unsigned char sectors;
42 unsigned short cylinders;
43 u32 start;
44};
45
46static int compat_hdio_getgeo(struct gendisk *disk, struct block_device *bdev,
47 struct compat_hd_geometry __user *ugeo)
48{
49 struct hd_geometry geo;
50 int ret;
51
52 if (!ugeo)
53 return -EINVAL;
54 if (!disk->fops->getgeo)
55 return -ENOTTY;
56
57 /*
58 * We need to set the startsect first, the driver may
59 * want to override it.
60 */
61 geo.start = get_start_sect(bdev);
62 ret = disk->fops->getgeo(bdev, &geo);
63 if (ret)
64 return ret;
65
66 ret = copy_to_user(ugeo, &geo, 4);
67 ret |= __put_user(geo.start, &ugeo->start);
68 if (ret)
69 ret = -EFAULT;
70
71 return ret;
72}
73
74static int compat_hdio_ioctl(struct inode *inode, struct file *file,
75 struct gendisk *disk, unsigned int cmd, unsigned long arg)
76{
77 mm_segment_t old_fs = get_fs();
78 unsigned long kval;
79 unsigned int __user *uvp;
80 int error;
81
82 set_fs(KERNEL_DS);
83 error = blkdev_driver_ioctl(inode, file, disk,
84 cmd, (unsigned long)(&kval));
85 set_fs(old_fs);
86
87 if (error == 0) {
88 uvp = compat_ptr(arg);
89 if (put_user(kval, uvp))
90 error = -EFAULT;
91 }
92 return error;
93}
94
18cf7f87
AB
95struct compat_blkpg_ioctl_arg {
96 compat_int_t op;
97 compat_int_t flags;
98 compat_int_t datalen;
99 compat_caddr_t data;
100};
101
102static int compat_blkpg_ioctl(struct inode *inode, struct file *file,
103 unsigned int cmd, struct compat_blkpg_ioctl_arg __user *ua32)
104{
105 struct blkpg_ioctl_arg __user *a = compat_alloc_user_space(sizeof(*a));
106 compat_caddr_t udata;
107 compat_int_t n;
108 int err;
109
110 err = get_user(n, &ua32->op);
111 err |= put_user(n, &a->op);
112 err |= get_user(n, &ua32->flags);
113 err |= put_user(n, &a->flags);
114 err |= get_user(n, &ua32->datalen);
115 err |= put_user(n, &a->datalen);
116 err |= get_user(udata, &ua32->data);
117 err |= put_user(compat_ptr(udata), &a->data);
118 if (err)
119 return err;
120
121 return blkdev_ioctl(inode, file, cmd, (unsigned long)a);
122}
123
f58c4c0a
AB
124#define BLKBSZGET_32 _IOR(0x12, 112, int)
125#define BLKBSZSET_32 _IOW(0x12, 113, int)
126#define BLKGETSIZE64_32 _IOR(0x12, 114, int)
127
171044d4
AB
128struct compat_blk_user_trace_setup {
129 char name[32];
130 u16 act_mask;
131 u32 buf_size;
132 u32 buf_nr;
133 compat_u64 start_lba;
134 compat_u64 end_lba;
135 u32 pid;
136};
137#define BLKTRACESETUP32 _IOWR(0x12, 115, struct compat_blk_user_trace_setup)
138
139static int compat_blk_trace_setup(struct block_device *bdev, char __user *arg)
140{
141 struct blk_user_trace_setup buts;
142 struct compat_blk_user_trace_setup cbuts;
143 struct request_queue *q;
144 int ret;
145
146 q = bdev_get_queue(bdev);
147 if (!q)
148 return -ENXIO;
149
150 if (copy_from_user(&cbuts, arg, sizeof(cbuts)))
151 return -EFAULT;
152
153 buts = (struct blk_user_trace_setup) {
154 .act_mask = cbuts.act_mask,
155 .buf_size = cbuts.buf_size,
156 .buf_nr = cbuts.buf_nr,
157 .start_lba = cbuts.start_lba,
158 .end_lba = cbuts.end_lba,
159 .pid = cbuts.pid,
160 };
161 memcpy(&buts.name, &cbuts.name, 32);
162
163 mutex_lock(&bdev->bd_mutex);
164 ret = do_blk_trace_setup(q, bdev, &buts);
165 mutex_unlock(&bdev->bd_mutex);
166 if (ret)
167 return ret;
168
169 if (copy_to_user(arg, &buts.name, 32))
170 return -EFAULT;
171
172 return 0;
173}
174
7199d4cd
AB
175static int compat_blkdev_driver_ioctl(struct inode *inode, struct file *file,
176 struct gendisk *disk, unsigned cmd, unsigned long arg)
177{
178 int ret;
179
180 switch (arg) {
9617db08
AB
181 case HDIO_GET_UNMASKINTR:
182 case HDIO_GET_MULTCOUNT:
183 case HDIO_GET_KEEPSETTINGS:
184 case HDIO_GET_32BIT:
185 case HDIO_GET_NOWERR:
186 case HDIO_GET_DMA:
187 case HDIO_GET_NICE:
188 case HDIO_GET_WCACHE:
189 case HDIO_GET_ACOUSTIC:
190 case HDIO_GET_ADDRESS:
191 case HDIO_GET_BUSSTATE:
192 return compat_hdio_ioctl(inode, file, disk, cmd, arg);
7199d4cd
AB
193 /*
194 * No handler required for the ones below, we just need to
195 * convert arg to a 64 bit pointer.
196 */
197 case BLKSECTSET:
198 /*
199 * 0x03 -- HD/IDE ioctl's used by hdparm and friends.
200 * Some need translations, these do not.
201 */
202 case HDIO_GET_IDENTITY:
203 case HDIO_DRIVE_TASK:
204 case HDIO_DRIVE_CMD:
205 case HDIO_SCAN_HWIF:
206 /* 0x330 is reserved -- it used to be HDIO_GETGEO_BIG */
207 case 0x330:
208 /* 0x02 -- Floppy ioctls */
209 case FDMSGON:
210 case FDMSGOFF:
211 case FDSETEMSGTRESH:
212 case FDFLUSH:
213 case FDWERRORCLR:
214 case FDSETMAXERRS:
215 case FDGETMAXERRS:
216 case FDGETDRVTYP:
217 case FDEJECT:
218 case FDCLRPRM:
219 case FDFMTBEG:
220 case FDFMTEND:
221 case FDRESET:
222 case FDTWADDLE:
223 case FDFMTTRK:
224 case FDRAWCMD:
225 /* CDROM stuff */
226 case CDROMPAUSE:
227 case CDROMRESUME:
228 case CDROMPLAYMSF:
229 case CDROMPLAYTRKIND:
230 case CDROMREADTOCHDR:
231 case CDROMREADTOCENTRY:
232 case CDROMSTOP:
233 case CDROMSTART:
234 case CDROMEJECT:
235 case CDROMVOLCTRL:
236 case CDROMSUBCHNL:
237 case CDROMMULTISESSION:
238 case CDROM_GET_MCN:
239 case CDROMRESET:
240 case CDROMVOLREAD:
241 case CDROMSEEK:
242 case CDROMPLAYBLK:
243 case CDROMCLOSETRAY:
244 case CDROM_DISC_STATUS:
245 case CDROM_CHANGER_NSLOTS:
246 case CDROM_GET_CAPABILITY:
247 /* Ignore cdrom.h about these next 5 ioctls, they absolutely do
248 * not take a struct cdrom_read, instead they take a struct cdrom_msf
249 * which is compatible.
250 */
251 case CDROMREADMODE2:
252 case CDROMREADMODE1:
253 case CDROMREADRAW:
254 case CDROMREADCOOKED:
255 case CDROMREADALL:
256 /* DVD ioctls */
257 case DVD_READ_STRUCT:
258 case DVD_WRITE_STRUCT:
259 case DVD_AUTH:
260 arg = (unsigned long)compat_ptr(arg);
261 /* These intepret arg as an unsigned long, not as a pointer,
262 * so we must not do compat_ptr() conversion. */
263 case HDIO_SET_MULTCOUNT:
264 case HDIO_SET_UNMASKINTR:
265 case HDIO_SET_KEEPSETTINGS:
266 case HDIO_SET_32BIT:
267 case HDIO_SET_NOWERR:
268 case HDIO_SET_DMA:
269 case HDIO_SET_PIO_MODE:
270 case HDIO_SET_NICE:
271 case HDIO_SET_WCACHE:
272 case HDIO_SET_ACOUSTIC:
273 case HDIO_SET_BUSSTATE:
274 case HDIO_SET_ADDRESS:
275 case CDROMEJECT_SW:
276 case CDROM_SET_OPTIONS:
277 case CDROM_CLEAR_OPTIONS:
278 case CDROM_SELECT_SPEED:
279 case CDROM_SELECT_DISC:
280 case CDROM_MEDIA_CHANGED:
281 case CDROM_DRIVE_STATUS:
282 case CDROM_LOCKDOOR:
283 case CDROM_DEBUG:
284 break;
285 default:
286 /* unknown ioctl number */
287 return -ENOIOCTLCMD;
288 }
289
290 if (disk->fops->unlocked_ioctl)
291 return disk->fops->unlocked_ioctl(file, cmd, arg);
292
293 if (disk->fops->ioctl) {
294 lock_kernel();
295 ret = disk->fops->ioctl(inode, file, cmd, arg);
296 unlock_kernel();
297 return ret;
298 }
299
300 return -ENOTTY;
301}
302
f58c4c0a
AB
303static int compat_blkdev_locked_ioctl(struct inode *inode, struct file *file,
304 struct block_device *bdev,
305 unsigned cmd, unsigned long arg)
306{
307 struct backing_dev_info *bdi;
308
309 switch (cmd) {
310 case BLKRAGET:
311 case BLKFRAGET:
312 if (!arg)
313 return -EINVAL;
314 bdi = blk_get_backing_dev_info(bdev);
315 if (bdi == NULL)
316 return -ENOTTY;
317 return compat_put_long(arg,
318 (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
319 case BLKROGET: /* compatible */
320 return compat_put_int(arg, bdev_read_only(bdev) != 0);
321 case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */
322 return compat_put_int(arg, block_size(bdev));
323 case BLKSSZGET: /* get block device hardware sector size */
324 return compat_put_int(arg, bdev_hardsect_size(bdev));
325 case BLKSECTGET:
326 return compat_put_ushort(arg,
327 bdev_get_queue(bdev)->max_sectors);
328 case BLKRASET: /* compatible, but no compat_ptr (!) */
329 case BLKFRASET:
330 if (!capable(CAP_SYS_ADMIN))
331 return -EACCES;
332 bdi = blk_get_backing_dev_info(bdev);
333 if (bdi == NULL)
334 return -ENOTTY;
335 bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
336 return 0;
337 case BLKGETSIZE:
338 if ((bdev->bd_inode->i_size >> 9) > ~0UL)
339 return -EFBIG;
340 return compat_put_ulong(arg, bdev->bd_inode->i_size >> 9);
341
342 case BLKGETSIZE64_32:
343 return compat_put_u64(arg, bdev->bd_inode->i_size);
171044d4
AB
344
345 case BLKTRACESETUP32:
346 return compat_blk_trace_setup(bdev, compat_ptr(arg));
347 case BLKTRACESTART: /* compatible */
348 case BLKTRACESTOP: /* compatible */
349 case BLKTRACETEARDOWN: /* compatible */
350 return blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
f58c4c0a
AB
351 }
352 return -ENOIOCTLCMD;
353}
354
355/* Most of the generic ioctls are handled in the normal fallback path.
356 This assumes the blkdev's low level compat_ioctl always returns
357 ENOIOCTLCMD for unknown ioctls. */
358long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
359{
360 int ret = -ENOIOCTLCMD;
361 struct inode *inode = file->f_mapping->host;
362 struct block_device *bdev = inode->i_bdev;
363 struct gendisk *disk = bdev->bd_disk;
364
365 switch (cmd) {
9617db08
AB
366 case HDIO_GETGEO:
367 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
f58c4c0a
AB
368 case BLKFLSBUF:
369 case BLKROSET:
370 /*
371 * the ones below are implemented in blkdev_locked_ioctl,
372 * but we call blkdev_ioctl, which gets the lock for us
373 */
374 case BLKRRPART:
375 return blkdev_ioctl(inode, file, cmd,
376 (unsigned long)compat_ptr(arg));
377 case BLKBSZSET_32:
378 return blkdev_ioctl(inode, file, BLKBSZSET,
379 (unsigned long)compat_ptr(arg));
18cf7f87
AB
380 case BLKPG:
381 return compat_blkpg_ioctl(inode, file, cmd, compat_ptr(arg));
f58c4c0a
AB
382 }
383
384 lock_kernel();
385 ret = compat_blkdev_locked_ioctl(inode, file, bdev, cmd, arg);
386 /* FIXME: why do we assume -> compat_ioctl needs the BKL? */
387 if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl)
388 ret = disk->fops->compat_ioctl(file, cmd, arg);
389 unlock_kernel();
390
7199d4cd
AB
391 if (ret != -ENOIOCTLCMD)
392 return ret;
393
394 return compat_blkdev_driver_ioctl(inode, file, disk, cmd, arg);
f58c4c0a 395}