code:
/*	$OpenBSD: linux_ipc.c,v 1.18 2014/09/14 14:17:23 jsg Exp $	*/
/*	$NetBSD: linux_ipc.c,v 1.10 1996/04/05 00:01:44 christos Exp $	*/

/*
 * Copyright (c) 1995 Frank van der Linden
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project
 *      by Frank van der Linden
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/systm.h>
#include <sys/stat.h>

#include <sys/mount.h>
#include <sys/syscallargs.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_ipc.h>
#include <compat/linux/linux_msg.h>
#include <compat/linux/linux_shm.h>
#include <compat/linux/linux_sem.h>
#include <compat/linux/linux_ipccall.h>

/*
 * Stuff to deal with the SysV ipc/shm/semaphore interface in Linux.
 * The main difference is, that Linux handles it all via one
 * system call, which has the usual maximum amount of 5 arguments.
 * This results in a kludge for calls that take 6 of them.
 *
 * The SYSVXXXX options have to be enabled to get the appropriate
 * functions to work.
 */

#ifdef SYSVSEM
int linux_semop(struct proc *, void *, register_t *);
int linux_semget(struct proc *, void *, register_t *);
int linux_semctl(struct proc *, void *, register_t *);
int bsd_to_linux_semid_ds(struct semid_ds *, struct linux_semid_ds *);
void linux_to_bsd_semid_ds(struct linux_semid_ds *, struct semid_ds *);
#endif

#ifdef SYSVMSG
int linux_msgsnd(struct proc *, void *, register_t *);
int linux_msgrcv(struct proc *, void *, register_t *);
int linux_msgget(struct proc *, void *, register_t *);
int linux_msgctl(struct proc *, void *, register_t *);
void linux_to_bsd_msqid_ds(struct linux_msqid_ds *, struct msqid_ds *);
int bsd_to_linux_msqid_ds(struct msqid_ds *, struct linux_msqid_ds *);
#endif

#ifdef SYSVSHM
int linux_shmat(struct proc *, void *, register_t *);
int linux_shmdt(struct proc *, void *, register_t *);
int linux_shmget(struct proc *, void *, register_t *);
int linux_shmctl(struct proc *, void *, register_t *);
void linux_to_bsd_shmid_ds(struct linux_shmid_ds *, struct shmid_ds *);
int bsd_to_linux_shmid_ds(struct shmid_ds *, struct linux_shmid_ds *);
#endif

#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
void linux_to_bsd_ipc_perm(struct linux_ipc_perm *, struct ipc_perm *);
void bsd_to_linux_ipc_perm(struct ipc_perm *, struct linux_ipc_perm *);
#endif

int
linux_sys_ipc(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;

	switch (SCARG(uap, what)) {
#ifdef SYSVSEM
	case LINUX_SYS_semop:
		return linux_semop(p, uap, retval);
	case LINUX_SYS_semget:
		return linux_semget(p, uap, retval);
	case LINUX_SYS_semctl:
		return linux_semctl(p, uap, retval);
#endif
#ifdef SYSVMSG
	case LINUX_SYS_msgsnd:
		return linux_msgsnd(p, uap, retval);
	case LINUX_SYS_msgrcv:
		return linux_msgrcv(p, uap, retval);
	case LINUX_SYS_msgget:
		return linux_msgget(p, uap, retval);
	case LINUX_SYS_msgctl:
		return linux_msgctl(p, uap, retval);
#endif
#ifdef SYSVSHM
	case LINUX_SYS_shmat:
		return linux_shmat(p, uap, retval);
	case LINUX_SYS_shmdt:
		return linux_shmdt(p, uap, retval);
	case LINUX_SYS_shmget:
		return linux_shmget(p, uap, retval);
	case LINUX_SYS_shmctl:
		return linux_shmctl(p, uap, retval);
#endif
	default:
		return ENOSYS;
	}
}

#if defined(SYSVMSG) || defined(SYSVSEM) || defined(SYSVSHM)
/*
 * Convert between Linux and OpenBSD ipc_perm structures. Only the
 * order of the fields is different.
 */
void
linux_to_bsd_ipc_perm(lpp, bpp)
	struct linux_ipc_perm *lpp;
	struct ipc_perm *bpp;
{

	bpp->key = lpp->l_key;
	bpp->uid = lpp->l_uid;
	bpp->gid = lpp->l_gid;
	bpp->cuid = lpp->l_cuid;
	bpp->cgid = lpp->l_cgid;
	bpp->mode = lpp->l_mode;
	bpp->seq = lpp->l_seq;
}

void
bsd_to_linux_ipc_perm(bpp, lpp)
	struct ipc_perm *bpp;
	struct linux_ipc_perm *lpp;
{

	lpp->l_key = bpp->key;
	lpp->l_uid = bpp->uid;
	lpp->l_gid = bpp->gid;
	lpp->l_cuid = bpp->cuid;
	lpp->l_cgid = bpp->cgid;
	lpp->l_mode = bpp->mode;
	lpp->l_seq = bpp->seq;
}
#endif

#ifdef SYSVSEM
/*
 * Semaphore operations. Most constants and structures are the same on
 * both systems. Only semctl() needs some extra work.
 */

/*
 * Convert between Linux and OpenBSD semid_ds structures.
 */
int
bsd_to_linux_semid_ds(bs, ls)
	struct semid_ds *bs;
	struct linux_semid_ds *ls;
{

	bsd_to_linux_ipc_perm(&bs->sem_perm, &ls->l_sem_perm);
	if (bs->sem_otime > LINUX_TIME_MAX)
		return EOVERFLOW;
	ls->l_sem_otime = (linux_time_t)bs->sem_otime;
	if (bs->sem_ctime > LINUX_TIME_MAX)
		return EOVERFLOW;
	ls->l_sem_ctime = (linux_time_t)bs->sem_ctime;
	ls->l_sem_nsems = bs->sem_nsems;
	ls->l_sem_base = bs->sem_base;

	return 0;
}

void
linux_to_bsd_semid_ds(ls, bs)
	struct linux_semid_ds *ls;
	struct semid_ds *bs;
{

	linux_to_bsd_ipc_perm(&ls->l_sem_perm, &bs->sem_perm);
	bs->sem_otime = ls->l_sem_otime;
	bs->sem_ctime = ls->l_sem_ctime;
	bs->sem_nsems = ls->l_sem_nsems;
	bs->sem_base = ls->l_sem_base;
}

int
linux_semop(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_semop_args bsa;

	SCARG(&bsa, semid) = SCARG(uap, a1);
	SCARG(&bsa, sops) = (struct sembuf *)SCARG(uap, ptr);
	SCARG(&bsa, nsops) = SCARG(uap, a2);

	return sys_semop(p, &bsa, retval);
}

int
linux_semget(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_semget_args bsa;

	SCARG(&bsa, key) = (key_t)SCARG(uap, a1);
	SCARG(&bsa, nsems) = SCARG(uap, a2);
	SCARG(&bsa, semflg) = SCARG(uap, a3);

	return sys_semget(p, &bsa, retval);
}

/*
 * Most of this can be handled by directly passing the arguments on,
 * buf IPC_* require a lot of copy{in,out} because of the extra indirection
 * (we are passed a pointer to a union cointaining a pointer to a semid_ds
 * structure.
 */
int
linux_semctl(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	caddr_t sg, unptr, dsp, ldsp;
	int error, cmd;
	struct sys___semctl_args bsa;
	struct linux_semid_ds lm;
	struct semid_ds bm;

	SCARG(&bsa, semid) = SCARG(uap, a1);
	SCARG(&bsa, semnum) = SCARG(uap, a2);
	SCARG(&bsa, cmd) = SCARG(uap, a3);
	SCARG(&bsa, arg) = (union semun *)SCARG(uap, ptr);
	switch(SCARG(uap, a3)) {
	case LINUX_GETVAL:
		cmd = GETVAL;
		break;
	case LINUX_GETPID:
		cmd = GETPID;
		break;
	case LINUX_GETNCNT:
		cmd = GETNCNT;
		break;
	case LINUX_GETZCNT:
		cmd = GETZCNT;
		break;
	case LINUX_SETVAL:
		cmd = SETVAL;
		break;
	case LINUX_IPC_RMID:
		cmd = IPC_RMID;
		break;
	case LINUX_IPC_SET:
		if ((error = copyin(SCARG(uap, ptr), &ldsp, sizeof ldsp)))
			return error;
		if ((error = copyin(ldsp, (caddr_t)&lm, sizeof lm)))
			return error;
		linux_to_bsd_semid_ds(&lm, &bm);
		sg = stackgap_init(p);
		unptr = stackgap_alloc(&sg, sizeof (union semun));
		dsp = stackgap_alloc(&sg, sizeof (struct semid_ds));
		if ((error = copyout((caddr_t)&bm, dsp, sizeof bm)))
			return error;
		if ((error = copyout((caddr_t)&dsp, unptr, sizeof dsp)))
			return error;
		SCARG(&bsa, arg) = (union semun *)unptr;
		return sys___semctl(p, &bsa, retval);
	case LINUX_IPC_STAT:
		sg = stackgap_init(p);
		unptr = stackgap_alloc(&sg, sizeof (union semun));
		dsp = stackgap_alloc(&sg, sizeof (struct semid_ds));
		if ((error = copyout((caddr_t)&dsp, unptr, sizeof dsp)))
			return error;
		SCARG(&bsa, arg) = (union semun *)unptr;
		if ((error = sys___semctl(p, &bsa, retval)))
			return error;
		if ((error = copyin(dsp, (caddr_t)&bm, sizeof bm)))
			return error;
		if ((error = bsd_to_linux_semid_ds(&bm, &lm)))
			return error;
		if ((error = copyin(SCARG(uap, ptr), &ldsp, sizeof ldsp)))
			return error;
		return copyout((caddr_t)&lm, ldsp, sizeof lm);
	default:
		return EINVAL;
	}
	SCARG(&bsa, cmd) = cmd;

	return sys___semctl(p, &bsa, retval);
}
#endif /* SYSVSEM */

#ifdef SYSVMSG

void
linux_to_bsd_msqid_ds(lmp, bmp)
	struct linux_msqid_ds *lmp;
	struct msqid_ds *bmp;
{

	linux_to_bsd_ipc_perm(&lmp->l_msg_perm, &bmp->msg_perm);
	bmp->msg_first = lmp->l_msg_first;
	bmp->msg_last = lmp->l_msg_last;
	bmp->msg_cbytes = lmp->l_msg_cbytes;
	bmp->msg_qnum = lmp->l_msg_qnum;
	bmp->msg_qbytes = lmp->l_msg_qbytes;
	bmp->msg_lspid = lmp->l_msg_lspid;
	bmp->msg_lrpid = lmp->l_msg_lrpid;
	bmp->msg_stime = lmp->l_msg_stime;
	bmp->msg_rtime = lmp->l_msg_rtime;
	bmp->msg_ctime = lmp->l_msg_ctime;
}

int
bsd_to_linux_msqid_ds(bmp, lmp)
	struct msqid_ds *bmp;
	struct linux_msqid_ds *lmp;
{

	bsd_to_linux_ipc_perm(&bmp->msg_perm, &lmp->l_msg_perm);
	lmp->l_msg_first = bmp->msg_first;
	lmp->l_msg_last = bmp->msg_last;
	lmp->l_msg_cbytes = bmp->msg_cbytes;
	lmp->l_msg_qnum = bmp->msg_qnum;
	lmp->l_msg_qbytes = bmp->msg_qbytes;
	lmp->l_msg_lspid = bmp->msg_lspid;
	lmp->l_msg_lrpid = bmp->msg_lrpid;

