code:
/*	$OpenBSD: linux_exec.c,v 1.43 2015/05/05 02:13:47 guenther Exp $	*/
/*	$NetBSD: linux_exec.c,v 1.13 1996/04/05 00:01:10 christos Exp $	*/

/*-
 * Copyright (c) 1994, 1995, 1998, 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Christos Zoulas, Frank van der Linden, Eric Haszlakiewicz and
 * Thor Lancelot Simon.
 *
 * 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/exec_elf.h>

#include <sys/mman.h>
#include <sys/syscallargs.h>
#include <sys/signalvar.h>

#include <uvm/uvm_extern.h>

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

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_syscall.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_syscallargs.h>
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_exec.h>
#include <compat/linux/linux_emuldata.h>

#define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *))

const char linux_emul_path[] = "/emul/linux";
extern int linux_error[];
extern char linux_sigcode[], linux_esigcode[];
extern struct sysent linux_sysent[];
#ifdef SYSCALL_DEBUG
extern char *linux_syscallnames[];
#endif

extern struct mutex futex_lock;
extern void futex_pool_init(void);

void linux_e_proc_exec(struct proc *, struct exec_package *);
void linux_e_proc_fork(struct proc *, struct proc *);
void linux_e_proc_exit(struct proc *);
void linux_e_proc_init(struct proc *, struct vmspace *);

struct emul emul_linux_elf = {
	"linux",
	linux_error,
	linux_sendsig,
	LINUX_SYS_syscall,
	LINUX_SYS_MAXSYSCALL,
	linux_sysent,
#ifdef SYSCALL_DEBUG
	linux_syscallnames,
#else
	NULL,
#endif
	LINUX_ELF_AUX_ARGSIZ,
	elf32_copyargs,
	setregs,
	exec_elf32_fixup,
	NULL,			/* coredump */
	linux_sigcode,
	linux_esigcode,
	0,
	NULL,
	linux_e_proc_exec,
	linux_e_proc_fork,
	linux_e_proc_exit,
};

/*
 * Allocate per-process structures. Called when executing Linux
 * process. We can reuse the old emuldata - if it's not null,
 * the executed process is of same emulation as original forked one.
 */
void
linux_e_proc_init(struct proc *p, struct vmspace *vmspace)
{
	if (!p->p_emuldata) {
		/* allocate new Linux emuldata */
		p->p_emuldata = malloc(sizeof(struct linux_emuldata),
		    M_EMULDATA, M_WAITOK|M_ZERO);
	}
	else {
		memset(p->p_emuldata, '\0', sizeof(struct linux_emuldata));
	}

	/* Set the process idea of the break to the real value */
	((struct linux_emuldata *)(p->p_emuldata))->p_break =
	    vmspace->vm_daddr + ptoa(vmspace->vm_dsize);
}

void
linux_e_proc_exec(struct proc *p, struct exec_package *epp)
{
	/* exec, use our vmspace */
	linux_e_proc_init(p, p->p_vmspace);
}

/*
 * Emulation per-process exit hook.
 */
void
linux_e_proc_exit(struct proc *p)
{
	struct linux_emuldata *emul = p->p_emuldata;

	if (emul->my_clear_tid) {
		pid_t zero = 0;

		if (copyout(&zero, emul->my_clear_tid, sizeof(zero)))
			psignal(p, SIGSEGV);
		/*
		 * not yet: futex(my_clear_tid, FUTEX_WAKE, 1, NULL, NULL, 0)
		 */
	}

	/* free Linux emuldata and set the pointer to null */
	free(p->p_emuldata, M_EMULDATA, 0);
	p->p_emuldata = NULL;
}

/*
 * Emulation fork hook.
 */
void
linux_e_proc_fork(struct proc *p, struct proc *parent)
{
	struct linux_emuldata *emul;
	struct linux_emuldata *parent_emul;

	/* Allocate new emuldata for the new process. */
	p->p_emuldata = NULL;

	/* fork, use parent's vmspace (our vmspace may not be setup yet) */
	linux_e_proc_init(p, parent->p_vmspace);

	emul = p->p_emuldata;
	parent_emul = parent->p_emuldata;

	emul->my_set_tid = parent_emul->child_set_tid;
	emul->my_clear_tid = parent_emul->child_clear_tid;
	emul->my_tls_base = parent_emul->child_tls_base;
	emul->set_tls_base = parent_emul->set_tls_base;
}

int
exec_linux_elf32_makecmds(struct proc *p, struct exec_package *epp)
{
	if (!(emul_linux_elf.e_flags & EMUL_ENABLED))
		return (ENOEXEC);

	return exec_elf32_makecmds(p, epp);
}

int
linux_elf_probe(struct proc *p, struct exec_package *epp, char *itp,
    u_long *pos)
{
	Elf32_Ehdr *eh = epp->ep_hdr;
	char *bp, *brand;
	int error;
	size_t len;

	if (!(emul_linux_elf.e_flags & EMUL_ENABLED))
		return (ENOEXEC);

	/*
	 * Modern Linux binaries carry an identification note.
	 */
	if (ELFNAME(os_pt_note)(p, epp, epp->ep_hdr, "GNU", 4, 0x10) == 0) {
		goto recognized;
	}

	brand = elf32_check_brand(eh);
	if (brand != NULL && strcmp(brand, "Linux") != 0)
		return (EINVAL);

	/*
	 * If this is a static binary, do not allow it to run, as it
	 * has not been identified. We'll give non-static binaries a
	 * chance to run, as the Linux ld.so name is usually unique
	 * enough to clear any ambiguity.
	 */
	if (itp == NULL)
		return (EINVAL);

recognized:
	if (itp) {
		if ((error = emul_find(p, NULL, linux_emul_path, itp, &bp, 0)))
			return (error);
		error = copystr(bp, itp, MAXPATHLEN, &len);
		free(bp, M_TEMP, 0);
		if (error)
			return (error);
	}
	epp->ep_emul = &emul_linux_elf;
	*pos = ELF32_NO_ADDR;

	mtx_init(&futex_lock, IPL_NONE);
	futex_pool_init();

	return (0);
}

/*
 * Execve(2). Just check the alternate emulation path, and pass it on
 * to the regular execve().
 */
int
linux_sys_execve(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_execve_args /* {
		syscallarg(char *) path;
		syscallarg(char **) argv;
		syscallarg(char **) envp;
        } */ *uap = v;
	struct sys_execve_args ap;
	caddr_t sg;

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

	SCARG(&ap, path) = SCARG(uap, path);
	SCARG(&ap, argp) = SCARG(uap, argp);
	SCARG(&ap, envp) = SCARG(uap, envp);

	return (sys_execve(p, &ap, retval));
}


code:
/*	$OpenBSD: linux_exec.h,v 1.10 2013/11/03 14:32:32 pirofti Exp $	*/
/*	$NetBSD: linux_exec.h,v 1.5 1995/10/07 06:27:01 mycroft 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_EXEC_H_
#define	_LINUX_EXEC_H_

int exec_linux_elf32_makecmds(struct proc *, struct exec_package *);

int linux_elf_probe(struct proc *, struct exec_package *, char *,
    u_long *);

#endif /* !_LINUX_EXEC_H_ */

license: unknown


code:
/*	$OpenBSD: linux_fcntl.h,v 1.4 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_fcntl.h,v 1.1 1995/02/28 23:25:40 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.
 */

/*
 * Various flag values used in Linux for open(2) and fcntl(2).
 */

#ifndef _LINUX_FCNTL_H_
#define _LINUX_FCNTL_H_

/* read/write mode for open(2) (as usual) */
#define LINUX_O_RDONLY		0x0000
#define LINUX_O_WRONLY		0x0001
#define LINUX_O_RDWR		0x0002
#define LINUX_O_ACCMODE		0x0003

/* flags used in open(2) */
#define LINUX_O_CREAT		0x0040
#define LINUX_O_EXCL		0x0080
#define LINUX_O_NOCTTY		0x0100
#define LINUX_O_TRUNC		0x0200
#define LINUX_O_APPEND		0x0400
#define LINUX_O_NDELAY		0x0800
#define LINUX_O_SYNC		0x1000

#define LINUX_FASYNC		0x2000

/* fcntl(2) operations */
#define LINUX_F_DUPFD		0
#define LINUX_F_GETFD		1
#define LINUX_F_SETFD		2
#define LINUX_F_GETFL		3
#define LINUX_F_SETFL		4
#define LINUX_F_GETLK		5
#define LINUX_F_SETLK		6
#define LINUX_F_SETLKW		7
#define LINUX_F_SETOWN		8
#define LINUX_F_GETOWN		9

#define	LINUX_F_GETLK64		12
#define	LINUX_F_SETLK64		13
#define	LINUX_F_SETLKW64	14

#define LINUX_F_RDLCK		0
#define LINUX_F_WRLCK		1
#define LINUX_F_UNLCK		2

#define LINUX_LOCK_EX		4
#define LINUX_LOCK_SH		8

/*
 * The arguments in the flock structure have a different order from the
 * BSD structure.
 */

struct linux_flock {
	short       l_type;
	short       l_whence;
	linux_off_t l_start;
	linux_off_t l_len;
	linux_pid_t l_pid;
};

struct linux_flock64 {
        short  l_type;
        short  l_whence;
        linux_loff_t l_start;
        linux_loff_t l_len;
        linux_pid_t  l_pid;
};

#endif /* _LINUX_FCNTL_H_ */


code:
/*	$OpenBSD: linux_fdio.c,v 1.7 2012/04/22 05:43:14 guenther Exp $	*/
/*	$NetBSD: linux_fdio.c,v 1.1 2000/12/10 14:12:16 fvdl Exp $	*/

