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