diff options
Diffstat (limited to 'fs/compat_ioctl.c')
-rw-r--r-- | fs/compat_ioctl.c | 90 |
1 files changed, 44 insertions, 46 deletions
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 7895bdb0c304..b4873ae84ca1 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c | |||
@@ -111,6 +111,8 @@ | |||
111 | #include <linux/dvb/frontend.h> | 111 | #include <linux/dvb/frontend.h> |
112 | #include <linux/dvb/video.h> | 112 | #include <linux/dvb/video.h> |
113 | 113 | ||
114 | #include <linux/sort.h> | ||
115 | |||
114 | #ifdef CONFIG_SPARC | 116 | #ifdef CONFIG_SPARC |
115 | #include <asm/fbio.h> | 117 | #include <asm/fbio.h> |
116 | #endif | 118 | #endif |
@@ -1048,15 +1050,13 @@ static int compat_ioctl_preallocate(struct file *file, unsigned long arg) | |||
1048 | } | 1050 | } |
1049 | #endif | 1051 | #endif |
1050 | 1052 | ||
1053 | /* | ||
1054 | * simple reversible transform to make our table more evenly | ||
1055 | * distributed after sorting. | ||
1056 | */ | ||
1057 | #define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff) | ||
1051 | 1058 | ||
1052 | struct ioctl_trans { | 1059 | #define COMPATIBLE_IOCTL(cmd) XFORM(cmd), |
1053 | unsigned long cmd; | ||
1054 | struct ioctl_trans *next; | ||
1055 | }; | ||
1056 | |||
1057 | /* pointer to compatible structure or no argument */ | ||
1058 | #define COMPATIBLE_IOCTL(cmd) { (cmd), }, | ||
1059 | |||
1060 | /* ioctl should not be warned about even if it's not implemented. | 1060 | /* ioctl should not be warned about even if it's not implemented. |
1061 | Valid reasons to use this: | 1061 | Valid reasons to use this: |
1062 | - It is implemented with ->compat_ioctl on some device, but programs | 1062 | - It is implemented with ->compat_ioctl on some device, but programs |
@@ -1066,7 +1066,7 @@ struct ioctl_trans { | |||
1066 | Most other reasons are not valid. */ | 1066 | Most other reasons are not valid. */ |
1067 | #define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd) | 1067 | #define IGNORE_IOCTL(cmd) COMPATIBLE_IOCTL(cmd) |
1068 | 1068 | ||
1069 | static struct ioctl_trans ioctl_start[] = { | 1069 | static unsigned int ioctl_pointer[] = { |
1070 | /* compatible ioctls first */ | 1070 | /* compatible ioctls first */ |
1071 | COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ | 1071 | COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ |
1072 | COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ | 1072 | COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ |
@@ -1710,14 +1710,6 @@ IGNORE_IOCTL(FBIOGCURSOR32) | |||
1710 | #endif | 1710 | #endif |
1711 | }; | 1711 | }; |
1712 | 1712 | ||
1713 | #define IOCTL_HASHSIZE 256 | ||
1714 | static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; | ||
1715 | |||
1716 | static inline unsigned long ioctl32_hash(unsigned long cmd) | ||
1717 | { | ||
1718 | return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE; | ||
1719 | } | ||
1720 | |||
1721 | /* | 1713 | /* |
1722 | * Convert common ioctl arguments based on their command number | 1714 | * Convert common ioctl arguments based on their command number |
1723 | * | 1715 | * |
@@ -1861,12 +1853,33 @@ static void compat_ioctl_error(struct file *filp, unsigned int fd, | |||
1861 | free_page((unsigned long)path); | 1853 | free_page((unsigned long)path); |
1862 | } | 1854 | } |
1863 | 1855 | ||
1856 | static int compat_ioctl_check_table(unsigned int xcmd) | ||
1857 | { | ||
1858 | int i; | ||
1859 | const int max = ARRAY_SIZE(ioctl_pointer) - 1; | ||
1860 | |||
1861 | BUILD_BUG_ON(max >= (1 << 16)); | ||
1862 | |||
1863 | /* guess initial offset into table, assuming a | ||
1864 | normalized distribution */ | ||
1865 | i = ((xcmd >> 16) * max) >> 16; | ||
1866 | |||
1867 | /* do linear search up first, until greater or equal */ | ||
1868 | while (ioctl_pointer[i] < xcmd && i < max) | ||
1869 | i++; | ||
1870 | |||
1871 | /* then do linear search down */ | ||
1872 | while (ioctl_pointer[i] > xcmd && i > 0) | ||
1873 | i--; | ||
1874 | |||
1875 | return ioctl_pointer[i] == xcmd; | ||
1876 | } | ||
1877 | |||
1864 | asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, | 1878 | asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, |
1865 | unsigned long arg) | 1879 | unsigned long arg) |
1866 | { | 1880 | { |
1867 | struct file *filp; | 1881 | struct file *filp; |
1868 | int error = -EBADF; | 1882 | int error = -EBADF; |
1869 | struct ioctl_trans *t; | ||
1870 | int fput_needed; | 1883 | int fput_needed; |
1871 | 1884 | ||
1872 | filp = fget_light(fd, &fput_needed); | 1885 | filp = fget_light(fd, &fput_needed); |
@@ -1923,10 +1936,8 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, | |||
1923 | break; | 1936 | break; |
1924 | } | 1937 | } |
1925 | 1938 | ||
1926 | for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) { | 1939 | if (compat_ioctl_check_table(XFORM(cmd))) |
1927 | if (t->cmd == cmd) | 1940 | goto found_handler; |
1928 | goto found_handler; | ||
1929 | } | ||
1930 | 1941 | ||
1931 | error = do_ioctl_trans(fd, cmd, arg, filp); | 1942 | error = do_ioctl_trans(fd, cmd, arg, filp); |
1932 | if (error == -ENOIOCTLCMD) { | 1943 | if (error == -ENOIOCTLCMD) { |
@@ -1949,35 +1960,22 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, | |||
1949 | return error; | 1960 | return error; |
1950 | } | 1961 | } |
1951 | 1962 | ||
1952 | static void ioctl32_insert_translation(struct ioctl_trans *trans) | 1963 | static int __init init_sys32_ioctl_cmp(const void *p, const void *q) |
1953 | { | 1964 | { |
1954 | unsigned long hash; | 1965 | unsigned int a, b; |
1955 | struct ioctl_trans *t; | 1966 | a = *(unsigned int *)p; |
1956 | 1967 | b = *(unsigned int *)q; | |
1957 | hash = ioctl32_hash (trans->cmd); | 1968 | if (a > b) |
1958 | if (!ioctl32_hash_table[hash]) | 1969 | return 1; |
1959 | ioctl32_hash_table[hash] = trans; | 1970 | if (a < b) |
1960 | else { | 1971 | return -1; |
1961 | t = ioctl32_hash_table[hash]; | 1972 | return 0; |
1962 | while (t->next) | ||
1963 | t = t->next; | ||
1964 | trans->next = NULL; | ||
1965 | t->next = trans; | ||
1966 | } | ||
1967 | } | 1973 | } |
1968 | 1974 | ||
1969 | static int __init init_sys32_ioctl(void) | 1975 | static int __init init_sys32_ioctl(void) |
1970 | { | 1976 | { |
1971 | int i; | 1977 | sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer), |
1972 | 1978 | init_sys32_ioctl_cmp, NULL); | |
1973 | for (i = 0; i < ARRAY_SIZE(ioctl_start); i++) { | ||
1974 | if (ioctl_start[i].next) { | ||
1975 | printk("ioctl translation %d bad\n",i); | ||
1976 | return -1; | ||
1977 | } | ||
1978 | |||
1979 | ioctl32_insert_translation(&ioctl_start[i]); | ||
1980 | } | ||
1981 | return 0; | 1979 | return 0; |
1982 | } | 1980 | } |
1983 | __initcall(init_sys32_ioctl); | 1981 | __initcall(init_sys32_ioctl); |