/*
 * Copyright (c) 2000 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Frank van der Linden for Wasabi Systems, Inc.
 *
 * 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
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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/systm.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/disklabel.h>

#include <sys/syscallargs.h>

#include <dev/isa/fdreg.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_ioctl.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_fdio.h>

#include <machine/ioctl_fd.h>

#include <compat/linux/linux_syscallargs.h>

int
linux_ioctl_fdio(struct proc *p, struct linux_sys_ioctl_args *uap,
		 register_t *retval)
{
	struct filedesc *fdp;
	struct file *fp;
	int error;
	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
	u_long com;
	struct fd_type fparams;
	struct linux_floppy_struct lflop;
	struct linux_floppy_drive_struct ldrive;

	com = (u_long)SCARG(uap, data);

	fdp = p->p_fd;
	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
		return (EBADF);

	FREF(fp);
	com = SCARG(uap, com);
	ioctlf = fp->f_ops->fo_ioctl;

	retval[0] = error = 0;

	switch (com) {
	case LINUX_FDMSGON:
	case LINUX_FDMSGOFF:
	case LINUX_FDTWADDLE:
	case LINUX_FDCLRPRM:
		/* whatever you say */
		break;
	case LINUX_FDPOLLDRVSTAT:
		/*
		 * Just fill in some innocent defaults.
		 */
		memset(&ldrive, 0, sizeof ldrive);
		ldrive.fd_ref = 1;
		ldrive.maxblock = 2;
		ldrive.maxtrack = ldrive.track = 1;
		ldrive.flags = LINUX_FD_DISK_WRITABLE;
		error = copyout(&ldrive, SCARG(uap, data), sizeof ldrive);
		break;
	case LINUX_FDGETPRM:
		error = ioctlf(fp, FD_GTYPE, (caddr_t)&fparams, p);
		if (error != 0)
			break;
		lflop.size = fparams.heads * fparams.sectrac * fparams.tracks;
		lflop.sect = fparams.sectrac;
		lflop.head = fparams.heads;
		lflop.track = fparams.tracks;
		lflop.stretch = fparams.step == 2 ? 1 : 0;
		lflop.spec1 = fparams.steprate;
		lflop.gap = fparams.gap1;
		lflop.fmt_gap = fparams.gap2;
		lflop.rate = fparams.rate;

		error = copyout(&lflop, SCARG(uap, data), sizeof lflop);
		break;
	case LINUX_FDSETPRM:
		/*
		 * Should use FDIOCSETFORMAT here, iff its interface
		 * is extended.
		 */
	case LINUX_FDDEFPRM:
	case LINUX_FDFMTBEG:
	case LINUX_FDFMTTRK:
	case LINUX_FDFMTEND:
	case LINUX_FDSETEMSGTRESH:
	case LINUX_FDFLUSH:
	case LINUX_FDSETMAXERRS:
	case LINUX_FDGETMAXERRS:
	case LINUX_FDGETDRVTYP:
	case LINUX_FDSETDRVPRM:
	case LINUX_FDGETDRVPRM:
	case LINUX_FDGETDRVSTAT:
	case LINUX_FDRESET:
	case LINUX_FDGETFDCSTAT:
	case LINUX_FDWERRORCLR:
	case LINUX_FDWERRORGET:
	case LINUX_FDRAWCMD:
	case LINUX_FDEJECT:
	default:
		error = EINVAL;
	}

	FRELE(fp, p);
	return 0;
}


code:
/*	$OpenBSD: linux_fdio.h,v 1.2 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_fdio.h,v 1.1 2000/12/10 14:12:16 fvdl Exp $	*/

/*
 * Copyright (c) 2000 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Frank van der Linden for Wasabi Systems, Inc.
 *
 * 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
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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_FDIO_H_
#define _LINUX_FDIO_H_

/*
 * Linux floppy ioctl call structures and defines.
 */

struct linux_floppy_struct {
	u_int size;
	u_int sect;
	u_int head;
	u_int track;
	u_int stretch;
	u_char gap;
	u_char rate;
	u_char spec1;
	u_char fmt_gap;
	const char *name;
};

struct linux_floppy_max_errors {
	u_int abort;
	u_int read_track;
	u_int reset;
	u_int recal;
	u_int reporting;
};

struct linux_floppy_drive_params {
	char cmos;
	u_long max_dtr;
	u_long hlt;
	u_long hut;
        u_long srt;
	u_long spinup;
	u_long spindown;
        u_char spindown_offset;
	u_char select_delay;
	u_char rps;
	u_char tracks;
	u_long timeout;
	u_char interleave_sect;
	struct linux_floppy_max_errors max_errors;
	char flags;
	char read_track;
	short autodetect[8];
	int checkfreq;
	int native_format;
};

struct linux_floppy_drive_struct {
	u_long flags;
	u_long spinup_date;
	u_long select_date;
	u_long first_read_date;
	short probed_format;
	short track;
	short maxblock;
	short maxtrack;
	int generation;
	int keep_data;
	int fd_ref;
	int fd_device;
	u_long last_checked;
	char *dmabuf;
	int bufblocks;
};

#define LINUX_FD_NEED_TWADDLE	0x01
#define LINUX_FD_VERIFY		0x02
#define LINUX_FD_DISK_NEWCHANGE	0x04
#define LINUX_FD_DISK_CHANGED	0x10
#define LINUX_FD_DISK_WRITABLE	0x20

struct linux_floppy_fdc_state {
	int spec1;
	int spec2;
	int dtr;
	u_char version;
	u_char dor;
	u_long address;
	u_int rawcmd:2;
	u_int reset:1;
	u_int need_configure:1;
	u_int perp_mode:2;
	u_int has_fifo:1;
	u_int driver_version;
	u_char track[4];
};

struct linux_floppy_write_errors {
	u_int write_errors;
	u_long first_error_sector;
	u_int first_error_generation;
	u_long last_error_sector;
	u_int last_error_generation;
	u_int badness;
};

struct linux_floppy_raw_cmd {
	u_int flags;
	void *data;
	caddr_t kernel_data;
	struct floppy_raw_cmd *next;
	long length;
	long phys_length;
	int buffer_length;
	u_char rate;
	u_char cmd_count;
	u_char cmd[16];
	u_char reply_count;
	u_char reply[16];
	int track;
	int resultcode;
	int reserved1;
	int reserved2;
};

struct linux_format_descr {
	u_int device;
	u_int head;
	u_int track;
};

typedef char linux_floppy_drive_name[16];

#define LINUX_FDCLRPRM		_LINUX_IO(2, 0x41)
#define LINUX_FDSETPRM		_LINUX_IOW(2, 0x42, struct linux_floppy_struct)
#define LINUX_FDDEFPRM		_LINUX_IOW(2, 0x43, struct linux_floppy_struct)
#define LINUX_FDGETPRM		_LINUX_IOR(2, 0x04, struct linux_floppy_struct)
#define LINUX_FDMSGON		_LINUX_IO(2, 0x45)
#define LINUX_FDMSGOFF		_LINUX_IO(2, 0x46)
#define LINUX_FDFMTBEG		_LINUX_IO(2, 0x47)
#define LINUX_FDFMTTRK		_LINUX_IOW(2, 0x48, struct linux_format_descr)
#define LINUX_FDFMTEND		_LINUX_IO(2, 0x49)
#define LINUX_FDSETEMSGTRESH	_LINUX_IO(2, 0x4a)
#define LINUX_FDFLUSH		_LINUX_IO(2, 0x4b)
#define LINUX_FDSETMAXERRS \
	_LINUX_IOW(2, 0x4c, struct linux_floppy_max_errors)
#define LINUX_FDGETMAXERRS \
	_LINUX_IOR(2, 0x0e, struct linux_floppy_max_errors)
#define LINUX_FDGETDRVTYP	_LINUX_IOR(2, 0x0f, linux_floppy_drive_name)
/* 0x90 is not a typo, that's how it's listed in the Linux include file */
#define LINUX_FDSETDRVPRM \
	_LINUX_IOW(2, 0x90, struct linux_floppy_drive_params)
#define LINUX_FDGETDRVPRM \
	_LINUX_IOR(2, 0x11, struct linux_floppy_drive_params)
#define LINUX_FDGETDRVSTAT \
	_LINUX_IOR(2, 0x12, struct linux_floppy_drive_struct)
#define LINUX_FDPOLLDRVSTAT \
	_LINUX_IOR(2, 0x13, struct linux_floppy_drive_struct)
#define LINUX_FDRESET		_LINUX_IO(2, 0x54)
#define LINUX_FDGETFDCSTAT \
	_LINUX_IOR(2, 0x15, struct linux_floppy_fdc_state)
#define LINUX_FDWERRORCLR	_LINUX_IO(2, 0x56)
#define LINUX_FDWERRORGET \
	_LINUX_IOR(2, 0x17, struct linux_floppy_write_errors)
#define LINUX_FDRAWCMD		_LINUX_IO(2, 0x58)
#define LINUX_FDTWADDLE		_LINUX_IO(2, 0x59)
#define LINUX_FDEJECT		_LINUX_IO(2, 0x5a)

#endif /* _LINUX_FDIO_H_ */


code:
/*	$OpenBSD: linux_file.c,v 1.30 2014/03/26 05:23:42 guenther Exp $	*/
/*	$NetBSD: linux_file.c,v 1.15 1996/05/20 01:59:09 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/filedesc.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/signalvar.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/stdint.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_fcntl.h>
#include <compat/linux/linux_util.h>

#include <machine/linux_machdep.h>

static int linux_to_bsd_ioflags(int);
static int bsd_to_linux_ioflags(int);
static void bsd_to_linux_flock(struct flock *, struct linux_flock *);
static void linux_to_bsd_flock(struct linux_flock *, struct flock *);
static int bsd_to_linux_stat(struct stat *, struct linux_stat *);
static int linux_stat1(struct proc *, void *, register_t *, int);

/*
 * Some file-related calls are handled here. The usual flag conversion
 * an structure conversion is done, and alternate emul path searching.
 */

/*
 * The next two functions convert between the Linux and OpenBSD values
 * of the flags used in open(2) and fcntl(2).
 */
static int
linux_to_bsd_ioflags(lflags)
	int lflags;
{
	int res = 0;

	res |= cvtto_bsd_mask(lflags, LINUX_O_WRONLY, O_WRONLY);
	res |= cvtto_bsd_mask(lflags, LINUX_O_RDONLY, O_RDONLY);
	res |= cvtto_bsd_mask(lflags, LINUX_O_RDWR, O_RDWR);
	res |= cvtto_bsd_mask(lflags, LINUX_O_CREAT, O_CREAT);
	res |= cvtto_bsd_mask(lflags, LINUX_O_EXCL, O_EXCL);
	res |= cvtto_bsd_mask(lflags, LINUX_O_NOCTTY, O_NOCTTY);
	res |= cvtto_bsd_mask(lflags, LINUX_O_TRUNC, O_TRUNC);
	res |= cvtto_bsd_mask(lflags, LINUX_O_NDELAY, O_NDELAY);
	res |= cvtto_bsd_mask(lflags, LINUX_O_SYNC, O_SYNC);
	res |= cvtto_bsd_mask(lflags, LINUX_FASYNC, O_ASYNC);
	res |= cvtto_bsd_mask(lflags, LINUX_O_APPEND, O_APPEND);

	return res;
}

static int
bsd_to_linux_ioflags(bflags)
	int bflags;
{
	int res = 0;

	res |= cvtto_linux_mask(bflags, O_WRONLY, LINUX_O_WRONLY);
	res |= cvtto_linux_mask(bflags, O_RDONLY, LINUX_O_RDONLY);
	res |= cvtto_linux_mask(bflags, O_RDWR, LINUX_O_RDWR);
	res |= cvtto_linux_mask(bflags, O_CREAT, LINUX_O_CREAT);
	res |= cvtto_linux_mask(bflags, O_EXCL, LINUX_O_EXCL);
	res |= cvtto_linux_mask(bflags, O_NOCTTY, LINUX_O_NOCTTY);
	res |= cvtto_linux_mask(bflags, O_TRUNC, LINUX_O_TRUNC);
	res |= cvtto_linux_mask(bflags, O_NDELAY, LINUX_O_NDELAY);
	res |= cvtto_linux_mask(bflags, O_SYNC, LINUX_O_SYNC);
	res |= cvtto_linux_mask(bflags, O_ASYNC, LINUX_FASYNC);
	res |= cvtto_linux_mask(bflags, O_APPEND, LINUX_O_APPEND);

	return res;
}