	if (bmp->msg_stime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lmp->l_msg_stime = (linux_time_t)bmp->msg_stime;
	if (bmp->msg_rtime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lmp->l_msg_rtime = (linux_time_t)bmp->msg_rtime;
	if (bmp->msg_ctime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lmp->l_msg_ctime = (linux_time_t)bmp->msg_ctime;

	return 0;
}

int
linux_msgsnd(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_msgsnd_args bma;

	SCARG(&bma, msqid) = SCARG(uap, a1);
	SCARG(&bma, msgp) = SCARG(uap, ptr);
	SCARG(&bma, msgsz) = SCARG(uap, a2);
	SCARG(&bma, msgflg) = SCARG(uap, a3);

	return sys_msgsnd(p, &bma, retval);
}

int
linux_msgrcv(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_msgrcv_args bma;
	struct linux_msgrcv_msgarg kluge;
	int error;

	if ((error = copyin(SCARG(uap, ptr), &kluge, sizeof kluge)))
		return error;

	SCARG(&bma, msqid) = SCARG(uap, a1);
	SCARG(&bma, msgp) = kluge.msg;
	SCARG(&bma, msgsz) = SCARG(uap, a2);
	SCARG(&bma, msgtyp) = kluge.type;
	SCARG(&bma, msgflg) = SCARG(uap, a3);

	return sys_msgrcv(p, &bma, retval);
}

int
linux_msgget(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_msgget_args bma;

	SCARG(&bma, key) = (key_t)SCARG(uap, a1);
	SCARG(&bma, msgflg) = SCARG(uap, a2);

	return sys_msgget(p, &bma, retval);
}

int
linux_msgctl(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_msgctl_args bma;
	caddr_t umsgptr, sg;
	struct linux_msqid_ds lm;
	struct msqid_ds bm;
	int error;

	SCARG(&bma, msqid) = SCARG(uap, a1);
	SCARG(&bma, cmd) = SCARG(uap, a2);
	switch (SCARG(uap, a2)) {
	case LINUX_IPC_RMID:
		return sys_msgctl(p, &bma, retval);
	case LINUX_IPC_SET:
		if ((error = copyin(SCARG(uap, ptr), (caddr_t)&lm, sizeof lm)))
			return error;
		linux_to_bsd_msqid_ds(&lm, &bm);
		sg = stackgap_init(p);
		umsgptr = stackgap_alloc(&sg, sizeof bm);
		if ((error = copyout((caddr_t)&bm, umsgptr, sizeof bm)))
			return error;
		SCARG(&bma, buf) = (struct msqid_ds *)umsgptr;
		return sys_msgctl(p, &bma, retval);
	case LINUX_IPC_STAT:
		sg = stackgap_init(p);
		umsgptr = stackgap_alloc(&sg, sizeof (struct msqid_ds));
		SCARG(&bma, buf) = (struct msqid_ds *)umsgptr;
		if ((error = sys_msgctl(p, &bma, retval)))
			return error;
		if ((error = copyin(umsgptr, (caddr_t)&bm, sizeof bm)))
			return error;
		if ((error = bsd_to_linux_msqid_ds(&bm, &lm)))
			return error;
		return copyout((caddr_t)&lm, SCARG(uap, ptr), sizeof lm);
	}
	return EINVAL;
}
#endif /* SYSVMSG */

#ifdef SYSVSHM
/*
 * shmat(2). Very straightforward, except that Linux passes a pointer
 * in which the return value is to be passed. This is subsequently
 * handled by libc, apparently.
 */
int
linux_shmat(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_shmat_args bsa;
	int error;

	SCARG(&bsa, shmid) = SCARG(uap, a1);
	SCARG(&bsa, shmaddr) = SCARG(uap, ptr);
	SCARG(&bsa, shmflg) = SCARG(uap, a2);

	if ((error = sys_shmat(p, &bsa, retval)))
		return error;

	if ((error = copyout(&retval[0], (caddr_t) SCARG(uap, a3),
	     sizeof retval[0])))
		return error;

	retval[0] = 0;
	return 0;
}

/*
 * shmdt(): this could have been mapped directly, if it wasn't for
 * the extra indirection by the linux_ipc system call.
 */
int
linux_shmdt(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_shmdt_args bsa;

	SCARG(&bsa, shmaddr) = SCARG(uap, ptr);

	return sys_shmdt(p, &bsa, retval);
}

/*
 * Same story as shmdt.
 */
int
linux_shmget(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	struct sys_shmget_args bsa;

	SCARG(&bsa, key) = SCARG(uap, a1);
	SCARG(&bsa, size) = SCARG(uap, a2);
	SCARG(&bsa, shmflg) = SCARG(uap, a3);

	return sys_shmget(p, &bsa, retval);
}

/*
 * Convert between Linux and OpenBSD shmid_ds structures.
 * The order of the fields is once again the difference, and
 * we also need a place to store the internal data pointer
 * in, which is unfortunately stored in this structure.
 *
 * We abuse a Linux internal field for that.
 */
void
linux_to_bsd_shmid_ds(lsp, bsp)
	struct linux_shmid_ds *lsp;
	struct shmid_ds *bsp;
{

	linux_to_bsd_ipc_perm(&lsp->l_shm_perm, &bsp->shm_perm);
	bsp->shm_segsz = lsp->l_shm_segsz;
	bsp->shm_lpid = lsp->l_shm_lpid;
	bsp->shm_cpid = lsp->l_shm_cpid;
	bsp->shm_nattch = lsp->l_shm_nattch;
	bsp->shm_atime = lsp->l_shm_atime;
	bsp->shm_dtime = lsp->l_shm_dtime;
	bsp->shm_ctime = lsp->l_shm_ctime;
	bsp->shm_internal = lsp->l_private2;	/* XXX Oh well. */
}

int
bsd_to_linux_shmid_ds(bsp, lsp)
	struct shmid_ds *bsp;
	struct linux_shmid_ds *lsp;
{

	bsd_to_linux_ipc_perm(&bsp->shm_perm, &lsp->l_shm_perm);
	lsp->l_shm_segsz = bsp->shm_segsz;
	lsp->l_shm_lpid = bsp->shm_lpid;
	lsp->l_shm_cpid = bsp->shm_cpid;
	lsp->l_shm_nattch = bsp->shm_nattch;
	if (bsp->shm_atime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lsp->l_shm_atime = (linux_time_t)bsp->shm_atime;
	if (bsp->shm_dtime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lsp->l_shm_dtime = (linux_time_t)bsp->shm_dtime;
	if (bsp->shm_ctime > LINUX_TIME_MAX)
		return EOVERFLOW;
	lsp->l_shm_ctime = (linux_time_t)bsp->shm_ctime;
	lsp->l_private2 = bsp->shm_internal;	/* XXX */

	return 0;
}

/*
 * shmctl. Not implemented (for now): IPC_INFO, SHM_INFO, SHM_STAT
 * SHM_LOCK and SHM_UNLOCK are passed on, but currently not implemented
 * by OpenBSD itself.
 *
 * The usual structure conversion and massaging is done.
 */
int
linux_shmctl(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ipc_args /* {
		syscallarg(int) what;
		syscallarg(int) a1;
		syscallarg(int) a2;
		syscallarg(int) a3;
		syscallarg(caddr_t) ptr;
	} */ *uap = v;
	int error;
	caddr_t sg;
	struct sys_shmctl_args bsa;
	struct shmid_ds *bsp, bs;
	struct linux_shmid_ds lseg;

	switch (SCARG(uap, a2)) {
	case LINUX_IPC_STAT:
		sg = stackgap_init(p);
		bsp = stackgap_alloc(&sg, sizeof (struct shmid_ds));
		SCARG(&bsa, shmid) = SCARG(uap, a1);
		SCARG(&bsa, cmd) = IPC_STAT;
		SCARG(&bsa, buf) = bsp;
		if ((error = sys_shmctl(p, &bsa, retval)))
			return error;
		if ((error = copyin((caddr_t) bsp, (caddr_t) &bs, sizeof bs)))
			return error;
		if ((error = bsd_to_linux_shmid_ds(&bs, &lseg)))
			return error;
		return copyout((caddr_t) &lseg, SCARG(uap, ptr), sizeof lseg);
	case LINUX_IPC_SET:
		if ((error = copyin(SCARG(uap, ptr), (caddr_t) &lseg,
		     sizeof lseg)))
			return error;
		linux_to_bsd_shmid_ds(&lseg, &bs);
		sg = stackgap_init(p);
		bsp = stackgap_alloc(&sg, sizeof (struct shmid_ds));
		if ((error = copyout((caddr_t) &bs, (caddr_t) bsp, sizeof bs)))
			return error;
		SCARG(&bsa, shmid) = SCARG(uap, a1);
		SCARG(&bsa, cmd) = IPC_SET;
		SCARG(&bsa, buf) = bsp;
		return sys_shmctl(p, &bsa, retval);
	case LINUX_IPC_RMID:
	case LINUX_SHM_LOCK:
	case LINUX_SHM_UNLOCK:
		SCARG(&bsa, shmid) = SCARG(uap, a1);
		switch (SCARG(uap, a2)) {
		case LINUX_IPC_RMID:
			SCARG(&bsa, cmd) = IPC_RMID;
			break;
		case LINUX_SHM_LOCK:
			SCARG(&bsa, cmd) = SHM_LOCK;
			break;
		case LINUX_SHM_UNLOCK:
			SCARG(&bsa, cmd) = SHM_UNLOCK;
			break;
		}
		SCARG(&bsa, buf) = NULL;
		return sys_shmctl(p, &bsa, retval);
	case LINUX_IPC_INFO:
	case LINUX_SHM_STAT:
	case LINUX_SHM_INFO:
	default:
		return EINVAL;
	}
}
#endif /* SYSVSHM */

int
linux_sys_pipe2(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_pipe2_args *uap = v;
	struct sys_pipe_args pargs;

	/*
	 * We don't really support pipe2, but glibc falls back to pipe
	 * if we signal that.
	 */
	if (SCARG(uap, flags) != 0)
		return ENOSYS;

	/* If no flag is set then this is a plain pipe call. */
	SCARG(&pargs, fdp) = SCARG(uap, fdp);
	return sys_pipe(p, &pargs, retval);
}


code:
/*	$OpenBSD: linux_ipc.h,v 1.4 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_ipc.h,v 1.1 1995/02/28 23:25:47 fvdl Exp $	*/

/*
 * Copyright (c) 1995 Frank van der Linden
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project
 *      by Frank van der Linden
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_IPC_H_
#define _LINUX_IPC_H_

/*
 * Structs and values to handle the SYSV ipc/shm/msg calls implemented
 * in Linux. Most values match the OpenBSD values (as they are both derived
 * from SysV values). Values that are the same may not be defined here.
 */

typedef int linux_key_t;

/*
 * The only thing different about the Linux ipc_perm structure is the
 * order of the fields.
 */
struct linux_ipc_perm {
	linux_key_t	l_key;
	ushort		l_uid;
	ushort		l_gid;
	ushort		l_cuid;
	ushort		l_cgid;
	ushort		l_mode;
	ushort		l_seq;
};

#define LINUX_IPC_RMID	0
#define LINUX_IPC_SET	1
#define LINUX_IPC_STAT	2
#define LINUX_IPC_INFO	3

#endif /* _LINUX_IPC_H_ */

license: unknown


code:
/*	$OpenBSD: linux_ipccall.h,v 1.3 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_ipccall.h,v 1.2 1995/08/15 21:14:33 fvdl Exp $	*/

/*
 * Copyright (c) 1995 Frank van der Linden
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project
 *      by Frank van der Linden
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_IPC_CALL_H_
#define _LINUX_IPC_CALL_H_

/*
 * Defines for the numbers passes as the first argument to the
 * linux_ipc() call, and based on which the actual system calls
 * are made.
 */
#define LINUX_SYS_semop		1
#define LINUX_SYS_semget	2
#define LINUX_SYS_semctl	3
#define LINUX_SYS_msgsnd	11
#define LINUX_SYS_msgrcv	12
#define LINUX_SYS_msgget	13
#define LINUX_SYS_msgctl	14
#define LINUX_SYS_shmat		21
#define LINUX_SYS_shmdt		22
#define LINUX_SYS_shmget	23
#define LINUX_SYS_shmctl	24

#endif /* _LINUX_IPC_CALL_H_ */


code:
/*	$OpenBSD: linux_misc.c,v 1.94 2015/05/07 08:53:32 mpi Exp $	*/
/*	$NetBSD: linux_misc.c,v 1.27 1996/05/20 01:59:21 fvdl Exp $	*/

/*-
 * Copyright (c) 1995, 1998, 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Frank van der Linden and Eric Haszlakiewicz; by Jason R. Thorpe
 * of the Numerical Aerospace Simulation Facility, NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Linux compatibility module. Try to deal with various Linux system calls.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/dirent.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/filedesc.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/swap.h>
#include <sys/resourcevar.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <sys/unistd.h>

#include <sys/syscallargs.h>

#include <uvm/uvm_extern.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_time.h>
#include <compat/linux/linux_fcntl.h>
#include <compat/linux/linux_misc.h>
#include <compat/linux/linux_mmap.h>
#include <compat/linux/linux_sched.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_dirent.h>
#include <compat/linux/linux_emuldata.h>

#include <compat/common/compat_dir.h>

/* linux_misc.c */
void	bsd_to_linux_statfs(const struct statfs *, struct linux_statfs *);
void	bsd_to_linux_statfs64(const struct statfs *, struct linux_statfs64 *);
int	linux_select1(struct proc *, register_t *, int, fd_set *,
	     fd_set *, fd_set *, struct linux_timeval *);
int	getdents_common(struct proc *, void *, register_t *, int);
void	linux_to_bsd_mmap_args(struct sys_mmap_args *,
	    const struct linux_sys_mmap2_args *);
void	bsd_to_linux_rusage(struct linux_rusage *, const struct rusage *);
void	bsd_to_linux_wstat(int *);

/*
 * The information on a terminated (or stopped) process needs
 * to be converted in order for Linux binaries to get a valid signal
 * number out of it.
 */
void
bsd_to_linux_wstat(status)
	int *status;
{

	if (WIFSIGNALED(*status))
		*status = (*status & ~0177) |
		    bsd_to_linux_sig[WTERMSIG(*status)];
	else if (WIFSTOPPED(*status))
		*status = (*status & ~0xff00) |
		    (bsd_to_linux_sig[WSTOPSIG(*status)] << 8);
}

/*
 * Convert an rusage to Linux format: small time_t in the timevals
 */
void
bsd_to_linux_rusage(struct linux_rusage *lrup, const struct rusage *rup)
{
	bsd_to_linux_timeval(&lrup->ru_utime, &rup->ru_utime);
	bsd_to_linux_timeval(&lrup->ru_utime, &rup->ru_utime);
	memcpy(&lrup->ru_maxrss, &rup->ru_maxrss,
	    offsetof(struct rusage, ru_nivcsw) -
	    offsetof(struct rusage, ru_maxrss) +
	    sizeof(lrup->ru_nivcsw));
}

/*
 * waitpid(2). Just forward on to linux_sys_wait4 with a NULL rusage.
 */
int
linux_sys_waitpid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_waitpid_args /* {
		syscallarg(int) pid;
		syscallarg(int *) status;
		syscallarg(int) options;
	} */ *uap = v;
	struct sys_wait4_args linux_w4a;

	SCARG(&linux_w4a, pid) = SCARG(uap, pid);
	SCARG(&linux_w4a, status) = SCARG(uap, status);
	SCARG(&linux_w4a, options) = SCARG(uap, options);
	SCARG(&linux_w4a, rusage) = NULL;

	return (linux_sys_wait4(p, &linux_w4a, retval));
}

/*
 * wait4(2): handle conversion of the options on entry and status and rusage
 * on return.
 */
int
linux_sys_wait4(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_wait4_args /* {
		syscallarg(int) pid;
		syscallarg(int *) status;
		syscallarg(int) options;
		syscallarg(struct linux_rusage *) rusage;
	} */ *uap = v;
	struct rusage ru;
	int error, status, linux_options, options;

	linux_options = SCARG(uap, options);
	options = 0;
	if (linux_options & ~(LINUX_WAIT4_WNOHANG|LINUX_WAIT4_WUNTRACED))
		return (EINVAL);

	if (linux_options & LINUX_WAIT4_WNOHANG)
		options |= WNOHANG;
	if (linux_options & LINUX_WAIT4_WUNTRACED)
		options |= WUNTRACED;

	if ((error = dowait4(p, SCARG(uap, pid),
	    SCARG(uap, status) ? &status : NULL, options,
	    SCARG(uap, rusage) ? &ru : NULL, retval)))
		return error;

	atomic_clearbits_int(&p->p_siglist, sigmask(SIGCHLD));

	if (SCARG(uap, rusage) != NULL) {
		struct linux_rusage lru;

		bsd_to_linux_rusage(&lru, &ru);
		if ((error = copyout(&lru, SCARG(uap, rusage), sizeof lru)))
			return error;
	}
	if (SCARG(uap, status) != NULL) {
		bsd_to_linux_wstat(&status);
		return copyout(&status, SCARG(uap, status), sizeof status);
	}

	return 0;
}

/*
 * getrusage(2): convert rusage on return
 */
int
linux_sys_getrusage(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_getrusage_args /* {
		syscallarg(int) who;
		syscallarg(struct linux_rusage *) rusage;
	} */ *uap = v;
	struct rusage ru;
	int error;

	error = dogetrusage(p, SCARG(uap, who), &ru);
	if (error == 0) {
		struct linux_rusage lru;

		bsd_to_linux_rusage(&lru, &ru);
		error = copyout(&lru, SCARG(uap, rusage), sizeof lru);
	}
	return error;
}

int
linux_sys_setresgid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_setresgid16_args /* {
		syscallarg(u_int16_t) rgid;
		syscallarg(u_int16_t) egid;
		syscallarg(u_int16_t) sgid;
	} */ *uap = v;
	struct sys_setresgid_args nuap;
	u_int16_t rgid, egid, sgid;

	rgid = SCARG(uap, rgid);
	SCARG(&nuap, rgid) = (rgid == (u_int16_t)-1) ? (gid_t)-1 : rgid;
	egid = SCARG(uap, egid);
	SCARG(&nuap, egid) = (egid == (u_int16_t)-1) ? (gid_t)-1 : egid;
	sgid = SCARG(uap, sgid);
	SCARG(&nuap, sgid) = (sgid == (u_int16_t)-1) ? (gid_t)-1 : sgid;

	return sys_setresgid(p, &nuap, retval);
}

int
linux_sys_getresgid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_getresgid16_args /* {
		syscallarg(u_int16_t *) rgid;
		syscallarg(u_int16_t *) egid;
		syscallarg(u_int16_t *) sgid;
	} */ *uap = v;
	struct sys_getresgid_args nuap;

	SCARG(&nuap, rgid) = (gid_t *)SCARG(uap, rgid);
	SCARG(&nuap, egid) = (gid_t *)SCARG(uap, egid);
	SCARG(&nuap, sgid) = (gid_t *)SCARG(uap, sgid);

	return sys_getresgid(p, &nuap, retval);
}

int
linux_sys_setresuid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_setresuid16_args /* {
		syscallarg(u_int16_t) ruid;
		syscallarg(u_int16_t) euid;
		syscallarg(u_int16_t) suid;
	} */ *uap = v;
	struct sys_setresuid_args nuap;
	u_int16_t ruid, euid, suid;

	ruid = SCARG(uap, ruid);
	SCARG(&nuap, ruid) = (ruid == (u_int16_t)-1) ? (uid_t)-1 : ruid;
	euid = SCARG(uap, euid);
	SCARG(&nuap, euid) = (euid == (u_int16_t)-1) ? (uid_t)-1 : euid;
	suid = SCARG(uap, suid);
	SCARG(&nuap, suid) = (suid == (u_int16_t)-1) ? (uid_t)-1 : suid;

	return sys_setresuid(p, &nuap, retval);
}

int
linux_sys_getresuid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_getresuid16_args /* {
		syscallarg(u_int16_t *) ruid;
		syscallarg(u_int16_t *) euid;
		syscallarg(u_int16_t *) suid;
	} */ *uap = v;
	struct sys_getresuid_args nuap;

