compat_ioctl: move BLKPG handling to block/compat_ioctl.c
[linux-2.6.git] / block / compat_ioctl.c
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
14 static int compat_put_ushort(unsigned long arg, unsigned short val)
15 {
16         return put_user(val, (unsigned short __user *)compat_ptr(arg));
17 }
18
19 static int compat_put_int(unsigned long arg, int val)
20 {
21         return put_user(val, (compat_int_t __user *)compat_ptr(arg));
22 }
23
24 static int compat_put_long(unsigned long arg, long val)
25 {
26         return put_user(val, (compat_long_t __user *)compat_ptr(arg));
27 }
28
29 static 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
34 static int compat_put_u64(unsigned long arg, u64 val)
35 {
36         return put_user(val, (compat_u64 __user *)compat_ptr(arg));
37 }
38
39 struct compat_hd_geometry {
40         unsigned char heads;
41         unsigned char sectors;
42         unsigned short cylinders;
43         u32 start;
44 };
45
46 static 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
74 static 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
95 struct 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
102 static 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
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
128 struct 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
139 static 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
175 static 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) {
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);
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
303 static 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);
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));
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. */
358 long 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) {
366         case HDIO_GETGEO:
367                 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
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));
380         case BLKPG:
381                 return compat_blkpg_ioctl(inode, file, cmd, compat_ptr(arg));
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
391         if (ret != -ENOIOCTLCMD)
392                 return ret;
393
394         return compat_blkdev_driver_ioctl(inode, file, disk, cmd, arg);
395 }