/*
 * creat(2) is an obsolete function, but it's present as a Linux
 * system call, so let's deal with it.
 *
 * Just call open(2) with the TRUNC, CREAT and WRONLY flags.
 */
int
linux_sys_creat(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_creat_args /* {
		syscallarg(char *) path;
		syscallarg(int) mode;
	} */ *uap = v;
	struct sys_open_args oa;
	caddr_t sg;

	sg = stackgap_init(p);
	LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));

	SCARG(&oa, path) = SCARG(uap, path);
	SCARG(&oa, flags) = O_CREAT | O_TRUNC | O_WRONLY;
	SCARG(&oa, mode) = SCARG(uap, mode);

	return sys_open(p, &oa, retval);
}

/*
 * open(2). Take care of the different flag values, and let the
 * OpenBSD syscall do the real work. See if this operation
 * gives the current process a controlling terminal.
 * (XXX is this necessary?)
 */
int
linux_sys_open(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_open_args /* {
		syscallarg(char *) path;
		syscallarg(int) flags;
		syscallarg(int) mode;
	} */ *uap = v;
	int error, fl;
	struct sys_open_args boa;
	caddr_t sg;

	sg = stackgap_init(p);

	fl = linux_to_bsd_ioflags(SCARG(uap, flags));

	if (fl & O_CREAT)
		LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
	else
		LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&boa, path) = SCARG(uap, path);
	SCARG(&boa, flags) = fl;
	SCARG(&boa, mode) = SCARG(uap, mode);

	if ((error = sys_open(p, &boa, retval)))
		return error;

	/*
	 * this bit from sunos_misc.c (and svr4_fcntl.c).
	 * If we are a session leader, and we don't have a controlling
	 * terminal yet, and the O_NOCTTY flag is not set, try to make
	 * this the controlling terminal.
	 */
        if (!(fl & O_NOCTTY) && SESS_LEADER(p->p_p) &&
	    !(p->p_p->ps_flags & PS_CONTROLT)) {
                struct filedesc *fdp = p->p_fd;
                struct file     *fp;

		if ((fp = fd_getfile(fdp, *retval)) == NULL)
			return (EBADF);
		FREF(fp);
                if (fp->f_type == DTYPE_VNODE)
                        (fp->f_ops->fo_ioctl) (fp, TIOCSCTTY, (caddr_t) 0, p);
		FRELE(fp, p);
        }
	return 0;
}

int
linux_sys_lseek(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_lseek_args /* {
		syscallarg(int) fd;
		syscallarg(long) offset;
		syscallarg(int) whence;
	} */ *uap = v;
	struct sys_lseek_args bla;

	SCARG(&bla, fd) = SCARG(uap, fd);
	SCARG(&bla, offset) = SCARG(uap, offset);
	SCARG(&bla, whence) = SCARG(uap, whence);

	return sys_lseek(p, &bla, retval);
}

/*
 * This appears to be part of a Linux attempt to switch to 64 bits file sizes.
 */
int
linux_sys_llseek(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_llseek_args /* {
		syscallarg(int) fd;
		syscallarg(uint32_t) ohigh;
		syscallarg(uint32_t) olow;
		syscallarg(caddr_t) res;
		syscallarg(int) whence;
	} */ *uap = v;
	struct sys_lseek_args bla;
	int error;
	off_t off;

	off = SCARG(uap, olow) | (((off_t) SCARG(uap, ohigh)) << 32);

	SCARG(&bla, fd) = SCARG(uap, fd);
	SCARG(&bla, offset) = off;
	SCARG(&bla, whence) = SCARG(uap, whence);

	if ((error = sys_lseek(p, &bla, retval)))
		return error;

	if ((error = copyout(retval, SCARG(uap, res), sizeof (off_t))))
		return error;

	retval[0] = 0;
	return 0;
}

/*
 * The next two functions take care of converting the flock
 * structure back and forth between Linux and OpenBSD format.
 * The only difference in the structures is the order of
 * the fields, and the 'whence' value.
 */
static void
bsd_to_linux_flock(bfp, lfp)
	struct flock *bfp;
	struct linux_flock *lfp;
{

	lfp->l_start = bfp->l_start;
	lfp->l_len = bfp->l_len;
	lfp->l_pid = bfp->l_pid;
	lfp->l_whence = bfp->l_whence;
	switch (bfp->l_type) {
	case F_RDLCK:
		lfp->l_type = LINUX_F_RDLCK;
		break;
	case F_UNLCK:
		lfp->l_type = LINUX_F_UNLCK;
		break;
	case F_WRLCK:
		lfp->l_type = LINUX_F_WRLCK;
		break;
	}
}

static void
linux_to_bsd_flock(lfp, bfp)
	struct linux_flock *lfp;
	struct flock *bfp;
{

	bfp->l_start = lfp->l_start;
	bfp->l_len = lfp->l_len;
	bfp->l_pid = lfp->l_pid;
	bfp->l_whence = lfp->l_whence;
	switch (lfp->l_type) {
	case LINUX_F_RDLCK:
		bfp->l_type = F_RDLCK;
		break;
	case LINUX_F_UNLCK:
		bfp->l_type = F_UNLCK;
		break;
	case LINUX_F_WRLCK:
		bfp->l_type = F_WRLCK;
		break;
	}
}

/*
 * Most actions in the fcntl() call are straightforward; simply
 * pass control to the OpenBSD system call. A few commands need
 * conversions after the actual system call has done its work,
 * because the flag values and lock structure are different.
 */
int
linux_sys_fcntl(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fcntl_args /* {
		syscallarg(int) fd;
		syscallarg(int) cmd;
		syscallarg(void *) arg;
	} */ *uap = v;
	int fd, cmd, error;
	intptr_t val;
	caddr_t arg, sg;
	struct linux_flock lfl;
	struct flock *bfp, bfl;
	struct sys_fcntl_args fca;
	struct filedesc *fdp;
	struct file *fp;
	struct vnode *vp;
	struct vattr va;
	long pgid;
	struct pgrp *pgrp;
	struct tty *tp, *(*d_tty)(dev_t);

	fd = SCARG(uap, fd);
	cmd = SCARG(uap, cmd);
	arg = (caddr_t) SCARG(uap, arg);

	switch (cmd) {
	case LINUX_F_DUPFD:
		cmd = F_DUPFD;
		break;
	case LINUX_F_GETFD:
		cmd = F_GETFD;
		break;
	case LINUX_F_SETFD:
		cmd = F_SETFD;
		break;
	case LINUX_F_GETFL:
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_GETFL;
		SCARG(&fca, arg) = arg;
		if ((error = sys_fcntl(p, &fca, retval)))
			return error;
		retval[0] = bsd_to_linux_ioflags(retval[0]);
		return 0;
	case LINUX_F_SETFL:
		val = linux_to_bsd_ioflags((intptr_t)SCARG(uap, arg));
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_SETFL;
		SCARG(&fca, arg) = (caddr_t) val;
		return sys_fcntl(p, &fca, retval);
	case LINUX_F_GETLK:
		sg = stackgap_init(p);
		if ((error = copyin(arg, &lfl, sizeof lfl)))
			return error;
		linux_to_bsd_flock(&lfl, &bfl);
		bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_GETLK;
		SCARG(&fca, arg) = bfp;
		if ((error = copyout(&bfl, bfp, sizeof bfl)))
			return error;
		if ((error = sys_fcntl(p, &fca, retval)))
			return error;
		if ((error = copyin(bfp, &bfl, sizeof bfl)))
			return error;
		bsd_to_linux_flock(&bfl, &lfl);
		error = copyout(&lfl, arg, sizeof lfl);
		return error;
		break;
	case LINUX_F_SETLK:
	case LINUX_F_SETLKW:
		cmd = (cmd == LINUX_F_SETLK ? F_SETLK : F_SETLKW);
		if ((error = copyin(arg, &lfl, sizeof lfl)))
			return error;
		linux_to_bsd_flock(&lfl, &bfl);
		sg = stackgap_init(p);
		bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
		if ((error = copyout(&bfl, bfp, sizeof bfl)))
			return error;
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = cmd;
		SCARG(&fca, arg) = bfp;
		return sys_fcntl(p, &fca, retval);
		break;
	case LINUX_F_SETOWN:
	case LINUX_F_GETOWN:
		/*
		 * We need to route around the normal fcntl() for these calls,
		 * since it uses TIOC{G,S}PGRP, which is too restrictive for
		 * Linux F_{G,S}ETOWN semantics. For sockets, this problem
		 * does not exist.
		 */
		fdp = p->p_fd;
		if ((fp = fd_getfile(fdp, fd)) == NULL)
			return (EBADF);
		if (fp->f_type == DTYPE_SOCKET) {
			cmd = cmd == LINUX_F_SETOWN ? F_SETOWN : F_GETOWN;
			break;
		}
		vp = (struct vnode *)fp->f_data;
		if (vp->v_type != VCHR)
			return EINVAL;
		FREF(fp);
		error = VOP_GETATTR(vp, &va, p->p_ucred, p);
		FRELE(fp, p);
		if (error)
			return error;
		d_tty = cdevsw[major(va.va_rdev)].d_tty;
		if (!d_tty || (!(tp = (*d_tty)(va.va_rdev))))
			return EINVAL;
		if (cmd == LINUX_F_GETOWN) {
			retval[0] = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
			return 0;
		}
		if ((long)arg <= 0) {
			pgid = -(long)arg;
		} else {
			struct process *pr = prfind((long)arg);
			if (pr == 0)
				return (ESRCH);
			pgid = (long)pr->ps_pgrp->pg_id;
		}
		pgrp = pgfind(pgid);
		if (pgrp == NULL || pgrp->pg_session != p->p_p->ps_session)
			return EPERM;
		tp->t_pgrp = pgrp;
		return 0;
	default:
		return EOPNOTSUPP;
	}

	SCARG(&fca, fd) = fd;
	SCARG(&fca, cmd) = cmd;
	SCARG(&fca, arg) = arg;

	return sys_fcntl(p, &fca, retval);
}

/*
 * Convert a OpenBSD stat structure to a Linux stat structure.
 * Only the order of the fields and the padding in the structure
 * is different. linux_fakedev is a machine-dependent function
 * which optionally converts device driver major/minor numbers
 * (XXX horrible, but what can you do against code that compares
 * things against constant major device numbers? sigh)
 */
static int
bsd_to_linux_stat(bsp, lsp)
	struct stat *bsp;
	struct linux_stat *lsp;
{
	if (bsp->st_ino > LINUX_INO_MAX)
		return EOVERFLOW;

	lsp->lst_dev     = bsp->st_dev;
	lsp->lst_ino     = (linux_ino_t)bsp->st_ino;
	lsp->lst_mode    = bsp->st_mode;
	lsp->lst_nlink   = bsp->st_nlink;
	lsp->lst_uid     = bsp->st_uid;
	lsp->lst_gid     = bsp->st_gid;
	lsp->lst_rdev    = linux_fakedev(bsp->st_rdev);
	lsp->lst_size    = bsp->st_size;
	lsp->lst_blksize = bsp->st_blksize;
	lsp->lst_blocks  = bsp->st_blocks;
	lsp->lst_atime   = bsp->st_atime;
	lsp->lst_mtime   = bsp->st_mtime;
	lsp->lst_ctime   = bsp->st_ctime;

