compat_ioctl: move cdrom handlers to block/compat_ioctl.c
[linux-block.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
b3087cc4
AB
95struct compat_cdrom_read_audio {
96 union cdrom_addr addr;
97 u8 addr_format;
98 compat_int_t nframes;
99 compat_caddr_t buf;
100};
101
102struct compat_cdrom_generic_command {
103 unsigned char cmd[CDROM_PACKET_SIZE];
104 compat_caddr_t buffer;
105 compat_uint_t buflen;
106 compat_int_t stat;
107 compat_caddr_t sense;
108 unsigned char data_direction;
109 compat_int_t quiet;
110 compat_int_t timeout;
111 compat_caddr_t reserved[1];
112};
113
114static int compat_cdrom_read_audio(struct inode *inode, struct file *file,
115 struct gendisk *disk, unsigned int cmd, unsigned long arg)
116{
117 struct cdrom_read_audio __user *cdread_audio;
118 struct compat_cdrom_read_audio __user *cdread_audio32;
119 __u32 data;
120 void __user *datap;
121
122 cdread_audio = compat_alloc_user_space(sizeof(*cdread_audio));
123 cdread_audio32 = compat_ptr(arg);
124
125 if (copy_in_user(&cdread_audio->addr,
126 &cdread_audio32->addr,
127 (sizeof(*cdread_audio32) -
128 sizeof(compat_caddr_t))))
129 return -EFAULT;
130
131 if (get_user(data, &cdread_audio32->buf))
132 return -EFAULT;
133 datap = compat_ptr(data);
134 if (put_user(datap, &cdread_audio->buf))
135 return -EFAULT;
136
137 return blkdev_driver_ioctl(inode, file, disk, cmd,
138 (unsigned long)cdread_audio);
139}
140
141static int compat_cdrom_generic_command(struct inode *inode, struct file *file,
142 struct gendisk *disk, unsigned int cmd, unsigned long arg)
143{
144 struct cdrom_generic_command __user *cgc;
145 struct compat_cdrom_generic_command __user *cgc32;
146 u32 data;
147 unsigned char dir;
148 int itmp;
149
150 cgc = compat_alloc_user_space(sizeof(*cgc));
151 cgc32 = compat_ptr(arg);
152
153 if (copy_in_user(&cgc->cmd, &cgc32->cmd, sizeof(cgc->cmd)) ||
154 get_user(data, &cgc32->buffer) ||
155 put_user(compat_ptr(data), &cgc->buffer) ||
156 copy_in_user(&cgc->buflen, &cgc32->buflen,
157 (sizeof(unsigned int) + sizeof(int))) ||
158 get_user(data, &cgc32->sense) ||
159 put_user(compat_ptr(data), &cgc->sense) ||
160 get_user(dir, &cgc32->data_direction) ||
161 put_user(dir, &cgc->data_direction) ||
162 get_user(itmp, &cgc32->quiet) ||
163 put_user(itmp, &cgc->quiet) ||
164 get_user(itmp, &cgc32->timeout) ||
165 put_user(itmp, &cgc->timeout) ||
166 get_user(data, &cgc32->reserved[0]) ||
167 put_user(compat_ptr(data), &cgc->reserved[0]))
168 return -EFAULT;
169
170 return blkdev_driver_ioctl(inode, file, disk, cmd, (unsigned long)cgc);
171}
172
18cf7f87
AB
173struct compat_blkpg_ioctl_arg {
174 compat_int_t op;
175 compat_int_t flags;
176 compat_int_t datalen;
177 compat_caddr_t data;
178};
179
180static int compat_blkpg_ioctl(struct inode *inode, struct file *file,
181 unsigned int cmd, struct compat_blkpg_ioctl_arg __user *ua32)
182{
183 struct blkpg_ioctl_arg __user *a = compat_alloc_user_space(sizeof(*a));
184 compat_caddr_t udata;
185 compat_int_t n;
186 int err;
187
188 err = get_user(n, &ua32->op);
189 err |= put_user(n, &a->op);
190 err |= get_user(n, &ua32->flags);
191 err |= put_user(n, &a->flags);
192 err |= get_user(n, &ua32->datalen);
193 err |= put_user(n, &a->datalen);
194 err |= get_user(udata, &ua32->data);
195 err |= put_user(compat_ptr(udata), &a->data);
196 if (err)
197 return err;
198
199 return blkdev_ioctl(inode, file, cmd, (unsigned long)a);
200}
201
f58c4c0a
AB
202#define BLKBSZGET_32 _IOR(0x12, 112, int)
203#define BLKBSZSET_32 _IOW(0x12, 113, int)
204#define BLKGETSIZE64_32 _IOR(0x12, 114, int)
205
171044d4
AB
206struct compat_blk_user_trace_setup {
207 char name[32];
208 u16 act_mask;
209 u32 buf_size;
210 u32 buf_nr;
211 compat_u64 start_lba;
212 compat_u64 end_lba;
213 u32 pid;
214};
215#define BLKTRACESETUP32 _IOWR(0x12, 115, struct compat_blk_user_trace_setup)
216
217static int compat_blk_trace_setup(struct block_device *bdev, char __user *arg)
218{
219 struct blk_user_trace_setup buts;
220 struct compat_blk_user_trace_setup cbuts;
221 struct request_queue *q;
222 int ret;
223
224 q = bdev_get_queue(bdev);
225 if (!q)
226 return -ENXIO;
227
228 if (copy_from_user(&cbuts, arg, sizeof(cbuts)))
229 return -EFAULT;
230
231 buts = (struct blk_user_trace_setup) {
232 .act_mask = cbuts.act_mask,
233 .buf_size = cbuts.buf_size,
234 .buf_nr = cbuts.buf_nr,
235 .start_lba = cbuts.start_lba,
236 .end_lba = cbuts.end_lba,
237 .pid = cbuts.pid,
238 };
239 memcpy(&buts.name, &cbuts.name, 32);
240
241 mutex_lock(&bdev->bd_mutex);
242 ret = do_blk_trace_setup(q, bdev, &buts);
243 mutex_unlock(&bdev->bd_mutex);
244 if (ret)
245 return ret;
246
247 if (copy_to_user(arg, &buts.name, 32))
248 return -EFAULT;
249
250 return 0;
251}
252
7199d4cd
AB
253static int compat_blkdev_driver_ioctl(struct inode *inode, struct file *file,
254 struct gendisk *disk, unsigned cmd, unsigned long arg)
255{
256 int ret;
257
258 switch (arg) {
9617db08
AB
259 case HDIO_GET_UNMASKINTR:
260 case HDIO_GET_MULTCOUNT:
261 case HDIO_GET_KEEPSETTINGS:
262 case HDIO_GET_32BIT:
263 case HDIO_GET_NOWERR:
264 case HDIO_GET_DMA:
265 case HDIO_GET_NICE:
266 case HDIO_GET_WCACHE:
267 case HDIO_GET_ACOUSTIC:
268 case HDIO_GET_ADDRESS:
269 case HDIO_GET_BUSSTATE:
270 return compat_hdio_ioctl(inode, file, disk, cmd, arg);
b3087cc4
AB
271 case CDROMREADAUDIO:
272 return compat_cdrom_read_audio(inode, file, disk, cmd, arg);
273 case CDROM_SEND_PACKET:
274 return compat_cdrom_generic_command(inode, file, disk, cmd, arg);
275
7199d4cd
AB
276 /*
277 * No handler required for the ones below, we just need to
278 * convert arg to a 64 bit pointer.
279 */
280 case BLKSECTSET:
281 /*
282 * 0x03 -- HD/IDE ioctl's used by hdparm and friends.
283 * Some need translations, these do not.
284 */
285 case HDIO_GET_IDENTITY:
286 case HDIO_DRIVE_TASK:
287 case HDIO_DRIVE_CMD:
288 case HDIO_SCAN_HWIF:
289 /* 0x330 is reserved -- it used to be HDIO_GETGEO_BIG */
290 case 0x330:
291 /* 0x02 -- Floppy ioctls */
292 case FDMSGON:
293 case FDMSGOFF:
294 case FDSETEMSGTRESH:
295 case FDFLUSH:
296 case FDWERRORCLR:
297 case FDSETMAXERRS:
298 case FDGETMAXERRS:
299 case FDGETDRVTYP:
300 case FDEJECT:
301 case FDCLRPRM:
302 case FDFMTBEG:
303 case FDFMTEND:
304 case FDRESET:
305 case FDTWADDLE:
306 case FDFMTTRK:
307 case FDRAWCMD:
308 /* CDROM stuff */
309 case CDROMPAUSE:
310 case CDROMRESUME:
311 case CDROMPLAYMSF:
312 case CDROMPLAYTRKIND:
313 case CDROMREADTOCHDR:
314 case CDROMREADTOCENTRY:
315 case CDROMSTOP:
316 case CDROMSTART:
317 case CDROMEJECT:
318 case CDROMVOLCTRL:
319 case CDROMSUBCHNL:
320 case CDROMMULTISESSION:
321 case CDROM_GET_MCN:
322 case CDROMRESET:
323 case CDROMVOLREAD:
324 case CDROMSEEK:
325 case CDROMPLAYBLK:
326 case CDROMCLOSETRAY:
327 case CDROM_DISC_STATUS:
328 case CDROM_CHANGER_NSLOTS:
329 case CDROM_GET_CAPABILITY:
330 /* Ignore cdrom.h about these next 5 ioctls, they absolutely do
331 * not take a struct cdrom_read, instead they take a struct cdrom_msf
332 * which is compatible.
333 */
334 case CDROMREADMODE2:
335 case CDROMREADMODE1:
336 case CDROMREADRAW:
337 case CDROMREADCOOKED:
338 case CDROMREADALL:
339 /* DVD ioctls */
340 case DVD_READ_STRUCT:
341 case DVD_WRITE_STRUCT:
342 case DVD_AUTH:
343 arg = (unsigned long)compat_ptr(arg);
344 /* These intepret arg as an unsigned long, not as a pointer,
345 * so we must not do compat_ptr() conversion. */
346 case HDIO_SET_MULTCOUNT:
347 case HDIO_SET_UNMASKINTR:
348 case HDIO_SET_KEEPSETTINGS:
349 case HDIO_SET_32BIT:
350 case HDIO_SET_NOWERR:
351 case HDIO_SET_DMA:
352 case HDIO_SET_PIO_MODE:
353 case HDIO_SET_NICE:
354 case HDIO_SET_WCACHE:
355 case HDIO_SET_ACOUSTIC:
356 case HDIO_SET_BUSSTATE:
357 case HDIO_SET_ADDRESS:
358 case CDROMEJECT_SW:
359 case CDROM_SET_OPTIONS:
360 case CDROM_CLEAR_OPTIONS:
361 case CDROM_SELECT_SPEED:
362 case CDROM_SELECT_DISC:
363 case CDROM_MEDIA_CHANGED:
364 case CDROM_DRIVE_STATUS:
365 case CDROM_LOCKDOOR:
366 case CDROM_DEBUG:
367 break;
368 default:
369 /* unknown ioctl number */
370 return -ENOIOCTLCMD;
371 }
372
373 if (disk->fops->unlocked_ioctl)
374 return disk->fops->unlocked_ioctl(file, cmd, arg);
375
376 if (disk->fops->ioctl) {
377 lock_kernel();
378 ret = disk->fops->ioctl(inode, file, cmd, arg);
379 unlock_kernel();
380 return ret;
381 }
382
383 return -ENOTTY;
384}
385
f58c4c0a
AB
386static int compat_blkdev_locked_ioctl(struct inode *inode, struct file *file,
387 struct block_device *bdev,
388 unsigned cmd, unsigned long arg)
389{
390 struct backing_dev_info *bdi;
391
392 switch (cmd) {
393 case BLKRAGET:
394 case BLKFRAGET:
395 if (!arg)
396 return -EINVAL;
397 bdi = blk_get_backing_dev_info(bdev);
398 if (bdi == NULL)
399 return -ENOTTY;
400 return compat_put_long(arg,
401 (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
402 case BLKROGET: /* compatible */
403 return compat_put_int(arg, bdev_read_only(bdev) != 0);
404 case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */
405 return compat_put_int(arg, block_size(bdev));
406 case BLKSSZGET: /* get block device hardware sector size */
407 return compat_put_int(arg, bdev_hardsect_size(bdev));
408 case BLKSECTGET:
409 return compat_put_ushort(arg,
410 bdev_get_queue(bdev)->max_sectors);
411 case BLKRASET: /* compatible, but no compat_ptr (!) */
412 case BLKFRASET:
413 if (!capable(CAP_SYS_ADMIN))
414 return -EACCES;
415 bdi = blk_get_backing_dev_info(bdev);
416 if (bdi == NULL)
417 return -ENOTTY;
418 bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
419 return 0;
420 case BLKGETSIZE:
421 if ((bdev->bd_inode->i_size >> 9) > ~0UL)
422 return -EFBIG;
423 return compat_put_ulong(arg, bdev->bd_inode->i_size >> 9);
424
425 case BLKGETSIZE64_32:
426 return compat_put_u64(arg, bdev->bd_inode->i_size);
171044d4
AB
427
428 case BLKTRACESETUP32:
429 return compat_blk_trace_setup(bdev, compat_ptr(arg));
430 case BLKTRACESTART: /* compatible */
431 case BLKTRACESTOP: /* compatible */
432 case BLKTRACETEARDOWN: /* compatible */
433 return blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
f58c4c0a
AB
434 }
435 return -ENOIOCTLCMD;
436}
437
438/* Most of the generic ioctls are handled in the normal fallback path.
439 This assumes the blkdev's low level compat_ioctl always returns
440 ENOIOCTLCMD for unknown ioctls. */
441long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
442{
443 int ret = -ENOIOCTLCMD;
444 struct inode *inode = file->f_mapping->host;
445 struct block_device *bdev = inode->i_bdev;
446 struct gendisk *disk = bdev->bd_disk;
447
448 switch (cmd) {
9617db08
AB
449 case HDIO_GETGEO:
450 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
f58c4c0a
AB
451 case BLKFLSBUF:
452 case BLKROSET:
453 /*
454 * the ones below are implemented in blkdev_locked_ioctl,
455 * but we call blkdev_ioctl, which gets the lock for us
456 */
457 case BLKRRPART:
458 return blkdev_ioctl(inode, file, cmd,
459 (unsigned long)compat_ptr(arg));
460 case BLKBSZSET_32:
461 return blkdev_ioctl(inode, file, BLKBSZSET,
462 (unsigned long)compat_ptr(arg));
18cf7f87
AB
463 case BLKPG:
464 return compat_blkpg_ioctl(inode, file, cmd, compat_ptr(arg));
f58c4c0a
AB
465 }
466
467 lock_kernel();
468 ret = compat_blkdev_locked_ioctl(inode, file, bdev, cmd, arg);
469 /* FIXME: why do we assume -> compat_ioctl needs the BKL? */
470 if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl)
471 ret = disk->fops->compat_ioctl(file, cmd, arg);
472 unlock_kernel();
473
7199d4cd
AB
474 if (ret != -ENOIOCTLCMD)
475 return ret;
476
477 return compat_blkdev_driver_ioctl(inode, file, disk, cmd, arg);
f58c4c0a 478}