	SCARG(&nuap, ruid) = (uid_t *)SCARG(uap, ruid);
	SCARG(&nuap, euid) = (uid_t *)SCARG(uap, euid);
	SCARG(&nuap, suid) = (uid_t *)SCARG(uap, suid);

	return sys_getresuid(p, &nuap, retval);
}

/*
 * This is the old brk(2) call. I don't think anything in the Linux
 * world uses this anymore
 */
int
linux_sys_break(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
#if 0
	struct linux_sys_brk_args /* {
		syscallarg(char *) nsize;
	} */ *uap = v;
#endif

	return ENOSYS;
}

/*
 * Linux brk(2). The check if the new address is >= the old one is
 * done in the kernel in Linux. OpenBSD does it in the library.
 */
int
linux_sys_brk(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_brk_args /* {
		syscallarg(char *) nsize;
	} */ *uap = v;
	char *nbrk = SCARG(uap, nsize);
	struct sys_obreak_args oba;
	struct vmspace *vm = p->p_vmspace;
	struct linux_emuldata *ed = (struct linux_emuldata*)p->p_emuldata;

	SCARG(&oba, nsize) = nbrk;

	if ((caddr_t) nbrk > vm->vm_daddr && sys_obreak(p, &oba, retval) == 0)
		ed->p_break = (char*)nbrk;
	else
		nbrk = ed->p_break;

	retval[0] = (register_t)nbrk;

	return 0;
}

/*
 * I wonder why Linux has gettimeofday() _and_ time().. Still, we
 * need to deal with it.
 */
int
linux_sys_time(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_time_args /* {
		linux_time_t *t;
	} */ *uap = v;
	struct timeval atv;
	linux_time_t tt;
	int error;

	microtime(&atv);

	if (atv.tv_sec > LINUX_TIME_MAX)
		return (EOVERFLOW);
	tt = (linux_time_t)atv.tv_sec;
	if (SCARG(uap, t) && (error = copyout(&tt, SCARG(uap, t), sizeof tt)))
		return error;

	retval[0] = tt;
	return 0;
}

/*
 * Convert BSD statfs structure to Linux statfs structure.
 * The Linux structure has less fields, and it also wants
 * the length of a name in a dir entry in a field, which
 * we fake (probably the wrong way).
 */
void
bsd_to_linux_statfs(const struct statfs *bsp, struct linux_statfs *lsp)
{

	/*
	 * Convert BSD filesystem names to Linux filesystem type numbers
	 * where possible.  Linux statfs uses a value of -1 to indicate
	 * an unsupported field.
	 */
	if (!strcmp(bsp->f_fstypename, MOUNT_FFS) ||
	    !strcmp(bsp->f_fstypename, MOUNT_MFS))
		lsp->l_ftype = LINUX_FSTYPE_FFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NFS))
		lsp->l_ftype = LINUX_FSTYPE_NFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_MSDOS))
		lsp->l_ftype = LINUX_FSTYPE_MSDOS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_EXT2FS))
		lsp->l_ftype = LINUX_FSTYPE_EXT2FS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_CD9660))
		lsp->l_ftype = LINUX_FSTYPE_CD9660;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NCPFS))
		lsp->l_ftype = LINUX_FSTYPE_NCPFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NTFS))
		lsp->l_ftype = LINUX_FSTYPE_NTFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_UDF))
		lsp->l_ftype = LINUX_FSTYPE_UDF;
	else if (!strcmp(bsp->f_fstypename, MOUNT_AFS))
		lsp->l_ftype = LINUX_FSTYPE_AFS;
	else
		lsp->l_ftype = -1;

	lsp->l_fbsize = bsp->f_bsize;
	lsp->l_fblocks = bsp->f_blocks;
	lsp->l_fbfree = bsp->f_bfree;
	lsp->l_fbavail = bsp->f_bavail;
	lsp->l_ffiles = bsp->f_files;
	lsp->l_fffree = bsp->f_ffree;
	lsp->l_ffsid.val[0] = bsp->f_fsid.val[0];
	lsp->l_ffsid.val[1] = bsp->f_fsid.val[1];
	lsp->l_fnamelen = MAXNAMLEN;	/* XXX */
}

/*
 * Implement the fs stat functions. Straightforward.
 */