	return 0;
}

/*
 * The stat functions below are plain sailing. stat and lstat are handled
 * by one function to avoid code duplication.
 */
int
linux_sys_fstat(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fstat_args /* {
		syscallarg(int) fd;
		syscallarg(linux_stat *) sp;
	} */ *uap = v;
	struct sys_fstat_args fsa;
	struct linux_stat tmplst;
	struct stat *st,tmpst;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);

	st = stackgap_alloc(&sg, sizeof (struct stat));

	SCARG(&fsa, fd) = SCARG(uap, fd);
	SCARG(&fsa, sb) = st;

	if ((error = sys_fstat(p, &fsa, retval)))
		return error;

	if ((error = copyin(st, &tmpst, sizeof tmpst)))
		return error;

	if ((error = bsd_to_linux_stat(&tmpst, &tmplst)))
		return error;

	if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
		return error;

	return 0;
}

static int
linux_stat1(p, v, retval, dolstat)
	struct proc *p;
	void *v;
	register_t *retval;
	int dolstat;
{
	struct sys_stat_args sa;
	struct linux_stat tmplst;
	struct stat *st, tmpst;
	caddr_t sg;
	int error;
	struct linux_sys_stat_args *uap = v;

	sg = stackgap_init(p);

	st = stackgap_alloc(&sg, sizeof (struct stat));
	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&sa, ub) = st;
	SCARG(&sa, path) = SCARG(uap, path);

	if ((error = (dolstat ? sys_lstat(p, &sa, retval) :
				sys_stat(p, &sa, retval))))
		return error;

	if ((error = copyin(st, &tmpst, sizeof tmpst)))
		return error;

	if ((error = bsd_to_linux_stat(&tmpst, &tmplst)))
		return error;

	if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
		return error;

	return 0;
}

int
linux_sys_stat(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_stat_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_stat *) sp;
	} */ *uap = v;

	return linux_stat1(p, uap, retval, 0);
}

int
linux_sys_lstat(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_lstat_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_stat *) sp;
	} */ *uap = v;

	return linux_stat1(p, uap, retval, 1);
}

/*
 * The following syscalls are mostly here because of the alternate path check.
 */
int
linux_sys_access(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_access_args /* {
		syscallarg(char *) path;
		syscallarg(int) flags;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_access(p, uap, retval);
}

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

{
	struct linux_sys_unlink_args /* {
		syscallarg(char *) path;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_unlink(p, uap, retval);
}

int
linux_sys_chdir(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_chdir_args /* {
		syscallarg(char *) path;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_chdir(p, uap, retval);
}

int
linux_sys_mknod(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_mknod_args /* {
		syscallarg(char *) path;
		syscallarg(int) mode;
		syscallarg(int) dev;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);
	struct sys_mkfifo_args bma;

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

	/*
	 * BSD handles FIFOs separately
	 */
	if (S_ISFIFO(SCARG(uap, mode))) {
		SCARG(&bma, path) = SCARG(uap, path);
		SCARG(&bma, mode) = SCARG(uap, mode);
		return sys_mkfifo(p, uap, retval);
	} else
		return sys_mknod(p, uap, retval);
}

int
linux_sys_chmod(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_chmod_args /* {
		syscallarg(char *) path;
		syscallarg(int) mode;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_chmod(p, uap, retval);
}

int
linux_sys_chown(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_chown_args /* {
		syscallarg(char *) path;
		syscallarg(uid_t) uid;
		syscallarg(gid_t) gid;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_chown(p, uap, retval);
}

int
linux_sys_chown16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_chown16_args /* {
		syscallarg(char *) path;
		syscallarg(int) uid;
		syscallarg(int) gid;
	} */ *uap = v;
	struct sys_chown_args bca;
	caddr_t sg = stackgap_init(p);

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

	SCARG(&bca, path) = SCARG(uap, path);
	SCARG(&bca, uid) = ((linux_uid_t)SCARG(uap, uid) == (linux_uid_t)-1) ?
		(uid_t)-1 : SCARG(uap, uid);
	SCARG(&bca, gid) = ((linux_gid_t)SCARG(uap, gid) == (linux_gid_t)-1) ?
		(gid_t)-1 : SCARG(uap, gid);

	return sys_chown(p, &bca, retval);
}

int
linux_sys_fchown16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fchown16_args /* {
		syscallarg(int) fd;
		syscallarg(int) uid;
		syscallarg(int) gid;
	} */ *uap = v;
	struct sys_fchown_args bfa;

	SCARG(&bfa, fd) = SCARG(uap, fd);
	SCARG(&bfa, uid) = ((linux_uid_t)SCARG(uap, uid) == (linux_uid_t)-1) ?
		(uid_t)-1 : SCARG(uap, uid);
	SCARG(&bfa, gid) = ((linux_gid_t)SCARG(uap, gid) == (linux_gid_t)-1) ?
		(gid_t)-1 : SCARG(uap, gid);

	return sys_fchown(p, &bfa, retval);
}

int
linux_sys_lchown16(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_lchown16_args /* {
		syscallarg(char *) path;
		syscallarg(int) uid;
		syscallarg(int) gid;
	} */ *uap = v;
	struct sys_lchown_args bla;

	SCARG(&bla, path) = SCARG(uap, path);
	SCARG(&bla, uid) = ((linux_uid_t)SCARG(uap, uid) == (linux_uid_t)-1) ?
		(uid_t)-1 : SCARG(uap, uid);
	SCARG(&bla, gid) = ((linux_gid_t)SCARG(uap, gid) == (linux_gid_t)-1) ?
		(gid_t)-1 : SCARG(uap, gid);

	return sys_lchown(p, &bla, retval);
}

int
linux_sys_rename(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_rename_args /* {
		syscallarg(char *) from;
		syscallarg(char *) to;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, from));
	LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, to));

	return sys_rename(p, uap, retval);
}

int
linux_sys_mkdir(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_mkdir_args /* {
		syscallarg(char *) path;
		syscallarg(int) mode;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_mkdir(p, uap, retval);
}

int
linux_sys_rmdir(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_rmdir_args /* {
		syscallarg(char *) path;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_rmdir(p, uap, retval);
}

int
linux_sys_symlink(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_symlink_args /* {
		syscallarg(char *) path;
		syscallarg(char *) to;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_symlink(p, uap, retval);
}

int
linux_sys_readlink(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_readlink_args /* {
		syscallarg(char *) name;
		syscallarg(char *) buf;
		syscallarg(int) count;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);

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

	return sys_readlink(p, uap, retval);
}

int
linux_sys_ftruncate(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ftruncate_args /* {
		syscallarg(int)  fd;
		syscallarg(long) length;
	} */ *uap = v;
	struct sys_ftruncate_args sta;

	SCARG(&sta, fd) = SCARG(uap, fd);
	SCARG(&sta, length) = SCARG(uap, length);

	return sys_ftruncate(p, uap, retval);
}

int
linux_sys_truncate(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_truncate_args /* {
		syscallarg(char *) path;
		syscallarg(long) length;
	} */ *uap = v;
	caddr_t sg = stackgap_init(p);
	struct sys_truncate_args sta;

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

	SCARG(&sta, path) = SCARG(uap, path);
	SCARG(&sta, length) = SCARG(uap, length);

	return sys_truncate(p, &sta, retval);
}

/*
 * This is just fsync() for now (just as it is in the Linux kernel)
 */
int
linux_sys_fdatasync(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fdatasync_args /* {
		syscallarg(int) fd;
	} */ *uap = v;
	return sys_fsync(p, uap, retval);
}

/*
 * pread(2).
 */
int
linux_sys_pread(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_pread_args /* {
		syscallarg(int) fd;
		syscallarg(void *) buf;
		syscallarg(size_t) nbyte;
		syscallarg(linux_off_t) offset;
	} */ *uap = v;
	struct sys_pread_args pra;

	SCARG(&pra, fd) = SCARG(uap, fd);
	SCARG(&pra, buf) = SCARG(uap, buf);
	SCARG(&pra, nbyte) = SCARG(uap, nbyte);
	SCARG(&pra, offset) = SCARG(uap, offset);

	return sys_pread(p, &pra, retval);
}

/*
 * pwrite(2).
 */
int
linux_sys_pwrite(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_pwrite_args /* {
		syscallarg(int) fd;
		syscallarg(char *) buf;
		syscallarg(size_t) nbyte;
		syscallarg(linux_off_t) offset;
	} */ *uap = v;
	struct sys_pwrite_args pra;

	SCARG(&pra, fd) = SCARG(uap, fd);
	SCARG(&pra, buf) = SCARG(uap, buf);
	SCARG(&pra, nbyte) = SCARG(uap, nbyte);
	SCARG(&pra, offset) = SCARG(uap, offset);

	return sys_pwrite(p, &pra, retval);
}


code:
/*	$OpenBSD: linux_file64.c,v 1.10 2014/09/14 14:17:23 jsg Exp $	*/
/*	$NetBSD: linux_file64.c,v 1.2 2000/12/12 22:24:56 jdolecek Exp $	*/

/*-
 * Copyright (c) 1995, 1998, 2000 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.
 *
 * 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 64bit filesystem calls. Used on 32bit archs, not used on 64bit ones.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/filedesc.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/tty.h>
#include <sys/conf.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_fcntl.h>
#include <compat/linux/linux_util.h>

#include <machine/linux_machdep.h>

void bsd_to_linux_flock64(struct flock *, struct linux_flock64 *);
void linux_to_bsd_flock64(struct linux_flock64 *, struct flock *);
static int bsd_to_linux_stat(struct stat *, struct linux_stat64 *);
static int linux_do_stat64(struct proc *, void *, register_t *, int);

/*
 * Convert a OpenBSD stat structure to a Linux stat structure.
 * Only the order of the fields and the padding in the structure
 * is different. linux_fakedev is a machine-dependent function
 * which optionally converts device driver major/minor numbers
 * (XXX horrible, but what can you do against code that compares
 * things against constant major device numbers? sigh)
 */
static int
bsd_to_linux_stat(bsp, lsp)
	struct stat *bsp;
	struct linux_stat64 *lsp;
{
	lsp->lst_dev     = bsp->st_dev;
	lsp->lst_ino     = (linux_ino64_t)bsp->st_ino;
	lsp->lst_mode    = (linux_mode_t)bsp->st_mode;
	if (bsp->st_nlink >= (1 << 15))
		lsp->lst_nlink = (1 << 15) - 1;
	else
		lsp->lst_nlink = (linux_nlink_t)bsp->st_nlink;
	lsp->lst_uid     = bsp->st_uid;
	lsp->lst_gid     = bsp->st_gid;
	lsp->lst_rdev    = linux_fakedev(bsp->st_rdev);
	lsp->lst_size    = bsp->st_size;
	lsp->lst_blksize = bsp->st_blksize;
	lsp->lst_blocks  = bsp->st_blocks;
	lsp->lst_atime   = bsp->st_atime;
	lsp->lst_mtime   = bsp->st_mtime;
	lsp->lst_ctime   = bsp->st_ctime;
#if LINUX_STAT64_HAS_BROKEN_ST_INO
	if (bsp->st_ino > LINUX_INO_MAX)
		return EOVERFLOW;
	lsp->__lst_ino   = (linux_ino_t)bsp->st_ino;
#endif

