ioctl32 cleanup -- generic

Pavel Machek (pavel@ucw.cz)
Sun, 9 Mar 2003 23:22:17 +0100


Hi!

This is ioctl32 cleanup -- it kills [crappy and] duplicated code and
moves it to fs/compat.c. fs/compat.c version is from x86-64, which
should have correct locking. Please apply,

Pavel

--- clean/fs/compat.c 2003-01-17 23:10:00.000000000 +0100
+++ linux/fs/compat.c 2003-03-09 22:39:06.000000000 +0100
@@ -4,7 +4,11 @@
* Kernel compatibililty routines for e.g. 32 bit syscall support
* on 64 bit kernels.
*
- * Copyright (C) 2002 Stephen Rothwell, IBM Corporation
+ * Copyright (C) 2002 Stephen Rothwell, IBM Corporation
+ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,6 +24,12 @@
#include <linux/namei.h>
#include <linux/file.h>
#include <linux/vfs.h>
+#include <linux/ioctl32.h>
+#include <linux/init.h>
+#include <linux/sockios.h> /* for SIOCDEVPRIVATE */
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>

#include <asm/uaccess.h>

@@ -159,3 +169,214 @@
out:
return error;
}
+
+
+/* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64 */
+
+#define IOCTL_HASHSIZE 256
+struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
+
+extern struct ioctl_trans ioctl_start[], ioctl_end[];
+
+static inline unsigned long ioctl32_hash(unsigned long cmd)
+{
+ return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
+}
+
+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 = 0;
+ t->next = trans;
+ }
+}
+
+static int __init init_sys32_ioctl(void)
+{
+ int i;
+
+ for (i = 0; &ioctl_start[i] < &ioctl_end[0]; 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);
+
+static struct ioctl_trans *ioctl_free_list;
+
+/* Never free them really. This avoids SMP races. With a Read-Copy-Update
+ enabled kernel we could just use the RCU infrastructure for this. */
+static void free_ioctl(struct ioctl_trans *t)
+{
+ t->cmd = 0;
+ mb();
+ t->next = ioctl_free_list;
+ ioctl_free_list = t;
+}
+
+int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
+{
+ struct ioctl_trans *t;
+ unsigned long hash = ioctl32_hash(cmd);
+
+ lock_kernel();
+ for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
+ t;
+ t = t->next) {
+ if (t->cmd == cmd) {
+ printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
+ unlock_kernel();
+ return -EINVAL;
+ }
+ }
+
+ if (ioctl_free_list) {
+ t = ioctl_free_list;
+ ioctl_free_list = t->next;
+ } else {
+ t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
+ if (!t) {
+ unlock_kernel();
+ return -ENOMEM;
+ }
+ }
+
+ t->next = NULL;
+ t->cmd = cmd;
+ t->handler = handler;
+ ioctl32_insert_translation(t);
+
+ unlock_kernel();
+ return 0;
+}
+
+static inline int builtin_ioctl(struct ioctl_trans *t)
+{
+ return t >= (struct ioctl_trans *)ioctl_start &&
+ t < (struct ioctl_trans *)ioctl_end;
+}
+
+/* Problem:
+ This function cannot unregister duplicate ioctls, because they are not
+ unique.
+ When they happen we need to extend the prototype to pass the handler too. */
+
+int unregister_ioctl32_conversion(unsigned int cmd)
+{
+ unsigned long hash = ioctl32_hash(cmd);
+ struct ioctl_trans *t, *t1;
+
+ lock_kernel();
+
+ t = (struct ioctl_trans *)ioctl32_hash_table[hash];
+ if (!t) {
+ unlock_kernel();
+ return -EINVAL;
+ }
+
+ if (t->cmd == cmd) {
+ if (builtin_ioctl(t)) {
+ printk("%p tried to unregister builtin ioctl %x\n",
+ __builtin_return_address(0), cmd);
+ } else {
+ ioctl32_hash_table[hash] = t->next;
+ free_ioctl(t);
+ unlock_kernel();
+ return 0;
+ }
+ }
+ while (t->next) {
+ t1 = (struct ioctl_trans *)(long)t->next;
+ if (t1->cmd == cmd) {
+ if (builtin_ioctl(t1)) {
+ printk("%p tried to unregister builtin ioctl %x\n",
+ __builtin_return_address(0), cmd);
+ goto out;
+ } else {
+ t->next = t1->next;
+ free_ioctl(t1);
+ unlock_kernel();
+ return 0;
+ }
+ }
+ t = t1;
+ }
+ printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
+ out:
+ unlock_kernel();
+ return -EINVAL;
+}
+
+EXPORT_SYMBOL(register_ioctl32_conversion);
+EXPORT_SYMBOL(unregister_ioctl32_conversion);
+
+asmlinkage long compact_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ int error = -EBADF;
+ int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
+ struct ioctl_trans *t;
+
+ filp = fget(fd);
+ if(!filp)
+ goto out2;
+
+ if (!filp->f_op || !filp->f_op->ioctl) {
+ error = sys_ioctl (fd, cmd, arg);
+ goto out;
+ }
+
+ t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
+
+ while (t && t->cmd != cmd)
+ t = (struct ioctl_trans *)t->next;
+ if (t) {
+ handler = t->handler;
+ error = handler(fd, cmd, arg, filp);
+ } else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
+ error = siocdevprivate_ioctl(fd, cmd, arg);
+ } else {
+ static int count;
+ if (++count <= 50) {
+ char buf[10];
+ char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?";
+
+ /* find the name of the device. */
+ if (path) {
+ fn = d_path(filp->f_dentry, filp->f_vfsmnt,
+ path, PAGE_SIZE);
+ }
+
+ sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
+ if (!isprint(buf[1]))
+ sprintf(buf, "%02x", buf[1]);
+ printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
+ "cmd(%08x){%s} arg(%08x) on %s\n",
+ current->comm, current->pid,
+ (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
+ fn);
+ if (path)
+ free_page((unsigned long)path);
+ }
+ error = -EINVAL;
+ }
+out:
+ fput(filp);
+out2:
+ return error;
+}
--- clean/include/linux/ioctl32.h 2002-10-20 16:22:47.000000000 +0200
+++ linux/include/linux/ioctl32.h 2003-03-06 22:12:05.000000000 +0100
@@ -19,5 +19,10 @@

extern int unregister_ioctl32_conversion(unsigned int cmd);

+struct ioctl_trans {
+ unsigned long cmd;
+ int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
+ struct ioctl_trans *next;
+};

#endif

-- 
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/