Merge initializer conflict manually
[linux-2.6/history.git] / fs / compat.c
blob3a8110c49164846cfc0f3d178a0a7c982f242d6c
1 /*
2 * linux/fs/compat.c
4 * Kernel compatibililty routines for e.g. 32 bit syscall support
5 * on 64 bit kernels.
7 * Copyright (C) 2002 Stephen Rothwell, IBM Corporation
8 * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
9 * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
10 * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
11 * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
18 #include <linux/linkage.h>
19 #include <linux/compat.h>
20 #include <linux/errno.h>
21 #include <linux/time.h>
22 #include <linux/fs.h>
23 #include <linux/fcntl.h>
24 #include <linux/namei.h>
25 #include <linux/file.h>
26 #include <linux/vfs.h>
27 #include <linux/ioctl32.h>
28 #include <linux/init.h>
29 #include <linux/sockios.h> /* for SIOCDEVPRIVATE */
30 #include <linux/fs.h>
31 #include <linux/smp_lock.h>
32 #include <linux/ctype.h>
33 #include <linux/module.h>
34 #include <net/sock.h> /* siocdevprivate_ioctl */
36 #include <asm/uaccess.h>
39 * Not all architectures have sys_utime, so implement this in terms
40 * of sys_utimes.
42 asmlinkage long compat_sys_utime(char *filename, struct compat_utimbuf *t)
44 struct timeval tv[2];
46 if (t) {
47 if (get_user(tv[0].tv_sec, &t->actime) ||
48 get_user(tv[1].tv_sec, &t->modtime))
49 return -EFAULT;
50 tv[0].tv_usec = 0;
51 tv[1].tv_usec = 0;
53 return do_utimes(filename, t ? tv : NULL);
57 asmlinkage long compat_sys_newstat(char * filename,
58 struct compat_stat *statbuf)
60 struct kstat stat;
61 int error = vfs_stat(filename, &stat);
63 if (!error)
64 error = cp_compat_stat(&stat, statbuf);
65 return error;
68 asmlinkage long compat_sys_newlstat(char * filename,
69 struct compat_stat *statbuf)
71 struct kstat stat;
72 int error = vfs_lstat(filename, &stat);
74 if (!error)
75 error = cp_compat_stat(&stat, statbuf);
76 return error;
79 asmlinkage long compat_sys_newfstat(unsigned int fd,
80 struct compat_stat * statbuf)
82 struct kstat stat;
83 int error = vfs_fstat(fd, &stat);
85 if (!error)
86 error = cp_compat_stat(&stat, statbuf);
87 return error;
90 static int put_compat_statfs(struct compat_statfs *ubuf, struct kstatfs *kbuf)
93 if (sizeof ubuf->f_blocks == 4) {
94 if ((kbuf->f_blocks | kbuf->f_bfree |
95 kbuf->f_bavail | kbuf->f_files | kbuf->f_ffree) &
96 0xffffffff00000000ULL)
97 return -EOVERFLOW;
99 if (verify_area(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
100 __put_user(kbuf->f_type, &ubuf->f_type) ||
101 __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
102 __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
103 __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
104 __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
105 __put_user(kbuf->f_files, &ubuf->f_files) ||
106 __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
107 __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
108 __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
109 __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
110 __put_user(kbuf->f_frsize, &ubuf->f_frsize))
111 return -EFAULT;
112 return 0;
116 * The following statfs calls are copies of code from fs/open.c and
117 * should be checked against those from time to time
119 asmlinkage long compat_sys_statfs(const char *path, struct compat_statfs *buf)
121 struct nameidata nd;
122 int error;
124 error = user_path_walk(path, &nd);
125 if (!error) {
126 struct kstatfs tmp;
127 error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);
128 if (!error && put_compat_statfs(buf, &tmp))
129 error = -EFAULT;
130 path_release(&nd);
132 return error;
135 asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs *buf)
137 struct file * file;
138 struct kstatfs tmp;
139 int error;
141 error = -EBADF;
142 file = fget(fd);
143 if (!file)
144 goto out;
145 error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
146 if (!error && put_compat_statfs(buf, &tmp))
147 error = -EFAULT;
148 fput(file);
149 out:
150 return error;
154 /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64 */
156 #define IOCTL_HASHSIZE 256
157 struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
159 extern struct ioctl_trans ioctl_start[], ioctl_end[];
161 static inline unsigned long ioctl32_hash(unsigned long cmd)
163 return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
166 static void ioctl32_insert_translation(struct ioctl_trans *trans)
168 unsigned long hash;
169 struct ioctl_trans *t;
171 hash = ioctl32_hash (trans->cmd);
172 if (!ioctl32_hash_table[hash])
173 ioctl32_hash_table[hash] = trans;
174 else {
175 t = ioctl32_hash_table[hash];
176 while (t->next)
177 t = t->next;
178 trans->next = 0;
179 t->next = trans;
183 static int __init init_sys32_ioctl(void)
185 int i;
187 for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) {
188 if (ioctl_start[i].next != 0) {
189 printk("ioctl translation %d bad\n",i);
190 return -1;
193 ioctl32_insert_translation(&ioctl_start[i]);
195 return 0;
198 __initcall(init_sys32_ioctl);
200 static struct ioctl_trans *ioctl_free_list;
202 /* Never free them really. This avoids SMP races. With a Read-Copy-Update
203 enabled kernel we could just use the RCU infrastructure for this. */
204 static void free_ioctl(struct ioctl_trans *t)
206 t->cmd = 0;
207 mb();
208 t->next = ioctl_free_list;
209 ioctl_free_list = t;
212 int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
214 struct ioctl_trans *t;
215 unsigned long hash = ioctl32_hash(cmd);
217 lock_kernel();
218 for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
220 t = t->next) {
221 if (t->cmd == cmd) {
222 printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
223 unlock_kernel();
224 return -EINVAL;
228 if (ioctl_free_list) {
229 t = ioctl_free_list;
230 ioctl_free_list = t->next;
231 } else {
232 t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
233 if (!t) {
234 unlock_kernel();
235 return -ENOMEM;
239 t->next = NULL;
240 t->cmd = cmd;
241 t->handler = handler;
242 ioctl32_insert_translation(t);
244 unlock_kernel();
245 return 0;
248 static inline int builtin_ioctl(struct ioctl_trans *t)
250 return t >= (struct ioctl_trans *)ioctl_start &&
251 t < (struct ioctl_trans *)ioctl_end;
254 /* Problem:
255 This function cannot unregister duplicate ioctls, because they are not
256 unique.
257 When they happen we need to extend the prototype to pass the handler too. */
259 int unregister_ioctl32_conversion(unsigned int cmd)
261 unsigned long hash = ioctl32_hash(cmd);
262 struct ioctl_trans *t, *t1;
264 lock_kernel();
266 t = (struct ioctl_trans *)ioctl32_hash_table[hash];
267 if (!t) {
268 unlock_kernel();
269 return -EINVAL;
272 if (t->cmd == cmd) {
273 if (builtin_ioctl(t)) {
274 printk("%p tried to unregister builtin ioctl %x\n",
275 __builtin_return_address(0), cmd);
276 } else {
277 ioctl32_hash_table[hash] = t->next;
278 free_ioctl(t);
279 unlock_kernel();
280 return 0;
283 while (t->next) {
284 t1 = (struct ioctl_trans *)(long)t->next;
285 if (t1->cmd == cmd) {
286 if (builtin_ioctl(t1)) {
287 printk("%p tried to unregister builtin ioctl %x\n",
288 __builtin_return_address(0), cmd);
289 goto out;
290 } else {
291 t->next = t1->next;
292 free_ioctl(t1);
293 unlock_kernel();
294 return 0;
297 t = t1;
299 printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
300 out:
301 unlock_kernel();
302 return -EINVAL;
305 EXPORT_SYMBOL(register_ioctl32_conversion);
306 EXPORT_SYMBOL(unregister_ioctl32_conversion);
308 asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
310 struct file * filp;
311 int error = -EBADF;
312 struct ioctl_trans *t;
314 filp = fget(fd);
315 if(!filp)
316 goto out2;
318 if (!filp->f_op || !filp->f_op->ioctl) {
319 error = sys_ioctl (fd, cmd, arg);
320 goto out;
323 t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
325 while (t && t->cmd != cmd)
326 t = (struct ioctl_trans *)t->next;
327 if (t) {
328 if (t->handler)
329 error = t->handler(fd, cmd, arg, filp);
330 else
331 error = sys_ioctl(fd, cmd, arg);
332 } else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
333 error = siocdevprivate_ioctl(fd, cmd, arg);
334 } else {
335 static int count;
336 if (++count <= 50) {
337 char buf[10];
338 char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?";
340 /* find the name of the device. */
341 if (path) {
342 fn = d_path(filp->f_dentry, filp->f_vfsmnt,
343 path, PAGE_SIZE);
346 sprintf(buf,"'%c'", (cmd>>24) & 0x3f);
347 if (!isprint(buf[1]))
348 sprintf(buf, "%02x", buf[1]);
349 printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
350 "cmd(%08x){%s} arg(%08x) on %s\n",
351 current->comm, current->pid,
352 (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
353 fn);
354 if (path)
355 free_page((unsigned long)path);
357 error = -EINVAL;
359 out:
360 fput(filp);
361 out2:
362 return error;
365 static int get_compat_flock(struct flock *kfl, struct compat_flock *ufl)
367 if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
368 __get_user(kfl->l_type, &ufl->l_type) ||
369 __get_user(kfl->l_whence, &ufl->l_whence) ||
370 __get_user(kfl->l_start, &ufl->l_start) ||
371 __get_user(kfl->l_len, &ufl->l_len) ||
372 __get_user(kfl->l_pid, &ufl->l_pid))
373 return -EFAULT;
374 return 0;
377 static int put_compat_flock(struct flock *kfl, struct compat_flock *ufl)
379 if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
380 __put_user(kfl->l_type, &ufl->l_type) ||
381 __put_user(kfl->l_whence, &ufl->l_whence) ||
382 __put_user(kfl->l_start, &ufl->l_start) ||
383 __put_user(kfl->l_len, &ufl->l_len) ||
384 __put_user(kfl->l_pid, &ufl->l_pid))
385 return -EFAULT;
386 return 0;
389 #ifndef HAVE_ARCH_GET_COMPAT_FLOCK64
390 static int get_compat_flock64(struct flock *kfl, struct compat_flock64 *ufl)
392 if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
393 __get_user(kfl->l_type, &ufl->l_type) ||
394 __get_user(kfl->l_whence, &ufl->l_whence) ||
395 __get_user(kfl->l_start, &ufl->l_start) ||
396 __get_user(kfl->l_len, &ufl->l_len) ||
397 __get_user(kfl->l_pid, &ufl->l_pid))
398 return -EFAULT;
399 return 0;
401 #endif
403 #ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64
404 static int put_compat_flock64(struct flock *kfl, struct compat_flock64 *ufl)
406 if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
407 __put_user(kfl->l_type, &ufl->l_type) ||
408 __put_user(kfl->l_whence, &ufl->l_whence) ||
409 __put_user(kfl->l_start, &ufl->l_start) ||
410 __put_user(kfl->l_len, &ufl->l_len) ||
411 __put_user(kfl->l_pid, &ufl->l_pid))
412 return -EFAULT;
413 return 0;
415 #endif
417 extern asmlinkage long sys_fcntl(unsigned int, unsigned int, unsigned long);
419 asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd,
420 unsigned long arg)
422 mm_segment_t old_fs;
423 struct flock f;
424 long ret;
426 switch (cmd) {
427 case F_GETLK:
428 case F_SETLK:
429 case F_SETLKW:
430 ret = get_compat_flock(&f, compat_ptr(arg));
431 if (ret != 0)
432 break;
433 old_fs = get_fs();
434 set_fs(KERNEL_DS);
435 ret = sys_fcntl(fd, cmd, (unsigned long)&f);
436 set_fs(old_fs);
437 if ((cmd == F_GETLK) && (ret == 0)) {
438 if ((f.l_start >= COMPAT_OFF_T_MAX) ||
439 ((f.l_start + f.l_len) >= COMPAT_OFF_T_MAX))
440 ret = -EOVERFLOW;
441 if (ret == 0)
442 ret = put_compat_flock(&f, compat_ptr(arg));
444 break;
446 case F_GETLK64:
447 case F_SETLK64:
448 case F_SETLKW64:
449 ret = get_compat_flock64(&f, compat_ptr(arg));
450 if (ret != 0)
451 break;
452 old_fs = get_fs();
453 set_fs(KERNEL_DS);
454 ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
455 ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
456 (unsigned long)&f);
457 set_fs(old_fs);
458 if ((cmd == F_GETLK64) && (ret == 0)) {
459 if ((f.l_start >= COMPAT_LOFF_T_MAX) ||
460 ((f.l_start + f.l_len) >= COMPAT_LOFF_T_MAX))
461 ret = -EOVERFLOW;
462 if (ret == 0)
463 ret = put_compat_flock64(&f, compat_ptr(arg));
465 break;
467 default:
468 ret = sys_fcntl(fd, cmd, arg);
469 break;
471 return ret;
474 asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd,
475 unsigned long arg)
477 if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64))
478 return -EINVAL;
479 return compat_sys_fcntl64(fd, cmd, arg);