	return 0;
}

/*
 * The stat functions below are plain sailing. stat and lstat are handled
 * by one function to avoid code duplication.
 */
int
linux_sys_fstat64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fstat64_args /* {
		syscallarg(int) fd;
		syscallarg(linux_stat64 *) sp;
	} */ *uap = v;
	struct sys_fstat_args fsa;
	struct linux_stat64 tmplst;
	struct stat *st,tmpst;
	caddr_t sg;
	int error;

	sg = stackgap_init(p);

	st = stackgap_alloc(&sg, sizeof (struct stat));

	SCARG(&fsa, fd) = SCARG(uap, fd);
	SCARG(&fsa, sb) = st;

	if ((error = sys_fstat(p, &fsa, retval)))
		return error;

	if ((error = copyin(st, &tmpst, sizeof tmpst)))
		return error;

	if ((error = bsd_to_linux_stat(&tmpst, &tmplst)))
		return error;

	if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
		return error;

	return 0;
}

static int
linux_do_stat64(p, v, retval, dolstat)
	struct proc *p;
	void *v;
	register_t *retval;
	int dolstat;
{
	struct sys_stat_args sa;
	struct linux_stat64 tmplst;
	struct stat *st, tmpst;
	caddr_t sg;
	int error;
	struct linux_sys_stat64_args *uap = v;

	sg = stackgap_init(p);
	st = stackgap_alloc(&sg, sizeof (struct stat));
	LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));

	SCARG(&sa, ub) = st;
	SCARG(&sa, path) = SCARG(uap, path);

	if ((error = (dolstat ? sys_lstat(p, &sa, retval) :
				sys_stat(p, &sa, retval))))
		return error;

	if ((error = copyin(st, &tmpst, sizeof tmpst)))
		return error;

	if ((error = bsd_to_linux_stat(&tmpst, &tmplst)))
		return error;

	if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
		return error;

	return 0;
}

int
linux_sys_stat64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_stat64_args /* {
		syscallarg(const char *) path;
		syscallarg(struct linux_stat64 *) sp;
	} */ *uap = v;

	return linux_do_stat64(p, uap, retval, 0);
}

int
linux_sys_lstat64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_lstat64_args /* {
		syscallarg(char *) path;
		syscallarg(struct linux_stat64 *) sp;
	} */ *uap = v;

	return linux_do_stat64(p, uap, retval, 1);
}

int
linux_sys_truncate64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_truncate64_args /* {
		syscallarg(char *) path;
		syscallarg(off_t) length;
	} */ *uap = v;
	struct sys_truncate_args ta;
	caddr_t sg = stackgap_init(p);

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

	SCARG(&ta, path) = SCARG(uap, path);
	SCARG(&ta, length) = SCARG(uap, length);

	return sys_truncate(p, &ta, retval);
}

/*
 * This is needed due to padding in OpenBSD's sys_ftruncate_args
 */
int
linux_sys_ftruncate64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_ftruncate64_args /* {
		syscallarg(int) fd;
		syscallarg(off_t) length;
        } */ *uap = v;
	struct sys_ftruncate_args fta;

	SCARG(&fta, fd) = SCARG(uap, fd);
	SCARG(&fta, length) = SCARG(uap, length);

	return sys_ftruncate(p, &fta, retval);
}

/*
 * The next two functions take care of converting the flock
 * structure back and forth between Linux and OpenBSD format.
 * The only difference in the structures is the order of
 * the fields, and the 'whence' value.
 */
void
bsd_to_linux_flock64(struct flock *bfp, struct linux_flock64 *lfp)
{
	lfp->l_start = bfp->l_start;
	lfp->l_len = bfp->l_len;
	lfp->l_pid = bfp->l_pid;
	lfp->l_whence = bfp->l_whence;
	switch (bfp->l_type) {
	case F_RDLCK:
		lfp->l_type = LINUX_F_RDLCK;
		break;
	case F_UNLCK:
		lfp->l_type = LINUX_F_UNLCK;
		break;
	case F_WRLCK:
		lfp->l_type = LINUX_F_WRLCK;
		break;
	}
}

void
linux_to_bsd_flock64(struct linux_flock64 *lfp, struct flock *bfp)
{
	bfp->l_start = lfp->l_start;
	bfp->l_len = lfp->l_len;
	bfp->l_pid = lfp->l_pid;
	bfp->l_whence = lfp->l_whence;
	switch (lfp->l_type) {
	case LINUX_F_RDLCK:
		bfp->l_type = F_RDLCK;
		break;
	case LINUX_F_UNLCK:
		bfp->l_type = F_UNLCK;
		break;
	case LINUX_F_WRLCK:
		bfp->l_type = F_WRLCK;
		break;
	}
}

int
linux_sys_fcntl64(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct linux_sys_fcntl64_args /* {
		syscallarg(u_int) fd;
		syscallarg(u_int) cmd;
		syscallarg(void *) arg;
	} */ *uap = v;
	int fd, cmd, error;
	caddr_t arg, sg;
	struct linux_flock64 lfl;
	struct flock *bfp, bfl;
	struct sys_fcntl_args fca;

	fd = SCARG(uap, fd);
	cmd = SCARG(uap, cmd);
	arg = (caddr_t) SCARG(uap, arg);

	switch (cmd) {
	case LINUX_F_GETLK64:
		sg = stackgap_init(p);
		if ((error = copyin(arg, &lfl, sizeof lfl)))
			return error;
		linux_to_bsd_flock64(&lfl, &bfl);
		bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_GETLK;
		SCARG(&fca, arg) = bfp;
		if ((error = copyout(&bfl, bfp, sizeof bfl)))
			return error;
		if ((error = sys_fcntl(p, &fca, retval)))
			return error;
		if ((error = copyin(bfp, &bfl, sizeof bfl)))
			return error;
		bsd_to_linux_flock64(&bfl, &lfl);
		error = copyout(&lfl, arg, sizeof lfl);
		return (error);
	case LINUX_F_SETLK64:
	case LINUX_F_SETLKW64:
		cmd = (cmd == LINUX_F_SETLK64 ? F_SETLK : F_SETLKW);
		if ((error = copyin(arg, &lfl, sizeof lfl)))
			return error;
		linux_to_bsd_flock64(&lfl, &bfl);
		sg = stackgap_init(p);
		bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
		if ((error = copyout(&bfl, bfp, sizeof bfl)))
			return error;
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = cmd;
		SCARG(&fca, arg) = bfp;
		return (sys_fcntl(p, &fca, retval));
	default:
		return (linux_sys_fcntl(p, v, retval));
	}
	/* NOTREACHED */
}


code:
/* $OpenBSD: linux_futex.c,v 1.16 2014/08/20 06:03:20 doug Exp $ */
/*	$NetBSD: linux_futex.c,v 1.26 2010/07/07 01:30:35 chs Exp $ */

/*-
 * Copyright (c) 2011 Paul Irofti <pirofti@openbsd.org>
 * Copyright (c) 2005 Emmanuel Dreyfus, 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 by Emmanuel Dreyfus
 * 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 THE AUTHOR 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 AUTHOR 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.
 */

#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/signal.h>
#include <sys/stdint.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/kernel.h>

#include <sys/syscallargs.h>

#include <compat/linux/linux_types.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/linux/linux_time.h>
#include <compat/linux/linux_futex.h>

#ifdef COMPATFUTEX_DEBUG
#define DPRINTF(x) 	printf x
#else
#define DPRINTF(x)
#endif

struct pool futex_pool;
struct pool futex_wp_pool;
int futex_pool_initialized;

struct futex;

struct waiting_proc {
	struct proc *p;
	struct futex *wp_new_futex;
	TAILQ_ENTRY(waiting_proc) wp_list;
	TAILQ_ENTRY(waiting_proc) wp_rqlist;
};

struct futex {
	void *f_uaddr;
	int f_refcount;
	LIST_ENTRY(futex) f_list;
	TAILQ_HEAD(, waiting_proc) f_waiting_proc;
	TAILQ_HEAD(, waiting_proc) f_requeue_proc;
};

static LIST_HEAD(futex_list, futex) futex_list;

struct mutex futex_lock;
void futex_pool_init(void);

int linux_do_futex(struct proc *, const struct linux_sys_futex_args *,
    register_t *, struct timespec *);

struct futex * futex_get(void *);
void futex_ref(struct futex *);
void futex_put(struct futex *);

int futex_sleep(struct futex **, struct proc *, int, struct waiting_proc *);
int futex_wake(struct futex *, int, struct futex *, int);
int futex_atomic_op(struct proc *, int, void *);

int futex_itimespecfix(struct timespec *ts);

int
linux_sys_futex(struct proc *p, void *v, register_t *retval)
{
        struct linux_sys_futex_args /* {
		syscallarg(int *) uaddr;
		syscallarg(int) op;
		syscallarg(int) val;
		syscallarg(const struct linux_timespec *) timeout;
		syscallarg(int *) uaddr2;
		syscallarg(int) val3;
	} */ *uap = v;

	struct linux_timespec lts;
	struct timespec ts = {0, 0};
	int error;

	if ((SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) == LINUX_FUTEX_WAIT &&
	    SCARG(uap, timeout) != NULL) {
		if ((error = copyin(SCARG(uap, timeout),
		    &lts, sizeof(lts))) != 0) {
			return error;
		}
		linux_to_bsd_timespec(&ts, &lts);
	}

	return linux_do_futex(p, uap, retval, &ts);
}