int
linux_sys_statfs(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_statfs_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_statfs *) sp;
	} */ *uap = v;
	struct statfs btmp, *bsp;
	struct linux_statfs ltmp;
	struct sys_statfs_args bsa;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);
	bsp = (struct statfs *) stackgap_alloc(&sg, sizeof (struct statfs));

	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&bsa, path) = SCARG(uap, path);
	SCARG(&bsa, buf) = bsp;

	if ((error = sys_statfs(p, &bsa, retval)))
		return error;

	if ((error = copyin((caddr_t) bsp, (caddr_t) &btmp, sizeof btmp)))
		return error;

	bsd_to_linux_statfs(&btmp, &ltmp);

	return copyout((caddr_t) &ltmp, (caddr_t) SCARG(uap, sp), sizeof ltmp);
}

void
bsd_to_linux_statfs64(const struct statfs *bsp, struct linux_statfs64 *lsp)
{

	/*
	 * Convert BSD filesystem names to Linux filesystem type numbers
	 * where possible.  Linux statfs uses a value of -1 to indicate
	 * an unsupported field.
	 */
	if (!strcmp(bsp->f_fstypename, MOUNT_FFS) ||
	    !strcmp(bsp->f_fstypename, MOUNT_MFS))
		lsp->l_ftype = LINUX_FSTYPE_FFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NFS))
		lsp->l_ftype = LINUX_FSTYPE_NFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_MSDOS))
		lsp->l_ftype = LINUX_FSTYPE_MSDOS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_EXT2FS))
		lsp->l_ftype = LINUX_FSTYPE_EXT2FS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_CD9660))
		lsp->l_ftype = LINUX_FSTYPE_CD9660;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NCPFS))
		lsp->l_ftype = LINUX_FSTYPE_NCPFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_NTFS))
		lsp->l_ftype = LINUX_FSTYPE_NTFS;
	else if (!strcmp(bsp->f_fstypename, MOUNT_UDF))
		lsp->l_ftype = LINUX_FSTYPE_UDF;
	else if (!strcmp(bsp->f_fstypename, MOUNT_AFS))
		lsp->l_ftype = LINUX_FSTYPE_AFS;
	else
		lsp->l_ftype = -1;

	lsp->l_fbsize = bsp->f_bsize;
	lsp->l_fblocks = bsp->f_blocks;
	lsp->l_fbfree = bsp->f_bfree;
	lsp->l_fbavail = bsp->f_bavail;
	lsp->l_ffiles = bsp->f_files;
	lsp->l_fffree = bsp->f_ffree;
	lsp->l_ffsid.val[0] = bsp->f_fsid.val[0];
	lsp->l_ffsid.val[1] = bsp->f_fsid.val[1];
	lsp->l_fnamelen = MAXNAMLEN;	/* XXX */
}

int
linux_sys_statfs64(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_statfs64_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_statfs64 *) sp;
	} */ *uap = v;
	struct statfs btmp, *bsp;
	struct linux_statfs64 ltmp;
	struct sys_statfs_args bsa;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);
	bsp = (struct statfs *) stackgap_alloc(&sg, sizeof (struct statfs));

	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&bsa, path) = SCARG(uap, path);
	SCARG(&bsa, buf) = bsp;

	if ((error = sys_statfs(p, &bsa, retval)))
		return error;

	if ((error = copyin((caddr_t) bsp, (caddr_t) &btmp, sizeof btmp)))
		return error;

	bsd_to_linux_statfs64(&btmp, &ltmp);

	return copyout((caddr_t) &ltmp, (caddr_t) SCARG(uap, sp), sizeof ltmp);
}

int
linux_sys_fstatfs(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fstatfs_args /* {
		syscallarg(int) fd;
		syscallarg(struct linux_statfs *) sp;
	} */ *uap = v;
	struct statfs btmp, *bsp;
	struct linux_statfs ltmp;
	struct sys_fstatfs_args bsa;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);
	bsp = (struct statfs *) stackgap_alloc(&sg, sizeof (struct statfs));

	SCARG(&bsa, fd) = SCARG(uap, fd);
	SCARG(&bsa, buf) = bsp;

	if ((error = sys_fstatfs(p, &bsa, retval)))
		return error;

	if ((error = copyin((caddr_t) bsp, (caddr_t) &btmp, sizeof btmp)))
		return error;

	bsd_to_linux_statfs(&btmp, &ltmp);

	return copyout((caddr_t) &ltmp, (caddr_t) SCARG(uap, sp), sizeof ltmp);
}

int
linux_sys_fstatfs64(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_fstatfs64_args /* {
		syscallarg(int) fd;
		syscallarg(struct linux_statfs64 *) sp;
	} */ *uap = v;
	struct statfs btmp, *bsp;
	struct linux_statfs64 ltmp;
	struct sys_fstatfs_args bsa;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);
	bsp = (struct statfs *) stackgap_alloc(&sg, sizeof (struct statfs));

	SCARG(&bsa, fd) = SCARG(uap, fd);
	SCARG(&bsa, buf) = bsp;

	if ((error = sys_fstatfs(p, &bsa, retval)))
		return error;

	if ((error = copyin((caddr_t) bsp, (caddr_t) &btmp, sizeof btmp)))
		return error;

	bsd_to_linux_statfs64(&btmp, &ltmp);

	return copyout((caddr_t) &ltmp, (caddr_t) SCARG(uap, sp), sizeof ltmp);
}
/*
 * uname(). Just copy the info from the various strings stored in the
 * kernel, and put it in the Linux utsname structure. That structure
 * is almost the same as the OpenBSD one, only it has fields 65 characters
 * long, and an extra domainname field.
 */
int
linux_sys_uname(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_uname_args /* {
		syscallarg(struct linux_utsname *) up;
	} */ *uap = v;
	extern char hostname[], machine[], domainname[];
	struct linux_utsname luts;
	int len;
	char *cp;

	strlcpy(luts.l_sysname, ostype, sizeof(luts.l_sysname));
	strlcpy(luts.l_nodename, hostname, sizeof(luts.l_nodename));
	strlcpy(luts.l_release, osrelease, sizeof(luts.l_release));
	strlcpy(luts.l_version, version, sizeof(luts.l_version));
	strlcpy(luts.l_machine, machine, sizeof(luts.l_machine));
	strlcpy(luts.l_domainname, domainname, sizeof(luts.l_domainname));

	/* This part taken from the uname() in libc */
	len = sizeof(luts.l_version);
	for (cp = luts.l_version; len--; ++cp)
		if (*cp == '\n' || *cp == '\t')
			*cp = (len > 1) ? ' ' : '\0';

	return copyout(&luts, SCARG(uap, up), sizeof(luts));
}

int
linux_sys_olduname(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_uname_args /* {
		syscallarg(struct linux_oldutsname *) up;
	} */ *uap = v;
	extern char hostname[], machine[];
	struct linux_oldutsname luts;
	int len;
	char *cp;

	strlcpy(luts.l_sysname, ostype, sizeof(luts.l_sysname));
	strlcpy(luts.l_nodename, hostname, sizeof(luts.l_nodename));
	strlcpy(luts.l_release, osrelease, sizeof(luts.l_release));
	strlcpy(luts.l_version, version, sizeof(luts.l_version));
	strlcpy(luts.l_machine, machine, sizeof(luts.l_machine));

	/* This part taken from the uname() in libc */
	len = sizeof(luts.l_version);
	for (cp = luts.l_version; len--; ++cp)
		if (*cp == '\n' || *cp == '\t')
			*cp = (len > 1) ? ' ' : '\0';

	return copyout(&luts, SCARG(uap, up), sizeof(luts));
}

int
linux_sys_oldolduname(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_uname_args /* {
		syscallarg(struct linux_oldoldutsname *) up;
	} */ *uap = v;
	extern char hostname[], machine[];
	struct linux_oldoldutsname luts;
	int len;
	char *cp;

	strlcpy(luts.l_sysname, ostype, sizeof(luts.l_sysname));
	strlcpy(luts.l_nodename, hostname, sizeof(luts.l_nodename));
	strlcpy(luts.l_release, osrelease, sizeof(luts.l_release));
	strlcpy(luts.l_version, version, sizeof(luts.l_version));
	strlcpy(luts.l_machine, machine, sizeof(luts.l_machine));

	/* This part taken from the uname() in libc */
	len = sizeof(luts.l_version);
	for (cp = luts.l_version; len--; ++cp)
		if (*cp == '\n' || *cp == '\t')
			*cp = (len > 1) ? ' ' : '\0';

	return copyout(&luts, SCARG(uap, up), sizeof(luts));
}

int
linux_sys_sethostname(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_sethostname_args *uap = v;
	int name;
	int error;

	if ((error = suser(p, 0)) != 0)
		return (error);
	name = KERN_HOSTNAME;
	return (kern_sysctl(&name, 1, 0, 0, SCARG(uap, hostname),
			    SCARG(uap, len), p));
}

/*
 * Linux wants to pass everything to a syscall in registers. However,
 * mmap() has 6 of them. Oops: out of register error. They just pass
 * everything in a structure.
 */
int
linux_sys_mmap(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_mmap_args /* {
		syscallarg(struct linux_mmap *) lmp;
	} */ *uap = v;
	struct linux_mmap lmap;
	struct linux_sys_mmap2_args nlmap;
	struct sys_mmap_args cma;
	int error;

	if ((error = copyin(SCARG(uap, lmp), &lmap, sizeof lmap)))
		return error;

	if (lmap.lm_pos & PAGE_MASK)
		return EINVAL;

	/* repackage into something sane */
	SCARG(&nlmap,addr) = (unsigned long)lmap.lm_addr;
	SCARG(&nlmap,len) = lmap.lm_len;
	SCARG(&nlmap,prot) = lmap.lm_prot;
	SCARG(&nlmap,flags) = lmap.lm_flags;
	SCARG(&nlmap,fd) = lmap.lm_fd;
	SCARG(&nlmap,offset) = (unsigned)lmap.lm_pos;

	linux_to_bsd_mmap_args(&cma, &nlmap);
	SCARG(&cma, pos) = (off_t)SCARG(&nlmap, offset);

	return sys_mmap(p, &cma, retval);
}

/*
 * Guts of most architectures' mmap64() implementations.  This shares
 * its list of arguments with linux_sys_mmap().
 *
 * The difference in linux_sys_mmap2() is that "offset" is actually
 * (offset / pagesize), not an absolute byte count.  This translation
 * to pagesize offsets is done inside glibc between the mmap64() call
 * point, and the actual syscall.
 */
int
linux_sys_mmap2(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_mmap2_args /* {
		syscallarg(unsigned long) addr;
		syscallarg(size_t) len;
		syscallarg(int) prot;
		syscallarg(int) flags;
		syscallarg(int) fd;
		syscallarg(linux_off_t) offset;
	} */ *uap = v;
	struct sys_mmap_args cma;

	linux_to_bsd_mmap_args(&cma, uap);
	SCARG(&cma, pos) = ((off_t)SCARG(uap, offset)) << PAGE_SHIFT;

	return sys_mmap(p, &cma, retval);
}

void
linux_to_bsd_mmap_args(cma, uap)
	struct sys_mmap_args *cma;
	const struct linux_sys_mmap2_args *uap;
{
	int flags = 0, fl = SCARG(uap, flags);

	flags |= cvtto_bsd_mask(fl, LINUX_MAP_SHARED, MAP_SHARED);
	flags |= cvtto_bsd_mask(fl, LINUX_MAP_PRIVATE, MAP_PRIVATE);
	flags |= cvtto_bsd_mask(fl, LINUX_MAP_FIXED, MAP_FIXED);
	flags |= cvtto_bsd_mask(fl, LINUX_MAP_ANON, MAP_ANON);
	/* XXX XAX ERH: Any other flags here?  There are more defined... */

	SCARG(cma, addr) = (void *)SCARG(uap, addr);
	SCARG(cma, len) = SCARG(uap, len);
	SCARG(cma, prot) = SCARG(uap, prot);
	if (SCARG(cma, prot) & PROT_WRITE) /* XXX */
		SCARG(cma, prot) |= PROT_READ;
	SCARG(cma, flags) = flags;
	SCARG(cma, fd) = flags & MAP_ANON ? -1 : SCARG(uap, fd);
	SCARG(cma, pad) = 0;
}

