ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / compat.c
1 /*
2  *  linux/fs/compat.c
3  *
4  *  Kernel compatibililty routines for e.g. 32 bit syscall support
5  *  on 64 bit kernels.
6  *
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)
12  *
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.
16  */
17
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/smb.h>
31 #include <linux/smb_mount.h>
32 #include <linux/ncp_mount.h>
33 #include <linux/smp_lock.h>
34 #include <linux/syscalls.h>
35 #include <linux/ctype.h>
36 #include <linux/module.h>
37 #include <net/sock.h>           /* siocdevprivate_ioctl */
38
39 #include <asm/uaccess.h>
40
41 /*
42  * Not all architectures have sys_utime, so implement this in terms
43  * of sys_utimes.
44  */
45 asmlinkage long compat_sys_utime(char *filename, struct compat_utimbuf *t)
46 {
47         struct timeval tv[2];
48
49         if (t) {
50                 if (get_user(tv[0].tv_sec, &t->actime) ||
51                     get_user(tv[1].tv_sec, &t->modtime))
52                         return -EFAULT;
53                 tv[0].tv_usec = 0;
54                 tv[1].tv_usec = 0;
55         }
56         return do_utimes(filename, t ? tv : NULL);
57 }
58
59 asmlinkage long compat_sys_utimes(char *filename, struct compat_timeval *t)
60 {
61         struct timeval tv[2];
62
63         if (t) { 
64                 if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
65                     get_user(tv[0].tv_usec, &t[0].tv_usec) ||
66                     get_user(tv[1].tv_sec, &t[1].tv_sec) ||
67                     get_user(tv[1].tv_usec, &t[1].tv_usec))
68                         return -EFAULT; 
69         } 
70         return do_utimes(filename, t ? tv : NULL);
71 }
72
73 asmlinkage long compat_sys_newstat(char * filename,
74                 struct compat_stat *statbuf)
75 {
76         struct kstat stat;
77         int error = vfs_stat(filename, &stat);
78
79         if (!error)
80                 error = cp_compat_stat(&stat, statbuf);
81         return error;
82 }
83
84 asmlinkage long compat_sys_newlstat(char * filename,
85                 struct compat_stat *statbuf)
86 {
87         struct kstat stat;
88         int error = vfs_lstat(filename, &stat);
89
90         if (!error)
91                 error = cp_compat_stat(&stat, statbuf);
92         return error;
93 }
94
95 asmlinkage long compat_sys_newfstat(unsigned int fd,
96                 struct compat_stat * statbuf)
97 {
98         struct kstat stat;
99         int error = vfs_fstat(fd, &stat);
100
101         if (!error)
102                 error = cp_compat_stat(&stat, statbuf);
103         return error;
104 }
105
106 static int put_compat_statfs(struct compat_statfs *ubuf, struct kstatfs *kbuf)
107 {
108         
109         if (sizeof ubuf->f_blocks == 4) {
110                 if ((kbuf->f_blocks | kbuf->f_bfree |
111                      kbuf->f_bavail | kbuf->f_files | kbuf->f_ffree) &
112                     0xffffffff00000000ULL)
113                         return -EOVERFLOW;
114         }
115         if (verify_area(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
116             __put_user(kbuf->f_type, &ubuf->f_type) ||
117             __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
118             __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
119             __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
120             __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
121             __put_user(kbuf->f_files, &ubuf->f_files) ||
122             __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
123             __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
124             __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
125             __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
126             __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
127             __put_user(0, &ubuf->f_spare[0]) || 
128             __put_user(0, &ubuf->f_spare[1]) || 
129             __put_user(0, &ubuf->f_spare[2]) || 
130             __put_user(0, &ubuf->f_spare[3]) || 
131             __put_user(0, &ubuf->f_spare[4]))
132                 return -EFAULT;
133         return 0;
134 }
135
136 /*
137  * The following statfs calls are copies of code from fs/open.c and
138  * should be checked against those from time to time
139  */
140 asmlinkage long compat_sys_statfs(const char *path, struct compat_statfs *buf)
141 {
142         struct nameidata nd;
143         int error;
144
145         error = user_path_walk(path, &nd);
146         if (!error) {
147                 struct kstatfs tmp;
148                 error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);
149                 if (!error && put_compat_statfs(buf, &tmp))
150                         error = -EFAULT;
151                 path_release(&nd);
152         }
153         return error;
154 }
155
156 asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs *buf)
157 {
158         struct file * file;
159         struct kstatfs tmp;
160         int error;
161
162         error = -EBADF;
163         file = fget(fd);
164         if (!file)
165                 goto out;
166         error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
167         if (!error && put_compat_statfs(buf, &tmp))
168                 error = -EFAULT;
169         fput(file);
170 out:
171         return error;
172 }
173
174 static int put_compat_statfs64(struct compat_statfs64 *ubuf, struct kstatfs *kbuf)
175 {
176         if (sizeof ubuf->f_blocks == 4) {
177                 if ((kbuf->f_blocks | kbuf->f_bfree |
178                      kbuf->f_bavail | kbuf->f_files | kbuf->f_ffree) &
179                     0xffffffff00000000ULL)
180                         return -EOVERFLOW;
181         }
182         if (verify_area(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
183             __put_user(kbuf->f_type, &ubuf->f_type) ||
184             __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
185             __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
186             __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
187             __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
188             __put_user(kbuf->f_files, &ubuf->f_files) ||
189             __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
190             __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
191             __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
192             __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
193             __put_user(kbuf->f_frsize, &ubuf->f_frsize))
194                 return -EFAULT;
195         return 0;
196 }
197
198 asmlinkage long compat_statfs64(const char *path, compat_size_t sz, struct compat_statfs64 *buf)
199 {
200         struct nameidata nd;
201         int error;
202
203         if (sz != sizeof(*buf))
204                 return -EINVAL;
205
206         error = user_path_walk(path, &nd);
207         if (!error) {
208                 struct kstatfs tmp;
209                 error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);
210                 if (!error && put_compat_statfs64(buf, &tmp))
211                         error = -EFAULT;
212                 path_release(&nd);
213         }
214         return error;
215 }
216
217 asmlinkage long compat_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 *buf)
218 {
219         struct file * file;
220         struct kstatfs tmp;
221         int error;
222
223         if (sz != sizeof(*buf))
224                 return -EINVAL;
225
226         error = -EBADF;
227         file = fget(fd);
228         if (!file)
229                 goto out;
230         error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
231         if (!error && put_compat_statfs64(buf, &tmp))
232                 error = -EFAULT;
233         fput(file);
234 out:
235         return error;
236 }
237
238 /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */
239
240 #define IOCTL_HASHSIZE 256
241 struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
242
243 extern struct ioctl_trans ioctl_start[];
244 extern int ioctl_table_size;
245
246 static inline unsigned long ioctl32_hash(unsigned long cmd)
247 {
248         return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
249 }
250
251 static void ioctl32_insert_translation(struct ioctl_trans *trans)
252 {
253         unsigned long hash;
254         struct ioctl_trans *t;
255
256         hash = ioctl32_hash (trans->cmd);
257         if (!ioctl32_hash_table[hash])
258                 ioctl32_hash_table[hash] = trans;
259         else {
260                 t = ioctl32_hash_table[hash];
261                 while (t->next)
262                         t = t->next;
263                 trans->next = 0;
264                 t->next = trans;
265         }
266 }
267
268 static int __init init_sys32_ioctl(void)
269 {
270         int i;
271
272         for (i = 0; i < ioctl_table_size; i++) {
273                 if (ioctl_start[i].next != 0) { 
274                         printk("ioctl translation %d bad\n",i); 
275                         return -1;
276                 }
277
278                 ioctl32_insert_translation(&ioctl_start[i]);
279         }
280         return 0;
281 }
282
283 __initcall(init_sys32_ioctl);
284
285 static struct ioctl_trans *ioctl_free_list;
286
287 /* Never free them really. This avoids SMP races. With a Read-Copy-Update
288    enabled kernel we could just use the RCU infrastructure for this. */
289 static void free_ioctl(struct ioctl_trans *t) 
290
291         t->cmd = 0; 
292         mb();
293         t->next = ioctl_free_list;
294         ioctl_free_list = t;
295
296
297 int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
298 {
299         struct ioctl_trans *t;
300         unsigned long hash = ioctl32_hash(cmd);
301
302         lock_kernel(); 
303         for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
304              t;
305              t = t->next) { 
306                 if (t->cmd == cmd) {
307                         printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
308                         unlock_kernel();
309                         return -EINVAL; 
310                 }
311         } 
312
313         if (ioctl_free_list) { 
314                 t = ioctl_free_list; 
315                 ioctl_free_list = t->next; 
316         } else { 
317                 t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL); 
318                 if (!t) { 
319                         unlock_kernel();
320                         return -ENOMEM;
321                 }
322         }
323         
324         t->next = NULL;
325         t->cmd = cmd;
326         t->handler = handler; 
327         ioctl32_insert_translation(t);
328
329         unlock_kernel();
330         return 0;
331 }
332
333 static inline int builtin_ioctl(struct ioctl_trans *t)
334
335         return t >= ioctl_start && t < (ioctl_start + ioctl_table_size);
336
337
338 /* Problem: 
339    This function cannot unregister duplicate ioctls, because they are not
340    unique.
341    When they happen we need to extend the prototype to pass the handler too. */
342
343 int unregister_ioctl32_conversion(unsigned int cmd)
344 {
345         unsigned long hash = ioctl32_hash(cmd);
346         struct ioctl_trans *t, *t1;
347
348         lock_kernel(); 
349
350         t = (struct ioctl_trans *)ioctl32_hash_table[hash];
351         if (!t) { 
352                 unlock_kernel();
353                 return -EINVAL;
354         } 
355
356         if (t->cmd == cmd) { 
357                 if (builtin_ioctl(t)) {
358                         printk("%p tried to unregister builtin ioctl %x\n",
359                                __builtin_return_address(0), cmd);
360                 } else { 
361                 ioctl32_hash_table[hash] = t->next;
362                         free_ioctl(t); 
363                         unlock_kernel();
364                 return 0;
365                 }
366         } 
367         while (t->next) {
368                 t1 = (struct ioctl_trans *)(long)t->next;
369                 if (t1->cmd == cmd) { 
370                         if (builtin_ioctl(t1)) {
371                                 printk("%p tried to unregister builtin ioctl %x\n",
372                                        __builtin_return_address(0), cmd);
373                                 goto out;
374                         } else { 
375                         t->next = t1->next;
376                                 free_ioctl(t1); 
377                                 unlock_kernel();
378                         return 0;
379                         }
380                 }
381                 t = t1;
382         }
383         printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
384  out:
385         unlock_kernel();
386         return -EINVAL;
387 }
388
389 EXPORT_SYMBOL(register_ioctl32_conversion); 
390 EXPORT_SYMBOL(unregister_ioctl32_conversion); 
391
392 asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
393 {
394         struct file * filp;
395         int error = -EBADF;
396         struct ioctl_trans *t;
397
398         filp = fget(fd);
399         if(!filp)
400                 goto out2;
401
402         if (!filp->f_op || !filp->f_op->ioctl) {
403                 error = sys_ioctl (fd, cmd, arg);
404                 goto out;
405         }
406
407         t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
408
409         while (t && t->cmd != cmd)
410                 t = (struct ioctl_trans *)t->next;
411         if (t) {
412                 if (t->handler) { 
413                         lock_kernel();
414                         error = t->handler(fd, cmd, arg, filp);
415                         unlock_kernel();
416                 } else
417                         error = sys_ioctl(fd, cmd, arg);
418         } else if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
419                 error = siocdevprivate_ioctl(fd, cmd, arg);
420         } else {
421                 static int count;
422                 if (++count <= 50) { 
423                         char buf[10];
424                         char *path = (char *)__get_free_page(GFP_KERNEL), *fn = "?"; 
425
426                         /* find the name of the device. */
427                         if (path) {
428                                 fn = d_path(filp->f_dentry, filp->f_vfsmnt, 
429                                             path, PAGE_SIZE);
430                         }
431
432                         sprintf(buf,"'%c'", (cmd>>24) & 0x3f); 
433                         if (!isprint(buf[1]))
434                             sprintf(buf, "%02x", buf[1]);
435                         printk("ioctl32(%s:%d): Unknown cmd fd(%d) "
436                                "cmd(%08x){%s} arg(%08x) on %s\n",
437                                current->comm, current->pid,
438                                (int)fd, (unsigned int)cmd, buf, (unsigned int)arg,
439                                fn);
440                         if (path) 
441                                 free_page((unsigned long)path); 
442                 }
443                 error = -EINVAL;
444         }
445 out:
446         fput(filp);
447 out2:
448         return error;
449 }
450
451 static int get_compat_flock(struct flock *kfl, struct compat_flock *ufl)
452 {
453         if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
454             __get_user(kfl->l_type, &ufl->l_type) ||
455             __get_user(kfl->l_whence, &ufl->l_whence) ||
456             __get_user(kfl->l_start, &ufl->l_start) ||
457             __get_user(kfl->l_len, &ufl->l_len) ||
458             __get_user(kfl->l_pid, &ufl->l_pid))
459                 return -EFAULT;
460         return 0;
461 }
462
463 static int put_compat_flock(struct flock *kfl, struct compat_flock *ufl)
464 {
465         if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
466             __put_user(kfl->l_type, &ufl->l_type) ||
467             __put_user(kfl->l_whence, &ufl->l_whence) ||
468             __put_user(kfl->l_start, &ufl->l_start) ||
469             __put_user(kfl->l_len, &ufl->l_len) ||
470             __put_user(kfl->l_pid, &ufl->l_pid))
471                 return -EFAULT;
472         return 0;
473 }
474
475 #ifndef HAVE_ARCH_GET_COMPAT_FLOCK64
476 static int get_compat_flock64(struct flock *kfl, struct compat_flock64 *ufl)
477 {
478         if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
479             __get_user(kfl->l_type, &ufl->l_type) ||
480             __get_user(kfl->l_whence, &ufl->l_whence) ||
481             __get_user(kfl->l_start, &ufl->l_start) ||
482             __get_user(kfl->l_len, &ufl->l_len) ||
483             __get_user(kfl->l_pid, &ufl->l_pid))
484                 return -EFAULT;
485         return 0;
486 }
487 #endif
488
489 #ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64
490 static int put_compat_flock64(struct flock *kfl, struct compat_flock64 *ufl)
491 {
492         if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
493             __put_user(kfl->l_type, &ufl->l_type) ||
494             __put_user(kfl->l_whence, &ufl->l_whence) ||
495             __put_user(kfl->l_start, &ufl->l_start) ||
496             __put_user(kfl->l_len, &ufl->l_len) ||
497             __put_user(kfl->l_pid, &ufl->l_pid))
498                 return -EFAULT;
499         return 0;
500 }
501 #endif
502
503 asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd,
504                 unsigned long arg)
505 {
506         mm_segment_t old_fs;
507         struct flock f;
508         long ret;
509
510         switch (cmd) {
511         case F_GETLK:
512         case F_SETLK:
513         case F_SETLKW:
514                 ret = get_compat_flock(&f, compat_ptr(arg));
515                 if (ret != 0)
516                         break;
517                 old_fs = get_fs();
518                 set_fs(KERNEL_DS);
519                 ret = sys_fcntl(fd, cmd, (unsigned long)&f);
520                 set_fs(old_fs);
521                 if ((cmd == F_GETLK) && (ret == 0)) {
522                         if ((f.l_start >= COMPAT_OFF_T_MAX) ||
523                             ((f.l_start + f.l_len) >= COMPAT_OFF_T_MAX))
524                                 ret = -EOVERFLOW;
525                         if (ret == 0)
526                                 ret = put_compat_flock(&f, compat_ptr(arg));
527                 }
528                 break;
529
530         case F_GETLK64:
531         case F_SETLK64:
532         case F_SETLKW64:
533                 ret = get_compat_flock64(&f, compat_ptr(arg));
534                 if (ret != 0)
535                         break;
536                 old_fs = get_fs();
537                 set_fs(KERNEL_DS);
538                 ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
539                                 ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
540                                 (unsigned long)&f);
541                 set_fs(old_fs);
542                 if ((cmd == F_GETLK64) && (ret == 0)) {
543                         if ((f.l_start >= COMPAT_LOFF_T_MAX) ||
544                             ((f.l_start + f.l_len) >= COMPAT_LOFF_T_MAX))
545                                 ret = -EOVERFLOW;
546                         if (ret == 0)
547                                 ret = put_compat_flock64(&f, compat_ptr(arg));
548                 }
549                 break;
550
551         default:
552                 ret = sys_fcntl(fd, cmd, arg);
553                 break;
554         }
555         return ret;
556 }
557
558 asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd,
559                 unsigned long arg)
560 {
561         if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64))
562                 return -EINVAL;
563         return compat_sys_fcntl64(fd, cmd, arg);
564 }
565
566 asmlinkage long
567 compat_sys_io_setup(unsigned nr_reqs, u32 *ctx32p)
568 {
569         long ret;
570         aio_context_t ctx64;
571
572         mm_segment_t oldfs = get_fs();
573         if (unlikely(get_user(ctx64, ctx32p)))
574                 return -EFAULT;
575
576         set_fs(KERNEL_DS);
577         ret = sys_io_setup(nr_reqs, &ctx64);
578         set_fs(oldfs);
579         /* truncating is ok because it's a user address */
580         if (!ret)
581                 ret = put_user((u32) ctx64, ctx32p);
582         return ret;
583 }
584
585 asmlinkage long
586 compat_sys_io_getevents(aio_context_t ctx_id,
587                                  unsigned long min_nr,
588                                  unsigned long nr,
589                                  struct io_event *events,
590                                  struct compat_timespec *timeout)
591 {
592         long ret;
593         struct timespec t;
594         struct timespec *ut = NULL;
595
596         ret = -EFAULT;
597         if (unlikely(!access_ok(VERIFY_WRITE, events, 
598                                 nr * sizeof(struct io_event))))
599                 goto out;
600         if (timeout) {
601                 if (get_compat_timespec(&t, timeout))
602                         goto out;
603
604                 ut = compat_alloc_user_space(sizeof(*ut));
605                 if (copy_to_user(ut, &t, sizeof(t)) )
606                         goto out;
607         } 
608         ret = sys_io_getevents(ctx_id, min_nr, nr, events, ut);
609 out:
610         return ret;
611 }
612
613 static inline long
614 copy_iocb(long nr, u32 *ptr32, u64 *ptr64)
615 {
616         compat_uptr_t uptr;
617         int i;
618
619         for (i = 0; i < nr; ++i) {
620                 if (get_user(uptr, ptr32 + i))
621                         return -EFAULT;
622                 if (put_user((u64)compat_ptr(uptr), ptr64 + i))
623                         return -EFAULT;
624         }
625         return 0;
626 }
627
628 #define MAX_AIO_SUBMITS         (PAGE_SIZE/sizeof(struct iocb *))
629
630 asmlinkage long
631 compat_sys_io_submit(aio_context_t ctx_id, int nr, u32 *iocb)
632 {
633         struct iocb **iocb64; 
634         long ret;
635
636         if (unlikely(nr < 0))
637                 return -EINVAL;
638
639         if (nr > MAX_AIO_SUBMITS)
640                 nr = MAX_AIO_SUBMITS;
641         
642         iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64));
643         ret = copy_iocb(nr, iocb, (u64 *) iocb64);
644         if (!ret)
645                 ret = sys_io_submit(ctx_id, nr, iocb64);
646         return ret;
647 }
648
649 struct compat_ncp_mount_data {
650         compat_int_t version;
651         compat_uint_t ncp_fd;
652         compat_uid_t mounted_uid;
653         compat_pid_t wdog_pid;
654         unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
655         compat_uint_t time_out;
656         compat_uint_t retry_count;
657         compat_uint_t flags;
658         compat_uid_t uid;
659         compat_gid_t gid;
660         compat_mode_t file_mode;
661         compat_mode_t dir_mode;
662 };
663
664 struct compat_ncp_mount_data_v4 {
665         compat_int_t version;
666         compat_ulong_t flags;
667         compat_ulong_t mounted_uid;
668         compat_long_t wdog_pid;
669         compat_uint_t ncp_fd;
670         compat_uint_t time_out;
671         compat_uint_t retry_count;
672         compat_ulong_t uid;
673         compat_ulong_t gid;
674         compat_ulong_t file_mode;
675         compat_ulong_t dir_mode;
676 };
677
678 static void *do_ncp_super_data_conv(void *raw_data)
679 {
680         int version = *(unsigned int *)raw_data;
681
682         if (version == 3) {
683                 struct compat_ncp_mount_data *c_n = raw_data;
684                 struct ncp_mount_data *n = raw_data;
685
686                 n->dir_mode = c_n->dir_mode;
687                 n->file_mode = c_n->file_mode;
688                 n->gid = c_n->gid;
689                 n->uid = c_n->uid;
690                 memmove (n->mounted_vol, c_n->mounted_vol, (sizeof (c_n->mounted_vol) + 3 * sizeof (unsigned int)));
691                 n->wdog_pid = c_n->wdog_pid;
692                 n->mounted_uid = c_n->mounted_uid;
693         } else if (version == 4) {
694                 struct compat_ncp_mount_data_v4 *c_n = raw_data;
695                 struct ncp_mount_data_v4 *n = raw_data;
696
697                 n->dir_mode = c_n->dir_mode;
698                 n->file_mode = c_n->file_mode;
699                 n->gid = c_n->gid;
700                 n->uid = c_n->uid;
701                 n->retry_count = c_n->retry_count;
702                 n->time_out = c_n->time_out;
703                 n->ncp_fd = c_n->ncp_fd;
704                 n->wdog_pid = c_n->wdog_pid;
705                 n->mounted_uid = c_n->mounted_uid;
706                 n->flags = c_n->flags;
707         } else if (version != 5) {
708                 return NULL;
709         }
710
711         return raw_data;
712 }
713
714 struct compat_smb_mount_data {
715         compat_int_t version;
716         compat_uid_t mounted_uid;
717         compat_uid_t uid;
718         compat_gid_t gid;
719         compat_mode_t file_mode;
720         compat_mode_t dir_mode;
721 };
722
723 static void *do_smb_super_data_conv(void *raw_data)
724 {
725         struct smb_mount_data *s = raw_data;
726         struct compat_smb_mount_data *c_s = raw_data;
727
728         if (c_s->version != SMB_MOUNT_OLDVERSION)
729                 goto out;
730         s->dir_mode = c_s->dir_mode;
731         s->file_mode = c_s->file_mode;
732         s->gid = c_s->gid;
733         s->uid = c_s->uid;
734         s->mounted_uid = c_s->mounted_uid;
735  out:
736         return raw_data;
737 }
738
739 extern int copy_mount_options (const void __user *, unsigned long *);
740
741 #define SMBFS_NAME      "smbfs"
742 #define NCPFS_NAME      "ncpfs"
743
744 asmlinkage int compat_sys_mount(char __user * dev_name, char __user * dir_name,
745                                 char __user * type, unsigned long flags,
746                                 void __user * data)
747 {
748         unsigned long type_page;
749         unsigned long data_page;
750         unsigned long dev_page;
751         char *dir_page;
752         int retval;
753
754         retval = copy_mount_options (type, &type_page);
755         if (retval < 0)
756                 goto out;
757
758         dir_page = getname(dir_name);
759         retval = PTR_ERR(dir_page);
760         if (IS_ERR(dir_page))
761                 goto out1;
762
763         retval = copy_mount_options (dev_name, &dev_page);
764         if (retval < 0)
765                 goto out2;
766
767         retval = copy_mount_options (data, &data_page);
768         if (retval < 0)
769                 goto out3;
770
771         retval = -EINVAL;
772
773         if (type_page) {
774                 if (!strcmp((char *)type_page, SMBFS_NAME)) {
775                         do_smb_super_data_conv((void *)data_page);
776                 } else if (!strcmp((char *)type_page, NCPFS_NAME)) {
777                         do_ncp_super_data_conv((void *)data_page);
778                 }
779         }
780
781         lock_kernel();
782         retval = do_mount((char*)dev_page, dir_page, (char*)type_page,
783                         flags, (void*)data_page);
784         unlock_kernel();
785
786         free_page(data_page);
787  out3:
788         free_page(dev_page);
789  out2:
790         putname(dir_page);
791  out1:
792         free_page(type_page);
793  out:
794         return retval;
795 }
796