int
linux_do_futex(struct proc *p, const struct linux_sys_futex_args *uap,
    register_t *retval, struct timespec *ts)
{
	/* {
		syscallarg(int *) uaddr;
		syscallarg(int) op;
		syscallarg(int) val;
		syscallarg(const struct linux_timespec *) timeout;
		syscallarg(int *) uaddr2;
		syscallarg(int) val3;
	} */
	int ret;
	int error = 0;
	struct futex *f;
	struct futex *newf;
	int timeout_hz;
	struct timeval tv;
	struct futex *f2;
	struct waiting_proc *wp;
	int op_ret;

	int uaddr_val;
	int args_val = SCARG(uap, val);

#ifdef COMPATFUTEX_DEBUG
	int tid = p->p_pid + THREAD_PID_OFFSET;
#endif

	/*
	 * Our implementation provides only private futexes. Most of the apps
	 * should use private futexes but don't claim so. Therefore we treat
	 * all futexes as private by clearing the FUTEX_PRIVATE_FLAG. It works
	 * in most cases (ie. when futexes are not shared on file descriptor
	 * or between different processes).
	 */
	switch (SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) {
	case LINUX_FUTEX_WAIT:

		DPRINTF(("FUTEX_WAIT\n"));
		mtx_enter(&futex_lock);
		if ((error = copyin(SCARG(uap, uaddr),
		    &uaddr_val, sizeof(uaddr_val))) != 0) {
			mtx_leave(&futex_lock);
			return error;
		}

		DPRINTF(("FUTEX_WAIT %d: Sleep if [%8.8X] %d == %d\n", tid,
		    SCARG(uap, uaddr), uaddr_val, args_val));

		/* Sleep only if [uaddr] == val */
		if (uaddr_val != args_val) {
			mtx_leave(&futex_lock);
			return EWOULDBLOCK;
		}

		/* Check for infinity */
		if (ts->tv_sec == 0 && ts->tv_nsec == 0) {
			timeout_hz = 0;
		} else {
			if ((error = futex_itimespecfix(ts)) != 0) {
				mtx_leave(&futex_lock);
				return error;
			}
			TIMESPEC_TO_TIMEVAL(&tv, ts);
			timeout_hz = tvtohz(&tv);
		}

		/*
		 * If the user process requests a non null timeout,
		 * make sure we do not turn it into an infinite
		 * timeout because timeout_hz is 0.
		 *
		 * We use a minimal timeout of 1/hz. Maybe it would make
		 * sense to just return ETIMEDOUT without sleeping.
		 */
		if (SCARG(uap, timeout) != NULL && timeout_hz == 0)
			timeout_hz = 1;

		DPRINTF(("FUTEX_WAIT %d: Sleep for %lld.%09lds (%dhz)\n",
		    tid, (long long)ts->tv_sec, ts->tv_nsec, timeout_hz));

		wp = pool_get(&futex_wp_pool, PR_NOWAIT);
		if (wp == NULL) {
			DPRINTF(("FUTEX_WAIT %d: Couldn't fetch a new wp from"
			    "futex_wp_pool.\n", tid));
			mtx_leave(&futex_lock);
			return EAGAIN;
		}
		DPRINTF(("FUTEX_WAIT: get wp %p\n", wp));

		f = futex_get(SCARG(uap, uaddr));
		ret = futex_sleep(&f, p, timeout_hz, wp);
		futex_put(f);
		mtx_leave(&futex_lock);

		DPRINTF(("FUTEX_WAIT: put wp %p\n", wp));
		pool_put(&futex_wp_pool, wp);

		DPRINTF(("FUTEX_WAIT %d: Woke up from uaddr %8.8X with "
		    "ret = %d\n", tid, SCARG(uap, uaddr), ret));

		*retval = ret ? -1 : 0;
		switch (ret) {
		case EWOULDBLOCK:	/* timeout */
			return ETIMEDOUT;
			break;
		case EINTR:		/* signal */
			return EINTR;
			break;
		case 0:			/* FUTEX_WAKE received */
			DPRINTF(("FUTEX_WAIT %d: uaddr = %p, got it\n",
			    tid, SCARG(uap, uaddr)));
			return 0;
			break;
		default:
			DPRINTF(("FUTEX_WAIT: unexpected ret = %d\n", ret));
			break;
		}

		/* NOTREACHED */
		break;

	case LINUX_FUTEX_WAKE:
		/*
		 * XXX: Linux is able cope with different addresses
		 * corresponding to the same mapped memory in the sleeping
		 * and the waker process(es).
		 */
		DPRINTF(("FUTEX_WAKE %d: uaddr = %p, val = %d\n",
		    tid, SCARG(uap, uaddr), args_val));

		if (args_val < 0)
			return EINVAL;

		mtx_enter(&futex_lock);
		f = futex_get(SCARG(uap, uaddr));
		*retval = futex_wake(f, args_val, NULL, 0);
		futex_put(f);
		mtx_leave(&futex_lock);

		break;

	case LINUX_FUTEX_CMP_REQUEUE:

		DPRINTF(("FUTEX_CMP_REQUEUE\n"));
		if (args_val < 0)
			return EINVAL;

		/*
		 * Don't allow using the same address for requeueing.
		 *
		 * glibc seems to cope with this.
		 */
		if (SCARG(uap, uaddr) == SCARG(uap, uaddr2)) {
			return EINVAL;
		}

		mtx_enter(&futex_lock);
		if ((error = copyin(SCARG(uap, uaddr),
		    &uaddr_val, sizeof(uaddr_val))) != 0) {
			mtx_leave(&futex_lock);
			return error;
		}

		DPRINTF(("FUTEX_CMP_REQUEUE %d: Check for possible race "
		    "condition [%8.8X] %d != %d", tid, SCARG(uap, uaddr),
		    uaddr_val, SCARG(uap, val3)));
		if (uaddr_val != SCARG(uap, val3)) {
			mtx_leave(&futex_lock);
			return EAGAIN;
		}

		/* Following code is the same as REQUEUE */
		DPRINTF(("FUTEX_CMP_REQUEUE %d: Wakeup %d processes and requeue"
		    " waiters at %8.8X\n", tid, SCARG(uap, val),
		    SCARG(uap, uaddr2)));

		f = futex_get(SCARG(uap, uaddr));
		newf = futex_get(SCARG(uap, uaddr2));
		/*
		 * Check if uaddr2 is in use.
		 * If true, return EINVAL to avoid deadlock.
		 *
		 * glibc seems to cope with this.
		 */
		if (newf->f_refcount != 1) {
			futex_put(f);
			futex_put(newf);
			mtx_leave(&futex_lock);
			return (EINVAL);
		}

		*retval = futex_wake(f, args_val, newf,
		    (int)(unsigned long)SCARG(uap, timeout));
		futex_put(f);
		futex_put(newf);
		mtx_leave(&futex_lock);

		break;

	case LINUX_FUTEX_REQUEUE:
		DPRINTF(("FUTEX_REQUEUE %d: Wakeup %d processes and requeue "
		    "waiters at %8.8X\n", tid, SCARG(uap, val),
		    SCARG(uap, uaddr2)));

		if (args_val < 0)
			return EINVAL;

		mtx_enter(&futex_lock);
		f = futex_get(SCARG(uap, uaddr));
		newf = futex_get(SCARG(uap, uaddr2));
		*retval = futex_wake(f, args_val, newf,
		    (int)(unsigned long)SCARG(uap, timeout));
		futex_put(f);
		futex_put(newf);
		mtx_leave(&futex_lock);

		break;

	case LINUX_FUTEX_FD:
		DPRINTF(("linux_sys_futex: unimplemented op %d\n",
		    SCARG(uap, op)));
		return ENOSYS;
	case LINUX_FUTEX_WAKE_OP:
		DPRINTF(("FUTEX_WAKE_OP %d: uaddr = %p, op = %d, "
		    "val = %d, uaddr2 = %p, val3 = %d\n",
		    tid, SCARG(uap, uaddr), SCARG(uap, op), args_val,
		    SCARG(uap, uaddr2), SCARG(uap, val3)));

		if (args_val < 0)
			return EINVAL;

		mtx_enter(&futex_lock);
		f = futex_get(SCARG(uap, uaddr));
		f2 = futex_get(SCARG(uap, uaddr2));
		mtx_leave(&futex_lock);

		/*
		 * This function returns a positive number as results and
		 * negative as errors
		 */
		op_ret = futex_atomic_op(p, SCARG(uap, val3),
		    SCARG(uap, uaddr2));
		if (op_ret < 0) {
			futex_put(f);
			futex_put(f2);
			return -op_ret;
		}

		mtx_enter(&futex_lock);
		ret = futex_wake(f, args_val, NULL, 0);
		futex_put(f);
		if (op_ret > 0) {
			op_ret = 0;
			/*
			 * Linux abuses the address of the timespec parameter
			 * as the number of retries
			 */
			op_ret += futex_wake(f2,
			    (int)(unsigned long)SCARG(uap, timeout), NULL, 0);
			ret += op_ret;
		}
		futex_put(f2);
		mtx_leave(&futex_lock);
		*retval = ret;
		break;
	default:
		DPRINTF(("linux_sys_futex: unknown op %d\n",
		    SCARG(uap, op)));
		return ENOSYS;
	}
	return 0;
}

void
futex_pool_init(void)
{
	DPRINTF(("Inside futex_pool_init()\n"));

	if (!futex_pool_initialized) {
		pool_init(&futex_pool, sizeof(struct futex), 0, 0, PR_DEBUGCHK,
		    "futexpl", &pool_allocator_nointr);
		pool_init(&futex_wp_pool, sizeof(struct waiting_proc), 0, 0,
		    PR_DEBUGCHK, "futexwppl", &pool_allocator_nointr);
		futex_pool_initialized = 1;
	}
}

/*
 * Get a futex.
 * If we have an existing one, we will return that with the refcount bumped.
 * Otherwise we will allocate and hook up a new one.
 * Must be called with futex_lock held, but we may unlock it in order to
 * sleep for allocation.
 */
struct futex *
futex_get(void *uaddr)
{
	struct futex *f, *newf;

	MUTEX_ASSERT_LOCKED(&futex_lock);
	LIST_FOREACH(f, &futex_list, f_list) {
		if (f->f_uaddr == uaddr) {
			f->f_refcount++;
			return f;
		}
	}
	mtx_leave(&futex_lock);

	/* Not found, create it */
	newf = pool_get(&futex_pool, PR_WAITOK|PR_ZERO);
	DPRINTF(("futex_get: get %p\n", newf));

	mtx_enter(&futex_lock);
	/* Did someone else create it in the meantime? */
	LIST_FOREACH(f, &futex_list, f_list) {
		if (f->f_uaddr == uaddr) {
			f->f_refcount++;
			DPRINTF(("futex_get: put %p (found %p)\n", newf, f));
			pool_put(&futex_pool, newf);
			return f;
		}
	}
	newf->f_uaddr = uaddr;
	newf->f_refcount = 1;
	TAILQ_INIT(&newf->f_waiting_proc);
	TAILQ_INIT(&newf->f_requeue_proc);
	LIST_INSERT_HEAD(&futex_list, newf, f_list);

	return newf;
}

/*
 * Grab a reference on a futex.
 * The futex lock must be locked.
 */
void
futex_ref(struct futex *f)
{
	MUTEX_ASSERT_LOCKED(&futex_lock);
	f->f_refcount++;
}

/*
 * Release our reference on the futex.
 * must be called with the futex_lock held.
 */
void
futex_put(struct futex *f)
{
	DPRINTF(("futex_put: put %p ref %d\n", f, f->f_refcount));
	MUTEX_ASSERT_LOCKED(&futex_lock);
	KASSERT(f->f_refcount > 0);
	f->f_refcount--;
	if (f->f_refcount == 0) {
		KASSERT(TAILQ_EMPTY(&f->f_waiting_proc));
		KASSERT(TAILQ_EMPTY(&f->f_requeue_proc));
		LIST_REMOVE(f, f_list);
		pool_put(&futex_pool, f);
	}
}