int
linux_sys_mremap(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{

	struct linux_sys_mremap_args /* {
		syscallarg(void *) old_address;
		syscallarg(size_t) old_size;
		syscallarg(size_t) new_size;
		syscallarg(u_long) flags;
	} */ *uap = v;
	struct sys_munmap_args mua;
	size_t old_size, new_size;
	int error;

	old_size = round_page(SCARG(uap, old_size));
	new_size = round_page(SCARG(uap, new_size));

	/*
	 * Growing mapped region.
	 */
	if (new_size > old_size) {
		/*
		 * XXX Implement me.  What we probably want to do is
		 * XXX dig out the guts of the old mapping, mmap that
		 * XXX object again with the new size, then munmap
		 * XXX the old mapping.
		 */
		*retval = 0;
		return (ENOMEM);
	}
	/*
	 * Shrinking mapped region.
	 */
	if (new_size < old_size) {
		SCARG(&mua, addr) = (caddr_t)SCARG(uap, old_address) + new_size;
		SCARG(&mua, len) = old_size - new_size;
		error = sys_munmap(p, &mua, retval);
		*retval = error ? 0 : (register_t)SCARG(uap, old_address);
		return (error);
	}

	/*
	 * No change.
	 */
	*retval = (register_t)SCARG(uap, old_address);
	return (0);

}

/*
 * This code is partly stolen from src/lib/libc/gen/times.c
 * XXX - CLK_TCK isn't declared in /sys, just in <time.h>, done here
 */

#define CLK_TCK 100
#define	CONVTCK(r)	(r.tv_sec * CLK_TCK + r.tv_usec / (1000000 / CLK_TCK))

int
linux_sys_times(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_times_args /* {
		syscallarg(struct linux_tms *) tms;
	} */ *uap = v;
	struct timeval t, ut, st;
	struct linux_tms ltms;
	time_t ticks;
	int error;

	calcru(&p->p_p->ps_tu, &ut, &st, NULL);

	ticks = CONVTCK(ut);
	if (ticks > LINUX_TIME_MAX)
		return EOVERFLOW;
	ltms.ltms_utime = (linux_clock_t)ticks;

	ticks = CONVTCK(st);
	if (ticks > LINUX_TIME_MAX)
		return EOVERFLOW;
	ltms.ltms_stime = (linux_clock_t)ticks;

	ticks = CONVTCK(p->p_p->ps_cru.ru_utime);
	if (ticks > LINUX_TIME_MAX)
		return EOVERFLOW;
	ltms.ltms_cutime = (linux_clock_t)ticks;

	ticks = CONVTCK(p->p_p->ps_cru.ru_stime);
	if (ticks > LINUX_TIME_MAX)
		return EOVERFLOW;
	ltms.ltms_cstime = (linux_clock_t)ticks;

	if ((error = copyout(&ltms, SCARG(uap, tms), sizeof ltms)))
		return error;

	microuptime(&t);

	retval[0] = ((linux_clock_t)(CONVTCK(t)));
	return 0;
}

/*
 * Alarm. This is a libc call which uses setitimer(2) in OpenBSD.
 * Fiddle with the timers to make it work.
 */
int
linux_sys_alarm(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_alarm_args /* {
		syscallarg(unsigned int) secs;
	} */ *uap = v;
	struct process *pr;
	struct itimerval *itp, it;
	struct timeval tv;
	int s;
	int timo;
	linux_time_t seconds_due = 0;

	pr = p->p_p;
	itp = &pr->ps_timer[ITIMER_REAL];
	s = splclock();
	/*
	 * Clear any pending timer alarms.
	 */
	getmicrouptime(&tv);
	timeout_del(&pr->ps_realit_to);
	timerclear(&itp->it_interval);
	if (timerisset(&itp->it_value) &&
	    timercmp(&itp->it_value, &tv, >))
		timersub(&itp->it_value, &tv, &itp->it_value);
	/*
	 * Return how many seconds were left (rounded up)
	 */
	if (itp->it_value.tv_sec > LINUX_TIME_MAX) {
		splx(s);
		return EOVERFLOW;
	}
	seconds_due = (linux_time_t)itp->it_value.tv_sec;
	if (itp->it_value.tv_usec) {
		if (seconds_due == LINUX_TIME_MAX) {
			splx(s);
			return EOVERFLOW;
		}
		seconds_due++;
	}
	retval[0] = seconds_due;

	/*
	 * alarm(0) just resets the timer.
	 */
	if (SCARG(uap, secs) == 0) {
		timerclear(&itp->it_value);
		splx(s);
		return 0;
	}

	/*
	 * Check the new alarm time for sanity, and set it.
	 */
	timerclear(&it.it_interval);
	it.it_value.tv_sec = SCARG(uap, secs);
	it.it_value.tv_usec = 0;
	if (itimerfix(&it.it_value)) {
		splx(s);
		return (EINVAL);
	}

	if (timerisset(&it.it_value)) {
		timo = tvtohz(&it.it_value);
		timeradd(&it.it_value, &tv, &it.it_value);
		timeout_add(&pr->ps_realit_to, timo);
	}
	pr->ps_timer[ITIMER_REAL] = it;
	splx(s);

	return 0;
}

/*
 * utime(). Do conversion to things that utimes() understands,
 * and pass it on.
 */
int
linux_sys_utime(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_utime_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_utimbuf *)times;
	} */ *uap = v;
	caddr_t sg;
	int error;
	struct sys_utimes_args ua;
	struct timeval tv[2], *tvp;
	struct linux_utimbuf lut;

	sg = stackgap_init(p);
	tvp = (struct timeval *) stackgap_alloc(&sg, sizeof(tv));
	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&ua, path) = SCARG(uap, path);

	if (SCARG(uap, times) != NULL) {
		if ((error = copyin(SCARG(uap, times), &lut, sizeof lut)))
			return error;
		tv[0].tv_usec = tv[1].tv_usec = 0;
		tv[0].tv_sec = lut.l_actime;
		tv[1].tv_sec = lut.l_modtime;
		if ((error = copyout(tv, tvp, sizeof tv)))
			return error;
		SCARG(&ua, tptr) = tvp;
	}
	else
		SCARG(&ua, tptr) = NULL;

	return sys_utimes(p, &ua, retval);
}

/*
 * The old Linux readdir was only able to read one entry at a time,
 * even though it had a 'count' argument. In fact, the emulation
 * of the old call was better than the original, because it did handle
 * the count arg properly. Don't bother with it anymore now, and use
 * it to distinguish between old and new. The difference is that the
 * newer one actually does multiple entries, and the reclen field
 * really is the reclen, not the namelength.
 */
int
linux_sys_readdir(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_readdir_args /* {
		syscallarg(int) fd;
		syscallarg(struct linux_dirent *) dent;
		syscallarg(unsigned int) count;
	} */ *uap = v;

	SCARG(uap, count) = 1;

	return linux_sys_getdents(p, uap, retval);
}

/*
 * Linux 'readdir' call. This code is mostly taken from the
 * SunOS getdents call (see compat/sunos/sunos_misc.c), though
 * an attempt has been made to keep it a little cleaner (failing
 * miserably, because of the cruft needed if count 1 is passed).
 *
 * The d_off field should contain the offset of the next valid entry,
 * but in Linux it has the offset of the entry itself. We emulate
 * that bug here.
 *
 * Read in BSD-style entries, convert them, and copy them out.
 *
 * Note that this doesn't handle union-mounted filesystems.
 */
int linux_readdir_callback(void *, struct dirent *);

struct linux_readdir_callback_args {
	caddr_t outp;
	int     resid;
	int     oldcall;
	int	is64bit;
};

int
linux_readdir_callback(arg, bdp)
	void *arg;
	struct dirent *bdp;
{
	struct linux_dirent64 idb64;
	struct linux_dirent idb;
	struct linux_readdir_callback_args *cb = arg;
	int linux_reclen;
	int error;

	if (cb->oldcall == 2)
		return (ENOMEM);

	linux_reclen = (cb->is64bit) ?
	     LINUX_RECLEN(&idb64, bdp->d_namlen) :
	     LINUX_RECLEN(&idb, bdp->d_namlen);

	if (cb->resid < linux_reclen)
		return (ENOMEM);

	if (cb->is64bit) {
		idb64.d_ino = (linux_ino64_t)bdp->d_fileno;
		idb64.d_off = (linux_off64_t)bdp->d_off;
		idb64.d_reclen = (u_short)linux_reclen;
		idb64.d_type = bdp->d_type;
		strlcpy(idb64.d_name, bdp->d_name, sizeof(idb64.d_name));
		error = copyout((caddr_t)&idb64, cb->outp, linux_reclen);
	} else {
		if (bdp->d_fileno > LINUX_INO_MAX)
			return EOVERFLOW;
		idb.d_ino = (linux_ino_t)bdp->d_fileno;
		if (cb->oldcall) {
			/*
			 * The old readdir() call misuses the offset
			 * and reclen fields.
			 */
			idb.d_off = (linux_off_t)linux_reclen;
			idb.d_reclen = (u_short)bdp->d_namlen;
		} else {
			idb.d_off = (linux_off_t)bdp->d_off;
			idb.d_reclen = (u_short)linux_reclen;
		}
		strlcpy(idb.d_name, bdp->d_name, sizeof(idb.d_name));
		error = copyout((caddr_t)&idb, cb->outp, linux_reclen);
	}
	if (error)
		return (error);

	/* advance output past Linux-shaped entry */
	cb->outp += linux_reclen;
	cb->resid -= linux_reclen;

	if (cb->oldcall == 1)
		++cb->oldcall;

	return (0);
}

int
linux_sys_getdents64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	return getdents_common(p, v, retval, 1);
}

int
linux_sys_getdents(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	return getdents_common(p, v, retval, 0);
}

int
getdents_common(p, v, retval, is64bit)
	struct proc *p;
	void *v;
	register_t *retval;
	int is64bit;
{
	struct linux_sys_getdents_args /* {
		syscallarg(int) fd;
		syscallarg(void *) dirent;
		syscallarg(unsigned) count;
	} */ *uap = v;
	struct linux_readdir_callback_args args;
	struct file *fp;
	int error;
	int nbytes = SCARG(uap, count);

	if ((error = getvnode(p, SCARG(uap, fd), &fp)) != 0)
		return (error);

	if (nbytes == 1) {	/* emulating old, broken behaviour */
		/* readdir(2) case. Always struct dirent. */
		if (is64bit) {
			FRELE(fp, p);
			return (EINVAL);
		}
		nbytes = sizeof(struct linux_dirent);
		args.oldcall = 1;
	} else {
		args.oldcall = 0;
	}

	args.resid = nbytes;
	args.outp = (caddr_t)SCARG(uap, dirent);
	args.is64bit = is64bit;

	if ((error = readdir_with_callback(fp, &fp->f_offset, nbytes,
	    linux_readdir_callback, &args)) != 0)
		goto exit;

	*retval = nbytes - args.resid;

 exit:
	FRELE(fp, p);
	return (error);
}

/*
 * Not sure why the arguments to this older version of select() were put
 * into a structure, because there are 5, and that can be handled all
 * in registers on the i386 like Linux wants to.
 */
int
linux_sys_oldselect(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_oldselect_args /* {
		syscallarg(struct linux_select *) lsp;
	} */ *uap = v;
	struct linux_select ls;
	int error;

	if ((error = copyin(SCARG(uap, lsp), &ls, sizeof(ls))))
		return error;

	return linux_select1(p, retval, ls.nfds, ls.readfds, ls.writefds,
	    ls.exceptfds, ls.timeout);
}

/*
 * Even when just using registers to pass arguments to syscalls you can
 * have 5 of them on the i386. So this newer version of select() does
 * this.
 */
int
linux_sys_select(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_select_args /* {
		syscallarg(int) nfds;
		syscallarg(fd_set *) readfds;
		syscallarg(fd_set *) writefds;
		syscallarg(fd_set *) exceptfds;
		syscallarg(struct linux_timeval *) timeout;
	} */ *uap = v;

	return linux_select1(p, retval, SCARG(uap, nfds), SCARG(uap, readfds),
	    SCARG(uap, writefds), SCARG(uap, exceptfds), SCARG(uap, timeout));
}

