Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[sfrench/cifs-2.6.git] / arch / sparc64 / kernel / sys_sunos32.c
1 /* $Id: sys_sunos32.c,v 1.64 2002/02/09 19:49:31 davem Exp $
2  * sys_sunos32.c: SunOS binary compatibility layer on sparc64.
3  *
4  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
5  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
6  *
7  * Based upon preliminary work which is:
8  *
9  * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/types.h>
15 #include <linux/capability.h>
16 #include <linux/compat.h>
17 #include <linux/mman.h>
18 #include <linux/mm.h>
19 #include <linux/swap.h>
20 #include <linux/fs.h>
21 #include <linux/file.h>
22 #include <linux/resource.h>
23 #include <linux/ipc.h>
24 #include <linux/shm.h>
25 #include <linux/msg.h>
26 #include <linux/sem.h>
27 #include <linux/signal.h>
28 #include <linux/uio.h>
29 #include <linux/utsname.h>
30 #include <linux/major.h>
31 #include <linux/stat.h>
32 #include <linux/slab.h>
33 #include <linux/pagemap.h>
34 #include <linux/errno.h>
35 #include <linux/smp.h>
36 #include <linux/smp_lock.h>
37 #include <linux/syscalls.h>
38
39 #include <asm/uaccess.h>
40 #include <asm/page.h>
41 #include <asm/pgtable.h>
42 #include <asm/pconf.h>
43 #include <asm/idprom.h> /* for gethostid() */
44 #include <asm/unistd.h>
45 #include <asm/system.h>
46 #include <asm/compat_signal.h>
47
48 /* For the nfs mount emulation */
49 #include <linux/socket.h>
50 #include <linux/in.h>
51 #include <linux/nfs.h>
52 #include <linux/nfs2.h>
53 #include <linux/nfs_mount.h>
54
55 /* for sunos_select */
56 #include <linux/time.h>
57 #include <linux/personality.h>
58
59 /* For SOCKET_I */
60 #include <net/sock.h>
61 #include <net/compat.h>
62
63 #define SUNOS_NR_OPEN   256
64
65 asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off)
66 {
67         struct file *file = NULL;
68         unsigned long retval, ret_type;
69
70         if (flags & MAP_NORESERVE) {
71                 static int cnt;
72                 if (cnt++ < 10)
73                         printk("%s:  unimplemented SunOS MAP_NORESERVE mmap() flag\n",
74                                current->comm);
75                 flags &= ~MAP_NORESERVE;
76         }
77         retval = -EBADF;
78         if (!(flags & MAP_ANONYMOUS)) {
79                 struct inode * inode;
80                 if (fd >= SUNOS_NR_OPEN)
81                         goto out;
82                 file = fget(fd);
83                 if (!file)
84                         goto out;
85                 inode = file->f_path.dentry->d_inode;
86                 if (imajor(inode) == MEM_MAJOR && iminor(inode) == 5) {
87                         flags |= MAP_ANONYMOUS;
88                         fput(file);
89                         file = NULL;
90                 }
91         }
92
93         retval = -EINVAL;
94         if (!(flags & MAP_FIXED))
95                 addr = 0;
96         else if (len > 0xf0000000 || addr > 0xf0000000 - len)
97                 goto out_putf;
98         ret_type = flags & _MAP_NEW;
99         flags &= ~_MAP_NEW;
100
101         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
102         down_write(&current->mm->mmap_sem);
103         retval = do_mmap(file,
104                          (unsigned long) addr, (unsigned long) len,
105                          (unsigned long) prot, (unsigned long) flags,
106                          (unsigned long) off);
107         up_write(&current->mm->mmap_sem);
108         if (!ret_type)
109                 retval = ((retval < 0xf0000000) ? 0 : retval);
110 out_putf:
111         if (file)
112                 fput(file);
113 out:
114         return (u32) retval;
115 }
116
117 asmlinkage int sunos_mctl(u32 addr, u32 len, int function, u32 arg)
118 {
119         return 0;
120 }
121
122 asmlinkage int sunos_brk(u32 baddr)
123 {
124         int freepages, retval = -ENOMEM;
125         unsigned long rlim;
126         unsigned long newbrk, oldbrk, brk = (unsigned long) baddr;
127
128         down_write(&current->mm->mmap_sem);
129         if (brk < current->mm->end_code)
130                 goto out;
131         newbrk = PAGE_ALIGN(brk);
132         oldbrk = PAGE_ALIGN(current->mm->brk);
133         retval = 0;
134         if (oldbrk == newbrk) {
135                 current->mm->brk = brk;
136                 goto out;
137         }
138         /* Always allow shrinking brk. */
139         if (brk <= current->mm->brk) {
140                 current->mm->brk = brk;
141                 do_munmap(current->mm, newbrk, oldbrk-newbrk);
142                 goto out;
143         }
144         /* Check against rlimit and stack.. */
145         retval = -ENOMEM;
146         rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
147         if (rlim >= RLIM_INFINITY)
148                 rlim = ~0;
149         if (brk - current->mm->end_code > rlim)
150                 goto out;
151         /* Check against existing mmap mappings. */
152         if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE))
153                 goto out;
154         /* stupid algorithm to decide if we have enough memory: while
155          * simple, it hopefully works in most obvious cases.. Easy to
156          * fool it, but this should catch most mistakes.
157          */
158         freepages = global_page_state(NR_FILE_PAGES);
159         freepages >>= 1;
160         freepages += nr_free_pages();
161         freepages += nr_swap_pages;
162         freepages -= num_physpages >> 4;
163         freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
164         if (freepages < 0)
165                 goto out;
166         /* Ok, we have probably got enough memory - let it rip. */
167         current->mm->brk = brk;
168         do_brk(oldbrk, newbrk-oldbrk);
169         retval = 0;
170 out:
171         up_write(&current->mm->mmap_sem);
172         return retval;
173 }
174
175 asmlinkage u32 sunos_sbrk(int increment)
176 {
177         int error, oldbrk;
178
179         /* This should do it hopefully... */
180         oldbrk = (int)current->mm->brk;
181         error = sunos_brk(((int) current->mm->brk) + increment);
182         if (!error)
183                 error = oldbrk;
184         return error;
185 }
186
187 asmlinkage u32 sunos_sstk(int increment)
188 {
189         printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n",
190                current->comm, increment);
191
192         return (u32)-1;
193 }
194
195 /* Give hints to the kernel as to what paging strategy to use...
196  * Completely bogus, don't remind me.
197  */
198 #define VA_NORMAL     0 /* Normal vm usage expected */
199 #define VA_ABNORMAL   1 /* Abnormal/random vm usage probable */
200 #define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */
201 #define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */
202 static char *vstrings[] = {
203         "VA_NORMAL",
204         "VA_ABNORMAL",
205         "VA_SEQUENTIAL",
206         "VA_INVALIDATE",
207 };
208
209 asmlinkage void sunos_vadvise(u32 strategy)
210 {
211         static int count;
212
213         /* I wanna see who uses this... */
214         if (count++ < 5)
215                 printk("%s: Advises us to use %s paging strategy\n",
216                        current->comm,
217                        strategy <= 3 ? vstrings[strategy] : "BOGUS");
218 }
219
220 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
221  * resource limit and is for backwards compatibility with older sunos
222  * revs.
223  */
224 asmlinkage int sunos_getdtablesize(void)
225 {
226         return SUNOS_NR_OPEN;
227 }
228
229
230 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
231
232 asmlinkage u32 sunos_sigblock(u32 blk_mask)
233 {
234         u32 old;
235
236         spin_lock_irq(&current->sighand->siglock);
237         old = (u32) current->blocked.sig[0];
238         current->blocked.sig[0] |= (blk_mask & _BLOCKABLE);
239         recalc_sigpending();
240         spin_unlock_irq(&current->sighand->siglock);
241         return old;
242 }
243
244 asmlinkage u32 sunos_sigsetmask(u32 newmask)
245 {
246         u32 retval;
247
248         spin_lock_irq(&current->sighand->siglock);
249         retval = (u32) current->blocked.sig[0];
250         current->blocked.sig[0] = (newmask & _BLOCKABLE);
251         recalc_sigpending();
252         spin_unlock_irq(&current->sighand->siglock);
253         return retval;
254 }
255
256 /* SunOS getdents is very similar to the newer Linux (iBCS2 compliant)    */
257 /* getdents system call, the format of the structure just has a different */
258 /* layout (d_off+d_ino instead of d_ino+d_off) */
259 struct sunos_dirent {
260     s32         d_off;
261     u32         d_ino;
262     u16         d_reclen;
263     u16         d_namlen;
264     char        d_name[1];
265 };
266
267 struct sunos_dirent_callback {
268     struct sunos_dirent __user *curr;
269     struct sunos_dirent __user *previous;
270     int count;
271     int error;
272 };
273
274 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de)))
275 #define ROUND_UP(x) (((x)+sizeof(s32)-1) & ~(sizeof(s32)-1))
276
277 static int sunos_filldir(void * __buf, const char * name, int namlen,
278                          loff_t offset, ino_t ino, unsigned int d_type)
279 {
280         struct sunos_dirent __user *dirent;
281         struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf;
282         int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
283         u32 d_ino;
284
285         buf->error = -EINVAL;   /* only used if we fail.. */
286         if (reclen > buf->count)
287                 return -EINVAL;
288         d_ino = ino;
289         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
290                 return -EOVERFLOW;
291         dirent = buf->previous;
292         if (dirent)
293                 put_user(offset, &dirent->d_off);
294         dirent = buf->curr;
295         buf->previous = dirent;
296         put_user(d_ino, &dirent->d_ino);
297         put_user(namlen, &dirent->d_namlen);
298         put_user(reclen, &dirent->d_reclen);
299         if (copy_to_user(dirent->d_name, name, namlen))
300                 return -EFAULT;
301         put_user(0, dirent->d_name + namlen);
302         dirent = (void __user *) dirent + reclen;
303         buf->curr = dirent;
304         buf->count -= reclen;
305         return 0;
306 }
307
308 asmlinkage int sunos_getdents(unsigned int fd, void __user *dirent, int cnt)
309 {
310         struct file * file;
311         struct sunos_dirent __user *lastdirent;
312         struct sunos_dirent_callback buf;
313         int error = -EBADF;
314
315         if (fd >= SUNOS_NR_OPEN)
316                 goto out;
317
318         file = fget(fd);
319         if (!file)
320                 goto out;
321
322         error = -EINVAL;
323         if (cnt < (sizeof(struct sunos_dirent) + 255))
324                 goto out_putf;
325
326         buf.curr = (struct sunos_dirent __user *) dirent;
327         buf.previous = NULL;
328         buf.count = cnt;
329         buf.error = 0;
330
331         error = vfs_readdir(file, sunos_filldir, &buf);
332         if (error < 0)
333                 goto out_putf;
334
335         lastdirent = buf.previous;
336         error = buf.error;
337         if (lastdirent) {
338                 put_user(file->f_pos, &lastdirent->d_off);
339                 error = cnt - buf.count;
340         }
341
342 out_putf:
343         fput(file);
344 out:
345         return error;
346 }
347
348 /* Old sunos getdirentries, severely broken compatibility stuff here. */
349 struct sunos_direntry {
350     u32         d_ino;
351     u16         d_reclen;
352     u16         d_namlen;
353     char        d_name[1];
354 };
355
356 struct sunos_direntry_callback {
357     struct sunos_direntry __user *curr;
358     struct sunos_direntry __user *previous;
359     int count;
360     int error;
361 };
362
363 static int sunos_filldirentry(void * __buf, const char * name, int namlen,
364                               loff_t offset, ino_t ino, unsigned int d_type)
365 {
366         struct sunos_direntry __user *dirent;
367         struct sunos_direntry_callback * buf =
368                 (struct sunos_direntry_callback *) __buf;
369         int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
370         u32 d_ino;
371
372         buf->error = -EINVAL;   /* only used if we fail.. */
373         if (reclen > buf->count)
374                 return -EINVAL;
375         d_ino = ino;
376         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino)
377                 return -EOVERFLOW;
378         dirent = buf->previous;
379         dirent = buf->curr;
380         buf->previous = dirent;
381         put_user(d_ino, &dirent->d_ino);
382         put_user(namlen, &dirent->d_namlen);
383         put_user(reclen, &dirent->d_reclen);
384         if (copy_to_user(dirent->d_name, name, namlen))
385                 return -EFAULT;
386         put_user(0, dirent->d_name + namlen);
387         dirent = (void __user *) dirent + reclen;
388         buf->curr = dirent;
389         buf->count -= reclen;
390         return 0;
391 }
392
393 asmlinkage int sunos_getdirentries(unsigned int fd,
394                                    void __user *dirent,
395                                    int cnt,
396                                    unsigned int __user *basep)
397 {
398         struct file * file;
399         struct sunos_direntry __user *lastdirent;
400         int error = -EBADF;
401         struct sunos_direntry_callback buf;
402
403         if (fd >= SUNOS_NR_OPEN)
404                 goto out;
405
406         file = fget(fd);
407         if (!file)
408                 goto out;
409
410         error = -EINVAL;
411         if (cnt < (sizeof(struct sunos_direntry) + 255))
412                 goto out_putf;
413
414         buf.curr = (struct sunos_direntry __user *) dirent;
415         buf.previous = NULL;
416         buf.count = cnt;
417         buf.error = 0;
418
419         error = vfs_readdir(file, sunos_filldirentry, &buf);
420         if (error < 0)
421                 goto out_putf;
422
423         lastdirent = buf.previous;
424         error = buf.error;
425         if (lastdirent) {
426                 put_user(file->f_pos, basep);
427                 error = cnt - buf.count;
428         }
429
430 out_putf:
431         fput(file);
432 out:
433         return error;
434 }
435
436 struct sunos_utsname {
437         char sname[9];
438         char nname[9];
439         char nnext[56];
440         char rel[9];
441         char ver[9];
442         char mach[9];
443 };
444
445 asmlinkage int sunos_uname(struct sunos_utsname __user *name)
446 {
447         int ret;
448
449         down_read(&uts_sem);
450         ret = copy_to_user(&name->sname[0], &utsname()->sysname[0],
451                            sizeof(name->sname) - 1);
452         ret |= copy_to_user(&name->nname[0], &utsname()->nodename[0],
453                             sizeof(name->nname) - 1);
454         ret |= put_user('\0', &name->nname[8]);
455         ret |= copy_to_user(&name->rel[0], &utsname()->release[0],
456                             sizeof(name->rel) - 1);
457         ret |= copy_to_user(&name->ver[0], &utsname()->version[0],
458                             sizeof(name->ver) - 1);
459         ret |= copy_to_user(&name->mach[0], &utsname()->machine[0],
460                             sizeof(name->mach) - 1);
461         up_read(&uts_sem);
462         return (ret ? -EFAULT : 0);
463 }
464
465 asmlinkage int sunos_nosys(void)
466 {
467         struct pt_regs *regs;
468         siginfo_t info;
469         static int cnt;
470
471         regs = current_thread_info()->kregs;
472         if (test_thread_flag(TIF_32BIT)) {
473                 regs->tpc &= 0xffffffff;
474                 regs->tnpc &= 0xffffffff;
475         }
476         info.si_signo = SIGSYS;
477         info.si_errno = 0;
478         info.si_code = __SI_FAULT|0x100;
479         info.si_addr = (void __user *)regs->tpc;
480         info.si_trapno = regs->u_regs[UREG_G1];
481         send_sig_info(SIGSYS, &info, current);
482         if (cnt++ < 4) {
483                 printk("Process makes ni_syscall number %d, register dump:\n",
484                        (int) regs->u_regs[UREG_G1]);
485                 show_regs(regs);
486         }
487         return -ENOSYS;
488 }
489
490 /* This is not a real and complete implementation yet, just to keep
491  * the easy SunOS binaries happy.
492  */
493 asmlinkage int sunos_fpathconf(int fd, int name)
494 {
495         int ret;
496
497         switch(name) {
498         case _PCONF_LINK:
499                 ret = LINK_MAX;
500                 break;
501         case _PCONF_CANON:
502                 ret = MAX_CANON;
503                 break;
504         case _PCONF_INPUT:
505                 ret = MAX_INPUT;
506                 break;
507         case _PCONF_NAME:
508                 ret = NAME_MAX;
509                 break;
510         case _PCONF_PATH:
511                 ret = PATH_MAX;
512                 break;
513         case _PCONF_PIPE:
514                 ret = PIPE_BUF;
515                 break;
516         case _PCONF_CHRESTRICT:         /* XXX Investigate XXX */
517                 ret = 1;
518                 break;
519         case _PCONF_NOTRUNC:            /* XXX Investigate XXX */
520         case _PCONF_VDISABLE:
521                 ret = 0;
522                 break;
523         default:
524                 ret = -EINVAL;
525                 break;
526         }
527         return ret;
528 }
529
530 asmlinkage int sunos_pathconf(u32 u_path, int name)
531 {
532         int ret;
533
534         ret = sunos_fpathconf(0, name); /* XXX cheese XXX */
535         return ret;
536 }
537
538 asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x)
539 {
540         int ret;
541
542         /* SunOS binaries expect that select won't change the tvp contents */
543         ret = compat_sys_select(width, compat_ptr(inp), compat_ptr(outp),
544                                 compat_ptr(exp), compat_ptr(tvp_x));
545         if (ret == -EINTR && tvp_x) {
546                 struct compat_timeval __user *tvp = compat_ptr(tvp_x);
547                 time_t sec, usec;
548
549                 __get_user(sec, &tvp->tv_sec);
550                 __get_user(usec, &tvp->tv_usec);
551                 if (sec == 0 && usec == 0)
552                         ret = 0;
553         }
554         return ret;
555 }
556
557 asmlinkage void sunos_nop(void)
558 {
559         return;
560 }
561
562 #if 0 /* This code doesn't translate user pointers correctly,
563        * disable for now. -DaveM
564        */
565
566 /* XXXXXXXXXX SunOS mount/umount. XXXXXXXXXXX */
567 #define SMNT_RDONLY       1
568 #define SMNT_NOSUID       2
569 #define SMNT_NEWTYPE      4
570 #define SMNT_GRPID        8
571 #define SMNT_REMOUNT      16
572 #define SMNT_NOSUB        32
573 #define SMNT_MULTI        64
574 #define SMNT_SYS5         128
575
576 struct sunos_fh_t {
577         char fh_data [NFS_FHSIZE];
578 };
579
580 struct sunos_nfs_mount_args {
581         struct sockaddr_in  *addr; /* file server address */
582         struct nfs_fh *fh;     /* File handle to be mounted */
583         int        flags;      /* flags */
584         int        wsize;      /* write size in bytes */
585         int        rsize;      /* read size in bytes */
586         int        timeo;      /* initial timeout in .1 secs */
587         int        retrans;    /* times to retry send */
588         char       *hostname;  /* server's hostname */
589         int        acregmin;   /* attr cache file min secs */
590         int        acregmax;   /* attr cache file max secs */
591         int        acdirmin;   /* attr cache dir min secs */
592         int        acdirmax;   /* attr cache dir max secs */
593         char       *netname;   /* server's netname */
594 };
595
596
597 /* Bind the socket on a local reserved port and connect it to the
598  * remote server.  This on Linux/i386 is done by the mount program,
599  * not by the kernel. 
600  */
601 /* XXXXXXXXXXXXXXXXXXXX */
602 static int
603 sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
604 {
605         struct sockaddr_in local;
606         struct sockaddr_in server;
607         int    try_port;
608         int    ret;
609         struct socket *socket;
610         struct inode  *inode;
611         struct file   *file;
612
613         file = fget(fd);
614         if (!file)
615                 return 0;
616
617         inode = file->f_path.dentry->d_inode;
618
619         socket = SOCKET_I(inode);
620         local.sin_family = AF_INET;
621         local.sin_addr.s_addr = htonl(INADDR_ANY);
622
623         /* IPPORT_RESERVED = 1024, can't find the definition in the kernel */
624         try_port = 1024;
625         do {
626                 local.sin_port = htons (--try_port);
627                 ret = socket->ops->bind(socket, (struct sockaddr*)&local,
628                                         sizeof(local));
629         } while (ret && try_port > (1024 / 2));
630
631         if (ret) {
632                 fput(file);
633                 return 0;
634         }
635
636         server.sin_family = AF_INET;
637         server.sin_addr = addr->sin_addr;
638         server.sin_port = NFS_PORT;
639
640         /* Call sys_connect */
641         ret = socket->ops->connect (socket, (struct sockaddr *) &server,
642                                     sizeof (server), file->f_flags);
643         fput(file);
644         if (ret < 0)
645                 return 0;
646         return 1;
647 }
648
649 /* XXXXXXXXXXXXXXXXXXXX */
650 static int get_default (int value, int def_value)
651 {
652     if (value)
653         return value;
654     else
655         return def_value;
656 }
657
658 /* XXXXXXXXXXXXXXXXXXXX */
659 static int sunos_nfs_mount(char *dir_name, int linux_flags, void __user *data)
660 {
661         int  server_fd, err;
662         char *the_name, *mount_page;
663         struct nfs_mount_data linux_nfs_mount;
664         struct sunos_nfs_mount_args sunos_mount;
665
666         /* Ok, here comes the fun part: Linux's nfs mount needs a
667          * socket connection to the server, but SunOS mount does not
668          * require this, so we use the information on the destination
669          * address to create a socket and bind it to a reserved
670          * port on this system
671          */
672         if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount)))
673                 return -EFAULT;
674
675         server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
676         if (server_fd < 0)
677                 return -ENXIO;
678
679         if (copy_from_user(&linux_nfs_mount.addr, sunos_mount.addr,
680                            sizeof(*sunos_mount.addr)) ||
681             copy_from_user(&linux_nfs_mount.root, sunos_mount.fh,
682                            sizeof(*sunos_mount.fh))) {
683                 sys_close (server_fd);
684                 return -EFAULT;
685         }
686
687         if (!sunos_nfs_get_server_fd (server_fd, &linux_nfs_mount.addr)){
688                 sys_close (server_fd);
689                 return -ENXIO;
690         }
691
692         /* Now, bind it to a locally reserved port */
693         linux_nfs_mount.version  = NFS_MOUNT_VERSION;
694         linux_nfs_mount.flags    = sunos_mount.flags;
695         linux_nfs_mount.fd       = server_fd;
696         
697         linux_nfs_mount.rsize    = get_default (sunos_mount.rsize, 8192);
698         linux_nfs_mount.wsize    = get_default (sunos_mount.wsize, 8192);
699         linux_nfs_mount.timeo    = get_default (sunos_mount.timeo, 10);
700         linux_nfs_mount.retrans  = sunos_mount.retrans;
701         
702         linux_nfs_mount.acregmin = sunos_mount.acregmin;
703         linux_nfs_mount.acregmax = sunos_mount.acregmax;
704         linux_nfs_mount.acdirmin = sunos_mount.acdirmin;
705         linux_nfs_mount.acdirmax = sunos_mount.acdirmax;
706
707         the_name = getname(sunos_mount.hostname);
708         if (IS_ERR(the_name))
709                 return PTR_ERR(the_name);
710
711         strlcpy(linux_nfs_mount.hostname, the_name,
712                 sizeof(linux_nfs_mount.hostname));
713         putname (the_name);
714         
715         mount_page = (char *) get_zeroed_page(GFP_KERNEL);
716         if (!mount_page)
717                 return -ENOMEM;
718
719         memcpy(mount_page, &linux_nfs_mount, sizeof(linux_nfs_mount));
720
721         err = do_mount("", dir_name, "nfs", linux_flags, mount_page);
722
723         free_page((unsigned long) mount_page);
724         return err;
725 }
726
727 /* XXXXXXXXXXXXXXXXXXXX */
728 asmlinkage int
729 sunos_mount(char *type, char *dir, int flags, void *data)
730 {
731         int linux_flags = 0;
732         int ret = -EINVAL;
733         char *dev_fname = 0;
734         char *dir_page, *type_page;
735
736         if (!capable (CAP_SYS_ADMIN))
737                 return -EPERM;
738
739         /* We don't handle the integer fs type */
740         if ((flags & SMNT_NEWTYPE) == 0)
741                 goto out;
742
743         /* Do not allow for those flags we don't support */
744         if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5))
745                 goto out;
746
747         if (flags & SMNT_REMOUNT)
748                 linux_flags |= MS_REMOUNT;
749         if (flags & SMNT_RDONLY)
750                 linux_flags |= MS_RDONLY;
751         if (flags & SMNT_NOSUID)
752                 linux_flags |= MS_NOSUID;
753
754         dir_page = getname(dir);
755         ret = PTR_ERR(dir_page);
756         if (IS_ERR(dir_page))
757                 goto out;
758
759         type_page = getname(type);
760         ret = PTR_ERR(type_page);
761         if (IS_ERR(type_page))
762                 goto out1;
763
764         if (strcmp(type_page, "ext2") == 0) {
765                 dev_fname = getname(data);
766         } else if (strcmp(type_page, "iso9660") == 0) {
767                 dev_fname = getname(data);
768         } else if (strcmp(type_page, "minix") == 0) {
769                 dev_fname = getname(data);
770         } else if (strcmp(type_page, "nfs") == 0) {
771                 ret = sunos_nfs_mount (dir_page, flags, data);
772                 goto out2;
773         } else if (strcmp(type_page, "ufs") == 0) {
774                 printk("Warning: UFS filesystem mounts unsupported.\n");
775                 ret = -ENODEV;
776                 goto out2;
777         } else if (strcmp(type_page, "proc")) {
778                 ret = -ENODEV;
779                 goto out2;
780         }
781         ret = PTR_ERR(dev_fname);
782         if (IS_ERR(dev_fname))
783                 goto out2;
784         lock_kernel();
785         ret = do_mount(dev_fname, dir_page, type_page, linux_flags, NULL);
786         unlock_kernel();
787         if (dev_fname)
788                 putname(dev_fname);
789 out2:
790         putname(type_page);
791 out1:
792         putname(dir_page);
793 out:
794         return ret;
795 }
796 #endif
797
798 asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid)
799 {
800         int ret;
801
802         /* So stupid... */
803         if ((!pid || pid == current->pid) &&
804             !pgid) {
805                 sys_setsid();
806                 ret = 0;
807         } else {
808                 ret = sys_setpgid(pid, pgid);
809         }
810         return ret;
811 }
812
813 /* So stupid... */
814 extern long compat_sys_wait4(compat_pid_t, compat_uint_t __user *, int,
815                              struct compat_rusage __user *);
816
817 asmlinkage int sunos_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, struct compat_rusage __user *ru)
818 {
819         int ret;
820
821         ret = compat_sys_wait4((pid ? pid : ((compat_pid_t)-1)),
822                                stat_addr, options, ru);
823         return ret;
824 }
825
826 asmlinkage int sunos_killpg(int pgrp, int sig)
827 {
828         int ret;
829
830         rcu_read_lock();
831         ret = -EINVAL;
832         if (pgrp > 0)
833                 ret = kill_pgrp(find_vpid(pgrp), sig, 0);
834         rcu_read_unlock();
835
836         return ret;
837 }
838
839 asmlinkage int sunos_audit(void)
840 {
841         printk ("sys_audit\n");
842         return -1;
843 }
844
845 asmlinkage u32 sunos_gethostid(void)
846 {
847         u32 ret;
848
849         ret = (((u32)idprom->id_machtype << 24) | ((u32)idprom->id_sernum));
850
851         return ret;
852 }
853
854 /* sysconf options, for SunOS compatibility */
855 #define   _SC_ARG_MAX             1
856 #define   _SC_CHILD_MAX           2
857 #define   _SC_CLK_TCK             3
858 #define   _SC_NGROUPS_MAX         4
859 #define   _SC_OPEN_MAX            5
860 #define   _SC_JOB_CONTROL         6
861 #define   _SC_SAVED_IDS           7
862 #define   _SC_VERSION             8
863
864 asmlinkage s32 sunos_sysconf (int name)
865 {
866         s32 ret;
867
868         switch (name){
869         case _SC_ARG_MAX:
870                 ret = ARG_MAX;
871                 break;
872         case _SC_CHILD_MAX:
873                 ret = current->signal->rlim[RLIMIT_NPROC].rlim_cur;
874                 break;
875         case _SC_CLK_TCK:
876                 ret = HZ;
877                 break;
878         case _SC_NGROUPS_MAX:
879                 ret = NGROUPS_MAX;
880                 break;
881         case _SC_OPEN_MAX:
882                 ret = current->signal->rlim[RLIMIT_NOFILE].rlim_cur;
883                 break;
884         case _SC_JOB_CONTROL:
885                 ret = 1;        /* yes, we do support job control */
886                 break;
887         case _SC_SAVED_IDS:
888                 ret = 1;        /* yes, we do support saved uids  */
889                 break;
890         case _SC_VERSION:
891                 /* mhm, POSIX_VERSION is in /usr/include/unistd.h
892                  * should it go on /usr/include/linux?
893                  */
894                 ret = 199009;
895                 break;
896         default:
897                 ret = -1;
898                 break;
899         };
900         return ret;
901 }
902
903 asmlinkage int sunos_semsys(int op, u32 arg1, u32 arg2, u32 arg3, void __user *ptr)
904 {
905         union semun arg4;
906         int ret;
907
908         switch (op) {
909         case 0:
910                 /* Most arguments match on a 1:1 basis but cmd doesn't */
911                 switch(arg3) {
912                 case 4:
913                         arg3=GETPID; break;
914                 case 5:
915                         arg3=GETVAL; break;
916                 case 6:
917                         arg3=GETALL; break;
918                 case 3:
919                         arg3=GETNCNT; break;
920                 case 7:
921                         arg3=GETZCNT; break;
922                 case 8:
923                         arg3=SETVAL; break;
924                 case 9:
925                         arg3=SETALL; break;
926                 }
927                 /* sys_semctl(): */
928                 /* value to modify semaphore to */
929                 arg4.__pad = ptr;
930                 ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4);
931                 break;
932         case 1:
933                 /* sys_semget(): */
934                 ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3);
935                 break;
936         case 2:
937                 /* sys_semop(): */
938                 ret = sys_semop((int)arg1, (struct sembuf __user *)(unsigned long)arg2,
939                                 (unsigned int) arg3);
940                 break;
941         default:
942                 ret = -EINVAL;
943                 break;
944         };
945         return ret;
946 }
947
948 struct msgbuf32 {
949         s32 mtype;
950         char mtext[1];
951 };
952
953 struct ipc_perm32
954 {
955         key_t             key;
956         compat_uid_t  uid;
957         compat_gid_t  gid;
958         compat_uid_t  cuid;
959         compat_gid_t  cgid;
960         compat_mode_t mode;
961         unsigned short  seq;
962 };
963
964 struct msqid_ds32
965 {
966         struct ipc_perm32 msg_perm;
967         u32 msg_first;
968         u32 msg_last;
969         compat_time_t msg_stime;
970         compat_time_t msg_rtime;
971         compat_time_t msg_ctime;
972         u32 wwait;
973         u32 rwait;
974         unsigned short msg_cbytes;
975         unsigned short msg_qnum;  
976         unsigned short msg_qbytes;
977         compat_ipc_pid_t msg_lspid;
978         compat_ipc_pid_t msg_lrpid;
979 };
980
981 static inline int sunos_msqid_get(struct msqid_ds32 __user *user,
982                                   struct msqid_ds *kern)
983 {
984         if (get_user(kern->msg_perm.key, &user->msg_perm.key)           ||
985             __get_user(kern->msg_perm.uid, &user->msg_perm.uid)         ||
986             __get_user(kern->msg_perm.gid, &user->msg_perm.gid)         ||
987             __get_user(kern->msg_perm.cuid, &user->msg_perm.cuid)       ||
988             __get_user(kern->msg_perm.cgid, &user->msg_perm.cgid)       ||
989             __get_user(kern->msg_stime, &user->msg_stime)               ||
990             __get_user(kern->msg_rtime, &user->msg_rtime)               ||
991             __get_user(kern->msg_ctime, &user->msg_ctime)               ||
992             __get_user(kern->msg_ctime, &user->msg_cbytes)              ||
993             __get_user(kern->msg_ctime, &user->msg_qnum)                ||
994             __get_user(kern->msg_ctime, &user->msg_qbytes)              ||
995             __get_user(kern->msg_ctime, &user->msg_lspid)               ||
996             __get_user(kern->msg_ctime, &user->msg_lrpid))
997                 return -EFAULT;
998         return 0;
999 }
1000
1001 static inline int sunos_msqid_put(struct msqid_ds32 __user *user,
1002                                   struct msqid_ds *kern)
1003 {
1004         if (put_user(kern->msg_perm.key, &user->msg_perm.key)           ||
1005             __put_user(kern->msg_perm.uid, &user->msg_perm.uid)         ||
1006             __put_user(kern->msg_perm.gid, &user->msg_perm.gid)         ||
1007             __put_user(kern->msg_perm.cuid, &user->msg_perm.cuid)       ||
1008             __put_user(kern->msg_perm.cgid, &user->msg_perm.cgid)       ||
1009             __put_user(kern->msg_stime, &user->msg_stime)               ||
1010             __put_user(kern->msg_rtime, &user->msg_rtime)               ||
1011             __put_user(kern->msg_ctime, &user->msg_ctime)               ||
1012             __put_user(kern->msg_ctime, &user->msg_cbytes)              ||
1013             __put_user(kern->msg_ctime, &user->msg_qnum)                ||
1014             __put_user(kern->msg_ctime, &user->msg_qbytes)              ||
1015             __put_user(kern->msg_ctime, &user->msg_lspid)               ||
1016             __put_user(kern->msg_ctime, &user->msg_lrpid))
1017                 return -EFAULT;
1018         return 0;
1019 }
1020
1021 static inline int sunos_msgbuf_get(struct msgbuf32 __user *user, struct msgbuf *kern, int len)
1022 {
1023         if (get_user(kern->mtype, &user->mtype) ||
1024             __copy_from_user(kern->mtext, &user->mtext, len))
1025                 return -EFAULT;
1026         return 0;
1027 }
1028
1029 static inline int sunos_msgbuf_put(struct msgbuf32 __user *user, struct msgbuf *kern, int len)
1030 {
1031         if (put_user(kern->mtype, &user->mtype) ||
1032             __copy_to_user(user->mtext, kern->mtext, len))
1033                 return -EFAULT;
1034         return 0;
1035 }
1036
1037 asmlinkage int sunos_msgsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
1038 {
1039         struct sparc_stackf32 __user *sp;
1040         struct msqid_ds kds;
1041         struct msgbuf *kmbuf;
1042         mm_segment_t old_fs = get_fs();
1043         u32 arg5;
1044         int rval;
1045
1046         switch(op) {
1047         case 0:
1048                 rval = sys_msgget((key_t)arg1, (int)arg2);
1049                 break;
1050         case 1:
1051                 if (!sunos_msqid_get((struct msqid_ds32 __user *)(unsigned long)arg3, &kds)) {
1052                         set_fs(KERNEL_DS);
1053                         rval = sys_msgctl((int)arg1, (int)arg2,
1054                                           (struct msqid_ds __user *)(unsigned long)arg3);
1055                         set_fs(old_fs);
1056                         if (!rval)
1057                                 rval = sunos_msqid_put((struct msqid_ds32 __user *)(unsigned long)arg3,
1058                                                        &kds);
1059                 } else
1060                         rval = -EFAULT;
1061                 break;
1062         case 2:
1063                 rval = -EFAULT;
1064                 kmbuf = kmalloc(sizeof(struct msgbuf) + arg3,
1065                                                  GFP_KERNEL);
1066                 if (!kmbuf)
1067                         break;
1068                 sp = (struct sparc_stackf32 __user *)
1069                         (current_thread_info()->kregs->u_regs[UREG_FP] & 0xffffffffUL);
1070                 if (get_user(arg5, &sp->xxargs[0])) {
1071                         rval = -EFAULT;
1072                         kfree(kmbuf);
1073                         break;
1074                 }
1075                 set_fs(KERNEL_DS);
1076                 rval = sys_msgrcv((int)arg1, (struct msgbuf __user *) kmbuf,
1077                                   (size_t)arg3,
1078                                   (long)arg4, (int)arg5);
1079                 set_fs(old_fs);
1080                 if (!rval)
1081                         rval = sunos_msgbuf_put((struct msgbuf32 __user *)(unsigned long)arg2,
1082                                                 kmbuf, arg3);
1083                 kfree(kmbuf);
1084                 break;
1085         case 3:
1086                 rval = -EFAULT;
1087                 kmbuf = kmalloc(sizeof(struct msgbuf) + arg3,
1088                                                  GFP_KERNEL);
1089                 if (!kmbuf || sunos_msgbuf_get((struct msgbuf32 __user *)(unsigned long)arg2,
1090                                                kmbuf, arg3))
1091                         break;
1092                 set_fs(KERNEL_DS);
1093                 rval = sys_msgsnd((int)arg1, (struct msgbuf __user *) kmbuf,
1094                                   (size_t)arg3, (int)arg4);
1095                 set_fs(old_fs);
1096                 kfree(kmbuf);
1097                 break;
1098         default:
1099                 rval = -EINVAL;
1100                 break;
1101         }
1102         return rval;
1103 }
1104
1105 struct shmid_ds32 {
1106         struct ipc_perm32       shm_perm;
1107         int                     shm_segsz;
1108         compat_time_t         shm_atime;
1109         compat_time_t         shm_dtime;
1110         compat_time_t         shm_ctime;
1111         compat_ipc_pid_t    shm_cpid; 
1112         compat_ipc_pid_t    shm_lpid; 
1113         unsigned short          shm_nattch;
1114 };
1115                                                         
1116 static inline int sunos_shmid_get(struct shmid_ds32 __user *user,
1117                                   struct shmid_ds *kern)
1118 {
1119         if (get_user(kern->shm_perm.key, &user->shm_perm.key)           ||
1120             __get_user(kern->shm_perm.uid, &user->shm_perm.uid)         ||
1121             __get_user(kern->shm_perm.gid, &user->shm_perm.gid)         ||
1122             __get_user(kern->shm_perm.cuid, &user->shm_perm.cuid)       ||
1123             __get_user(kern->shm_perm.cgid, &user->shm_perm.cgid)       ||
1124             __get_user(kern->shm_segsz, &user->shm_segsz)               ||
1125             __get_user(kern->shm_atime, &user->shm_atime)               ||
1126             __get_user(kern->shm_dtime, &user->shm_dtime)               ||
1127             __get_user(kern->shm_ctime, &user->shm_ctime)               ||
1128             __get_user(kern->shm_cpid, &user->shm_cpid)                 ||
1129             __get_user(kern->shm_lpid, &user->shm_lpid)                 ||
1130             __get_user(kern->shm_nattch, &user->shm_nattch))
1131                 return -EFAULT;
1132         return 0;
1133 }
1134
1135 static inline int sunos_shmid_put(struct shmid_ds32 __user *user,
1136                                   struct shmid_ds *kern)
1137 {
1138         if (put_user(kern->shm_perm.key, &user->shm_perm.key)           ||
1139             __put_user(kern->shm_perm.uid, &user->shm_perm.uid)         ||
1140             __put_user(kern->shm_perm.gid, &user->shm_perm.gid)         ||
1141             __put_user(kern->shm_perm.cuid, &user->shm_perm.cuid)       ||
1142             __put_user(kern->shm_perm.cgid, &user->shm_perm.cgid)       ||
1143             __put_user(kern->shm_segsz, &user->shm_segsz)               ||
1144             __put_user(kern->shm_atime, &user->shm_atime)               ||
1145             __put_user(kern->shm_dtime, &user->shm_dtime)               ||
1146             __put_user(kern->shm_ctime, &user->shm_ctime)               ||
1147             __put_user(kern->shm_cpid, &user->shm_cpid)                 ||
1148             __put_user(kern->shm_lpid, &user->shm_lpid)                 ||
1149             __put_user(kern->shm_nattch, &user->shm_nattch))
1150                 return -EFAULT;
1151         return 0;
1152 }
1153
1154 asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3)
1155 {
1156         struct shmid_ds ksds;
1157         unsigned long raddr;
1158         mm_segment_t old_fs = get_fs();
1159         int rval;
1160
1161         switch(op) {
1162         case 0:
1163                 /* do_shmat(): attach a shared memory area */
1164                 rval = do_shmat((int)arg1,(char __user *)(unsigned long)arg2,(int)arg3,&raddr);
1165                 if (!rval)
1166                         rval = (int) raddr;
1167                 break;
1168         case 1:
1169                 /* sys_shmctl(): modify shared memory area attr. */
1170                 if (!sunos_shmid_get((struct shmid_ds32 __user *)(unsigned long)arg3, &ksds)) {
1171                         set_fs(KERNEL_DS);
1172                         rval = sys_shmctl((int) arg1,(int) arg2,
1173                                           (struct shmid_ds __user *) &ksds);
1174                         set_fs(old_fs);
1175                         if (!rval)
1176                                 rval = sunos_shmid_put((struct shmid_ds32 __user *)(unsigned long)arg3,
1177                                                        &ksds);
1178                 } else
1179                         rval = -EFAULT;
1180                 break;
1181         case 2:
1182                 /* sys_shmdt(): detach a shared memory area */
1183                 rval = sys_shmdt((char __user *)(unsigned long)arg1);
1184                 break;
1185         case 3:
1186                 /* sys_shmget(): get a shared memory area */
1187                 rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3);
1188                 break;
1189         default:
1190                 rval = -EINVAL;
1191                 break;
1192         };
1193         return rval;
1194 }
1195
1196 extern asmlinkage long sparc32_open(const char __user * filename, int flags, int mode);
1197
1198 asmlinkage int sunos_open(u32 fname, int flags, int mode)
1199 {
1200         const char __user *filename = compat_ptr(fname);
1201
1202         return sparc32_open(filename, flags, mode);
1203 }
1204
1205 #define SUNOS_EWOULDBLOCK 35
1206
1207 /* see the sunos man page read(2v) for an explanation
1208    of this garbage. We use O_NDELAY to mark
1209    file descriptors that have been set non-blocking 
1210    using 4.2BSD style calls. (tridge) */
1211
1212 static inline int check_nonblock(int ret, int fd)
1213 {
1214         if (ret == -EAGAIN) {
1215                 struct file * file = fget(fd);
1216                 if (file) {
1217                         if (file->f_flags & O_NDELAY)
1218                                 ret = -SUNOS_EWOULDBLOCK;
1219                         fput(file);
1220                 }
1221         }
1222         return ret;
1223 }
1224
1225 asmlinkage int sunos_read(unsigned int fd, char __user *buf, u32 count)
1226 {
1227         int ret;
1228
1229         ret = check_nonblock(sys_read(fd, buf, count), fd);
1230         return ret;
1231 }
1232
1233 asmlinkage int sunos_readv(u32 fd, void __user *vector, s32 count)
1234 {
1235         int ret;
1236
1237         ret = check_nonblock(compat_sys_readv(fd, vector, count), fd);
1238         return ret;
1239 }
1240
1241 asmlinkage int sunos_write(unsigned int fd, char __user *buf, u32 count)
1242 {
1243         int ret;
1244
1245         ret = check_nonblock(sys_write(fd, buf, count), fd);
1246         return ret;
1247 }
1248
1249 asmlinkage int sunos_writev(u32 fd, void __user *vector, s32 count)
1250 {
1251         int ret;
1252
1253         ret = check_nonblock(compat_sys_writev(fd, vector, count), fd);
1254         return ret;
1255 }
1256
1257 asmlinkage int sunos_recv(u32 __fd, void __user *ubuf, int size, unsigned flags)
1258 {
1259         int ret, fd = (int) __fd;
1260
1261         ret = check_nonblock(sys_recv(fd, ubuf, size, flags), fd);
1262         return ret;
1263 }
1264
1265 asmlinkage int sunos_send(u32 __fd, void __user *buff, int len, unsigned flags)
1266 {
1267         int ret, fd = (int) __fd;
1268
1269         ret = check_nonblock(sys_send(fd, buff, len, flags), fd);
1270         return ret;
1271 }
1272
1273 asmlinkage int sunos_accept(u32 __fd, struct sockaddr __user *sa, int __user *addrlen)
1274 {
1275         int ret, fd = (int) __fd;
1276
1277         while (1) {
1278                 ret = check_nonblock(sys_accept(fd, sa, addrlen), fd);
1279                 if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
1280                         break;
1281         }
1282         return ret;
1283 }
1284
1285 #define SUNOS_SV_INTERRUPT 2
1286
1287 asmlinkage int sunos_sigaction (int sig,
1288                                 struct old_sigaction32 __user *act,
1289                                 struct old_sigaction32 __user *oact)
1290 {
1291         struct k_sigaction new_ka, old_ka;
1292         int ret;
1293
1294         if (act) {
1295                 compat_old_sigset_t mask;
1296                 u32 u_handler;
1297
1298                 if (get_user(u_handler, &act->sa_handler) ||
1299                     __get_user(new_ka.sa.sa_flags, &act->sa_flags))
1300                         return -EFAULT;
1301                 new_ka.sa.sa_handler = compat_ptr(u_handler);
1302                 __get_user(mask, &act->sa_mask);
1303                 new_ka.sa.sa_restorer = NULL;
1304                 new_ka.ka_restorer = NULL;
1305                 siginitset(&new_ka.sa.sa_mask, mask);
1306                 new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
1307         }
1308
1309         ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
1310
1311         if (!ret && oact) {
1312                 old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT;
1313                 if (put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler) ||
1314                     __put_user(old_ka.sa.sa_flags, &oact->sa_flags))
1315                         return -EFAULT;
1316                 __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
1317         }
1318
1319         return ret;
1320 }
1321
1322 asmlinkage int sunos_setsockopt(u32 __fd, u32 __level, u32 __optname,
1323                                 char __user *optval, u32 __optlen)
1324 {
1325         int fd = (int) __fd;
1326         int level = (int) __level;
1327         int optname = (int) __optname;
1328         int optlen = (int) __optlen;
1329         int tr_opt = optname;
1330         int ret;
1331
1332         if (level == SOL_IP) {
1333                 /* Multicast socketopts (ttl, membership) */
1334                 if (tr_opt >=2 && tr_opt <= 6)
1335                         tr_opt += 30;
1336         }
1337         ret = sys_setsockopt(fd, level, tr_opt,
1338                              optval, optlen);
1339         return ret;
1340 }
1341
1342 asmlinkage int sunos_getsockopt(u32 __fd, u32 __level, u32 __optname,
1343                                 char __user *optval, int __user *optlen)
1344 {
1345         int fd = (int) __fd;
1346         int level = (int) __level;
1347         int optname = (int) __optname;
1348         int tr_opt = optname;
1349         int ret;
1350
1351         if (level == SOL_IP) {
1352                 /* Multicast socketopts (ttl, membership) */
1353                 if (tr_opt >=2 && tr_opt <= 6)
1354                         tr_opt += 30;
1355         }
1356         ret = compat_sys_getsockopt(fd, level, tr_opt,
1357                                     optval, optlen);
1358         return ret;
1359 }