int
futex_sleep(struct futex **fp, struct proc *p, int timeout,
    struct waiting_proc *wp)
{
	struct futex *f, *newf;
	int ret;

	MUTEX_ASSERT_LOCKED(&futex_lock);

	f = *fp;
	wp->p = p;
	wp->wp_new_futex = NULL;

	DPRINTF(("futex_sleep: sleep on futex %p\n", f));

	do {
		TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list);

		ret = msleep(f, &futex_lock, PUSER | PCATCH, "futex_sleep",
		    timeout);

		TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);

		/* did futex_wake() tells us to requeue? if not, we're done! */
		newf = wp->wp_new_futex;
		if (newf == NULL)
			break;

		/* yes, so clean up our requeue bits... */
		DPRINTF(("futex_sleep: requeue futex %p\n", newf));
		wp->wp_new_futex = NULL;
		TAILQ_REMOVE(&newf->f_requeue_proc, wp, wp_rqlist);

		/*
		 * ...and discard our reference to the old futex.
		 * The requeuing has already given us a reference
		 * to the new one.
		 */
		futex_put(f);
		*fp = f = newf;

		/* only restart if msleep() didn't fail (timeout or signal) */
	} while (ret == 0);

	return ret;
}

int
futex_wake(struct futex *f, int n, struct futex *newf, int n2)
{
	struct waiting_proc *wp;
	int count = 0;

	KASSERT(newf != f);
	MUTEX_ASSERT_LOCKED(&futex_lock);

	/*
	 * first, wake up any threads sleeping on this futex.
	 * note that sleeping threads are not in the process of requeueing.
	 */
	if (!TAILQ_EMPTY(&f->f_waiting_proc))
		wakeup(f); /* only call wakeup once */
	TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) {
		KASSERT(wp->wp_new_futex == NULL);

		DPRINTF(("futex_wake: signal futex %p ref %d\n",
			     f, f->f_refcount));
		if (count <= n) {
			count++;
		} else {
			if (newf == NULL)
				break;

			/* matching futex_put() is called by the other thread */
			futex_ref(newf);
			wp->wp_new_futex = newf;
			TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist);
			DPRINTF(("futex_wake: requeue newf %p ref %d\n",
				     newf, newf->f_refcount));
			if (count - n >= n2)
				goto out;
		}
	}

	/*
	 * next, deal with threads that are requeuing to this futex.
	 * we don't need to signal these threads, any thread on the
	 * requeue list has already been signaled but hasn't had a chance
	 * to run and requeue itself yet.  if we would normally wake
	 * a thread, just remove the requeue info.  if we would normally
	 * requeue a thread, change the requeue target.
	 */
	while ((wp = TAILQ_FIRST(&f->f_requeue_proc)) != NULL) {
		/* XXX: talk to oga, should mtx_enter again, recursive */
		KASSERT(wp->wp_new_futex == f);

		DPRINTF(("futex_wake: unrequeue f %p ref %d\n",
			     f, f->f_refcount));
		wp->wp_new_futex = NULL;
		TAILQ_REMOVE(&f->f_requeue_proc, wp, wp_rqlist);
		futex_put(f);

		if (count <= n) {
			count++;
		} else {
			if (newf == NULL) {
				break;
			}

			/*matching futex_put() is called by the other thread.*/
			futex_ref(newf);
			wp->wp_new_futex = newf;
			TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist);
			DPRINTF(("futex_wake: rerequeue newf %p ref %d\n",
				     newf, newf->f_refcount));
			if (count - n >= n2)
				break;
		}
	}

out:
	return count;
}

int
futex_atomic_op(struct proc *p, int encoded_op, void *uaddr)
{
	const int op = (encoded_op >> 28) & 7;
	const int cmp = (encoded_op >> 24) & 15;
	const int cmparg = (encoded_op << 20) >> 20;
	int oparg = (encoded_op << 8) >> 20;
	int error, oldval, cval;

	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
		oparg = 1 << oparg;

	if (copyin(uaddr, &cval, sizeof(int)) != 0)
		return -EFAULT;

	for (;;) {
		int nval;

		switch (op) {
		case FUTEX_OP_SET:
			nval = oparg;
			break;
		case FUTEX_OP_ADD:
			nval = cval + oparg;
			break;
		case FUTEX_OP_OR:
			nval = cval | oparg;
			break;
		case FUTEX_OP_ANDN:
			nval = cval & ~oparg;
			break;
		case FUTEX_OP_XOR:
			nval = cval ^ oparg;
			break;
		default:
			return -ENOSYS;
		}

		oldval = nval;
		error = futex_atomic_ucas_int32(uaddr, cval, nval);
		if (oldval == cval || error) {
			break;
		}
		cval = oldval;
	}

	if (error)
		return -EFAULT;

	switch (cmp) {
	case FUTEX_OP_CMP_EQ:
		return (oldval == cmparg);
	case FUTEX_OP_CMP_NE:
		return (oldval != cmparg);
	case FUTEX_OP_CMP_LT:
		return (oldval < cmparg);
	case FUTEX_OP_CMP_GE:
		return (oldval >= cmparg);
	case FUTEX_OP_CMP_LE:
		return (oldval <= cmparg);
	case FUTEX_OP_CMP_GT:
		return (oldval > cmparg);
	default:
		return -ENOSYS;
	}
}

int
linux_sys_set_robust_list(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_set_robust_list_args /* {
		syscallarg(struct linux_robust_list_head *) head;
		syscallarg(size_t) len;
	} */ *uap = v;
	struct linux_emuldata *led;

	if (SCARG(uap, len) != sizeof(struct linux_robust_list_head))
		return EINVAL;
	led = p->p_emuldata;
	led->led_robust_head = SCARG(uap, head);
	*retval = 0;
	return 0;
}

int
linux_sys_get_robust_list(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_get_robust_list_args /* {
		syscallarg(int) pid;
		syscallarg(struct linux_robust_list_head **) head;
		syscallarg(size_t *) len;
	} */ *uap = v;
	struct proc *q;
	struct linux_emuldata *led;
	struct linux_robust_list_head *head;
	size_t len;
	int error = 0;

	if (!SCARG(uap, pid)) {
		led = p->p_emuldata;
		head = led->led_robust_head;
	} else {
		if (!SCARG(uap, pid))
			q = p;
		else if ((q = pfind(SCARG(uap, pid))) == NULL)
			return ESRCH;
		else if (p->p_p != q->p_p)
			return EPERM;

		led = q->p_emuldata;
		head = led->led_robust_head;
	}

	len = sizeof(*head);
	error = copyout(&len, SCARG(uap, len), sizeof(len));
	if (error)
		return error;
	return copyout(&head, SCARG(uap, head), sizeof(head));
}

int
futex_itimespecfix(struct timespec *ts)
{
	if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
		return EINVAL;
	if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000)
		ts->tv_nsec = tick * 1000;
	return 0;
}


code:
/* $OpenBSD: linux_futex.h,v 1.1 2011/09/18 02:23:18 pirofti Exp $ */
/*	$NetBSD: linux_futex.h,v 1.4 2010/07/07 01:30:35 chs Exp $ */

/*-
 * Copyright (c) 2005 Emmanuel Dreyfus, 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 by Emmanuel Dreyfus
 * 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 THE AUTHOR 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 AUTHOR 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_FUTEX_H_
#define _LINUX_FUTEX_H_

#define LINUX_FUTEX_WAIT	0
#define LINUX_FUTEX_WAKE	1
#define LINUX_FUTEX_FD		2
#define LINUX_FUTEX_REQUEUE	3
#define LINUX_FUTEX_CMP_REQUEUE	4
#define LINUX_FUTEX_WAKE_OP	5

#define LINUX_FUTEX_PRIVATE_FLAG	128

#define FUTEX_OP_SET		0
#define FUTEX_OP_ADD		1
#define FUTEX_OP_OR		2
#define FUTEX_OP_ANDN		3
#define FUTEX_OP_XOR		4

#define FUTEX_OP_OPARG_SHIFT	8

#define FUTEX_OP_CMP_EQ		0
#define FUTEX_OP_CMP_NE		1
#define FUTEX_OP_CMP_LT		2
#define FUTEX_OP_CMP_LE		3
#define FUTEX_OP_CMP_GT		4
#define FUTEX_OP_CMP_GE		5

struct linux_robust_list {
	struct linux_robust_list	*next;
};

struct linux_robust_list_head {
	struct linux_robust_list	list;
	unsigned long			futex_offset;
	struct linux_robust_list	*pending_list;
};

#define FUTEX_WAITERS		0x80000000
#define FUTEX_OWNER_DIED	0x40000000
#define FUTEX_TID_MASK		0x3fffffff

#endif /* !_LINUX_FUTEX_H_ */


code:
/*	$OpenBSD: linux_hdio.c,v 1.9 2014/03/26 05:23:42 guenther Exp $	*/
/*	$NetBSD: linux_hdio.c,v 1.1 2000/12/10 14:12:17 fvdl Exp $	*/