/*
 * Common code for the old and new versions of select(). A couple of
 * things are important:
 * 1) return the amount of time left in the 'timeout' parameter
 * 2) select never returns ERESTART on Linux, always return EINTR
 */
int
linux_select1(struct proc *p, register_t *retval, int nfds, fd_set *readfds,
    fd_set *writefds, fd_set *exceptfds, struct linux_timeval *timeout)
{
	struct sys_select_args bsa;
	struct linux_timeval lutv;
	struct timeval tv0, tv1, utv, *tvp;
	caddr_t sg;
	int error;

	SCARG(&bsa, nd) = nfds;
	SCARG(&bsa, in) = readfds;
	SCARG(&bsa, ou) = writefds;
	SCARG(&bsa, ex) = exceptfds;

	/*
	 * Store current time for computation of the amount of
	 * time left.
	 */
	if (timeout) {
		if ((error = copyin(timeout, &lutv, sizeof(lutv))))
			return error;
		linux_to_bsd_timeval(&utv, &lutv);
		if (itimerfix(&utv)) {
			/*
			 * The timeval was invalid.  Convert it to something
			 * valid that will act as it does under Linux.
			 */
			utv.tv_sec += utv.tv_usec / 1000000;
			utv.tv_usec %= 1000000;
			if (utv.tv_usec < 0) {
				utv.tv_sec -= 1;
				utv.tv_usec += 1000000;
			}
			if (utv.tv_sec < 0)
				timerclear(&utv);
		}

		sg = stackgap_init(p);
		tvp = stackgap_alloc(&sg, sizeof(utv));
		if ((error = copyout(&utv, tvp, sizeof(utv))))
			return error;
		SCARG(&bsa, tv) = tvp;
		microtime(&tv0);
	} else
		SCARG(&bsa, tv) = NULL;

	error = sys_select(p, &bsa, retval);
	if (error) {
		/*
		 * See fs/select.c in the Linux kernel.  Without this,
		 * Maelstrom doesn't work.
		 */
		if (error == ERESTART)
			error = EINTR;
		else if (error != EINTR)
			return error;
	}

	if (timeout) {
		if (*retval) {
			/*
			 * Compute how much time was left of the timeout,
			 * by subtracting the current time and the time
			 * before we started the call, and subtracting
			 * that result from the user-supplied value.
			 */
			microtime(&tv1);
			timersub(&tv1, &tv0, &tv1);
			timersub(&utv, &tv1, &utv);
			if (utv.tv_sec < 0)
				timerclear(&utv);
		} else
			timerclear(&utv);
		bsd_to_linux_timeval(&lutv, &utv);	/* can't fail */
		if ((error = copyout(&lutv, timeout, sizeof(lutv))))
			return error;
	}

	return (error);
}

/*
 * Get the process group of a certain process. Look it up
 * and return the value.
 */
int
linux_sys_getpgid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_getpgid_args /* {
		syscallarg(int) pid;
	} */ *uap = v;
	struct process *targpr;

	if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != p->p_p->ps_pid) {
		if ((targpr = prfind(SCARG(uap, pid))) == 0)
			return ESRCH;
	}
	else
		targpr = p->p_p;

	retval[0] = targpr->ps_pgid;
	return 0;
}

/*
 * Set the 'personality' (emulation mode) for the current process. Only
 * accept the Linux personality here (0). This call is needed because
 * the Linux ELF crt0 issues it in an ugly kludge to make sure that
 * ELF binaries run in Linux mode, not SVR4 mode.
 */
int
linux_sys_personality(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_personality_args /* {
		syscallarg(int) per;
	} */ *uap = v;

	if (SCARG(uap, per) != 0)
		return EINVAL;
	retval[0] = 0;
	return 0;
}

/*
 * The calls are here because of type conversions.
 */
int
linux_sys_setreuid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_setreuid16_args /* {
		syscallarg(int) ruid;
		syscallarg(int) euid;
	} */ *uap = v;
	struct sys_setreuid_args bsa;

	SCARG(&bsa, ruid) = ((linux_uid_t)SCARG(uap, ruid) == (linux_uid_t)-1) ?
		(uid_t)-1 : SCARG(uap, ruid);
	SCARG(&bsa, euid) = ((linux_uid_t)SCARG(uap, euid) == (linux_uid_t)-1) ?
		(uid_t)-1 : SCARG(uap, euid);

	return sys_setreuid(p, &bsa, retval);
}

int
linux_sys_setregid16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_setregid16_args /* {
		syscallarg(int) rgid;
		syscallarg(int) egid;
	} */ *uap = v;
	struct sys_setregid_args bsa;

	SCARG(&bsa, rgid) = ((linux_gid_t)SCARG(uap, rgid) == (linux_gid_t)-1) ?
		(uid_t)-1 : SCARG(uap, rgid);
	SCARG(&bsa, egid) = ((linux_gid_t)SCARG(uap, egid) == (linux_gid_t)-1) ?
		(uid_t)-1 : SCARG(uap, egid);

	return sys_setregid(p, &bsa, retval);
}

int
linux_sys___sysctl(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys___sysctl_args /* {
		syscallarg(struct linux___sysctl *) lsp;
	} */ *uap = v;
	struct linux___sysctl ls;
	struct sys___sysctl_args bsa;
	int error;

	if ((error = copyin(SCARG(uap, lsp), &ls, sizeof ls)))
		return error;
	SCARG(&bsa, name) = ls.name;
	SCARG(&bsa, namelen) = ls.namelen;
	SCARG(&bsa, old) = ls.old;
	SCARG(&bsa, oldlenp) = ls.oldlenp;
	SCARG(&bsa, new) = ls.new;
	SCARG(&bsa, newlen) = ls.newlen;

	return sys___sysctl(p, &bsa, retval);
}

/*
 * We have nonexistent fsuid equal to uid.
 * If modification is requested, refuse.
 */
int
linux_sys_setfsuid(p, v, retval)
	 struct proc *p;
	 void *v;
	 register_t *retval;
{
	 struct linux_sys_setfsuid_args /* {
		 syscallarg(uid_t) uid;
	 } */ *uap = v;
	 uid_t uid;

	 uid = SCARG(uap, uid);
	 if (p->p_ucred->cr_ruid != uid)
		 return sys_nosys(p, v, retval);
	 else
		 return (0);
}

int
linux_sys_getfsuid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	return sys_getuid(p, v, retval);
}

int
linux_sys_nice(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_nice_args /* {
		syscallarg(int) incr;
	} */ *uap = v;
	struct sys_setpriority_args bsa;

	SCARG(&bsa, which) = PRIO_PROCESS;
	SCARG(&bsa, who) = 0;
	SCARG(&bsa, prio) = SCARG(uap, incr);
	return sys_setpriority(p, &bsa, retval);
}

int
linux_sys_getpid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{

	*retval = p->p_p->ps_pid;
	return (0);
}

linux_pid_t
linux_sys_gettid(struct proc *p, void *v, register_t *retval)
{
	*retval = p->p_pid + THREAD_PID_OFFSET;
	return (0);
}

int
linux_sys_getuid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{

	*retval = p->p_ucred->cr_ruid;
	return (0);
}

