basic modification from way back
[powerpc.git] / fs / compat_ioctl.c
index a0c3d20..d92bc3e 100644 (file)
@@ -57,7 +57,6 @@
 #include <linux/serial.h>
 #include <linux/if_tun.h>
 #include <linux/ctype.h>
-#include <linux/ioctl32.h>
 #include <linux/syscalls.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
@@ -65,7 +64,6 @@
 #include <linux/atalk.h>
 #include <linux/blktrace_api.h>
 
-#include <net/sock.h>          /* siocdevprivate_ioctl */
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci.h>
 #include <net/bluetooth/rfcomm.h>
@@ -474,7 +472,7 @@ static int bond_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
        };
 }
 
-int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+static int siocdevprivate_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
        struct ifreq __user *u_ifreq64;
        struct ifreq32 __user *u_ifreq32 = compat_ptr(arg);
@@ -686,8 +684,10 @@ static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg)
        if (!err) {
                err = copy_to_user (ugeo, &geo, 4);
                err |= __put_user (geo.start, &ugeo->start);
+               if (err)
+                       err = -EFAULT;
        }
-       return err ? -EFAULT : 0;
+       return err;
 }
 
 static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
@@ -2384,6 +2384,16 @@ lp_timeout_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
        return sys_ioctl(fd, cmd, (unsigned long)tn);
 }
 
+
+typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int,
+                                       unsigned long, struct file *);
+
+struct ioctl_trans {
+       unsigned long cmd;
+       ioctl_trans_handler_t handler;
+       struct ioctl_trans *next;
+};
+
 #define HANDLE_IOCTL(cmd,handler) \
        { (cmd), (ioctl_trans_handler_t)(handler) },
 
@@ -2404,7 +2414,7 @@ lp_timeout_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
    Most other reasons are not valid. */
 #define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd)
 
-struct ioctl_trans ioctl_start[] = {
+static struct ioctl_trans ioctl_start[] = {
 /* compatible ioctls first */
 COMPATIBLE_IOCTL(0x4B50)   /* KDGHWCLK - not in the kernel, but don't complain */
 COMPATIBLE_IOCTL(0x4B51)   /* KDSHWCLK - not in the kernel, but don't complain */
@@ -3464,4 +3474,156 @@ IGNORE_IOCTL(VFAT_IOCTL_READDIR_BOTH32)
 IGNORE_IOCTL(VFAT_IOCTL_READDIR_SHORT32)
 };
 
-int ioctl_table_size = ARRAY_SIZE(ioctl_start);
+#define IOCTL_HASHSIZE 256
+static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
+
+static inline unsigned long ioctl32_hash(unsigned long cmd)
+{
+       return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
+}
+
+static void compat_ioctl_error(struct file *filp, unsigned int fd,
+               unsigned int cmd, unsigned long arg)
+{
+       char buf[10];
+       char *fn = "?";
+       char *path;
+
+       /* find the name of the device. */
+       path = (char *)__get_free_page(GFP_KERNEL);
+       if (path) {
+               fn = d_path(filp->f_path.dentry, filp->f_path.mnt, path, PAGE_SIZE);
+               if (IS_ERR(fn))
+                       fn = "?";
+       }
+
+        sprintf(buf,"'%c'", (cmd>>_IOC_TYPESHIFT) & _IOC_TYPEMASK);
+       if (!isprint(buf[1]))
+               sprintf(buf, "%02x", buf[1]);
+       compat_printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
+                       "cmd(%08x){t:%s;sz:%u} arg(%08x) on %s\n",
+                       current->comm, current->pid,
+                       (int)fd, (unsigned int)cmd, buf,
+                       (cmd >> _IOC_SIZESHIFT) & _IOC_SIZEMASK,
+                       (unsigned int)arg, fn);
+
+       if (path)
+               free_page((unsigned long)path);
+}
+
+asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
+                               unsigned long arg)
+{
+       struct file *filp;
+       int error = -EBADF;
+       struct ioctl_trans *t;
+       int fput_needed;
+
+       filp = fget_light(fd, &fput_needed);
+       if (!filp)
+               goto out;
+
+       /* RED-PEN how should LSM module know it's handling 32bit? */
+       error = security_file_ioctl(filp, cmd, arg);
+       if (error)
+               goto out_fput;
+
+       /*
+        * To allow the compat_ioctl handlers to be self contained
+        * we need to check the common ioctls here first.
+        * Just handle them with the standard handlers below.
+        */
+       switch (cmd) {
+       case FIOCLEX:
+       case FIONCLEX:
+       case FIONBIO:
+       case FIOASYNC:
+       case FIOQSIZE:
+               break;
+
+       case FIBMAP:
+       case FIGETBSZ:
+       case FIONREAD:
+               if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))
+                       break;
+               /*FALL THROUGH*/
+
+       default:
+               if (filp->f_op && filp->f_op->compat_ioctl) {
+                       error = filp->f_op->compat_ioctl(filp, cmd, arg);
+                       if (error != -ENOIOCTLCMD)
+                               goto out_fput;
+               }
+
+               if (!filp->f_op ||
+                   (!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl))
+                       goto do_ioctl;
+               break;
+       }
+
+       for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) {
+               if (t->cmd == cmd)
+                       goto found_handler;
+       }
+
+       if (S_ISSOCK(filp->f_path.dentry->d_inode->i_mode) &&
+           cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
+               error = siocdevprivate_ioctl(fd, cmd, arg);
+       } else {
+               static int count;
+
+               if (++count <= 50)
+                       compat_ioctl_error(filp, fd, cmd, arg);
+               error = -EINVAL;
+       }
+
+       goto out_fput;
+
+ found_handler:
+       if (t->handler) {
+               lock_kernel();
+               error = t->handler(fd, cmd, arg, filp);
+               unlock_kernel();
+               goto out_fput;
+       }
+
+ do_ioctl:
+       error = vfs_ioctl(filp, fd, cmd, arg);
+ out_fput:
+       fput_light(filp, fput_needed);
+ out:
+       return error;
+}
+
+static void ioctl32_insert_translation(struct ioctl_trans *trans)
+{
+       unsigned long hash;
+       struct ioctl_trans *t;
+
+       hash = ioctl32_hash (trans->cmd);
+       if (!ioctl32_hash_table[hash])
+               ioctl32_hash_table[hash] = trans;
+       else {
+               t = ioctl32_hash_table[hash];
+               while (t->next)
+                       t = t->next;
+               trans->next = NULL;
+               t->next = trans;
+       }
+}
+
+static int __init init_sys32_ioctl(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ioctl_start); i++) {
+               if (ioctl_start[i].next != 0) {
+                       printk("ioctl translation %d bad\n",i);
+                       return -1;
+               }
+
+               ioctl32_insert_translation(&ioctl_start[i]);
+       }
+       return 0;
+}
+__initcall(init_sys32_ioctl);