/*
 * Copyright (c) 2000 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Frank van der Linden for Wasabi Systems, Inc.
 *
 * 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
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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/systm.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>

#include <dev/ata/atareg.h>
#include <dev/ic/wdcreg.h>
#include <sys/ataio.h>

#include <sys/syscallargs.h>

#include <compat/linux/linux_types.h>
#include <compat/linux/linux_ioctl.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_hdio.h>

#include <compat/linux/linux_syscallargs.h>

int
linux_ioctl_hdio(struct proc *p, struct linux_sys_ioctl_args *uap,
		 register_t *retval)
{
	u_long com;
	int error, error1;
	caddr_t sg;
	struct filedesc *fdp;
	struct file *fp;
	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
	struct ataparams *atap, ata;
	struct atareq req;
	struct disklabel label, *labp;
	struct partinfo partp;
	struct linux_hd_geometry hdg;
	struct linux_hd_big_geometry hdg_big;

	fdp = p->p_fd;
	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
		return (EBADF);

	FREF(fp);
	com = SCARG(uap, com);
	ioctlf = fp->f_ops->fo_ioctl;
	retval[0] = error = 0;

	com = SCARG(uap, com);

	switch (com) {
	case LINUX_HDIO_OBSOLETE_IDENTITY:
	case LINUX_HDIO_GET_IDENTITY:
		sg = stackgap_init(p);
		atap = stackgap_alloc(&sg, DEV_BSIZE);
		if (atap == NULL) {
			error = ENOMEM;
			break;
		}

		req.flags = ATACMD_READ;
		req.command = WDCC_IDENTIFY;
		req.databuf = (caddr_t)atap;
		req.datalen = DEV_BSIZE;
		req.timeout = 1000;
		error = ioctlf(fp, ATAIOCCOMMAND, (caddr_t)&req, p);
		if (error != 0)
			break;
		if (req.retsts != ATACMD_OK) {
			error = EIO;
			break;
		}
		error = copyin(atap, &ata, sizeof ata);
		if (error != 0)
			break;
		/*
		 * 142 is the size of the old structure used by Linux,
		 * which doesn't seem to be defined anywhere anymore.
		 */
		error = copyout(&ata, SCARG(uap, data),
		    com == LINUX_HDIO_GET_IDENTITY ? sizeof ata : 142);
		break;
	case LINUX_HDIO_GETGEO:
		error = linux_machdepioctl(p, uap, retval);
		if (error == 0)
			break;
		error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p);
		error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p);
		if (error != 0 && error1 != 0) {
			error = error1;
			break;
		}
		labp = error != 0 ? &label : partp.disklab;
		hdg.start = error1 != 0 ? DL_GETPOFFSET(partp.part) & 0x7fffffff: 0;
		hdg.heads = labp->d_ntracks;
		hdg.cylinders = labp->d_ncylinders;
		hdg.sectors = labp->d_nsectors;
		error = copyout(&hdg, SCARG(uap, data), sizeof hdg);
		break;
	case LINUX_HDIO_GETGEO_BIG:
		error = linux_machdepioctl(p, uap, retval);
		if (error == 0)
			break;
	case LINUX_HDIO_GETGEO_BIG_RAW:
		error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p);
		error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p);
		if (error != 0 && error1 != 0) {
			error = error1;
			break;
		}
		labp = error != 0 ? &label : partp.disklab;
		hdg_big.start = error1 != 0 ? DL_GETPOFFSET(partp.part) & 0x7fffffff : 0;
		hdg_big.heads = labp->d_ntracks;
		hdg_big.cylinders = labp->d_ncylinders;
		hdg_big.sectors = labp->d_nsectors;
		error = copyout(&hdg_big, SCARG(uap, data), sizeof hdg_big);
		break;
	case LINUX_HDIO_GET_UNMASKINTR:
	case LINUX_HDIO_GET_MULTCOUNT:
	case LINUX_HDIO_GET_KEEPSETTINGS:
	case LINUX_HDIO_GET_32BIT:
	case LINUX_HDIO_GET_NOWERR:
	case LINUX_HDIO_GET_DMA:
	case LINUX_HDIO_GET_NICE:
	case LINUX_HDIO_DRIVE_RESET:
	case LINUX_HDIO_TRISTATE_HWIF:
	case LINUX_HDIO_DRIVE_TASK:
	case LINUX_HDIO_DRIVE_CMD:
	case LINUX_HDIO_SET_MULTCOUNT:
	case LINUX_HDIO_SET_UNMASKINTR:
	case LINUX_HDIO_SET_KEEPSETTINGS:
	case LINUX_HDIO_SET_32BIT:
	case LINUX_HDIO_SET_NOWERR:
	case LINUX_HDIO_SET_DMA:
	case LINUX_HDIO_SET_PIO_MODE:
	case LINUX_HDIO_SCAN_HWIF:
	case LINUX_HDIO_SET_NICE:
	case LINUX_HDIO_UNREGISTER_HWIF:
		error = EINVAL;
	}

	FRELE(fp, p);
	return error;
}


code:
/*	$OpenBSD: linux_hdio.h,v 1.2 2011/04/05 22:54:30 pirofti Exp $	*/
/*	$NetBSD: linux_hdio.h,v 1.1 2000/12/10 14:12:17 fvdl Exp $	*/

/*
 * Copyright (c) 2000 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Frank van der Linden for Wasabi Systems, Inc.
 *
 * 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
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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_HDIO_H_
#define _LINUX_HDIO_H_

/*
 * Linux 'hd' (mostly really IDE disk) ioctl calls.
 */

#define LINUX_HDIO_GETGEO		0x0301
#define LINUX_HDIO_GETGEO_BIG		0x0330
#define LINUX_HDIO_GETGEO_BIG_RAW	0x0331
#define LINUX_HDIO_GET_UNMASKINTR	0x0302
#define LINUX_HDIO_GET_MULTCOUNT	0x0304
#define LINUX_HDIO_OBSOLETE_IDENTITY	0x0307
#define LINUX_HDIO_GET_KEEPSETTINGS	0x0308
#define LINUX_HDIO_GET_32BIT		0x0309
#define LINUX_HDIO_GET_NOWERR		0x030a
#define LINUX_HDIO_GET_DMA		0x030b
#define LINUX_HDIO_GET_NICE		0x030c
#define LINUX_HDIO_GET_IDENTITY		0x030d

#define LINUX_HDIO_DRIVE_RESET		0x031c
#define LINUX_HDIO_TRISTATE_HWIF	0x031d
#define LINUX_HDIO_DRIVE_TASK		0x031e
#define LINUX_HDIO_DRIVE_CMD		0x031f

#define LINUX_HDIO_SET_MULTCOUNT	0x0321
#define LINUX_HDIO_SET_UNMASKINTR	0x0322
#define LINUX_HDIO_SET_KEEPSETTINGS	0x0323
#define LINUX_HDIO_SET_32BIT		0x0324
#define LINUX_HDIO_SET_NOWERR		0x0325
#define LINUX_HDIO_SET_DMA		0x0326
#define LINUX_HDIO_SET_PIO_MODE		0x0327
#define LINUX_HDIO_SCAN_HWIF		0x0328
#define LINUX_HDIO_SET_NICE		0x0329
#define LINUX_HDIO_UNREGISTER_HWIF	0x032a

struct linux_hd_geometry {
	u_char heads;
	u_char sectors;
	u_short cylinders;
	u_long start;
};

struct linux_hd_big_geometry {
	u_char heads;
	u_char sectors;
	u_int cylinders;
	u_long start;
};

#endif /* _LINUX_HDIO_H_ */


code:
/*	$OpenBSD: linux_ioctl.c,v 1.12 2015/04/19 08:37:32 ratchov Exp $	*/
/*	$NetBSD: linux_ioctl.c,v 1.14 1996/04/05 00:01:28 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/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/mount.h>

#include <sys/socket.h>
#include <net/if.h>
#include <sys/sockio.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_ioctl.h>

/*
 * Most ioctl command are just converted to their OpenBSD values,
 * and passed on. The ones that take structure pointers and (flag)
 * values need some massaging. This is done the usual way by
 * allocating stackgap memory, letting the actual ioctl call do its
 * work their and converting back the data afterwards.
 */
int
linux_sys_ioctl(p, v, retval)
	register struct proc *p;
	void *v;
	register_t *retval;
{
	register struct linux_sys_ioctl_args /* {
		syscallarg(int) fd;
		syscallarg(u_long) com;
		syscallarg(caddr_t) data;
	} */ *uap = v;

	switch (LINUX_IOCGROUP(SCARG(uap, com))) {
	case 't':
	case 'f':
	case 'T':	/* XXX MIDI sequencer uses 'T' as well */
		return linux_ioctl_termios(p, uap, retval);
	case 'S':
		return linux_ioctl_cdrom(p, uap, retval);
	case 'r':	/* VFAT ioctls; not yet support */
		return (EINVAL);
	case 0x89:
		return linux_ioctl_socket(p, uap, retval);
	case 0x03:
		return linux_ioctl_hdio(p, uap, retval);
	case 0x02:
		return linux_ioctl_fdio(p, uap, retval);
	case 0x12:
		return linux_ioctl_blkio(p, uap, retval);
	default:
		return linux_machdepioctl(p, uap, retval);
	}
}


code:
/*	$OpenBSD: linux_ioctl.h,v 1.8 2015/04/19 08:37:32 ratchov Exp $	*/
/*	$NetBSD: linux_ioctl.h,v 1.4 1996/04/05 00:01:36 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.
 */

#define	_LINUX_IOC_NRBITS	8
#define	_LINUX_IOC_TYPEBITS	8
#define	_LINUX_IOC_SIZEBITS	14
#define	_LINUX_IOC_DIRBITS	2

#define	_LINUX_IOC_NRSHIFT	0

#define	_LINUX_IOC_NONE		0U
#define	_LINUX_IOC_WRITE	1U
#define	_LINUX_IOC_READ		2U

#define	_LINUX_IOC_NRMASK	((1 << _LINUX_IOC_NRBITS) - 1)
#define	_LINUX_IOC_TYPEMASK	((1 << _LINUX_IOC_TYPEBITS) - 1)
#define	_LINUX_IOC_SIZEMASK	((1 << _LINUX_IOC_SIZEBITS) - 1)
#define	_LINUX_IOC_DIRMASK	((1 << _LINUX_IOC_DIRBITS) - 1)

#define	_LINUX_IOC_TYPESHIFT	(_LINUX_IOC_NRSHIFT + _LINUX_IOC_NRBITS)
#define	_LINUX_IOC_SIZESHIFT	(_LINUX_IOC_TYPESHIFT + _LINUX_IOC_TYPEBITS)
#define	_LINUX_IOC_DIRSHIFT	(_LINUX_IOC_SIZESHIFT + _LINUX_IOC_SIZEBITS)

#define	_LINUX_IOC(dir,type,nr,size)		\
	(((nr)   << _LINUX_IOC_NRSHIFT) |	\
	 ((type) << _LINUX_IOC_TYPESHIFT) |	\
	 ((size) << _LINUX_IOC_SIZESHIFT) |	\
	 ((dir)  << _LINUX_IOC_DIRSHIFT))

#define _LINUX_IO(type,nr)		\
	_LINUX_IOC(_LINUX_IOC_NONE,(type),(nr),0)
#define _LINUX_IOR(type,nr,size)	\
	_LINUX_IOC(_LINUX_IOC_READ,(type),(nr),sizeof(size))
#define _LINUX_IOW(type,nr,size)	\
	_LINUX_IOC(_LINUX_IOC_WRITE,(type),(nr),sizeof(size))
#define _LINUX_IOWR(type,nr,size)	\
	_LINUX_IOC(_LINUX_IOC_READ|_LINUX_IOC_WRITE,(type),(nr),sizeof(size))

#define	_LINUX_IOC_DIR(nr)	\
	(((nr) >> _LINUX_IOC_DIRSHIFT) & _LINUX_IOC_DIRMASK)
#define	_LINUX_IOC_TYPE(nr)	\
	(((nr) >> _LINUX_IOC_TYPESHIFT) & _LINUX_IOC_TYPEMASK)
#define	_LINUX_IOC_NR(nr)	\
	(((nr) >> _LINUX_IOC_NRSHIFT) & _LINUX_IOC_NRMASK)
#define	_LINUX_IOC_SIZE(nr)	\
	(((nr) >> _LINUX_IOC_SIZESHIFT) & _LINUX_IOC_SIZEMASK)

#define	LINUX_IOCGROUP(x)	_LINUX_IOC_TYPE(x)

struct linux_sys_ioctl_args;
int linux_machdepioctl(struct proc *, void *, register_t *);
int linux_ioctl_termios(struct proc *, void *, register_t *);
int linux_ioctl_cdrom(struct proc *, void *, register_t *);
int linux_ioctl_socket(struct proc *, void *, register_t *);
int linux_ioctl_hdio(struct proc *, struct linux_sys_ioctl_args *,
    register_t *);
int linux_ioctl_fdio(struct proc *, struct linux_sys_ioctl_args *,
    register_t *);
int linux_ioctl_blkio(struct proc *, struct linux_sys_ioctl_args *,
    register_t *);