int
linux_sys_getgid(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{

	*retval = p->p_ucred->cr_rgid;
	return (0);
}

/*
 * sysinfo()
 */
/* ARGSUSED */
int
linux_sys_sysinfo(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_sysinfo_args /* {
		syscallarg(struct linux_sysinfo *) sysinfo;
	} */ *uap = v;
	struct linux_sysinfo si;
	struct loadavg *la;
	extern long bufpages;
	struct timeval tv;

	getmicrouptime(&tv);
	if (tv.tv_sec > LINUX_TIME_MAX)
		return EOVERFLOW;
	si.uptime = (linux_time_t)tv.tv_sec;
	la = &averunnable;
	si.loads[0] = la->ldavg[0] * LINUX_SYSINFO_LOADS_SCALE / la->fscale;
	si.loads[1] = la->ldavg[1] * LINUX_SYSINFO_LOADS_SCALE / la->fscale;
	si.loads[2] = la->ldavg[2] * LINUX_SYSINFO_LOADS_SCALE / la->fscale;
	si.totalram = ptoa(physmem);
	si.freeram = uvmexp.free * uvmexp.pagesize;
	si.sharedram = 0;/* XXX */
	si.bufferram = bufpages * PAGE_SIZE;
	si.totalswap = uvmexp.swpages * PAGE_SIZE;
	si.freeswap = (uvmexp.swpages - uvmexp.swpginuse) * PAGE_SIZE;
	si.procs = nthreads;
	/* The following are only present in newer Linux kernels. */
	si.totalbig = 0;
	si.freebig = 0;
	si.mem_unit = 1;

	return (copyout(&si, SCARG(uap, sysinfo), sizeof(si)));
}

int
linux_sys_mprotect(struct proc *p, void *v, register_t *retval)
{
	struct sys_mprotect_args *uap = v;

	if (SCARG(uap, prot) & (PROT_WRITE | PROT_EXEC))
		SCARG(uap, prot) |= PROT_READ;
	return (sys_mprotect(p, uap, retval));
}

int
linux_sys_setdomainname(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_setdomainname_args *uap = v;
	int error, mib[1];

	if ((error = suser(p, 0)))
		return (error);
	mib[0] = KERN_DOMAINNAME;
	return (kern_sysctl(mib, 1, NULL, NULL, SCARG(uap, name),
	    SCARG(uap, len), p));
}

int
linux_sys_swapon(struct proc *p, void *v, register_t *retval)
{
	struct sys_swapctl_args ua;
	struct linux_sys_swapon_args /* {
		syscallarg(const char *) name;
	} */ *uap = v;

	SCARG(&ua, cmd) = SWAP_ON;
	SCARG(&ua, arg) = (void *)SCARG(uap, name);
	SCARG(&ua, misc) = 0;	/* priority */
	return (sys_swapctl(p, &ua, retval));
}

int
linux_sys_prctl(struct proc *p, void *v, register_t *retval)
{
	int error = 0, max_size, pdeath_signal;
	char comm[LINUX_MAX_COMM_LEN];
	struct linux_emuldata *ed = (struct linux_emuldata*)p->p_emuldata;

	struct linux_sys_prctl_args /* {
		int option;
		unsigned long arg2;
		unsigned long arg3;
		unsigned long arg4;
		unsigned long arg5;
	} */ *uap = v;

	switch (SCARG(uap, option)) {
	case LINUX_PR_SET_PDEATHSIG:
		if (SCARG(uap, arg2) >= LINUX__NSIG)
			return (EINVAL);
		ed->pdeath_signal = SCARG(uap, arg2);
		break;
	case LINUX_PR_GET_PDEATHSIG:
		pdeath_signal = ed->pdeath_signal;
		error = copyout(&pdeath_signal, (void *)SCARG(uap, arg2),
		    sizeof(pdeath_signal));
		break;
	case LINUX_PR_GET_KEEPCAPS:
		/*
		 * Indicate that we always clear the effective and
		 * permitted capability sets when the user id becomes
		 * non-zero (actually the capability sets are simply
		 * always zero in the current implementation).
		 */
		*retval = 0;
		break;
	case LINUX_PR_SET_KEEPCAPS:
		 /*
		  * Ignore requests to keep the effective and permitted
		  * capability sets when the user id becomes non-zero.
		  */
		break;
	case LINUX_PR_SET_NAME:
		/*
		 * To be on the safe side we need to make sure not to
		 * overflow the size a linux program expects. We already
		 * do this here in the copyin, so that we don't need to
		 * check on copyout.
		 */
		max_size = MIN(sizeof(comm), sizeof(p->p_comm));
		error = copyinstr((void *)SCARG(uap, arg2), comm,
		    max_size, NULL);

		/* Linux silently truncates the name if it is too long. */
		if (error == ENAMETOOLONG) {
			/*
			 * XXX: copyinstr() isn't documented to populate the
			 * array completely, so do a copyin() to be on the
			 * safe side. This should be changed in case copyinstr()
			 * is changed to guarantee this.
			 */
			error = copyin((void *)SCARG(uap, arg2), comm,
			    max_size - 1);
			comm[max_size - 1] = '\0';
		}
		if (error)
			return (error);
		strlcpy(p->p_comm, comm, sizeof(p->p_comm));
		break;
	case LINUX_PR_GET_NAME:
		strlcpy(comm, p->p_comm, sizeof(comm));
		error = copyout(comm, (void *)SCARG(uap, arg2),
		    strlen(comm) + 1);
		break;
	default:
		printf("linux_sys_prctl: unsupported option %d\n",
		    SCARG(uap, option));
		error = EINVAL;
		break;
	}

	return (error);
}


code:
/*	$OpenBSD: linux_misc.h,v 1.7 2013/10/25 04:51:39 guenther Exp $	*/
/*	$NetBSD: linux_misc.h,v 1.3 1999/05/13 00:31:57 thorpej Exp $	*/

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Eric Haszlakiewicz.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_MISC_H_
#define _LINUX_MISC_H_

/* defined for prctl(2) */
#define LINUX_PR_SET_PDEATHSIG	1	/* Second arg is signal. */
#define LINUX_PR_GET_PDEATHSIG	2	/*
					 * Second arg is a ptr to return the
					 * signal.
					 */
#define LINUX_PR_GET_KEEPCAPS	7	/* Get drop capabilities on setuid */
#define LINUX_PR_SET_KEEPCAPS	8	/* Set drop capabilities on setuid */
#define LINUX_PR_SET_NAME	15	/* Set process name. */
#define LINUX_PR_GET_NAME	16	/* Get process name. */

#define LINUX_MAX_COMM_LEN	16	/* Maximum length of process name. */

/* This looks very unportable to me, but this is how Linux defines it. */
struct linux_sysinfo {
	long uptime;
	unsigned long loads[3];
#define LINUX_SYSINFO_LOADS_SCALE 65536
	unsigned long totalram;
	unsigned long freeram;
	unsigned long sharedram;
	unsigned long bufferram;
	unsigned long totalswap;
	unsigned long freeswap;
	unsigned short procs;
	unsigned long totalbig;
	unsigned long freebig;
	unsigned int mem_unit;
	char _f[20-2*sizeof(long)-sizeof(int)];
};

struct linux_rusage {
	struct linux_timeval ru_utime;	/* user time used */
	struct linux_timeval ru_stime;	/* system time used */
	long	ru_maxrss;		/* max resident set size */
	long	ru_ixrss;		/* integral shared text memory size */
	long	ru_idrss;		/* integral unshared data " */
	long	ru_isrss;		/* integral unshared stack " */
	long	ru_minflt;		/* page reclaims */
	long	ru_majflt;		/* page faults */
	long	ru_nswap;		/* swaps */
	long	ru_inblock;		/* block input operations */
	long	ru_oublock;		/* block output operations */
	long	ru_msgsnd;		/* messages sent */
	long	ru_msgrcv;		/* messages received */
	long	ru_nsignals;		/* signals received */
	long	ru_nvcsw;		/* voluntary context switches */
	long	ru_nivcsw;		/* involuntary " */
};

/*
 * Options passed to the Linux wait4() system call.
 */
#define	LINUX_WAIT4_WNOHANG	0x00000001
#define	LINUX_WAIT4_WUNTRACED	0x00000002
#define	LINUX_WAIT4_WCLONE	0x80000000

#endif /* !_LINUX_MISC_H_ */


code:
/*	$OpenBSD: linux_mmap.h,v 1.3 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_mmap.h,v 1.1 1995/02/28 23:25:52 fvdl Exp $	*/
/*
 * Copyright (c) 1995 Frank van der Linden
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project
 *      by Frank van der Linden
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_MMAP_H_
#define _LINUX_MMAP_H_

#define LINUX_PROT_NONE		0x00
#define LINUX_PROT_READ		0x01
#define LINUX_PROT_WRITE	0x02
#define LINUX_PROT_EXEC		0x04

#define LINUX_MAP_SHARED	0x0001
#define LINUX_MAP_PRIVATE	0x0002

#define LINUX_MAP_FIXED		0x0010
#define LINUX_MAP_ANON		0x0020

/* the following flags are silently ignored */

#define LINUX_MAP_GROWSDOWN	0x0400
#define LINUX_MAP_DENYWRITE	0x0800
#define LINUX_MAP_EXECUTABLE	0x1000

#endif /* !_LINUX_MMAP_H_ */


code:
/*	$OpenBSD: linux_mount.c,v 1.4 1996/10/16 12:25:26 deraadt Exp $	*/

/*
 * Copyright (c) 1996 Erik Theisen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/filedesc.h>

#include <sys/syscallargs.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_errno.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>

/*
 * These are just dummy mount/umount functions
 * who's purpose is to satisfy brain dead code
 * that bindly calls mount().  They always
 * return EPERM.
 *
 * You really shouldn't be running code via
 * emulation that mounts FSs.
 */
int
linux_sys_mount(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_mount_args /* {
		syscallarg(char *) specialfile;
		syscallarg(char *) dir;
		syscallarg(char *) filesystemtype;
		syscallarg(long) rwflag;
		syscallarg(void *) data;
	} *uap = v */ ;
        return EPERM;
}

int
linux_sys_umount(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_umount_args /* {
		syscallarg(char *) specialfile;
	} *uap = v */ ;
        return EPERM;
}


code:
/*	$OpenBSD: linux_msg.h,v 1.3 2011/04/05 22:54:31 pirofti Exp $	*/
/*	$NetBSD: linux_msg.h,v 1.2 1995/08/15 21:14:34 fvdl Exp $	*/

/*
 * Copyright (c) 1995 Frank van der Linden
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed for the NetBSD Project
 *      by Frank van der Linden
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_MSG_H_
#define _LINUX_MSG_H_

/*
 * msq_id_ds structure. Mostly the same fields, except for some internal
 * ones.
 */
struct linux_msqid_ds {
	struct linux_ipc_perm	l_msg_perm;
	void			*l_msg_first;
	void			*l_msg_last;
	linux_time_t		l_msg_stime;
	linux_time_t		l_msg_rtime;
	linux_time_t		l_msg_ctime;
	void			*l_wwait;	/* Linux internal */
	void			*l_rwait;	/* Linux internal */
	ushort			l_msg_cbytes;
	ushort			l_msg_qnum;
	ushort			l_msg_qbytes;
	ushort			l_msg_lspid;
	ushort			l_msg_lrpid;
};

#define LINUX_MSG_NOERROR	0x1000
#define LINUX_MSG_EXCEPT	0x2000

/*
 * The notorious anonymous message structure.
 */
struct linux_mymsg {
	long	l_mtype;
	char	l_mtext[1];
};

/*
 * This kludge is used for the 6th argument to the msgrcv system
 * call, to get around the maximum of 5 arguments to a syscall in Linux.
 */
struct linux_msgrcv_msgarg {
	struct linux_mymsg *msg;
	int type;
};
/*
 * For msgctl calls.
 */
struct linux_msginfo {
	int	l_msgpool;
	int	l_msgmap;
	int	l_msgmax;
	int	l_msgmnb;
	int	l_msgmni;
	int	l_msgssz;
	int	l_msgtql;
	ushort	l_msgseg;
};

#define LINUX_MSG_STAT	11
#define LINUX_MSG_INFO	12

#endif /* _LINUX_MSG_H_ */


code:
/*	$OpenBSD: linux_resource.c,v 1.6 2012/09/05 17:13:37 deraadt Exp $	*/

/*
 * Copyright (c) 2000 Niklas Hallqvist
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Linux "resource" syscall emulation
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/syscallargs.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_resource.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>

/* linux_resource.c */
int	linux_to_bsd_rlimit(u_int);
int	linux_dogetrlimit(struct proc *, void *, register_t *, rlim_t);

static u_int linux_to_bsd_rlimit_map[] = {
	RLIMIT_CPU,
	RLIMIT_FSIZE,
	RLIMIT_DATA,
	RLIMIT_STACK,
	RLIMIT_CORE,
	RLIMIT_RSS,
	RLIMIT_NPROC,
	RLIMIT_NOFILE,
	RLIMIT_MEMLOCK,
	RLIM_NLIMITS		/* LINUX_RLIMIT_AS not supported */
};

int
linux_to_bsd_rlimit(which)
	u_int which;
{
	if (which >= LINUX_RLIM_NLIMITS)
		return (RLIM_NLIMITS);
	return (linux_to_bsd_rlimit_map[which]);
}

struct compat_sys_setrlimit_args {
	syscallarg(int) which;
	syscallarg(struct olimit *) rlp;
};

struct compat_linux_rlimit {
	int32_t rlim_cur;		/* current (soft) limit */
	int32_t rlim_max;		/* maximum value for rlim_cur */
};

int compat_sys_setrlimit(struct proc *p, void *v, register_t *retval);
int
compat_sys_setrlimit(struct proc *p, void *v, register_t *retval)
{
	struct compat_sys_setrlimit_args *uap = v;
	struct compat_linux_rlimit olim;
	struct rlimit lim;
	int error;

	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&olim,
	    sizeof (olim));
	if (error)
		return (error);
	lim.rlim_cur = olim.rlim_cur;
	lim.rlim_max = olim.rlim_max;
	return (dosetrlimit(p, SCARG(uap, which), &lim));
}

int
linux_sys_setrlimit(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_setrlimit_args /* {
		syscallarg(u_int) which;
		syscallarg(struct linux_rlimit *) rlp;
	} */ *uap = v;

	SCARG(uap, which) = linux_to_bsd_rlimit(SCARG(uap, which));
	if (SCARG(uap, which) == RLIM_NLIMITS)
		return (EINVAL);
	return (compat_sys_setrlimit(p, v, retval));
}

int
linux_dogetrlimit(p, v, retval, max)
	struct proc *p;
	void *v;
	register_t *retval;
	rlim_t max;
{
	struct linux_sys_getrlimit_args /* {
		syscallarg(u_int) which;
		syscallarg(struct linux_rlimit *) rlp;
	} */ *uap = v;
	u_int which;
	struct linux_rlimit rlim;

	which = linux_to_bsd_rlimit(SCARG(uap, which));
	if (which == RLIM_NLIMITS)
		return (EINVAL);

	rlim.rlim_cur = MIN(p->p_rlimit[which].rlim_cur, max);
	rlim.rlim_max = MIN(p->p_rlimit[which].rlim_max, max);
	return (copyout(&rlim, SCARG(uap, rlp), sizeof rlim));
}

int
linux_sys_getrlimit(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	return (linux_dogetrlimit(p, v, retval, LINUX_OLD_RLIM_INFINITY));
}

int
linux_sys_ugetrlimit(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	return (linux_dogetrlimit(p, v, retval, LINUX_RLIM_INFINITY));
}


code:
/*	$OpenBSD: linux_resource.h,v 1.2 2003/06/03 20:49:28 deraadt Exp $	*/

/*
 * Copyright (c) 2000 Niklas Hallqvist
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _LINUX_RESOURCE_H_
#define _LINUX_RESOURCE_H_

/*
 * Resource limits
 */
#define	LINUX_RLIMIT_CPU	0	/* cpu time in milliseconds */
#define	LINUX_RLIMIT_FSIZE	1	/* maximum file size */
#define	LINUX_RLIMIT_DATA	2	/* data size */
#define	LINUX_RLIMIT_STACK	3	/* stack size */
#define	LINUX_RLIMIT_CORE	4	/* core file size */
#define	LINUX_RLIMIT_RSS	5	/* resident set size */
#define	LINUX_RLIMIT_NPROC	6	/* number of processes */
#define	LINUX_RLIMIT_NOFILE	7	/* number of open files */
#define	LINUX_RLIMIT_MEMLOCK	8	/* locked-in-memory address space */
#define	LINUX_RLIMIT_AS		9	/* address space limit */

#define	LINUX_RLIM_NLIMITS	10	/* number of resource limits */

#define LINUX_RLIM_INFINITY	0xFFFFFFFF
#define LINUX_OLD_RLIM_INFINITY	0x7FFFFFFF

struct linux_rlimit {
	u_long	rlim_cur;
	u_long	rlim_max;
};

