compat_ioctl: add compat_blkdev_driver_ioctl()
[safe/jmp/linux-2.6] / 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 #define BLKBSZGET_32            _IOR(0x12, 112, int)
40 #define BLKBSZSET_32            _IOW(0x12, 113, int)
41 #define BLKGETSIZE64_32         _IOR(0x12, 114, int)
42
43 static int compat_blkdev_driver_ioctl(struct inode *inode, struct file *file,
44                         struct gendisk *disk, unsigned cmd, unsigned long arg)
45 {
46         int ret;
47
48         switch (arg) {
49         /*
50          * No handler required for the ones below, we just need to
51          * convert arg to a 64 bit pointer.
52          */
53         case BLKSECTSET:
54         /*
55          * 0x03 -- HD/IDE ioctl's used by hdparm and friends.
56          *         Some need translations, these do not.
57          */
58         case HDIO_GET_IDENTITY:
59         case HDIO_DRIVE_TASK:
60         case HDIO_DRIVE_CMD:
61         case HDIO_SCAN_HWIF:
62         /* 0x330 is reserved -- it used to be HDIO_GETGEO_BIG */
63         case 0x330:
64         /* 0x02 -- Floppy ioctls */
65         case FDMSGON:
66         case FDMSGOFF:
67         case FDSETEMSGTRESH:
68         case FDFLUSH:
69         case FDWERRORCLR:
70         case FDSETMAXERRS:
71         case FDGETMAXERRS:
72         case FDGETDRVTYP:
73         case FDEJECT:
74         case FDCLRPRM:
75         case FDFMTBEG:
76         case FDFMTEND:
77         case FDRESET:
78         case FDTWADDLE:
79         case FDFMTTRK:
80         case FDRAWCMD:
81         /* CDROM stuff */
82         case CDROMPAUSE:
83         case CDROMRESUME:
84         case CDROMPLAYMSF:
85         case CDROMPLAYTRKIND:
86         case CDROMREADTOCHDR:
87         case CDROMREADTOCENTRY:
88         case CDROMSTOP:
89         case CDROMSTART:
90         case CDROMEJECT:
91         case CDROMVOLCTRL:
92         case CDROMSUBCHNL:
93         case CDROMMULTISESSION:
94         case CDROM_GET_MCN:
95         case CDROMRESET:
96         case CDROMVOLREAD:
97         case CDROMSEEK:
98         case CDROMPLAYBLK:
99         case CDROMCLOSETRAY:
100         case CDROM_DISC_STATUS:
101         case CDROM_CHANGER_NSLOTS:
102         case CDROM_GET_CAPABILITY:
103         /* Ignore cdrom.h about these next 5 ioctls, they absolutely do
104          * not take a struct cdrom_read, instead they take a struct cdrom_msf
105          * which is compatible.
106          */
107         case CDROMREADMODE2:
108         case CDROMREADMODE1:
109         case CDROMREADRAW:
110         case CDROMREADCOOKED:
111         case CDROMREADALL:
112         /* DVD ioctls */
113         case DVD_READ_STRUCT:
114         case DVD_WRITE_STRUCT:
115         case DVD_AUTH:
116                 arg = (unsigned long)compat_ptr(arg);
117         /* These intepret arg as an unsigned long, not as a pointer,
118          * so we must not do compat_ptr() conversion. */
119         case HDIO_SET_MULTCOUNT:
120         case HDIO_SET_UNMASKINTR:
121         case HDIO_SET_KEEPSETTINGS:
122         case HDIO_SET_32BIT:
123         case HDIO_SET_NOWERR:
124         case HDIO_SET_DMA:
125         case HDIO_SET_PIO_MODE:
126         case HDIO_SET_NICE:
127         case HDIO_SET_WCACHE:
128         case HDIO_SET_ACOUSTIC:
129         case HDIO_SET_BUSSTATE:
130         case HDIO_SET_ADDRESS:
131         case CDROMEJECT_SW:
132         case CDROM_SET_OPTIONS:
133         case CDROM_CLEAR_OPTIONS:
134         case CDROM_SELECT_SPEED:
135         case CDROM_SELECT_DISC:
136         case CDROM_MEDIA_CHANGED:
137         case CDROM_DRIVE_STATUS:
138         case CDROM_LOCKDOOR:
139         case CDROM_DEBUG:
140                 break;
141         default:
142                 /* unknown ioctl number */
143                 return -ENOIOCTLCMD;
144         }
145
146         if (disk->fops->unlocked_ioctl)
147                 return disk->fops->unlocked_ioctl(file, cmd, arg);
148
149         if (disk->fops->ioctl) {
150                 lock_kernel();
151                 ret = disk->fops->ioctl(inode, file, cmd, arg);
152                 unlock_kernel();
153                 return ret;
154         }
155
156         return -ENOTTY;
157 }
158
159 static int compat_blkdev_locked_ioctl(struct inode *inode, struct file *file,
160                                 struct block_device *bdev,
161                                 unsigned cmd, unsigned long arg)
162 {
163         struct backing_dev_info *bdi;
164
165         switch (cmd) {
166         case BLKRAGET:
167         case BLKFRAGET:
168                 if (!arg)
169                         return -EINVAL;
170                 bdi = blk_get_backing_dev_info(bdev);
171                 if (bdi == NULL)
172                         return -ENOTTY;
173                 return compat_put_long(arg,
174                                        (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
175         case BLKROGET: /* compatible */
176                 return compat_put_int(arg, bdev_read_only(bdev) != 0);
177         case BLKBSZGET_32: /* get the logical block size (cf. BLKSSZGET) */
178                 return compat_put_int(arg, block_size(bdev));
179         case BLKSSZGET: /* get block device hardware sector size */
180                 return compat_put_int(arg, bdev_hardsect_size(bdev));
181         case BLKSECTGET:
182                 return compat_put_ushort(arg,
183                                          bdev_get_queue(bdev)->max_sectors);
184         case BLKRASET: /* compatible, but no compat_ptr (!) */
185         case BLKFRASET:
186                 if (!capable(CAP_SYS_ADMIN))
187                         return -EACCES;
188                 bdi = blk_get_backing_dev_info(bdev);
189                 if (bdi == NULL)
190                         return -ENOTTY;
191                 bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
192                 return 0;
193         case BLKGETSIZE:
194                 if ((bdev->bd_inode->i_size >> 9) > ~0UL)
195                         return -EFBIG;
196                 return compat_put_ulong(arg, bdev->bd_inode->i_size >> 9);
197
198         case BLKGETSIZE64_32:
199                 return compat_put_u64(arg, bdev->bd_inode->i_size);
200         }
201         return -ENOIOCTLCMD;
202 }
203
204 /* Most of the generic ioctls are handled in the normal fallback path.
205    This assumes the blkdev's low level compat_ioctl always returns
206    ENOIOCTLCMD for unknown ioctls. */
207 long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
208 {
209         int ret = -ENOIOCTLCMD;
210         struct inode *inode = file->f_mapping->host;
211         struct block_device *bdev = inode->i_bdev;
212         struct gendisk *disk = bdev->bd_disk;
213
214         switch (cmd) {
215         case BLKFLSBUF:
216         case BLKROSET:
217         /*
218          * the ones below are implemented in blkdev_locked_ioctl,
219          * but we call blkdev_ioctl, which gets the lock for us
220          */
221         case BLKRRPART:
222                 return blkdev_ioctl(inode, file, cmd,
223                                 (unsigned long)compat_ptr(arg));
224         case BLKBSZSET_32:
225                 return blkdev_ioctl(inode, file, BLKBSZSET,
226                                 (unsigned long)compat_ptr(arg));
227         }
228
229         lock_kernel();
230         ret = compat_blkdev_locked_ioctl(inode, file, bdev, cmd, arg);
231         /* FIXME: why do we assume -> compat_ioctl needs the BKL? */
232         if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl)
233                 ret = disk->fops->compat_ioctl(file, cmd, arg);
234         unlock_kernel();
235
236         if (ret != -ENOIOCTLCMD)
237                 return ret;
238
239         return compat_blkdev_driver_ioctl(inode, file, disk, cmd, arg);
240 }