#endif /* !_LINUX_RESOURCE_H_ */


code:
/*	$OpenBSD: linux_sched.c,v 1.18 2014/07/09 14:42:53 guenther Exp $	*/
/*	$NetBSD: linux_sched.c,v 1.6 2000/05/28 05:49:05 thorpej Exp $	*/

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center; by Matthias Scheler.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Linux compatibility module. Try to deal with scheduler related syscalls.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/syscallargs.h>
#include <sys/signalvar.h>

#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/linux_machdep.h>

#include <compat/linux/linux_emuldata.h>
#include <compat/linux/linux_types.h>
#include <compat/linux/linux_sched.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>

void linux_child_return(void *);

int
linux_sys_clone(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_clone_args *uap = v;
	struct linux_emuldata *emul = p->p_emuldata;
	int cflags = SCARG(uap, flags);
	int flags = FORK_TFORK;
	int error = 0;

	/*
	 * We only support certain bits.  The Linux crew keep adding more,
	 * so let's test for anything outside of what we support and complain
	 * about them.  Not everything in this list is completely supported,
	 * they just aren't _always_ an error.
	 */
	if (cflags & ~(LINUX_CLONE_CSIGNAL | LINUX_CLONE_VM | LINUX_CLONE_FS |
	    LINUX_CLONE_FILES | LINUX_CLONE_SIGHAND | LINUX_CLONE_VFORK |
	    LINUX_CLONE_PARENT | LINUX_CLONE_THREAD | LINUX_CLONE_SYSVSEM |
	    LINUX_CLONE_DETACHED | LINUX_CLONE_UNTRACED | LINUX_CLONE_SETTLS |
	    LINUX_CLONE_PARENT_SETTID | LINUX_CLONE_CHILD_CLEARTID |
	    LINUX_CLONE_CHILD_SETTID))
		return (EINVAL);

	if (cflags & LINUX_CLONE_VM)
		flags |= FORK_SHAREVM;
	if (cflags & LINUX_CLONE_FILES)
		flags |= FORK_SHAREFILES;
	if (cflags & LINUX_CLONE_SIGHAND) {
		/* According to Linux, CLONE_SIGHAND requires CLONE_VM */
		if ((cflags & LINUX_CLONE_VM) == 0)
			return (EINVAL);
		flags |= FORK_SIGHAND;
	}
	if (cflags & LINUX_CLONE_VFORK)
		flags |= FORK_PPWAIT;
	if (cflags & LINUX_CLONE_THREAD) {
		/*
		 * Linux agrees with us: CLONE_THREAD requires
		 * CLONE_SIGHAND.  Unlike Linux, we also also require
		 * CLONE_FS and CLONE_SYSVSEM.  Also, we decree it
		 * to be incompatible with CLONE_VFORK, as I don't
		 * want to work out whether that's 100% safe.
		 * Requires CLONE_FILES so that the rest of the kernel
		 * can assume that threads share an fd table.
		 */
#define REQUIRED	\
	( LINUX_CLONE_SIGHAND \
	| LINUX_CLONE_FS \
	| LINUX_CLONE_SYSVSEM \
	| LINUX_CLONE_FILES \
	)
#define BANNED		\
	LINUX_CLONE_VFORK
		if ((cflags & (REQUIRED | BANNED)) != REQUIRED)
			return (EINVAL);
		flags |= FORK_THREAD;
	} else {
		/*
		 * These are only supported with CLONE_THREAD.  Arguably,
		 * CLONE_FS should be in this list, because we don't
		 * support sharing of working directory and root directory
		 * (chdir + chroot) except via threads.  On the other
		 * hand, we tie the sharing of umask to the sharing of
		 * files, so a process that doesn't request CLONE_FS but
		 * does ask for CLONE_FILES is going to get some of the
		 * former's effect.  Some programs (e.g., Opera) at least
		 * _seem_ to work if we let it through, so we'll just
		 * cross our fingers for now and silently ignore it if
		 * CLONE_FILES was also requested.
		 */
		if (cflags & (LINUX_CLONE_PARENT | LINUX_CLONE_SYSVSEM))
			return (EINVAL);
		if ((cflags & (LINUX_CLONE_FS | LINUX_CLONE_FILES)) ==
		    LINUX_CLONE_FS)
			return (EINVAL);

		/* We don't support alternate exit signals. */
		if ((cflags & LINUX_CLONE_CSIGNAL) != LINUX_SIGCHLD)
			return (EINVAL);
	}
	/*
	 * Since we don't support CLONE_PTRACE, the CLONE_UNTRACED
	 * flag can be silently ignored.  CLONE_DETACHED is always
	 * ignored by Linux.
	 */
	if (cflags & LINUX_CLONE_CHILD_SETTID)
		emul->child_set_tid = SCARG(uap, child_tidptr);
	else
		emul->child_set_tid = NULL;

	if (cflags & LINUX_CLONE_CHILD_CLEARTID)
		emul->child_clear_tid = SCARG(uap, child_tidptr);
	else
		emul->child_clear_tid = NULL;

	if (cflags & LINUX_CLONE_PARENT_SETTID)
		if (SCARG(uap, parent_tidptr) == NULL)
			return (EINVAL);

	if (cflags & LINUX_CLONE_SETTLS) {
		struct l_segment_descriptor ldesc;

		error = copyin(SCARG(uap, tls), &ldesc, sizeof(ldesc));
		if (error)
			return (error);

		if (ldesc.entry_number != GUGS_SEL)
			return (EINVAL);
		emul->child_tls_base = ldesc.base_addr;
		emul->set_tls_base = 1;
	}
	else
		emul->set_tls_base = 0;

	error = fork1(p, flags, SCARG(uap, stack), 0, linux_child_return,
	    NULL, retval, NULL);
	if (error)
		return error;

	if (cflags & LINUX_CLONE_PARENT_SETTID) {
		pid_t pid = retval[0];

		error = copyout(&pid, SCARG(uap, parent_tidptr), sizeof(pid));
	}
	return (error);
}

int
linux_sys_sched_setparam(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_setparam_args /* {
		syscallarg(linux_pid_t) pid;
		syscallarg(const struct linux_sched_param *) sp;
	} */ *uap = v;
	int error;
	struct linux_sched_param lp;
	struct proc *p;

	/*
	 * We only check for valid parameters and return afterwards.
	 */

	if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
		return (EINVAL);

	error = copyin(SCARG(uap, sp), &lp, sizeof(lp));
	if (error)
		return (error);

	if (SCARG(uap, pid) != 0) {
		struct ucred *uc = cp->p_ucred;

		if ((p = pfind(SCARG(uap, pid))) == NULL)
			return (ESRCH);
		if (!(cp == p ||
		      uc->cr_uid == 0 ||
		      uc->cr_ruid == p->p_ucred->cr_ruid ||
		      uc->cr_uid == p->p_ucred->cr_ruid ||
		      uc->cr_ruid == p->p_ucred->cr_uid ||
		      uc->cr_uid == p->p_ucred->cr_uid))
			return (EPERM);
	}

	return (0);
}

int
linux_sys_sched_getparam(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_getparam_args /* {
		syscallarg(linux_pid_t) pid;
		syscallarg(struct linux_sched_param *) sp;
	} */ *uap = v;
	struct proc *p;
	struct linux_sched_param lp;

	/*
	 * We only check for valid parameters and return a dummy priority
	 * afterwards.
	 */
	if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
		return (EINVAL);

	if (SCARG(uap, pid) != 0) {
		struct ucred *uc = cp->p_ucred;

		if ((p = pfind(SCARG(uap, pid))) == NULL)
			return (ESRCH);
		if (!(cp == p ||
		      uc->cr_uid == 0 ||
		      uc->cr_ruid == p->p_ucred->cr_ruid ||
		      uc->cr_uid == p->p_ucred->cr_ruid ||
		      uc->cr_ruid == p->p_ucred->cr_uid ||
		      uc->cr_uid == p->p_ucred->cr_uid))
			return (EPERM);
	}

	lp.sched_priority = 0;
	return (copyout(&lp, SCARG(uap, sp), sizeof lp));
}

int
linux_sys_sched_setscheduler(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_setscheduler_args /* {
		syscallarg(linux_pid_t) pid;
		syscallarg(int) policy;
		syscallarg(cont struct linux_sched_scheduler *) sp;
	} */ *uap = v;
	int error;
	struct linux_sched_param lp;
	struct proc *p;

	/*
	 * We only check for valid parameters and return afterwards.
	 */

	if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
		return (EINVAL);

	error = copyin(SCARG(uap, sp), &lp, sizeof(lp));
	if (error)
		return (error);

	if (SCARG(uap, pid) != 0) {
		struct ucred *uc = cp->p_ucred;

		if ((p = pfind(SCARG(uap, pid))) == NULL)
			return (ESRCH);
		if (!(cp == p ||
		      uc->cr_uid == 0 ||
		      uc->cr_ruid == p->p_ucred->cr_ruid ||
		      uc->cr_uid == p->p_ucred->cr_ruid ||
		      uc->cr_ruid == p->p_ucred->cr_uid ||
		      uc->cr_uid == p->p_ucred->cr_uid))
			return (EPERM);
	}

	/*
	 * We can't emulate anything but the default scheduling policy.
	 */
	if (SCARG(uap, policy) != LINUX_SCHED_OTHER || lp.sched_priority != 0)
		return (EINVAL);

	return (0);
}

int
linux_sys_sched_getscheduler(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_getscheduler_args /* {
		syscallarg(linux_pid_t) pid;
	} */ *uap = v;
	struct proc *p;

	*retval = -1;

	/*
	 * We only check for valid parameters and return afterwards.
	 */

	if (SCARG(uap, pid) != 0) {
		struct ucred *uc = cp->p_ucred;

		if ((p = pfind(SCARG(uap, pid))) == NULL)
			return (ESRCH);
		if (!(cp == p ||
		      uc->cr_uid == 0 ||
		      uc->cr_ruid == p->p_ucred->cr_ruid ||
		      uc->cr_uid == p->p_ucred->cr_ruid ||
		      uc->cr_ruid == p->p_ucred->cr_uid ||
		      uc->cr_uid == p->p_ucred->cr_uid))
			return (EPERM);
	}

	/*
	 * We can't emulate anything but the default scheduling policy.
	 */
	*retval = LINUX_SCHED_OTHER;
	return (0);
}

int
linux_sys_sched_yield(struct proc *cp, void *v, register_t *retval)
{
	need_resched(curcpu());
	return (0);
}

int
linux_sys_sched_get_priority_max(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_get_priority_max_args /* {
		syscallarg(int) policy;
	} */ *uap = v;

	/*
	 * We can't emulate anything but the default scheduling policy.
	 */
	if (SCARG(uap, policy) != LINUX_SCHED_OTHER) {
		*retval = -1;
		return (EINVAL);
	}

	*retval = 0;
	return (0);
}

int
linux_sys_sched_get_priority_min(struct proc *cp, void *v, register_t *retval)
{
	struct linux_sys_sched_get_priority_min_args /* {
		syscallarg(int) policy;
	} */ *uap = v;

	/*
	 * We can't emulate anything but the default scheduling policy.
	 */
	if (SCARG(uap, policy) != LINUX_SCHED_OTHER) {
		*retval = -1;
		return (EINVAL);
	}

	*retval = 0;
	return (0);
}

int
linux_sys_set_tid_address(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_set_tid_address_args *uap = v;
	struct linux_emuldata *emul = p->p_emuldata;

	emul->my_clear_tid = SCARG(uap, tidptr);

	*retval = p->p_p->ps_pid;
	return 0;
}

void
linux_child_return(void *arg)
{
	struct proc *p = (struct proc *)arg;
	struct linux_emuldata *emul = p->p_emuldata;

	if (emul->set_tls_base)
		i386_set_threadbase(p, emul->my_tls_base, TSEG_GS);

	if (emul->my_set_tid) {
		pid_t pid = p->p_pid + THREAD_PID_OFFSET;

		if (copyout(&pid, emul->my_set_tid, sizeof(pid)))
			psignal(p, SIGSEGV);
	}

	child_return(p);
}