summaryrefslogtreecommitdiffstats
path: root/sys/compat/linux
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linux')
-rw-r--r--sys/compat/linux/linux_emul.c372
-rw-r--r--sys/compat/linux/linux_emul.h92
-rw-r--r--sys/compat/linux/linux_file.c1531
-rw-r--r--sys/compat/linux/linux_file.h36
-rw-r--r--sys/compat/linux/linux_fork.c287
-rw-r--r--sys/compat/linux/linux_futex.c887
-rw-r--r--sys/compat/linux/linux_futex.h81
-rw-r--r--sys/compat/linux/linux_getcwd.c469
-rw-r--r--sys/compat/linux/linux_ioctl.c3220
-rw-r--r--sys/compat/linux/linux_ioctl.h654
-rw-r--r--sys/compat/linux/linux_ipc.c891
-rw-r--r--sys/compat/linux/linux_ipc.h142
-rw-r--r--sys/compat/linux/linux_mib.c611
-rw-r--r--sys/compat/linux/linux_mib.h50
-rw-r--r--sys/compat/linux/linux_misc.c1926
-rw-r--r--sys/compat/linux/linux_misc.h77
-rw-r--r--sys/compat/linux/linux_signal.c656
-rw-r--r--sys/compat/linux/linux_signal.h46
-rw-r--r--sys/compat/linux/linux_socket.c1684
-rw-r--r--sys/compat/linux/linux_socket.h119
-rw-r--r--sys/compat/linux/linux_stats.c624
-rw-r--r--sys/compat/linux/linux_sysctl.c143
-rw-r--r--sys/compat/linux/linux_sysproto.h36
-rw-r--r--sys/compat/linux/linux_time.c239
-rw-r--r--sys/compat/linux/linux_uid16.c306
-rw-r--r--sys/compat/linux/linux_util.c243
-rw-r--r--sys/compat/linux/linux_util.h133
-rw-r--r--sys/compat/linux/linux_videodev.h367
-rw-r--r--sys/compat/linux/linux_videodev_compat.h59
29 files changed, 15981 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
new file mode 100644
index 0000000..7585c0d
--- /dev/null
+++ b/sys/compat/linux/linux_emul.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 2006 Roman Divacky
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/unistd.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_futex.h>
+
+struct sx emul_shared_lock;
+struct mtx emul_lock;
+
+/* this returns locked reference to the emuldata entry (if found) */
+struct linux_emuldata *
+em_find(struct proc *p, int locked)
+{
+ struct linux_emuldata *em;
+
+ if (locked == EMUL_DOLOCK)
+ EMUL_LOCK(&emul_lock);
+
+ em = p->p_emuldata;
+
+ if (em == NULL && locked == EMUL_DOLOCK)
+ EMUL_UNLOCK(&emul_lock);
+
+ return (em);
+}
+
+int
+linux_proc_init(struct thread *td, pid_t child, int flags)
+{
+ struct linux_emuldata *em, *p_em;
+ struct proc *p;
+
+ if (child != 0) {
+ /* non-exec call */
+ em = malloc(sizeof *em, M_LINUX, M_WAITOK | M_ZERO);
+ em->pid = child;
+ em->pdeath_signal = 0;
+ em->flags = 0;
+ em->robust_futexes = NULL;
+ if (flags & LINUX_CLONE_THREAD) {
+ /* handled later in the code */
+ } else {
+ struct linux_emuldata_shared *s;
+
+ s = malloc(sizeof *s, M_LINUX, M_WAITOK | M_ZERO);
+ s->refs = 1;
+ s->group_pid = child;
+
+ LIST_INIT(&s->threads);
+ em->shared = s;
+ }
+ } else {
+ /* lookup the old one */
+ em = em_find(td->td_proc, EMUL_DOLOCK);
+ KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
+ }
+
+ em->child_clear_tid = NULL;
+ em->child_set_tid = NULL;
+
+ /*
+ * allocate the shared struct only in clone()/fork cases in the case
+ * of clone() td = calling proc and child = pid of the newly created
+ * proc
+ */
+ if (child != 0) {
+ if (flags & LINUX_CLONE_THREAD) {
+ /* lookup the parent */
+ /*
+ * we dont have to lock the p_em because
+ * its waiting for us in linux_clone so
+ * there is no chance of it changing the
+ * p_em->shared address
+ */
+ p_em = em_find(td->td_proc, EMUL_DONTLOCK);
+ KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_THREAD\n"));
+ em->shared = p_em->shared;
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ em->shared->refs++;
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+ } else {
+ /*
+ * handled earlier to avoid malloc(M_WAITOK) with
+ * rwlock held
+ */
+ }
+ }
+ if (child != 0) {
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ LIST_INSERT_HEAD(&em->shared->threads, em, threads);
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ p = pfind(child);
+ KASSERT(p != NULL, ("process not found in proc_init\n"));
+ p->p_emuldata = em;
+ PROC_UNLOCK(p);
+ } else
+ EMUL_UNLOCK(&emul_lock);
+
+ return (0);
+}
+
+void
+linux_proc_exit(void *arg __unused, struct proc *p)
+{
+ struct linux_emuldata *em;
+ int error, shared_flags, shared_xstat;
+ struct thread *td = FIRST_THREAD_IN_PROC(p);
+ int *child_clear_tid;
+ struct proc *q, *nq;
+
+ if (__predict_true(p->p_sysent != &elf_linux_sysvec))
+ return;
+
+ release_futexes(p);
+
+ /* find the emuldata */
+ em = em_find(p, EMUL_DOLOCK);
+
+ KASSERT(em != NULL, ("proc_exit: emuldata not found.\n"));
+
+ /* reparent all procs that are not a thread leader to initproc */
+ if (em->shared->group_pid != p->p_pid) {
+ child_clear_tid = em->child_clear_tid;
+ EMUL_UNLOCK(&emul_lock);
+ sx_xlock(&proctree_lock);
+ wakeup(initproc);
+ PROC_LOCK(p);
+ proc_reparent(p, initproc);
+ p->p_sigparent = SIGCHLD;
+ PROC_UNLOCK(p);
+ sx_xunlock(&proctree_lock);
+ } else {
+ child_clear_tid = em->child_clear_tid;
+ EMUL_UNLOCK(&emul_lock);
+ }
+
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ shared_flags = em->shared->flags;
+ shared_xstat = em->shared->xstat;
+ LIST_REMOVE(em, threads);
+
+ em->shared->refs--;
+ if (em->shared->refs == 0) {
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+ free(em->shared, M_LINUX);
+ } else
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ if ((shared_flags & EMUL_SHARED_HASXSTAT) != 0)
+ p->p_xstat = shared_xstat;
+
+ if (child_clear_tid != NULL) {
+ struct linux_sys_futex_args cup;
+ int null = 0;
+
+ error = copyout(&null, child_clear_tid, sizeof(null));
+ if (error) {
+ free(em, M_LINUX);
+ return;
+ }
+
+ /* futexes stuff */
+ cup.uaddr = child_clear_tid;
+ cup.op = LINUX_FUTEX_WAKE;
+ cup.val = 0x7fffffff; /* Awake everyone */
+ cup.timeout = NULL;
+ cup.uaddr2 = NULL;
+ cup.val3 = 0;
+ error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup);
+ /*
+ * this cannot happen at the moment and if this happens it
+ * probably means there is a user space bug
+ */
+ if (error)
+ printf(LMSG("futex stuff in proc_exit failed.\n"));
+ }
+
+ /* clean the stuff up */
+ free(em, M_LINUX);
+
+ /* this is a little weird but rewritten from exit1() */
+ sx_xlock(&proctree_lock);
+ q = LIST_FIRST(&p->p_children);
+ for (; q != NULL; q = nq) {
+ nq = LIST_NEXT(q, p_sibling);
+ if (q->p_flag & P_WEXIT)
+ continue;
+ if (__predict_false(q->p_sysent != &elf_linux_sysvec))
+ continue;
+ em = em_find(q, EMUL_DOLOCK);
+ KASSERT(em != NULL, ("linux_reparent: emuldata not found: %i\n", q->p_pid));
+ PROC_LOCK(q);
+ if ((q->p_flag & P_WEXIT) == 0 && em->pdeath_signal != 0) {
+ psignal(q, em->pdeath_signal);
+ }
+ PROC_UNLOCK(q);
+ EMUL_UNLOCK(&emul_lock);
+ }
+ sx_xunlock(&proctree_lock);
+}
+
+/*
+ * This is used in a case of transition from FreeBSD binary execing to linux binary
+ * in this case we create linux emuldata proc entry with the pid of the currently running
+ * process.
+ */
+void
+linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
+{
+ if (__predict_false(imgp->sysent == &elf_linux_sysvec
+ && p->p_sysent != &elf_linux_sysvec))
+ linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0);
+ if (__predict_false((p->p_sysent->sv_flags & SV_ABI_MASK) ==
+ SV_ABI_LINUX))
+ /* Kill threads regardless of imgp->sysent value */
+ linux_kill_threads(FIRST_THREAD_IN_PROC(p), SIGKILL);
+ if (__predict_false(imgp->sysent != &elf_linux_sysvec
+ && p->p_sysent == &elf_linux_sysvec)) {
+ struct linux_emuldata *em;
+
+ /*
+ * XXX:There's a race because here we assign p->p_emuldata NULL
+ * but the process is still counted as linux one for a short
+ * time so some other process might reference it and try to
+ * access its p->p_emuldata and panicing on a NULL reference.
+ */
+ em = em_find(p, EMUL_DONTLOCK);
+
+ KASSERT(em != NULL, ("proc_exec: emuldata not found.\n"));
+
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ LIST_REMOVE(em, threads);
+
+ PROC_LOCK(p);
+ p->p_emuldata = NULL;
+ PROC_UNLOCK(p);
+
+ em->shared->refs--;
+ if (em->shared->refs == 0) {
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+ free(em->shared, M_LINUX);
+ } else
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ free(em, M_LINUX);
+ }
+}
+
+void
+linux_schedtail(struct thread *td)
+{
+ struct linux_emuldata *em;
+ struct proc *p;
+ int error = 0;
+ int *child_set_tid;
+
+ p = td->td_proc;
+
+ /* find the emuldata */
+ em = em_find(p, EMUL_DOLOCK);
+
+ KASSERT(em != NULL, ("linux_schedtail: emuldata not found.\n"));
+ child_set_tid = em->child_set_tid;
+ EMUL_UNLOCK(&emul_lock);
+
+ if (child_set_tid != NULL)
+ error = copyout(&p->p_pid, (int *)child_set_tid,
+ sizeof(p->p_pid));
+
+ return;
+}
+
+int
+linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args)
+{
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(set_tid_address))
+ printf(ARGS(set_tid_address, "%p"), args->tidptr);
+#endif
+
+ /* find the emuldata */
+ em = em_find(td->td_proc, EMUL_DOLOCK);
+
+ KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n"));
+
+ em->child_clear_tid = args->tidptr;
+ td->td_retval[0] = td->td_proc->p_pid;
+
+ EMUL_UNLOCK(&emul_lock);
+ return 0;
+}
+
+void
+linux_kill_threads(struct thread *td, int sig)
+{
+ struct linux_emuldata *em, *td_em, *tmp_em;
+ struct proc *sp;
+
+ td_em = em_find(td->td_proc, EMUL_DONTLOCK);
+
+ KASSERT(td_em != NULL, ("linux_kill_threads: emuldata not found.\n"));
+
+ EMUL_SHARED_RLOCK(&emul_shared_lock);
+ LIST_FOREACH_SAFE(em, &td_em->shared->threads, threads, tmp_em) {
+ if (em->pid == td_em->pid)
+ continue;
+
+ sp = pfind(em->pid);
+ if ((sp->p_flag & P_WEXIT) == 0)
+ psignal(sp, sig);
+ PROC_UNLOCK(sp);
+#ifdef DEBUG
+ printf(LMSG("linux_kill_threads: kill PID %d\n"), em->pid);
+#endif
+ }
+ EMUL_SHARED_RUNLOCK(&emul_shared_lock);
+}
diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
new file mode 100644
index 0000000..3acde64
--- /dev/null
+++ b/sys/compat/linux/linux_emul.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2006 Roman Divacky
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_EMUL_H_
+#define _LINUX_EMUL_H_
+
+#define EMUL_SHARED_HASXSTAT 0x01
+
+struct linux_emuldata_shared {
+ int refs;
+ int flags;
+ int xstat;
+ pid_t group_pid;
+
+ LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */
+};
+
+/*
+ * modeled after similar structure in NetBSD
+ * this will be extended as we need more functionality
+ */
+struct linux_emuldata {
+ pid_t pid;
+
+ int *child_set_tid; /* in clone(): Child's TID to set on clone */
+ int *child_clear_tid;/* in clone(): Child's TID to clear on exit */
+
+ struct linux_emuldata_shared *shared;
+
+ int pdeath_signal; /* parent death signal */
+ int flags; /* different emuldata flags */
+
+ struct linux_robust_list_head *robust_futexes;
+
+ LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */
+};
+
+struct linux_emuldata *em_find(struct proc *, int locked);
+
+#define EMUL_LOCK(l) mtx_lock(l)
+#define EMUL_UNLOCK(l) mtx_unlock(l)
+
+#define EMUL_SHARED_RLOCK(l) sx_slock(l)
+#define EMUL_SHARED_RUNLOCK(l) sx_sunlock(l)
+#define EMUL_SHARED_WLOCK(l) sx_xlock(l)
+#define EMUL_SHARED_WUNLOCK(l) sx_xunlock(l)
+
+/* for em_find use */
+#define EMUL_DOLOCK 1
+#define EMUL_DONTLOCK 0
+
+/* emuldata flags */
+#define LINUX_XDEPR_REQUEUEOP 0x00000001 /* uses deprecated
+ futex REQUEUE op*/
+
+int linux_proc_init(struct thread *, pid_t, int);
+void linux_proc_exit(void *, struct proc *);
+void linux_schedtail(struct thread *);
+void linux_proc_exec(void *, struct proc *, struct image_params *);
+void linux_kill_threads(struct thread *, int);
+
+extern struct sx emul_shared_lock;
+extern struct mtx emul_lock;
+
+#endif /* !_LINUX_EMUL_H_ */
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
new file mode 100644
index 0000000..44ad193
--- /dev/null
+++ b/sys/compat/linux/linux_file.c
@@ -0,0 +1,1531 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/sx.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/tty.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+
+#include <security/mac/mac_framework.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_file.h>
+
+int
+linux_creat(struct thread *td, struct linux_creat_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(creat))
+ printf(ARGS(creat, "%s, %d"), path, args->mode);
+#endif
+ error = kern_open(td, path, UIO_SYSSPACE, O_WRONLY | O_CREAT | O_TRUNC,
+ args->mode);
+ LFREEPATH(path);
+ return (error);
+}
+
+
+static int
+linux_common_open(struct thread *td, int dirfd, char *path, int l_flags, int mode)
+{
+ struct proc *p = td->td_proc;
+ struct file *fp;
+ int fd;
+ int bsd_flags, error;
+
+ bsd_flags = 0;
+ switch (l_flags & LINUX_O_ACCMODE) {
+ case LINUX_O_WRONLY:
+ bsd_flags |= O_WRONLY;
+ break;
+ case LINUX_O_RDWR:
+ bsd_flags |= O_RDWR;
+ break;
+ default:
+ bsd_flags |= O_RDONLY;
+ }
+ if (l_flags & LINUX_O_NDELAY)
+ bsd_flags |= O_NONBLOCK;
+ if (l_flags & LINUX_O_APPEND)
+ bsd_flags |= O_APPEND;
+ if (l_flags & LINUX_O_SYNC)
+ bsd_flags |= O_FSYNC;
+ if (l_flags & LINUX_O_NONBLOCK)
+ bsd_flags |= O_NONBLOCK;
+ if (l_flags & LINUX_FASYNC)
+ bsd_flags |= O_ASYNC;
+ if (l_flags & LINUX_O_CREAT)
+ bsd_flags |= O_CREAT;
+ if (l_flags & LINUX_O_TRUNC)
+ bsd_flags |= O_TRUNC;
+ if (l_flags & LINUX_O_EXCL)
+ bsd_flags |= O_EXCL;
+ if (l_flags & LINUX_O_NOCTTY)
+ bsd_flags |= O_NOCTTY;
+ if (l_flags & LINUX_O_DIRECT)
+ bsd_flags |= O_DIRECT;
+ if (l_flags & LINUX_O_NOFOLLOW)
+ bsd_flags |= O_NOFOLLOW;
+ if (l_flags & LINUX_O_DIRECTORY)
+ bsd_flags |= O_DIRECTORY;
+ /* XXX LINUX_O_NOATIME: unable to be easily implemented. */
+
+ error = kern_openat(td, dirfd, path, UIO_SYSSPACE, bsd_flags, mode);
+
+ if (!error) {
+ fd = td->td_retval[0];
+ /*
+ * XXX In between kern_open() and fget(), another process
+ * having the same filedesc could use that fd without
+ * checking below.
+ */
+ error = fget(td, fd, &fp);
+ if (!error) {
+ sx_slock(&proctree_lock);
+ PROC_LOCK(p);
+ if (!(bsd_flags & O_NOCTTY) &&
+ SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
+ PROC_UNLOCK(p);
+ sx_unlock(&proctree_lock);
+ if (fp->f_type == DTYPE_VNODE)
+ (void) fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0,
+ td->td_ucred, td);
+ } else {
+ PROC_UNLOCK(p);
+ sx_sunlock(&proctree_lock);
+ }
+ fdrop(fp, td);
+ /*
+ * XXX as above, fdrop()/kern_close() pair is racy.
+ */
+ if (error)
+ kern_close(td, fd);
+ }
+ }
+
+#ifdef DEBUG
+ if (ldebug(open))
+ printf(LMSG("open returns error %d"), error);
+#endif
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_openat(struct thread *td, struct linux_openat_args *args)
+{
+ char *path;
+ int dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ if (args->flags & LINUX_O_CREAT)
+ LCONVPATH_AT(td, args->filename, &path, 1, dfd);
+ else
+ LCONVPATH_AT(td, args->filename, &path, 0, dfd);
+#ifdef DEBUG
+ if (ldebug(openat))
+ printf(ARGS(openat, "%i, %s, 0x%x, 0x%x"), args->dfd,
+ path, args->flags, args->mode);
+#endif
+ return (linux_common_open(td, dfd, path, args->flags, args->mode));
+}
+
+int
+linux_open(struct thread *td, struct linux_open_args *args)
+{
+ char *path;
+
+ if (args->flags & LINUX_O_CREAT)
+ LCONVPATHCREAT(td, args->path, &path);
+ else
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(open))
+ printf(ARGS(open, "%s, 0x%x, 0x%x"),
+ path, args->flags, args->mode);
+#endif
+
+ return (linux_common_open(td, AT_FDCWD, path, args->flags, args->mode));
+}
+
+int
+linux_lseek(struct thread *td, struct linux_lseek_args *args)
+{
+
+ struct lseek_args /* {
+ int fd;
+ int pad;
+ off_t offset;
+ int whence;
+ } */ tmp_args;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(lseek))
+ printf(ARGS(lseek, "%d, %ld, %d"),
+ args->fdes, (long)args->off, args->whence);
+#endif
+ tmp_args.fd = args->fdes;
+ tmp_args.offset = (off_t)args->off;
+ tmp_args.whence = args->whence;
+ error = lseek(td, &tmp_args);
+ return error;
+}
+
+int
+linux_llseek(struct thread *td, struct linux_llseek_args *args)
+{
+ struct lseek_args bsd_args;
+ int error;
+ off_t off;
+
+#ifdef DEBUG
+ if (ldebug(llseek))
+ printf(ARGS(llseek, "%d, %d:%d, %d"),
+ args->fd, args->ohigh, args->olow, args->whence);
+#endif
+ off = (args->olow) | (((off_t) args->ohigh) << 32);
+
+ bsd_args.fd = args->fd;
+ bsd_args.offset = off;
+ bsd_args.whence = args->whence;
+
+ if ((error = lseek(td, &bsd_args)))
+ return error;
+
+ if ((error = copyout(td->td_retval, args->res, sizeof (off_t))))
+ return error;
+
+ td->td_retval[0] = 0;
+ return 0;
+}
+
+int
+linux_readdir(struct thread *td, struct linux_readdir_args *args)
+{
+ struct linux_getdents_args lda;
+
+ lda.fd = args->fd;
+ lda.dent = args->dent;
+ lda.count = 1;
+ return linux_getdents(td, &lda);
+}
+
+/*
+ * Note that linux_getdents(2) and linux_getdents64(2) have the same
+ * arguments. They only differ in the definition of struct dirent they
+ * operate on. We use this to common the code, with the exception of
+ * accessing struct dirent. Note that linux_readdir(2) is implemented
+ * by means of linux_getdents(2). In this case we never operate on
+ * struct dirent64 and thus don't need to handle it...
+ */
+
+struct l_dirent {
+ l_ulong d_ino;
+ l_off_t d_off;
+ l_ushort d_reclen;
+ char d_name[LINUX_NAME_MAX + 1];
+};
+
+struct l_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ l_ushort d_reclen;
+ u_char d_type;
+ char d_name[LINUX_NAME_MAX + 1];
+};
+
+/*
+ * Linux uses the last byte in the dirent buffer to store d_type,
+ * at least glibc-2.7 requires it. That is why l_dirent is padded with 2 bytes.
+ */
+#define LINUX_RECLEN(namlen) \
+ roundup((offsetof(struct l_dirent, d_name) + (namlen) + 2), \
+ sizeof(l_ulong))
+
+#define LINUX_RECLEN64(namlen) \
+ roundup((offsetof(struct l_dirent64, d_name) + (namlen) + 1), \
+ sizeof(uint64_t))
+
+#define LINUX_MAXRECLEN max(LINUX_RECLEN(LINUX_NAME_MAX), \
+ LINUX_RECLEN64(LINUX_NAME_MAX))
+#define LINUX_DIRBLKSIZ 512
+
+static int
+getdents_common(struct thread *td, struct linux_getdents64_args *args,
+ int is64bit)
+{
+ struct dirent *bdp;
+ struct vnode *vp;
+ caddr_t inp, buf; /* BSD-format */
+ int len, reclen; /* BSD-format */
+ caddr_t outp; /* Linux-format */
+ int resid, linuxreclen=0; /* Linux-format */
+ caddr_t lbuf; /* Linux-format */
+ struct file *fp;
+ struct uio auio;
+ struct iovec aiov;
+ off_t off;
+ struct l_dirent *linux_dirent;
+ struct l_dirent64 *linux_dirent64;
+ int buflen, error, eofflag, nbytes, justone;
+ u_long *cookies = NULL, *cookiep;
+ int ncookies, vfslocked;
+
+ nbytes = args->count;
+ if (nbytes == 1) {
+ /* readdir(2) case. Always struct dirent. */
+ if (is64bit)
+ return (EINVAL);
+ nbytes = sizeof(*linux_dirent);
+ justone = 1;
+ } else
+ justone = 0;
+
+ if ((error = getvnode(td->td_proc->p_fd, args->fd, &fp)) != 0)
+ return (error);
+
+ if ((fp->f_flag & FREAD) == 0) {
+ fdrop(fp, td);
+ return (EBADF);
+ }
+
+ vp = fp->f_vnode;
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ if (vp->v_type != VDIR) {
+ VFS_UNLOCK_GIANT(vfslocked);
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+
+ off = fp->f_offset;
+
+ buflen = max(LINUX_DIRBLKSIZ, nbytes);
+ buflen = min(buflen, MAXBSIZE);
+ buf = malloc(buflen, M_TEMP, M_WAITOK);
+ lbuf = malloc(LINUX_MAXRECLEN, M_TEMP, M_WAITOK | M_ZERO);
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+
+ aiov.iov_base = buf;
+ aiov.iov_len = buflen;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_td = td;
+ auio.uio_resid = buflen;
+ auio.uio_offset = off;
+
+ if (cookies) {
+ free(cookies, M_TEMP);
+ cookies = NULL;
+ }
+
+#ifdef MAC
+ /*
+ * Do directory search MAC check using non-cached credentials.
+ */
+ if ((error = mac_vnode_check_readdir(td->td_ucred, vp)))
+ goto out;
+#endif /* MAC */
+ if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies,
+ &cookies)))
+ goto out;
+
+ inp = buf;
+ outp = (caddr_t)args->dirent;
+ resid = nbytes;
+ if ((len = buflen - auio.uio_resid) <= 0)
+ goto eof;
+
+ cookiep = cookies;
+
+ if (cookies) {
+ /*
+ * When using cookies, the vfs has the option of reading from
+ * a different offset than that supplied (UFS truncates the
+ * offset to a block boundary to make sure that it never reads
+ * partway through a directory entry, even if the directory
+ * has been compacted).
+ */
+ while (len > 0 && ncookies > 0 && *cookiep <= off) {
+ bdp = (struct dirent *) inp;
+ len -= bdp->d_reclen;
+ inp += bdp->d_reclen;
+ cookiep++;
+ ncookies--;
+ }
+ }
+
+ while (len > 0) {
+ if (cookiep && ncookies == 0)
+ break;
+ bdp = (struct dirent *) inp;
+ reclen = bdp->d_reclen;
+ if (reclen & 3) {
+ error = EFAULT;
+ goto out;
+ }
+
+ if (bdp->d_fileno == 0) {
+ inp += reclen;
+ if (cookiep) {
+ off = *cookiep++;
+ ncookies--;
+ } else
+ off += reclen;
+
+ len -= reclen;
+ continue;
+ }
+
+ linuxreclen = (is64bit)
+ ? LINUX_RECLEN64(bdp->d_namlen)
+ : LINUX_RECLEN(bdp->d_namlen);
+
+ if (reclen > len || resid < linuxreclen) {
+ outp++;
+ break;
+ }
+
+ if (justone) {
+ /* readdir(2) case. */
+ linux_dirent = (struct l_dirent*)lbuf;
+ linux_dirent->d_ino = bdp->d_fileno;
+ linux_dirent->d_off = (l_off_t)linuxreclen;
+ linux_dirent->d_reclen = (l_ushort)bdp->d_namlen;
+ strlcpy(linux_dirent->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent, d_name));
+ error = copyout(linux_dirent, outp, linuxreclen);
+ }
+ if (is64bit) {
+ linux_dirent64 = (struct l_dirent64*)lbuf;
+ linux_dirent64->d_ino = bdp->d_fileno;
+ linux_dirent64->d_off = (cookiep)
+ ? (l_off_t)*cookiep
+ : (l_off_t)(off + reclen);
+ linux_dirent64->d_reclen = (l_ushort)linuxreclen;
+ linux_dirent64->d_type = bdp->d_type;
+ strlcpy(linux_dirent64->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent64, d_name));
+ error = copyout(linux_dirent64, outp, linuxreclen);
+ } else if (!justone) {
+ linux_dirent = (struct l_dirent*)lbuf;
+ linux_dirent->d_ino = bdp->d_fileno;
+ linux_dirent->d_off = (cookiep)
+ ? (l_off_t)*cookiep
+ : (l_off_t)(off + reclen);
+ linux_dirent->d_reclen = (l_ushort)linuxreclen;
+ /*
+ * Copy d_type to last byte of l_dirent buffer
+ */
+ lbuf[linuxreclen-1] = bdp->d_type;
+ strlcpy(linux_dirent->d_name, bdp->d_name,
+ linuxreclen - offsetof(struct l_dirent, d_name)-1);
+ error = copyout(linux_dirent, outp, linuxreclen);
+ }
+
+ if (error)
+ goto out;
+
+ inp += reclen;
+ if (cookiep) {
+ off = *cookiep++;
+ ncookies--;
+ } else
+ off += reclen;
+
+ outp += linuxreclen;
+ resid -= linuxreclen;
+ len -= reclen;
+ if (justone)
+ break;
+ }
+
+ if (outp == (caddr_t)args->dirent) {
+ nbytes = resid;
+ goto eof;
+ }
+
+ fp->f_offset = off;
+ if (justone)
+ nbytes = resid + linuxreclen;
+
+eof:
+ td->td_retval[0] = nbytes - resid;
+
+out:
+ if (cookies)
+ free(cookies, M_TEMP);
+
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+ fdrop(fp, td);
+ free(buf, M_TEMP);
+ free(lbuf, M_TEMP);
+ return (error);
+}
+
+int
+linux_getdents(struct thread *td, struct linux_getdents_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(getdents))
+ printf(ARGS(getdents, "%d, *, %d"), args->fd, args->count);
+#endif
+
+ return (getdents_common(td, (struct linux_getdents64_args*)args, 0));
+}
+
+int
+linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(getdents64))
+ printf(ARGS(getdents64, "%d, *, %d"), args->fd, args->count);
+#endif
+
+ return (getdents_common(td, args, 1));
+}
+
+/*
+ * These exist mainly for hooks for doing /compat/linux translation.
+ */
+
+int
+linux_access(struct thread *td, struct linux_access_args *args)
+{
+ char *path;
+ int error;
+
+ /* linux convention */
+ if (args->flags & ~(F_OK | X_OK | W_OK | R_OK))
+ return (EINVAL);
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(access))
+ printf(ARGS(access, "%s, %d"), path, args->flags);
+#endif
+ error = kern_access(td, path, UIO_SYSSPACE, args->flags);
+ LFREEPATH(path);
+
+ return (error);
+}
+
+int
+linux_faccessat(struct thread *td, struct linux_faccessat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ /* linux convention */
+ if (args->mode & ~(F_OK | X_OK | W_OK | R_OK))
+ return (EINVAL);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(access))
+ printf(ARGS(access, "%s, %d"), path, args->mode);
+#endif
+
+ error = kern_accessat(td, dfd, path, UIO_SYSSPACE, 0 /* XXX */,
+ args->mode);
+ LFREEPATH(path);
+
+ return (error);
+}
+
+int
+linux_unlink(struct thread *td, struct linux_unlink_args *args)
+{
+ char *path;
+ int error;
+ struct stat st;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(unlink))
+ printf(ARGS(unlink, "%s"), path);
+#endif
+
+ error = kern_unlink(td, path, UIO_SYSSPACE);
+ if (error == EPERM)
+ /* Introduce POSIX noncompliant behaviour of Linux */
+ if (kern_stat(td, path, UIO_SYSSPACE, &st) == 0)
+ if (S_ISDIR(st.st_mode))
+ error = EISDIR;
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args)
+{
+ char *path;
+ int error, dfd;
+ struct stat st;
+
+ if (args->flag & ~LINUX_AT_REMOVEDIR)
+ return (EINVAL);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(unlinkat))
+ printf(ARGS(unlinkat, "%s"), path);
+#endif
+
+ if (args->flag & LINUX_AT_REMOVEDIR)
+ error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE);
+ else
+ error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0);
+ if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
+ /* Introduce POSIX noncompliant behaviour of Linux */
+ if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
+ UIO_SYSSPACE, &st) == 0 && S_ISDIR(st.st_mode))
+ error = EISDIR;
+ }
+ LFREEPATH(path);
+ return (error);
+}
+int
+linux_chdir(struct thread *td, struct linux_chdir_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(chdir))
+ printf(ARGS(chdir, "%s"), path);
+#endif
+ error = kern_chdir(td, path, UIO_SYSSPACE);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_chmod(struct thread *td, struct linux_chmod_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(chmod))
+ printf(ARGS(chmod, "%s, %d"), path, args->mode);
+#endif
+ error = kern_chmod(td, path, UIO_SYSSPACE, args->mode);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_fchmodat(struct thread *td, struct linux_fchmodat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fchmodat))
+ printf(ARGS(fchmodat, "%s, %d"), path, args->mode);
+#endif
+
+ error = kern_fchmodat(td, dfd, path, UIO_SYSSPACE, args->mode, 0);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHCREAT(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(mkdir))
+ printf(ARGS(mkdir, "%s, %d"), path, args->mode);
+#endif
+ error = kern_mkdir(td, path, UIO_SYSSPACE, args->mode);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_mkdirat(struct thread *td, struct linux_mkdirat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHCREAT_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(mkdirat))
+ printf(ARGS(mkdirat, "%s, %d"), path, args->mode);
+#endif
+ error = kern_mkdirat(td, dfd, path, UIO_SYSSPACE, args->mode);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(rmdir))
+ printf(ARGS(rmdir, "%s"), path);
+#endif
+ error = kern_rmdir(td, path, UIO_SYSSPACE);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_rename(struct thread *td, struct linux_rename_args *args)
+{
+ char *from, *to;
+ int error;
+
+ LCONVPATHEXIST(td, args->from, &from);
+ /* Expand LCONVPATHCREATE so that `from' can be freed on errors */
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
+ if (to == NULL) {
+ LFREEPATH(from);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(rename))
+ printf(ARGS(rename, "%s, %s"), from, to);
+#endif
+ error = kern_rename(td, from, to, UIO_SYSSPACE);
+ LFREEPATH(from);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_renameat(struct thread *td, struct linux_renameat_args *args)
+{
+ char *from, *to;
+ int error, olddfd, newdfd;
+
+ olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
+ newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &from, olddfd);
+ /* Expand LCONVPATHCREATE so that `from' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
+ if (to == NULL) {
+ LFREEPATH(from);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(renameat))
+ printf(ARGS(renameat, "%s, %s"), from, to);
+#endif
+ error = kern_renameat(td, olddfd, from, newdfd, to, UIO_SYSSPACE);
+ LFREEPATH(from);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_symlink(struct thread *td, struct linux_symlink_args *args)
+{
+ char *path, *to;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
+ if (to == NULL) {
+ LFREEPATH(path);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(symlink))
+ printf(ARGS(symlink, "%s, %s"), path, to);
+#endif
+ error = kern_symlink(td, path, to, UIO_SYSSPACE);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_symlinkat(struct thread *td, struct linux_symlinkat_args *args)
+{
+ char *path, *to;
+ int error, dfd;
+
+ dfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &path, dfd);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, dfd);
+ if (to == NULL) {
+ LFREEPATH(path);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(symlinkat))
+ printf(ARGS(symlinkat, "%s, %s"), path, to);
+#endif
+
+ error = kern_symlinkat(td, path, dfd, to, UIO_SYSSPACE);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_readlink(struct thread *td, struct linux_readlink_args *args)
+{
+ char *name;
+ int error;
+
+ LCONVPATHEXIST(td, args->name, &name);
+
+#ifdef DEBUG
+ if (ldebug(readlink))
+ printf(ARGS(readlink, "%s, %p, %d"), name, (void *)args->buf,
+ args->count);
+#endif
+ error = kern_readlink(td, name, UIO_SYSSPACE, args->buf, UIO_USERSPACE,
+ args->count);
+ LFREEPATH(name);
+ return (error);
+}
+
+int
+linux_readlinkat(struct thread *td, struct linux_readlinkat_args *args)
+{
+ char *name;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->path, &name, dfd);
+
+#ifdef DEBUG
+ if (ldebug(readlinkat))
+ printf(ARGS(readlinkat, "%s, %p, %d"), name, (void *)args->buf,
+ args->bufsiz);
+#endif
+
+ error = kern_readlinkat(td, dfd, name, UIO_SYSSPACE, args->buf,
+ UIO_USERSPACE, args->bufsiz);
+ LFREEPATH(name);
+ return (error);
+}
+
+int
+linux_truncate(struct thread *td, struct linux_truncate_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(truncate))
+ printf(ARGS(truncate, "%s, %ld"), path, (long)args->length);
+#endif
+
+ error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_truncate64(struct thread *td, struct linux_truncate64_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(truncate64))
+ printf(ARGS(truncate64, "%s, %jd"), path, args->length);
+#endif
+
+ error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
+ LFREEPATH(path);
+ return (error);
+}
+int
+linux_ftruncate(struct thread *td, struct linux_ftruncate_args *args)
+{
+ struct ftruncate_args /* {
+ int fd;
+ int pad;
+ off_t length;
+ } */ nuap;
+
+ nuap.fd = args->fd;
+ nuap.length = args->length;
+ return (ftruncate(td, &nuap));
+}
+
+int
+linux_link(struct thread *td, struct linux_link_args *args)
+{
+ char *path, *to;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
+ if (to == NULL) {
+ LFREEPATH(path);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(link))
+ printf(ARGS(link, "%s, %s"), path, to);
+#endif
+ error = kern_link(td, path, to, UIO_SYSSPACE);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_linkat(struct thread *td, struct linux_linkat_args *args)
+{
+ char *path, *to;
+ int error, olddfd, newdfd;
+
+ /*
+ * They really introduced flags argument which is forbidden to
+ * use.
+ */
+ if (args->flags != 0)
+ return (EINVAL);
+
+ olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
+ newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &path, olddfd);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
+ if (to == NULL) {
+ LFREEPATH(path);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(linkat))
+ printf(ARGS(linkat, "%i, %s, %i, %s, %i"), args->olddfd, path,
+ args->newdfd, to, args->flags);
+#endif
+
+ error = kern_linkat(td, olddfd, newdfd, path, to, UIO_SYSSPACE, FOLLOW);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
+linux_fdatasync(td, uap)
+ struct thread *td;
+ struct linux_fdatasync_args *uap;
+{
+ struct fsync_args bsd;
+
+ bsd.fd = uap->fd;
+ return fsync(td, &bsd);
+}
+
+int
+linux_pread(td, uap)
+ struct thread *td;
+ struct linux_pread_args *uap;
+{
+ struct pread_args bsd;
+ struct vnode *vp;
+ int error;
+
+ bsd.fd = uap->fd;
+ bsd.buf = uap->buf;
+ bsd.nbyte = uap->nbyte;
+ bsd.offset = uap->offset;
+
+ error = pread(td, &bsd);
+
+ if (error == 0) {
+ /* This seems to violate POSIX but linux does it */
+ if ((error = fgetvp(td, uap->fd, &vp)) != 0)
+ return (error);
+ if (vp->v_type == VDIR) {
+ vrele(vp);
+ return (EISDIR);
+ }
+ vrele(vp);
+ }
+
+ return (error);
+}
+
+int
+linux_pwrite(td, uap)
+ struct thread *td;
+ struct linux_pwrite_args *uap;
+{
+ struct pwrite_args bsd;
+
+ bsd.fd = uap->fd;
+ bsd.buf = uap->buf;
+ bsd.nbyte = uap->nbyte;
+ bsd.offset = uap->offset;
+ return pwrite(td, &bsd);
+}
+
+int
+linux_mount(struct thread *td, struct linux_mount_args *args)
+{
+ struct ufs_args ufs;
+ char fstypename[MFSNAMELEN];
+ char mntonname[MNAMELEN], mntfromname[MNAMELEN];
+ int error;
+ int fsflags;
+ void *fsdata;
+
+ error = copyinstr(args->filesystemtype, fstypename, MFSNAMELEN - 1,
+ NULL);
+ if (error)
+ return (error);
+ error = copyinstr(args->specialfile, mntfromname, MNAMELEN - 1, NULL);
+ if (error)
+ return (error);
+ error = copyinstr(args->dir, mntonname, MNAMELEN - 1, NULL);
+ if (error)
+ return (error);
+
+#ifdef DEBUG
+ if (ldebug(mount))
+ printf(ARGS(mount, "%s, %s, %s"),
+ fstypename, mntfromname, mntonname);
+#endif
+
+ if (strcmp(fstypename, "ext2") == 0) {
+ strcpy(fstypename, "ext2fs");
+ fsdata = &ufs;
+ ufs.fspec = mntfromname;
+#define DEFAULT_ROOTID -2
+ ufs.export.ex_root = DEFAULT_ROOTID;
+ ufs.export.ex_flags =
+ args->rwflag & LINUX_MS_RDONLY ? MNT_EXRDONLY : 0;
+ } else if (strcmp(fstypename, "proc") == 0) {
+ strcpy(fstypename, "linprocfs");
+ fsdata = NULL;
+ } else if (strcmp(fstypename, "vfat") == 0) {
+ strcpy(fstypename, "msdosfs");
+ fsdata = NULL;
+ } else {
+ return (ENODEV);
+ }
+
+ fsflags = 0;
+
+ if ((args->rwflag & 0xffff0000) == 0xc0ed0000) {
+ /*
+ * Linux SYNC flag is not included; the closest equivalent
+ * FreeBSD has is !ASYNC, which is our default.
+ */
+ if (args->rwflag & LINUX_MS_RDONLY)
+ fsflags |= MNT_RDONLY;
+ if (args->rwflag & LINUX_MS_NOSUID)
+ fsflags |= MNT_NOSUID;
+ if (args->rwflag & LINUX_MS_NOEXEC)
+ fsflags |= MNT_NOEXEC;
+ if (args->rwflag & LINUX_MS_REMOUNT)
+ fsflags |= MNT_UPDATE;
+ }
+
+ if (strcmp(fstypename, "linprocfs") == 0) {
+ error = kernel_vmount(fsflags,
+ "fstype", fstypename,
+ "fspath", mntonname,
+ NULL);
+ } else if (strcmp(fstypename, "msdosfs") == 0) {
+ error = kernel_vmount(fsflags,
+ "fstype", fstypename,
+ "fspath", mntonname,
+ "from", mntfromname,
+ NULL);
+ } else
+ error = EOPNOTSUPP;
+ return (error);
+}
+
+int
+linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
+{
+ struct linux_umount_args args2;
+
+ args2.path = args->path;
+ args2.flags = 0;
+ return (linux_umount(td, &args2));
+}
+
+int
+linux_umount(struct thread *td, struct linux_umount_args *args)
+{
+ struct unmount_args bsd;
+
+ bsd.path = args->path;
+ bsd.flags = args->flags; /* XXX correct? */
+ return (unmount(td, &bsd));
+}
+
+/*
+ * fcntl family of syscalls
+ */
+
+struct l_flock {
+ l_short l_type;
+ l_short l_whence;
+ l_off_t l_start;
+ l_off_t l_len;
+ l_pid_t l_pid;
+}
+#if defined(__amd64__) && defined(COMPAT_LINUX32)
+__packed
+#endif
+;
+
+static void
+linux_to_bsd_flock(struct l_flock *linux_flock, struct flock *bsd_flock)
+{
+ switch (linux_flock->l_type) {
+ case LINUX_F_RDLCK:
+ bsd_flock->l_type = F_RDLCK;
+ break;
+ case LINUX_F_WRLCK:
+ bsd_flock->l_type = F_WRLCK;
+ break;
+ case LINUX_F_UNLCK:
+ bsd_flock->l_type = F_UNLCK;
+ break;
+ default:
+ bsd_flock->l_type = -1;
+ break;
+ }
+ bsd_flock->l_whence = linux_flock->l_whence;
+ bsd_flock->l_start = (off_t)linux_flock->l_start;
+ bsd_flock->l_len = (off_t)linux_flock->l_len;
+ bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
+ bsd_flock->l_sysid = 0;
+}
+
+static void
+bsd_to_linux_flock(struct flock *bsd_flock, struct l_flock *linux_flock)
+{
+ switch (bsd_flock->l_type) {
+ case F_RDLCK:
+ linux_flock->l_type = LINUX_F_RDLCK;
+ break;
+ case F_WRLCK:
+ linux_flock->l_type = LINUX_F_WRLCK;
+ break;
+ case F_UNLCK:
+ linux_flock->l_type = LINUX_F_UNLCK;
+ break;
+ }
+ linux_flock->l_whence = bsd_flock->l_whence;
+ linux_flock->l_start = (l_off_t)bsd_flock->l_start;
+ linux_flock->l_len = (l_off_t)bsd_flock->l_len;
+ linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
+}
+
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+struct l_flock64 {
+ l_short l_type;
+ l_short l_whence;
+ l_loff_t l_start;
+ l_loff_t l_len;
+ l_pid_t l_pid;
+}
+#if defined(__amd64__) && defined(COMPAT_LINUX32)
+__packed
+#endif
+;
+
+static void
+linux_to_bsd_flock64(struct l_flock64 *linux_flock, struct flock *bsd_flock)
+{
+ switch (linux_flock->l_type) {
+ case LINUX_F_RDLCK:
+ bsd_flock->l_type = F_RDLCK;
+ break;
+ case LINUX_F_WRLCK:
+ bsd_flock->l_type = F_WRLCK;
+ break;
+ case LINUX_F_UNLCK:
+ bsd_flock->l_type = F_UNLCK;
+ break;
+ default:
+ bsd_flock->l_type = -1;
+ break;
+ }
+ bsd_flock->l_whence = linux_flock->l_whence;
+ bsd_flock->l_start = (off_t)linux_flock->l_start;
+ bsd_flock->l_len = (off_t)linux_flock->l_len;
+ bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
+ bsd_flock->l_sysid = 0;
+}
+
+static void
+bsd_to_linux_flock64(struct flock *bsd_flock, struct l_flock64 *linux_flock)
+{
+ switch (bsd_flock->l_type) {
+ case F_RDLCK:
+ linux_flock->l_type = LINUX_F_RDLCK;
+ break;
+ case F_WRLCK:
+ linux_flock->l_type = LINUX_F_WRLCK;
+ break;
+ case F_UNLCK:
+ linux_flock->l_type = LINUX_F_UNLCK;
+ break;
+ }
+ linux_flock->l_whence = bsd_flock->l_whence;
+ linux_flock->l_start = (l_loff_t)bsd_flock->l_start;
+ linux_flock->l_len = (l_loff_t)bsd_flock->l_len;
+ linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
+}
+#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+
+static int
+fcntl_common(struct thread *td, struct linux_fcntl64_args *args)
+{
+ struct l_flock linux_flock;
+ struct flock bsd_flock;
+ struct file *fp;
+ long arg;
+ int error, result;
+
+ switch (args->cmd) {
+ case LINUX_F_DUPFD:
+ return (kern_fcntl(td, args->fd, F_DUPFD, args->arg));
+
+ case LINUX_F_GETFD:
+ return (kern_fcntl(td, args->fd, F_GETFD, 0));
+
+ case LINUX_F_SETFD:
+ return (kern_fcntl(td, args->fd, F_SETFD, args->arg));
+
+ case LINUX_F_GETFL:
+ error = kern_fcntl(td, args->fd, F_GETFL, 0);
+ result = td->td_retval[0];
+ td->td_retval[0] = 0;
+ if (result & O_RDONLY)
+ td->td_retval[0] |= LINUX_O_RDONLY;
+ if (result & O_WRONLY)
+ td->td_retval[0] |= LINUX_O_WRONLY;
+ if (result & O_RDWR)
+ td->td_retval[0] |= LINUX_O_RDWR;
+ if (result & O_NDELAY)
+ td->td_retval[0] |= LINUX_O_NONBLOCK;
+ if (result & O_APPEND)
+ td->td_retval[0] |= LINUX_O_APPEND;
+ if (result & O_FSYNC)
+ td->td_retval[0] |= LINUX_O_SYNC;
+ if (result & O_ASYNC)
+ td->td_retval[0] |= LINUX_FASYNC;
+#ifdef LINUX_O_NOFOLLOW
+ if (result & O_NOFOLLOW)
+ td->td_retval[0] |= LINUX_O_NOFOLLOW;
+#endif
+#ifdef LINUX_O_DIRECT
+ if (result & O_DIRECT)
+ td->td_retval[0] |= LINUX_O_DIRECT;
+#endif
+ return (error);
+
+ case LINUX_F_SETFL:
+ arg = 0;
+ if (args->arg & LINUX_O_NDELAY)
+ arg |= O_NONBLOCK;
+ if (args->arg & LINUX_O_APPEND)
+ arg |= O_APPEND;
+ if (args->arg & LINUX_O_SYNC)
+ arg |= O_FSYNC;
+ if (args->arg & LINUX_FASYNC)
+ arg |= O_ASYNC;
+#ifdef LINUX_O_NOFOLLOW
+ if (args->arg & LINUX_O_NOFOLLOW)
+ arg |= O_NOFOLLOW;
+#endif
+#ifdef LINUX_O_DIRECT
+ if (args->arg & LINUX_O_DIRECT)
+ arg |= O_DIRECT;
+#endif
+ return (kern_fcntl(td, args->fd, F_SETFL, arg));
+
+ case LINUX_F_GETLK:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock(&linux_flock, &bsd_flock);
+ error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
+ if (error)
+ return (error);
+ bsd_to_linux_flock(&bsd_flock, &linux_flock);
+ return (copyout(&linux_flock, (void *)args->arg,
+ sizeof(linux_flock)));
+
+ case LINUX_F_SETLK:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock(&linux_flock, &bsd_flock);
+ return (kern_fcntl(td, args->fd, F_SETLK,
+ (intptr_t)&bsd_flock));
+
+ case LINUX_F_SETLKW:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock(&linux_flock, &bsd_flock);
+ return (kern_fcntl(td, args->fd, F_SETLKW,
+ (intptr_t)&bsd_flock));
+
+ case LINUX_F_GETOWN:
+ return (kern_fcntl(td, args->fd, F_GETOWN, 0));
+
+ case LINUX_F_SETOWN:
+ /*
+ * XXX some Linux applications depend on F_SETOWN having no
+ * significant effect for pipes (SIGIO is not delivered for
+ * pipes under Linux-2.2.35 at least).
+ */
+ error = fget(td, args->fd, &fp);
+ if (error)
+ return (error);
+ if (fp->f_type == DTYPE_PIPE) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ fdrop(fp, td);
+
+ return (kern_fcntl(td, args->fd, F_SETOWN, args->arg));
+ }
+
+ return (EINVAL);
+}
+
+int
+linux_fcntl(struct thread *td, struct linux_fcntl_args *args)
+{
+ struct linux_fcntl64_args args64;
+
+#ifdef DEBUG
+ if (ldebug(fcntl))
+ printf(ARGS(fcntl, "%d, %08x, *"), args->fd, args->cmd);
+#endif
+
+ args64.fd = args->fd;
+ args64.cmd = args->cmd;
+ args64.arg = args->arg;
+ return (fcntl_common(td, &args64));
+}
+
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+int
+linux_fcntl64(struct thread *td, struct linux_fcntl64_args *args)
+{
+ struct l_flock64 linux_flock;
+ struct flock bsd_flock;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(fcntl64))
+ printf(ARGS(fcntl64, "%d, %08x, *"), args->fd, args->cmd);
+#endif
+
+ switch (args->cmd) {
+ case LINUX_F_GETLK64:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock64(&linux_flock, &bsd_flock);
+ error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
+ if (error)
+ return (error);
+ bsd_to_linux_flock64(&bsd_flock, &linux_flock);
+ return (copyout(&linux_flock, (void *)args->arg,
+ sizeof(linux_flock)));
+
+ case LINUX_F_SETLK64:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock64(&linux_flock, &bsd_flock);
+ return (kern_fcntl(td, args->fd, F_SETLK,
+ (intptr_t)&bsd_flock));
+
+ case LINUX_F_SETLKW64:
+ error = copyin((void *)args->arg, &linux_flock,
+ sizeof(linux_flock));
+ if (error)
+ return (error);
+ linux_to_bsd_flock64(&linux_flock, &bsd_flock);
+ return (kern_fcntl(td, args->fd, F_SETLKW,
+ (intptr_t)&bsd_flock));
+ }
+
+ return (fcntl_common(td, args));
+}
+#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+
+int
+linux_chown(struct thread *td, struct linux_chown_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(chown))
+ printf(ARGS(chown, "%s, %d, %d"), path, args->uid, args->gid);
+#endif
+ error = kern_chown(td, path, UIO_SYSSPACE, args->uid, args->gid);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_fchownat(struct thread *td, struct linux_fchownat_args *args)
+{
+ char *path;
+ int error, dfd, follow;
+
+ if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
+ return (EINVAL);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fchownat))
+ printf(ARGS(fchownat, "%s, %d, %d"), path, args->uid, args->gid);
+#endif
+
+ follow = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) == 0 ? 0 :
+ AT_SYMLINK_NOFOLLOW;
+ error = kern_fchownat(td, dfd, path, UIO_SYSSPACE, args->uid, args->gid,
+ follow);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_lchown(struct thread *td, struct linux_lchown_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(lchown))
+ printf(ARGS(lchown, "%s, %d, %d"), path, args->uid, args->gid);
+#endif
+ error = kern_lchown(td, path, UIO_SYSSPACE, args->uid, args->gid);
+ LFREEPATH(path);
+ return (error);
+}
diff --git a/sys/compat/linux/linux_file.h b/sys/compat/linux/linux_file.h
new file mode 100644
index 0000000..e229cb6
--- /dev/null
+++ b/sys/compat/linux/linux_file.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007 Roman Divacky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_FILE_H_
+#define _LINUX_FILE_H_
+
+#define LINUX_AT_FDCWD -100
+#define LINUX_AT_SYMLINK_NOFOLLOW 0x100
+#define LINUX_AT_REMOVEDIR 0x200
+
+#endif /* !_LINUX_FILE_H_ */
diff --git a/sys/compat/linux/linux_fork.c b/sys/compat/linux/linux_fork.c
new file mode 100644
index 0000000..bf1d45c
--- /dev/null
+++ b/sys/compat/linux/linux_fork.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins
+ * Copyright (c) 2002 Doug Rabson
+ * Copyright (c) 2000 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_signal.h>
+#include <compat/linux/linux_emul.h>
+
+
+int
+linux_fork(struct thread *td, struct linux_fork_args *args)
+{
+ int error;
+ struct proc *p2;
+ struct thread *td2;
+
+#ifdef DEBUG
+ if (ldebug(fork))
+ printf(ARGS(fork, ""));
+#endif
+
+ if ((error = fork1(td, RFFDG | RFPROC | RFSTOPPED, 0, &p2)) != 0)
+ return (error);
+
+ td->td_retval[0] = p2->p_pid;
+ td->td_retval[1] = 0;
+
+ error = linux_proc_init(td, td->td_retval[0], 0);
+ if (error)
+ return (error);
+
+ td2 = FIRST_THREAD_IN_PROC(p2);
+
+ /*
+ * Make this runnable after we are finished with it.
+ */
+ thread_lock(td2);
+ TD_SET_CAN_RUN(td2);
+ sched_add(td2, SRQ_BORING);
+ thread_unlock(td2);
+
+ return (0);
+}
+
+int
+linux_vfork(struct thread *td, struct linux_vfork_args *args)
+{
+ int error;
+ struct proc *p2;
+ struct thread *td2;
+
+#ifdef DEBUG
+ if (ldebug(vfork))
+ printf(ARGS(vfork, ""));
+#endif
+
+ /* Exclude RFPPWAIT */
+ if ((error = fork1(td, RFFDG | RFPROC | RFMEM | RFSTOPPED, 0, &p2)) != 0)
+ return (error);
+
+ td->td_retval[0] = p2->p_pid;
+
+ error = linux_proc_init(td, td->td_retval[0], 0);
+ if (error)
+ return (error);
+
+ PROC_LOCK(p2);
+ p2->p_flag |= P_PPWAIT;
+ PROC_UNLOCK(p2);
+
+ td2 = FIRST_THREAD_IN_PROC(p2);
+
+ /*
+ * Make this runnable after we are finished with it.
+ */
+ thread_lock(td2);
+ TD_SET_CAN_RUN(td2);
+ sched_add(td2, SRQ_BORING);
+ thread_unlock(td2);
+
+ /* wait for the children to exit, ie. emulate vfork */
+ PROC_LOCK(p2);
+ while (p2->p_flag & P_PPWAIT)
+ cv_wait(&p2->p_pwait, &p2->p_mtx);
+ PROC_UNLOCK(p2);
+
+ return (0);
+}
+
+int
+linux_clone(struct thread *td, struct linux_clone_args *args)
+{
+ int error, ff = RFPROC | RFSTOPPED;
+ struct proc *p2;
+ struct thread *td2;
+ int exit_signal;
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(clone)) {
+ printf(ARGS(clone, "flags %x, stack %p, parent tid: %p, "
+ "child tid: %p"), (unsigned)args->flags,
+ args->stack, args->parent_tidptr, args->child_tidptr);
+ }
+#endif
+
+ exit_signal = args->flags & 0x000000ff;
+ if (LINUX_SIG_VALID(exit_signal)) {
+ if (exit_signal <= LINUX_SIGTBLSZ)
+ exit_signal =
+ linux_to_bsd_signal[_SIG_IDX(exit_signal)];
+ } else if (exit_signal != 0)
+ return (EINVAL);
+
+ if (args->flags & LINUX_CLONE_VM)
+ ff |= RFMEM;
+ if (args->flags & LINUX_CLONE_SIGHAND)
+ ff |= RFSIGSHARE;
+ /*
+ * XXX: In Linux, sharing of fs info (chroot/cwd/umask)
+ * and open files is independant. In FreeBSD, its in one
+ * structure but in reality it does not cause any problems
+ * because both of these flags are usually set together.
+ */
+ if (!(args->flags & (LINUX_CLONE_FILES | LINUX_CLONE_FS)))
+ ff |= RFFDG;
+
+ /*
+ * Attempt to detect when linux_clone(2) is used for creating
+ * kernel threads. Unfortunately despite the existence of the
+ * CLONE_THREAD flag, version of linuxthreads package used in
+ * most popular distros as of beginning of 2005 doesn't make
+ * any use of it. Therefore, this detection relies on
+ * empirical observation that linuxthreads sets certain
+ * combination of flags, so that we can make more or less
+ * precise detection and notify the FreeBSD kernel that several
+ * processes are in fact part of the same threading group, so
+ * that special treatment is necessary for signal delivery
+ * between those processes and fd locking.
+ */
+ if ((args->flags & 0xffffff00) == LINUX_THREADING_FLAGS)
+ ff |= RFTHREAD;
+
+ if (args->flags & LINUX_CLONE_PARENT_SETTID)
+ if (args->parent_tidptr == NULL)
+ return (EINVAL);
+
+ error = fork1(td, ff, 0, &p2);
+ if (error)
+ return (error);
+
+ if (args->flags & (LINUX_CLONE_PARENT | LINUX_CLONE_THREAD)) {
+ sx_xlock(&proctree_lock);
+ PROC_LOCK(p2);
+ proc_reparent(p2, td->td_proc->p_pptr);
+ PROC_UNLOCK(p2);
+ sx_xunlock(&proctree_lock);
+ }
+
+ /* create the emuldata */
+ error = linux_proc_init(td, p2->p_pid, args->flags);
+ /* reference it - no need to check this */
+ em = em_find(p2, EMUL_DOLOCK);
+ KASSERT(em != NULL, ("clone: emuldata not found."));
+ /* and adjust it */
+
+ if (args->flags & LINUX_CLONE_THREAD) {
+#ifdef notyet
+ PROC_LOCK(p2);
+ p2->p_pgrp = td->td_proc->p_pgrp;
+ PROC_UNLOCK(p2);
+#endif
+ exit_signal = 0;
+ }
+
+ if (args->flags & LINUX_CLONE_CHILD_SETTID)
+ em->child_set_tid = args->child_tidptr;
+ else
+ em->child_set_tid = NULL;
+
+ if (args->flags & LINUX_CLONE_CHILD_CLEARTID)
+ em->child_clear_tid = args->child_tidptr;
+ else
+ em->child_clear_tid = NULL;
+
+ EMUL_UNLOCK(&emul_lock);
+
+ if (args->flags & LINUX_CLONE_PARENT_SETTID) {
+ error = copyout(&p2->p_pid, args->parent_tidptr,
+ sizeof(p2->p_pid));
+ if (error)
+ printf(LMSG("copyout failed!"));
+ }
+
+ PROC_LOCK(p2);
+ p2->p_sigparent = exit_signal;
+ PROC_UNLOCK(p2);
+ td2 = FIRST_THREAD_IN_PROC(p2);
+ /*
+ * In a case of stack = NULL, we are supposed to COW calling process
+ * stack. This is what normal fork() does, so we just keep tf_rsp arg
+ * intact.
+ */
+ if (args->stack)
+ linux_set_upcall_kse(td2, PTROUT(args->stack));
+
+ if (args->flags & LINUX_CLONE_SETTLS)
+ linux_set_cloned_tls(td2, args->tls);
+
+#ifdef DEBUG
+ if (ldebug(clone))
+ printf(LMSG("clone: successful rfork to %d, "
+ "stack %p sig = %d"), (int)p2->p_pid, args->stack,
+ exit_signal);
+#endif
+ if (args->flags & LINUX_CLONE_VFORK) {
+ PROC_LOCK(p2);
+ p2->p_flag |= P_PPWAIT;
+ PROC_UNLOCK(p2);
+ }
+
+ /*
+ * Make this runnable after we are finished with it.
+ */
+ thread_lock(td2);
+ TD_SET_CAN_RUN(td2);
+ sched_add(td2, SRQ_BORING);
+ thread_unlock(td2);
+
+ td->td_retval[0] = p2->p_pid;
+ td->td_retval[1] = 0;
+
+ if (args->flags & LINUX_CLONE_VFORK) {
+ /* wait for the children to exit, ie. emulate vfork */
+ PROC_LOCK(p2);
+ while (p2->p_flag & P_PPWAIT)
+ cv_wait(&p2->p_pwait, &p2->p_mtx);
+ PROC_UNLOCK(p2);
+ }
+
+ return (0);
+}
diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c
new file mode 100644
index 0000000..736bd28
--- /dev/null
+++ b/sys/compat/linux/linux_futex.c
@@ -0,0 +1,887 @@
+/* $NetBSD: linux_futex.c,v 1.7 2006/07/24 19:01:49 manu 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#if 0
+__KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.7 2006/07/24 19:01:49 manu Exp $");
+#endif
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sched.h>
+#include <sys/sx.h>
+#include <sys/umtx.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_futex.h>
+#include <compat/linux/linux_util.h>
+
+MALLOC_DEFINE(M_FUTEX, "futex", "Linux futexes");
+MALLOC_DEFINE(M_FUTEX_WP, "futex wp", "Linux futexes wp");
+
+struct futex;
+
+struct waiting_proc {
+ uint32_t wp_flags;
+ struct futex *wp_futex;
+ TAILQ_ENTRY(waiting_proc) wp_list;
+};
+
+struct futex {
+ struct sx f_lck;
+ uint32_t *f_uaddr; /* user-supplied value, for debug */
+ struct umtx_key f_key;
+ uint32_t f_refcount;
+ uint32_t f_bitset;
+ LIST_ENTRY(futex) f_list;
+ TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc;
+};
+
+struct futex_list futex_list;
+
+#define FUTEX_LOCK(f) sx_xlock(&(f)->f_lck)
+#define FUTEX_UNLOCK(f) sx_xunlock(&(f)->f_lck)
+#define FUTEX_INIT(f) sx_init_flags(&(f)->f_lck, "ftlk", SX_DUPOK)
+#define FUTEX_DESTROY(f) sx_destroy(&(f)->f_lck)
+#define FUTEX_ASSERT_LOCKED(f) sx_assert(&(f)->f_lck, SA_XLOCKED)
+
+struct mtx futex_mtx; /* protects the futex list */
+#define FUTEXES_LOCK mtx_lock(&futex_mtx)
+#define FUTEXES_UNLOCK mtx_unlock(&futex_mtx)
+
+/* flags for futex_get() */
+#define FUTEX_CREATE_WP 0x1 /* create waiting_proc */
+#define FUTEX_DONTCREATE 0x2 /* don't create futex if not exists */
+#define FUTEX_DONTEXISTS 0x4 /* return EINVAL if futex exists */
+#define FUTEX_SHARED 0x8 /* shared futex */
+
+/* wp_flags */
+#define FUTEX_WP_REQUEUED 0x1 /* wp requeued - wp moved from wp_list
+ * of futex where thread sleep to wp_list
+ * of another futex.
+ */
+#define FUTEX_WP_REMOVED 0x2 /* wp is woken up and removed from futex
+ * wp_list to prevent double wakeup.
+ */
+
+/* support.s */
+int futex_xchgl(int oparg, uint32_t *uaddr, int *oldval);
+int futex_addl(int oparg, uint32_t *uaddr, int *oldval);
+int futex_orl(int oparg, uint32_t *uaddr, int *oldval);
+int futex_andl(int oparg, uint32_t *uaddr, int *oldval);
+int futex_xorl(int oparg, uint32_t *uaddr, int *oldval);
+
+static void
+futex_put(struct futex *f, struct waiting_proc *wp)
+{
+
+ FUTEX_ASSERT_LOCKED(f);
+ if (wp != NULL) {
+ if ((wp->wp_flags & FUTEX_WP_REMOVED) == 0)
+ TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
+ free(wp, M_FUTEX_WP);
+ }
+
+ FUTEXES_LOCK;
+ if (--f->f_refcount == 0) {
+ LIST_REMOVE(f, f_list);
+ FUTEXES_UNLOCK;
+ FUTEX_UNLOCK(f);
+
+ LINUX_CTR3(sys_futex, "futex_put destroy uaddr %p ref %d "
+ "shared %d", f->f_uaddr, f->f_refcount, f->f_key.shared);
+ umtx_key_release(&f->f_key);
+ FUTEX_DESTROY(f);
+ free(f, M_FUTEX);
+ return;
+ }
+
+ LINUX_CTR3(sys_futex, "futex_put uaddr %p ref %d shared %d",
+ f->f_uaddr, f->f_refcount, f->f_key.shared);
+ FUTEXES_UNLOCK;
+ FUTEX_UNLOCK(f);
+}
+
+static int
+futex_get0(uint32_t *uaddr, struct futex **newf, uint32_t flags)
+{
+ struct futex *f, *tmpf;
+ struct umtx_key key;
+ int error;
+
+ *newf = tmpf = NULL;
+
+ error = umtx_key_get(uaddr, TYPE_FUTEX, (flags & FUTEX_SHARED) ?
+ AUTO_SHARE : THREAD_SHARE, &key);
+ if (error)
+ return (error);
+retry:
+ FUTEXES_LOCK;
+ LIST_FOREACH(f, &futex_list, f_list) {
+ if (umtx_key_match(&f->f_key, &key)) {
+ if (tmpf != NULL) {
+ FUTEX_UNLOCK(tmpf);
+ FUTEX_DESTROY(tmpf);
+ free(tmpf, M_FUTEX);
+ }
+ if (flags & FUTEX_DONTEXISTS) {
+ FUTEXES_UNLOCK;
+ umtx_key_release(&key);
+ return (EINVAL);
+ }
+
+ /*
+ * Increment refcount of the found futex to
+ * prevent it from deallocation before FUTEX_LOCK()
+ */
+ ++f->f_refcount;
+ FUTEXES_UNLOCK;
+ umtx_key_release(&key);
+
+ FUTEX_LOCK(f);
+ *newf = f;
+ LINUX_CTR3(sys_futex, "futex_get uaddr %p ref %d shared %d",
+ uaddr, f->f_refcount, f->f_key.shared);
+ return (0);
+ }
+ }
+
+ if (flags & FUTEX_DONTCREATE) {
+ FUTEXES_UNLOCK;
+ umtx_key_release(&key);
+ LINUX_CTR1(sys_futex, "futex_get uaddr %p null", uaddr);
+ return (0);
+ }
+
+ if (tmpf == NULL) {
+ FUTEXES_UNLOCK;
+ tmpf = malloc(sizeof(*tmpf), M_FUTEX, M_WAITOK | M_ZERO);
+ tmpf->f_uaddr = uaddr;
+ tmpf->f_key = key;
+ tmpf->f_refcount = 1;
+ tmpf->f_bitset = FUTEX_BITSET_MATCH_ANY;
+ FUTEX_INIT(tmpf);
+ TAILQ_INIT(&tmpf->f_waiting_proc);
+
+ /*
+ * Lock the new futex before an insert into the futex_list
+ * to prevent futex usage by other.
+ */
+ FUTEX_LOCK(tmpf);
+ goto retry;
+ }
+
+ LIST_INSERT_HEAD(&futex_list, tmpf, f_list);
+ FUTEXES_UNLOCK;
+
+ LINUX_CTR3(sys_futex, "futex_get uaddr %p ref %d shared %d new",
+ uaddr, tmpf->f_refcount, tmpf->f_key.shared);
+ *newf = tmpf;
+ return (0);
+}
+
+static int
+futex_get(uint32_t *uaddr, struct waiting_proc **wp, struct futex **f,
+ uint32_t flags)
+{
+ int error;
+
+ if (flags & FUTEX_CREATE_WP) {
+ *wp = malloc(sizeof(struct waiting_proc), M_FUTEX_WP, M_WAITOK);
+ (*wp)->wp_flags = 0;
+ }
+ error = futex_get0(uaddr, f, flags);
+ if (error) {
+ if (flags & FUTEX_CREATE_WP)
+ free(*wp, M_FUTEX_WP);
+ return (error);
+ }
+ if (flags & FUTEX_CREATE_WP) {
+ TAILQ_INSERT_HEAD(&(*f)->f_waiting_proc, *wp, wp_list);
+ (*wp)->wp_futex = *f;
+ }
+
+ return (error);
+}
+
+static int
+futex_sleep(struct futex *f, struct waiting_proc *wp, int timeout)
+{
+ int error;
+
+ FUTEX_ASSERT_LOCKED(f);
+ LINUX_CTR4(sys_futex, "futex_sleep enter uaddr %p wp %p timo %d ref %d",
+ f->f_uaddr, wp, timeout, f->f_refcount);
+ error = sx_sleep(wp, &f->f_lck, PCATCH, "futex", timeout);
+ if (wp->wp_flags & FUTEX_WP_REQUEUED) {
+ KASSERT(f != wp->wp_futex, ("futex != wp_futex"));
+ LINUX_CTR5(sys_futex, "futex_sleep out error %d uaddr %p w"
+ " %p requeued uaddr %p ref %d",
+ error, f->f_uaddr, wp, wp->wp_futex->f_uaddr,
+ wp->wp_futex->f_refcount);
+ futex_put(f, NULL);
+ f = wp->wp_futex;
+ FUTEX_LOCK(f);
+ } else
+ LINUX_CTR3(sys_futex, "futex_sleep out error %d uaddr %p wp %p",
+ error, f->f_uaddr, wp);
+
+ futex_put(f, wp);
+ return (error);
+}
+
+static int
+futex_wake(struct futex *f, int n, uint32_t bitset)
+{
+ struct waiting_proc *wp, *wpt;
+ int count = 0;
+
+ if (bitset == 0)
+ return (EINVAL);
+
+ FUTEX_ASSERT_LOCKED(f);
+ TAILQ_FOREACH_SAFE(wp, &f->f_waiting_proc, wp_list, wpt) {
+ LINUX_CTR3(sys_futex, "futex_wake uaddr %p wp %p ref %d",
+ f->f_uaddr, wp, f->f_refcount);
+ /*
+ * Unless we find a matching bit in
+ * the bitset, continue searching.
+ */
+ if (!(wp->wp_futex->f_bitset & bitset))
+ continue;
+
+ wp->wp_flags |= FUTEX_WP_REMOVED;
+ TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
+ wakeup_one(wp);
+ if (++count == n)
+ break;
+ }
+
+ return (count);
+}
+
+static int
+futex_requeue(struct futex *f, int n, struct futex *f2, int n2)
+{
+ struct waiting_proc *wp, *wpt;
+ int count = 0;
+
+ FUTEX_ASSERT_LOCKED(f);
+ FUTEX_ASSERT_LOCKED(f2);
+
+ TAILQ_FOREACH_SAFE(wp, &f->f_waiting_proc, wp_list, wpt) {
+ if (++count <= n) {
+ LINUX_CTR2(sys_futex, "futex_req_wake uaddr %p wp %p",
+ f->f_uaddr, wp);
+ wp->wp_flags |= FUTEX_WP_REMOVED;
+ TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
+ wakeup_one(wp);
+ } else {
+ LINUX_CTR3(sys_futex, "futex_requeue uaddr %p wp %p to %p",
+ f->f_uaddr, wp, f2->f_uaddr);
+ wp->wp_flags |= FUTEX_WP_REQUEUED;
+ /* Move wp to wp_list of f2 futex */
+ TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
+ TAILQ_INSERT_HEAD(&f2->f_waiting_proc, wp, wp_list);
+
+ /*
+ * Thread which sleeps on wp after waking should
+ * acquire f2 lock, so increment refcount of f2 to
+ * prevent it from premature deallocation.
+ */
+ wp->wp_futex = f2;
+ FUTEXES_LOCK;
+ ++f2->f_refcount;
+ FUTEXES_UNLOCK;
+ if (count - n >= n2)
+ break;
+ }
+ }
+
+ return (count);
+}
+
+static int
+futex_wait(struct futex *f, struct waiting_proc *wp, struct l_timespec *ts,
+ uint32_t bitset)
+{
+ struct l_timespec timeout;
+ struct timeval tv;
+ int timeout_hz;
+ int error;
+
+ if (bitset == 0)
+ return (EINVAL);
+ f->f_bitset = bitset;
+
+ if (ts != NULL) {
+ error = copyin(ts, &timeout, sizeof(timeout));
+ if (error)
+ return (error);
+ TIMESPEC_TO_TIMEVAL(&tv, &timeout);
+ error = itimerfix(&tv);
+ if (error)
+ return (error);
+ timeout_hz = tvtohz(&tv);
+ } else
+ timeout_hz = 0;
+
+ error = futex_sleep(f, wp, timeout_hz);
+ if (error == EWOULDBLOCK)
+ error = ETIMEDOUT;
+
+ return (error);
+}
+
+static int
+futex_atomic_op(struct thread *td, int encoded_op, uint32_t *uaddr)
+{
+ int op = (encoded_op >> 28) & 7;
+ int cmp = (encoded_op >> 24) & 15;
+ int oparg = (encoded_op << 8) >> 20;
+ int cmparg = (encoded_op << 20) >> 20;
+ int oldval = 0, ret;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+ oparg = 1 << oparg;
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("futex_atomic_op: op = %d, cmp = %d, oparg = %x, "
+ "cmparg = %x, uaddr = %p\n",
+ op, cmp, oparg, cmparg, uaddr);
+#endif
+ /* XXX: Linux verifies access here and returns EFAULT */
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ ret = futex_xchgl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_ADD:
+ ret = futex_addl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_OR:
+ ret = futex_orl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_ANDN:
+ ret = futex_andl(~oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_XOR:
+ ret = futex_xorl(oparg, uaddr, &oldval);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ if (ret)
+ return (ret);
+
+ 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_futex(struct thread *td, struct linux_sys_futex_args *args)
+{
+ int clockrt, nrwake, op_ret, ret, val;
+ struct linux_emuldata *em;
+ struct waiting_proc *wp;
+ struct futex *f, *f2;
+ int error;
+ uint32_t flags;
+
+ if (args->op & LINUX_FUTEX_PRIVATE_FLAG) {
+ flags = 0;
+ args->op &= ~LINUX_FUTEX_PRIVATE_FLAG;
+ } else
+ flags = FUTEX_SHARED;
+
+ /*
+ * Currently support for switching between CLOCK_MONOTONIC and
+ * CLOCK_REALTIME is not present. However Linux forbids the use of
+ * FUTEX_CLOCK_REALTIME with any op except FUTEX_WAIT_BITSET and
+ * FUTEX_WAIT_REQUEUE_PI.
+ */
+ clockrt = args->op & LINUX_FUTEX_CLOCK_REALTIME;
+ args->op = args->op & ~LINUX_FUTEX_CLOCK_REALTIME;
+ if (clockrt && args->op != LINUX_FUTEX_WAIT_BITSET &&
+ args->op != LINUX_FUTEX_WAIT_REQUEUE_PI)
+ return (ENOSYS);
+
+ error = 0;
+ f = f2 = NULL;
+
+ switch (args->op) {
+ case LINUX_FUTEX_WAIT:
+ args->val3 = FUTEX_BITSET_MATCH_ANY;
+ /* FALLTHROUGH */
+
+ case LINUX_FUTEX_WAIT_BITSET:
+
+ LINUX_CTR3(sys_futex, "WAIT uaddr %p val %d val3 %d",
+ args->uaddr, args->val, args->val3);
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf(ARGS(sys_futex,
+ "futex_wait uaddr %p val %d val3 %d"),
+ args->uaddr, args->val, args->val3);
+#endif
+ error = futex_get(args->uaddr, &wp, &f,
+ flags | FUTEX_CREATE_WP);
+ if (error)
+ return (error);
+ error = copyin(args->uaddr, &val, sizeof(val));
+ if (error) {
+ LINUX_CTR1(sys_futex, "WAIT copyin failed %d",
+ error);
+ futex_put(f, wp);
+ return (error);
+ }
+ if (val != args->val) {
+ LINUX_CTR4(sys_futex,
+ "WAIT uaddr %p val %d != uval %d val3 %d",
+ args->uaddr, args->val, val, args->val3);
+ futex_put(f, wp);
+ return (EWOULDBLOCK);
+ }
+
+ error = futex_wait(f, wp, args->timeout, args->val3);
+ break;
+
+ case LINUX_FUTEX_WAKE:
+ args->val3 = FUTEX_BITSET_MATCH_ANY;
+ /* FALLTHROUGH */
+
+ case LINUX_FUTEX_WAKE_BITSET:
+
+ LINUX_CTR3(sys_futex, "WAKE uaddr %p val % d val3 %d",
+ args->uaddr, args->val, args->val3);
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf(ARGS(sys_futex, "futex_wake uaddr %p val %d val3 %d"),
+ args->uaddr, args->val, args->val3);
+#endif
+ error = futex_get(args->uaddr, NULL, &f,
+ flags | FUTEX_DONTCREATE);
+ if (error)
+ return (error);
+ if (f == NULL) {
+ td->td_retval[0] = 0;
+ return (error);
+ }
+ td->td_retval[0] = futex_wake(f, args->val, args->val3);
+ futex_put(f, NULL);
+ break;
+
+ case LINUX_FUTEX_CMP_REQUEUE:
+
+ LINUX_CTR5(sys_futex, "CMP_REQUEUE uaddr %p "
+ "val %d val3 %d uaddr2 %p val2 %d",
+ args->uaddr, args->val, args->val3, args->uaddr2,
+ (int)(unsigned long)args->timeout);
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf(ARGS(sys_futex, "futex_cmp_requeue uaddr %p "
+ "val %d val3 %d uaddr2 %p val2 %d"),
+ args->uaddr, args->val, args->val3, args->uaddr2,
+ (int)(unsigned long)args->timeout);
+#endif
+
+ /*
+ * Linux allows this, we would not, it is an incorrect
+ * usage of declared ABI, so return EINVAL.
+ */
+ if (args->uaddr == args->uaddr2)
+ return (EINVAL);
+ error = futex_get(args->uaddr, NULL, &f, flags);
+ if (error)
+ return (error);
+
+ /*
+ * To avoid deadlocks return EINVAL if second futex
+ * exists at this time.
+ *
+ * Glibc fall back to FUTEX_WAKE in case of any error
+ * returned by FUTEX_CMP_REQUEUE.
+ */
+ error = futex_get(args->uaddr2, NULL, &f2,
+ flags | FUTEX_DONTEXISTS);
+ if (error) {
+ futex_put(f, NULL);
+ return (error);
+ }
+ error = copyin(args->uaddr, &val, sizeof(val));
+ if (error) {
+ LINUX_CTR1(sys_futex, "CMP_REQUEUE copyin failed %d",
+ error);
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ return (error);
+ }
+ if (val != args->val3) {
+ LINUX_CTR2(sys_futex, "CMP_REQUEUE val %d != uval %d",
+ args->val, val);
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ return (EAGAIN);
+ }
+
+ nrwake = (int)(unsigned long)args->timeout;
+ td->td_retval[0] = futex_requeue(f, args->val, f2, nrwake);
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ break;
+
+ case LINUX_FUTEX_WAKE_OP:
+
+ LINUX_CTR5(sys_futex, "WAKE_OP "
+ "uaddr %p op %d val %x uaddr2 %p val3 %x",
+ args->uaddr, args->op, args->val,
+ args->uaddr2, args->val3);
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf(ARGS(sys_futex, "futex_wake_op "
+ "uaddr %p op %d val %x uaddr2 %p val3 %x"),
+ args->uaddr, args->op, args->val,
+ args->uaddr2, args->val3);
+#endif
+ error = futex_get(args->uaddr, NULL, &f, flags);
+ if (error)
+ return (error);
+ if (args->uaddr != args->uaddr2)
+ error = futex_get(args->uaddr2, NULL, &f2, flags);
+ if (error) {
+ futex_put(f, NULL);
+ return (error);
+ }
+
+ /*
+ * This function returns positive number as results and
+ * negative as errors
+ */
+ op_ret = futex_atomic_op(td, args->val3, args->uaddr2);
+
+ if (op_ret < 0) {
+ /* XXX: We don't handle the EFAULT yet. */
+ if (op_ret != -EFAULT) {
+ if (f2 != NULL)
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ return (-op_ret);
+ }
+ if (f2 != NULL)
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ return (EFAULT);
+ }
+
+ ret = futex_wake(f, args->val, args->val3);
+
+ if (op_ret > 0) {
+ op_ret = 0;
+ nrwake = (int)(unsigned long)args->timeout;
+
+ if (f2 != NULL)
+ op_ret += futex_wake(f2, nrwake, args->val3);
+ else
+ op_ret += futex_wake(f, nrwake, args->val3);
+ ret += op_ret;
+
+ }
+ if (f2 != NULL)
+ futex_put(f2, NULL);
+ futex_put(f, NULL);
+ td->td_retval[0] = ret;
+ break;
+
+ case LINUX_FUTEX_LOCK_PI:
+ /* not yet implemented */
+ linux_msg(td,
+ "linux_sys_futex: "
+ "op LINUX_FUTEX_LOCK_PI not implemented\n");
+ return (ENOSYS);
+
+ case LINUX_FUTEX_UNLOCK_PI:
+ /* not yet implemented */
+ linux_msg(td,
+ "linux_sys_futex: "
+ "op LINUX_FUTEX_UNLOCK_PI not implemented\n");
+ return (ENOSYS);
+
+ case LINUX_FUTEX_TRYLOCK_PI:
+ /* not yet implemented */
+ linux_msg(td,
+ "linux_sys_futex: "
+ "op LINUX_FUTEX_TRYLOCK_PI not implemented\n");
+ return (ENOSYS);
+
+ case LINUX_FUTEX_REQUEUE:
+
+ /*
+ * Glibc does not use this operation since version 2.3.3,
+ * as it is racy and replaced by FUTEX_CMP_REQUEUE operation.
+ * Glibc versions prior to 2.3.3 fall back to FUTEX_WAKE when
+ * FUTEX_REQUEUE returned EINVAL.
+ */
+ em = em_find(td->td_proc, EMUL_DONTLOCK);
+ if ((em->flags & LINUX_XDEPR_REQUEUEOP) == 0) {
+ linux_msg(td,
+ "linux_sys_futex: "
+ "unsupported futex_requeue op\n");
+ em->flags |= LINUX_XDEPR_REQUEUEOP;
+ }
+ return (EINVAL);
+
+ case LINUX_FUTEX_WAIT_REQUEUE_PI:
+ /* not yet implemented */
+ linux_msg(td,
+ "linux_sys_futex: "
+ "op FUTEX_WAIT_REQUEUE_PI not implemented\n");
+ return (ENOSYS);
+
+ case LINUX_FUTEX_CMP_REQUEUE_PI:
+ /* not yet implemented */
+ linux_msg(td,
+ "linux_sys_futex: "
+ "op LINUX_FUTEX_CMP_REQUEUE_PI not implemented\n");
+ return (ENOSYS);
+
+ default:
+ linux_msg(td,
+ "linux_sys_futex: unknown op %d\n", args->op);
+ return (ENOSYS);
+ }
+
+ return (error);
+}
+
+int
+linux_set_robust_list(struct thread *td, struct linux_set_robust_list_args *args)
+{
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(set_robust_list))
+ printf(ARGS(set_robust_list, "head %p len %d"),
+ args->head, args->len);
+#endif
+
+ if (args->len != sizeof(struct linux_robust_list_head))
+ return (EINVAL);
+
+ em = em_find(td->td_proc, EMUL_DOLOCK);
+ em->robust_futexes = args->head;
+ EMUL_UNLOCK(&emul_lock);
+
+ return (0);
+}
+
+int
+linux_get_robust_list(struct thread *td, struct linux_get_robust_list_args *args)
+{
+ struct linux_emuldata *em;
+ struct linux_robust_list_head *head;
+ l_size_t len = sizeof(struct linux_robust_list_head);
+ int error = 0;
+
+#ifdef DEBUG
+ if (ldebug(get_robust_list))
+ printf(ARGS(get_robust_list, ""));
+#endif
+
+ if (!args->pid) {
+ em = em_find(td->td_proc, EMUL_DONTLOCK);
+ head = em->robust_futexes;
+ } else {
+ struct proc *p;
+
+ p = pfind(args->pid);
+ if (p == NULL)
+ return (ESRCH);
+
+ em = em_find(p, EMUL_DONTLOCK);
+ /* XXX: ptrace? */
+ if (priv_check(td, PRIV_CRED_SETUID) ||
+ priv_check(td, PRIV_CRED_SETEUID) ||
+ p_candebug(td, p)) {
+ PROC_UNLOCK(p);
+ return (EPERM);
+ }
+ head = em->robust_futexes;
+
+ PROC_UNLOCK(p);
+ }
+
+ error = copyout(&len, args->len, sizeof(l_size_t));
+ if (error)
+ return (EFAULT);
+
+ error = copyout(head, args->head, sizeof(struct linux_robust_list_head));
+
+ return (error);
+}
+
+static int
+handle_futex_death(struct proc *p, uint32_t *uaddr, int pi)
+{
+ uint32_t uval, nval, mval;
+ struct futex *f;
+ int error;
+
+retry:
+ if (copyin(uaddr, &uval, 4))
+ return (EFAULT);
+ if ((uval & FUTEX_TID_MASK) == p->p_pid) {
+ mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
+ nval = casuword32(uaddr, uval, mval);
+
+ if (nval == -1)
+ return (EFAULT);
+
+ if (nval != uval)
+ goto retry;
+
+ if (!pi && (uval & FUTEX_WAITERS)) {
+ error = futex_get(uaddr, NULL, &f,
+ FUTEX_DONTCREATE | FUTEX_SHARED);
+ if (error)
+ return (error);
+ if (f != NULL) {
+ futex_wake(f, 1, FUTEX_BITSET_MATCH_ANY);
+ futex_put(f, NULL);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+fetch_robust_entry(struct linux_robust_list **entry,
+ struct linux_robust_list **head, int *pi)
+{
+ l_ulong uentry;
+
+ if (copyin((const void *)head, &uentry, sizeof(l_ulong)))
+ return (EFAULT);
+
+ *entry = (void *)(uentry & ~1UL);
+ *pi = uentry & 1;
+
+ return (0);
+}
+
+/* This walks the list of robust futexes releasing them. */
+void
+release_futexes(struct proc *p)
+{
+ struct linux_robust_list_head *head = NULL;
+ struct linux_robust_list *entry, *next_entry, *pending;
+ unsigned int limit = 2048, pi, next_pi, pip;
+ struct linux_emuldata *em;
+ l_long futex_offset;
+ int rc;
+
+ em = em_find(p, EMUL_DONTLOCK);
+ head = em->robust_futexes;
+
+ if (head == NULL)
+ return;
+
+ if (fetch_robust_entry(&entry, PTRIN(&head->list.next), &pi))
+ return;
+
+ if (copyin(&head->futex_offset, &futex_offset, sizeof(futex_offset)))
+ return;
+
+ if (fetch_robust_entry(&pending, PTRIN(&head->pending_list), &pip))
+ return;
+
+ while (entry != &head->list) {
+ rc = fetch_robust_entry(&next_entry, PTRIN(&entry->next), &next_pi);
+
+ if (entry != pending)
+ if (handle_futex_death(p, (uint32_t *)entry + futex_offset, pi))
+ return;
+ if (rc)
+ return;
+
+ entry = next_entry;
+ pi = next_pi;
+
+ if (!--limit)
+ break;
+
+ sched_relinquish(curthread);
+ }
+
+ if (pending)
+ handle_futex_death(p, (uint32_t *)pending + futex_offset, pip);
+}
diff --git a/sys/compat/linux/linux_futex.h b/sys/compat/linux/linux_futex.h
new file mode 100644
index 0000000..0990daa
--- /dev/null
+++ b/sys/compat/linux/linux_futex.h
@@ -0,0 +1,81 @@
+/* $NetBSD: linux_futex.h,v 1.2 2005/12/11 12:20:19 christos 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_FUTEX_H
+#define _LINUX_FUTEX_H
+
+extern LIST_HEAD(futex_list, futex) futex_list;
+extern struct mtx futex_mtx;
+
+#define LINUX_FUTEX_WAIT 0
+#define LINUX_FUTEX_WAKE 1
+#define LINUX_FUTEX_FD 2 /* unused */
+#define LINUX_FUTEX_REQUEUE 3
+#define LINUX_FUTEX_CMP_REQUEUE 4
+#define LINUX_FUTEX_WAKE_OP 5
+#define LINUX_FUTEX_LOCK_PI 6
+#define LINUX_FUTEX_UNLOCK_PI 7
+#define LINUX_FUTEX_TRYLOCK_PI 8
+#define LINUX_FUTEX_WAIT_BITSET 9
+#define LINUX_FUTEX_WAKE_BITSET 10
+#define LINUX_FUTEX_WAIT_REQUEUE_PI 11
+#define LINUX_FUTEX_CMP_REQUEUE_PI 12
+
+#define LINUX_FUTEX_PRIVATE_FLAG 128
+#define LINUX_FUTEX_CLOCK_REALTIME 256
+
+#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */
+#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */
+#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */
+#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */
+#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */
+
+#define FUTEX_OP_OPARG_SHIFT 8 /* Use (1 << OPARG) instead of OPARG. */
+
+#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */
+#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */
+#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */
+#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */
+#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */
+#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */
+
+#define FUTEX_WAITERS 0x80000000
+#define FUTEX_OWNER_DIED 0x40000000
+#define FUTEX_TID_MASK 0x3fffffff
+#define FUTEX_BITSET_MATCH_ANY 0xffffffff
+
+void release_futexes(struct proc *);
+
+#endif /* !_LINUX_FUTEX_H */
diff --git a/sys/compat/linux/linux_getcwd.c b/sys/compat/linux/linux_getcwd.c
new file mode 100644
index 0000000..cad5a22
--- /dev/null
+++ b/sys/compat/linux/linux_getcwd.c
@@ -0,0 +1,469 @@
+/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */
+/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Bill Sommerfeld.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/syscallsubr.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_util.h>
+
+#include <security/mac/mac_framework.h>
+
+static int
+linux_getcwd_scandir(struct vnode **, struct vnode **,
+ char **, char *, struct thread *);
+static int
+linux_getcwd_common(struct vnode *, struct vnode *,
+ char **, char *, int, int, struct thread *);
+
+#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
+
+/*
+ * Vnode variable naming conventions in this file:
+ *
+ * rvp: the current root we're aiming towards.
+ * lvp, *lvpp: the "lower" vnode
+ * uvp, *uvpp: the "upper" vnode.
+ *
+ * Since all the vnodes we're dealing with are directories, and the
+ * lookups are going *up* in the filesystem rather than *down*, the
+ * usual "pvp" (parent) or "dvp" (directory) naming conventions are
+ * too confusing.
+ */
+
+/*
+ * XXX Will infinite loop in certain cases if a directory read reliably
+ * returns EINVAL on last block.
+ * XXX is EINVAL the right thing to return if a directory is malformed?
+ */
+
+/*
+ * XXX Untested vs. mount -o union; probably does the wrong thing.
+ */
+
+/*
+ * Find parent vnode of *lvpp, return in *uvpp
+ *
+ * If we care about the name, scan it looking for name of directory
+ * entry pointing at lvp.
+ *
+ * Place the name in the buffer which starts at bufp, immediately
+ * before *bpp, and move bpp backwards to point at the start of it.
+ *
+ * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
+ * On exit, *uvpp is either NULL or is a locked vnode reference.
+ */
+static int
+linux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td)
+ struct vnode **lvpp;
+ struct vnode **uvpp;
+ char **bpp;
+ char *bufp;
+ struct thread *td;
+{
+ int error = 0;
+ int eofflag;
+ off_t off;
+ int tries;
+ struct uio uio;
+ struct iovec iov;
+ char *dirbuf = NULL;
+ int dirbuflen;
+ ino_t fileno;
+ struct vattr va;
+ struct vnode *uvp = NULL;
+ struct vnode *lvp = *lvpp;
+ struct componentname cn;
+ int len, reclen;
+ tries = 0;
+
+ /*
+ * If we want the filename, get some info we need while the
+ * current directory is still locked.
+ */
+ if (bufp != NULL) {
+ error = VOP_GETATTR(lvp, &va, td->td_ucred);
+ if (error) {
+ vput(lvp);
+ *lvpp = NULL;
+ *uvpp = NULL;
+ return error;
+ }
+ }
+
+ /*
+ * Ok, we have to do it the hard way..
+ * Next, get parent vnode using lookup of ..
+ */
+ cn.cn_nameiop = LOOKUP;
+ cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
+ cn.cn_thread = td;
+ cn.cn_cred = td->td_ucred;
+ cn.cn_pnbuf = NULL;
+ cn.cn_nameptr = "..";
+ cn.cn_namelen = 2;
+ cn.cn_consume = 0;
+ cn.cn_lkflags = LK_SHARED;
+
+ /*
+ * At this point, lvp is locked and will be unlocked by the lookup.
+ * On successful return, *uvpp will be locked
+ */
+#ifdef MAC
+ error = mac_vnode_check_lookup(td->td_ucred, lvp, &cn);
+ if (error == 0)
+#endif
+ error = VOP_LOOKUP(lvp, uvpp, &cn);
+ if (error) {
+ vput(lvp);
+ *lvpp = NULL;
+ *uvpp = NULL;
+ return error;
+ }
+ uvp = *uvpp;
+
+ /* If we don't care about the pathname, we're done */
+ if (bufp == NULL) {
+ vput(lvp);
+ *lvpp = NULL;
+ return 0;
+ }
+
+ fileno = va.va_fileid;
+
+ dirbuflen = DIRBLKSIZ;
+ if (dirbuflen < va.va_blocksize)
+ dirbuflen = va.va_blocksize;
+ dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
+
+#if 0
+unionread:
+#endif
+ off = 0;
+ do {
+ /* call VOP_READDIR of parent */
+ iov.iov_base = dirbuf;
+ iov.iov_len = dirbuflen;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = off;
+ uio.uio_resid = dirbuflen;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = td;
+
+ eofflag = 0;
+
+#ifdef MAC
+ error = mac_vnode_check_readdir(td->td_ucred, uvp);
+ if (error == 0)
+#endif /* MAC */
+ error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag,
+ 0, 0);
+
+ off = uio.uio_offset;
+
+ /*
+ * Try again if NFS tosses its cookies.
+ * XXX this can still loop forever if the directory is busted
+ * such that the second or subsequent page of it always
+ * returns EINVAL
+ */
+ if ((error == EINVAL) && (tries < 3)) {
+ off = 0;
+ tries++;
+ continue; /* once more, with feeling */
+ }
+
+ if (!error) {
+ char *cpos;
+ struct dirent *dp;
+
+ cpos = dirbuf;
+ tries = 0;
+
+ /* scan directory page looking for matching vnode */
+ for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) {
+ dp = (struct dirent *) cpos;
+ reclen = dp->d_reclen;
+
+ /* check for malformed directory.. */
+ if (reclen < DIRENT_MINSIZE) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * XXX should perhaps do VOP_LOOKUP to
+ * check that we got back to the right place,
+ * but getting the locking games for that
+ * right would be heinous.
+ */
+ if ((dp->d_type != DT_WHT) &&
+ (dp->d_fileno == fileno)) {
+ char *bp = *bpp;
+ bp -= dp->d_namlen;
+
+ if (bp <= bufp) {
+ error = ERANGE;
+ goto out;
+ }
+ bcopy(dp->d_name, bp, dp->d_namlen);
+ error = 0;
+ *bpp = bp;
+ goto out;
+ }
+ cpos += reclen;
+ }
+ }
+ } while (!eofflag);
+ error = ENOENT;
+
+out:
+ vput(lvp);
+ *lvpp = NULL;
+ free(dirbuf, M_TEMP);
+ return error;
+}
+
+
+/*
+ * common routine shared by sys___getcwd() and linux_vn_isunder()
+ */
+
+#define GETCWD_CHECK_ACCESS 0x0001
+
+static int
+linux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td)
+ struct vnode *lvp;
+ struct vnode *rvp;
+ char **bpp;
+ char *bufp;
+ int limit;
+ int flags;
+ struct thread *td;
+{
+ struct filedesc *fdp = td->td_proc->p_fd;
+ struct vnode *uvp = NULL;
+ char *bp = NULL;
+ int error;
+ accmode_t accmode = VEXEC;
+
+ if (rvp == NULL) {
+ rvp = fdp->fd_rdir;
+ if (rvp == NULL)
+ rvp = rootvnode;
+ }
+
+ VREF(rvp);
+ VREF(lvp);
+
+ /*
+ * Error handling invariant:
+ * Before a `goto out':
+ * lvp is either NULL, or locked and held.
+ * uvp is either NULL, or locked and held.
+ */
+
+ error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
+ if (error != 0)
+ panic("vn_lock LK_RETRY returned error %d", error);
+ if (bufp)
+ bp = *bpp;
+ /*
+ * this loop will terminate when one of the following happens:
+ * - we hit the root
+ * - getdirentries or lookup fails
+ * - we run out of space in the buffer.
+ */
+ if (lvp == rvp) {
+ if (bp)
+ *(--bp) = '/';
+ goto out;
+ }
+ do {
+ if (lvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * access check here is optional, depending on
+ * whether or not caller cares.
+ */
+ if (flags & GETCWD_CHECK_ACCESS) {
+ error = VOP_ACCESS(lvp, accmode, td->td_ucred, td);
+ if (error)
+ goto out;
+ accmode = VEXEC|VREAD;
+ }
+
+ /*
+ * step up if we're a covered vnode..
+ */
+ while (lvp->v_vflag & VV_ROOT) {
+ struct vnode *tvp;
+
+ if (lvp == rvp)
+ goto out;
+
+ tvp = lvp;
+ lvp = lvp->v_mount->mnt_vnodecovered;
+ vput(tvp);
+ /*
+ * hodie natus est radici frater
+ */
+ if (lvp == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ VREF(lvp);
+ error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
+ if (error != 0)
+ panic("vn_lock LK_RETRY returned %d", error);
+ }
+ error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td);
+ if (error)
+ goto out;
+#ifdef DIAGNOSTIC
+ if (lvp != NULL)
+ panic("getcwd: oops, forgot to null lvp");
+ if (bufp && (bp <= bufp)) {
+ panic("getcwd: oops, went back too far");
+ }
+#endif
+ if (bp)
+ *(--bp) = '/';
+ lvp = uvp;
+ uvp = NULL;
+ limit--;
+ } while ((lvp != rvp) && (limit > 0));
+
+out:
+ if (bpp)
+ *bpp = bp;
+ if (uvp)
+ vput(uvp);
+ if (lvp)
+ vput(lvp);
+ vrele(rvp);
+ return error;
+}
+
+
+/*
+ * Find pathname of process's current directory.
+ *
+ * Use vfs vnode-to-name reverse cache; if that fails, fall back
+ * to reading directory contents.
+ */
+
+int
+linux_getcwd(struct thread *td, struct linux_getcwd_args *args)
+{
+ caddr_t bp, bend, path;
+ int error, len, lenused;
+
+#ifdef DEBUG
+ if (ldebug(getcwd))
+ printf(ARGS(getcwd, "%p, %ld"), args->buf, (long)args->bufsize);
+#endif
+
+ len = args->bufsize;
+
+ if (len > MAXPATHLEN*4)
+ len = MAXPATHLEN*4;
+ else if (len < 2)
+ return ERANGE;
+
+ path = (char *)malloc(len, M_TEMP, M_WAITOK);
+
+ error = kern___getcwd(td, path, UIO_SYSSPACE, len);
+ if (!error) {
+ lenused = strlen(path) + 1;
+ if (lenused <= args->bufsize) {
+ td->td_retval[0] = lenused;
+ error = copyout(path, args->buf, lenused);
+ }
+ else
+ error = ERANGE;
+ } else {
+ bp = &path[len];
+ bend = bp;
+ *(--bp) = '\0';
+
+ /*
+ * 5th argument here is "max number of vnodes to traverse".
+ * Since each entry takes up at least 2 bytes in the output buffer,
+ * limit it to N/2 vnodes for an N byte buffer.
+ */
+
+ mtx_lock(&Giant);
+ error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL,
+ &bp, path, len/2, GETCWD_CHECK_ACCESS, td);
+ mtx_unlock(&Giant);
+
+ if (error)
+ goto out;
+ lenused = bend - bp;
+ td->td_retval[0] = lenused;
+ /* put the result into user buffer */
+ error = copyout(bp, args->buf, lenused);
+ }
+out:
+ free(path, M_TEMP);
+ return (error);
+}
+
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
new file mode 100644
index 0000000..4237f6f
--- /dev/null
+++ b/sys/compat/linux/linux_ioctl.c
@@ -0,0 +1,3220 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opt_compat.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/cdio.h>
+#include <sys/dvdio.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+#include <sys/consio.h>
+#include <sys/ctype.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/filio.h>
+#include <sys/jail.h>
+#include <sys/kbio.h>
+#include <sys/kernel.h>
+#include <sys/linker_set.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/sbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/soundcard.h>
+#include <sys/stdint.h>
+#include <sys/sx.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/vnet.h>
+
+#include <dev/usb/usb_ioctl.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_ioctl.h>
+#include <compat/linux/linux_mib.h>
+#include <compat/linux/linux_socket.h>
+#include <compat/linux/linux_util.h>
+
+#include <compat/linux/linux_videodev.h>
+#include <compat/linux/linux_videodev_compat.h>
+
+CTASSERT(LINUX_IFNAMSIZ == IFNAMSIZ);
+
+static linux_ioctl_function_t linux_ioctl_cdrom;
+static linux_ioctl_function_t linux_ioctl_vfat;
+static linux_ioctl_function_t linux_ioctl_console;
+static linux_ioctl_function_t linux_ioctl_hdio;
+static linux_ioctl_function_t linux_ioctl_disk;
+static linux_ioctl_function_t linux_ioctl_socket;
+static linux_ioctl_function_t linux_ioctl_sound;
+static linux_ioctl_function_t linux_ioctl_termio;
+static linux_ioctl_function_t linux_ioctl_private;
+static linux_ioctl_function_t linux_ioctl_drm;
+static linux_ioctl_function_t linux_ioctl_sg;
+static linux_ioctl_function_t linux_ioctl_v4l;
+static linux_ioctl_function_t linux_ioctl_special;
+static linux_ioctl_function_t linux_ioctl_fbsd_usb;
+
+static struct linux_ioctl_handler cdrom_handler =
+{ linux_ioctl_cdrom, LINUX_IOCTL_CDROM_MIN, LINUX_IOCTL_CDROM_MAX };
+static struct linux_ioctl_handler vfat_handler =
+{ linux_ioctl_vfat, LINUX_IOCTL_VFAT_MIN, LINUX_IOCTL_VFAT_MAX };
+static struct linux_ioctl_handler console_handler =
+{ linux_ioctl_console, LINUX_IOCTL_CONSOLE_MIN, LINUX_IOCTL_CONSOLE_MAX };
+static struct linux_ioctl_handler hdio_handler =
+{ linux_ioctl_hdio, LINUX_IOCTL_HDIO_MIN, LINUX_IOCTL_HDIO_MAX };
+static struct linux_ioctl_handler disk_handler =
+{ linux_ioctl_disk, LINUX_IOCTL_DISK_MIN, LINUX_IOCTL_DISK_MAX };
+static struct linux_ioctl_handler socket_handler =
+{ linux_ioctl_socket, LINUX_IOCTL_SOCKET_MIN, LINUX_IOCTL_SOCKET_MAX };
+static struct linux_ioctl_handler sound_handler =
+{ linux_ioctl_sound, LINUX_IOCTL_SOUND_MIN, LINUX_IOCTL_SOUND_MAX };
+static struct linux_ioctl_handler termio_handler =
+{ linux_ioctl_termio, LINUX_IOCTL_TERMIO_MIN, LINUX_IOCTL_TERMIO_MAX };
+static struct linux_ioctl_handler private_handler =
+{ linux_ioctl_private, LINUX_IOCTL_PRIVATE_MIN, LINUX_IOCTL_PRIVATE_MAX };
+static struct linux_ioctl_handler drm_handler =
+{ linux_ioctl_drm, LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX };
+static struct linux_ioctl_handler sg_handler =
+{ linux_ioctl_sg, LINUX_IOCTL_SG_MIN, LINUX_IOCTL_SG_MAX };
+static struct linux_ioctl_handler video_handler =
+{ linux_ioctl_v4l, LINUX_IOCTL_VIDEO_MIN, LINUX_IOCTL_VIDEO_MAX };
+static struct linux_ioctl_handler fbsd_usb =
+{ linux_ioctl_fbsd_usb, FBSD_LUSB_MIN, FBSD_LUSB_MAX };
+
+DATA_SET(linux_ioctl_handler_set, cdrom_handler);
+DATA_SET(linux_ioctl_handler_set, vfat_handler);
+DATA_SET(linux_ioctl_handler_set, console_handler);
+DATA_SET(linux_ioctl_handler_set, hdio_handler);
+DATA_SET(linux_ioctl_handler_set, disk_handler);
+DATA_SET(linux_ioctl_handler_set, socket_handler);
+DATA_SET(linux_ioctl_handler_set, sound_handler);
+DATA_SET(linux_ioctl_handler_set, termio_handler);
+DATA_SET(linux_ioctl_handler_set, private_handler);
+DATA_SET(linux_ioctl_handler_set, drm_handler);
+DATA_SET(linux_ioctl_handler_set, sg_handler);
+DATA_SET(linux_ioctl_handler_set, video_handler);
+DATA_SET(linux_ioctl_handler_set, fbsd_usb);
+
+struct handler_element
+{
+ TAILQ_ENTRY(handler_element) list;
+ int (*func)(struct thread *, struct linux_ioctl_args *);
+ int low, high, span;
+};
+
+static TAILQ_HEAD(, handler_element) handlers =
+ TAILQ_HEAD_INITIALIZER(handlers);
+static struct sx linux_ioctl_sx;
+SX_SYSINIT(linux_ioctl, &linux_ioctl_sx, "linux ioctl handlers");
+
+/*
+ * hdio related ioctls for VMWare support
+ */
+
+struct linux_hd_geometry {
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int16_t cylinders;
+ u_int32_t start;
+};
+
+struct linux_hd_big_geometry {
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int32_t cylinders;
+ u_int32_t start;
+};
+
+static int
+linux_ioctl_hdio(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error;
+ u_int sectorsize, fwcylinders, fwheads, fwsectors;
+ off_t mediasize, bytespercyl;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ switch (args->cmd & 0xffff) {
+ case LINUX_HDIO_GET_GEO:
+ case LINUX_HDIO_GET_GEO_BIG:
+ error = fo_ioctl(fp, DIOCGMEDIASIZE,
+ (caddr_t)&mediasize, td->td_ucred, td);
+ if (!error)
+ error = fo_ioctl(fp, DIOCGSECTORSIZE,
+ (caddr_t)&sectorsize, td->td_ucred, td);
+ if (!error)
+ error = fo_ioctl(fp, DIOCGFWHEADS,
+ (caddr_t)&fwheads, td->td_ucred, td);
+ if (!error)
+ error = fo_ioctl(fp, DIOCGFWSECTORS,
+ (caddr_t)&fwsectors, td->td_ucred, td);
+ /*
+ * XXX: DIOCGFIRSTOFFSET is not yet implemented, so
+ * so pretend that GEOM always says 0. This is NOT VALID
+ * for slices or partitions, only the per-disk raw devices.
+ */
+
+ fdrop(fp, td);
+ if (error)
+ return (error);
+ /*
+ * 1. Calculate the number of bytes in a cylinder,
+ * given the firmware's notion of heads and sectors
+ * per cylinder.
+ * 2. Calculate the number of cylinders, given the total
+ * size of the media.
+ * All internal calculations should have 64-bit precision.
+ */
+ bytespercyl = (off_t) sectorsize * fwheads * fwsectors;
+ fwcylinders = mediasize / bytespercyl;
+#if defined(DEBUG)
+ linux_msg(td, "HDIO_GET_GEO: mediasize %jd, c/h/s %d/%d/%d, "
+ "bpc %jd",
+ (intmax_t)mediasize, fwcylinders, fwheads, fwsectors,
+ (intmax_t)bytespercyl);
+#endif
+ if ((args->cmd & 0xffff) == LINUX_HDIO_GET_GEO) {
+ struct linux_hd_geometry hdg;
+
+ hdg.cylinders = fwcylinders;
+ hdg.heads = fwheads;
+ hdg.sectors = fwsectors;
+ hdg.start = 0;
+ error = copyout(&hdg, (void *)args->arg, sizeof(hdg));
+ } else if ((args->cmd & 0xffff) == LINUX_HDIO_GET_GEO_BIG) {
+ struct linux_hd_big_geometry hdbg;
+
+ hdbg.cylinders = fwcylinders;
+ hdbg.heads = fwheads;
+ hdbg.sectors = fwsectors;
+ hdbg.start = 0;
+ error = copyout(&hdbg, (void *)args->arg, sizeof(hdbg));
+ }
+ return (error);
+ break;
+ default:
+ /* XXX */
+ linux_msg(td,
+ "ioctl fd=%d, cmd=0x%x ('%c',%d) is not implemented",
+ args->fd, (int)(args->cmd & 0xffff),
+ (int)(args->cmd & 0xff00) >> 8,
+ (int)(args->cmd & 0xff));
+ break;
+ }
+ fdrop(fp, td);
+ return (ENOIOCTL);
+}
+
+static int
+linux_ioctl_disk(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error;
+ u_int sectorsize;
+ off_t mediasize;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ switch (args->cmd & 0xffff) {
+ case LINUX_BLKGETSIZE:
+ error = fo_ioctl(fp, DIOCGSECTORSIZE,
+ (caddr_t)&sectorsize, td->td_ucred, td);
+ if (!error)
+ error = fo_ioctl(fp, DIOCGMEDIASIZE,
+ (caddr_t)&mediasize, td->td_ucred, td);
+ fdrop(fp, td);
+ if (error)
+ return (error);
+ sectorsize = mediasize / sectorsize;
+ /*
+ * XXX: How do we know we return the right size of integer ?
+ */
+ return (copyout(&sectorsize, (void *)args->arg,
+ sizeof(sectorsize)));
+ break;
+ }
+ fdrop(fp, td);
+ return (ENOIOCTL);
+}
+
+/*
+ * termio related ioctls
+ */
+
+struct linux_termio {
+ unsigned short c_iflag;
+ unsigned short c_oflag;
+ unsigned short c_cflag;
+ unsigned short c_lflag;
+ unsigned char c_line;
+ unsigned char c_cc[LINUX_NCC];
+};
+
+struct linux_termios {
+ unsigned int c_iflag;
+ unsigned int c_oflag;
+ unsigned int c_cflag;
+ unsigned int c_lflag;
+ unsigned char c_line;
+ unsigned char c_cc[LINUX_NCCS];
+};
+
+struct linux_winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+
+struct speedtab {
+ int sp_speed; /* Speed. */
+ int sp_code; /* Code. */
+};
+
+static struct speedtab sptab[] = {
+ { B0, LINUX_B0 }, { B50, LINUX_B50 },
+ { B75, LINUX_B75 }, { B110, LINUX_B110 },
+ { B134, LINUX_B134 }, { B150, LINUX_B150 },
+ { B200, LINUX_B200 }, { B300, LINUX_B300 },
+ { B600, LINUX_B600 }, { B1200, LINUX_B1200 },
+ { B1800, LINUX_B1800 }, { B2400, LINUX_B2400 },
+ { B4800, LINUX_B4800 }, { B9600, LINUX_B9600 },
+ { B19200, LINUX_B19200 }, { B38400, LINUX_B38400 },
+ { B57600, LINUX_B57600 }, { B115200, LINUX_B115200 },
+ {-1, -1 }
+};
+
+struct linux_serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ int reserved[4];
+};
+
+static int
+linux_to_bsd_speed(int code, struct speedtab *table)
+{
+ for ( ; table->sp_code != -1; table++)
+ if (table->sp_code == code)
+ return (table->sp_speed);
+ return -1;
+}
+
+static int
+bsd_to_linux_speed(int speed, struct speedtab *table)
+{
+ for ( ; table->sp_speed != -1; table++)
+ if (table->sp_speed == speed)
+ return (table->sp_code);
+ return -1;
+}
+
+static void
+bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios)
+{
+ int i;
+
+#ifdef DEBUG
+ if (ldebug(ioctl)) {
+ printf("LINUX: BSD termios structure (input):\n");
+ printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n",
+ bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag,
+ bios->c_ispeed, bios->c_ospeed);
+ printf("c_cc ");
+ for (i=0; i<NCCS; i++)
+ printf("%02x ", bios->c_cc[i]);
+ printf("\n");
+ }
+#endif
+
+ lios->c_iflag = 0;
+ if (bios->c_iflag & IGNBRK)
+ lios->c_iflag |= LINUX_IGNBRK;
+ if (bios->c_iflag & BRKINT)
+ lios->c_iflag |= LINUX_BRKINT;
+ if (bios->c_iflag & IGNPAR)
+ lios->c_iflag |= LINUX_IGNPAR;
+ if (bios->c_iflag & PARMRK)
+ lios->c_iflag |= LINUX_PARMRK;
+ if (bios->c_iflag & INPCK)
+ lios->c_iflag |= LINUX_INPCK;
+ if (bios->c_iflag & ISTRIP)
+ lios->c_iflag |= LINUX_ISTRIP;
+ if (bios->c_iflag & INLCR)
+ lios->c_iflag |= LINUX_INLCR;
+ if (bios->c_iflag & IGNCR)
+ lios->c_iflag |= LINUX_IGNCR;
+ if (bios->c_iflag & ICRNL)
+ lios->c_iflag |= LINUX_ICRNL;
+ if (bios->c_iflag & IXON)
+ lios->c_iflag |= LINUX_IXON;
+ if (bios->c_iflag & IXANY)
+ lios->c_iflag |= LINUX_IXANY;
+ if (bios->c_iflag & IXOFF)
+ lios->c_iflag |= LINUX_IXOFF;
+ if (bios->c_iflag & IMAXBEL)
+ lios->c_iflag |= LINUX_IMAXBEL;
+
+ lios->c_oflag = 0;
+ if (bios->c_oflag & OPOST)
+ lios->c_oflag |= LINUX_OPOST;
+ if (bios->c_oflag & ONLCR)
+ lios->c_oflag |= LINUX_ONLCR;
+ if (bios->c_oflag & TAB3)
+ lios->c_oflag |= LINUX_XTABS;
+
+ lios->c_cflag = bsd_to_linux_speed(bios->c_ispeed, sptab);
+ lios->c_cflag |= (bios->c_cflag & CSIZE) >> 4;
+ if (bios->c_cflag & CSTOPB)
+ lios->c_cflag |= LINUX_CSTOPB;
+ if (bios->c_cflag & CREAD)
+ lios->c_cflag |= LINUX_CREAD;
+ if (bios->c_cflag & PARENB)
+ lios->c_cflag |= LINUX_PARENB;
+ if (bios->c_cflag & PARODD)
+ lios->c_cflag |= LINUX_PARODD;
+ if (bios->c_cflag & HUPCL)
+ lios->c_cflag |= LINUX_HUPCL;
+ if (bios->c_cflag & CLOCAL)
+ lios->c_cflag |= LINUX_CLOCAL;
+ if (bios->c_cflag & CRTSCTS)
+ lios->c_cflag |= LINUX_CRTSCTS;
+
+ lios->c_lflag = 0;
+ if (bios->c_lflag & ISIG)
+ lios->c_lflag |= LINUX_ISIG;
+ if (bios->c_lflag & ICANON)
+ lios->c_lflag |= LINUX_ICANON;
+ if (bios->c_lflag & ECHO)
+ lios->c_lflag |= LINUX_ECHO;
+ if (bios->c_lflag & ECHOE)
+ lios->c_lflag |= LINUX_ECHOE;
+ if (bios->c_lflag & ECHOK)
+ lios->c_lflag |= LINUX_ECHOK;
+ if (bios->c_lflag & ECHONL)
+ lios->c_lflag |= LINUX_ECHONL;
+ if (bios->c_lflag & NOFLSH)
+ lios->c_lflag |= LINUX_NOFLSH;
+ if (bios->c_lflag & TOSTOP)
+ lios->c_lflag |= LINUX_TOSTOP;
+ if (bios->c_lflag & ECHOCTL)
+ lios->c_lflag |= LINUX_ECHOCTL;
+ if (bios->c_lflag & ECHOPRT)
+ lios->c_lflag |= LINUX_ECHOPRT;
+ if (bios->c_lflag & ECHOKE)
+ lios->c_lflag |= LINUX_ECHOKE;
+ if (bios->c_lflag & FLUSHO)
+ lios->c_lflag |= LINUX_FLUSHO;
+ if (bios->c_lflag & PENDIN)
+ lios->c_lflag |= LINUX_PENDIN;
+ if (bios->c_lflag & IEXTEN)
+ lios->c_lflag |= LINUX_IEXTEN;
+
+ for (i=0; i<LINUX_NCCS; i++)
+ lios->c_cc[i] = LINUX_POSIX_VDISABLE;
+ lios->c_cc[LINUX_VINTR] = bios->c_cc[VINTR];
+ lios->c_cc[LINUX_VQUIT] = bios->c_cc[VQUIT];
+ lios->c_cc[LINUX_VERASE] = bios->c_cc[VERASE];
+ lios->c_cc[LINUX_VKILL] = bios->c_cc[VKILL];
+ lios->c_cc[LINUX_VEOF] = bios->c_cc[VEOF];
+ lios->c_cc[LINUX_VEOL] = bios->c_cc[VEOL];
+ lios->c_cc[LINUX_VMIN] = bios->c_cc[VMIN];
+ lios->c_cc[LINUX_VTIME] = bios->c_cc[VTIME];
+ lios->c_cc[LINUX_VEOL2] = bios->c_cc[VEOL2];
+ lios->c_cc[LINUX_VSUSP] = bios->c_cc[VSUSP];
+ lios->c_cc[LINUX_VSTART] = bios->c_cc[VSTART];
+ lios->c_cc[LINUX_VSTOP] = bios->c_cc[VSTOP];
+ lios->c_cc[LINUX_VREPRINT] = bios->c_cc[VREPRINT];
+ lios->c_cc[LINUX_VDISCARD] = bios->c_cc[VDISCARD];
+ lios->c_cc[LINUX_VWERASE] = bios->c_cc[VWERASE];
+ lios->c_cc[LINUX_VLNEXT] = bios->c_cc[VLNEXT];
+
+ for (i=0; i<LINUX_NCCS; i++) {
+ if (i != LINUX_VMIN && i != LINUX_VTIME &&
+ lios->c_cc[i] == _POSIX_VDISABLE)
+ lios->c_cc[i] = LINUX_POSIX_VDISABLE;
+ }
+ lios->c_line = 0;
+
+#ifdef DEBUG
+ if (ldebug(ioctl)) {
+ printf("LINUX: LINUX termios structure (output):\n");
+ printf("i=%08x o=%08x c=%08x l=%08x line=%d\n",
+ lios->c_iflag, lios->c_oflag, lios->c_cflag,
+ lios->c_lflag, (int)lios->c_line);
+ printf("c_cc ");
+ for (i=0; i<LINUX_NCCS; i++)
+ printf("%02x ", lios->c_cc[i]);
+ printf("\n");
+ }
+#endif
+}
+
+static void
+linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios)
+{
+ int i;
+
+#ifdef DEBUG
+ if (ldebug(ioctl)) {
+ printf("LINUX: LINUX termios structure (input):\n");
+ printf("i=%08x o=%08x c=%08x l=%08x line=%d\n",
+ lios->c_iflag, lios->c_oflag, lios->c_cflag,
+ lios->c_lflag, (int)lios->c_line);
+ printf("c_cc ");
+ for (i=0; i<LINUX_NCCS; i++)
+ printf("%02x ", lios->c_cc[i]);
+ printf("\n");
+ }
+#endif
+
+ bios->c_iflag = 0;
+ if (lios->c_iflag & LINUX_IGNBRK)
+ bios->c_iflag |= IGNBRK;
+ if (lios->c_iflag & LINUX_BRKINT)
+ bios->c_iflag |= BRKINT;
+ if (lios->c_iflag & LINUX_IGNPAR)
+ bios->c_iflag |= IGNPAR;
+ if (lios->c_iflag & LINUX_PARMRK)
+ bios->c_iflag |= PARMRK;
+ if (lios->c_iflag & LINUX_INPCK)
+ bios->c_iflag |= INPCK;
+ if (lios->c_iflag & LINUX_ISTRIP)
+ bios->c_iflag |= ISTRIP;
+ if (lios->c_iflag & LINUX_INLCR)
+ bios->c_iflag |= INLCR;
+ if (lios->c_iflag & LINUX_IGNCR)
+ bios->c_iflag |= IGNCR;
+ if (lios->c_iflag & LINUX_ICRNL)
+ bios->c_iflag |= ICRNL;
+ if (lios->c_iflag & LINUX_IXON)
+ bios->c_iflag |= IXON;
+ if (lios->c_iflag & LINUX_IXANY)
+ bios->c_iflag |= IXANY;
+ if (lios->c_iflag & LINUX_IXOFF)
+ bios->c_iflag |= IXOFF;
+ if (lios->c_iflag & LINUX_IMAXBEL)
+ bios->c_iflag |= IMAXBEL;
+
+ bios->c_oflag = 0;
+ if (lios->c_oflag & LINUX_OPOST)
+ bios->c_oflag |= OPOST;
+ if (lios->c_oflag & LINUX_ONLCR)
+ bios->c_oflag |= ONLCR;
+ if (lios->c_oflag & LINUX_XTABS)
+ bios->c_oflag |= TAB3;
+
+ bios->c_cflag = (lios->c_cflag & LINUX_CSIZE) << 4;
+ if (lios->c_cflag & LINUX_CSTOPB)
+ bios->c_cflag |= CSTOPB;
+ if (lios->c_cflag & LINUX_CREAD)
+ bios->c_cflag |= CREAD;
+ if (lios->c_cflag & LINUX_PARENB)
+ bios->c_cflag |= PARENB;
+ if (lios->c_cflag & LINUX_PARODD)
+ bios->c_cflag |= PARODD;
+ if (lios->c_cflag & LINUX_HUPCL)
+ bios->c_cflag |= HUPCL;
+ if (lios->c_cflag & LINUX_CLOCAL)
+ bios->c_cflag |= CLOCAL;
+ if (lios->c_cflag & LINUX_CRTSCTS)
+ bios->c_cflag |= CRTSCTS;
+
+ bios->c_lflag = 0;
+ if (lios->c_lflag & LINUX_ISIG)
+ bios->c_lflag |= ISIG;
+ if (lios->c_lflag & LINUX_ICANON)
+ bios->c_lflag |= ICANON;
+ if (lios->c_lflag & LINUX_ECHO)
+ bios->c_lflag |= ECHO;
+ if (lios->c_lflag & LINUX_ECHOE)
+ bios->c_lflag |= ECHOE;
+ if (lios->c_lflag & LINUX_ECHOK)
+ bios->c_lflag |= ECHOK;
+ if (lios->c_lflag & LINUX_ECHONL)
+ bios->c_lflag |= ECHONL;
+ if (lios->c_lflag & LINUX_NOFLSH)
+ bios->c_lflag |= NOFLSH;
+ if (lios->c_lflag & LINUX_TOSTOP)
+ bios->c_lflag |= TOSTOP;
+ if (lios->c_lflag & LINUX_ECHOCTL)
+ bios->c_lflag |= ECHOCTL;
+ if (lios->c_lflag & LINUX_ECHOPRT)
+ bios->c_lflag |= ECHOPRT;
+ if (lios->c_lflag & LINUX_ECHOKE)
+ bios->c_lflag |= ECHOKE;
+ if (lios->c_lflag & LINUX_FLUSHO)
+ bios->c_lflag |= FLUSHO;
+ if (lios->c_lflag & LINUX_PENDIN)
+ bios->c_lflag |= PENDIN;
+ if (lios->c_lflag & LINUX_IEXTEN)
+ bios->c_lflag |= IEXTEN;
+
+ for (i=0; i<NCCS; i++)
+ bios->c_cc[i] = _POSIX_VDISABLE;
+ bios->c_cc[VINTR] = lios->c_cc[LINUX_VINTR];
+ bios->c_cc[VQUIT] = lios->c_cc[LINUX_VQUIT];
+ bios->c_cc[VERASE] = lios->c_cc[LINUX_VERASE];
+ bios->c_cc[VKILL] = lios->c_cc[LINUX_VKILL];
+ bios->c_cc[VEOF] = lios->c_cc[LINUX_VEOF];
+ bios->c_cc[VEOL] = lios->c_cc[LINUX_VEOL];
+ bios->c_cc[VMIN] = lios->c_cc[LINUX_VMIN];
+ bios->c_cc[VTIME] = lios->c_cc[LINUX_VTIME];
+ bios->c_cc[VEOL2] = lios->c_cc[LINUX_VEOL2];
+ bios->c_cc[VSUSP] = lios->c_cc[LINUX_VSUSP];
+ bios->c_cc[VSTART] = lios->c_cc[LINUX_VSTART];
+ bios->c_cc[VSTOP] = lios->c_cc[LINUX_VSTOP];
+ bios->c_cc[VREPRINT] = lios->c_cc[LINUX_VREPRINT];
+ bios->c_cc[VDISCARD] = lios->c_cc[LINUX_VDISCARD];
+ bios->c_cc[VWERASE] = lios->c_cc[LINUX_VWERASE];
+ bios->c_cc[VLNEXT] = lios->c_cc[LINUX_VLNEXT];
+
+ for (i=0; i<NCCS; i++) {
+ if (i != VMIN && i != VTIME &&
+ bios->c_cc[i] == LINUX_POSIX_VDISABLE)
+ bios->c_cc[i] = _POSIX_VDISABLE;
+ }
+
+ bios->c_ispeed = bios->c_ospeed =
+ linux_to_bsd_speed(lios->c_cflag & LINUX_CBAUD, sptab);
+
+#ifdef DEBUG
+ if (ldebug(ioctl)) {
+ printf("LINUX: BSD termios structure (output):\n");
+ printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n",
+ bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag,
+ bios->c_ispeed, bios->c_ospeed);
+ printf("c_cc ");
+ for (i=0; i<NCCS; i++)
+ printf("%02x ", bios->c_cc[i]);
+ printf("\n");
+ }
+#endif
+}
+
+static void
+bsd_to_linux_termio(struct termios *bios, struct linux_termio *lio)
+{
+ struct linux_termios lios;
+
+ bsd_to_linux_termios(bios, &lios);
+ lio->c_iflag = lios.c_iflag;
+ lio->c_oflag = lios.c_oflag;
+ lio->c_cflag = lios.c_cflag;
+ lio->c_lflag = lios.c_lflag;
+ lio->c_line = lios.c_line;
+ memcpy(lio->c_cc, lios.c_cc, LINUX_NCC);
+}
+
+static void
+linux_to_bsd_termio(struct linux_termio *lio, struct termios *bios)
+{
+ struct linux_termios lios;
+ int i;
+
+ lios.c_iflag = lio->c_iflag;
+ lios.c_oflag = lio->c_oflag;
+ lios.c_cflag = lio->c_cflag;
+ lios.c_lflag = lio->c_lflag;
+ for (i=LINUX_NCC; i<LINUX_NCCS; i++)
+ lios.c_cc[i] = LINUX_POSIX_VDISABLE;
+ memcpy(lios.c_cc, lio->c_cc, LINUX_NCC);
+ linux_to_bsd_termios(&lios, bios);
+}
+
+static int
+linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct termios bios;
+ struct linux_termios lios;
+ struct linux_termio lio;
+ struct file *fp;
+ int error;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_TCGETS:
+ error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred,
+ td);
+ if (error)
+ break;
+ bsd_to_linux_termios(&bios, &lios);
+ error = copyout(&lios, (void *)args->arg, sizeof(lios));
+ break;
+
+ case LINUX_TCSETS:
+ error = copyin((void *)args->arg, &lios, sizeof(lios));
+ if (error)
+ break;
+ linux_to_bsd_termios(&lios, &bios);
+ error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETSW:
+ error = copyin((void *)args->arg, &lios, sizeof(lios));
+ if (error)
+ break;
+ linux_to_bsd_termios(&lios, &bios);
+ error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETSF:
+ error = copyin((void *)args->arg, &lios, sizeof(lios));
+ if (error)
+ break;
+ linux_to_bsd_termios(&lios, &bios);
+ error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCGETA:
+ error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred,
+ td);
+ if (error)
+ break;
+ bsd_to_linux_termio(&bios, &lio);
+ error = (copyout(&lio, (void *)args->arg, sizeof(lio)));
+ break;
+
+ case LINUX_TCSETA:
+ error = copyin((void *)args->arg, &lio, sizeof(lio));
+ if (error)
+ break;
+ linux_to_bsd_termio(&lio, &bios);
+ error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETAW:
+ error = copyin((void *)args->arg, &lio, sizeof(lio));
+ if (error)
+ break;
+ linux_to_bsd_termio(&lio, &bios);
+ error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ case LINUX_TCSETAF:
+ error = copyin((void *)args->arg, &lio, sizeof(lio));
+ if (error)
+ break;
+ linux_to_bsd_termio(&lio, &bios);
+ error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred,
+ td));
+ break;
+
+ /* LINUX_TCSBRK */
+
+ case LINUX_TCXONC: {
+ switch (args->arg) {
+ case LINUX_TCOOFF:
+ args->cmd = TIOCSTOP;
+ break;
+ case LINUX_TCOON:
+ args->cmd = TIOCSTART;
+ break;
+ case LINUX_TCIOFF:
+ case LINUX_TCION: {
+ int c;
+ struct write_args wr;
+ error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios,
+ td->td_ucred, td);
+ if (error)
+ break;
+ fdrop(fp, td);
+ c = (args->arg == LINUX_TCIOFF) ? VSTOP : VSTART;
+ c = bios.c_cc[c];
+ if (c != _POSIX_VDISABLE) {
+ wr.fd = args->fd;
+ wr.buf = &c;
+ wr.nbyte = sizeof(c);
+ return (write(td, &wr));
+ } else
+ return (0);
+ }
+ default:
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ args->arg = 0;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+ }
+
+ case LINUX_TCFLSH: {
+ int val;
+ switch (args->arg) {
+ case LINUX_TCIFLUSH:
+ val = FREAD;
+ break;
+ case LINUX_TCOFLUSH:
+ val = FWRITE;
+ break;
+ case LINUX_TCIOFLUSH:
+ val = FREAD | FWRITE;
+ break;
+ default:
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ error = (fo_ioctl(fp,TIOCFLUSH,(caddr_t)&val,td->td_ucred,td));
+ break;
+ }
+
+ case LINUX_TIOCEXCL:
+ args->cmd = TIOCEXCL;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCNXCL:
+ args->cmd = TIOCNXCL;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCSCTTY:
+ args->cmd = TIOCSCTTY;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCGPGRP:
+ args->cmd = TIOCGPGRP;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCSPGRP:
+ args->cmd = TIOCSPGRP;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* LINUX_TIOCOUTQ */
+ /* LINUX_TIOCSTI */
+
+ case LINUX_TIOCGWINSZ:
+ args->cmd = TIOCGWINSZ;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCSWINSZ:
+ args->cmd = TIOCSWINSZ;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCMGET:
+ args->cmd = TIOCMGET;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCMBIS:
+ args->cmd = TIOCMBIS;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCMBIC:
+ args->cmd = TIOCMBIC;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCMSET:
+ args->cmd = TIOCMSET;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* TIOCGSOFTCAR */
+ /* TIOCSSOFTCAR */
+
+ case LINUX_FIONREAD: /* LINUX_TIOCINQ */
+ args->cmd = FIONREAD;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* LINUX_TIOCLINUX */
+
+ case LINUX_TIOCCONS:
+ args->cmd = TIOCCONS;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCGSERIAL: {
+ struct linux_serial_struct lss;
+ lss.type = LINUX_PORT_16550A;
+ lss.flags = 0;
+ lss.close_delay = 0;
+ error = copyout(&lss, (void *)args->arg, sizeof(lss));
+ break;
+ }
+
+ case LINUX_TIOCSSERIAL: {
+ struct linux_serial_struct lss;
+ error = copyin((void *)args->arg, &lss, sizeof(lss));
+ if (error)
+ break;
+ /* XXX - It really helps to have an implementation that
+ * does nothing. NOT!
+ */
+ error = 0;
+ break;
+ }
+
+ case LINUX_TIOCPKT:
+ args->cmd = TIOCPKT;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_FIONBIO:
+ args->cmd = FIONBIO;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCNOTTY:
+ args->cmd = TIOCNOTTY;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCSETD: {
+ int line;
+ switch (args->arg) {
+ case LINUX_N_TTY:
+ line = TTYDISC;
+ break;
+ case LINUX_N_SLIP:
+ line = SLIPDISC;
+ break;
+ case LINUX_N_PPP:
+ line = PPPDISC;
+ break;
+ default:
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ error = (fo_ioctl(fp, TIOCSETD, (caddr_t)&line, td->td_ucred,
+ td));
+ break;
+ }
+
+ case LINUX_TIOCGETD: {
+ int linux_line;
+ int bsd_line = TTYDISC;
+ error = fo_ioctl(fp, TIOCGETD, (caddr_t)&bsd_line,
+ td->td_ucred, td);
+ if (error)
+ return (error);
+ switch (bsd_line) {
+ case TTYDISC:
+ linux_line = LINUX_N_TTY;
+ break;
+ case SLIPDISC:
+ linux_line = LINUX_N_SLIP;
+ break;
+ case PPPDISC:
+ linux_line = LINUX_N_PPP;
+ break;
+ default:
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ error = (copyout(&linux_line, (void *)args->arg, sizeof(int)));
+ break;
+ }
+
+ /* LINUX_TCSBRKP */
+ /* LINUX_TIOCTTYGSTRUCT */
+
+ case LINUX_FIONCLEX:
+ args->cmd = FIONCLEX;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_FIOCLEX:
+ args->cmd = FIOCLEX;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_FIOASYNC:
+ args->cmd = FIOASYNC;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* LINUX_TIOCSERCONFIG */
+ /* LINUX_TIOCSERGWILD */
+ /* LINUX_TIOCSERSWILD */
+ /* LINUX_TIOCGLCKTRMIOS */
+ /* LINUX_TIOCSLCKTRMIOS */
+
+ case LINUX_TIOCSBRK:
+ args->cmd = TIOCSBRK;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_TIOCCBRK:
+ args->cmd = TIOCCBRK;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+ case LINUX_TIOCGPTN: {
+ int nb;
+
+ error = fo_ioctl(fp, TIOCGPTN, (caddr_t)&nb, td->td_ucred, td);
+ if (!error)
+ error = copyout(&nb, (void *)args->arg,
+ sizeof(int));
+ break;
+ }
+ case LINUX_TIOCSPTLCK:
+ /* Our unlockpt() does nothing. */
+ error = 0;
+ break;
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+ fdrop(fp, td);
+ return (error);
+}
+
+/*
+ * CDROM related ioctls
+ */
+
+struct linux_cdrom_msf
+{
+ u_char cdmsf_min0;
+ u_char cdmsf_sec0;
+ u_char cdmsf_frame0;
+ u_char cdmsf_min1;
+ u_char cdmsf_sec1;
+ u_char cdmsf_frame1;
+};
+
+struct linux_cdrom_tochdr
+{
+ u_char cdth_trk0;
+ u_char cdth_trk1;
+};
+
+union linux_cdrom_addr
+{
+ struct {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+};
+
+struct linux_cdrom_tocentry
+{
+ u_char cdte_track;
+ u_char cdte_adr:4;
+ u_char cdte_ctrl:4;
+ u_char cdte_format;
+ union linux_cdrom_addr cdte_addr;
+ u_char cdte_datamode;
+};
+
+struct linux_cdrom_subchnl
+{
+ u_char cdsc_format;
+ u_char cdsc_audiostatus;
+ u_char cdsc_adr:4;
+ u_char cdsc_ctrl:4;
+ u_char cdsc_trk;
+ u_char cdsc_ind;
+ union linux_cdrom_addr cdsc_absaddr;
+ union linux_cdrom_addr cdsc_reladdr;
+};
+
+struct l_cdrom_read_audio {
+ union linux_cdrom_addr addr;
+ u_char addr_format;
+ l_int nframes;
+ u_char *buf;
+};
+
+struct l_dvd_layer {
+ u_char book_version:4;
+ u_char book_type:4;
+ u_char min_rate:4;
+ u_char disc_size:4;
+ u_char layer_type:4;
+ u_char track_path:1;
+ u_char nlayers:2;
+ u_char track_density:4;
+ u_char linear_density:4;
+ u_char bca:1;
+ u_int32_t start_sector;
+ u_int32_t end_sector;
+ u_int32_t end_sector_l0;
+};
+
+struct l_dvd_physical {
+ u_char type;
+ u_char layer_num;
+ struct l_dvd_layer layer[4];
+};
+
+struct l_dvd_copyright {
+ u_char type;
+ u_char layer_num;
+ u_char cpst;
+ u_char rmi;
+};
+
+struct l_dvd_disckey {
+ u_char type;
+ l_uint agid:2;
+ u_char value[2048];
+};
+
+struct l_dvd_bca {
+ u_char type;
+ l_int len;
+ u_char value[188];
+};
+
+struct l_dvd_manufact {
+ u_char type;
+ u_char layer_num;
+ l_int len;
+ u_char value[2048];
+};
+
+typedef union {
+ u_char type;
+ struct l_dvd_physical physical;
+ struct l_dvd_copyright copyright;
+ struct l_dvd_disckey disckey;
+ struct l_dvd_bca bca;
+ struct l_dvd_manufact manufact;
+} l_dvd_struct;
+
+typedef u_char l_dvd_key[5];
+typedef u_char l_dvd_challenge[10];
+
+struct l_dvd_lu_send_agid {
+ u_char type;
+ l_uint agid:2;
+};
+
+struct l_dvd_host_send_challenge {
+ u_char type;
+ l_uint agid:2;
+ l_dvd_challenge chal;
+};
+
+struct l_dvd_send_key {
+ u_char type;
+ l_uint agid:2;
+ l_dvd_key key;
+};
+
+struct l_dvd_lu_send_challenge {
+ u_char type;
+ l_uint agid:2;
+ l_dvd_challenge chal;
+};
+
+struct l_dvd_lu_send_title_key {
+ u_char type;
+ l_uint agid:2;
+ l_dvd_key title_key;
+ l_int lba;
+ l_uint cpm:1;
+ l_uint cp_sec:1;
+ l_uint cgms:2;
+};
+
+struct l_dvd_lu_send_asf {
+ u_char type;
+ l_uint agid:2;
+ l_uint asf:1;
+};
+
+struct l_dvd_host_send_rpcstate {
+ u_char type;
+ u_char pdrc;
+};
+
+struct l_dvd_lu_send_rpcstate {
+ u_char type:2;
+ u_char vra:3;
+ u_char ucca:3;
+ u_char region_mask;
+ u_char rpc_scheme;
+};
+
+typedef union {
+ u_char type;
+ struct l_dvd_lu_send_agid lsa;
+ struct l_dvd_host_send_challenge hsc;
+ struct l_dvd_send_key lsk;
+ struct l_dvd_lu_send_challenge lsc;
+ struct l_dvd_send_key hsk;
+ struct l_dvd_lu_send_title_key lstk;
+ struct l_dvd_lu_send_asf lsasf;
+ struct l_dvd_host_send_rpcstate hrpcs;
+ struct l_dvd_lu_send_rpcstate lrpcs;
+} l_dvd_authinfo;
+
+static void
+bsd_to_linux_msf_lba(u_char af, union msf_lba *bp, union linux_cdrom_addr *lp)
+{
+ if (af == CD_LBA_FORMAT)
+ lp->lba = bp->lba;
+ else {
+ lp->msf.minute = bp->msf.minute;
+ lp->msf.second = bp->msf.second;
+ lp->msf.frame = bp->msf.frame;
+ }
+}
+
+static void
+set_linux_cdrom_addr(union linux_cdrom_addr *addr, int format, int lba)
+{
+ if (format == LINUX_CDROM_MSF) {
+ addr->msf.frame = lba % 75;
+ lba /= 75;
+ lba += 2;
+ addr->msf.second = lba % 60;
+ addr->msf.minute = lba / 60;
+ } else
+ addr->lba = lba;
+}
+
+static int
+linux_to_bsd_dvd_struct(l_dvd_struct *lp, struct dvd_struct *bp)
+{
+ bp->format = lp->type;
+ switch (bp->format) {
+ case DVD_STRUCT_PHYSICAL:
+ if (bp->layer_num >= 4)
+ return (EINVAL);
+ bp->layer_num = lp->physical.layer_num;
+ break;
+ case DVD_STRUCT_COPYRIGHT:
+ bp->layer_num = lp->copyright.layer_num;
+ break;
+ case DVD_STRUCT_DISCKEY:
+ bp->agid = lp->disckey.agid;
+ break;
+ case DVD_STRUCT_BCA:
+ case DVD_STRUCT_MANUFACT:
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+bsd_to_linux_dvd_struct(struct dvd_struct *bp, l_dvd_struct *lp)
+{
+ switch (bp->format) {
+ case DVD_STRUCT_PHYSICAL: {
+ struct dvd_layer *blp = (struct dvd_layer *)bp->data;
+ struct l_dvd_layer *llp = &lp->physical.layer[bp->layer_num];
+ memset(llp, 0, sizeof(*llp));
+ llp->book_version = blp->book_version;
+ llp->book_type = blp->book_type;
+ llp->min_rate = blp->max_rate;
+ llp->disc_size = blp->disc_size;
+ llp->layer_type = blp->layer_type;
+ llp->track_path = blp->track_path;
+ llp->nlayers = blp->nlayers;
+ llp->track_density = blp->track_density;
+ llp->linear_density = blp->linear_density;
+ llp->bca = blp->bca;
+ llp->start_sector = blp->start_sector;
+ llp->end_sector = blp->end_sector;
+ llp->end_sector_l0 = blp->end_sector_l0;
+ break;
+ }
+ case DVD_STRUCT_COPYRIGHT:
+ lp->copyright.cpst = bp->cpst;
+ lp->copyright.rmi = bp->rmi;
+ break;
+ case DVD_STRUCT_DISCKEY:
+ memcpy(lp->disckey.value, bp->data, sizeof(lp->disckey.value));
+ break;
+ case DVD_STRUCT_BCA:
+ lp->bca.len = bp->length;
+ memcpy(lp->bca.value, bp->data, sizeof(lp->bca.value));
+ break;
+ case DVD_STRUCT_MANUFACT:
+ lp->manufact.len = bp->length;
+ memcpy(lp->manufact.value, bp->data,
+ sizeof(lp->manufact.value));
+ /* lp->manufact.layer_num is unused in linux (redhat 7.0) */
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+linux_to_bsd_dvd_authinfo(l_dvd_authinfo *lp, int *bcode,
+ struct dvd_authinfo *bp)
+{
+ switch (lp->type) {
+ case LINUX_DVD_LU_SEND_AGID:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_AGID;
+ bp->agid = lp->lsa.agid;
+ break;
+ case LINUX_DVD_HOST_SEND_CHALLENGE:
+ *bcode = DVDIOCSENDKEY;
+ bp->format = DVD_SEND_CHALLENGE;
+ bp->agid = lp->hsc.agid;
+ memcpy(bp->keychal, lp->hsc.chal, 10);
+ break;
+ case LINUX_DVD_LU_SEND_KEY1:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_KEY1;
+ bp->agid = lp->lsk.agid;
+ break;
+ case LINUX_DVD_LU_SEND_CHALLENGE:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_CHALLENGE;
+ bp->agid = lp->lsc.agid;
+ break;
+ case LINUX_DVD_HOST_SEND_KEY2:
+ *bcode = DVDIOCSENDKEY;
+ bp->format = DVD_SEND_KEY2;
+ bp->agid = lp->hsk.agid;
+ memcpy(bp->keychal, lp->hsk.key, 5);
+ break;
+ case LINUX_DVD_LU_SEND_TITLE_KEY:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_TITLE_KEY;
+ bp->agid = lp->lstk.agid;
+ bp->lba = lp->lstk.lba;
+ break;
+ case LINUX_DVD_LU_SEND_ASF:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_ASF;
+ bp->agid = lp->lsasf.agid;
+ break;
+ case LINUX_DVD_INVALIDATE_AGID:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_INVALIDATE_AGID;
+ bp->agid = lp->lsa.agid;
+ break;
+ case LINUX_DVD_LU_SEND_RPC_STATE:
+ *bcode = DVDIOCREPORTKEY;
+ bp->format = DVD_REPORT_RPC;
+ break;
+ case LINUX_DVD_HOST_SEND_RPC_STATE:
+ *bcode = DVDIOCSENDKEY;
+ bp->format = DVD_SEND_RPC;
+ bp->region = lp->hrpcs.pdrc;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+bsd_to_linux_dvd_authinfo(struct dvd_authinfo *bp, l_dvd_authinfo *lp)
+{
+ switch (lp->type) {
+ case LINUX_DVD_LU_SEND_AGID:
+ lp->lsa.agid = bp->agid;
+ break;
+ case LINUX_DVD_HOST_SEND_CHALLENGE:
+ lp->type = LINUX_DVD_LU_SEND_KEY1;
+ break;
+ case LINUX_DVD_LU_SEND_KEY1:
+ memcpy(lp->lsk.key, bp->keychal, sizeof(lp->lsk.key));
+ break;
+ case LINUX_DVD_LU_SEND_CHALLENGE:
+ memcpy(lp->lsc.chal, bp->keychal, sizeof(lp->lsc.chal));
+ break;
+ case LINUX_DVD_HOST_SEND_KEY2:
+ lp->type = LINUX_DVD_AUTH_ESTABLISHED;
+ break;
+ case LINUX_DVD_LU_SEND_TITLE_KEY:
+ memcpy(lp->lstk.title_key, bp->keychal,
+ sizeof(lp->lstk.title_key));
+ lp->lstk.cpm = bp->cpm;
+ lp->lstk.cp_sec = bp->cp_sec;
+ lp->lstk.cgms = bp->cgms;
+ break;
+ case LINUX_DVD_LU_SEND_ASF:
+ lp->lsasf.asf = bp->asf;
+ break;
+ case LINUX_DVD_INVALIDATE_AGID:
+ break;
+ case LINUX_DVD_LU_SEND_RPC_STATE:
+ lp->lrpcs.type = bp->reg_type;
+ lp->lrpcs.vra = bp->vend_rsts;
+ lp->lrpcs.ucca = bp->user_rsts;
+ lp->lrpcs.region_mask = bp->region;
+ lp->lrpcs.rpc_scheme = bp->rpc_scheme;
+ break;
+ case LINUX_DVD_HOST_SEND_RPC_STATE:
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+linux_ioctl_cdrom(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_CDROMPAUSE:
+ args->cmd = CDIOCPAUSE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMRESUME:
+ args->cmd = CDIOCRESUME;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMPLAYMSF:
+ args->cmd = CDIOCPLAYMSF;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMPLAYTRKIND:
+ args->cmd = CDIOCPLAYTRACKS;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMREADTOCHDR: {
+ struct ioc_toc_header th;
+ struct linux_cdrom_tochdr lth;
+ error = fo_ioctl(fp, CDIOREADTOCHEADER, (caddr_t)&th,
+ td->td_ucred, td);
+ if (!error) {
+ lth.cdth_trk0 = th.starting_track;
+ lth.cdth_trk1 = th.ending_track;
+ copyout(&lth, (void *)args->arg, sizeof(lth));
+ }
+ break;
+ }
+
+ case LINUX_CDROMREADTOCENTRY: {
+ struct linux_cdrom_tocentry lte;
+ struct ioc_read_toc_single_entry irtse;
+
+ error = copyin((void *)args->arg, &lte, sizeof(lte));
+ if (error)
+ break;
+ irtse.address_format = lte.cdte_format;
+ irtse.track = lte.cdte_track;
+ error = fo_ioctl(fp, CDIOREADTOCENTRY, (caddr_t)&irtse,
+ td->td_ucred, td);
+ if (!error) {
+ lte.cdte_ctrl = irtse.entry.control;
+ lte.cdte_adr = irtse.entry.addr_type;
+ bsd_to_linux_msf_lba(irtse.address_format,
+ &irtse.entry.addr, &lte.cdte_addr);
+ error = copyout(&lte, (void *)args->arg, sizeof(lte));
+ }
+ break;
+ }
+
+ case LINUX_CDROMSTOP:
+ args->cmd = CDIOCSTOP;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMSTART:
+ args->cmd = CDIOCSTART;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_CDROMEJECT:
+ args->cmd = CDIOCEJECT;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* LINUX_CDROMVOLCTRL */
+
+ case LINUX_CDROMSUBCHNL: {
+ struct linux_cdrom_subchnl sc;
+ struct ioc_read_subchannel bsdsc;
+ struct cd_sub_channel_info bsdinfo;
+
+ bsdsc.address_format = CD_LBA_FORMAT;
+ bsdsc.data_format = CD_CURRENT_POSITION;
+ bsdsc.track = 0;
+ bsdsc.data_len = sizeof(bsdinfo);
+ bsdsc.data = &bsdinfo;
+ error = fo_ioctl(fp, CDIOCREADSUBCHANNEL_SYSSPACE,
+ (caddr_t)&bsdsc, td->td_ucred, td);
+ if (error)
+ break;
+ error = copyin((void *)args->arg, &sc, sizeof(sc));
+ if (error)
+ break;
+ sc.cdsc_audiostatus = bsdinfo.header.audio_status;
+ sc.cdsc_adr = bsdinfo.what.position.addr_type;
+ sc.cdsc_ctrl = bsdinfo.what.position.control;
+ sc.cdsc_trk = bsdinfo.what.position.track_number;
+ sc.cdsc_ind = bsdinfo.what.position.index_number;
+ set_linux_cdrom_addr(&sc.cdsc_absaddr, sc.cdsc_format,
+ bsdinfo.what.position.absaddr.lba);
+ set_linux_cdrom_addr(&sc.cdsc_reladdr, sc.cdsc_format,
+ bsdinfo.what.position.reladdr.lba);
+ error = copyout(&sc, (void *)args->arg, sizeof(sc));
+ break;
+ }
+
+ /* LINUX_CDROMREADMODE2 */
+ /* LINUX_CDROMREADMODE1 */
+ /* LINUX_CDROMREADAUDIO */
+ /* LINUX_CDROMEJECT_SW */
+ /* LINUX_CDROMMULTISESSION */
+ /* LINUX_CDROM_GET_UPC */
+
+ case LINUX_CDROMRESET:
+ args->cmd = CDIOCRESET;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* LINUX_CDROMVOLREAD */
+ /* LINUX_CDROMREADRAW */
+ /* LINUX_CDROMREADCOOKED */
+ /* LINUX_CDROMSEEK */
+ /* LINUX_CDROMPLAYBLK */
+ /* LINUX_CDROMREADALL */
+ /* LINUX_CDROMCLOSETRAY */
+ /* LINUX_CDROMLOADFROMSLOT */
+ /* LINUX_CDROMGETSPINDOWN */
+ /* LINUX_CDROMSETSPINDOWN */
+ /* LINUX_CDROM_SET_OPTIONS */
+ /* LINUX_CDROM_CLEAR_OPTIONS */
+ /* LINUX_CDROM_SELECT_SPEED */
+ /* LINUX_CDROM_SELECT_DISC */
+ /* LINUX_CDROM_MEDIA_CHANGED */
+ /* LINUX_CDROM_DRIVE_STATUS */
+ /* LINUX_CDROM_DISC_STATUS */
+ /* LINUX_CDROM_CHANGER_NSLOTS */
+ /* LINUX_CDROM_LOCKDOOR */
+ /* LINUX_CDROM_DEBUG */
+ /* LINUX_CDROM_GET_CAPABILITY */
+ /* LINUX_CDROMAUDIOBUFSIZ */
+
+ case LINUX_DVD_READ_STRUCT: {
+ l_dvd_struct *lds;
+ struct dvd_struct *bds;
+
+ lds = malloc(sizeof(*lds), M_LINUX, M_WAITOK);
+ bds = malloc(sizeof(*bds), M_LINUX, M_WAITOK);
+ error = copyin((void *)args->arg, lds, sizeof(*lds));
+ if (error)
+ goto out;
+ error = linux_to_bsd_dvd_struct(lds, bds);
+ if (error)
+ goto out;
+ error = fo_ioctl(fp, DVDIOCREADSTRUCTURE, (caddr_t)bds,
+ td->td_ucred, td);
+ if (error)
+ goto out;
+ error = bsd_to_linux_dvd_struct(bds, lds);
+ if (error)
+ goto out;
+ error = copyout(lds, (void *)args->arg, sizeof(*lds));
+ out:
+ free(bds, M_LINUX);
+ free(lds, M_LINUX);
+ break;
+ }
+
+ /* LINUX_DVD_WRITE_STRUCT */
+
+ case LINUX_DVD_AUTH: {
+ l_dvd_authinfo lda;
+ struct dvd_authinfo bda;
+ int bcode;
+
+ error = copyin((void *)args->arg, &lda, sizeof(lda));
+ if (error)
+ break;
+ error = linux_to_bsd_dvd_authinfo(&lda, &bcode, &bda);
+ if (error)
+ break;
+ error = fo_ioctl(fp, bcode, (caddr_t)&bda, td->td_ucred,
+ td);
+ if (error) {
+ if (lda.type == LINUX_DVD_HOST_SEND_KEY2) {
+ lda.type = LINUX_DVD_AUTH_FAILURE;
+ copyout(&lda, (void *)args->arg, sizeof(lda));
+ }
+ break;
+ }
+ error = bsd_to_linux_dvd_authinfo(&bda, &lda);
+ if (error)
+ break;
+ error = copyout(&lda, (void *)args->arg, sizeof(lda));
+ break;
+ }
+
+ case LINUX_SCSI_GET_BUS_NUMBER:
+ case LINUX_SCSI_GET_IDLUN:
+ error = linux_ioctl_sg(td, args);
+ break;
+
+ /* LINUX_CDROM_SEND_PACKET */
+ /* LINUX_CDROM_NEXT_WRITABLE */
+ /* LINUX_CDROM_LAST_WRITTEN */
+
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+ fdrop(fp, td);
+ return (error);
+}
+
+static int
+linux_ioctl_vfat(struct thread *td, struct linux_ioctl_args *args)
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * Sound related ioctls
+ */
+
+struct linux_mixer_info {
+ char id[16];
+ char name[32];
+ int modify_counter;
+ int fillers[10];
+};
+
+struct linux_old_mixer_info {
+ char id[16];
+ char name[32];
+};
+
+static u_int32_t dirbits[4] = { IOC_VOID, IOC_IN, IOC_OUT, IOC_INOUT };
+
+#define SETDIR(c) (((c) & ~IOC_DIRMASK) | dirbits[args->cmd >> 30])
+
+static int
+linux_ioctl_sound(struct thread *td, struct linux_ioctl_args *args)
+{
+
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_SOUND_MIXER_WRITE_VOLUME:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_VOLUME);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_BASS:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_BASS);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_TREBLE:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_TREBLE);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_SYNTH:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_SYNTH);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_PCM:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_PCM);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_SPEAKER:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_SPEAKER);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_LINE:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_MIC:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_MIC);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_CD:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_CD);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_IMIX:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_IMIX);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_ALTPCM:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_ALTPCM);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_RECLEV:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_RECLEV);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_IGAIN:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_IGAIN);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_OGAIN:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_OGAIN);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_LINE1:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE1);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_LINE2:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE2);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_LINE3:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE3);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_INFO: {
+ /* Key on encoded length */
+ switch ((args->cmd >> 16) & 0x1fff) {
+ case 0x005c: { /* SOUND_MIXER_INFO */
+ struct linux_mixer_info info;
+ bzero(&info, sizeof(info));
+ strncpy(info.id, "OSS", sizeof(info.id) - 1);
+ strncpy(info.name, "FreeBSD OSS Mixer", sizeof(info.name) - 1);
+ copyout(&info, (void *)args->arg, sizeof(info));
+ return (0);
+ }
+ case 0x0030: { /* SOUND_OLD_MIXER_INFO */
+ struct linux_old_mixer_info info;
+ bzero(&info, sizeof(info));
+ strncpy(info.id, "OSS", sizeof(info.id) - 1);
+ strncpy(info.name, "FreeBSD OSS Mixer", sizeof(info.name) - 1);
+ copyout(&info, (void *)args->arg, sizeof(info));
+ return (0);
+ }
+ default:
+ return (ENOIOCTL);
+ }
+ break;
+ }
+
+ case LINUX_OSS_GETVERSION: {
+ int version = linux_get_oss_version(td);
+ return (copyout(&version, (void *)args->arg, sizeof(int)));
+ }
+
+ case LINUX_SOUND_MIXER_READ_STEREODEVS:
+ args->cmd = SOUND_MIXER_READ_STEREODEVS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_READ_CAPS:
+ args->cmd = SOUND_MIXER_READ_CAPS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_READ_RECMASK:
+ args->cmd = SOUND_MIXER_READ_RECMASK;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_READ_DEVMASK:
+ args->cmd = SOUND_MIXER_READ_DEVMASK;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_MIXER_WRITE_RECSRC:
+ args->cmd = SETDIR(SOUND_MIXER_WRITE_RECSRC);
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_RESET:
+ args->cmd = SNDCTL_DSP_RESET;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SYNC:
+ args->cmd = SNDCTL_DSP_SYNC;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SPEED:
+ args->cmd = SNDCTL_DSP_SPEED;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_STEREO:
+ args->cmd = SNDCTL_DSP_STEREO;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETBLKSIZE: /* LINUX_SNDCTL_DSP_SETBLKSIZE */
+ args->cmd = SNDCTL_DSP_GETBLKSIZE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SETFMT:
+ args->cmd = SNDCTL_DSP_SETFMT;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_PCM_WRITE_CHANNELS:
+ args->cmd = SOUND_PCM_WRITE_CHANNELS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SOUND_PCM_WRITE_FILTER:
+ args->cmd = SOUND_PCM_WRITE_FILTER;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_POST:
+ args->cmd = SNDCTL_DSP_POST;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SUBDIVIDE:
+ args->cmd = SNDCTL_DSP_SUBDIVIDE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SETFRAGMENT:
+ args->cmd = SNDCTL_DSP_SETFRAGMENT;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETFMTS:
+ args->cmd = SNDCTL_DSP_GETFMTS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETOSPACE:
+ args->cmd = SNDCTL_DSP_GETOSPACE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETISPACE:
+ args->cmd = SNDCTL_DSP_GETISPACE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_NONBLOCK:
+ args->cmd = SNDCTL_DSP_NONBLOCK;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETCAPS:
+ args->cmd = SNDCTL_DSP_GETCAPS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SETTRIGGER: /* LINUX_SNDCTL_GETTRIGGER */
+ args->cmd = SNDCTL_DSP_SETTRIGGER;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETIPTR:
+ args->cmd = SNDCTL_DSP_GETIPTR;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETOPTR:
+ args->cmd = SNDCTL_DSP_GETOPTR;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_SETDUPLEX:
+ args->cmd = SNDCTL_DSP_SETDUPLEX;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_DSP_GETODELAY:
+ args->cmd = SNDCTL_DSP_GETODELAY;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_RESET:
+ args->cmd = SNDCTL_SEQ_RESET;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_SYNC:
+ args->cmd = SNDCTL_SEQ_SYNC;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SYNTH_INFO:
+ args->cmd = SNDCTL_SYNTH_INFO;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_CTRLRATE:
+ args->cmd = SNDCTL_SEQ_CTRLRATE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_GETOUTCOUNT:
+ args->cmd = SNDCTL_SEQ_GETOUTCOUNT;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_GETINCOUNT:
+ args->cmd = SNDCTL_SEQ_GETINCOUNT;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_PERCMODE:
+ args->cmd = SNDCTL_SEQ_PERCMODE;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_FM_LOAD_INSTR:
+ args->cmd = SNDCTL_FM_LOAD_INSTR;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_TESTMIDI:
+ args->cmd = SNDCTL_SEQ_TESTMIDI;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_RESETSAMPLES:
+ args->cmd = SNDCTL_SEQ_RESETSAMPLES;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_NRSYNTHS:
+ args->cmd = SNDCTL_SEQ_NRSYNTHS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_NRMIDIS:
+ args->cmd = SNDCTL_SEQ_NRMIDIS;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_MIDI_INFO:
+ args->cmd = SNDCTL_MIDI_INFO;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SEQ_TRESHOLD:
+ args->cmd = SNDCTL_SEQ_TRESHOLD;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ case LINUX_SNDCTL_SYNTH_MEMAVL:
+ args->cmd = SNDCTL_SYNTH_MEMAVL;
+ return (ioctl(td, (struct ioctl_args *)args));
+
+ }
+
+ return (ENOIOCTL);
+}
+
+/*
+ * Console related ioctls
+ */
+
+#define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG)
+
+static int
+linux_ioctl_console(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_KIOCSOUND:
+ args->cmd = KIOCSOUND;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDMKTONE:
+ args->cmd = KDMKTONE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDGETLED:
+ args->cmd = KDGETLED;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDSETLED:
+ args->cmd = KDSETLED;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDSETMODE:
+ args->cmd = KDSETMODE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDGETMODE:
+ args->cmd = KDGETMODE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDGKBMODE:
+ args->cmd = KDGKBMODE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_KDSKBMODE: {
+ int kbdmode;
+ switch (args->arg) {
+ case LINUX_KBD_RAW:
+ kbdmode = K_RAW;
+ break;
+ case LINUX_KBD_XLATE:
+ kbdmode = K_XLATE;
+ break;
+ case LINUX_KBD_MEDIUMRAW:
+ kbdmode = K_RAW;
+ break;
+ default:
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+ error = (fo_ioctl(fp, KDSKBMODE, (caddr_t)&kbdmode,
+ td->td_ucred, td));
+ break;
+ }
+
+ case LINUX_VT_OPENQRY:
+ args->cmd = VT_OPENQRY;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_VT_GETMODE:
+ args->cmd = VT_GETMODE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_VT_SETMODE: {
+ struct vt_mode mode;
+ if ((error = copyin((void *)args->arg, &mode, sizeof(mode))))
+ break;
+ if (!ISSIGVALID(mode.frsig) && ISSIGVALID(mode.acqsig))
+ mode.frsig = mode.acqsig;
+ if ((error = copyout(&mode, (void *)args->arg, sizeof(mode))))
+ break;
+ args->cmd = VT_SETMODE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+ }
+
+ case LINUX_VT_GETSTATE:
+ args->cmd = VT_GETACTIVE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_VT_RELDISP:
+ args->cmd = VT_RELDISP;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_VT_ACTIVATE:
+ args->cmd = VT_ACTIVATE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case LINUX_VT_WAITACTIVE:
+ args->cmd = VT_WAITACTIVE;
+ error = (ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+ fdrop(fp, td);
+ return (error);
+}
+
+/*
+ * Criteria for interface name translation
+ */
+#define IFP_IS_ETH(ifp) (ifp->if_type == IFT_ETHER)
+
+/*
+ * Interface function used by linprocfs (at the time of writing). It's not
+ * used by the Linuxulator itself.
+ */
+int
+linux_ifname(struct ifnet *ifp, char *buffer, size_t buflen)
+{
+ struct ifnet *ifscan;
+ int ethno;
+
+ IFNET_RLOCK_ASSERT();
+
+ /* Short-circuit non ethernet interfaces */
+ if (!IFP_IS_ETH(ifp))
+ return (strlcpy(buffer, ifp->if_xname, buflen));
+
+ /* Determine the (relative) unit number for ethernet interfaces */
+ ethno = 0;
+ TAILQ_FOREACH(ifscan, &V_ifnet, if_link) {
+ if (ifscan == ifp)
+ return (snprintf(buffer, buflen, "eth%d", ethno));
+ if (IFP_IS_ETH(ifscan))
+ ethno++;
+ }
+
+ return (0);
+}
+
+/*
+ * Translate a Linux interface name to a FreeBSD interface name,
+ * and return the associated ifnet structure
+ * bsdname and lxname need to be least IFNAMSIZ bytes long, but
+ * can point to the same buffer.
+ */
+
+static struct ifnet *
+ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
+{
+ struct ifnet *ifp;
+ int len, unit;
+ char *ep;
+ int is_eth, index;
+
+ for (len = 0; len < LINUX_IFNAMSIZ; ++len)
+ if (!isalpha(lxname[len]))
+ break;
+ if (len == 0 || len == LINUX_IFNAMSIZ)
+ return (NULL);
+ unit = (int)strtoul(lxname + len, &ep, 10);
+ if (ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ)
+ return (NULL);
+ index = 0;
+ is_eth = (len == 3 && !strncmp(lxname, "eth", len)) ? 1 : 0;
+ CURVNET_SET(TD_TO_VNET(td));
+ IFNET_RLOCK();
+ TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+ /*
+ * Allow Linux programs to use FreeBSD names. Don't presume
+ * we never have an interface named "eth", so don't make
+ * the test optional based on is_eth.
+ */
+ if (strncmp(ifp->if_xname, lxname, LINUX_IFNAMSIZ) == 0)
+ break;
+ if (is_eth && IFP_IS_ETH(ifp) && unit == index++)
+ break;
+ }
+ IFNET_RUNLOCK();
+ CURVNET_RESTORE();
+ if (ifp != NULL)
+ strlcpy(bsdname, ifp->if_xname, IFNAMSIZ);
+ return (ifp);
+}
+
+/*
+ * Implement the SIOCGIFCONF ioctl
+ */
+
+static int
+linux_ifconf(struct thread *td, struct ifconf *uifc)
+{
+#ifdef COMPAT_LINUX32
+ struct l_ifconf ifc;
+#else
+ struct ifconf ifc;
+#endif
+ struct l_ifreq ifr;
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sbuf *sb;
+ int error, ethno, full = 0, valid_len, max_len;
+
+ error = copyin(uifc, &ifc, sizeof(ifc));
+ if (error != 0)
+ return (error);
+
+ max_len = MAXPHYS - 1;
+
+ CURVNET_SET(TD_TO_VNET(td));
+ /* handle the 'request buffer size' case */
+ if (ifc.ifc_buf == PTROUT(NULL)) {
+ ifc.ifc_len = 0;
+ IFNET_RLOCK();
+ TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ struct sockaddr *sa = ifa->ifa_addr;
+ if (sa->sa_family == AF_INET)
+ ifc.ifc_len += sizeof(ifr);
+ }
+ }
+ IFNET_RUNLOCK();
+ error = copyout(&ifc, uifc, sizeof(ifc));
+ CURVNET_RESTORE();
+ return (error);
+ }
+
+ if (ifc.ifc_len <= 0) {
+ CURVNET_RESTORE();
+ return (EINVAL);
+ }
+
+again:
+ /* Keep track of eth interfaces */
+ ethno = 0;
+ if (ifc.ifc_len <= max_len) {
+ max_len = ifc.ifc_len;
+ full = 1;
+ }
+ sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN);
+ max_len = 0;
+ valid_len = 0;
+
+ /* Return all AF_INET addresses of all interfaces */
+ IFNET_RLOCK();
+ TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+ int addrs = 0;
+
+ bzero(&ifr, sizeof(ifr));
+ if (IFP_IS_ETH(ifp))
+ snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d",
+ ethno++);
+ else
+ strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ);
+
+ /* Walk the address list */
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ struct sockaddr *sa = ifa->ifa_addr;
+
+ if (sa->sa_family == AF_INET) {
+ ifr.ifr_addr.sa_family = LINUX_AF_INET;
+ memcpy(ifr.ifr_addr.sa_data, sa->sa_data,
+ sizeof(ifr.ifr_addr.sa_data));
+ sbuf_bcat(sb, &ifr, sizeof(ifr));
+ max_len += sizeof(ifr);
+ addrs++;
+ }
+
+ if (sbuf_error(sb) == 0)
+ valid_len = sbuf_len(sb);
+ }
+ if (addrs == 0) {
+ bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ sbuf_bcat(sb, &ifr, sizeof(ifr));
+ max_len += sizeof(ifr);
+
+ if (sbuf_error(sb) == 0)
+ valid_len = sbuf_len(sb);
+ }
+ }
+ IFNET_RUNLOCK();
+
+ if (valid_len != max_len && !full) {
+ sbuf_delete(sb);
+ goto again;
+ }
+
+ ifc.ifc_len = valid_len;
+ sbuf_finish(sb);
+ memcpy(PTRIN(ifc.ifc_buf), sbuf_data(sb), ifc.ifc_len);
+ error = copyout(&ifc, uifc, sizeof(ifc));
+ sbuf_delete(sb);
+ CURVNET_RESTORE();
+
+ return (error);
+}
+
+static int
+linux_gifflags(struct thread *td, struct ifnet *ifp, struct l_ifreq *ifr)
+{
+ l_short flags;
+
+ flags = (ifp->if_flags | ifp->if_drv_flags) & 0xffff;
+ /* these flags have no Linux equivalent */
+ flags &= ~(IFF_SMART|IFF_DRV_OACTIVE|IFF_SIMPLEX|
+ IFF_LINK0|IFF_LINK1|IFF_LINK2);
+ /* Linux' multicast flag is in a different bit */
+ if (flags & IFF_MULTICAST) {
+ flags &= ~IFF_MULTICAST;
+ flags |= 0x1000;
+ }
+
+ return (copyout(&flags, &ifr->ifr_flags, sizeof(flags)));
+}
+
+#define ARPHRD_ETHER 1
+#define ARPHRD_LOOPBACK 772
+
+static int
+linux_gifhwaddr(struct ifnet *ifp, struct l_ifreq *ifr)
+{
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ struct l_sockaddr lsa;
+
+ if (ifp->if_type == IFT_LOOP) {
+ bzero(&lsa, sizeof(lsa));
+ lsa.sa_family = ARPHRD_LOOPBACK;
+ return (copyout(&lsa, &ifr->ifr_hwaddr, sizeof(lsa)));
+ }
+
+ if (ifp->if_type != IFT_ETHER)
+ return (ENOENT);
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ sdl = (struct sockaddr_dl*)ifa->ifa_addr;
+ if (sdl != NULL && (sdl->sdl_family == AF_LINK) &&
+ (sdl->sdl_type == IFT_ETHER)) {
+ bzero(&lsa, sizeof(lsa));
+ lsa.sa_family = ARPHRD_ETHER;
+ bcopy(LLADDR(sdl), lsa.sa_data, LINUX_IFHWADDRLEN);
+ return (copyout(&lsa, &ifr->ifr_hwaddr, sizeof(lsa)));
+ }
+ }
+
+ return (ENOENT);
+}
+
+
+ /*
+* If we fault in bsd_to_linux_ifreq() then we will fault when we call
+* the native ioctl(). Thus, we don't really need to check the return
+* value of this function.
+*/
+static int
+bsd_to_linux_ifreq(struct ifreq *arg)
+{
+ struct ifreq ifr;
+ size_t ifr_len = sizeof(struct ifreq);
+ int error;
+
+ if ((error = copyin(arg, &ifr, ifr_len)))
+ return (error);
+
+ *(u_short *)&ifr.ifr_addr = ifr.ifr_addr.sa_family;
+
+ error = copyout(&ifr, arg, ifr_len);
+
+ return (error);
+}
+
+/*
+ * Socket related ioctls
+ */
+
+static int
+linux_ioctl_socket(struct thread *td, struct linux_ioctl_args *args)
+{
+ char lifname[LINUX_IFNAMSIZ], ifname[IFNAMSIZ];
+ struct ifnet *ifp;
+ struct file *fp;
+ int error, type;
+
+ ifp = NULL;
+ error = 0;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ type = fp->f_type;
+ fdrop(fp, td);
+ if (type != DTYPE_SOCKET) {
+ /* not a socket - probably a tap / vmnet device */
+ switch (args->cmd) {
+ case LINUX_SIOCGIFADDR:
+ case LINUX_SIOCSIFADDR:
+ case LINUX_SIOCGIFFLAGS:
+ return (linux_ioctl_special(td, args));
+ default:
+ return (ENOIOCTL);
+ }
+ }
+
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_FIOGETOWN:
+ case LINUX_FIOSETOWN:
+ case LINUX_SIOCADDMULTI:
+ case LINUX_SIOCATMARK:
+ case LINUX_SIOCDELMULTI:
+ case LINUX_SIOCGIFCONF:
+ case LINUX_SIOCGPGRP:
+ case LINUX_SIOCSPGRP:
+ case LINUX_SIOCGIFCOUNT:
+ /* these ioctls don't take an interface name */
+#ifdef DEBUG
+ printf("%s(): ioctl %d\n", __func__,
+ args->cmd & 0xffff);
+#endif
+ break;
+
+ case LINUX_SIOCGIFFLAGS:
+ case LINUX_SIOCGIFADDR:
+ case LINUX_SIOCSIFADDR:
+ case LINUX_SIOCGIFDSTADDR:
+ case LINUX_SIOCGIFBRDADDR:
+ case LINUX_SIOCGIFNETMASK:
+ case LINUX_SIOCSIFNETMASK:
+ case LINUX_SIOCGIFMTU:
+ case LINUX_SIOCSIFMTU:
+ case LINUX_SIOCSIFNAME:
+ case LINUX_SIOCGIFHWADDR:
+ case LINUX_SIOCSIFHWADDR:
+ case LINUX_SIOCDEVPRIVATE:
+ case LINUX_SIOCDEVPRIVATE+1:
+ case LINUX_SIOCGIFINDEX:
+ /* copy in the interface name and translate it. */
+ error = copyin((void *)args->arg, lifname, LINUX_IFNAMSIZ);
+ if (error != 0)
+ return (error);
+#ifdef DEBUG
+ printf("%s(): ioctl %d on %.*s\n", __func__,
+ args->cmd & 0xffff, LINUX_IFNAMSIZ, lifname);
+#endif
+ ifp = ifname_linux_to_bsd(td, lifname, ifname);
+ if (ifp == NULL)
+ return (EINVAL);
+ /*
+ * We need to copy it back out in case we pass the
+ * request on to our native ioctl(), which will expect
+ * the ifreq to be in user space and have the correct
+ * interface name.
+ */
+ error = copyout(ifname, (void *)args->arg, IFNAMSIZ);
+ if (error != 0)
+ return (error);
+#ifdef DEBUG
+ printf("%s(): %s translated to %s\n", __func__,
+ lifname, ifname);
+#endif
+ break;
+
+ default:
+ return (ENOIOCTL);
+ }
+
+ switch (args->cmd & 0xffff) {
+
+ case LINUX_FIOSETOWN:
+ args->cmd = FIOSETOWN;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCSPGRP:
+ args->cmd = SIOCSPGRP;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_FIOGETOWN:
+ args->cmd = FIOGETOWN;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCGPGRP:
+ args->cmd = SIOCGPGRP;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCATMARK:
+ args->cmd = SIOCATMARK;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ /* LINUX_SIOCGSTAMP */
+
+ case LINUX_SIOCGIFCONF:
+ error = linux_ifconf(td, (struct ifconf *)args->arg);
+ break;
+
+ case LINUX_SIOCGIFFLAGS:
+ args->cmd = SIOCGIFFLAGS;
+ error = linux_gifflags(td, ifp, (struct l_ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCGIFADDR:
+ args->cmd = SIOCGIFADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ bsd_to_linux_ifreq((struct ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCSIFADDR:
+ /* XXX probably doesn't work, included for completeness */
+ args->cmd = SIOCSIFADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCGIFDSTADDR:
+ args->cmd = SIOCGIFDSTADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ bsd_to_linux_ifreq((struct ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCGIFBRDADDR:
+ args->cmd = SIOCGIFBRDADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ bsd_to_linux_ifreq((struct ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCGIFNETMASK:
+ args->cmd = SIOCGIFNETMASK;
+ error = ioctl(td, (struct ioctl_args *)args);
+ bsd_to_linux_ifreq((struct ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCSIFNETMASK:
+ error = ENOIOCTL;
+ break;
+
+ case LINUX_SIOCGIFMTU:
+ args->cmd = SIOCGIFMTU;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCSIFMTU:
+ args->cmd = SIOCSIFMTU;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCSIFNAME:
+ error = ENOIOCTL;
+ break;
+
+ case LINUX_SIOCGIFHWADDR:
+ error = linux_gifhwaddr(ifp, (struct l_ifreq *)args->arg);
+ break;
+
+ case LINUX_SIOCSIFHWADDR:
+ error = ENOIOCTL;
+ break;
+
+ case LINUX_SIOCADDMULTI:
+ args->cmd = SIOCADDMULTI;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCDELMULTI:
+ args->cmd = SIOCDELMULTI;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCGIFINDEX:
+ args->cmd = SIOCGIFINDEX;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCGIFCOUNT:
+ error = 0;
+ break;
+
+ /*
+ * XXX This is slightly bogus, but these ioctls are currently
+ * XXX only used by the aironet (if_an) network driver.
+ */
+ case LINUX_SIOCDEVPRIVATE:
+ args->cmd = SIOCGPRIVATE_0;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+
+ case LINUX_SIOCDEVPRIVATE+1:
+ args->cmd = SIOCGPRIVATE_1;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+ }
+
+ if (ifp != NULL)
+ /* restore the original interface name */
+ copyout(lifname, (void *)args->arg, LINUX_IFNAMSIZ);
+
+#ifdef DEBUG
+ printf("%s(): returning %d\n", __func__, error);
+#endif
+ return (error);
+}
+
+/*
+ * Device private ioctl handler
+ */
+static int
+linux_ioctl_private(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error, type;
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ type = fp->f_type;
+ fdrop(fp, td);
+ if (type == DTYPE_SOCKET)
+ return (linux_ioctl_socket(td, args));
+ return (ENOIOCTL);
+}
+
+/*
+ * DRM ioctl handler (sys/dev/drm)
+ */
+static int
+linux_ioctl_drm(struct thread *td, struct linux_ioctl_args *args)
+{
+ args->cmd = SETDIR(args->cmd);
+ return ioctl(td, (struct ioctl_args *)args);
+}
+
+static int
+linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ u_long cmd;
+ int error;
+
+ if ((error = fget(td, args->fd, &fp)) != 0) {
+ printf("sg_linux_ioctl: fget returned %d\n", error);
+ return (error);
+ }
+ cmd = args->cmd;
+
+ error = (fo_ioctl(fp, cmd, (caddr_t)args->arg, td->td_ucred, td));
+ fdrop(fp, td);
+ return (error);
+}
+
+/*
+ * Video4Linux (V4L) ioctl handler
+ */
+static int
+linux_to_bsd_v4l_tuner(struct l_video_tuner *lvt, struct video_tuner *vt)
+{
+ vt->tuner = lvt->tuner;
+ strlcpy(vt->name, lvt->name, LINUX_VIDEO_TUNER_NAME_SIZE);
+ vt->rangelow = lvt->rangelow; /* possible long size conversion */
+ vt->rangehigh = lvt->rangehigh; /* possible long size conversion */
+ vt->flags = lvt->flags;
+ vt->mode = lvt->mode;
+ vt->signal = lvt->signal;
+ return (0);
+}
+
+static int
+bsd_to_linux_v4l_tuner(struct video_tuner *vt, struct l_video_tuner *lvt)
+{
+ lvt->tuner = vt->tuner;
+ strlcpy(lvt->name, vt->name, LINUX_VIDEO_TUNER_NAME_SIZE);
+ lvt->rangelow = vt->rangelow; /* possible long size conversion */
+ lvt->rangehigh = vt->rangehigh; /* possible long size conversion */
+ lvt->flags = vt->flags;
+ lvt->mode = vt->mode;
+ lvt->signal = vt->signal;
+ return (0);
+}
+
+#ifdef COMPAT_LINUX_V4L_CLIPLIST
+static int
+linux_to_bsd_v4l_clip(struct l_video_clip *lvc, struct video_clip *vc)
+{
+ vc->x = lvc->x;
+ vc->y = lvc->y;
+ vc->width = lvc->width;
+ vc->height = lvc->height;
+ vc->next = PTRIN(lvc->next); /* possible pointer size conversion */
+ return (0);
+}
+#endif
+
+static int
+linux_to_bsd_v4l_window(struct l_video_window *lvw, struct video_window *vw)
+{
+ vw->x = lvw->x;
+ vw->y = lvw->y;
+ vw->width = lvw->width;
+ vw->height = lvw->height;
+ vw->chromakey = lvw->chromakey;
+ vw->flags = lvw->flags;
+ vw->clips = PTRIN(lvw->clips); /* possible pointer size conversion */
+ vw->clipcount = lvw->clipcount;
+ return (0);
+}
+
+static int
+bsd_to_linux_v4l_window(struct video_window *vw, struct l_video_window *lvw)
+{
+ lvw->x = vw->x;
+ lvw->y = vw->y;
+ lvw->width = vw->width;
+ lvw->height = vw->height;
+ lvw->chromakey = vw->chromakey;
+ lvw->flags = vw->flags;
+ lvw->clips = PTROUT(vw->clips); /* possible pointer size conversion */
+ lvw->clipcount = vw->clipcount;
+ return (0);
+}
+
+static int
+linux_to_bsd_v4l_buffer(struct l_video_buffer *lvb, struct video_buffer *vb)
+{
+ vb->base = PTRIN(lvb->base); /* possible pointer size conversion */
+ vb->height = lvb->height;
+ vb->width = lvb->width;
+ vb->depth = lvb->depth;
+ vb->bytesperline = lvb->bytesperline;
+ return (0);
+}
+
+static int
+bsd_to_linux_v4l_buffer(struct video_buffer *vb, struct l_video_buffer *lvb)
+{
+ lvb->base = PTROUT(vb->base); /* possible pointer size conversion */
+ lvb->height = vb->height;
+ lvb->width = vb->width;
+ lvb->depth = vb->depth;
+ lvb->bytesperline = vb->bytesperline;
+ return (0);
+}
+
+static int
+linux_to_bsd_v4l_code(struct l_video_code *lvc, struct video_code *vc)
+{
+ strlcpy(vc->loadwhat, lvc->loadwhat, LINUX_VIDEO_CODE_LOADWHAT_SIZE);
+ vc->datasize = lvc->datasize;
+ vc->data = PTRIN(lvc->data); /* possible pointer size conversion */
+ return (0);
+}
+
+#ifdef COMPAT_LINUX_V4L_CLIPLIST
+static int
+linux_v4l_clip_copy(void *lvc, struct video_clip **ppvc)
+{
+ int error;
+ struct video_clip vclip;
+ struct l_video_clip l_vclip;
+
+ error = copyin(lvc, &l_vclip, sizeof(l_vclip));
+ if (error) return (error);
+ linux_to_bsd_v4l_clip(&l_vclip, &vclip);
+ /* XXX: If there can be no concurrency: s/M_NOWAIT/M_WAITOK/ */
+ if ((*ppvc = malloc(sizeof(**ppvc), M_LINUX, M_NOWAIT)) == NULL)
+ return (ENOMEM); /* XXX: linux has no ENOMEM here */
+ memcpy(*ppvc, &vclip, sizeof(vclip));
+ (*ppvc)->next = NULL;
+ return (0);
+}
+
+static int
+linux_v4l_cliplist_free(struct video_window *vw)
+{
+ struct video_clip **ppvc;
+ struct video_clip **ppvc_next;
+
+ for (ppvc = &(vw->clips); *ppvc != NULL; ppvc = ppvc_next) {
+ ppvc_next = &((*ppvc)->next);
+ free(*ppvc, M_LINUX);
+ }
+ vw->clips = NULL;
+
+ return (0);
+}
+
+static int
+linux_v4l_cliplist_copy(struct l_video_window *lvw, struct video_window *vw)
+{
+ int error;
+ int clipcount;
+ void *plvc;
+ struct video_clip **ppvc;
+
+ /*
+ * XXX: The cliplist is used to pass in a list of clipping
+ * rectangles or, if clipcount == VIDEO_CLIP_BITMAP, a
+ * clipping bitmap. Some Linux apps, however, appear to
+ * leave cliplist and clips uninitialized. In any case,
+ * the cliplist is not used by pwc(4), at the time of
+ * writing, FreeBSD's only V4L driver. When a driver
+ * that uses the cliplist is developed, this code may
+ * need re-examiniation.
+ */
+ error = 0;
+ clipcount = vw->clipcount;
+ if (clipcount == VIDEO_CLIP_BITMAP) {
+ /*
+ * In this case, the pointer (clips) is overloaded
+ * to be a "void *" to a bitmap, therefore there
+ * is no struct video_clip to copy now.
+ */
+ } else if (clipcount > 0 && clipcount <= 16384) {
+ /*
+ * Clips points to list of clip rectangles, so
+ * copy the list.
+ *
+ * XXX: Upper limit of 16384 was used here to try to
+ * avoid cases when clipcount and clips pointer
+ * are uninitialized and therefore have high random
+ * values, as is the case in the Linux Skype
+ * application. The value 16384 was chosen as that
+ * is what is used in the Linux stradis(4) MPEG
+ * decoder driver, the only place we found an
+ * example of cliplist use.
+ */
+ plvc = PTRIN(lvw->clips);
+ vw->clips = NULL;
+ ppvc = &(vw->clips);
+ while (clipcount-- > 0) {
+ if (plvc == 0) {
+ error = EFAULT;
+ break;
+ } else {
+ error = linux_v4l_clip_copy(plvc, ppvc);
+ if (error) {
+ linux_v4l_cliplist_free(vw);
+ break;
+ }
+ }
+ ppvc = &((*ppvc)->next);
+ plvc = PTRIN(((struct l_video_clip *) plvc)->next);
+ }
+ } else {
+ /*
+ * clipcount == 0 or negative (but not VIDEO_CLIP_BITMAP)
+ * Force cliplist to null.
+ */
+ vw->clipcount = 0;
+ vw->clips = NULL;
+ }
+ return (error);
+}
+#endif
+
+static int
+linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ int error;
+ struct video_tuner vtun;
+ struct video_window vwin;
+ struct video_buffer vbuf;
+ struct video_code vcode;
+ struct l_video_tuner l_vtun;
+ struct l_video_window l_vwin;
+ struct l_video_buffer l_vbuf;
+ struct l_video_code l_vcode;
+
+ switch (args->cmd & 0xffff) {
+ case LINUX_VIDIOCGCAP: args->cmd = VIDIOCGCAP; break;
+ case LINUX_VIDIOCGCHAN: args->cmd = VIDIOCGCHAN; break;
+ case LINUX_VIDIOCSCHAN: args->cmd = VIDIOCSCHAN; break;
+
+ case LINUX_VIDIOCGTUNER:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun));
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+ linux_to_bsd_v4l_tuner(&l_vtun, &vtun);
+ error = fo_ioctl(fp, VIDIOCGTUNER, &vtun, td->td_ucred, td);
+ if (!error) {
+ bsd_to_linux_v4l_tuner(&vtun, &l_vtun);
+ error = copyout(&l_vtun, (void *) args->arg,
+ sizeof(l_vtun));
+ }
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCSTUNER:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun));
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+ linux_to_bsd_v4l_tuner(&l_vtun, &vtun);
+ error = fo_ioctl(fp, VIDIOCSTUNER, &vtun, td->td_ucred, td);
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCGPICT: args->cmd = VIDIOCGPICT; break;
+ case LINUX_VIDIOCSPICT: args->cmd = VIDIOCSPICT; break;
+ case LINUX_VIDIOCCAPTURE: args->cmd = VIDIOCCAPTURE; break;
+
+ case LINUX_VIDIOCGWIN:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = fo_ioctl(fp, VIDIOCGWIN, &vwin, td->td_ucred, td);
+ if (!error) {
+ bsd_to_linux_v4l_window(&vwin, &l_vwin);
+ error = copyout(&l_vwin, (void *) args->arg,
+ sizeof(l_vwin));
+ }
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCSWIN:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = copyin((void *) args->arg, &l_vwin, sizeof(l_vwin));
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+ linux_to_bsd_v4l_window(&l_vwin, &vwin);
+#ifdef COMPAT_LINUX_V4L_CLIPLIST
+ error = linux_v4l_cliplist_copy(&l_vwin, &vwin);
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+#endif
+ error = fo_ioctl(fp, VIDIOCSWIN, &vwin, td->td_ucred, td);
+ fdrop(fp, td);
+#ifdef COMPAT_LINUX_V4L_CLIPLIST
+ linux_v4l_cliplist_free(&vwin);
+#endif
+ return (error);
+
+ case LINUX_VIDIOCGFBUF:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = fo_ioctl(fp, VIDIOCGFBUF, &vbuf, td->td_ucred, td);
+ if (!error) {
+ bsd_to_linux_v4l_buffer(&vbuf, &l_vbuf);
+ error = copyout(&l_vbuf, (void *) args->arg,
+ sizeof(l_vbuf));
+ }
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCSFBUF:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = copyin((void *) args->arg, &l_vbuf, sizeof(l_vbuf));
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+ linux_to_bsd_v4l_buffer(&l_vbuf, &vbuf);
+ error = fo_ioctl(fp, VIDIOCSFBUF, &vbuf, td->td_ucred, td);
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCKEY: args->cmd = VIDIOCKEY; break;
+ case LINUX_VIDIOCGFREQ: args->cmd = VIDIOCGFREQ; break;
+ case LINUX_VIDIOCSFREQ: args->cmd = VIDIOCSFREQ; break;
+ case LINUX_VIDIOCGAUDIO: args->cmd = VIDIOCGAUDIO; break;
+ case LINUX_VIDIOCSAUDIO: args->cmd = VIDIOCSAUDIO; break;
+ case LINUX_VIDIOCSYNC: args->cmd = VIDIOCSYNC; break;
+ case LINUX_VIDIOCMCAPTURE: args->cmd = VIDIOCMCAPTURE; break;
+ case LINUX_VIDIOCGMBUF: args->cmd = VIDIOCGMBUF; break;
+ case LINUX_VIDIOCGUNIT: args->cmd = VIDIOCGUNIT; break;
+ case LINUX_VIDIOCGCAPTURE: args->cmd = VIDIOCGCAPTURE; break;
+ case LINUX_VIDIOCSCAPTURE: args->cmd = VIDIOCSCAPTURE; break;
+ case LINUX_VIDIOCSPLAYMODE: args->cmd = VIDIOCSPLAYMODE; break;
+ case LINUX_VIDIOCSWRITEMODE: args->cmd = VIDIOCSWRITEMODE; break;
+ case LINUX_VIDIOCGPLAYINFO: args->cmd = VIDIOCGPLAYINFO; break;
+
+ case LINUX_VIDIOCSMICROCODE:
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ error = copyin((void *) args->arg, &l_vcode, sizeof(l_vcode));
+ if (error) {
+ fdrop(fp, td);
+ return (error);
+ }
+ linux_to_bsd_v4l_code(&l_vcode, &vcode);
+ error = fo_ioctl(fp, VIDIOCSMICROCODE, &vcode, td->td_ucred, td);
+ fdrop(fp, td);
+ return (error);
+
+ case LINUX_VIDIOCGVBIFMT: args->cmd = VIDIOCGVBIFMT; break;
+ case LINUX_VIDIOCSVBIFMT: args->cmd = VIDIOCSVBIFMT; break;
+ default: return (ENOIOCTL);
+ }
+
+ error = ioctl(td, (struct ioctl_args *)args);
+ return (error);
+}
+
+/*
+ * Special ioctl handler
+ */
+static int
+linux_ioctl_special(struct thread *td, struct linux_ioctl_args *args)
+{
+ int error;
+
+ switch (args->cmd) {
+ case LINUX_SIOCGIFADDR:
+ args->cmd = SIOCGIFADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+ case LINUX_SIOCSIFADDR:
+ args->cmd = SIOCSIFADDR;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+ case LINUX_SIOCGIFFLAGS:
+ args->cmd = SIOCGIFFLAGS;
+ error = ioctl(td, (struct ioctl_args *)args);
+ break;
+ default:
+ error = ENOIOCTL;
+ }
+
+ return (error);
+}
+
+/*
+ * Support for emulators/linux-libusb. This port uses FBSD_LUSB* macros
+ * instead of USB* ones. This lets us to provide correct values for cmd.
+ * 0xffffffe0 -- 0xffffffff range seemed to be the least collision-prone.
+ */
+static int
+linux_ioctl_fbsd_usb(struct thread *td, struct linux_ioctl_args *args)
+{
+ int error;
+
+ error = 0;
+ switch (args->cmd) {
+ case FBSD_LUSB_DEVICEENUMERATE:
+ args->cmd = USB_DEVICEENUMERATE;
+ break;
+ case FBSD_LUSB_DEV_QUIRK_ADD:
+ args->cmd = USB_DEV_QUIRK_ADD;
+ break;
+ case FBSD_LUSB_DEV_QUIRK_GET:
+ args->cmd = USB_DEV_QUIRK_GET;
+ break;
+ case FBSD_LUSB_DEV_QUIRK_REMOVE:
+ args->cmd = USB_DEV_QUIRK_REMOVE;
+ break;
+ case FBSD_LUSB_DO_REQUEST:
+ args->cmd = USB_DO_REQUEST;
+ break;
+ case FBSD_LUSB_FS_CLEAR_STALL_SYNC:
+ args->cmd = USB_FS_CLEAR_STALL_SYNC;
+ break;
+ case FBSD_LUSB_FS_CLOSE:
+ args->cmd = USB_FS_CLOSE;
+ break;
+ case FBSD_LUSB_FS_COMPLETE:
+ args->cmd = USB_FS_COMPLETE;
+ break;
+ case FBSD_LUSB_FS_INIT:
+ args->cmd = USB_FS_INIT;
+ break;
+ case FBSD_LUSB_FS_OPEN:
+ args->cmd = USB_FS_OPEN;
+ break;
+ case FBSD_LUSB_FS_START:
+ args->cmd = USB_FS_START;
+ break;
+ case FBSD_LUSB_FS_STOP:
+ args->cmd = USB_FS_STOP;
+ break;
+ case FBSD_LUSB_FS_UNINIT:
+ args->cmd = USB_FS_UNINIT;
+ break;
+ case FBSD_LUSB_GET_CONFIG:
+ args->cmd = USB_GET_CONFIG;
+ break;
+ case FBSD_LUSB_GET_DEVICEINFO:
+ args->cmd = USB_GET_DEVICEINFO;
+ break;
+ case FBSD_LUSB_GET_DEVICE_DESC:
+ args->cmd = USB_GET_DEVICE_DESC;
+ break;
+ case FBSD_LUSB_GET_FULL_DESC:
+ args->cmd = USB_GET_FULL_DESC;
+ break;
+ case FBSD_LUSB_GET_IFACE_DRIVER:
+ args->cmd = USB_GET_IFACE_DRIVER;
+ break;
+ case FBSD_LUSB_GET_PLUGTIME:
+ args->cmd = USB_GET_PLUGTIME;
+ break;
+ case FBSD_LUSB_GET_POWER_MODE:
+ args->cmd = USB_GET_POWER_MODE;
+ break;
+ case FBSD_LUSB_GET_REPORT_DESC:
+ args->cmd = USB_GET_REPORT_DESC;
+ break;
+ case FBSD_LUSB_GET_REPORT_ID:
+ args->cmd = USB_GET_REPORT_ID;
+ break;
+ case FBSD_LUSB_GET_TEMPLATE:
+ args->cmd = USB_GET_TEMPLATE;
+ break;
+ case FBSD_LUSB_IFACE_DRIVER_ACTIVE:
+ args->cmd = USB_IFACE_DRIVER_ACTIVE;
+ break;
+ case FBSD_LUSB_IFACE_DRIVER_DETACH:
+ args->cmd = USB_IFACE_DRIVER_DETACH;
+ break;
+ case FBSD_LUSB_QUIRK_NAME_GET:
+ args->cmd = USB_QUIRK_NAME_GET;
+ break;
+ case FBSD_LUSB_READ_DIR:
+ args->cmd = USB_READ_DIR;
+ break;
+ case FBSD_LUSB_SET_ALTINTERFACE:
+ args->cmd = USB_SET_ALTINTERFACE;
+ break;
+ case FBSD_LUSB_SET_CONFIG:
+ args->cmd = USB_SET_CONFIG;
+ break;
+ case FBSD_LUSB_SET_IMMED:
+ args->cmd = USB_SET_IMMED;
+ break;
+ case FBSD_LUSB_SET_POWER_MODE:
+ args->cmd = USB_SET_POWER_MODE;
+ break;
+ case FBSD_LUSB_SET_TEMPLATE:
+ args->cmd = USB_SET_TEMPLATE;
+ break;
+ default:
+ error = ENOIOCTL;
+ }
+ if (error != ENOIOCTL)
+ error = ioctl(td, (struct ioctl_args *)args);
+ return (error);
+}
+
+/*
+ * main ioctl syscall function
+ */
+
+int
+linux_ioctl(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct file *fp;
+ struct handler_element *he;
+ int error, cmd;
+
+#ifdef DEBUG
+ if (ldebug(ioctl))
+ printf(ARGS(ioctl, "%d, %04lx, *"), args->fd,
+ (unsigned long)args->cmd);
+#endif
+
+ if ((error = fget(td, args->fd, &fp)) != 0)
+ return (error);
+ if ((fp->f_flag & (FREAD|FWRITE)) == 0) {
+ fdrop(fp, td);
+ return (EBADF);
+ }
+
+ /* Iterate over the ioctl handlers */
+ cmd = args->cmd & 0xffff;
+ sx_slock(&linux_ioctl_sx);
+ mtx_lock(&Giant);
+ TAILQ_FOREACH(he, &handlers, list) {
+ if (cmd >= he->low && cmd <= he->high) {
+ error = (*he->func)(td, args);
+ if (error != ENOIOCTL) {
+ mtx_unlock(&Giant);
+ sx_sunlock(&linux_ioctl_sx);
+ fdrop(fp, td);
+ return (error);
+ }
+ }
+ }
+ mtx_unlock(&Giant);
+ sx_sunlock(&linux_ioctl_sx);
+ fdrop(fp, td);
+
+ linux_msg(td, "ioctl fd=%d, cmd=0x%x ('%c',%d) is not implemented",
+ args->fd, (int)(args->cmd & 0xffff),
+ (int)(args->cmd & 0xff00) >> 8, (int)(args->cmd & 0xff));
+
+ return (EINVAL);
+}
+
+int
+linux_ioctl_register_handler(struct linux_ioctl_handler *h)
+{
+ struct handler_element *he, *cur;
+
+ if (h == NULL || h->func == NULL)
+ return (EINVAL);
+
+ /*
+ * Reuse the element if the handler is already on the list, otherwise
+ * create a new element.
+ */
+ sx_xlock(&linux_ioctl_sx);
+ TAILQ_FOREACH(he, &handlers, list) {
+ if (he->func == h->func)
+ break;
+ }
+ if (he == NULL) {
+ he = malloc(sizeof(*he),
+ M_LINUX, M_WAITOK);
+ he->func = h->func;
+ } else
+ TAILQ_REMOVE(&handlers, he, list);
+
+ /* Initialize range information. */
+ he->low = h->low;
+ he->high = h->high;
+ he->span = h->high - h->low + 1;
+
+ /* Add the element to the list, sorted on span. */
+ TAILQ_FOREACH(cur, &handlers, list) {
+ if (cur->span > he->span) {
+ TAILQ_INSERT_BEFORE(cur, he, list);
+ sx_xunlock(&linux_ioctl_sx);
+ return (0);
+ }
+ }
+ TAILQ_INSERT_TAIL(&handlers, he, list);
+ sx_xunlock(&linux_ioctl_sx);
+
+ return (0);
+}
+
+int
+linux_ioctl_unregister_handler(struct linux_ioctl_handler *h)
+{
+ struct handler_element *he;
+
+ if (h == NULL || h->func == NULL)
+ return (EINVAL);
+
+ sx_xlock(&linux_ioctl_sx);
+ TAILQ_FOREACH(he, &handlers, list) {
+ if (he->func == h->func) {
+ TAILQ_REMOVE(&handlers, he, list);
+ sx_xunlock(&linux_ioctl_sx);
+ free(he, M_LINUX);
+ return (0);
+ }
+ }
+ sx_xunlock(&linux_ioctl_sx);
+
+ return (EINVAL);
+}
diff --git a/sys/compat/linux/linux_ioctl.h b/sys/compat/linux/linux_ioctl.h
new file mode 100644
index 0000000..08ccf88
--- /dev/null
+++ b/sys/compat/linux/linux_ioctl.h
@@ -0,0 +1,654 @@
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_IOCTL_H_
+#define _LINUX_IOCTL_H_
+
+/*
+ * disk
+ */
+#define LINUX_BLKROSET 0x125d
+#define LINUX_BLKROGET 0x125e
+#define LINUX_BLKRRPART 0x125f
+#define LINUX_BLKGETSIZE 0x1260
+#define LINUX_BLKFLSBUF 0x1261
+#define LINUX_BLKRASET 0x1262
+#define LINUX_BLKRAGET 0x1263
+#define LINUX_BLKFRASET 0x1264
+#define LINUX_BLKFRAGET 0x1265
+#define LINUX_BLKSECTSET 0x1266
+#define LINUX_BLKSECTGET 0x1267
+#define LINUX_BLKSSZGET 0x1268
+
+#define LINUX_IOCTL_DISK_MIN LINUX_BLKROSET
+#define LINUX_IOCTL_DISK_MAX LINUX_BLKSSZGET
+
+/*
+ * hdio
+ */
+#define LINUX_HDIO_GET_GEO 0x0301
+#define LINUX_HDIO_GET_IDENTITY 0x030D /* not yet implemented */
+#define LINUX_HDIO_GET_GEO_BIG 0x0330
+
+#define LINUX_IOCTL_HDIO_MIN LINUX_HDIO_GET_GEO
+#define LINUX_IOCTL_HDIO_MAX LINUX_HDIO_GET_GEO_BIG
+
+/*
+ * cdrom
+ */
+#define LINUX_CDROMPAUSE 0x5301
+#define LINUX_CDROMRESUME 0x5302
+#define LINUX_CDROMPLAYMSF 0x5303
+#define LINUX_CDROMPLAYTRKIND 0x5304
+#define LINUX_CDROMREADTOCHDR 0x5305
+#define LINUX_CDROMREADTOCENTRY 0x5306
+#define LINUX_CDROMSTOP 0x5307
+#define LINUX_CDROMSTART 0x5308
+#define LINUX_CDROMEJECT 0x5309
+#define LINUX_CDROMVOLCTRL 0x530a
+#define LINUX_CDROMSUBCHNL 0x530b
+#define LINUX_CDROMREADMODE2 0x530c
+#define LINUX_CDROMREADMODE1 0x530d
+#define LINUX_CDROMREADAUDIO 0x530e
+#define LINUX_CDROMEJECT_SW 0x530f
+#define LINUX_CDROMMULTISESSION 0x5310
+#define LINUX_CDROM_GET_UPC 0x5311
+#define LINUX_CDROMRESET 0x5312
+#define LINUX_CDROMVOLREAD 0x5313
+#define LINUX_CDROMREADRAW 0x5314
+#define LINUX_CDROMREADCOOKED 0x5315
+#define LINUX_CDROMSEEK 0x5316
+#define LINUX_CDROMPLAYBLK 0x5317
+#define LINUX_CDROMREADALL 0x5318
+#define LINUX_CDROMCLOSETRAY 0x5319
+#define LINUX_CDROMLOADFROMSLOT 0x531a
+#define LINUX_CDROMGETSPINDOWN 0x531d
+#define LINUX_CDROMSETSPINDOWN 0x531e
+#define LINUX_CDROM_SET_OPTIONS 0x5320
+#define LINUX_CDROM_CLEAR_OPTIONS 0x5321
+#define LINUX_CDROM_SELECT_SPEED 0x5322
+#define LINUX_CDROM_SELECT_DISC 0x5323
+#define LINUX_CDROM_MEDIA_CHANGED 0x5325
+#define LINUX_CDROM_DRIVE_STATUS 0x5326
+#define LINUX_CDROM_DISC_STATUS 0x5327
+#define LINUX_CDROM_CHANGER_NSLOTS 0x5328
+#define LINUX_CDROM_LOCKDOOR 0x5329
+#define LINUX_CDROM_DEBUG 0x5330
+#define LINUX_CDROM_GET_CAPABILITY 0x5331
+#define LINUX_CDROMAUDIOBUFSIZ 0x5382
+#define LINUX_SCSI_GET_IDLUN 0x5382
+#define LINUX_SCSI_GET_BUS_NUMBER 0x5386
+#define LINUX_DVD_READ_STRUCT 0x5390
+#define LINUX_DVD_WRITE_STRUCT 0x5391
+#define LINUX_DVD_AUTH 0x5392
+#define LINUX_CDROM_SEND_PACKET 0x5393
+#define LINUX_CDROM_NEXT_WRITABLE 0x5394
+#define LINUX_CDROM_LAST_WRITTEN 0x5395
+
+#define LINUX_IOCTL_CDROM_MIN LINUX_CDROMPAUSE
+#define LINUX_IOCTL_CDROM_MAX LINUX_CDROM_LAST_WRITTEN
+
+#define LINUX_CDROM_LBA 0x01
+#define LINUX_CDROM_MSF 0x02
+
+#define LINUX_DVD_LU_SEND_AGID 0
+#define LINUX_DVD_HOST_SEND_CHALLENGE 1
+#define LINUX_DVD_LU_SEND_KEY1 2
+#define LINUX_DVD_LU_SEND_CHALLENGE 3
+#define LINUX_DVD_HOST_SEND_KEY2 4
+#define LINUX_DVD_AUTH_ESTABLISHED 5
+#define LINUX_DVD_AUTH_FAILURE 6
+#define LINUX_DVD_LU_SEND_TITLE_KEY 7
+#define LINUX_DVD_LU_SEND_ASF 8
+#define LINUX_DVD_INVALIDATE_AGID 9
+#define LINUX_DVD_LU_SEND_RPC_STATE 10
+#define LINUX_DVD_HOST_SEND_RPC_STATE 11
+
+/*
+ * SG
+ */
+#define LINUX_SG_SET_TIMEOUT 0x2201
+#define LINUX_SG_GET_TIMEOUT 0x2202
+#define LINUX_SG_EMULATED_HOST 0x2203
+#define LINUX_SG_SET_TRANSFORM 0x2204
+#define LINUX_SG_GET_TRANSFORM 0x2205
+#define LINUX_SG_GET_COMMAND_Q 0x2270
+#define LINUX_SG_SET_COMMAND_Q 0x2271
+#define LINUX_SG_SET_RESERVED_SIZE 0x2275
+#define LINUX_SG_GET_RESERVED_SIZE 0x2272
+#define LINUX_SG_GET_SCSI_ID 0x2276
+#define LINUX_SG_SET_FORCE_LOW_DMA 0x2279
+#define LINUX_SG_GET_LOW_DMA 0x227a
+#define LINUX_SG_SET_FORCE_PACK_ID 0x227b
+#define LINUX_SG_GET_PACK_ID 0x227c
+#define LINUX_SG_GET_NUM_WAITING 0x227d
+#define LINUX_SG_SET_DEBUG 0x227e
+#define LINUX_SG_GET_SG_TABLESIZE 0x227f
+#define LINUX_SG_GET_VERSION_NUM 0x2282
+#define LINUX_SG_NEXT_CMD_LEN 0x2283
+#define LINUX_SG_SCSI_RESET 0x2284
+#define LINUX_SG_IO 0x2285
+#define LINUX_SG_GET_REQUEST_TABLE 0x2286
+#define LINUX_SG_SET_KEEP_ORPHAN 0x2287
+#define LINUX_SG_GET_KEEP_ORPHAN 0x2288
+#define LINUX_SG_GET_ACCESS_COUNT 0x2289
+
+#define LINUX_IOCTL_SG_MIN 0x2200
+#define LINUX_IOCTL_SG_MAX 0x22ff
+
+/*
+ * VFAT
+ */
+#define LINUX_VFAT_READDIR_BOTH 0x7201
+
+#define LINUX_IOCTL_VFAT_MIN LINUX_VFAT_READDIR_BOTH
+#define LINUX_IOCTL_VFAT_MAX LINUX_VFAT_READDIR_BOTH
+
+/*
+ * console
+ */
+#define LINUX_KIOCSOUND 0x4B2F
+#define LINUX_KDMKTONE 0x4B30
+#define LINUX_KDGETLED 0x4B31
+#define LINUX_KDSETLED 0x4B32
+#define LINUX_KDSETMODE 0x4B3A
+#define LINUX_KDGETMODE 0x4B3B
+#define LINUX_KDGKBMODE 0x4B44
+#define LINUX_KDSKBMODE 0x4B45
+#define LINUX_VT_OPENQRY 0x5600
+#define LINUX_VT_GETMODE 0x5601
+#define LINUX_VT_SETMODE 0x5602
+#define LINUX_VT_GETSTATE 0x5603
+#define LINUX_VT_RELDISP 0x5605
+#define LINUX_VT_ACTIVATE 0x5606
+#define LINUX_VT_WAITACTIVE 0x5607
+
+#define LINUX_IOCTL_CONSOLE_MIN LINUX_KIOCSOUND
+#define LINUX_IOCTL_CONSOLE_MAX LINUX_VT_WAITACTIVE
+
+#define LINUX_LED_SCR 0x01
+#define LINUX_LED_NUM 0x02
+#define LINUX_LED_CAP 0x04
+
+#define LINUX_KD_TEXT 0x0
+#define LINUX_KD_GRAPHICS 0x1
+#define LINUX_KD_TEXT0 0x2
+#define LINUX_KD_TEXT1 0x3
+
+#define LINUX_KBD_RAW 0
+#define LINUX_KBD_XLATE 1
+#define LINUX_KBD_MEDIUMRAW 2
+
+/*
+ * socket
+ */
+#define LINUX_FIOSETOWN 0x8901
+#define LINUX_SIOCSPGRP 0x8902
+#define LINUX_FIOGETOWN 0x8903
+#define LINUX_SIOCGPGRP 0x8904
+#define LINUX_SIOCATMARK 0x8905
+#define LINUX_SIOCGSTAMP 0x8906
+#define LINUX_SIOCGIFCONF 0x8912
+#define LINUX_SIOCGIFFLAGS 0x8913
+#define LINUX_SIOCGIFADDR 0x8915
+#define LINUX_SIOCSIFADDR 0x8916
+#define LINUX_SIOCGIFDSTADDR 0x8917
+#define LINUX_SIOCGIFBRDADDR 0x8919
+#define LINUX_SIOCGIFNETMASK 0x891b
+#define LINUX_SIOCSIFNETMASK 0x891c
+#define LINUX_SIOCGIFMTU 0x8921
+#define LINUX_SIOCSIFMTU 0x8922
+#define LINUX_SIOCSIFNAME 0x8923
+#define LINUX_SIOCSIFHWADDR 0x8924
+#define LINUX_SIOCGIFHWADDR 0x8927
+#define LINUX_SIOCADDMULTI 0x8931
+#define LINUX_SIOCDELMULTI 0x8932
+#define LINUX_SIOCGIFINDEX 0x8933
+#define LINUX_SIOGIFINDEX LINUX_SIOCGIFINDEX
+#define LINUX_SIOCGIFCOUNT 0x8938
+
+#define LINUX_IOCTL_SOCKET_MIN LINUX_FIOSETOWN
+#define LINUX_IOCTL_SOCKET_MAX LINUX_SIOCGIFCOUNT
+
+/*
+ * Device private ioctl calls
+ */
+#define LINUX_SIOCDEVPRIVATE 0x89F0 /* to 89FF */
+#define LINUX_IOCTL_PRIVATE_MIN LINUX_SIOCDEVPRIVATE
+#define LINUX_IOCTL_PRIVATE_MAX LINUX_SIOCDEVPRIVATE+0xf
+
+/*
+ * sound
+ */
+#define LINUX_SOUND_MIXER_WRITE_VOLUME 0x4d00
+#define LINUX_SOUND_MIXER_WRITE_BASS 0x4d01
+#define LINUX_SOUND_MIXER_WRITE_TREBLE 0x4d02
+#define LINUX_SOUND_MIXER_WRITE_SYNTH 0x4d03
+#define LINUX_SOUND_MIXER_WRITE_PCM 0x4d04
+#define LINUX_SOUND_MIXER_WRITE_SPEAKER 0x4d05
+#define LINUX_SOUND_MIXER_WRITE_LINE 0x4d06
+#define LINUX_SOUND_MIXER_WRITE_MIC 0x4d07
+#define LINUX_SOUND_MIXER_WRITE_CD 0x4d08
+#define LINUX_SOUND_MIXER_WRITE_IMIX 0x4d09
+#define LINUX_SOUND_MIXER_WRITE_ALTPCM 0x4d0A
+#define LINUX_SOUND_MIXER_WRITE_RECLEV 0x4d0B
+#define LINUX_SOUND_MIXER_WRITE_IGAIN 0x4d0C
+#define LINUX_SOUND_MIXER_WRITE_OGAIN 0x4d0D
+#define LINUX_SOUND_MIXER_WRITE_LINE1 0x4d0E
+#define LINUX_SOUND_MIXER_WRITE_LINE2 0x4d0F
+#define LINUX_SOUND_MIXER_WRITE_LINE3 0x4d10
+#define LINUX_SOUND_MIXER_INFO 0x4d65
+#define LINUX_OSS_GETVERSION 0x4d76
+#define LINUX_SOUND_MIXER_READ_STEREODEVS 0x4dfb
+#define LINUX_SOUND_MIXER_READ_CAPS 0x4dfc
+#define LINUX_SOUND_MIXER_READ_RECMASK 0x4dfd
+#define LINUX_SOUND_MIXER_READ_DEVMASK 0x4dfe
+#define LINUX_SOUND_MIXER_WRITE_RECSRC 0x4dff
+#define LINUX_SNDCTL_DSP_RESET 0x5000
+#define LINUX_SNDCTL_DSP_SYNC 0x5001
+#define LINUX_SNDCTL_DSP_SPEED 0x5002
+#define LINUX_SNDCTL_DSP_STEREO 0x5003
+#define LINUX_SNDCTL_DSP_GETBLKSIZE 0x5004
+#define LINUX_SNDCTL_DSP_SETBLKSIZE LINUX_SNDCTL_DSP_GETBLKSIZE
+#define LINUX_SNDCTL_DSP_SETFMT 0x5005
+#define LINUX_SOUND_PCM_WRITE_CHANNELS 0x5006
+#define LINUX_SOUND_PCM_WRITE_FILTER 0x5007
+#define LINUX_SNDCTL_DSP_POST 0x5008
+#define LINUX_SNDCTL_DSP_SUBDIVIDE 0x5009
+#define LINUX_SNDCTL_DSP_SETFRAGMENT 0x500A
+#define LINUX_SNDCTL_DSP_GETFMTS 0x500B
+#define LINUX_SNDCTL_DSP_GETOSPACE 0x500C
+#define LINUX_SNDCTL_DSP_GETISPACE 0x500D
+#define LINUX_SNDCTL_DSP_NONBLOCK 0x500E
+#define LINUX_SNDCTL_DSP_GETCAPS 0x500F
+#define LINUX_SNDCTL_DSP_GETTRIGGER 0x5010
+#define LINUX_SNDCTL_DSP_SETTRIGGER LINUX_SNDCTL_DSP_GETTRIGGER
+#define LINUX_SNDCTL_DSP_GETIPTR 0x5011
+#define LINUX_SNDCTL_DSP_GETOPTR 0x5012
+#define LINUX_SNDCTL_DSP_SETDUPLEX 0x5016
+#define LINUX_SNDCTL_DSP_GETODELAY 0x5017
+#define LINUX_SNDCTL_SEQ_RESET 0x5100
+#define LINUX_SNDCTL_SEQ_SYNC 0x5101
+#define LINUX_SNDCTL_SYNTH_INFO 0x5102
+#define LINUX_SNDCTL_SEQ_CTRLRATE 0x5103
+#define LINUX_SNDCTL_SEQ_GETOUTCOUNT 0x5104
+#define LINUX_SNDCTL_SEQ_GETINCOUNT 0x5105
+#define LINUX_SNDCTL_SEQ_PERCMODE 0x5106
+#define LINUX_SNDCTL_FM_LOAD_INSTR 0x5107
+#define LINUX_SNDCTL_SEQ_TESTMIDI 0x5108
+#define LINUX_SNDCTL_SEQ_RESETSAMPLES 0x5109
+#define LINUX_SNDCTL_SEQ_NRSYNTHS 0x510A
+#define LINUX_SNDCTL_SEQ_NRMIDIS 0x510B
+#define LINUX_SNDCTL_MIDI_INFO 0x510C
+#define LINUX_SNDCTL_SEQ_TRESHOLD 0x510D
+#define LINUX_SNDCTL_SYNTH_MEMAVL 0x510E
+
+#define LINUX_IOCTL_SOUND_MIN LINUX_SOUND_MIXER_WRITE_VOLUME
+#define LINUX_IOCTL_SOUND_MAX LINUX_SNDCTL_SYNTH_MEMAVL
+
+/*
+ * termio
+ */
+#define LINUX_TCGETS 0x5401
+#define LINUX_TCSETS 0x5402
+#define LINUX_TCSETSW 0x5403
+#define LINUX_TCSETSF 0x5404
+#define LINUX_TCGETA 0x5405
+#define LINUX_TCSETA 0x5406
+#define LINUX_TCSETAW 0x5407
+#define LINUX_TCSETAF 0x5408
+#define LINUX_TCSBRK 0x5409
+#define LINUX_TCXONC 0x540A
+#define LINUX_TCFLSH 0x540B
+
+#define LINUX_TIOCEXCL 0x540C
+#define LINUX_TIOCNXCL 0x540D
+#define LINUX_TIOCSCTTY 0x540E
+
+#define LINUX_TIOCGPGRP 0x540F
+#define LINUX_TIOCSPGRP 0x5410
+
+#define LINUX_TIOCOUTQ 0x5411
+#define LINUX_TIOCSTI 0x5412
+
+#define LINUX_TIOCGWINSZ 0x5413
+#define LINUX_TIOCSWINSZ 0x5414
+
+#define LINUX_TIOCMGET 0x5415
+#define LINUX_TIOCMBIS 0x5416
+#define LINUX_TIOCMBIC 0x5417
+#define LINUX_TIOCMSET 0x5418
+#define LINUX_TIOCGSOFTCAR 0x5419
+#define LINUX_TIOCSSOFTCAR 0x541A
+
+#define LINUX_FIONREAD 0x541B
+
+#define LINUX_TIOCINQ FIONREAD
+#define LINUX_TIOCLINUX 0x541C
+#define LINUX_TIOCCONS 0x541D
+#define LINUX_TIOCGSERIAL 0x541E
+#define LINUX_TIOCSSERIAL 0x541F
+#define LINUX_TIOCPKT 0x5420
+
+#define LINUX_FIONBIO 0x5421
+
+#define LINUX_TIOCNOTTY 0x5422
+#define LINUX_TIOCSETD 0x5423
+#define LINUX_TIOCGETD 0x5424
+#define LINUX_TCSBRKP 0x5425
+#define LINUX_TIOCTTYGSTRUCT 0x5426
+
+#define LINUX_TIOCSBRK 0x5427
+#define LINUX_TIOCCBRK 0x5428
+
+#define LINUX_TIOCGPTN 0x5430
+#define LINUX_TIOCSPTLCK 0x5431
+
+#define LINUX_FIONCLEX 0x5450
+#define LINUX_FIOCLEX 0x5451
+#define LINUX_FIOASYNC 0x5452
+
+#define LINUX_TIOCSERCONFIG 0x5453
+#define LINUX_TIOCSERGWILD 0x5454
+#define LINUX_TIOCSERSWILD 0x5455
+#define LINUX_TIOCGLCKTRMIOS 0x5456
+#define LINUX_TIOCSLCKTRMIOS 0x5457
+
+#define LINUX_IOCTL_TERMIO_MIN LINUX_TCGETS
+#define LINUX_IOCTL_TERMIO_MAX LINUX_TIOCSLCKTRMIOS
+
+/* arguments for tcflow() and LINUX_TCXONC */
+#define LINUX_TCOOFF 0
+#define LINUX_TCOON 1
+#define LINUX_TCIOFF 2
+#define LINUX_TCION 3
+
+/* arguments for tcflush() and LINUX_TCFLSH */
+#define LINUX_TCIFLUSH 0
+#define LINUX_TCOFLUSH 1
+#define LINUX_TCIOFLUSH 2
+
+/* line disciplines */
+#define LINUX_N_TTY 0
+#define LINUX_N_SLIP 1
+#define LINUX_N_MOUSE 2
+#define LINUX_N_PPP 3
+
+/* Linux termio c_cc values */
+#define LINUX_VINTR 0
+#define LINUX_VQUIT 1
+#define LINUX_VERASE 2
+#define LINUX_VKILL 3
+#define LINUX_VEOF 4
+#define LINUX_VTIME 5
+#define LINUX_VMIN 6
+#define LINUX_VSWTC 7
+#define LINUX_NCC 8
+
+/* Linux termios c_cc values */
+/* In addition to the termio values */
+#define LINUX_VSTART 8
+#define LINUX_VSTOP 9
+#define LINUX_VSUSP 10
+#define LINUX_VEOL 11
+#define LINUX_VREPRINT 12
+#define LINUX_VDISCARD 13
+#define LINUX_VWERASE 14
+#define LINUX_VLNEXT 15
+#define LINUX_VEOL2 16
+#define LINUX_NCCS 19
+
+#define LINUX_POSIX_VDISABLE '\0'
+
+/* Linux c_iflag masks */
+#define LINUX_IGNBRK 0x0000001
+#define LINUX_BRKINT 0x0000002
+#define LINUX_IGNPAR 0x0000004
+#define LINUX_PARMRK 0x0000008
+#define LINUX_INPCK 0x0000010
+#define LINUX_ISTRIP 0x0000020
+#define LINUX_INLCR 0x0000040
+#define LINUX_IGNCR 0x0000080
+#define LINUX_ICRNL 0x0000100
+
+#define LINUX_IUCLC 0x0000200
+#define LINUX_IXON 0x0000400
+#define LINUX_IXANY 0x0000800
+#define LINUX_IXOFF 0x0001000
+
+#define LINUX_IMAXBEL 0x0002000
+
+/* Linux c_oflag masks */
+#define LINUX_OPOST 0x0000001
+
+#define LINUX_OLCUC 0x0000002
+#define LINUX_ONLCR 0x0000004
+
+#define LINUX_OCRNL 0x0000008
+#define LINUX_ONOCR 0x0000010
+#define LINUX_ONLRET 0x0000020
+#define LINUX_OFILL 0x0000040
+#define LINUX_OFDEL 0x0000080
+
+#define LINUX_NLDLY 0x0000100
+#define LINUX_NL0 0x0000000
+#define LINUX_NL1 0x0000100
+#define LINUX_CRDLY 0x0000600
+#define LINUX_CR0 0x0000000
+#define LINUX_CR1 0x0000200
+#define LINUX_CR2 0x0000400
+#define LINUX_CR3 0x0000600
+#define LINUX_TABDLY 0x0001800
+#define LINUX_TAB0 0x0000000
+#define LINUX_TAB1 0x0000800
+#define LINUX_TAB2 0x0001000
+#define LINUX_TAB3 0x0001800
+#define LINUX_XTABS 0x0001800
+#define LINUX_BSDLY 0x0002000
+#define LINUX_BS0 0x0000000
+#define LINUX_BS1 0x0002000
+#define LINUX_VTDLY 0x0004000
+#define LINUX_VT0 0x0000000
+#define LINUX_VT1 0x0004000
+#define LINUX_FFDLY 0x0008000
+#define LINUX_FF0 0x0000000
+#define LINUX_FF1 0x0008000
+
+#define LINUX_CBAUD 0x0000100f
+
+#define LINUX_B0 0x00000000
+#define LINUX_B50 0x00000001
+#define LINUX_B75 0x00000002
+#define LINUX_B110 0x00000003
+#define LINUX_B134 0x00000004
+#define LINUX_B150 0x00000005
+#define LINUX_B200 0x00000006
+#define LINUX_B300 0x00000007
+#define LINUX_B600 0x00000008
+#define LINUX_B1200 0x00000009
+#define LINUX_B1800 0x0000000a
+#define LINUX_B2400 0x0000000b
+#define LINUX_B4800 0x0000000c
+#define LINUX_B9600 0x0000000d
+#define LINUX_B19200 0x0000000e
+#define LINUX_B38400 0x0000000f
+#define LINUX_EXTA LINUX_B19200
+#define LINUX_EXTB LINUX_B38400
+
+#define LINUX_CBAUDEX 0x00001000
+#define LINUX_B57600 0x00001001
+#define LINUX_B115200 0x00001002
+
+#define LINUX_CSIZE 0x00000030
+#define LINUX_CS5 0x00000000
+#define LINUX_CS6 0x00000010
+#define LINUX_CS7 0x00000020
+#define LINUX_CS8 0x00000030
+#define LINUX_CSTOPB 0x00000040
+#define LINUX_CREAD 0x00000080
+#define LINUX_PARENB 0x00000100
+#define LINUX_PARODD 0x00000200
+#define LINUX_HUPCL 0x00000400
+#define LINUX_CLOCAL 0x00000800
+
+#define LINUX_CRTSCTS 0x80000000
+
+/* Linux c_lflag masks */
+#define LINUX_ISIG 0x00000001
+#define LINUX_ICANON 0x00000002
+#define LINUX_XCASE 0x00000004
+#define LINUX_ECHO 0x00000008
+#define LINUX_ECHOE 0x00000010
+#define LINUX_ECHOK 0x00000020
+#define LINUX_ECHONL 0x00000040
+#define LINUX_NOFLSH 0x00000080
+#define LINUX_TOSTOP 0x00000100
+#define LINUX_ECHOCTL 0x00000200
+#define LINUX_ECHOPRT 0x00000400
+#define LINUX_ECHOKE 0x00000800
+#define LINUX_FLUSHO 0x00001000
+#define LINUX_PENDIN 0x00002000
+#define LINUX_IEXTEN 0x00008000
+
+/* serial_struct values for TIOC[GS]SERIAL ioctls */
+#define LINUX_ASYNC_CLOSING_WAIT_INF 0
+#define LINUX_ASYNC_CLOSING_WAIT_NONE 65535
+
+#define LINUX_PORT_UNKNOWN 0
+#define LINUX_PORT_8250 1
+#define LINUX_PORT_16450 2
+#define LINUX_PORT_16550 3
+#define LINUX_PORT_16550A 4
+#define LINUX_PORT_CIRRUS 5
+#define LINUX_PORT_16650 6
+
+#define LINUX_PORT_MAX 6
+
+#define LINUX_ASYNC_HUP_NOTIFY 0x0001
+#define LINUX_ASYNC_FOURPORT 0x0002
+#define LINUX_ASYNC_SAK 0x0004
+#define LINUX_ASYNC_SPLIT_TERMIOS 0x0008
+#define LINUX_ASYNC_SPD_MASK 0x0030
+#define LINUX_ASYNC_SPD_HI 0x0010
+#define LINUX_ASYNC_SPD_VHI 0x0020
+#define LINUX_ASYNC_SPD_CUST 0x0030
+#define LINUX_ASYNC_SKIP_TEST 0x0040
+#define LINUX_ASYNC_AUTO_IRQ 0x0080
+#define LINUX_ASYNC_SESSION_LOCKOUT 0x0100
+#define LINUX_ASYNC_PGRP_LOCKOUT 0x0200
+#define LINUX_ASYNC_CALLOUT_NOHUP 0x0400
+#define LINUX_ASYNC_FLAGS 0x0FFF
+
+#define LINUX_IOCTL_DRM_MIN 0x6400
+#define LINUX_IOCTL_DRM_MAX 0x64ff
+
+/*
+ * This doesn't really belong here, but I can't think of a better
+ * place to put it.
+ */
+struct ifnet;
+int linux_ifname(struct ifnet *, char *, size_t);
+
+/*
+ * video
+ */
+#define LINUX_VIDIOCGCAP 0x7601
+#define LINUX_VIDIOCGCHAN 0x7602
+#define LINUX_VIDIOCSCHAN 0x7603
+#define LINUX_VIDIOCGTUNER 0x7604
+#define LINUX_VIDIOCSTUNER 0x7605
+#define LINUX_VIDIOCGPICT 0x7606
+#define LINUX_VIDIOCSPICT 0x7607
+#define LINUX_VIDIOCCAPTURE 0x7608
+#define LINUX_VIDIOCGWIN 0x7609
+#define LINUX_VIDIOCSWIN 0x760a
+#define LINUX_VIDIOCGFBUF 0x760b
+#define LINUX_VIDIOCSFBUF 0x760c
+#define LINUX_VIDIOCKEY 0x760d
+#define LINUX_VIDIOCGFREQ 0x760e
+#define LINUX_VIDIOCSFREQ 0x760f
+#define LINUX_VIDIOCGAUDIO 0x7610
+#define LINUX_VIDIOCSAUDIO 0x7611
+#define LINUX_VIDIOCSYNC 0x7623
+#define LINUX_VIDIOCMCAPTURE 0x7613
+#define LINUX_VIDIOCGMBUF 0x7614
+#define LINUX_VIDIOCGUNIT 0x7615
+#define LINUX_VIDIOCGCAPTURE 0x7616
+#define LINUX_VIDIOCSCAPTURE 0x7617
+#define LINUX_VIDIOCSPLAYMODE 0x7618
+#define LINUX_VIDIOCSWRITEMODE 0x7619
+#define LINUX_VIDIOCGPLAYINFO 0x761a
+#define LINUX_VIDIOCSMICROCODE 0x761b
+#define LINUX_VIDIOCGVBIFMT 0x761c
+#define LINUX_VIDIOCSVBIFMT 0x761d
+
+#define LINUX_IOCTL_VIDEO_MIN LINUX_VIDIOCGCAP
+#define LINUX_IOCTL_VIDEO_MAX LINUX_VIDIOCSVBIFMT
+
+/*
+ * Our libusb(8) calls emulated within linux(4).
+ */
+#define FBSD_LUSB_DEVICEENUMERATE 0xffff
+#define FBSD_LUSB_DEV_QUIRK_ADD 0xfffe
+#define FBSD_LUSB_DEV_QUIRK_GET 0xfffd
+#define FBSD_LUSB_DEV_QUIRK_REMOVE 0xfffc
+#define FBSD_LUSB_DO_REQUEST 0xfffb
+#define FBSD_LUSB_FS_CLEAR_STALL_SYNC 0xfffa
+#define FBSD_LUSB_FS_CLOSE 0xfff9
+#define FBSD_LUSB_FS_COMPLETE 0xfff8
+#define FBSD_LUSB_FS_INIT 0xfff7
+#define FBSD_LUSB_FS_OPEN 0xfff6
+#define FBSD_LUSB_FS_START 0xfff5
+#define FBSD_LUSB_FS_STOP 0xfff4
+#define FBSD_LUSB_FS_UNINIT 0xfff3
+#define FBSD_LUSB_GET_CONFIG 0xfff2
+#define FBSD_LUSB_GET_DEVICEINFO 0xfff1
+#define FBSD_LUSB_GET_DEVICE_DESC 0xfff0
+#define FBSD_LUSB_GET_FULL_DESC 0xffef
+#define FBSD_LUSB_GET_IFACE_DRIVER 0xffee
+#define FBSD_LUSB_GET_PLUGTIME 0xffed
+#define FBSD_LUSB_GET_POWER_MODE 0xffec
+#define FBSD_LUSB_GET_REPORT_DESC 0xffeb
+#define FBSD_LUSB_GET_REPORT_ID 0xffea
+#define FBSD_LUSB_GET_TEMPLATE 0xffe9
+#define FBSD_LUSB_IFACE_DRIVER_ACTIVE 0xffe8
+#define FBSD_LUSB_IFACE_DRIVER_DETACH 0xffe7
+#define FBSD_LUSB_QUIRK_NAME_GET 0xffe6
+#define FBSD_LUSB_READ_DIR 0xffe5
+#define FBSD_LUSB_SET_ALTINTERFACE 0xffe4
+#define FBSD_LUSB_SET_CONFIG 0xffe3
+#define FBSD_LUSB_SET_IMMED 0xffe2
+#define FBSD_LUSB_SET_POWER_MODE 0xffe1
+#define FBSD_LUSB_SET_TEMPLATE 0xffe0
+
+#define FBSD_LUSB_MAX 0xffff
+#define FBSD_LUSB_MIN 0xffe0
+
+#endif /* !_LINUX_IOCTL_H_ */
diff --git a/sys/compat/linux/linux_ipc.c b/sys/compat/linux/linux_ipc.c
new file mode 100644
index 0000000..97cea53
--- /dev/null
+++ b/sys/compat/linux/linux_ipc.c
@@ -0,0 +1,891 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/proc.h>
+#include <sys/limits.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+
+#include "opt_compat.h"
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#include <machine/../linux32/linux32_ipc64.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#include <machine/../linux/linux_ipc64.h>
+#endif
+#include <compat/linux/linux_ipc.h>
+#include <compat/linux/linux_util.h>
+
+struct l_seminfo {
+ l_int semmap;
+ l_int semmni;
+ l_int semmns;
+ l_int semmnu;
+ l_int semmsl;
+ l_int semopm;
+ l_int semume;
+ l_int semusz;
+ l_int semvmx;
+ l_int semaem;
+};
+
+struct l_shminfo {
+ l_int shmmax;
+ l_int shmmin;
+ l_int shmmni;
+ l_int shmseg;
+ l_int shmall;
+};
+
+struct l_shm_info {
+ l_int used_ids;
+ l_ulong shm_tot; /* total allocated shm */
+ l_ulong shm_rss; /* total resident shm */
+ l_ulong shm_swp; /* total swapped shm */
+ l_ulong swap_attempts;
+ l_ulong swap_successes;
+};
+
+struct l_msginfo {
+ l_int msgpool;
+ l_int msgmap;
+ l_int msgmax;
+ l_int msgmnb;
+ l_int msgmni;
+ l_int msgssz;
+ l_int msgtql;
+ l_ushort msgseg;
+};
+
+static void
+bsd_to_linux_shminfo( struct shminfo *bpp, struct l_shminfo *lpp)
+{
+
+ lpp->shmmax = bpp->shmmax;
+ lpp->shmmin = bpp->shmmin;
+ lpp->shmmni = bpp->shmmni;
+ lpp->shmseg = bpp->shmseg;
+ lpp->shmall = bpp->shmall;
+}
+
+static void
+bsd_to_linux_shm_info( struct shm_info *bpp, struct l_shm_info *lpp)
+{
+
+ lpp->used_ids = bpp->used_ids ;
+ lpp->shm_tot = bpp->shm_tot ;
+ lpp->shm_rss = bpp->shm_rss ;
+ lpp->shm_swp = bpp->shm_swp ;
+ lpp->swap_attempts = bpp->swap_attempts ;
+ lpp->swap_successes = bpp->swap_successes ;
+}
+
+struct l_ipc_perm {
+ l_key_t key;
+ l_uid16_t uid;
+ l_gid16_t gid;
+ l_uid16_t cuid;
+ l_gid16_t cgid;
+ l_ushort mode;
+ l_ushort seq;
+};
+
+static void
+linux_to_bsd_ipc_perm(struct l_ipc_perm *lpp, struct ipc_perm *bpp)
+{
+
+ bpp->key = lpp->key;
+ bpp->uid = lpp->uid;
+ bpp->gid = lpp->gid;
+ bpp->cuid = lpp->cuid;
+ bpp->cgid = lpp->cgid;
+ bpp->mode = lpp->mode;
+ bpp->seq = lpp->seq;
+}
+
+
+static void
+bsd_to_linux_ipc_perm(struct ipc_perm *bpp, struct l_ipc_perm *lpp)
+{
+
+ lpp->key = bpp->key;
+ lpp->uid = bpp->uid;
+ lpp->gid = bpp->gid;
+ lpp->cuid = bpp->cuid;
+ lpp->cgid = bpp->cgid;
+ lpp->mode = bpp->mode;
+ lpp->seq = bpp->seq;
+}
+
+struct l_msqid_ds {
+ struct l_ipc_perm msg_perm;
+ l_uintptr_t msg_first; /* first message on queue,unused */
+ l_uintptr_t msg_last; /* last message in queue,unused */
+ l_time_t msg_stime; /* last msgsnd time */
+ l_time_t msg_rtime; /* last msgrcv time */
+ l_time_t msg_ctime; /* last change time */
+ l_ulong msg_lcbytes; /* Reuse junk fields for 32 bit */
+ l_ulong msg_lqbytes; /* ditto */
+ l_ushort msg_cbytes; /* current number of bytes on queue */
+ l_ushort msg_qnum; /* number of messages in queue */
+ l_ushort msg_qbytes; /* max number of bytes on queue */
+ l_pid_t msg_lspid; /* pid of last msgsnd */
+ l_pid_t msg_lrpid; /* last receive pid */
+}
+#if defined(__amd64__) && defined(COMPAT_LINUX32)
+__packed
+#endif
+;
+
+struct l_semid_ds {
+ struct l_ipc_perm sem_perm;
+ l_time_t sem_otime;
+ l_time_t sem_ctime;
+ l_uintptr_t sem_base;
+ l_uintptr_t sem_pending;
+ l_uintptr_t sem_pending_last;
+ l_uintptr_t undo;
+ l_ushort sem_nsems;
+}
+#if defined(__amd64__) && defined(COMPAT_LINUX32)
+__packed
+#endif
+;
+
+struct l_shmid_ds {
+ struct l_ipc_perm shm_perm;
+ l_int shm_segsz;
+ l_time_t shm_atime;
+ l_time_t shm_dtime;
+ l_time_t shm_ctime;
+ l_ushort shm_cpid;
+ l_ushort shm_lpid;
+ l_short shm_nattch;
+ l_ushort private1;
+ l_uintptr_t private2;
+ l_uintptr_t private3;
+};
+
+static void
+linux_to_bsd_semid_ds(struct l_semid_ds *lsp, struct semid_ds *bsp)
+{
+
+ linux_to_bsd_ipc_perm(&lsp->sem_perm, &bsp->sem_perm);
+ bsp->sem_otime = lsp->sem_otime;
+ bsp->sem_ctime = lsp->sem_ctime;
+ bsp->sem_nsems = lsp->sem_nsems;
+ bsp->sem_base = PTRIN(lsp->sem_base);
+}
+
+static void
+bsd_to_linux_semid_ds(struct semid_ds *bsp, struct l_semid_ds *lsp)
+{
+
+ bsd_to_linux_ipc_perm(&bsp->sem_perm, &lsp->sem_perm);
+ lsp->sem_otime = bsp->sem_otime;
+ lsp->sem_ctime = bsp->sem_ctime;
+ lsp->sem_nsems = bsp->sem_nsems;
+ lsp->sem_base = PTROUT(bsp->sem_base);
+}
+
+static void
+linux_to_bsd_shmid_ds(struct l_shmid_ds *lsp, struct shmid_ds *bsp)
+{
+
+ linux_to_bsd_ipc_perm(&lsp->shm_perm, &bsp->shm_perm);
+ bsp->shm_segsz = lsp->shm_segsz;
+ bsp->shm_lpid = lsp->shm_lpid;
+ bsp->shm_cpid = lsp->shm_cpid;
+ bsp->shm_nattch = lsp->shm_nattch;
+ bsp->shm_atime = lsp->shm_atime;
+ bsp->shm_dtime = lsp->shm_dtime;
+ bsp->shm_ctime = lsp->shm_ctime;
+}
+
+static void
+bsd_to_linux_shmid_ds(struct shmid_ds *bsp, struct l_shmid_ds *lsp)
+{
+
+ bsd_to_linux_ipc_perm(&bsp->shm_perm, &lsp->shm_perm);
+ if (bsp->shm_segsz > INT_MAX)
+ lsp->shm_segsz = INT_MAX;
+ else
+ lsp->shm_segsz = bsp->shm_segsz;
+ lsp->shm_lpid = bsp->shm_lpid;
+ lsp->shm_cpid = bsp->shm_cpid;
+ if (bsp->shm_nattch > SHRT_MAX)
+ lsp->shm_nattch = SHRT_MAX;
+ else
+ lsp->shm_nattch = bsp->shm_nattch;
+ lsp->shm_atime = bsp->shm_atime;
+ lsp->shm_dtime = bsp->shm_dtime;
+ lsp->shm_ctime = bsp->shm_ctime;
+ lsp->private3 = 0;
+}
+
+static void
+linux_to_bsd_msqid_ds(struct l_msqid_ds *lsp, struct msqid_ds *bsp)
+{
+
+ linux_to_bsd_ipc_perm(&lsp->msg_perm, &bsp->msg_perm);
+ bsp->msg_cbytes = lsp->msg_cbytes;
+ bsp->msg_qnum = lsp->msg_qnum;
+ bsp->msg_qbytes = lsp->msg_qbytes;
+ bsp->msg_lspid = lsp->msg_lspid;
+ bsp->msg_lrpid = lsp->msg_lrpid;
+ bsp->msg_stime = lsp->msg_stime;
+ bsp->msg_rtime = lsp->msg_rtime;
+ bsp->msg_ctime = lsp->msg_ctime;
+}
+
+static void
+bsd_to_linux_msqid_ds(struct msqid_ds *bsp, struct l_msqid_ds *lsp)
+{
+
+ bsd_to_linux_ipc_perm(&bsp->msg_perm, &lsp->msg_perm);
+ lsp->msg_cbytes = bsp->msg_cbytes;
+ lsp->msg_qnum = bsp->msg_qnum;
+ lsp->msg_qbytes = bsp->msg_qbytes;
+ lsp->msg_lspid = bsp->msg_lspid;
+ lsp->msg_lrpid = bsp->msg_lrpid;
+ lsp->msg_stime = bsp->msg_stime;
+ lsp->msg_rtime = bsp->msg_rtime;
+ lsp->msg_ctime = bsp->msg_ctime;
+}
+
+static void
+linux_ipc_perm_to_ipc64_perm(struct l_ipc_perm *in, struct l_ipc64_perm *out)
+{
+
+ /* XXX: do we really need to do something here? */
+ out->key = in->key;
+ out->uid = in->uid;
+ out->gid = in->gid;
+ out->cuid = in->cuid;
+ out->cgid = in->cgid;
+ out->mode = in->mode;
+ out->seq = in->seq;
+}
+
+static int
+linux_msqid_pullup(l_int ver, struct l_msqid_ds *linux_msqid, caddr_t uaddr)
+{
+ struct l_msqid64_ds linux_msqid64;
+ int error;
+
+ if (ver == LINUX_IPC_64) {
+ error = copyin(uaddr, &linux_msqid64, sizeof(linux_msqid64));
+ if (error != 0)
+ return (error);
+
+ bzero(linux_msqid, sizeof(*linux_msqid));
+
+ linux_msqid->msg_perm.uid = linux_msqid64.msg_perm.uid;
+ linux_msqid->msg_perm.gid = linux_msqid64.msg_perm.gid;
+ linux_msqid->msg_perm.mode = linux_msqid64.msg_perm.mode;
+
+ if (linux_msqid64.msg_qbytes > USHRT_MAX)
+ linux_msqid->msg_lqbytes = linux_msqid64.msg_qbytes;
+ else
+ linux_msqid->msg_qbytes = linux_msqid64.msg_qbytes;
+ } else
+ error = copyin(uaddr, linux_msqid, sizeof(*linux_msqid));
+
+ return (error);
+}
+
+static int
+linux_msqid_pushdown(l_int ver, struct l_msqid_ds *linux_msqid, caddr_t uaddr)
+{
+ struct l_msqid64_ds linux_msqid64;
+
+ if (ver == LINUX_IPC_64) {
+ bzero(&linux_msqid64, sizeof(linux_msqid64));
+
+ linux_ipc_perm_to_ipc64_perm(&linux_msqid->msg_perm,
+ &linux_msqid64.msg_perm);
+
+ linux_msqid64.msg_stime = linux_msqid->msg_stime;
+ linux_msqid64.msg_rtime = linux_msqid->msg_rtime;
+ linux_msqid64.msg_ctime = linux_msqid->msg_ctime;
+
+ if (linux_msqid->msg_cbytes == 0)
+ linux_msqid64.msg_cbytes = linux_msqid->msg_lcbytes;
+ else
+ linux_msqid64.msg_cbytes = linux_msqid->msg_cbytes;
+
+ linux_msqid64.msg_qnum = linux_msqid->msg_qnum;
+
+ if (linux_msqid->msg_qbytes == 0)
+ linux_msqid64.msg_qbytes = linux_msqid->msg_lqbytes;
+ else
+ linux_msqid64.msg_qbytes = linux_msqid->msg_qbytes;
+
+ linux_msqid64.msg_lspid = linux_msqid->msg_lspid;
+ linux_msqid64.msg_lrpid = linux_msqid->msg_lrpid;
+
+ return (copyout(&linux_msqid64, uaddr, sizeof(linux_msqid64)));
+ } else
+ return (copyout(linux_msqid, uaddr, sizeof(*linux_msqid)));
+}
+
+static int
+linux_semid_pullup(l_int ver, struct l_semid_ds *linux_semid, caddr_t uaddr)
+{
+ struct l_semid64_ds linux_semid64;
+ int error;
+
+ if (ver == LINUX_IPC_64) {
+ error = copyin(uaddr, &linux_semid64, sizeof(linux_semid64));
+ if (error != 0)
+ return (error);
+
+ bzero(linux_semid, sizeof(*linux_semid));
+
+ linux_semid->sem_perm.uid = linux_semid64.sem_perm.uid;
+ linux_semid->sem_perm.gid = linux_semid64.sem_perm.gid;
+ linux_semid->sem_perm.mode = linux_semid64.sem_perm.mode;
+ } else
+ error = copyin(uaddr, linux_semid, sizeof(*linux_semid));
+
+ return (error);
+}
+
+static int
+linux_semid_pushdown(l_int ver, struct l_semid_ds *linux_semid, caddr_t uaddr)
+{
+ struct l_semid64_ds linux_semid64;
+
+ if (ver == LINUX_IPC_64) {
+ bzero(&linux_semid64, sizeof(linux_semid64));
+
+ linux_ipc_perm_to_ipc64_perm(&linux_semid->sem_perm,
+ &linux_semid64.sem_perm);
+
+ linux_semid64.sem_otime = linux_semid->sem_otime;
+ linux_semid64.sem_ctime = linux_semid->sem_ctime;
+ linux_semid64.sem_nsems = linux_semid->sem_nsems;
+
+ return (copyout(&linux_semid64, uaddr, sizeof(linux_semid64)));
+ } else
+ return (copyout(linux_semid, uaddr, sizeof(*linux_semid)));
+}
+
+static int
+linux_shmid_pullup(l_int ver, struct l_shmid_ds *linux_shmid, caddr_t uaddr)
+{
+ struct l_shmid64_ds linux_shmid64;
+ int error;
+
+ if (ver == LINUX_IPC_64) {
+ error = copyin(uaddr, &linux_shmid64, sizeof(linux_shmid64));
+ if (error != 0)
+ return (error);
+
+ bzero(linux_shmid, sizeof(*linux_shmid));
+
+ linux_shmid->shm_perm.uid = linux_shmid64.shm_perm.uid;
+ linux_shmid->shm_perm.gid = linux_shmid64.shm_perm.gid;
+ linux_shmid->shm_perm.mode = linux_shmid64.shm_perm.mode;
+ } else
+ error = copyin(uaddr, linux_shmid, sizeof(*linux_shmid));
+
+ return (error);
+}
+
+static int
+linux_shmid_pushdown(l_int ver, struct l_shmid_ds *linux_shmid, caddr_t uaddr)
+{
+ struct l_shmid64_ds linux_shmid64;
+
+ /*
+ * XXX: This is backwards and loses information in shm_nattch
+ * and shm_segsz. We should probably either expose the BSD
+ * shmid structure directly and convert it to either the
+ * non-64 or 64 variant directly or the code should always
+ * convert to the 64 variant and then truncate values into the
+ * non-64 variant if needed since the 64 variant has more
+ * precision.
+ */
+ if (ver == LINUX_IPC_64) {
+ bzero(&linux_shmid64, sizeof(linux_shmid64));
+
+ linux_ipc_perm_to_ipc64_perm(&linux_shmid->shm_perm,
+ &linux_shmid64.shm_perm);
+
+ linux_shmid64.shm_segsz = linux_shmid->shm_segsz;
+ linux_shmid64.shm_atime = linux_shmid->shm_atime;
+ linux_shmid64.shm_dtime = linux_shmid->shm_dtime;
+ linux_shmid64.shm_ctime = linux_shmid->shm_ctime;
+ linux_shmid64.shm_cpid = linux_shmid->shm_cpid;
+ linux_shmid64.shm_lpid = linux_shmid->shm_lpid;
+ linux_shmid64.shm_nattch = linux_shmid->shm_nattch;
+
+ return (copyout(&linux_shmid64, uaddr, sizeof(linux_shmid64)));
+ } else
+ return (copyout(linux_shmid, uaddr, sizeof(*linux_shmid)));
+}
+
+static int
+linux_shminfo_pushdown(l_int ver, struct l_shminfo *linux_shminfo,
+ caddr_t uaddr)
+{
+ struct l_shminfo64 linux_shminfo64;
+
+ if (ver == LINUX_IPC_64) {
+ bzero(&linux_shminfo64, sizeof(linux_shminfo64));
+
+ linux_shminfo64.shmmax = linux_shminfo->shmmax;
+ linux_shminfo64.shmmin = linux_shminfo->shmmin;
+ linux_shminfo64.shmmni = linux_shminfo->shmmni;
+ linux_shminfo64.shmseg = linux_shminfo->shmseg;
+ linux_shminfo64.shmall = linux_shminfo->shmall;
+
+ return (copyout(&linux_shminfo64, uaddr,
+ sizeof(linux_shminfo64)));
+ } else
+ return (copyout(linux_shminfo, uaddr, sizeof(*linux_shminfo)));
+}
+
+int
+linux_semop(struct thread *td, struct linux_semop_args *args)
+{
+ struct semop_args /* {
+ int semid;
+ struct sembuf *sops;
+ int nsops;
+ } */ bsd_args;
+
+ bsd_args.semid = args->semid;
+ bsd_args.sops = PTRIN(args->tsops);
+ bsd_args.nsops = args->nsops;
+ return (semop(td, &bsd_args));
+}
+
+int
+linux_semget(struct thread *td, struct linux_semget_args *args)
+{
+ struct semget_args /* {
+ key_t key;
+ int nsems;
+ int semflg;
+ } */ bsd_args;
+
+ if (args->nsems < 0)
+ return (EINVAL);
+ bsd_args.key = args->key;
+ bsd_args.nsems = args->nsems;
+ bsd_args.semflg = args->semflg;
+ return (semget(td, &bsd_args));
+}
+
+int
+linux_semctl(struct thread *td, struct linux_semctl_args *args)
+{
+ struct l_semid_ds linux_semid;
+ struct l_seminfo linux_seminfo;
+ struct semid_ds semid;
+ union semun semun;
+ register_t rval;
+ int cmd, error;
+
+ switch (args->cmd & ~LINUX_IPC_64) {
+ case LINUX_IPC_RMID:
+ cmd = IPC_RMID;
+ break;
+ case LINUX_GETNCNT:
+ cmd = GETNCNT;
+ break;
+ case LINUX_GETPID:
+ cmd = GETPID;
+ break;
+ case LINUX_GETVAL:
+ cmd = GETVAL;
+ break;
+ case LINUX_GETZCNT:
+ cmd = GETZCNT;
+ break;
+ case LINUX_SETVAL:
+ cmd = SETVAL;
+ semun.val = args->arg.val;
+ break;
+ case LINUX_IPC_SET:
+ cmd = IPC_SET;
+ error = linux_semid_pullup(args->cmd & LINUX_IPC_64,
+ &linux_semid, PTRIN(args->arg.buf));
+ if (error)
+ return (error);
+ linux_to_bsd_semid_ds(&linux_semid, &semid);
+ semun.buf = &semid;
+ return (kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ td->td_retval));
+ case LINUX_IPC_STAT:
+ case LINUX_SEM_STAT:
+ if ((args->cmd & ~LINUX_IPC_64) == LINUX_IPC_STAT)
+ cmd = IPC_STAT;
+ else
+ cmd = SEM_STAT;
+ semun.buf = &semid;
+ error = kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ &rval);
+ if (error)
+ return (error);
+ bsd_to_linux_semid_ds(&semid, &linux_semid);
+ error = linux_semid_pushdown(args->cmd & LINUX_IPC_64,
+ &linux_semid, PTRIN(args->arg.buf));
+ if (error == 0)
+ td->td_retval[0] = (cmd == SEM_STAT) ? rval : 0;
+ return (error);
+ case LINUX_IPC_INFO:
+ case LINUX_SEM_INFO:
+ bcopy(&seminfo, &linux_seminfo, sizeof(linux_seminfo) );
+/* XXX BSD equivalent?
+#define used_semids 10
+#define used_sems 10
+ linux_seminfo.semusz = used_semids;
+ linux_seminfo.semaem = used_sems;
+*/
+ error = copyout(&linux_seminfo,
+ PTRIN(args->arg.buf), sizeof(linux_seminfo));
+ if (error)
+ return (error);
+ td->td_retval[0] = seminfo.semmni;
+ return (0); /* No need for __semctl call */
+ case LINUX_GETALL:
+ cmd = GETALL;
+ semun.val = args->arg.val;
+ break;
+ case LINUX_SETALL:
+ cmd = SETALL;
+ semun.val = args->arg.val;
+ break;
+ default:
+ linux_msg(td, "ipc type %d is not implemented",
+ args->cmd & ~LINUX_IPC_64);
+ return (EINVAL);
+ }
+ return (kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ td->td_retval));
+}
+
+int
+linux_msgsnd(struct thread *td, struct linux_msgsnd_args *args)
+{
+ const void *msgp;
+ long mtype;
+ l_long lmtype;
+ int error;
+
+ if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax)
+ return (EINVAL);
+ msgp = PTRIN(args->msgp);
+ if ((error = copyin(msgp, &lmtype, sizeof(lmtype))) != 0)
+ return (error);
+ mtype = (long)lmtype;
+ return (kern_msgsnd(td, args->msqid,
+ (const char *)msgp + sizeof(lmtype),
+ args->msgsz, args->msgflg, mtype));
+}
+
+int
+linux_msgrcv(struct thread *td, struct linux_msgrcv_args *args)
+{
+ void *msgp;
+ long mtype;
+ l_long lmtype;
+ int error;
+
+ if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax)
+ return (EINVAL);
+ msgp = PTRIN(args->msgp);
+ if ((error = kern_msgrcv(td, args->msqid,
+ (char *)msgp + sizeof(lmtype), args->msgsz,
+ args->msgtyp, args->msgflg, &mtype)) != 0)
+ return (error);
+ lmtype = (l_long)mtype;
+ return (copyout(&lmtype, msgp, sizeof(lmtype)));
+}
+
+int
+linux_msgget(struct thread *td, struct linux_msgget_args *args)
+{
+ struct msgget_args /* {
+ key_t key;
+ int msgflg;
+ } */ bsd_args;
+
+ bsd_args.key = args->key;
+ bsd_args.msgflg = args->msgflg;
+ return (msgget(td, &bsd_args));
+}
+
+int
+linux_msgctl(struct thread *td, struct linux_msgctl_args *args)
+{
+ int error, bsd_cmd;
+ struct l_msqid_ds linux_msqid;
+ struct msqid_ds bsd_msqid;
+
+ bsd_cmd = args->cmd & ~LINUX_IPC_64;
+ switch (bsd_cmd) {
+ case LINUX_IPC_INFO:
+ case LINUX_MSG_INFO: {
+ struct l_msginfo linux_msginfo;
+
+ /*
+ * XXX MSG_INFO uses the same data structure but returns different
+ * dynamic counters in msgpool, msgmap, and msgtql fields.
+ */
+ linux_msginfo.msgpool = (long)msginfo.msgmni *
+ (long)msginfo.msgmnb / 1024L; /* XXX MSG_INFO. */
+ linux_msginfo.msgmap = msginfo.msgmnb; /* XXX MSG_INFO. */
+ linux_msginfo.msgmax = msginfo.msgmax;
+ linux_msginfo.msgmnb = msginfo.msgmnb;
+ linux_msginfo.msgmni = msginfo.msgmni;
+ linux_msginfo.msgssz = msginfo.msgssz;
+ linux_msginfo.msgtql = msginfo.msgtql; /* XXX MSG_INFO. */
+ linux_msginfo.msgseg = msginfo.msgseg;
+ error = copyout(&linux_msginfo, PTRIN(args->buf),
+ sizeof(linux_msginfo));
+ if (error == 0)
+ td->td_retval[0] = msginfo.msgmni; /* XXX */
+
+ return (error);
+ }
+
+ /*
+ * TODO: implement this
+ * case LINUX_MSG_STAT:
+ */
+ case LINUX_IPC_STAT:
+ /* NOTHING */
+ break;
+
+ case LINUX_IPC_SET:
+ error = linux_msqid_pullup(args->cmd & LINUX_IPC_64,
+ &linux_msqid, PTRIN(args->buf));
+ if (error)
+ return (error);
+ linux_to_bsd_msqid_ds(&linux_msqid, &bsd_msqid);
+ break;
+
+ case LINUX_IPC_RMID:
+ /* NOTHING */
+ break;
+
+ default:
+ return (EINVAL);
+ break;
+ }
+
+ error = kern_msgctl(td, args->msqid, bsd_cmd, &bsd_msqid);
+ if (error != 0)
+ if (bsd_cmd != LINUX_IPC_RMID || error != EINVAL)
+ return (error);
+
+ if (bsd_cmd == LINUX_IPC_STAT) {
+ bsd_to_linux_msqid_ds(&bsd_msqid, &linux_msqid);
+ return (linux_msqid_pushdown(args->cmd & LINUX_IPC_64,
+ &linux_msqid, PTRIN(args->buf)));
+ }
+
+ return (0);
+}
+
+int
+linux_shmat(struct thread *td, struct linux_shmat_args *args)
+{
+ struct shmat_args /* {
+ int shmid;
+ void *shmaddr;
+ int shmflg;
+ } */ bsd_args;
+ int error;
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+ l_uintptr_t addr;
+#endif
+
+ bsd_args.shmid = args->shmid;
+ bsd_args.shmaddr = PTRIN(args->shmaddr);
+ bsd_args.shmflg = args->shmflg;
+ if ((error = shmat(td, &bsd_args)))
+ return (error);
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+ addr = td->td_retval[0];
+ if ((error = copyout(&addr, PTRIN(args->raddr), sizeof(addr))))
+ return (error);
+ td->td_retval[0] = 0;
+#endif
+ return (0);
+}
+
+int
+linux_shmdt(struct thread *td, struct linux_shmdt_args *args)
+{
+ struct shmdt_args /* {
+ void *shmaddr;
+ } */ bsd_args;
+
+ bsd_args.shmaddr = PTRIN(args->shmaddr);
+ return (shmdt(td, &bsd_args));
+}
+
+int
+linux_shmget(struct thread *td, struct linux_shmget_args *args)
+{
+ struct shmget_args /* {
+ key_t key;
+ int size;
+ int shmflg;
+ } */ bsd_args;
+
+ bsd_args.key = args->key;
+ bsd_args.size = args->size;
+ bsd_args.shmflg = args->shmflg;
+ return (shmget(td, &bsd_args));
+}
+
+int
+linux_shmctl(struct thread *td, struct linux_shmctl_args *args)
+{
+ struct l_shmid_ds linux_shmid;
+ struct l_shminfo linux_shminfo;
+ struct l_shm_info linux_shm_info;
+ struct shmid_ds bsd_shmid;
+ int error;
+
+ switch (args->cmd & ~LINUX_IPC_64) {
+
+ case LINUX_IPC_INFO: {
+ struct shminfo bsd_shminfo;
+
+ /* Perform shmctl wanting removed segments lookup */
+ error = kern_shmctl(td, args->shmid, IPC_INFO,
+ (void *)&bsd_shminfo, NULL);
+ if (error)
+ return (error);
+
+ bsd_to_linux_shminfo(&bsd_shminfo, &linux_shminfo);
+
+ return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64,
+ &linux_shminfo, PTRIN(args->buf)));
+ }
+
+ case LINUX_SHM_INFO: {
+ struct shm_info bsd_shm_info;
+
+ /* Perform shmctl wanting removed segments lookup */
+ error = kern_shmctl(td, args->shmid, SHM_INFO,
+ (void *)&bsd_shm_info, NULL);
+ if (error)
+ return (error);
+
+ bsd_to_linux_shm_info(&bsd_shm_info, &linux_shm_info);
+
+ return (copyout(&linux_shm_info, PTRIN(args->buf),
+ sizeof(struct l_shm_info)));
+ }
+
+ case LINUX_IPC_STAT:
+ /* Perform shmctl wanting removed segments lookup */
+ error = kern_shmctl(td, args->shmid, IPC_STAT,
+ (void *)&bsd_shmid, NULL);
+ if (error)
+ return (error);
+
+ bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid);
+
+ return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64,
+ &linux_shmid, PTRIN(args->buf)));
+
+ case LINUX_SHM_STAT:
+ /* Perform shmctl wanting removed segments lookup */
+ error = kern_shmctl(td, args->shmid, IPC_STAT,
+ (void *)&bsd_shmid, NULL);
+ if (error)
+ return (error);
+
+ bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid);
+
+ return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64,
+ &linux_shmid, PTRIN(args->buf)));
+
+ case LINUX_IPC_SET:
+ error = linux_shmid_pullup(args->cmd & LINUX_IPC_64,
+ &linux_shmid, PTRIN(args->buf));
+ if (error)
+ return (error);
+
+ linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid);
+
+ /* Perform shmctl wanting removed segments lookup */
+ return (kern_shmctl(td, args->shmid, IPC_SET,
+ (void *)&bsd_shmid, NULL));
+
+ case LINUX_IPC_RMID: {
+ void *buf;
+
+ if (args->buf == 0)
+ buf = NULL;
+ else {
+ error = linux_shmid_pullup(args->cmd & LINUX_IPC_64,
+ &linux_shmid, PTRIN(args->buf));
+ if (error)
+ return (error);
+ linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid);
+ buf = (void *)&bsd_shmid;
+ }
+ return (kern_shmctl(td, args->shmid, IPC_RMID, buf, NULL));
+ }
+
+ case LINUX_SHM_LOCK:
+ /* FALLTHROUGH */
+ case LINUX_SHM_UNLOCK:
+ /* FALLTHROUGH */
+ default:
+ linux_msg(td, "ipc type %d not implemented",
+ args->cmd & ~LINUX_IPC_64);
+ return (EINVAL);
+ }
+}
+
+MODULE_DEPEND(linux, sysvmsg, 1, 1, 1);
+MODULE_DEPEND(linux, sysvsem, 1, 1, 1);
+MODULE_DEPEND(linux, sysvshm, 1, 1, 1);
diff --git a/sys/compat/linux/linux_ipc.h b/sys/compat/linux/linux_ipc.h
new file mode 100644
index 0000000..4c01342
--- /dev/null
+++ b/sys/compat/linux/linux_ipc.h
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2000 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_IPC_H_
+#define _LINUX_IPC_H_
+
+/*
+ * Version flags for semctl, msgctl, and shmctl commands
+ * These are passed as bitflags or-ed with the actual command
+ */
+#define LINUX_IPC_OLD 0 /* Old version (no 32-bit UID support on many
+ architectures) */
+#define LINUX_IPC_64 0x0100 /* New version (support 32-bit UIDs, bigger
+ message sizes, etc. */
+
+#if defined(__i386__) || defined(__amd64__)
+
+struct linux_msgctl_args
+{
+ l_int msqid;
+ l_int cmd;
+ struct l_msqid_ds *buf;
+};
+
+struct linux_msgget_args
+{
+ l_key_t key;
+ l_int msgflg;
+};
+
+struct linux_msgrcv_args
+{
+ l_int msqid;
+ struct l_msgbuf *msgp;
+ l_size_t msgsz;
+ l_long msgtyp;
+ l_int msgflg;
+};
+
+struct linux_msgsnd_args
+{
+ l_int msqid;
+ struct l_msgbuf *msgp;
+ l_size_t msgsz;
+ l_int msgflg;
+};
+
+struct linux_semctl_args
+{
+ l_int semid;
+ l_int semnum;
+ l_int cmd;
+ union l_semun arg;
+};
+
+struct linux_semget_args
+{
+ l_key_t key;
+ l_int nsems;
+ l_int semflg;
+};
+
+struct linux_semop_args
+{
+ l_int semid;
+ struct l_sembuf *tsops;
+ l_uint nsops;
+};
+
+struct linux_shmat_args
+{
+ l_int shmid;
+ char *shmaddr;
+ l_int shmflg;
+ l_ulong *raddr;
+};
+
+struct linux_shmctl_args
+{
+ l_int shmid;
+ l_int cmd;
+ struct l_shmid_ds *buf;
+};
+
+struct linux_shmdt_args
+{
+ char *shmaddr;
+};
+
+struct linux_shmget_args
+{
+ l_key_t key;
+ l_size_t size;
+ l_int shmflg;
+};
+
+int linux_msgctl(struct thread *, struct linux_msgctl_args *);
+int linux_msgget(struct thread *, struct linux_msgget_args *);
+int linux_msgrcv(struct thread *, struct linux_msgrcv_args *);
+int linux_msgsnd(struct thread *, struct linux_msgsnd_args *);
+
+int linux_semctl(struct thread *, struct linux_semctl_args *);
+int linux_semget(struct thread *, struct linux_semget_args *);
+int linux_semop(struct thread *, struct linux_semop_args *);
+
+int linux_shmat(struct thread *, struct linux_shmat_args *);
+int linux_shmctl(struct thread *, struct linux_shmctl_args *);
+int linux_shmdt(struct thread *, struct linux_shmdt_args *);
+int linux_shmget(struct thread *, struct linux_shmget_args *);
+
+#define LINUX_MSG_INFO 12
+
+#endif /* __i386__ || __amd64__ */
+
+#endif /* _LINUX_IPC_H_ */
diff --git a/sys/compat/linux/linux_mib.c b/sys/compat/linux/linux_mib.c
new file mode 100644
index 0000000..1bb8bb7
--- /dev/null
+++ b/sys/compat/linux/linux_mib.c
@@ -0,0 +1,611 @@
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/jail.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+
+#include "opt_compat.h"
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#else
+#include <machine/../linux/linux.h>
+#endif
+#include <compat/linux/linux_mib.h>
+
+struct linux_prison {
+ char pr_osname[LINUX_MAX_UTSNAME];
+ char pr_osrelease[LINUX_MAX_UTSNAME];
+ int pr_oss_version;
+ int pr_osrel;
+};
+
+static struct linux_prison lprison0 = {
+ .pr_osname = "Linux",
+ .pr_osrelease = "2.6.16",
+ .pr_oss_version = 0x030600,
+ .pr_osrel = 2006016
+};
+
+static unsigned linux_osd_jail_slot;
+
+SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0,
+ "Linux mode");
+
+static int linux_set_osname(struct thread *td, char *osname);
+static int linux_set_osrelease(struct thread *td, char *osrelease);
+static int linux_set_oss_version(struct thread *td, int oss_version);
+
+static int
+linux_sysctl_osname(SYSCTL_HANDLER_ARGS)
+{
+ char osname[LINUX_MAX_UTSNAME];
+ int error;
+
+ linux_get_osname(req->td, osname);
+ error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ error = linux_set_osname(req->td, osname);
+ return (error);
+}
+
+SYSCTL_PROC(_compat_linux, OID_AUTO, osname,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
+ 0, 0, linux_sysctl_osname, "A",
+ "Linux kernel OS name");
+
+static int
+linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS)
+{
+ char osrelease[LINUX_MAX_UTSNAME];
+ int error;
+
+ linux_get_osrelease(req->td, osrelease);
+ error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ error = linux_set_osrelease(req->td, osrelease);
+ return (error);
+}
+
+SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
+ 0, 0, linux_sysctl_osrelease, "A",
+ "Linux kernel OS release");
+
+static int
+linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS)
+{
+ int oss_version;
+ int error;
+
+ oss_version = linux_get_oss_version(req->td);
+ error = sysctl_handle_int(oidp, &oss_version, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ error = linux_set_oss_version(req->td, oss_version);
+ return (error);
+}
+
+SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
+ 0, 0, linux_sysctl_oss_version, "I",
+ "Linux OSS version");
+
+/*
+ * Map the osrelease into integer
+ */
+static int
+linux_map_osrel(char *osrelease, int *osrel)
+{
+ char *sep, *eosrelease;
+ int len, v0, v1, v2, v;
+
+ len = strlen(osrelease);
+ eosrelease = osrelease + len;
+ v0 = strtol(osrelease, &sep, 10);
+ if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.')
+ return (EINVAL);
+ osrelease = sep + 1;
+ v1 = strtol(osrelease, &sep, 10);
+ if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.')
+ return (EINVAL);
+ osrelease = sep + 1;
+ v2 = strtol(osrelease, &sep, 10);
+ if (osrelease == sep || sep != eosrelease)
+ return (EINVAL);
+
+ v = v0 * 1000000 + v1 * 1000 + v2;
+ if (v < 1000000)
+ return (EINVAL);
+
+ *osrel = v;
+ return (0);
+}
+
+/*
+ * Find a prison with Linux info.
+ * Return the Linux info and the (locked) prison.
+ */
+static struct linux_prison *
+linux_find_prison(struct prison *spr, struct prison **prp)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+
+ if (!linux_osd_jail_slot)
+ /* In case osd_register failed. */
+ spr = &prison0;
+ for (pr = spr;; pr = pr->pr_parent) {
+ mtx_lock(&pr->pr_mtx);
+ lpr = (pr == &prison0)
+ ? &lprison0
+ : osd_jail_get(pr, linux_osd_jail_slot);
+ if (lpr != NULL)
+ break;
+ mtx_unlock(&pr->pr_mtx);
+ }
+ *prp = pr;
+ return (lpr);
+}
+
+/*
+ * Ensure a prison has its own Linux info. If lprp is non-null, point it to
+ * the Linux info and lock the prison.
+ */
+static int
+linux_alloc_prison(struct prison *pr, struct linux_prison **lprp)
+{
+ struct prison *ppr;
+ struct linux_prison *lpr, *nlpr;
+ int error;
+
+ /* If this prison already has Linux info, return that. */
+ error = 0;
+ lpr = linux_find_prison(pr, &ppr);
+ if (ppr == pr)
+ goto done;
+ /*
+ * Allocate a new info record. Then check again, in case something
+ * changed during the allocation.
+ */
+ mtx_unlock(&ppr->pr_mtx);
+ nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK);
+ lpr = linux_find_prison(pr, &ppr);
+ if (ppr == pr) {
+ free(nlpr, M_PRISON);
+ goto done;
+ }
+ /* Inherit the initial values from the ancestor. */
+ mtx_lock(&pr->pr_mtx);
+ error = osd_jail_set(pr, linux_osd_jail_slot, nlpr);
+ if (error == 0) {
+ bcopy(lpr, nlpr, sizeof(*lpr));
+ lpr = nlpr;
+ } else {
+ free(nlpr, M_PRISON);
+ lpr = NULL;
+ }
+ mtx_unlock(&ppr->pr_mtx);
+ done:
+ if (lprp != NULL)
+ *lprp = lpr;
+ else
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+}
+
+/*
+ * Jail OSD methods for Linux prison data.
+ */
+static int
+linux_prison_create(void *obj, void *data)
+{
+ struct prison *pr = obj;
+ struct vfsoptlist *opts = data;
+ int jsys;
+
+ if (vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)) == 0 &&
+ jsys == JAIL_SYS_INHERIT)
+ return (0);
+ /*
+ * Inherit a prison's initial values from its parent
+ * (different from JAIL_SYS_INHERIT which also inherits changes).
+ */
+ return linux_alloc_prison(pr, NULL);
+}
+
+static int
+linux_prison_check(void *obj __unused, void *data)
+{
+ struct vfsoptlist *opts = data;
+ char *osname, *osrelease;
+ int error, jsys, len, osrel, oss_version;
+
+ /* Check that the parameters are correct. */
+ error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
+ if (error != ENOENT) {
+ if (error != 0)
+ return (error);
+ if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT)
+ return (EINVAL);
+ }
+ error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
+ if (error != ENOENT) {
+ if (error != 0)
+ return (error);
+ if (len == 0 || osname[len - 1] != '\0')
+ return (EINVAL);
+ if (len > LINUX_MAX_UTSNAME) {
+ vfs_opterror(opts, "linux.osname too long");
+ return (ENAMETOOLONG);
+ }
+ }
+ error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
+ if (error != ENOENT) {
+ if (error != 0)
+ return (error);
+ if (len == 0 || osrelease[len - 1] != '\0')
+ return (EINVAL);
+ if (len > LINUX_MAX_UTSNAME) {
+ vfs_opterror(opts, "linux.osrelease too long");
+ return (ENAMETOOLONG);
+ }
+ error = linux_map_osrel(osrelease, &osrel);
+ if (error != 0) {
+ vfs_opterror(opts, "linux.osrelease format error");
+ return (error);
+ }
+ }
+ error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
+ sizeof(oss_version));
+ return (error == ENOENT ? 0 : error);
+}
+
+static int
+linux_prison_set(void *obj, void *data)
+{
+ struct linux_prison *lpr;
+ struct prison *pr = obj;
+ struct vfsoptlist *opts = data;
+ char *osname, *osrelease;
+ int error, gotversion, jsys, len, oss_version;
+
+ /* Set the parameters, which should be correct. */
+ error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
+ if (error == ENOENT)
+ jsys = -1;
+ error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
+ if (error == ENOENT)
+ osname = NULL;
+ else
+ jsys = JAIL_SYS_NEW;
+ error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
+ if (error == ENOENT)
+ osrelease = NULL;
+ else
+ jsys = JAIL_SYS_NEW;
+ error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
+ sizeof(oss_version));
+ if (error == ENOENT)
+ gotversion = 0;
+ else {
+ gotversion = 1;
+ jsys = JAIL_SYS_NEW;
+ }
+ switch (jsys) {
+ case JAIL_SYS_INHERIT:
+ /* "linux=inherit": inherit the parent's Linux info. */
+ mtx_lock(&pr->pr_mtx);
+ osd_jail_del(pr, linux_osd_jail_slot);
+ mtx_unlock(&pr->pr_mtx);
+ break;
+ case JAIL_SYS_NEW:
+ /*
+ * "linux=new" or "linux.*":
+ * the prison gets its own Linux info.
+ */
+ error = linux_alloc_prison(pr, &lpr);
+ if (error) {
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+ }
+ if (osrelease) {
+ error = linux_map_osrel(osrelease, &lpr->pr_osrel);
+ if (error) {
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+ }
+ strlcpy(lpr->pr_osrelease, osrelease,
+ LINUX_MAX_UTSNAME);
+ }
+ if (osname)
+ strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
+ if (gotversion)
+ lpr->pr_oss_version = oss_version;
+ mtx_unlock(&pr->pr_mtx);
+ }
+ return (0);
+}
+
+SYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters");
+SYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME,
+ "Jail Linux kernel OS name");
+SYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME,
+ "Jail Linux kernel OS release");
+SYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW,
+ "I", "Jail Linux OSS version");
+
+static int
+linux_prison_get(void *obj, void *data)
+{
+ struct linux_prison *lpr;
+ struct prison *ppr;
+ struct prison *pr = obj;
+ struct vfsoptlist *opts = data;
+ int error, i;
+
+ static int version0;
+
+ /* See if this prison is the one with the Linux info. */
+ lpr = linux_find_prison(pr, &ppr);
+ i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
+ error = vfs_setopt(opts, "linux", &i, sizeof(i));
+ if (error != 0 && error != ENOENT)
+ goto done;
+ if (i) {
+ error = vfs_setopts(opts, "linux.osname", lpr->pr_osname);
+ if (error != 0 && error != ENOENT)
+ goto done;
+ error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease);
+ if (error != 0 && error != ENOENT)
+ goto done;
+ error = vfs_setopt(opts, "linux.oss_version",
+ &lpr->pr_oss_version, sizeof(lpr->pr_oss_version));
+ if (error != 0 && error != ENOENT)
+ goto done;
+ } else {
+ /*
+ * If this prison is inheriting its Linux info, report
+ * empty/zero parameters.
+ */
+ error = vfs_setopts(opts, "linux.osname", "");
+ if (error != 0 && error != ENOENT)
+ goto done;
+ error = vfs_setopts(opts, "linux.osrelease", "");
+ if (error != 0 && error != ENOENT)
+ goto done;
+ error = vfs_setopt(opts, "linux.oss_version", &version0,
+ sizeof(lpr->pr_oss_version));
+ if (error != 0 && error != ENOENT)
+ goto done;
+ }
+ error = 0;
+
+ done:
+ mtx_unlock(&ppr->pr_mtx);
+ return (error);
+}
+
+static void
+linux_prison_destructor(void *data)
+{
+
+ free(data, M_PRISON);
+}
+
+void
+linux_osd_jail_register(void)
+{
+ struct prison *pr;
+ osd_method_t methods[PR_MAXMETHOD] = {
+ [PR_METHOD_CREATE] = linux_prison_create,
+ [PR_METHOD_GET] = linux_prison_get,
+ [PR_METHOD_SET] = linux_prison_set,
+ [PR_METHOD_CHECK] = linux_prison_check
+ };
+
+ linux_osd_jail_slot =
+ osd_jail_register(linux_prison_destructor, methods);
+ if (linux_osd_jail_slot > 0) {
+ /* Copy the system linux info to any current prisons. */
+ sx_xlock(&allprison_lock);
+ TAILQ_FOREACH(pr, &allprison, pr_list)
+ (void)linux_alloc_prison(pr, NULL);
+ sx_xunlock(&allprison_lock);
+ }
+}
+
+void
+linux_osd_jail_deregister(void)
+{
+
+ if (linux_osd_jail_slot)
+ osd_jail_deregister(linux_osd_jail_slot);
+}
+
+void
+linux_get_osname(struct thread *td, char *dst)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME);
+ mtx_unlock(&pr->pr_mtx);
+}
+
+static int
+linux_set_osname(struct thread *td, char *osname)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
+ mtx_unlock(&pr->pr_mtx);
+ return (0);
+}
+
+void
+linux_get_osrelease(struct thread *td, char *dst)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME);
+ mtx_unlock(&pr->pr_mtx);
+}
+
+int
+linux_kernver(struct thread *td)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+ int osrel;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ osrel = lpr->pr_osrel;
+ mtx_unlock(&pr->pr_mtx);
+ return (osrel);
+}
+
+static int
+linux_set_osrelease(struct thread *td, char *osrelease)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+ int error;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ error = linux_map_osrel(osrelease, &lpr->pr_osrel);
+ if (error == 0)
+ strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME);
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+}
+
+int
+linux_get_oss_version(struct thread *td)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+ int version;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ version = lpr->pr_oss_version;
+ mtx_unlock(&pr->pr_mtx);
+ return (version);
+}
+
+static int
+linux_set_oss_version(struct thread *td, int oss_version)
+{
+ struct prison *pr;
+ struct linux_prison *lpr;
+
+ lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
+ lpr->pr_oss_version = oss_version;
+ mtx_unlock(&pr->pr_mtx);
+ return (0);
+}
+
+#if defined(DEBUG) || defined(KTR)
+
+u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))];
+
+static int
+linux_debug(int syscall, int toggle, int global)
+{
+
+ if (global) {
+ char c = toggle ? 0 : 0xff;
+
+ memset(linux_debug_map, c, sizeof(linux_debug_map));
+ return (0);
+ }
+ if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL)
+ return (EINVAL);
+ if (toggle)
+ clrbit(linux_debug_map, syscall);
+ else
+ setbit(linux_debug_map, syscall);
+ return (0);
+}
+
+/*
+ * Usage: sysctl linux.debug=<syscall_nr>.<0/1>
+ *
+ * E.g.: sysctl linux.debug=21.0
+ *
+ * As a special case, syscall "all" will apply to all syscalls globally.
+ */
+#define LINUX_MAX_DEBUGSTR 16
+static int
+linux_sysctl_debug(SYSCTL_HANDLER_ARGS)
+{
+ char value[LINUX_MAX_DEBUGSTR], *p;
+ int error, sysc, toggle;
+ int global = 0;
+
+ value[0] = '\0';
+ error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ for (p = value; *p != '\0' && *p != '.'; p++);
+ if (*p == '\0')
+ return (EINVAL);
+ *p++ = '\0';
+ sysc = strtol(value, NULL, 0);
+ toggle = strtol(p, NULL, 0);
+ if (strcmp(value, "all") == 0)
+ global = 1;
+ error = linux_debug(sysc, toggle, global);
+ return (error);
+}
+
+SYSCTL_PROC(_compat_linux, OID_AUTO, debug,
+ CTLTYPE_STRING | CTLFLAG_RW,
+ 0, 0, linux_sysctl_debug, "A",
+ "Linux debugging control");
+
+#endif /* DEBUG || KTR */
diff --git a/sys/compat/linux/linux_mib.h b/sys/compat/linux/linux_mib.h
new file mode 100644
index 0000000..e8eedf9
--- /dev/null
+++ b/sys/compat/linux/linux_mib.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1999 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_MIB_H_
+#define _LINUX_MIB_H_
+
+void linux_osd_jail_register(void);
+void linux_osd_jail_deregister(void);
+
+void linux_get_osname(struct thread *td, char *dst);
+
+void linux_get_osrelease(struct thread *td, char *dst);
+
+int linux_get_oss_version(struct thread *td);
+
+int linux_kernver(struct thread *td);
+
+#define LINUX_KERNVER_2004000 2004000
+#define LINUX_KERNVER_2006000 2006000
+
+#define linux_use26(t) (linux_kernver(t) >= LINUX_KERNVER_2006000)
+
+#endif /* _LINUX_MIB_H_ */
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
new file mode 100644
index 0000000..bbc805d
--- /dev/null
+++ b/sys/compat/linux/linux_misc.c
@@ -0,0 +1,1926 @@
+/*-
+ * Copyright (c) 2002 Doug Rabson
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/blist.h>
+#include <sys/fcntl.h>
+#if defined(__i386__)
+#include <sys/imgact_aout.h>
+#endif
+#include <sys/jail.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/racct.h>
+#include <sys/resourcevar.h>
+#include <sys/sched.h>
+#include <sys/signalvar.h>
+#include <sys/stat.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysctl.h>
+#include <sys/sysproto.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vmmeter.h>
+#include <sys/vnode.h>
+#include <sys/wait.h>
+#include <sys/cpuset.h>
+
+#include <security/mac/mac_framework.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/swap_pager.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_file.h>
+#include <compat/linux/linux_mib.h>
+#include <compat/linux/linux_signal.h>
+#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_sysproto.h>
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_misc.h>
+
+int stclohz; /* Statistics clock frequency */
+
+static unsigned int linux_to_bsd_resource[LINUX_RLIM_NLIMITS] = {
+ RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK,
+ RLIMIT_CORE, RLIMIT_RSS, RLIMIT_NPROC, RLIMIT_NOFILE,
+ RLIMIT_MEMLOCK, RLIMIT_AS
+};
+
+struct l_sysinfo {
+ l_long uptime; /* Seconds since boot */
+ l_ulong loads[3]; /* 1, 5, and 15 minute load averages */
+#define LINUX_SYSINFO_LOADS_SCALE 65536
+ l_ulong totalram; /* Total usable main memory size */
+ l_ulong freeram; /* Available memory size */
+ l_ulong sharedram; /* Amount of shared memory */
+ l_ulong bufferram; /* Memory used by buffers */
+ l_ulong totalswap; /* Total swap space size */
+ l_ulong freeswap; /* swap space still available */
+ l_ushort procs; /* Number of current processes */
+ l_ushort pads;
+ l_ulong totalbig;
+ l_ulong freebig;
+ l_uint mem_unit;
+ char _f[20-2*sizeof(l_long)-sizeof(l_int)]; /* padding */
+};
+int
+linux_sysinfo(struct thread *td, struct linux_sysinfo_args *args)
+{
+ struct l_sysinfo sysinfo;
+ vm_object_t object;
+ int i, j;
+ struct timespec ts;
+
+ getnanouptime(&ts);
+ if (ts.tv_nsec != 0)
+ ts.tv_sec++;
+ sysinfo.uptime = ts.tv_sec;
+
+ /* Use the information from the mib to get our load averages */
+ for (i = 0; i < 3; i++)
+ sysinfo.loads[i] = averunnable.ldavg[i] *
+ LINUX_SYSINFO_LOADS_SCALE / averunnable.fscale;
+
+ sysinfo.totalram = physmem * PAGE_SIZE;
+ sysinfo.freeram = sysinfo.totalram - cnt.v_wire_count * PAGE_SIZE;
+
+ sysinfo.sharedram = 0;
+ mtx_lock(&vm_object_list_mtx);
+ TAILQ_FOREACH(object, &vm_object_list, object_list)
+ if (object->shadow_count > 1)
+ sysinfo.sharedram += object->resident_page_count;
+ mtx_unlock(&vm_object_list_mtx);
+
+ sysinfo.sharedram *= PAGE_SIZE;
+ sysinfo.bufferram = 0;
+
+ swap_pager_status(&i, &j);
+ sysinfo.totalswap = i * PAGE_SIZE;
+ sysinfo.freeswap = (i - j) * PAGE_SIZE;
+
+ sysinfo.procs = nprocs;
+
+ /* The following are only present in newer Linux kernels. */
+ sysinfo.totalbig = 0;
+ sysinfo.freebig = 0;
+ sysinfo.mem_unit = 1;
+
+ return (copyout(&sysinfo, args->info, sizeof(sysinfo)));
+}
+
+int
+linux_alarm(struct thread *td, struct linux_alarm_args *args)
+{
+ struct itimerval it, old_it;
+ u_int secs;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(alarm))
+ printf(ARGS(alarm, "%u"), args->secs);
+#endif
+
+ secs = args->secs;
+
+ if (secs > INT_MAX)
+ secs = INT_MAX;
+
+ it.it_value.tv_sec = (long) secs;
+ it.it_value.tv_usec = 0;
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+ error = kern_setitimer(td, ITIMER_REAL, &it, &old_it);
+ if (error)
+ return (error);
+ if (timevalisset(&old_it.it_value)) {
+ if (old_it.it_value.tv_usec != 0)
+ old_it.it_value.tv_sec++;
+ td->td_retval[0] = old_it.it_value.tv_sec;
+ }
+ return (0);
+}
+
+int
+linux_brk(struct thread *td, struct linux_brk_args *args)
+{
+ struct vmspace *vm = td->td_proc->p_vmspace;
+ vm_offset_t new, old;
+ struct obreak_args /* {
+ char * nsize;
+ } */ tmp;
+
+#ifdef DEBUG
+ if (ldebug(brk))
+ printf(ARGS(brk, "%p"), (void *)(uintptr_t)args->dsend);
+#endif
+ old = (vm_offset_t)vm->vm_daddr + ctob(vm->vm_dsize);
+ new = (vm_offset_t)args->dsend;
+ tmp.nsize = (char *)new;
+ if (((caddr_t)new > vm->vm_daddr) && !obreak(td, &tmp))
+ td->td_retval[0] = (long)new;
+ else
+ td->td_retval[0] = (long)old;
+
+ return (0);
+}
+
+#if defined(__i386__)
+/* XXX: what about amd64/linux32? */
+
+int
+linux_uselib(struct thread *td, struct linux_uselib_args *args)
+{
+ struct nameidata ni;
+ struct vnode *vp;
+ struct exec *a_out;
+ struct vattr attr;
+ vm_offset_t vmaddr;
+ unsigned long file_offset;
+ vm_offset_t buffer;
+ unsigned long bss_size;
+ char *library;
+ int error;
+ int locked, vfslocked;
+
+ LCONVPATHEXIST(td, args->library, &library);
+
+#ifdef DEBUG
+ if (ldebug(uselib))
+ printf(ARGS(uselib, "%s"), library);
+#endif
+
+ a_out = NULL;
+ vfslocked = 0;
+ locked = 0;
+ vp = NULL;
+
+ NDINIT(&ni, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
+ UIO_SYSSPACE, library, td);
+ error = namei(&ni);
+ LFREEPATH(library);
+ if (error)
+ goto cleanup;
+
+ vp = ni.ni_vp;
+ vfslocked = NDHASGIANT(&ni);
+ NDFREE(&ni, NDF_ONLY_PNBUF);
+
+ /*
+ * From here on down, we have a locked vnode that must be unlocked.
+ * XXX: The code below largely duplicates exec_check_permissions().
+ */
+ locked = 1;
+
+ /* Writable? */
+ if (vp->v_writecount) {
+ error = ETXTBSY;
+ goto cleanup;
+ }
+
+ /* Executable? */
+ error = VOP_GETATTR(vp, &attr, td->td_ucred);
+ if (error)
+ goto cleanup;
+
+ if ((vp->v_mount->mnt_flag & MNT_NOEXEC) ||
+ ((attr.va_mode & 0111) == 0) || (attr.va_type != VREG)) {
+ /* EACCESS is what exec(2) returns. */
+ error = ENOEXEC;
+ goto cleanup;
+ }
+
+ /* Sensible size? */
+ if (attr.va_size == 0) {
+ error = ENOEXEC;
+ goto cleanup;
+ }
+
+ /* Can we access it? */
+ error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td);
+ if (error)
+ goto cleanup;
+
+ /*
+ * XXX: This should use vn_open() so that it is properly authorized,
+ * and to reduce code redundancy all over the place here.
+ * XXX: Not really, it duplicates far more of exec_check_permissions()
+ * than vn_open().
+ */
+#ifdef MAC
+ error = mac_vnode_check_open(td->td_ucred, vp, VREAD);
+ if (error)
+ goto cleanup;
+#endif
+ error = VOP_OPEN(vp, FREAD, td->td_ucred, td, NULL);
+ if (error)
+ goto cleanup;
+
+ /* Pull in executable header into kernel_map */
+ error = vm_mmap(kernel_map, (vm_offset_t *)&a_out, PAGE_SIZE,
+ VM_PROT_READ, VM_PROT_READ, 0, OBJT_VNODE, vp, 0);
+ if (error)
+ goto cleanup;
+
+ /* Is it a Linux binary ? */
+ if (((a_out->a_magic >> 16) & 0xff) != 0x64) {
+ error = ENOEXEC;
+ goto cleanup;
+ }
+
+ /*
+ * While we are here, we should REALLY do some more checks
+ */
+
+ /* Set file/virtual offset based on a.out variant. */
+ switch ((int)(a_out->a_magic & 0xffff)) {
+ case 0413: /* ZMAGIC */
+ file_offset = 1024;
+ break;
+ case 0314: /* QMAGIC */
+ file_offset = 0;
+ break;
+ default:
+ error = ENOEXEC;
+ goto cleanup;
+ }
+
+ bss_size = round_page(a_out->a_bss);
+
+ /* Check various fields in header for validity/bounds. */
+ if (a_out->a_text & PAGE_MASK || a_out->a_data & PAGE_MASK) {
+ error = ENOEXEC;
+ goto cleanup;
+ }
+
+ /* text + data can't exceed file size */
+ if (a_out->a_data + a_out->a_text > attr.va_size) {
+ error = EFAULT;
+ goto cleanup;
+ }
+
+ /*
+ * text/data/bss must not exceed limits
+ * XXX - this is not complete. it should check current usage PLUS
+ * the resources needed by this library.
+ */
+ PROC_LOCK(td->td_proc);
+ if (a_out->a_text > maxtsiz ||
+ a_out->a_data + bss_size > lim_cur(td->td_proc, RLIMIT_DATA) ||
+ racct_set(td->td_proc, RACCT_DATA, a_out->a_data +
+ bss_size) != 0) {
+ PROC_UNLOCK(td->td_proc);
+ error = ENOMEM;
+ goto cleanup;
+ }
+ PROC_UNLOCK(td->td_proc);
+
+ /*
+ * Prevent more writers.
+ * XXX: Note that if any of the VM operations fail below we don't
+ * clear this flag.
+ */
+ vp->v_vflag |= VV_TEXT;
+
+ /*
+ * Lock no longer needed
+ */
+ locked = 0;
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ /*
+ * Check if file_offset page aligned. Currently we cannot handle
+ * misalinged file offsets, and so we read in the entire image
+ * (what a waste).
+ */
+ if (file_offset & PAGE_MASK) {
+#ifdef DEBUG
+ printf("uselib: Non page aligned binary %lu\n", file_offset);
+#endif
+ /* Map text+data read/write/execute */
+
+ /* a_entry is the load address and is page aligned */
+ vmaddr = trunc_page(a_out->a_entry);
+
+ /* get anon user mapping, read+write+execute */
+ error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0,
+ &vmaddr, a_out->a_text + a_out->a_data, FALSE, VM_PROT_ALL,
+ VM_PROT_ALL, 0);
+ if (error)
+ goto cleanup;
+
+ /* map file into kernel_map */
+ error = vm_mmap(kernel_map, &buffer,
+ round_page(a_out->a_text + a_out->a_data + file_offset),
+ VM_PROT_READ, VM_PROT_READ, 0, OBJT_VNODE, vp,
+ trunc_page(file_offset));
+ if (error)
+ goto cleanup;
+
+ /* copy from kernel VM space to user space */
+ error = copyout(PTRIN(buffer + file_offset),
+ (void *)vmaddr, a_out->a_text + a_out->a_data);
+
+ /* release temporary kernel space */
+ vm_map_remove(kernel_map, buffer, buffer +
+ round_page(a_out->a_text + a_out->a_data + file_offset));
+
+ if (error)
+ goto cleanup;
+ } else {
+#ifdef DEBUG
+ printf("uselib: Page aligned binary %lu\n", file_offset);
+#endif
+ /*
+ * for QMAGIC, a_entry is 20 bytes beyond the load address
+ * to skip the executable header
+ */
+ vmaddr = trunc_page(a_out->a_entry);
+
+ /*
+ * Map it all into the process's space as a single
+ * copy-on-write "data" segment.
+ */
+ error = vm_mmap(&td->td_proc->p_vmspace->vm_map, &vmaddr,
+ a_out->a_text + a_out->a_data, VM_PROT_ALL, VM_PROT_ALL,
+ MAP_PRIVATE | MAP_FIXED, OBJT_VNODE, vp, file_offset);
+ if (error)
+ goto cleanup;
+ }
+#ifdef DEBUG
+ printf("mem=%08lx = %08lx %08lx\n", (long)vmaddr, ((long *)vmaddr)[0],
+ ((long *)vmaddr)[1]);
+#endif
+ if (bss_size != 0) {
+ /* Calculate BSS start address */
+ vmaddr = trunc_page(a_out->a_entry) + a_out->a_text +
+ a_out->a_data;
+
+ /* allocate some 'anon' space */
+ error = vm_map_find(&td->td_proc->p_vmspace->vm_map, NULL, 0,
+ &vmaddr, bss_size, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0);
+ if (error)
+ goto cleanup;
+ }
+
+cleanup:
+ /* Unlock vnode if needed */
+ if (locked) {
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+ }
+
+ /* Release the kernel mapping. */
+ if (a_out)
+ vm_map_remove(kernel_map, (vm_offset_t)a_out,
+ (vm_offset_t)a_out + PAGE_SIZE);
+
+ return (error);
+}
+
+#endif /* __i386__ */
+
+int
+linux_select(struct thread *td, struct linux_select_args *args)
+{
+ l_timeval ltv;
+ struct timeval tv0, tv1, utv, *tvp;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(select))
+ printf(ARGS(select, "%d, %p, %p, %p, %p"), args->nfds,
+ (void *)args->readfds, (void *)args->writefds,
+ (void *)args->exceptfds, (void *)args->timeout);
+#endif
+
+ /*
+ * Store current time for computation of the amount of
+ * time left.
+ */
+ if (args->timeout) {
+ if ((error = copyin(args->timeout, &ltv, sizeof(ltv))))
+ goto select_out;
+ utv.tv_sec = ltv.tv_sec;
+ utv.tv_usec = ltv.tv_usec;
+#ifdef DEBUG
+ if (ldebug(select))
+ printf(LMSG("incoming timeout (%jd/%ld)"),
+ (intmax_t)utv.tv_sec, utv.tv_usec);
+#endif
+
+ if (itimerfix(&utv)) {
+ /*
+ * The timeval was invalid. Convert it to something
+ * valid that will act as it does under Linux.
+ */
+ utv.tv_sec += utv.tv_usec / 1000000;
+ utv.tv_usec %= 1000000;
+ if (utv.tv_usec < 0) {
+ utv.tv_sec -= 1;
+ utv.tv_usec += 1000000;
+ }
+ if (utv.tv_sec < 0)
+ timevalclear(&utv);
+ }
+ microtime(&tv0);
+ tvp = &utv;
+ } else
+ tvp = NULL;
+
+ error = kern_select(td, args->nfds, args->readfds, args->writefds,
+ args->exceptfds, tvp, sizeof(l_int) * 8);
+
+#ifdef DEBUG
+ if (ldebug(select))
+ printf(LMSG("real select returns %d"), error);
+#endif
+ if (error)
+ goto select_out;
+
+ if (args->timeout) {
+ if (td->td_retval[0]) {
+ /*
+ * Compute how much time was left of the timeout,
+ * by subtracting the current time and the time
+ * before we started the call, and subtracting
+ * that result from the user-supplied value.
+ */
+ microtime(&tv1);
+ timevalsub(&tv1, &tv0);
+ timevalsub(&utv, &tv1);
+ if (utv.tv_sec < 0)
+ timevalclear(&utv);
+ } else
+ timevalclear(&utv);
+#ifdef DEBUG
+ if (ldebug(select))
+ printf(LMSG("outgoing timeout (%jd/%ld)"),
+ (intmax_t)utv.tv_sec, utv.tv_usec);
+#endif
+ ltv.tv_sec = utv.tv_sec;
+ ltv.tv_usec = utv.tv_usec;
+ if ((error = copyout(&ltv, args->timeout, sizeof(ltv))))
+ goto select_out;
+ }
+
+select_out:
+#ifdef DEBUG
+ if (ldebug(select))
+ printf(LMSG("select_out -> %d"), error);
+#endif
+ return (error);
+}
+
+int
+linux_mremap(struct thread *td, struct linux_mremap_args *args)
+{
+ struct munmap_args /* {
+ void *addr;
+ size_t len;
+ } */ bsd_args;
+ int error = 0;
+
+#ifdef DEBUG
+ if (ldebug(mremap))
+ printf(ARGS(mremap, "%p, %08lx, %08lx, %08lx"),
+ (void *)(uintptr_t)args->addr,
+ (unsigned long)args->old_len,
+ (unsigned long)args->new_len,
+ (unsigned long)args->flags);
+#endif
+
+ if (args->flags & ~(LINUX_MREMAP_FIXED | LINUX_MREMAP_MAYMOVE)) {
+ td->td_retval[0] = 0;
+ return (EINVAL);
+ }
+
+ /*
+ * Check for the page alignment.
+ * Linux defines PAGE_MASK to be FreeBSD ~PAGE_MASK.
+ */
+ if (args->addr & PAGE_MASK) {
+ td->td_retval[0] = 0;
+ return (EINVAL);
+ }
+
+ args->new_len = round_page(args->new_len);
+ args->old_len = round_page(args->old_len);
+
+ if (args->new_len > args->old_len) {
+ td->td_retval[0] = 0;
+ return (ENOMEM);
+ }
+
+ if (args->new_len < args->old_len) {
+ bsd_args.addr =
+ (caddr_t)((uintptr_t)args->addr + args->new_len);
+ bsd_args.len = args->old_len - args->new_len;
+ error = munmap(td, &bsd_args);
+ }
+
+ td->td_retval[0] = error ? 0 : (uintptr_t)args->addr;
+ return (error);
+}
+
+#define LINUX_MS_ASYNC 0x0001
+#define LINUX_MS_INVALIDATE 0x0002
+#define LINUX_MS_SYNC 0x0004
+
+int
+linux_msync(struct thread *td, struct linux_msync_args *args)
+{
+ struct msync_args bsd_args;
+
+ bsd_args.addr = (caddr_t)(uintptr_t)args->addr;
+ bsd_args.len = (uintptr_t)args->len;
+ bsd_args.flags = args->fl & ~LINUX_MS_SYNC;
+
+ return (msync(td, &bsd_args));
+}
+
+int
+linux_time(struct thread *td, struct linux_time_args *args)
+{
+ struct timeval tv;
+ l_time_t tm;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(time))
+ printf(ARGS(time, "*"));
+#endif
+
+ microtime(&tv);
+ tm = tv.tv_sec;
+ if (args->tm && (error = copyout(&tm, args->tm, sizeof(tm))))
+ return (error);
+ td->td_retval[0] = tm;
+ return (0);
+}
+
+struct l_times_argv {
+ l_clock_t tms_utime;
+ l_clock_t tms_stime;
+ l_clock_t tms_cutime;
+ l_clock_t tms_cstime;
+};
+
+
+/*
+ * Glibc versions prior to 2.2.1 always use hard-coded CLK_TCK value.
+ * Since 2.2.1 Glibc uses value exported from kernel via AT_CLKTCK
+ * auxiliary vector entry.
+ */
+#define CLK_TCK 100
+
+#define CONVOTCK(r) (r.tv_sec * CLK_TCK + r.tv_usec / (1000000 / CLK_TCK))
+#define CONVNTCK(r) (r.tv_sec * stclohz + r.tv_usec / (1000000 / stclohz))
+
+#define CONVTCK(r) (linux_kernver(td) >= LINUX_KERNVER_2004000 ? \
+ CONVNTCK(r) : CONVOTCK(r))
+
+int
+linux_times(struct thread *td, struct linux_times_args *args)
+{
+ struct timeval tv, utime, stime, cutime, cstime;
+ struct l_times_argv tms;
+ struct proc *p;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(times))
+ printf(ARGS(times, "*"));
+#endif
+
+ if (args->buf != NULL) {
+ p = td->td_proc;
+ PROC_LOCK(p);
+ PROC_SLOCK(p);
+ calcru(p, &utime, &stime);
+ PROC_SUNLOCK(p);
+ calccru(p, &cutime, &cstime);
+ PROC_UNLOCK(p);
+
+ tms.tms_utime = CONVTCK(utime);
+ tms.tms_stime = CONVTCK(stime);
+
+ tms.tms_cutime = CONVTCK(cutime);
+ tms.tms_cstime = CONVTCK(cstime);
+
+ if ((error = copyout(&tms, args->buf, sizeof(tms))))
+ return (error);
+ }
+
+ microuptime(&tv);
+ td->td_retval[0] = (int)CONVTCK(tv);
+ return (0);
+}
+
+int
+linux_newuname(struct thread *td, struct linux_newuname_args *args)
+{
+ struct l_new_utsname utsname;
+ char osname[LINUX_MAX_UTSNAME];
+ char osrelease[LINUX_MAX_UTSNAME];
+ char *p;
+
+#ifdef DEBUG
+ if (ldebug(newuname))
+ printf(ARGS(newuname, "*"));
+#endif
+
+ linux_get_osname(td, osname);
+ linux_get_osrelease(td, osrelease);
+
+ bzero(&utsname, sizeof(utsname));
+ strlcpy(utsname.sysname, osname, LINUX_MAX_UTSNAME);
+ getcredhostname(td->td_ucred, utsname.nodename, LINUX_MAX_UTSNAME);
+ getcreddomainname(td->td_ucred, utsname.domainname, LINUX_MAX_UTSNAME);
+ strlcpy(utsname.release, osrelease, LINUX_MAX_UTSNAME);
+ strlcpy(utsname.version, version, LINUX_MAX_UTSNAME);
+ for (p = utsname.version; *p != '\0'; ++p)
+ if (*p == '\n') {
+ *p = '\0';
+ break;
+ }
+ strlcpy(utsname.machine, linux_platform, LINUX_MAX_UTSNAME);
+
+ return (copyout(&utsname, args->buf, sizeof(utsname)));
+}
+
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+struct l_utimbuf {
+ l_time_t l_actime;
+ l_time_t l_modtime;
+};
+
+int
+linux_utime(struct thread *td, struct linux_utime_args *args)
+{
+ struct timeval tv[2], *tvp;
+ struct l_utimbuf lut;
+ char *fname;
+ int error;
+
+ LCONVPATHEXIST(td, args->fname, &fname);
+
+#ifdef DEBUG
+ if (ldebug(utime))
+ printf(ARGS(utime, "%s, *"), fname);
+#endif
+
+ if (args->times) {
+ if ((error = copyin(args->times, &lut, sizeof lut))) {
+ LFREEPATH(fname);
+ return (error);
+ }
+ tv[0].tv_sec = lut.l_actime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = lut.l_modtime;
+ tv[1].tv_usec = 0;
+ tvp = tv;
+ } else
+ tvp = NULL;
+
+ error = kern_utimes(td, fname, UIO_SYSSPACE, tvp, UIO_SYSSPACE);
+ LFREEPATH(fname);
+ return (error);
+}
+
+int
+linux_utimes(struct thread *td, struct linux_utimes_args *args)
+{
+ l_timeval ltv[2];
+ struct timeval tv[2], *tvp = NULL;
+ char *fname;
+ int error;
+
+ LCONVPATHEXIST(td, args->fname, &fname);
+
+#ifdef DEBUG
+ if (ldebug(utimes))
+ printf(ARGS(utimes, "%s, *"), fname);
+#endif
+
+ if (args->tptr != NULL) {
+ if ((error = copyin(args->tptr, ltv, sizeof ltv))) {
+ LFREEPATH(fname);
+ return (error);
+ }
+ tv[0].tv_sec = ltv[0].tv_sec;
+ tv[0].tv_usec = ltv[0].tv_usec;
+ tv[1].tv_sec = ltv[1].tv_sec;
+ tv[1].tv_usec = ltv[1].tv_usec;
+ tvp = tv;
+ }
+
+ error = kern_utimes(td, fname, UIO_SYSSPACE, tvp, UIO_SYSSPACE);
+ LFREEPATH(fname);
+ return (error);
+}
+
+int
+linux_futimesat(struct thread *td, struct linux_futimesat_args *args)
+{
+ l_timeval ltv[2];
+ struct timeval tv[2], *tvp = NULL;
+ char *fname;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &fname, dfd);
+
+#ifdef DEBUG
+ if (ldebug(futimesat))
+ printf(ARGS(futimesat, "%s, *"), fname);
+#endif
+
+ if (args->utimes != NULL) {
+ if ((error = copyin(args->utimes, ltv, sizeof ltv))) {
+ LFREEPATH(fname);
+ return (error);
+ }
+ tv[0].tv_sec = ltv[0].tv_sec;
+ tv[0].tv_usec = ltv[0].tv_usec;
+ tv[1].tv_sec = ltv[1].tv_sec;
+ tv[1].tv_usec = ltv[1].tv_usec;
+ tvp = tv;
+ }
+
+ error = kern_utimesat(td, dfd, fname, UIO_SYSSPACE, tvp, UIO_SYSSPACE);
+ LFREEPATH(fname);
+ return (error);
+}
+#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+
+int
+linux_common_wait(struct thread *td, int pid, int *status,
+ int options, struct rusage *ru)
+{
+ int error, tmpstat;
+
+ error = kern_wait(td, pid, &tmpstat, options, ru);
+ if (error)
+ return (error);
+
+ if (status) {
+ tmpstat &= 0xffff;
+ if (WIFSIGNALED(tmpstat))
+ tmpstat = (tmpstat & 0xffffff80) |
+ BSD_TO_LINUX_SIGNAL(WTERMSIG(tmpstat));
+ else if (WIFSTOPPED(tmpstat))
+ tmpstat = (tmpstat & 0xffff00ff) |
+ (BSD_TO_LINUX_SIGNAL(WSTOPSIG(tmpstat)) << 8);
+ error = copyout(&tmpstat, status, sizeof(int));
+ }
+
+ return (error);
+}
+
+int
+linux_waitpid(struct thread *td, struct linux_waitpid_args *args)
+{
+ int options;
+
+#ifdef DEBUG
+ if (ldebug(waitpid))
+ printf(ARGS(waitpid, "%d, %p, %d"),
+ args->pid, (void *)args->status, args->options);
+#endif
+ /*
+ * this is necessary because the test in kern_wait doesn't work
+ * because we mess with the options here
+ */
+ if (args->options & ~(WUNTRACED | WNOHANG | WCONTINUED | __WCLONE))
+ return (EINVAL);
+
+ options = (args->options & (WNOHANG | WUNTRACED));
+ /* WLINUXCLONE should be equal to __WCLONE, but we make sure */
+ if (args->options & __WCLONE)
+ options |= WLINUXCLONE;
+
+ return (linux_common_wait(td, args->pid, args->status, options, NULL));
+}
+
+
+int
+linux_mknod(struct thread *td, struct linux_mknod_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHCREAT(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(mknod))
+ printf(ARGS(mknod, "%s, %d, %d"), path, args->mode, args->dev);
+#endif
+
+ switch (args->mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFSOCK:
+ error = kern_mkfifo(td, path, UIO_SYSSPACE, args->mode);
+ break;
+
+ case S_IFCHR:
+ case S_IFBLK:
+ error = kern_mknod(td, path, UIO_SYSSPACE, args->mode,
+ args->dev);
+ break;
+
+ case S_IFDIR:
+ error = EPERM;
+ break;
+
+ case 0:
+ args->mode |= S_IFREG;
+ /* FALLTHROUGH */
+ case S_IFREG:
+ error = kern_open(td, path, UIO_SYSSPACE,
+ O_WRONLY | O_CREAT | O_TRUNC, args->mode);
+ if (error == 0)
+ kern_close(td, td->td_retval[0]);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_mknodat(struct thread *td, struct linux_mknodat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHCREAT_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(mknodat))
+ printf(ARGS(mknodat, "%s, %d, %d"), path, args->mode, args->dev);
+#endif
+
+ switch (args->mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFSOCK:
+ error = kern_mkfifoat(td, dfd, path, UIO_SYSSPACE, args->mode);
+ break;
+
+ case S_IFCHR:
+ case S_IFBLK:
+ error = kern_mknodat(td, dfd, path, UIO_SYSSPACE, args->mode,
+ args->dev);
+ break;
+
+ case S_IFDIR:
+ error = EPERM;
+ break;
+
+ case 0:
+ args->mode |= S_IFREG;
+ /* FALLTHROUGH */
+ case S_IFREG:
+ error = kern_openat(td, dfd, path, UIO_SYSSPACE,
+ O_WRONLY | O_CREAT | O_TRUNC, args->mode);
+ if (error == 0)
+ kern_close(td, td->td_retval[0]);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ LFREEPATH(path);
+ return (error);
+}
+
+/*
+ * UGH! This is just about the dumbest idea I've ever heard!!
+ */
+int
+linux_personality(struct thread *td, struct linux_personality_args *args)
+{
+#ifdef DEBUG
+ if (ldebug(personality))
+ printf(ARGS(personality, "%lu"), (unsigned long)args->per);
+#endif
+ if (args->per != 0)
+ return (EINVAL);
+
+ /* Yes Jim, it's still a Linux... */
+ td->td_retval[0] = 0;
+ return (0);
+}
+
+struct l_itimerval {
+ l_timeval it_interval;
+ l_timeval it_value;
+};
+
+#define B2L_ITIMERVAL(bip, lip) \
+ (bip)->it_interval.tv_sec = (lip)->it_interval.tv_sec; \
+ (bip)->it_interval.tv_usec = (lip)->it_interval.tv_usec; \
+ (bip)->it_value.tv_sec = (lip)->it_value.tv_sec; \
+ (bip)->it_value.tv_usec = (lip)->it_value.tv_usec;
+
+int
+linux_setitimer(struct thread *td, struct linux_setitimer_args *uap)
+{
+ int error;
+ struct l_itimerval ls;
+ struct itimerval aitv, oitv;
+
+#ifdef DEBUG
+ if (ldebug(setitimer))
+ printf(ARGS(setitimer, "%p, %p"),
+ (void *)uap->itv, (void *)uap->oitv);
+#endif
+
+ if (uap->itv == NULL) {
+ uap->itv = uap->oitv;
+ return (linux_getitimer(td, (struct linux_getitimer_args *)uap));
+ }
+
+ error = copyin(uap->itv, &ls, sizeof(ls));
+ if (error != 0)
+ return (error);
+ B2L_ITIMERVAL(&aitv, &ls);
+#ifdef DEBUG
+ if (ldebug(setitimer)) {
+ printf("setitimer: value: sec: %jd, usec: %ld\n",
+ (intmax_t)aitv.it_value.tv_sec, aitv.it_value.tv_usec);
+ printf("setitimer: interval: sec: %jd, usec: %ld\n",
+ (intmax_t)aitv.it_interval.tv_sec, aitv.it_interval.tv_usec);
+ }
+#endif
+ error = kern_setitimer(td, uap->which, &aitv, &oitv);
+ if (error != 0 || uap->oitv == NULL)
+ return (error);
+ B2L_ITIMERVAL(&ls, &oitv);
+
+ return (copyout(&ls, uap->oitv, sizeof(ls)));
+}
+
+int
+linux_getitimer(struct thread *td, struct linux_getitimer_args *uap)
+{
+ int error;
+ struct l_itimerval ls;
+ struct itimerval aitv;
+
+#ifdef DEBUG
+ if (ldebug(getitimer))
+ printf(ARGS(getitimer, "%p"), (void *)uap->itv);
+#endif
+ error = kern_getitimer(td, uap->which, &aitv);
+ if (error != 0)
+ return (error);
+ B2L_ITIMERVAL(&ls, &aitv);
+ return (copyout(&ls, uap->itv, sizeof(ls)));
+}
+
+int
+linux_nice(struct thread *td, struct linux_nice_args *args)
+{
+ struct setpriority_args bsd_args;
+
+ bsd_args.which = PRIO_PROCESS;
+ bsd_args.who = 0; /* current process */
+ bsd_args.prio = args->inc;
+ return (setpriority(td, &bsd_args));
+}
+
+int
+linux_setgroups(struct thread *td, struct linux_setgroups_args *args)
+{
+ struct ucred *newcred, *oldcred;
+ l_gid_t *linux_gidset;
+ gid_t *bsd_gidset;
+ int ngrp, error;
+ struct proc *p;
+
+ ngrp = args->gidsetsize;
+ if (ngrp < 0 || ngrp >= ngroups_max + 1)
+ return (EINVAL);
+ linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_TEMP, M_WAITOK);
+ error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t));
+ if (error)
+ goto out;
+ newcred = crget();
+ p = td->td_proc;
+ PROC_LOCK(p);
+ oldcred = crcopysafe(p, newcred);
+
+ /*
+ * cr_groups[0] holds egid. Setting the whole set from
+ * the supplied set will cause egid to be changed too.
+ * Keep cr_groups[0] unchanged to prevent that.
+ */
+
+ if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS, 0)) != 0) {
+ PROC_UNLOCK(p);
+ crfree(newcred);
+ goto out;
+ }
+
+ if (ngrp > 0) {
+ newcred->cr_ngroups = ngrp + 1;
+
+ bsd_gidset = newcred->cr_groups;
+ ngrp--;
+ while (ngrp >= 0) {
+ bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
+ ngrp--;
+ }
+ } else
+ newcred->cr_ngroups = 1;
+
+ setsugid(p);
+ p->p_ucred = newcred;
+ PROC_UNLOCK(p);
+ crfree(oldcred);
+ error = 0;
+out:
+ free(linux_gidset, M_TEMP);
+ return (error);
+}
+
+int
+linux_getgroups(struct thread *td, struct linux_getgroups_args *args)
+{
+ struct ucred *cred;
+ l_gid_t *linux_gidset;
+ gid_t *bsd_gidset;
+ int bsd_gidsetsz, ngrp, error;
+
+ cred = td->td_ucred;
+ bsd_gidset = cred->cr_groups;
+ bsd_gidsetsz = cred->cr_ngroups - 1;
+
+ /*
+ * cr_groups[0] holds egid. Returning the whole set
+ * here will cause a duplicate. Exclude cr_groups[0]
+ * to prevent that.
+ */
+
+ if ((ngrp = args->gidsetsize) == 0) {
+ td->td_retval[0] = bsd_gidsetsz;
+ return (0);
+ }
+
+ if (ngrp < bsd_gidsetsz)
+ return (EINVAL);
+
+ ngrp = 0;
+ linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
+ M_TEMP, M_WAITOK);
+ while (ngrp < bsd_gidsetsz) {
+ linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
+ ngrp++;
+ }
+
+ error = copyout(linux_gidset, args->grouplist, ngrp * sizeof(l_gid_t));
+ free(linux_gidset, M_TEMP);
+ if (error)
+ return (error);
+
+ td->td_retval[0] = ngrp;
+ return (0);
+}
+
+int
+linux_setrlimit(struct thread *td, struct linux_setrlimit_args *args)
+{
+ struct rlimit bsd_rlim;
+ struct l_rlimit rlim;
+ u_int which;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(setrlimit))
+ printf(ARGS(setrlimit, "%d, %p"),
+ args->resource, (void *)args->rlim);
+#endif
+
+ if (args->resource >= LINUX_RLIM_NLIMITS)
+ return (EINVAL);
+
+ which = linux_to_bsd_resource[args->resource];
+ if (which == -1)
+ return (EINVAL);
+
+ error = copyin(args->rlim, &rlim, sizeof(rlim));
+ if (error)
+ return (error);
+
+ bsd_rlim.rlim_cur = (rlim_t)rlim.rlim_cur;
+ bsd_rlim.rlim_max = (rlim_t)rlim.rlim_max;
+ return (kern_setrlimit(td, which, &bsd_rlim));
+}
+
+int
+linux_old_getrlimit(struct thread *td, struct linux_old_getrlimit_args *args)
+{
+ struct l_rlimit rlim;
+ struct proc *p = td->td_proc;
+ struct rlimit bsd_rlim;
+ u_int which;
+
+#ifdef DEBUG
+ if (ldebug(old_getrlimit))
+ printf(ARGS(old_getrlimit, "%d, %p"),
+ args->resource, (void *)args->rlim);
+#endif
+
+ if (args->resource >= LINUX_RLIM_NLIMITS)
+ return (EINVAL);
+
+ which = linux_to_bsd_resource[args->resource];
+ if (which == -1)
+ return (EINVAL);
+
+ PROC_LOCK(p);
+ lim_rlimit(p, which, &bsd_rlim);
+ PROC_UNLOCK(p);
+
+#ifdef COMPAT_LINUX32
+ rlim.rlim_cur = (unsigned int)bsd_rlim.rlim_cur;
+ if (rlim.rlim_cur == UINT_MAX)
+ rlim.rlim_cur = INT_MAX;
+ rlim.rlim_max = (unsigned int)bsd_rlim.rlim_max;
+ if (rlim.rlim_max == UINT_MAX)
+ rlim.rlim_max = INT_MAX;
+#else
+ rlim.rlim_cur = (unsigned long)bsd_rlim.rlim_cur;
+ if (rlim.rlim_cur == ULONG_MAX)
+ rlim.rlim_cur = LONG_MAX;
+ rlim.rlim_max = (unsigned long)bsd_rlim.rlim_max;
+ if (rlim.rlim_max == ULONG_MAX)
+ rlim.rlim_max = LONG_MAX;
+#endif
+ return (copyout(&rlim, args->rlim, sizeof(rlim)));
+}
+
+int
+linux_getrlimit(struct thread *td, struct linux_getrlimit_args *args)
+{
+ struct l_rlimit rlim;
+ struct proc *p = td->td_proc;
+ struct rlimit bsd_rlim;
+ u_int which;
+
+#ifdef DEBUG
+ if (ldebug(getrlimit))
+ printf(ARGS(getrlimit, "%d, %p"),
+ args->resource, (void *)args->rlim);
+#endif
+
+ if (args->resource >= LINUX_RLIM_NLIMITS)
+ return (EINVAL);
+
+ which = linux_to_bsd_resource[args->resource];
+ if (which == -1)
+ return (EINVAL);
+
+ PROC_LOCK(p);
+ lim_rlimit(p, which, &bsd_rlim);
+ PROC_UNLOCK(p);
+
+ rlim.rlim_cur = (l_ulong)bsd_rlim.rlim_cur;
+ rlim.rlim_max = (l_ulong)bsd_rlim.rlim_max;
+ return (copyout(&rlim, args->rlim, sizeof(rlim)));
+}
+
+int
+linux_sched_setscheduler(struct thread *td,
+ struct linux_sched_setscheduler_args *args)
+{
+ struct sched_setscheduler_args bsd;
+
+#ifdef DEBUG
+ if (ldebug(sched_setscheduler))
+ printf(ARGS(sched_setscheduler, "%d, %d, %p"),
+ args->pid, args->policy, (const void *)args->param);
+#endif
+
+ switch (args->policy) {
+ case LINUX_SCHED_OTHER:
+ bsd.policy = SCHED_OTHER;
+ break;
+ case LINUX_SCHED_FIFO:
+ bsd.policy = SCHED_FIFO;
+ break;
+ case LINUX_SCHED_RR:
+ bsd.policy = SCHED_RR;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ bsd.pid = args->pid;
+ bsd.param = (struct sched_param *)args->param;
+ return (sched_setscheduler(td, &bsd));
+}
+
+int
+linux_sched_getscheduler(struct thread *td,
+ struct linux_sched_getscheduler_args *args)
+{
+ struct sched_getscheduler_args bsd;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(sched_getscheduler))
+ printf(ARGS(sched_getscheduler, "%d"), args->pid);
+#endif
+
+ bsd.pid = args->pid;
+ error = sched_getscheduler(td, &bsd);
+
+ switch (td->td_retval[0]) {
+ case SCHED_OTHER:
+ td->td_retval[0] = LINUX_SCHED_OTHER;
+ break;
+ case SCHED_FIFO:
+ td->td_retval[0] = LINUX_SCHED_FIFO;
+ break;
+ case SCHED_RR:
+ td->td_retval[0] = LINUX_SCHED_RR;
+ break;
+ }
+
+ return (error);
+}
+
+int
+linux_sched_get_priority_max(struct thread *td,
+ struct linux_sched_get_priority_max_args *args)
+{
+ struct sched_get_priority_max_args bsd;
+
+#ifdef DEBUG
+ if (ldebug(sched_get_priority_max))
+ printf(ARGS(sched_get_priority_max, "%d"), args->policy);
+#endif
+
+ switch (args->policy) {
+ case LINUX_SCHED_OTHER:
+ bsd.policy = SCHED_OTHER;
+ break;
+ case LINUX_SCHED_FIFO:
+ bsd.policy = SCHED_FIFO;
+ break;
+ case LINUX_SCHED_RR:
+ bsd.policy = SCHED_RR;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (sched_get_priority_max(td, &bsd));
+}
+
+int
+linux_sched_get_priority_min(struct thread *td,
+ struct linux_sched_get_priority_min_args *args)
+{
+ struct sched_get_priority_min_args bsd;
+
+#ifdef DEBUG
+ if (ldebug(sched_get_priority_min))
+ printf(ARGS(sched_get_priority_min, "%d"), args->policy);
+#endif
+
+ switch (args->policy) {
+ case LINUX_SCHED_OTHER:
+ bsd.policy = SCHED_OTHER;
+ break;
+ case LINUX_SCHED_FIFO:
+ bsd.policy = SCHED_FIFO;
+ break;
+ case LINUX_SCHED_RR:
+ bsd.policy = SCHED_RR;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (sched_get_priority_min(td, &bsd));
+}
+
+#define REBOOT_CAD_ON 0x89abcdef
+#define REBOOT_CAD_OFF 0
+#define REBOOT_HALT 0xcdef0123
+#define REBOOT_RESTART 0x01234567
+#define REBOOT_RESTART2 0xA1B2C3D4
+#define REBOOT_POWEROFF 0x4321FEDC
+#define REBOOT_MAGIC1 0xfee1dead
+#define REBOOT_MAGIC2 0x28121969
+#define REBOOT_MAGIC2A 0x05121996
+#define REBOOT_MAGIC2B 0x16041998
+
+int
+linux_reboot(struct thread *td, struct linux_reboot_args *args)
+{
+ struct reboot_args bsd_args;
+
+#ifdef DEBUG
+ if (ldebug(reboot))
+ printf(ARGS(reboot, "0x%x"), args->cmd);
+#endif
+
+ if (args->magic1 != REBOOT_MAGIC1)
+ return (EINVAL);
+
+ switch (args->magic2) {
+ case REBOOT_MAGIC2:
+ case REBOOT_MAGIC2A:
+ case REBOOT_MAGIC2B:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ switch (args->cmd) {
+ case REBOOT_CAD_ON:
+ case REBOOT_CAD_OFF:
+ return (priv_check(td, PRIV_REBOOT));
+ case REBOOT_HALT:
+ bsd_args.opt = RB_HALT;
+ break;
+ case REBOOT_RESTART:
+ case REBOOT_RESTART2:
+ bsd_args.opt = 0;
+ break;
+ case REBOOT_POWEROFF:
+ bsd_args.opt = RB_POWEROFF;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (reboot(td, &bsd_args));
+}
+
+
+/*
+ * The FreeBSD native getpid(2), getgid(2) and getuid(2) also modify
+ * td->td_retval[1] when COMPAT_43 is defined. This clobbers registers that
+ * are assumed to be preserved. The following lightweight syscalls fixes
+ * this. See also linux_getgid16() and linux_getuid16() in linux_uid16.c
+ *
+ * linux_getpid() - MP SAFE
+ * linux_getgid() - MP SAFE
+ * linux_getuid() - MP SAFE
+ */
+
+int
+linux_getpid(struct thread *td, struct linux_getpid_args *args)
+{
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(getpid))
+ printf(ARGS(getpid, ""));
+#endif
+
+ if (linux_use26(td)) {
+ em = em_find(td->td_proc, EMUL_DONTLOCK);
+ KASSERT(em != NULL, ("getpid: emuldata not found.\n"));
+ td->td_retval[0] = em->shared->group_pid;
+ } else {
+ td->td_retval[0] = td->td_proc->p_pid;
+ }
+
+ return (0);
+}
+
+int
+linux_gettid(struct thread *td, struct linux_gettid_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(gettid))
+ printf(ARGS(gettid, ""));
+#endif
+
+ td->td_retval[0] = td->td_proc->p_pid;
+ return (0);
+}
+
+
+int
+linux_getppid(struct thread *td, struct linux_getppid_args *args)
+{
+ struct linux_emuldata *em;
+ struct proc *p, *pp;
+
+#ifdef DEBUG
+ if (ldebug(getppid))
+ printf(ARGS(getppid, ""));
+#endif
+
+ if (!linux_use26(td)) {
+ PROC_LOCK(td->td_proc);
+ td->td_retval[0] = td->td_proc->p_pptr->p_pid;
+ PROC_UNLOCK(td->td_proc);
+ return (0);
+ }
+
+ em = em_find(td->td_proc, EMUL_DONTLOCK);
+
+ KASSERT(em != NULL, ("getppid: process emuldata not found.\n"));
+
+ /* find the group leader */
+ p = pfind(em->shared->group_pid);
+
+ if (p == NULL) {
+#ifdef DEBUG
+ printf(LMSG("parent process not found.\n"));
+#endif
+ return (0);
+ }
+
+ pp = p->p_pptr; /* switch to parent */
+ PROC_LOCK(pp);
+ PROC_UNLOCK(p);
+
+ /* if its also linux process */
+ if (pp->p_sysent == &elf_linux_sysvec) {
+ em = em_find(pp, EMUL_DONTLOCK);
+ KASSERT(em != NULL, ("getppid: parent emuldata not found.\n"));
+
+ td->td_retval[0] = em->shared->group_pid;
+ } else
+ td->td_retval[0] = pp->p_pid;
+
+ PROC_UNLOCK(pp);
+
+ return (0);
+}
+
+int
+linux_getgid(struct thread *td, struct linux_getgid_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(getgid))
+ printf(ARGS(getgid, ""));
+#endif
+
+ td->td_retval[0] = td->td_ucred->cr_rgid;
+ return (0);
+}
+
+int
+linux_getuid(struct thread *td, struct linux_getuid_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(getuid))
+ printf(ARGS(getuid, ""));
+#endif
+
+ td->td_retval[0] = td->td_ucred->cr_ruid;
+ return (0);
+}
+
+
+int
+linux_getsid(struct thread *td, struct linux_getsid_args *args)
+{
+ struct getsid_args bsd;
+
+#ifdef DEBUG
+ if (ldebug(getsid))
+ printf(ARGS(getsid, "%i"), args->pid);
+#endif
+
+ bsd.pid = args->pid;
+ return (getsid(td, &bsd));
+}
+
+int
+linux_nosys(struct thread *td, struct nosys_args *ignore)
+{
+
+ return (ENOSYS);
+}
+
+int
+linux_getpriority(struct thread *td, struct linux_getpriority_args *args)
+{
+ struct getpriority_args bsd_args;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(getpriority))
+ printf(ARGS(getpriority, "%i, %i"), args->which, args->who);
+#endif
+
+ bsd_args.which = args->which;
+ bsd_args.who = args->who;
+ error = getpriority(td, &bsd_args);
+ td->td_retval[0] = 20 - td->td_retval[0];
+ return (error);
+}
+
+int
+linux_sethostname(struct thread *td, struct linux_sethostname_args *args)
+{
+ int name[2];
+
+#ifdef DEBUG
+ if (ldebug(sethostname))
+ printf(ARGS(sethostname, "*, %i"), args->len);
+#endif
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_HOSTNAME;
+ return (userland_sysctl(td, name, 2, 0, 0, 0, args->hostname,
+ args->len, 0, 0));
+}
+
+int
+linux_setdomainname(struct thread *td, struct linux_setdomainname_args *args)
+{
+ int name[2];
+
+#ifdef DEBUG
+ if (ldebug(setdomainname))
+ printf(ARGS(setdomainname, "*, %i"), args->len);
+#endif
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_NISDOMAINNAME;
+ return (userland_sysctl(td, name, 2, 0, 0, 0, args->name,
+ args->len, 0, 0));
+}
+
+int
+linux_exit_group(struct thread *td, struct linux_exit_group_args *args)
+{
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(exit_group))
+ printf(ARGS(exit_group, "%i"), args->error_code);
+#endif
+
+ em = em_find(td->td_proc, EMUL_DONTLOCK);
+ if (em->shared->refs > 1) {
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ em->shared->flags |= EMUL_SHARED_HASXSTAT;
+ em->shared->xstat = W_EXITCODE(args->error_code, 0);
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+ if (linux_use26(td))
+ linux_kill_threads(td, SIGKILL);
+ }
+
+ /*
+ * XXX: we should send a signal to the parent if
+ * SIGNAL_EXIT_GROUP is set. We ignore that (temporarily?)
+ * as it doesnt occur often.
+ */
+ exit1(td, W_EXITCODE(args->error_code, 0));
+
+ return (0);
+}
+
+#define _LINUX_CAPABILITY_VERSION 0x19980330
+
+struct l_user_cap_header {
+ l_int version;
+ l_int pid;
+};
+
+struct l_user_cap_data {
+ l_int effective;
+ l_int permitted;
+ l_int inheritable;
+};
+
+int
+linux_capget(struct thread *td, struct linux_capget_args *args)
+{
+ struct l_user_cap_header luch;
+ struct l_user_cap_data lucd;
+ int error;
+
+ if (args->hdrp == NULL)
+ return (EFAULT);
+
+ error = copyin(args->hdrp, &luch, sizeof(luch));
+ if (error != 0)
+ return (error);
+
+ if (luch.version != _LINUX_CAPABILITY_VERSION) {
+ luch.version = _LINUX_CAPABILITY_VERSION;
+ error = copyout(&luch, args->hdrp, sizeof(luch));
+ if (error)
+ return (error);
+ return (EINVAL);
+ }
+
+ if (luch.pid)
+ return (EPERM);
+
+ if (args->datap) {
+ /*
+ * The current implementation doesn't support setting
+ * a capability (it's essentially a stub) so indicate
+ * that no capabilities are currently set or available
+ * to request.
+ */
+ bzero (&lucd, sizeof(lucd));
+ error = copyout(&lucd, args->datap, sizeof(lucd));
+ }
+
+ return (error);
+}
+
+int
+linux_capset(struct thread *td, struct linux_capset_args *args)
+{
+ struct l_user_cap_header luch;
+ struct l_user_cap_data lucd;
+ int error;
+
+ if (args->hdrp == NULL || args->datap == NULL)
+ return (EFAULT);
+
+ error = copyin(args->hdrp, &luch, sizeof(luch));
+ if (error != 0)
+ return (error);
+
+ if (luch.version != _LINUX_CAPABILITY_VERSION) {
+ luch.version = _LINUX_CAPABILITY_VERSION;
+ error = copyout(&luch, args->hdrp, sizeof(luch));
+ if (error)
+ return (error);
+ return (EINVAL);
+ }
+
+ if (luch.pid)
+ return (EPERM);
+
+ error = copyin(args->datap, &lucd, sizeof(lucd));
+ if (error != 0)
+ return (error);
+
+ /* We currently don't support setting any capabilities. */
+ if (lucd.effective || lucd.permitted || lucd.inheritable) {
+ linux_msg(td,
+ "capset effective=0x%x, permitted=0x%x, "
+ "inheritable=0x%x is not implemented",
+ (int)lucd.effective, (int)lucd.permitted,
+ (int)lucd.inheritable);
+ return (EPERM);
+ }
+
+ return (0);
+}
+
+int
+linux_prctl(struct thread *td, struct linux_prctl_args *args)
+{
+ int error = 0, max_size;
+ struct proc *p = td->td_proc;
+ char comm[LINUX_MAX_COMM_LEN];
+ struct linux_emuldata *em;
+ int pdeath_signal;
+
+#ifdef DEBUG
+ if (ldebug(prctl))
+ printf(ARGS(prctl, "%d, %d, %d, %d, %d"), args->option,
+ args->arg2, args->arg3, args->arg4, args->arg5);
+#endif
+
+ switch (args->option) {
+ case LINUX_PR_SET_PDEATHSIG:
+ if (!LINUX_SIG_VALID(args->arg2))
+ return (EINVAL);
+ em = em_find(p, EMUL_DOLOCK);
+ KASSERT(em != NULL, ("prctl: emuldata not found.\n"));
+ em->pdeath_signal = args->arg2;
+ EMUL_UNLOCK(&emul_lock);
+ break;
+ case LINUX_PR_GET_PDEATHSIG:
+ em = em_find(p, EMUL_DOLOCK);
+ KASSERT(em != NULL, ("prctl: emuldata not found.\n"));
+ pdeath_signal = em->pdeath_signal;
+ EMUL_UNLOCK(&emul_lock);
+ error = copyout(&pdeath_signal,
+ (void *)(register_t)args->arg2,
+ sizeof(pdeath_signal));
+ break;
+ case LINUX_PR_GET_KEEPCAPS:
+ /*
+ * Indicate that we always clear the effective and
+ * permitted capability sets when the user id becomes
+ * non-zero (actually the capability sets are simply
+ * always zero in the current implementation).
+ */
+ td->td_retval[0] = 0;
+ break;
+ case LINUX_PR_SET_KEEPCAPS:
+ /*
+ * Ignore requests to keep the effective and permitted
+ * capability sets when the user id becomes non-zero.
+ */
+ break;
+ case LINUX_PR_SET_NAME:
+ /*
+ * To be on the safe side we need to make sure to not
+ * overflow the size a linux program expects. We already
+ * do this here in the copyin, so that we don't need to
+ * check on copyout.
+ */
+ max_size = MIN(sizeof(comm), sizeof(p->p_comm));
+ error = copyinstr((void *)(register_t)args->arg2, comm,
+ max_size, NULL);
+
+ /* Linux silently truncates the name if it is too long. */
+ if (error == ENAMETOOLONG) {
+ /*
+ * XXX: copyinstr() isn't documented to populate the
+ * array completely, so do a copyin() to be on the
+ * safe side. This should be changed in case
+ * copyinstr() is changed to guarantee this.
+ */
+ error = copyin((void *)(register_t)args->arg2, comm,
+ max_size - 1);
+ comm[max_size - 1] = '\0';
+ }
+ if (error)
+ return (error);
+
+ PROC_LOCK(p);
+ strlcpy(p->p_comm, comm, sizeof(p->p_comm));
+ PROC_UNLOCK(p);
+ break;
+ case LINUX_PR_GET_NAME:
+ PROC_LOCK(p);
+ strlcpy(comm, p->p_comm, sizeof(comm));
+ PROC_UNLOCK(p);
+ error = copyout(comm, (void *)(register_t)args->arg2,
+ strlen(comm) + 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+/*
+ * Get affinity of a process.
+ */
+int
+linux_sched_getaffinity(struct thread *td,
+ struct linux_sched_getaffinity_args *args)
+{
+ int error;
+ struct cpuset_getaffinity_args cga;
+
+#ifdef DEBUG
+ if (ldebug(sched_getaffinity))
+ printf(ARGS(sched_getaffinity, "%d, %d, *"), args->pid,
+ args->len);
+#endif
+ if (args->len < sizeof(cpuset_t))
+ return (EINVAL);
+
+ cga.level = CPU_LEVEL_WHICH;
+ cga.which = CPU_WHICH_PID;
+ cga.id = args->pid;
+ cga.cpusetsize = sizeof(cpuset_t);
+ cga.mask = (cpuset_t *) args->user_mask_ptr;
+
+ if ((error = cpuset_getaffinity(td, &cga)) == 0)
+ td->td_retval[0] = sizeof(cpuset_t);
+
+ return (error);
+}
+
+/*
+ * Set affinity of a process.
+ */
+int
+linux_sched_setaffinity(struct thread *td,
+ struct linux_sched_setaffinity_args *args)
+{
+ struct cpuset_setaffinity_args csa;
+
+#ifdef DEBUG
+ if (ldebug(sched_setaffinity))
+ printf(ARGS(sched_setaffinity, "%d, %d, *"), args->pid,
+ args->len);
+#endif
+ if (args->len < sizeof(cpuset_t))
+ return (EINVAL);
+
+ csa.level = CPU_LEVEL_WHICH;
+ csa.which = CPU_WHICH_PID;
+ csa.id = args->pid;
+ csa.cpusetsize = sizeof(cpuset_t);
+ csa.mask = (cpuset_t *) args->user_mask_ptr;
+
+ return (cpuset_setaffinity(td, &csa));
+}
diff --git a/sys/compat/linux/linux_misc.h b/sys/compat/linux/linux_misc.h
new file mode 100644
index 0000000..b771825
--- /dev/null
+++ b/sys/compat/linux/linux_misc.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2006 Roman Divacky
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_MISC_H_
+#define _LINUX_MISC_H_
+
+/* defines for prctl */
+#define LINUX_PR_SET_PDEATHSIG 1 /* Second arg is a signal. */
+#define LINUX_PR_GET_PDEATHSIG 2 /*
+ * Second arg is a ptr to return the
+ * signal.
+ */
+#define LINUX_PR_GET_KEEPCAPS 7 /* Get drop capabilities on setuid */
+#define LINUX_PR_SET_KEEPCAPS 8 /* Set drop capabilities on setuid */
+#define LINUX_PR_SET_NAME 15 /* Set process name. */
+#define LINUX_PR_GET_NAME 16 /* Get process name. */
+
+#define LINUX_MAX_COMM_LEN 16 /* Maximum length of the process name. */
+
+#define LINUX_MREMAP_MAYMOVE 1
+#define LINUX_MREMAP_FIXED 2
+
+extern const char *linux_platform;
+
+/*
+ * Non-standard aux entry types used in Linux ELF binaries.
+ */
+
+#define LINUX_AT_PLATFORM 15 /* String identifying CPU */
+#define LINUX_AT_HWCAP 16 /* CPU capabilities */
+#define LINUX_AT_CLKTCK 17 /* frequency at which times() increments */
+#define LINUX_AT_SECURE 23 /* secure mode boolean */
+#define LINUX_AT_BASE_PLATFORM 24 /* string identifying real platform, may
+ * differ from AT_PLATFORM.
+ */
+#define LINUX_AT_EXECFN 31 /* filename of program */
+
+/* Linux sets the i387 to extended precision. */
+#if defined(__i386__) || defined(__amd64__)
+#define __LINUX_NPXCW__ 0x37f
+#endif
+
+extern int stclohz;
+
+#define __WCLONE 0x80000000
+
+int linux_common_wait(struct thread *td, int pid, int *status,
+ int options, struct rusage *ru);
+
+#endif /* _LINUX_MISC_H_ */
diff --git a/sys/compat/linux/linux_signal.c b/sys/compat/linux/linux_signal.c
new file mode 100644
index 0000000..82dd2ee
--- /dev/null
+++ b/sys/compat/linux/linux_signal.c
@@ -0,0 +1,656 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+
+#include <security/audit/audit.h>
+
+#include "opt_compat.h"
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_signal.h>
+#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_emul.h>
+
+void
+linux_to_bsd_sigset(l_sigset_t *lss, sigset_t *bss)
+{
+ int b, l;
+
+ SIGEMPTYSET(*bss);
+ bss->__bits[0] = lss->__bits[0] & ~((1U << LINUX_SIGTBLSZ) - 1);
+ bss->__bits[1] = lss->__bits[1];
+ for (l = 1; l <= LINUX_SIGTBLSZ; l++) {
+ if (LINUX_SIGISMEMBER(*lss, l)) {
+ b = linux_to_bsd_signal[_SIG_IDX(l)];
+ if (b)
+ SIGADDSET(*bss, b);
+ }
+ }
+}
+
+void
+bsd_to_linux_sigset(sigset_t *bss, l_sigset_t *lss)
+{
+ int b, l;
+
+ LINUX_SIGEMPTYSET(*lss);
+ lss->__bits[0] = bss->__bits[0] & ~((1U << LINUX_SIGTBLSZ) - 1);
+ lss->__bits[1] = bss->__bits[1];
+ for (b = 1; b <= LINUX_SIGTBLSZ; b++) {
+ if (SIGISMEMBER(*bss, b)) {
+ l = bsd_to_linux_signal[_SIG_IDX(b)];
+ if (l)
+ LINUX_SIGADDSET(*lss, l);
+ }
+ }
+}
+
+static void
+linux_to_bsd_sigaction(l_sigaction_t *lsa, struct sigaction *bsa)
+{
+
+ linux_to_bsd_sigset(&lsa->lsa_mask, &bsa->sa_mask);
+ bsa->sa_handler = PTRIN(lsa->lsa_handler);
+ bsa->sa_flags = 0;
+ if (lsa->lsa_flags & LINUX_SA_NOCLDSTOP)
+ bsa->sa_flags |= SA_NOCLDSTOP;
+ if (lsa->lsa_flags & LINUX_SA_NOCLDWAIT)
+ bsa->sa_flags |= SA_NOCLDWAIT;
+ if (lsa->lsa_flags & LINUX_SA_SIGINFO)
+ bsa->sa_flags |= SA_SIGINFO;
+ if (lsa->lsa_flags & LINUX_SA_ONSTACK)
+ bsa->sa_flags |= SA_ONSTACK;
+ if (lsa->lsa_flags & LINUX_SA_RESTART)
+ bsa->sa_flags |= SA_RESTART;
+ if (lsa->lsa_flags & LINUX_SA_ONESHOT)
+ bsa->sa_flags |= SA_RESETHAND;
+ if (lsa->lsa_flags & LINUX_SA_NOMASK)
+ bsa->sa_flags |= SA_NODEFER;
+}
+
+static void
+bsd_to_linux_sigaction(struct sigaction *bsa, l_sigaction_t *lsa)
+{
+
+ bsd_to_linux_sigset(&bsa->sa_mask, &lsa->lsa_mask);
+#ifdef COMPAT_LINUX32
+ lsa->lsa_handler = (uintptr_t)bsa->sa_handler;
+#else
+ lsa->lsa_handler = bsa->sa_handler;
+#endif
+ lsa->lsa_restorer = 0; /* unsupported */
+ lsa->lsa_flags = 0;
+ if (bsa->sa_flags & SA_NOCLDSTOP)
+ lsa->lsa_flags |= LINUX_SA_NOCLDSTOP;
+ if (bsa->sa_flags & SA_NOCLDWAIT)
+ lsa->lsa_flags |= LINUX_SA_NOCLDWAIT;
+ if (bsa->sa_flags & SA_SIGINFO)
+ lsa->lsa_flags |= LINUX_SA_SIGINFO;
+ if (bsa->sa_flags & SA_ONSTACK)
+ lsa->lsa_flags |= LINUX_SA_ONSTACK;
+ if (bsa->sa_flags & SA_RESTART)
+ lsa->lsa_flags |= LINUX_SA_RESTART;
+ if (bsa->sa_flags & SA_RESETHAND)
+ lsa->lsa_flags |= LINUX_SA_ONESHOT;
+ if (bsa->sa_flags & SA_NODEFER)
+ lsa->lsa_flags |= LINUX_SA_NOMASK;
+}
+
+int
+linux_do_sigaction(struct thread *td, int linux_sig, l_sigaction_t *linux_nsa,
+ l_sigaction_t *linux_osa)
+{
+ struct sigaction act, oact, *nsa, *osa;
+ int error, sig;
+
+ if (!LINUX_SIG_VALID(linux_sig))
+ return (EINVAL);
+
+ osa = (linux_osa != NULL) ? &oact : NULL;
+ if (linux_nsa != NULL) {
+ nsa = &act;
+ linux_to_bsd_sigaction(linux_nsa, nsa);
+ } else
+ nsa = NULL;
+
+ if (linux_sig <= LINUX_SIGTBLSZ)
+ sig = linux_to_bsd_signal[_SIG_IDX(linux_sig)];
+ else
+ sig = linux_sig;
+
+ error = kern_sigaction(td, sig, nsa, osa, 0);
+ if (error)
+ return (error);
+
+ if (linux_osa != NULL)
+ bsd_to_linux_sigaction(osa, linux_osa);
+
+ return (0);
+}
+
+
+int
+linux_signal(struct thread *td, struct linux_signal_args *args)
+{
+ l_sigaction_t nsa, osa;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(signal))
+ printf(ARGS(signal, "%d, %p"),
+ args->sig, (void *)(uintptr_t)args->handler);
+#endif
+
+ nsa.lsa_handler = args->handler;
+ nsa.lsa_flags = LINUX_SA_ONESHOT | LINUX_SA_NOMASK;
+ LINUX_SIGEMPTYSET(nsa.lsa_mask);
+
+ error = linux_do_sigaction(td, args->sig, &nsa, &osa);
+ td->td_retval[0] = (int)(intptr_t)osa.lsa_handler;
+
+ return (error);
+}
+
+int
+linux_rt_sigaction(struct thread *td, struct linux_rt_sigaction_args *args)
+{
+ l_sigaction_t nsa, osa;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(rt_sigaction))
+ printf(ARGS(rt_sigaction, "%ld, %p, %p, %ld"),
+ (long)args->sig, (void *)args->act,
+ (void *)args->oact, (long)args->sigsetsize);
+#endif
+
+ if (args->sigsetsize != sizeof(l_sigset_t))
+ return (EINVAL);
+
+ if (args->act != NULL) {
+ error = copyin(args->act, &nsa, sizeof(l_sigaction_t));
+ if (error)
+ return (error);
+ }
+
+ error = linux_do_sigaction(td, args->sig,
+ args->act ? &nsa : NULL,
+ args->oact ? &osa : NULL);
+
+ if (args->oact != NULL && !error) {
+ error = copyout(&osa, args->oact, sizeof(l_sigaction_t));
+ }
+
+ return (error);
+}
+
+static int
+linux_do_sigprocmask(struct thread *td, int how, l_sigset_t *new,
+ l_sigset_t *old)
+{
+ sigset_t omask, nmask;
+ sigset_t *nmaskp;
+ int error;
+
+ td->td_retval[0] = 0;
+
+ switch (how) {
+ case LINUX_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case LINUX_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case LINUX_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (new != NULL) {
+ linux_to_bsd_sigset(new, &nmask);
+ nmaskp = &nmask;
+ } else
+ nmaskp = NULL;
+ error = kern_sigprocmask(td, how, nmaskp, &omask, 0);
+ if (error == 0 && old != NULL)
+ bsd_to_linux_sigset(&omask, old);
+
+ return (error);
+}
+
+int
+linux_sigprocmask(struct thread *td, struct linux_sigprocmask_args *args)
+{
+ l_osigset_t mask;
+ l_sigset_t set, oset;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(sigprocmask))
+ printf(ARGS(sigprocmask, "%d, *, *"), args->how);
+#endif
+
+ if (args->mask != NULL) {
+ error = copyin(args->mask, &mask, sizeof(l_osigset_t));
+ if (error)
+ return (error);
+ LINUX_SIGEMPTYSET(set);
+ set.__bits[0] = mask;
+ }
+
+ error = linux_do_sigprocmask(td, args->how,
+ args->mask ? &set : NULL,
+ args->omask ? &oset : NULL);
+
+ if (args->omask != NULL && !error) {
+ mask = oset.__bits[0];
+ error = copyout(&mask, args->omask, sizeof(l_osigset_t));
+ }
+
+ return (error);
+}
+
+int
+linux_rt_sigprocmask(struct thread *td, struct linux_rt_sigprocmask_args *args)
+{
+ l_sigset_t set, oset;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(rt_sigprocmask))
+ printf(ARGS(rt_sigprocmask, "%d, %p, %p, %ld"),
+ args->how, (void *)args->mask,
+ (void *)args->omask, (long)args->sigsetsize);
+#endif
+
+ if (args->sigsetsize != sizeof(l_sigset_t))
+ return EINVAL;
+
+ if (args->mask != NULL) {
+ error = copyin(args->mask, &set, sizeof(l_sigset_t));
+ if (error)
+ return (error);
+ }
+
+ error = linux_do_sigprocmask(td, args->how,
+ args->mask ? &set : NULL,
+ args->omask ? &oset : NULL);
+
+ if (args->omask != NULL && !error) {
+ error = copyout(&oset, args->omask, sizeof(l_sigset_t));
+ }
+
+ return (error);
+}
+
+int
+linux_sgetmask(struct thread *td, struct linux_sgetmask_args *args)
+{
+ struct proc *p = td->td_proc;
+ l_sigset_t mask;
+
+#ifdef DEBUG
+ if (ldebug(sgetmask))
+ printf(ARGS(sgetmask, ""));
+#endif
+
+ PROC_LOCK(p);
+ bsd_to_linux_sigset(&td->td_sigmask, &mask);
+ PROC_UNLOCK(p);
+ td->td_retval[0] = mask.__bits[0];
+ return (0);
+}
+
+int
+linux_ssetmask(struct thread *td, struct linux_ssetmask_args *args)
+{
+ struct proc *p = td->td_proc;
+ l_sigset_t lset;
+ sigset_t bset;
+
+#ifdef DEBUG
+ if (ldebug(ssetmask))
+ printf(ARGS(ssetmask, "%08lx"), (unsigned long)args->mask);
+#endif
+
+ PROC_LOCK(p);
+ bsd_to_linux_sigset(&td->td_sigmask, &lset);
+ td->td_retval[0] = lset.__bits[0];
+ LINUX_SIGEMPTYSET(lset);
+ lset.__bits[0] = args->mask;
+ linux_to_bsd_sigset(&lset, &bset);
+ td->td_sigmask = bset;
+ SIG_CANTMASK(td->td_sigmask);
+ signotify(td);
+ PROC_UNLOCK(p);
+ return (0);
+}
+
+/*
+ * MPSAFE
+ */
+int
+linux_sigpending(struct thread *td, struct linux_sigpending_args *args)
+{
+ struct proc *p = td->td_proc;
+ sigset_t bset;
+ l_sigset_t lset;
+ l_osigset_t mask;
+
+#ifdef DEBUG
+ if (ldebug(sigpending))
+ printf(ARGS(sigpending, "*"));
+#endif
+
+ PROC_LOCK(p);
+ bset = p->p_siglist;
+ SIGSETOR(bset, td->td_siglist);
+ SIGSETAND(bset, td->td_sigmask);
+ PROC_UNLOCK(p);
+ bsd_to_linux_sigset(&bset, &lset);
+ mask = lset.__bits[0];
+ return (copyout(&mask, args->mask, sizeof(mask)));
+}
+
+/*
+ * MPSAFE
+ */
+int
+linux_rt_sigpending(struct thread *td, struct linux_rt_sigpending_args *args)
+{
+ struct proc *p = td->td_proc;
+ sigset_t bset;
+ l_sigset_t lset;
+
+ if (args->sigsetsize > sizeof(lset))
+ return EINVAL;
+ /* NOT REACHED */
+
+#ifdef DEBUG
+ if (ldebug(rt_sigpending))
+ printf(ARGS(rt_sigpending, "*"));
+#endif
+
+ PROC_LOCK(p);
+ bset = p->p_siglist;
+ SIGSETOR(bset, td->td_siglist);
+ SIGSETAND(bset, td->td_sigmask);
+ PROC_UNLOCK(p);
+ bsd_to_linux_sigset(&bset, &lset);
+ return (copyout(&lset, args->set, args->sigsetsize));
+}
+
+/*
+ * MPSAFE
+ */
+int
+linux_rt_sigtimedwait(struct thread *td,
+ struct linux_rt_sigtimedwait_args *args)
+{
+ int error, sig;
+ l_timeval ltv;
+ struct timeval tv;
+ struct timespec ts, *tsa;
+ l_sigset_t lset;
+ sigset_t bset;
+ l_siginfo_t linfo;
+ ksiginfo_t info;
+
+#ifdef DEBUG
+ if (ldebug(rt_sigtimedwait))
+ printf(ARGS(rt_sigtimedwait, "*"));
+#endif
+ if (args->sigsetsize != sizeof(l_sigset_t))
+ return (EINVAL);
+
+ if ((error = copyin(args->mask, &lset, sizeof(lset))))
+ return (error);
+ linux_to_bsd_sigset(&lset, &bset);
+
+ tsa = NULL;
+ if (args->timeout) {
+ if ((error = copyin(args->timeout, &ltv, sizeof(ltv))))
+ return (error);
+#ifdef DEBUG
+ if (ldebug(rt_sigtimedwait))
+ printf(LMSG("linux_rt_sigtimedwait: "
+ "incoming timeout (%d/%d)\n"),
+ ltv.tv_sec, ltv.tv_usec);
+#endif
+ tv.tv_sec = (long)ltv.tv_sec;
+ tv.tv_usec = (suseconds_t)ltv.tv_usec;
+ if (itimerfix(&tv)) {
+ /*
+ * The timeout was invalid. Convert it to something
+ * valid that will act as it does under Linux.
+ */
+ tv.tv_sec += tv.tv_usec / 1000000;
+ tv.tv_usec %= 1000000;
+ if (tv.tv_usec < 0) {
+ tv.tv_sec -= 1;
+ tv.tv_usec += 1000000;
+ }
+ if (tv.tv_sec < 0)
+ timevalclear(&tv);
+#ifdef DEBUG
+ if (ldebug(rt_sigtimedwait))
+ printf(LMSG("linux_rt_sigtimedwait: "
+ "converted timeout (%jd/%ld)\n"),
+ (intmax_t)tv.tv_sec, tv.tv_usec);
+#endif
+ }
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
+ tsa = &ts;
+ }
+ error = kern_sigtimedwait(td, bset, &info, tsa);
+#ifdef DEBUG
+ if (ldebug(rt_sigtimedwait))
+ printf(LMSG("linux_rt_sigtimedwait: "
+ "sigtimedwait returning (%d)\n"), error);
+#endif
+ if (error)
+ return (error);
+
+ sig = BSD_TO_LINUX_SIGNAL(info.ksi_signo);
+
+ if (args->ptr) {
+ memset(&linfo, 0, sizeof(linfo));
+ ksiginfo_to_lsiginfo(&info, &linfo, sig);
+ error = copyout(&linfo, args->ptr, sizeof(linfo));
+ }
+ if (error == 0)
+ td->td_retval[0] = sig;
+
+ return (error);
+}
+
+int
+linux_kill(struct thread *td, struct linux_kill_args *args)
+{
+ struct kill_args /* {
+ int pid;
+ int signum;
+ } */ tmp;
+
+#ifdef DEBUG
+ if (ldebug(kill))
+ printf(ARGS(kill, "%d, %d"), args->pid, args->signum);
+#endif
+
+ /*
+ * Allow signal 0 as a means to check for privileges
+ */
+ if (!LINUX_SIG_VALID(args->signum) && args->signum != 0)
+ return (EINVAL);
+
+ if (args->signum > 0 && args->signum <= LINUX_SIGTBLSZ)
+ tmp.signum = linux_to_bsd_signal[_SIG_IDX(args->signum)];
+ else
+ tmp.signum = args->signum;
+
+ tmp.pid = args->pid;
+ return (kill(td, &tmp));
+}
+
+static int
+linux_do_tkill(struct thread *td, l_int tgid, l_int pid, l_int signum)
+{
+ struct proc *proc = td->td_proc;
+ struct linux_emuldata *em;
+ struct proc *p;
+ ksiginfo_t ksi;
+ int error;
+
+ AUDIT_ARG_SIGNUM(signum);
+ AUDIT_ARG_PID(pid);
+
+ /*
+ * Allow signal 0 as a means to check for privileges
+ */
+ if (!LINUX_SIG_VALID(signum) && signum != 0)
+ return (EINVAL);
+
+ if (signum > 0 && signum <= LINUX_SIGTBLSZ)
+ signum = linux_to_bsd_signal[_SIG_IDX(signum)];
+
+ if ((p = pfind(pid)) == NULL) {
+ if ((p = zpfind(pid)) == NULL)
+ return (ESRCH);
+ }
+
+ AUDIT_ARG_PROCESS(p);
+ error = p_cansignal(td, p, signum);
+ if (error != 0 || signum == 0)
+ goto out;
+
+ error = ESRCH;
+ em = em_find(p, EMUL_DONTLOCK);
+
+ if (em == NULL) {
+#ifdef DEBUG
+ printf("emuldata not found in do_tkill.\n");
+#endif
+ goto out;
+ }
+ if (tgid > 0 && em->shared->group_pid != tgid)
+ goto out;
+
+ ksiginfo_init(&ksi);
+ ksi.ksi_signo = signum;
+ ksi.ksi_code = LINUX_SI_TKILL;
+ ksi.ksi_errno = 0;
+ ksi.ksi_pid = proc->p_pid;
+ ksi.ksi_uid = proc->p_ucred->cr_ruid;
+
+ error = pksignal(p, ksi.ksi_signo, &ksi);
+
+out:
+ PROC_UNLOCK(p);
+ return (error);
+}
+
+int
+linux_tgkill(struct thread *td, struct linux_tgkill_args *args)
+{
+
+#ifdef DEBUG
+ if (ldebug(tgkill))
+ printf(ARGS(tgkill, "%d, %d, %d"), args->tgid, args->pid, args->sig);
+#endif
+ if (args->pid <= 0 || args->tgid <=0)
+ return (EINVAL);
+
+ return (linux_do_tkill(td, args->tgid, args->pid, args->sig));
+}
+
+int
+linux_tkill(struct thread *td, struct linux_tkill_args *args)
+{
+#ifdef DEBUG
+ if (ldebug(tkill))
+ printf(ARGS(tkill, "%i, %i"), args->tid, args->sig);
+#endif
+ if (args->tid <= 0)
+ return (EINVAL);
+
+ return (linux_do_tkill(td, 0, args->tid, args->sig));
+}
+
+void
+ksiginfo_to_lsiginfo(ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig)
+{
+
+ lsi->lsi_signo = sig;
+ lsi->lsi_code = ksi->ksi_code;
+
+ switch (sig) {
+ case LINUX_SIGPOLL:
+ /* XXX si_fd? */
+ lsi->lsi_band = ksi->ksi_band;
+ break;
+ case LINUX_SIGCHLD:
+ lsi->lsi_pid = ksi->ksi_pid;
+ lsi->lsi_uid = ksi->ksi_uid;
+ lsi->lsi_status = ksi->ksi_status;
+ break;
+ case LINUX_SIGBUS:
+ case LINUX_SIGILL:
+ case LINUX_SIGFPE:
+ case LINUX_SIGSEGV:
+ lsi->lsi_addr = PTROUT(ksi->ksi_addr);
+ break;
+ default:
+ /* XXX SI_TIMER etc... */
+ lsi->lsi_pid = ksi->ksi_pid;
+ lsi->lsi_uid = ksi->ksi_uid;
+ break;
+ }
+ if (sig >= LINUX_SIGRTMIN) {
+ lsi->lsi_int = ksi->ksi_info.si_value.sival_int;
+ lsi->lsi_ptr = PTROUT(ksi->ksi_info.si_value.sival_ptr);
+ }
+}
diff --git a/sys/compat/linux/linux_signal.h b/sys/compat/linux/linux_signal.h
new file mode 100644
index 0000000..939009f
--- /dev/null
+++ b/sys/compat/linux/linux_signal.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2000 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_SIGNAL_H_
+#define _LINUX_SIGNAL_H_
+
+#define LINUX_SI_TKILL -6;
+
+void linux_to_bsd_sigset(l_sigset_t *, sigset_t *);
+void bsd_to_linux_sigset(sigset_t *, l_sigset_t *);
+int linux_do_sigaction(struct thread *, int, l_sigaction_t *, l_sigaction_t *);
+void ksiginfo_to_lsiginfo(ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig);
+
+#define LINUX_SIG_VALID(sig) ((sig) <= LINUX_NSIG && (sig) > 0)
+
+#define BSD_TO_LINUX_SIGNAL(sig) \
+ (((sig) <= LINUX_SIGTBLSZ) ? bsd_to_linux_signal[_SIG_IDX(sig)] : sig)
+
+#endif /* _LINUX_SIGNAL_H_ */
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
new file mode 100644
index 0000000..6940e45
--- /dev/null
+++ b/sys/compat/linux/linux_socket.c
@@ -0,0 +1,1684 @@
+/*-
+ * Copyright (c) 1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* XXX we use functions that might not exist. */
+#include "opt_compat.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syscallsubr.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/un.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#endif
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_socket.h>
+#include <compat/linux/linux_util.h>
+
+static int do_sa_get(struct sockaddr **, const struct osockaddr *, int *,
+ struct malloc_type *);
+static int linux_to_bsd_domain(int);
+
+/*
+ * Reads a linux sockaddr and does any necessary translation.
+ * Linux sockaddrs don't have a length field, only a family.
+ */
+static int
+linux_getsockaddr(struct sockaddr **sap, const struct osockaddr *osa, int len)
+{
+ int osalen = len;
+
+ return (do_sa_get(sap, osa, &osalen, M_SONAME));
+}
+
+/*
+ * Copy the osockaddr structure pointed to by osa to kernel, adjust
+ * family and convert to sockaddr.
+ */
+static int
+do_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen,
+ struct malloc_type *mtype)
+{
+ int error=0, bdom;
+ struct sockaddr *sa;
+ struct osockaddr *kosa;
+ int alloclen;
+#ifdef INET6
+ int oldv6size;
+ struct sockaddr_in6 *sin6;
+#endif
+
+ if (*osalen < 2 || *osalen > UCHAR_MAX || !osa)
+ return (EINVAL);
+
+ alloclen = *osalen;
+#ifdef INET6
+ oldv6size = 0;
+ /*
+ * Check for old (pre-RFC2553) sockaddr_in6. We may accept it
+ * if it's a v4-mapped address, so reserve the proper space
+ * for it.
+ */
+ if (alloclen == sizeof (struct sockaddr_in6) - sizeof (u_int32_t)) {
+ alloclen = sizeof (struct sockaddr_in6);
+ oldv6size = 1;
+ }
+#endif
+
+ kosa = malloc(alloclen, mtype, M_WAITOK);
+
+ if ((error = copyin(osa, kosa, *osalen)))
+ goto out;
+
+ bdom = linux_to_bsd_domain(kosa->sa_family);
+ if (bdom == -1) {
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+
+#ifdef INET6
+ /*
+ * Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6,
+ * which lacks the scope id compared with RFC2553 one. If we detect
+ * the situation, reject the address and write a message to system log.
+ *
+ * Still accept addresses for which the scope id is not used.
+ */
+ if (oldv6size && bdom == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)kosa;
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
+ (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
+ !IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
+ !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
+ sin6->sin6_scope_id = 0;
+ } else {
+ log(LOG_DEBUG,
+ "obsolete pre-RFC2553 sockaddr_in6 rejected\n");
+ error = EINVAL;
+ goto out;
+ }
+ } else
+#endif
+ if (bdom == AF_INET) {
+ alloclen = sizeof(struct sockaddr_in);
+ if (*osalen < alloclen) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+
+ sa = (struct sockaddr *) kosa;
+ sa->sa_family = bdom;
+ sa->sa_len = alloclen;
+
+ *sap = sa;
+ *osalen = alloclen;
+ return (0);
+
+out:
+ free(kosa, mtype);
+ return (error);
+}
+
+static int
+linux_to_bsd_domain(int domain)
+{
+
+ switch (domain) {
+ case LINUX_AF_UNSPEC:
+ return (AF_UNSPEC);
+ case LINUX_AF_UNIX:
+ return (AF_LOCAL);
+ case LINUX_AF_INET:
+ return (AF_INET);
+ case LINUX_AF_INET6:
+ return (AF_INET6);
+ case LINUX_AF_AX25:
+ return (AF_CCITT);
+ case LINUX_AF_IPX:
+ return (AF_IPX);
+ case LINUX_AF_APPLETALK:
+ return (AF_APPLETALK);
+ }
+ return (-1);
+}
+
+static int
+bsd_to_linux_domain(int domain)
+{
+
+ switch (domain) {
+ case AF_UNSPEC:
+ return (LINUX_AF_UNSPEC);
+ case AF_LOCAL:
+ return (LINUX_AF_UNIX);
+ case AF_INET:
+ return (LINUX_AF_INET);
+ case AF_INET6:
+ return (LINUX_AF_INET6);
+ case AF_CCITT:
+ return (LINUX_AF_AX25);
+ case AF_IPX:
+ return (LINUX_AF_IPX);
+ case AF_APPLETALK:
+ return (LINUX_AF_APPLETALK);
+ }
+ return (-1);
+}
+
+static int
+linux_to_bsd_sockopt_level(int level)
+{
+
+ switch (level) {
+ case LINUX_SOL_SOCKET:
+ return (SOL_SOCKET);
+ }
+ return (level);
+}
+
+static int
+bsd_to_linux_sockopt_level(int level)
+{
+
+ switch (level) {
+ case SOL_SOCKET:
+ return (LINUX_SOL_SOCKET);
+ }
+ return (level);
+}
+
+static int
+linux_to_bsd_ip_sockopt(int opt)
+{
+
+ switch (opt) {
+ case LINUX_IP_TOS:
+ return (IP_TOS);
+ case LINUX_IP_TTL:
+ return (IP_TTL);
+ case LINUX_IP_OPTIONS:
+ return (IP_OPTIONS);
+ case LINUX_IP_MULTICAST_IF:
+ return (IP_MULTICAST_IF);
+ case LINUX_IP_MULTICAST_TTL:
+ return (IP_MULTICAST_TTL);
+ case LINUX_IP_MULTICAST_LOOP:
+ return (IP_MULTICAST_LOOP);
+ case LINUX_IP_ADD_MEMBERSHIP:
+ return (IP_ADD_MEMBERSHIP);
+ case LINUX_IP_DROP_MEMBERSHIP:
+ return (IP_DROP_MEMBERSHIP);
+ case LINUX_IP_HDRINCL:
+ return (IP_HDRINCL);
+ }
+ return (-1);
+}
+
+static int
+linux_to_bsd_so_sockopt(int opt)
+{
+
+ switch (opt) {
+ case LINUX_SO_DEBUG:
+ return (SO_DEBUG);
+ case LINUX_SO_REUSEADDR:
+ return (SO_REUSEADDR);
+ case LINUX_SO_TYPE:
+ return (SO_TYPE);
+ case LINUX_SO_ERROR:
+ return (SO_ERROR);
+ case LINUX_SO_DONTROUTE:
+ return (SO_DONTROUTE);
+ case LINUX_SO_BROADCAST:
+ return (SO_BROADCAST);
+ case LINUX_SO_SNDBUF:
+ return (SO_SNDBUF);
+ case LINUX_SO_RCVBUF:
+ return (SO_RCVBUF);
+ case LINUX_SO_KEEPALIVE:
+ return (SO_KEEPALIVE);
+ case LINUX_SO_OOBINLINE:
+ return (SO_OOBINLINE);
+ case LINUX_SO_LINGER:
+ return (SO_LINGER);
+ case LINUX_SO_PEERCRED:
+ return (LOCAL_PEERCRED);
+ case LINUX_SO_RCVLOWAT:
+ return (SO_RCVLOWAT);
+ case LINUX_SO_SNDLOWAT:
+ return (SO_SNDLOWAT);
+ case LINUX_SO_RCVTIMEO:
+ return (SO_RCVTIMEO);
+ case LINUX_SO_SNDTIMEO:
+ return (SO_SNDTIMEO);
+ case LINUX_SO_TIMESTAMP:
+ return (SO_TIMESTAMP);
+ case LINUX_SO_ACCEPTCONN:
+ return (SO_ACCEPTCONN);
+ }
+ return (-1);
+}
+
+static int
+linux_to_bsd_msg_flags(int flags)
+{
+ int ret_flags = 0;
+
+ if (flags & LINUX_MSG_OOB)
+ ret_flags |= MSG_OOB;
+ if (flags & LINUX_MSG_PEEK)
+ ret_flags |= MSG_PEEK;
+ if (flags & LINUX_MSG_DONTROUTE)
+ ret_flags |= MSG_DONTROUTE;
+ if (flags & LINUX_MSG_CTRUNC)
+ ret_flags |= MSG_CTRUNC;
+ if (flags & LINUX_MSG_TRUNC)
+ ret_flags |= MSG_TRUNC;
+ if (flags & LINUX_MSG_DONTWAIT)
+ ret_flags |= MSG_DONTWAIT;
+ if (flags & LINUX_MSG_EOR)
+ ret_flags |= MSG_EOR;
+ if (flags & LINUX_MSG_WAITALL)
+ ret_flags |= MSG_WAITALL;
+ if (flags & LINUX_MSG_NOSIGNAL)
+ ret_flags |= MSG_NOSIGNAL;
+#if 0 /* not handled */
+ if (flags & LINUX_MSG_PROXY)
+ ;
+ if (flags & LINUX_MSG_FIN)
+ ;
+ if (flags & LINUX_MSG_SYN)
+ ;
+ if (flags & LINUX_MSG_CONFIRM)
+ ;
+ if (flags & LINUX_MSG_RST)
+ ;
+ if (flags & LINUX_MSG_ERRQUEUE)
+ ;
+#endif
+ return ret_flags;
+}
+
+/*
+* If bsd_to_linux_sockaddr() or linux_to_bsd_sockaddr() faults, then the
+* native syscall will fault. Thus, we don't really need to check the
+* return values for these functions.
+*/
+
+static int
+bsd_to_linux_sockaddr(struct sockaddr *arg)
+{
+ struct sockaddr sa;
+ size_t sa_len = sizeof(struct sockaddr);
+ int error;
+
+ if ((error = copyin(arg, &sa, sa_len)))
+ return (error);
+
+ *(u_short *)&sa = sa.sa_family;
+
+ error = copyout(&sa, arg, sa_len);
+
+ return (error);
+}
+
+static int
+linux_to_bsd_sockaddr(struct sockaddr *arg, int len)
+{
+ struct sockaddr sa;
+ size_t sa_len = sizeof(struct sockaddr);
+ int error;
+
+ if ((error = copyin(arg, &sa, sa_len)))
+ return (error);
+
+ sa.sa_family = *(sa_family_t *)&sa;
+ sa.sa_len = len;
+
+ error = copyout(&sa, arg, sa_len);
+
+ return (error);
+}
+
+
+static int
+linux_sa_put(struct osockaddr *osa)
+{
+ struct osockaddr sa;
+ int error, bdom;
+
+ /*
+ * Only read/write the osockaddr family part, the rest is
+ * not changed.
+ */
+ error = copyin(osa, &sa, sizeof(sa.sa_family));
+ if (error)
+ return (error);
+
+ bdom = bsd_to_linux_domain(sa.sa_family);
+ if (bdom == -1)
+ return (EINVAL);
+
+ sa.sa_family = bdom;
+ error = copyout(&sa, osa, sizeof(sa.sa_family));
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static int
+linux_to_bsd_cmsg_type(int cmsg_type)
+{
+
+ switch (cmsg_type) {
+ case LINUX_SCM_RIGHTS:
+ return (SCM_RIGHTS);
+ case LINUX_SCM_CREDENTIALS:
+ return (SCM_CREDS);
+ }
+ return (-1);
+}
+
+static int
+bsd_to_linux_cmsg_type(int cmsg_type)
+{
+
+ switch (cmsg_type) {
+ case SCM_RIGHTS:
+ return (LINUX_SCM_RIGHTS);
+ case SCM_CREDS:
+ return (LINUX_SCM_CREDENTIALS);
+ }
+ return (-1);
+}
+
+static int
+linux_to_bsd_msghdr(struct msghdr *bhdr, const struct l_msghdr *lhdr)
+{
+ if (lhdr->msg_controllen > INT_MAX)
+ return (ENOBUFS);
+
+ bhdr->msg_name = PTRIN(lhdr->msg_name);
+ bhdr->msg_namelen = lhdr->msg_namelen;
+ bhdr->msg_iov = PTRIN(lhdr->msg_iov);
+ bhdr->msg_iovlen = lhdr->msg_iovlen;
+ bhdr->msg_control = PTRIN(lhdr->msg_control);
+
+ /*
+ * msg_controllen is skipped since BSD and LINUX control messages
+ * are potentially different sizes (e.g. the cred structure used
+ * by SCM_CREDS is different between the two operating system).
+ *
+ * The caller can set it (if necessary) after converting all the
+ * control messages.
+ */
+
+ bhdr->msg_flags = linux_to_bsd_msg_flags(lhdr->msg_flags);
+ return (0);
+}
+
+static int
+bsd_to_linux_msghdr(const struct msghdr *bhdr, struct l_msghdr *lhdr)
+{
+ lhdr->msg_name = PTROUT(bhdr->msg_name);
+ lhdr->msg_namelen = bhdr->msg_namelen;
+ lhdr->msg_iov = PTROUT(bhdr->msg_iov);
+ lhdr->msg_iovlen = bhdr->msg_iovlen;
+ lhdr->msg_control = PTROUT(bhdr->msg_control);
+
+ /*
+ * msg_controllen is skipped since BSD and LINUX control messages
+ * are potentially different sizes (e.g. the cred structure used
+ * by SCM_CREDS is different between the two operating system).
+ *
+ * The caller can set it (if necessary) after converting all the
+ * control messages.
+ */
+
+ /* msg_flags skipped */
+ return (0);
+}
+
+static int
+linux_set_socket_flags(struct thread *td, int s, int flags)
+{
+ int error;
+
+ if (flags & LINUX_SOCK_NONBLOCK) {
+ error = kern_fcntl(td, s, F_SETFL, O_NONBLOCK);
+ if (error)
+ return (error);
+ }
+ if (flags & LINUX_SOCK_CLOEXEC) {
+ error = kern_fcntl(td, s, F_SETFD, FD_CLOEXEC);
+ if (error)
+ return (error);
+ }
+ return (0);
+}
+
+static int
+linux_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
+ struct mbuf *control, enum uio_seg segflg)
+{
+ struct sockaddr *to;
+ int error;
+
+ if (mp->msg_name != NULL) {
+ error = linux_getsockaddr(&to, mp->msg_name, mp->msg_namelen);
+ if (error)
+ return (error);
+ mp->msg_name = to;
+ } else
+ to = NULL;
+
+ error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control,
+ segflg);
+
+ if (to)
+ free(to, M_SONAME);
+ return (error);
+}
+
+/* Return 0 if IP_HDRINCL is set for the given socket. */
+static int
+linux_check_hdrincl(struct thread *td, int s)
+{
+ int error, optval, size_val;
+
+ size_val = sizeof(optval);
+ error = kern_getsockopt(td, s, IPPROTO_IP, IP_HDRINCL,
+ &optval, UIO_SYSSPACE, &size_val);
+ if (error)
+ return (error);
+
+ return (optval == 0);
+}
+
+struct linux_sendto_args {
+ int s;
+ l_uintptr_t msg;
+ int len;
+ int flags;
+ l_uintptr_t to;
+ int tolen;
+};
+
+/*
+ * Updated sendto() when IP_HDRINCL is set:
+ * tweak endian-dependent fields in the IP packet.
+ */
+static int
+linux_sendto_hdrincl(struct thread *td, struct linux_sendto_args *linux_args)
+{
+/*
+ * linux_ip_copysize defines how many bytes we should copy
+ * from the beginning of the IP packet before we customize it for BSD.
+ * It should include all the fields we modify (ip_len and ip_off).
+ */
+#define linux_ip_copysize 8
+
+ struct ip *packet;
+ struct msghdr msg;
+ struct iovec aiov[1];
+ int error;
+
+ /* Check that the packet isn't too big or too small. */
+ if (linux_args->len < linux_ip_copysize ||
+ linux_args->len > IP_MAXPACKET)
+ return (EINVAL);
+
+ packet = (struct ip *)malloc(linux_args->len, M_TEMP, M_WAITOK);
+
+ /* Make kernel copy of the packet to be sent */
+ if ((error = copyin(PTRIN(linux_args->msg), packet,
+ linux_args->len)))
+ goto goout;
+
+ /* Convert fields from Linux to BSD raw IP socket format */
+ packet->ip_len = linux_args->len;
+ packet->ip_off = ntohs(packet->ip_off);
+
+ /* Prepare the msghdr and iovec structures describing the new packet */
+ msg.msg_name = PTRIN(linux_args->to);
+ msg.msg_namelen = linux_args->tolen;
+ msg.msg_iov = aiov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_flags = 0;
+ aiov[0].iov_base = (char *)packet;
+ aiov[0].iov_len = linux_args->len;
+ error = linux_sendit(td, linux_args->s, &msg, linux_args->flags,
+ NULL, UIO_SYSSPACE);
+goout:
+ free(packet, M_TEMP);
+ return (error);
+}
+
+struct linux_socket_args {
+ int domain;
+ int type;
+ int protocol;
+};
+
+static int
+linux_socket(struct thread *td, struct linux_socket_args *args)
+{
+ struct socket_args /* {
+ int domain;
+ int type;
+ int protocol;
+ } */ bsd_args;
+ int retval_socket, socket_flags;
+
+ bsd_args.protocol = args->protocol;
+ socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
+ if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
+ return (EINVAL);
+ bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
+ if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
+ return (EINVAL);
+ bsd_args.domain = linux_to_bsd_domain(args->domain);
+ if (bsd_args.domain == -1)
+ return (EAFNOSUPPORT);
+
+ retval_socket = socket(td, &bsd_args);
+ if (retval_socket)
+ return (retval_socket);
+
+ retval_socket = linux_set_socket_flags(td, td->td_retval[0],
+ socket_flags);
+ if (retval_socket) {
+ (void)kern_close(td, td->td_retval[0]);
+ goto out;
+ }
+
+ if (bsd_args.type == SOCK_RAW
+ && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0)
+ && bsd_args.domain == PF_INET) {
+ /* It's a raw IP socket: set the IP_HDRINCL option. */
+ int hdrincl;
+
+ hdrincl = 1;
+ /* We ignore any error returned by kern_setsockopt() */
+ kern_setsockopt(td, td->td_retval[0], IPPROTO_IP, IP_HDRINCL,
+ &hdrincl, UIO_SYSSPACE, sizeof(hdrincl));
+ }
+#ifdef INET6
+ /*
+ * Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by default
+ * and some apps depend on this. So, set V6ONLY to 0 for Linux apps.
+ * For simplicity we do this unconditionally of the net.inet6.ip6.v6only
+ * sysctl value.
+ */
+ if (bsd_args.domain == PF_INET6) {
+ int v6only;
+
+ v6only = 0;
+ /* We ignore any error returned by setsockopt() */
+ kern_setsockopt(td, td->td_retval[0], IPPROTO_IPV6, IPV6_V6ONLY,
+ &v6only, UIO_SYSSPACE, sizeof(v6only));
+ }
+#endif
+
+out:
+ return (retval_socket);
+}
+
+struct linux_bind_args {
+ int s;
+ l_uintptr_t name;
+ int namelen;
+};
+
+static int
+linux_bind(struct thread *td, struct linux_bind_args *args)
+{
+ struct sockaddr *sa;
+ int error;
+
+ error = linux_getsockaddr(&sa, PTRIN(args->name),
+ args->namelen);
+ if (error)
+ return (error);
+
+ error = kern_bind(td, args->s, sa);
+ free(sa, M_SONAME);
+ if (error == EADDRNOTAVAIL && args->namelen != sizeof(struct sockaddr_in))
+ return (EINVAL);
+ return (error);
+}
+
+struct linux_connect_args {
+ int s;
+ l_uintptr_t name;
+ int namelen;
+};
+int linux_connect(struct thread *, struct linux_connect_args *);
+
+int
+linux_connect(struct thread *td, struct linux_connect_args *args)
+{
+ struct socket *so;
+ struct sockaddr *sa;
+ u_int fflag;
+ int error;
+
+ error = linux_getsockaddr(&sa, (struct osockaddr *)PTRIN(args->name),
+ args->namelen);
+ if (error)
+ return (error);
+
+ error = kern_connect(td, args->s, sa);
+ free(sa, M_SONAME);
+ if (error != EISCONN)
+ return (error);
+
+ /*
+ * Linux doesn't return EISCONN the first time it occurs,
+ * when on a non-blocking socket. Instead it returns the
+ * error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
+ *
+ * XXXRW: Instead of using fgetsock(), check that it is a
+ * socket and use the file descriptor reference instead of
+ * creating a new one.
+ */
+ error = fgetsock(td, args->s, &so, &fflag);
+ if (error == 0) {
+ error = EISCONN;
+ if (fflag & FNONBLOCK) {
+ SOCK_LOCK(so);
+ if (so->so_emuldata == 0)
+ error = so->so_error;
+ so->so_emuldata = (void *)1;
+ SOCK_UNLOCK(so);
+ }
+ fputsock(so);
+ }
+ return (error);
+}
+
+struct linux_listen_args {
+ int s;
+ int backlog;
+};
+
+static int
+linux_listen(struct thread *td, struct linux_listen_args *args)
+{
+ struct listen_args /* {
+ int s;
+ int backlog;
+ } */ bsd_args;
+
+ bsd_args.s = args->s;
+ bsd_args.backlog = args->backlog;
+ return (listen(td, &bsd_args));
+}
+
+static int
+linux_accept_common(struct thread *td, int s, l_uintptr_t addr,
+ l_uintptr_t namelen, int flags)
+{
+ struct accept_args /* {
+ int s;
+ struct sockaddr * __restrict name;
+ socklen_t * __restrict anamelen;
+ } */ bsd_args;
+ int error;
+
+ if (flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
+ return (EINVAL);
+
+ bsd_args.s = s;
+ /* XXX: */
+ bsd_args.name = (struct sockaddr * __restrict)PTRIN(addr);
+ bsd_args.anamelen = PTRIN(namelen);/* XXX */
+ error = accept(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.name);
+ if (error) {
+ if (error == EFAULT && namelen != sizeof(struct sockaddr_in))
+ return (EINVAL);
+ return (error);
+ }
+
+ /*
+ * linux appears not to copy flags from the parent socket to the
+ * accepted one, so we must clear the flags in the new descriptor
+ * and apply the requested flags.
+ */
+ error = kern_fcntl(td, td->td_retval[0], F_SETFL, 0);
+ if (error)
+ goto out;
+ error = linux_set_socket_flags(td, td->td_retval[0], flags);
+ if (error)
+ goto out;
+ if (addr)
+ error = linux_sa_put(PTRIN(addr));
+
+out:
+ if (error) {
+ (void)kern_close(td, td->td_retval[0]);
+ td->td_retval[0] = 0;
+ }
+ return (error);
+}
+
+struct linux_accept_args {
+ int s;
+ l_uintptr_t addr;
+ l_uintptr_t namelen;
+};
+
+static int
+linux_accept(struct thread *td, struct linux_accept_args *args)
+{
+
+ return (linux_accept_common(td, args->s, args->addr,
+ args->namelen, 0));
+}
+
+struct linux_accept4_args {
+ int s;
+ l_uintptr_t addr;
+ l_uintptr_t namelen;
+ int flags;
+};
+
+static int
+linux_accept4(struct thread *td, struct linux_accept4_args *args)
+{
+
+ return (linux_accept_common(td, args->s, args->addr,
+ args->namelen, args->flags));
+}
+
+struct linux_getsockname_args {
+ int s;
+ l_uintptr_t addr;
+ l_uintptr_t namelen;
+};
+
+static int
+linux_getsockname(struct thread *td, struct linux_getsockname_args *args)
+{
+ struct getsockname_args /* {
+ int fdes;
+ struct sockaddr * __restrict asa;
+ socklen_t * __restrict alen;
+ } */ bsd_args;
+ int error;
+
+ bsd_args.fdes = args->s;
+ /* XXX: */
+ bsd_args.asa = (struct sockaddr * __restrict)PTRIN(args->addr);
+ bsd_args.alen = PTRIN(args->namelen); /* XXX */
+ error = getsockname(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
+ if (error)
+ return (error);
+ error = linux_sa_put(PTRIN(args->addr));
+ if (error)
+ return (error);
+ return (0);
+}
+
+struct linux_getpeername_args {
+ int s;
+ l_uintptr_t addr;
+ l_uintptr_t namelen;
+};
+
+static int
+linux_getpeername(struct thread *td, struct linux_getpeername_args *args)
+{
+ struct getpeername_args /* {
+ int fdes;
+ caddr_t asa;
+ int *alen;
+ } */ bsd_args;
+ int error;
+
+ bsd_args.fdes = args->s;
+ bsd_args.asa = (struct sockaddr *)PTRIN(args->addr);
+ bsd_args.alen = (int *)PTRIN(args->namelen);
+ error = getpeername(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.asa);
+ if (error)
+ return (error);
+ error = linux_sa_put(PTRIN(args->addr));
+ if (error)
+ return (error);
+ return (0);
+}
+
+struct linux_socketpair_args {
+ int domain;
+ int type;
+ int protocol;
+ l_uintptr_t rsv;
+};
+
+static int
+linux_socketpair(struct thread *td, struct linux_socketpair_args *args)
+{
+ struct socketpair_args /* {
+ int domain;
+ int type;
+ int protocol;
+ int *rsv;
+ } */ bsd_args;
+ int error, socket_flags;
+ int sv[2];
+
+ bsd_args.domain = linux_to_bsd_domain(args->domain);
+ if (bsd_args.domain != PF_LOCAL)
+ return (EAFNOSUPPORT);
+
+ socket_flags = args->type & ~LINUX_SOCK_TYPE_MASK;
+ if (socket_flags & ~(LINUX_SOCK_CLOEXEC | LINUX_SOCK_NONBLOCK))
+ return (EINVAL);
+ bsd_args.type = args->type & LINUX_SOCK_TYPE_MASK;
+ if (bsd_args.type < 0 || bsd_args.type > LINUX_SOCK_MAX)
+ return (EINVAL);
+
+ if (args->protocol != 0 && args->protocol != PF_UNIX)
+
+ /*
+ * Use of PF_UNIX as protocol argument is not right,
+ * but Linux does it.
+ * Do not map PF_UNIX as its Linux value is identical
+ * to FreeBSD one.
+ */
+ return (EPROTONOSUPPORT);
+ else
+ bsd_args.protocol = 0;
+ bsd_args.rsv = (int *)PTRIN(args->rsv);
+ error = kern_socketpair(td, bsd_args.domain, bsd_args.type,
+ bsd_args.protocol, sv);
+ if (error)
+ return (error);
+ error = linux_set_socket_flags(td, sv[0], socket_flags);
+ if (error)
+ goto out;
+ error = linux_set_socket_flags(td, sv[1], socket_flags);
+ if (error)
+ goto out;
+
+ error = copyout(sv, bsd_args.rsv, 2 * sizeof(int));
+
+out:
+ if (error) {
+ (void)kern_close(td, sv[0]);
+ (void)kern_close(td, sv[1]);
+ }
+ return (error);
+}
+
+struct linux_send_args {
+ int s;
+ l_uintptr_t msg;
+ int len;
+ int flags;
+};
+
+static int
+linux_send(struct thread *td, struct linux_send_args *args)
+{
+ struct sendto_args /* {
+ int s;
+ caddr_t buf;
+ int len;
+ int flags;
+ caddr_t to;
+ int tolen;
+ } */ bsd_args;
+
+ bsd_args.s = args->s;
+ bsd_args.buf = (caddr_t)PTRIN(args->msg);
+ bsd_args.len = args->len;
+ bsd_args.flags = args->flags;
+ bsd_args.to = NULL;
+ bsd_args.tolen = 0;
+ return sendto(td, &bsd_args);
+}
+
+struct linux_recv_args {
+ int s;
+ l_uintptr_t msg;
+ int len;
+ int flags;
+};
+
+static int
+linux_recv(struct thread *td, struct linux_recv_args *args)
+{
+ struct recvfrom_args /* {
+ int s;
+ caddr_t buf;
+ int len;
+ int flags;
+ struct sockaddr *from;
+ socklen_t fromlenaddr;
+ } */ bsd_args;
+
+ bsd_args.s = args->s;
+ bsd_args.buf = (caddr_t)PTRIN(args->msg);
+ bsd_args.len = args->len;
+ bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
+ bsd_args.from = NULL;
+ bsd_args.fromlenaddr = 0;
+ return (recvfrom(td, &bsd_args));
+}
+
+static int
+linux_sendto(struct thread *td, struct linux_sendto_args *args)
+{
+ struct msghdr msg;
+ struct iovec aiov;
+ int error;
+
+ if (linux_check_hdrincl(td, args->s) == 0)
+ /* IP_HDRINCL set, tweak the packet before sending */
+ return (linux_sendto_hdrincl(td, args));
+
+ msg.msg_name = PTRIN(args->to);
+ msg.msg_namelen = args->tolen;
+ msg.msg_iov = &aiov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_flags = 0;
+ aiov.iov_base = PTRIN(args->msg);
+ aiov.iov_len = args->len;
+ error = linux_sendit(td, args->s, &msg, args->flags, NULL,
+ UIO_USERSPACE);
+ return (error);
+}
+
+struct linux_recvfrom_args {
+ int s;
+ l_uintptr_t buf;
+ int len;
+ int flags;
+ l_uintptr_t from;
+ l_uintptr_t fromlen;
+};
+
+static int
+linux_recvfrom(struct thread *td, struct linux_recvfrom_args *args)
+{
+ struct recvfrom_args /* {
+ int s;
+ caddr_t buf;
+ size_t len;
+ int flags;
+ struct sockaddr * __restrict from;
+ socklen_t * __restrict fromlenaddr;
+ } */ bsd_args;
+ size_t len;
+ int error;
+
+ if ((error = copyin(PTRIN(args->fromlen), &len, sizeof(size_t))))
+ return (error);
+
+ bsd_args.s = args->s;
+ bsd_args.buf = PTRIN(args->buf);
+ bsd_args.len = args->len;
+ bsd_args.flags = linux_to_bsd_msg_flags(args->flags);
+ /* XXX: */
+ bsd_args.from = (struct sockaddr * __restrict)PTRIN(args->from);
+ bsd_args.fromlenaddr = PTRIN(args->fromlen);/* XXX */
+
+ linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.from, len);
+ error = recvfrom(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.from);
+
+ if (error)
+ return (error);
+ if (args->from) {
+ error = linux_sa_put((struct osockaddr *)
+ PTRIN(args->from));
+ if (error)
+ return (error);
+ }
+ return (0);
+}
+
+struct linux_sendmsg_args {
+ int s;
+ l_uintptr_t msg;
+ int flags;
+};
+
+static int
+linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
+{
+ struct cmsghdr *cmsg;
+ struct cmsgcred cmcred;
+ struct mbuf *control;
+ struct msghdr msg;
+ struct l_cmsghdr linux_cmsg;
+ struct l_cmsghdr *ptr_cmsg;
+ struct l_msghdr linux_msg;
+ struct iovec *iov;
+ socklen_t datalen;
+ struct sockaddr *sa;
+ sa_family_t sa_family;
+ void *data;
+ int error;
+
+ error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
+ if (error)
+ return (error);
+
+ /*
+ * Some Linux applications (ping) define a non-NULL control data
+ * pointer, but a msg_controllen of 0, which is not allowed in the
+ * FreeBSD system call interface. NULL the msg_control pointer in
+ * order to handle this case. This should be checked, but allows the
+ * Linux ping to work.
+ */
+ if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0)
+ linux_msg.msg_control = PTROUT(NULL);
+
+ error = linux_to_bsd_msghdr(&msg, &linux_msg);
+ if (error)
+ return (error);
+
+#ifdef COMPAT_LINUX32
+ error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
+ &iov, EMSGSIZE);
+#else
+ error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
+#endif
+ if (error)
+ return (error);
+
+ control = NULL;
+ cmsg = NULL;
+
+ if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
+ error = kern_getsockname(td, args->s, &sa, &datalen);
+ if (error)
+ goto bad;
+ sa_family = sa->sa_family;
+ free(sa, M_SONAME);
+
+ error = ENOBUFS;
+ cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
+ control = m_get(M_WAIT, MT_CONTROL);
+ if (control == NULL)
+ goto bad;
+
+ do {
+ error = copyin(ptr_cmsg, &linux_cmsg,
+ sizeof(struct l_cmsghdr));
+ if (error)
+ goto bad;
+
+ error = EINVAL;
+ if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
+ goto bad;
+
+ /*
+ * Now we support only SCM_RIGHTS and SCM_CRED,
+ * so return EINVAL in any other cmsg_type
+ */
+ cmsg->cmsg_type =
+ linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
+ cmsg->cmsg_level =
+ linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level);
+ if (cmsg->cmsg_type == -1
+ || cmsg->cmsg_level != SOL_SOCKET)
+ goto bad;
+
+ /*
+ * Some applications (e.g. pulseaudio) attempt to
+ * send ancillary data even if the underlying protocol
+ * doesn't support it which is not allowed in the
+ * FreeBSD system call interface.
+ */
+ if (sa_family != AF_UNIX)
+ continue;
+
+ data = LINUX_CMSG_DATA(ptr_cmsg);
+ datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
+
+ switch (cmsg->cmsg_type)
+ {
+ case SCM_RIGHTS:
+ break;
+
+ case SCM_CREDS:
+ data = &cmcred;
+ datalen = sizeof(cmcred);
+
+ /*
+ * The lower levels will fill in the structure
+ */
+ bzero(data, datalen);
+ break;
+ }
+
+ cmsg->cmsg_len = CMSG_LEN(datalen);
+
+ error = ENOBUFS;
+ if (!m_append(control, CMSG_HDRSZ, (c_caddr_t) cmsg))
+ goto bad;
+ if (!m_append(control, datalen, (c_caddr_t) data))
+ goto bad;
+ } while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
+
+ if (m_length(control, NULL) == 0) {
+ m_freem(control);
+ control = NULL;
+ }
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_flags = 0;
+ error = linux_sendit(td, args->s, &msg, args->flags, control,
+ UIO_USERSPACE);
+
+bad:
+ free(iov, M_IOV);
+ if (cmsg)
+ free(cmsg, M_TEMP);
+ return (error);
+}
+
+struct linux_recvmsg_args {
+ int s;
+ l_uintptr_t msg;
+ int flags;
+};
+
+static int
+linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
+{
+ struct cmsghdr *cm;
+ struct cmsgcred *cmcred;
+ struct msghdr msg;
+ struct l_cmsghdr *linux_cmsg = NULL;
+ struct l_ucred linux_ucred;
+ socklen_t datalen, outlen;
+ struct l_msghdr linux_msg;
+ struct iovec *iov, *uiov;
+ struct mbuf *control = NULL;
+ struct mbuf **controlp;
+ caddr_t outbuf;
+ void *data;
+ int error, i, fd, fds, *fdp;
+
+ error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg));
+ if (error)
+ return (error);
+
+ error = linux_to_bsd_msghdr(&msg, &linux_msg);
+ if (error)
+ return (error);
+
+#ifdef COMPAT_LINUX32
+ error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen,
+ &iov, EMSGSIZE);
+#else
+ error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE);
+#endif
+ if (error)
+ return (error);
+
+ if (msg.msg_name) {
+ error = linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name,
+ msg.msg_namelen);
+ if (error)
+ goto bad;
+ }
+
+ uiov = msg.msg_iov;
+ msg.msg_iov = iov;
+ controlp = (msg.msg_control != NULL) ? &control : NULL;
+ error = kern_recvit(td, args->s, &msg, UIO_USERSPACE, controlp);
+ msg.msg_iov = uiov;
+ if (error)
+ goto bad;
+
+ error = bsd_to_linux_msghdr(&msg, &linux_msg);
+ if (error)
+ goto bad;
+
+ if (linux_msg.msg_name) {
+ error = bsd_to_linux_sockaddr((struct sockaddr *)
+ PTRIN(linux_msg.msg_name));
+ if (error)
+ goto bad;
+ }
+ if (linux_msg.msg_name && linux_msg.msg_namelen > 2) {
+ error = linux_sa_put(PTRIN(linux_msg.msg_name));
+ if (error)
+ goto bad;
+ }
+
+ outbuf = PTRIN(linux_msg.msg_control);
+ outlen = 0;
+
+ if (control) {
+ linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO);
+
+ msg.msg_control = mtod(control, struct cmsghdr *);
+ msg.msg_controllen = control->m_len;
+
+ cm = CMSG_FIRSTHDR(&msg);
+
+ while (cm != NULL) {
+ linux_cmsg->cmsg_type =
+ bsd_to_linux_cmsg_type(cm->cmsg_type);
+ linux_cmsg->cmsg_level =
+ bsd_to_linux_sockopt_level(cm->cmsg_level);
+ if (linux_cmsg->cmsg_type == -1
+ || cm->cmsg_level != SOL_SOCKET)
+ {
+ error = EINVAL;
+ goto bad;
+ }
+
+ data = CMSG_DATA(cm);
+ datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
+
+ switch (cm->cmsg_type)
+ {
+ case SCM_RIGHTS:
+ if (args->flags & LINUX_MSG_CMSG_CLOEXEC) {
+ fds = datalen / sizeof(int);
+ fdp = data;
+ for (i = 0; i < fds; i++) {
+ fd = *fdp++;
+ (void)kern_fcntl(td, fd,
+ F_SETFD, FD_CLOEXEC);
+ }
+ }
+ break;
+
+ case SCM_CREDS:
+ /*
+ * Currently LOCAL_CREDS is never in
+ * effect for Linux so no need to worry
+ * about sockcred
+ */
+ if (datalen != sizeof (*cmcred)) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ cmcred = (struct cmsgcred *)data;
+ bzero(&linux_ucred, sizeof(linux_ucred));
+ linux_ucred.pid = cmcred->cmcred_pid;
+ linux_ucred.uid = cmcred->cmcred_uid;
+ linux_ucred.gid = cmcred->cmcred_gid;
+ data = &linux_ucred;
+ datalen = sizeof(linux_ucred);
+ break;
+ }
+
+ if (outlen + LINUX_CMSG_LEN(datalen) >
+ linux_msg.msg_controllen) {
+ if (outlen == 0) {
+ error = EMSGSIZE;
+ goto bad;
+ } else {
+ linux_msg.msg_flags |=
+ LINUX_MSG_CTRUNC;
+ goto out;
+ }
+ }
+
+ linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen);
+
+ error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ);
+ if (error)
+ goto bad;
+ outbuf += L_CMSG_HDRSZ;
+
+ error = copyout(data, outbuf, datalen);
+ if (error)
+ goto bad;
+
+ outbuf += LINUX_CMSG_ALIGN(datalen);
+ outlen += LINUX_CMSG_LEN(datalen);
+
+ cm = CMSG_NXTHDR(&msg, cm);
+ }
+ }
+
+out:
+ linux_msg.msg_controllen = outlen;
+ error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg));
+
+bad:
+ free(iov, M_IOV);
+ if (control != NULL)
+ m_freem(control);
+ if (linux_cmsg != NULL)
+ free(linux_cmsg, M_TEMP);
+
+ return (error);
+}
+
+struct linux_shutdown_args {
+ int s;
+ int how;
+};
+
+static int
+linux_shutdown(struct thread *td, struct linux_shutdown_args *args)
+{
+ struct shutdown_args /* {
+ int s;
+ int how;
+ } */ bsd_args;
+
+ bsd_args.s = args->s;
+ bsd_args.how = args->how;
+ return (shutdown(td, &bsd_args));
+}
+
+struct linux_setsockopt_args {
+ int s;
+ int level;
+ int optname;
+ l_uintptr_t optval;
+ int optlen;
+};
+
+static int
+linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
+{
+ struct setsockopt_args /* {
+ int s;
+ int level;
+ int name;
+ caddr_t val;
+ int valsize;
+ } */ bsd_args;
+ l_timeval linux_tv;
+ struct timeval tv;
+ int error, name;
+
+ bsd_args.s = args->s;
+ bsd_args.level = linux_to_bsd_sockopt_level(args->level);
+ switch (bsd_args.level) {
+ case SOL_SOCKET:
+ name = linux_to_bsd_so_sockopt(args->optname);
+ switch (name) {
+ case SO_RCVTIMEO:
+ /* FALLTHROUGH */
+ case SO_SNDTIMEO:
+ error = copyin(PTRIN(args->optval), &linux_tv,
+ sizeof(linux_tv));
+ if (error)
+ return (error);
+ tv.tv_sec = linux_tv.tv_sec;
+ tv.tv_usec = linux_tv.tv_usec;
+ return (kern_setsockopt(td, args->s, bsd_args.level,
+ name, &tv, UIO_SYSSPACE, sizeof(tv)));
+ /* NOTREACHED */
+ break;
+ default:
+ break;
+ }
+ break;
+ case IPPROTO_IP:
+ name = linux_to_bsd_ip_sockopt(args->optname);
+ break;
+ case IPPROTO_TCP:
+ /* Linux TCP option values match BSD's */
+ name = args->optname;
+ break;
+ default:
+ name = -1;
+ break;
+ }
+ if (name == -1)
+ return (ENOPROTOOPT);
+
+ bsd_args.name = name;
+ bsd_args.val = PTRIN(args->optval);
+ bsd_args.valsize = args->optlen;
+
+ if (name == IPV6_NEXTHOP) {
+ linux_to_bsd_sockaddr((struct sockaddr *)bsd_args.val,
+ bsd_args.valsize);
+ error = setsockopt(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
+ } else
+ error = setsockopt(td, &bsd_args);
+
+ return (error);
+}
+
+struct linux_getsockopt_args {
+ int s;
+ int level;
+ int optname;
+ l_uintptr_t optval;
+ l_uintptr_t optlen;
+};
+
+static int
+linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
+{
+ struct getsockopt_args /* {
+ int s;
+ int level;
+ int name;
+ caddr_t val;
+ int *avalsize;
+ } */ bsd_args;
+ l_timeval linux_tv;
+ struct timeval tv;
+ socklen_t tv_len, xulen;
+ struct xucred xu;
+ struct l_ucred lxu;
+ int error, name;
+
+ bsd_args.s = args->s;
+ bsd_args.level = linux_to_bsd_sockopt_level(args->level);
+ switch (bsd_args.level) {
+ case SOL_SOCKET:
+ name = linux_to_bsd_so_sockopt(args->optname);
+ switch (name) {
+ case SO_RCVTIMEO:
+ /* FALLTHROUGH */
+ case SO_SNDTIMEO:
+ tv_len = sizeof(tv);
+ error = kern_getsockopt(td, args->s, bsd_args.level,
+ name, &tv, UIO_SYSSPACE, &tv_len);
+ if (error)
+ return (error);
+ linux_tv.tv_sec = tv.tv_sec;
+ linux_tv.tv_usec = tv.tv_usec;
+ return (copyout(&linux_tv, PTRIN(args->optval),
+ sizeof(linux_tv)));
+ /* NOTREACHED */
+ break;
+ case LOCAL_PEERCRED:
+ if (args->optlen != sizeof(lxu))
+ return (EINVAL);
+ xulen = sizeof(xu);
+ error = kern_getsockopt(td, args->s, bsd_args.level,
+ name, &xu, UIO_SYSSPACE, &xulen);
+ if (error)
+ return (error);
+ /*
+ * XXX Use 0 for pid as the FreeBSD does not cache peer pid.
+ */
+ lxu.pid = 0;
+ lxu.uid = xu.cr_uid;
+ lxu.gid = xu.cr_gid;
+ return (copyout(&lxu, PTRIN(args->optval), sizeof(lxu)));
+ /* NOTREACHED */
+ break;
+ default:
+ break;
+ }
+ break;
+ case IPPROTO_IP:
+ name = linux_to_bsd_ip_sockopt(args->optname);
+ break;
+ case IPPROTO_TCP:
+ /* Linux TCP option values match BSD's */
+ name = args->optname;
+ break;
+ default:
+ name = -1;
+ break;
+ }
+ if (name == -1)
+ return (EINVAL);
+
+ bsd_args.name = name;
+ bsd_args.val = PTRIN(args->optval);
+ bsd_args.avalsize = PTRIN(args->optlen);
+
+ if (name == IPV6_NEXTHOP) {
+ error = getsockopt(td, &bsd_args);
+ bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.val);
+ } else
+ error = getsockopt(td, &bsd_args);
+
+ return (error);
+}
+
+/* Argument list sizes for linux_socketcall */
+
+#define LINUX_AL(x) ((x) * sizeof(l_ulong))
+
+static const unsigned char lxs_args[] = {
+ LINUX_AL(0) /* unused*/, LINUX_AL(3) /* socket */,
+ LINUX_AL(3) /* bind */, LINUX_AL(3) /* connect */,
+ LINUX_AL(2) /* listen */, LINUX_AL(3) /* accept */,
+ LINUX_AL(3) /* getsockname */, LINUX_AL(3) /* getpeername */,
+ LINUX_AL(4) /* socketpair */, LINUX_AL(4) /* send */,
+ LINUX_AL(4) /* recv */, LINUX_AL(6) /* sendto */,
+ LINUX_AL(6) /* recvfrom */, LINUX_AL(2) /* shutdown */,
+ LINUX_AL(5) /* setsockopt */, LINUX_AL(5) /* getsockopt */,
+ LINUX_AL(3) /* sendmsg */, LINUX_AL(3) /* recvmsg */,
+ LINUX_AL(4) /* accept4 */
+};
+
+#define LINUX_AL_SIZE sizeof(lxs_args) / sizeof(lxs_args[0]) - 1
+
+int
+linux_socketcall(struct thread *td, struct linux_socketcall_args *args)
+{
+ l_ulong a[6];
+ void *arg;
+ int error;
+
+ if (args->what < LINUX_SOCKET || args->what > LINUX_AL_SIZE)
+ return (EINVAL);
+ error = copyin(PTRIN(args->args), a, lxs_args[args->what]);
+ if (error)
+ return (error);
+
+ arg = a;
+ switch (args->what) {
+ case LINUX_SOCKET:
+ return (linux_socket(td, arg));
+ case LINUX_BIND:
+ return (linux_bind(td, arg));
+ case LINUX_CONNECT:
+ return (linux_connect(td, arg));
+ case LINUX_LISTEN:
+ return (linux_listen(td, arg));
+ case LINUX_ACCEPT:
+ return (linux_accept(td, arg));
+ case LINUX_GETSOCKNAME:
+ return (linux_getsockname(td, arg));
+ case LINUX_GETPEERNAME:
+ return (linux_getpeername(td, arg));
+ case LINUX_SOCKETPAIR:
+ return (linux_socketpair(td, arg));
+ case LINUX_SEND:
+ return (linux_send(td, arg));
+ case LINUX_RECV:
+ return (linux_recv(td, arg));
+ case LINUX_SENDTO:
+ return (linux_sendto(td, arg));
+ case LINUX_RECVFROM:
+ return (linux_recvfrom(td, arg));
+ case LINUX_SHUTDOWN:
+ return (linux_shutdown(td, arg));
+ case LINUX_SETSOCKOPT:
+ return (linux_setsockopt(td, arg));
+ case LINUX_GETSOCKOPT:
+ return (linux_getsockopt(td, arg));
+ case LINUX_SENDMSG:
+ return (linux_sendmsg(td, arg));
+ case LINUX_RECVMSG:
+ return (linux_recvmsg(td, arg));
+ case LINUX_ACCEPT4:
+ return (linux_accept4(td, arg));
+ }
+
+ uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
+ return (ENOSYS);
+}
diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h
new file mode 100644
index 0000000..1fe53ed
--- /dev/null
+++ b/sys/compat/linux/linux_socket.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2000 Assar Westerlund
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_SOCKET_H_
+#define _LINUX_SOCKET_H_
+
+/* msg flags in recvfrom/recvmsg */
+
+#define LINUX_MSG_OOB 0x01
+#define LINUX_MSG_PEEK 0x02
+#define LINUX_MSG_DONTROUTE 0x04
+#define LINUX_MSG_CTRUNC 0x08
+#define LINUX_MSG_PROXY 0x10
+#define LINUX_MSG_TRUNC 0x20
+#define LINUX_MSG_DONTWAIT 0x40
+#define LINUX_MSG_EOR 0x80
+#define LINUX_MSG_WAITALL 0x100
+#define LINUX_MSG_FIN 0x200
+#define LINUX_MSG_SYN 0x400
+#define LINUX_MSG_CONFIRM 0x800
+#define LINUX_MSG_RST 0x1000
+#define LINUX_MSG_ERRQUEUE 0x2000
+#define LINUX_MSG_NOSIGNAL 0x4000
+#define LINUX_MSG_CMSG_CLOEXEC 0x40000000
+
+/* Socket-level control message types */
+
+#define LINUX_SCM_RIGHTS 0x01
+#define LINUX_SCM_CREDENTIALS 0x02
+
+/* Ancilliary data object information macros */
+
+#define LINUX_CMSG_ALIGN(len) roundup2(len, sizeof(l_ulong))
+#define LINUX_CMSG_DATA(cmsg) ((void *)((char *)(cmsg) + \
+ LINUX_CMSG_ALIGN(sizeof(struct l_cmsghdr))))
+#define LINUX_CMSG_SPACE(len) (LINUX_CMSG_ALIGN(sizeof(struct l_cmsghdr)) + \
+ LINUX_CMSG_ALIGN(len))
+#define LINUX_CMSG_LEN(len) (LINUX_CMSG_ALIGN(sizeof(struct l_cmsghdr)) + \
+ (len))
+#define LINUX_CMSG_FIRSTHDR(msg) \
+ ((msg)->msg_controllen >= \
+ sizeof(struct l_cmsghdr) ? \
+ (struct l_cmsghdr *) \
+ PTRIN((msg)->msg_control) : \
+ (struct l_cmsghdr *)(NULL))
+#define LINUX_CMSG_NXTHDR(msg, cmsg) \
+ ((((char *)(cmsg) + \
+ LINUX_CMSG_ALIGN((cmsg)->cmsg_len) + \
+ sizeof(*(cmsg))) > \
+ (((char *)PTRIN((msg)->msg_control)) + \
+ (msg)->msg_controllen)) ? \
+ (struct l_cmsghdr *) NULL : \
+ (struct l_cmsghdr *)((char *)(cmsg) + \
+ LINUX_CMSG_ALIGN((cmsg)->cmsg_len)))
+
+#define CMSG_HDRSZ CMSG_LEN(0)
+#define L_CMSG_HDRSZ LINUX_CMSG_LEN(0)
+
+/* Supported address families */
+
+#define LINUX_AF_UNSPEC 0
+#define LINUX_AF_UNIX 1
+#define LINUX_AF_INET 2
+#define LINUX_AF_AX25 3
+#define LINUX_AF_IPX 4
+#define LINUX_AF_APPLETALK 5
+#define LINUX_AF_INET6 10
+
+/* Supported socket types */
+
+#define LINUX_SOCK_STREAM 1
+#define LINUX_SOCK_DGRAM 2
+#define LINUX_SOCK_RAW 3
+#define LINUX_SOCK_RDM 4
+#define LINUX_SOCK_SEQPACKET 5
+
+#define LINUX_SOCK_MAX LINUX_SOCK_SEQPACKET
+
+#define LINUX_SOCK_TYPE_MASK 0xf
+
+/* Flags for socket, socketpair, accept4 */
+
+#define LINUX_SOCK_CLOEXEC LINUX_O_CLOEXEC
+#define LINUX_SOCK_NONBLOCK LINUX_O_NONBLOCK
+
+struct l_ucred {
+ uint32_t pid;
+ uint32_t uid;
+ uint32_t gid;
+};
+
+#endif /* _LINUX_SOCKET_H_ */
diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c
new file mode 100644
index 0000000..8fa08b6
--- /dev/null
+++ b/sys/compat/linux/linux_stats.c
@@ -0,0 +1,624 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/dirent.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/syscallsubr.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+#include <sys/vnode.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_file.h>
+
+#define LINUX_SHMFS_MAGIC 0x01021994
+
+static void
+translate_vnhook_major_minor(struct vnode *vp, struct stat *sb)
+{
+ int major, minor;
+
+ if (vp->v_type == VCHR && vp->v_rdev != NULL &&
+ linux_driver_get_major_minor(vp->v_rdev->si_name,
+ &major, &minor) == 0) {
+ sb->st_rdev = (major << 8 | minor);
+ }
+}
+
+static int
+linux_kern_statat(struct thread *td, int flag, int fd, char *path,
+ enum uio_seg pathseg, struct stat *sbp)
+{
+
+ return (kern_statat_vnhook(td, flag, fd, path, pathseg, sbp,
+ translate_vnhook_major_minor));
+}
+
+static int
+linux_kern_stat(struct thread *td, char *path, enum uio_seg pathseg,
+ struct stat *sbp)
+{
+
+ return (linux_kern_statat(td, 0, AT_FDCWD, path, pathseg, sbp));
+}
+
+static int
+linux_kern_lstat(struct thread *td, char *path, enum uio_seg pathseg,
+ struct stat *sbp)
+{
+
+ return (linux_kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, path,
+ pathseg, sbp));
+}
+
+/*
+ * XXX: This was removed from newstat_copyout(), and almost identical
+ * XXX: code was in stat64_copyout(). findcdev() needs to be replaced
+ * XXX: with something that does lookup and locking properly.
+ * XXX: When somebody fixes this: please try to avoid duplicating it.
+ */
+#if 0
+static void
+disk_foo(struct somestat *tbuf)
+{
+ struct cdevsw *cdevsw;
+ struct cdev *dev;
+
+ /* Lie about disk drives which are character devices
+ * in FreeBSD but block devices under Linux.
+ */
+ if (S_ISCHR(tbuf.st_mode) &&
+ (dev = findcdev(buf->st_rdev)) != NULL) {
+ cdevsw = dev_refthread(dev);
+ if (cdevsw != NULL) {
+ if (cdevsw->d_flags & D_DISK) {
+ tbuf.st_mode &= ~S_IFMT;
+ tbuf.st_mode |= S_IFBLK;
+
+ /* XXX this may not be quite right */
+ /* Map major number to 0 */
+ tbuf.st_dev = minor(buf->st_dev) & 0xf;
+ tbuf.st_rdev = buf->st_rdev & 0xff;
+ }
+ dev_relthread(dev);
+ }
+ }
+
+}
+#endif
+
+static void
+translate_fd_major_minor(struct thread *td, int fd, struct stat *buf)
+{
+ struct file *fp;
+ struct vnode *vp;
+ int major, minor;
+
+ if ((!S_ISCHR(buf->st_mode) && !S_ISBLK(buf->st_mode)) ||
+ fget(td, fd, &fp) != 0)
+ return;
+ vp = fp->f_vnode;
+ if (vp != NULL && vp->v_rdev != NULL &&
+ linux_driver_get_major_minor(vp->v_rdev->si_name,
+ &major, &minor) == 0) {
+ buf->st_rdev = (major << 8 | minor);
+ } else if (fp->f_type == DTYPE_PTS) {
+ struct tty *tp = fp->f_data;
+
+ /* Convert the numbers for the slave device. */
+ if (linux_driver_get_major_minor(tp->t_dev->si_name,
+ &major, &minor) == 0) {
+ buf->st_rdev = (major << 8 | minor);
+ }
+ }
+ fdrop(fp, td);
+}
+
+static int
+newstat_copyout(struct stat *buf, void *ubuf)
+{
+ struct l_newstat tbuf;
+
+ bzero(&tbuf, sizeof(tbuf));
+ tbuf.st_dev = minor(buf->st_dev) | (major(buf->st_dev) << 8);
+ tbuf.st_ino = buf->st_ino;
+ tbuf.st_mode = buf->st_mode;
+ tbuf.st_nlink = buf->st_nlink;
+ tbuf.st_uid = buf->st_uid;
+ tbuf.st_gid = buf->st_gid;
+ tbuf.st_rdev = buf->st_rdev;
+ tbuf.st_size = buf->st_size;
+ tbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
+ tbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
+ tbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
+ tbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
+ tbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
+ tbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
+ tbuf.st_blksize = buf->st_blksize;
+ tbuf.st_blocks = buf->st_blocks;
+
+ return (copyout(&tbuf, ubuf, sizeof(tbuf)));
+}
+
+int
+linux_newstat(struct thread *td, struct linux_newstat_args *args)
+{
+ struct stat buf;
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(newstat))
+ printf(ARGS(newstat, "%s, *"), path);
+#endif
+
+ error = linux_kern_stat(td, path, UIO_SYSSPACE, &buf);
+ LFREEPATH(path);
+ if (error)
+ return (error);
+ return (newstat_copyout(&buf, args->buf));
+}
+
+int
+linux_newlstat(struct thread *td, struct linux_newlstat_args *args)
+{
+ struct stat sb;
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(newlstat))
+ printf(ARGS(newlstat, "%s, *"), path);
+#endif
+
+ error = linux_kern_lstat(td, path, UIO_SYSSPACE, &sb);
+ LFREEPATH(path);
+ if (error)
+ return (error);
+ return (newstat_copyout(&sb, args->buf));
+}
+
+int
+linux_newfstat(struct thread *td, struct linux_newfstat_args *args)
+{
+ struct stat buf;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(newfstat))
+ printf(ARGS(newfstat, "%d, *"), args->fd);
+#endif
+
+ error = kern_fstat(td, args->fd, &buf);
+ translate_fd_major_minor(td, args->fd, &buf);
+ if (!error)
+ error = newstat_copyout(&buf, args->buf);
+
+ return (error);
+}
+
+static int
+stat_copyout(struct stat *buf, void *ubuf)
+{
+ struct l_stat lbuf;
+
+ bzero(&lbuf, sizeof(lbuf));
+ lbuf.st_dev = buf->st_dev;
+ lbuf.st_ino = buf->st_ino;
+ lbuf.st_mode = buf->st_mode;
+ lbuf.st_nlink = buf->st_nlink;
+ lbuf.st_uid = buf->st_uid;
+ lbuf.st_gid = buf->st_gid;
+ lbuf.st_rdev = buf->st_rdev;
+ if (buf->st_size < (quad_t)1 << 32)
+ lbuf.st_size = buf->st_size;
+ else
+ lbuf.st_size = -2;
+ lbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
+ lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
+ lbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
+ lbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
+ lbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
+ lbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
+ lbuf.st_blksize = buf->st_blksize;
+ lbuf.st_blocks = buf->st_blocks;
+ lbuf.st_flags = buf->st_flags;
+ lbuf.st_gen = buf->st_gen;
+
+ return (copyout(&lbuf, ubuf, sizeof(lbuf)));
+}
+
+int
+linux_stat(struct thread *td, struct linux_stat_args *args)
+{
+ struct stat buf;
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(stat))
+ printf(ARGS(stat, "%s, *"), path);
+#endif
+ error = linux_kern_stat(td, path, UIO_SYSSPACE, &buf);
+ if (error) {
+ LFREEPATH(path);
+ return (error);
+ }
+ LFREEPATH(path);
+ return(stat_copyout(&buf, args->up));
+}
+
+int
+linux_lstat(struct thread *td, struct linux_lstat_args *args)
+{
+ struct stat buf;
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(lstat))
+ printf(ARGS(lstat, "%s, *"), path);
+#endif
+ error = linux_kern_lstat(td, path, UIO_SYSSPACE, &buf);
+ if (error) {
+ LFREEPATH(path);
+ return (error);
+ }
+ LFREEPATH(path);
+ return(stat_copyout(&buf, args->up));
+}
+
+/* XXX - All fields of type l_int are defined as l_long on i386 */
+struct l_statfs {
+ l_int f_type;
+ l_int f_bsize;
+ l_int f_blocks;
+ l_int f_bfree;
+ l_int f_bavail;
+ l_int f_files;
+ l_int f_ffree;
+ l_fsid_t f_fsid;
+ l_int f_namelen;
+ l_int f_spare[6];
+};
+
+#define LINUX_CODA_SUPER_MAGIC 0x73757245L
+#define LINUX_EXT2_SUPER_MAGIC 0xEF53L
+#define LINUX_HPFS_SUPER_MAGIC 0xf995e849L
+#define LINUX_ISOFS_SUPER_MAGIC 0x9660L
+#define LINUX_MSDOS_SUPER_MAGIC 0x4d44L
+#define LINUX_NCP_SUPER_MAGIC 0x564cL
+#define LINUX_NFS_SUPER_MAGIC 0x6969L
+#define LINUX_NTFS_SUPER_MAGIC 0x5346544EL
+#define LINUX_PROC_SUPER_MAGIC 0x9fa0L
+#define LINUX_UFS_SUPER_MAGIC 0x00011954L /* XXX - UFS_MAGIC in Linux */
+#define LINUX_DEVFS_SUPER_MAGIC 0x1373L
+
+static long
+bsd_to_linux_ftype(const char *fstypename)
+{
+ int i;
+ static struct {const char *bsd_name; long linux_type;} b2l_tbl[] = {
+ {"ufs", LINUX_UFS_SUPER_MAGIC},
+ {"cd9660", LINUX_ISOFS_SUPER_MAGIC},
+ {"nfs", LINUX_NFS_SUPER_MAGIC},
+ {"ext2fs", LINUX_EXT2_SUPER_MAGIC},
+ {"procfs", LINUX_PROC_SUPER_MAGIC},
+ {"msdosfs", LINUX_MSDOS_SUPER_MAGIC},
+ {"ntfs", LINUX_NTFS_SUPER_MAGIC},
+ {"nwfs", LINUX_NCP_SUPER_MAGIC},
+ {"hpfs", LINUX_HPFS_SUPER_MAGIC},
+ {"coda", LINUX_CODA_SUPER_MAGIC},
+ {"devfs", LINUX_DEVFS_SUPER_MAGIC},
+ {NULL, 0L}};
+
+ for (i = 0; b2l_tbl[i].bsd_name != NULL; i++)
+ if (strcmp(b2l_tbl[i].bsd_name, fstypename) == 0)
+ return (b2l_tbl[i].linux_type);
+
+ return (0L);
+}
+
+static void
+bsd_to_linux_statfs(struct statfs *bsd_statfs, struct l_statfs *linux_statfs)
+{
+
+ linux_statfs->f_type = bsd_to_linux_ftype(bsd_statfs->f_fstypename);
+ linux_statfs->f_bsize = bsd_statfs->f_bsize;
+ linux_statfs->f_blocks = bsd_statfs->f_blocks;
+ linux_statfs->f_bfree = bsd_statfs->f_bfree;
+ linux_statfs->f_bavail = bsd_statfs->f_bavail;
+ linux_statfs->f_ffree = bsd_statfs->f_ffree;
+ linux_statfs->f_files = bsd_statfs->f_files;
+ linux_statfs->f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
+ linux_statfs->f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
+ linux_statfs->f_namelen = MAXNAMLEN;
+}
+
+int
+linux_statfs(struct thread *td, struct linux_statfs_args *args)
+{
+ struct l_statfs linux_statfs;
+ struct statfs bsd_statfs;
+ char *path;
+ int error, dev_shm;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(statfs))
+ printf(ARGS(statfs, "%s, *"), path);
+#endif
+ dev_shm = 0;
+ error = kern_statfs(td, path, UIO_SYSSPACE, &bsd_statfs);
+ if (strncmp(path, "/dev/shm", sizeof("/dev/shm") - 1) == 0)
+ dev_shm = (path[8] == '\0'
+ || (path[8] == '/' && path[9] == '\0'));
+ LFREEPATH(path);
+ if (error)
+ return (error);
+ bsd_to_linux_statfs(&bsd_statfs, &linux_statfs);
+ if (dev_shm)
+ linux_statfs.f_type = LINUX_SHMFS_MAGIC;
+ return copyout(&linux_statfs, args->buf, sizeof(linux_statfs));
+}
+
+static void
+bsd_to_linux_statfs64(struct statfs *bsd_statfs, struct l_statfs64 *linux_statfs)
+{
+
+ linux_statfs->f_type = bsd_to_linux_ftype(bsd_statfs->f_fstypename);
+ linux_statfs->f_bsize = bsd_statfs->f_bsize;
+ linux_statfs->f_blocks = bsd_statfs->f_blocks;
+ linux_statfs->f_bfree = bsd_statfs->f_bfree;
+ linux_statfs->f_bavail = bsd_statfs->f_bavail;
+ linux_statfs->f_ffree = bsd_statfs->f_ffree;
+ linux_statfs->f_files = bsd_statfs->f_files;
+ linux_statfs->f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
+ linux_statfs->f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
+ linux_statfs->f_namelen = MAXNAMLEN;
+}
+
+int
+linux_statfs64(struct thread *td, struct linux_statfs64_args *args)
+{
+ struct l_statfs64 linux_statfs;
+ struct statfs bsd_statfs;
+ char *path;
+ int error;
+
+ if (args->bufsize != sizeof(struct l_statfs64))
+ return EINVAL;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(statfs64))
+ printf(ARGS(statfs64, "%s, *"), path);
+#endif
+ error = kern_statfs(td, path, UIO_SYSSPACE, &bsd_statfs);
+ LFREEPATH(path);
+ if (error)
+ return (error);
+ bsd_to_linux_statfs64(&bsd_statfs, &linux_statfs);
+ return copyout(&linux_statfs, args->buf, sizeof(linux_statfs));
+}
+
+int
+linux_fstatfs(struct thread *td, struct linux_fstatfs_args *args)
+{
+ struct l_statfs linux_statfs;
+ struct statfs bsd_statfs;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(fstatfs))
+ printf(ARGS(fstatfs, "%d, *"), args->fd);
+#endif
+ error = kern_fstatfs(td, args->fd, &bsd_statfs);
+ if (error)
+ return error;
+ bsd_to_linux_statfs(&bsd_statfs, &linux_statfs);
+ return copyout(&linux_statfs, args->buf, sizeof(linux_statfs));
+}
+
+struct l_ustat
+{
+ l_daddr_t f_tfree;
+ l_ino_t f_tinode;
+ char f_fname[6];
+ char f_fpack[6];
+};
+
+int
+linux_ustat(struct thread *td, struct linux_ustat_args *args)
+{
+#ifdef DEBUG
+ if (ldebug(ustat))
+ printf(ARGS(ustat, "%d, *"), args->dev);
+#endif
+
+ return (EOPNOTSUPP);
+}
+
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+
+static int
+stat64_copyout(struct stat *buf, void *ubuf)
+{
+ struct l_stat64 lbuf;
+
+ bzero(&lbuf, sizeof(lbuf));
+ lbuf.st_dev = minor(buf->st_dev) | (major(buf->st_dev) << 8);
+ lbuf.st_ino = buf->st_ino;
+ lbuf.st_mode = buf->st_mode;
+ lbuf.st_nlink = buf->st_nlink;
+ lbuf.st_uid = buf->st_uid;
+ lbuf.st_gid = buf->st_gid;
+ lbuf.st_rdev = buf->st_rdev;
+ lbuf.st_size = buf->st_size;
+ lbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
+ lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
+ lbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
+ lbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
+ lbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
+ lbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
+ lbuf.st_blksize = buf->st_blksize;
+ lbuf.st_blocks = buf->st_blocks;
+
+ /*
+ * The __st_ino field makes all the difference. In the Linux kernel
+ * it is conditionally compiled based on STAT64_HAS_BROKEN_ST_INO,
+ * but without the assignment to __st_ino the runtime linker refuses
+ * to mmap(2) any shared libraries. I guess it's broken alright :-)
+ */
+ lbuf.__st_ino = buf->st_ino;
+
+ return (copyout(&lbuf, ubuf, sizeof(lbuf)));
+}
+
+int
+linux_stat64(struct thread *td, struct linux_stat64_args *args)
+{
+ struct stat buf;
+ char *filename;
+ int error;
+
+ LCONVPATHEXIST(td, args->filename, &filename);
+
+#ifdef DEBUG
+ if (ldebug(stat64))
+ printf(ARGS(stat64, "%s, *"), filename);
+#endif
+
+ error = linux_kern_stat(td, filename, UIO_SYSSPACE, &buf);
+ LFREEPATH(filename);
+ if (error)
+ return (error);
+ return (stat64_copyout(&buf, args->statbuf));
+}
+
+int
+linux_lstat64(struct thread *td, struct linux_lstat64_args *args)
+{
+ struct stat sb;
+ char *filename;
+ int error;
+
+ LCONVPATHEXIST(td, args->filename, &filename);
+
+#ifdef DEBUG
+ if (ldebug(lstat64))
+ printf(ARGS(lstat64, "%s, *"), args->filename);
+#endif
+
+ error = linux_kern_lstat(td, filename, UIO_SYSSPACE, &sb);
+ LFREEPATH(filename);
+ if (error)
+ return (error);
+ return (stat64_copyout(&sb, args->statbuf));
+}
+
+int
+linux_fstat64(struct thread *td, struct linux_fstat64_args *args)
+{
+ struct stat buf;
+ int error;
+
+#ifdef DEBUG
+ if (ldebug(fstat64))
+ printf(ARGS(fstat64, "%d, *"), args->fd);
+#endif
+
+ error = kern_fstat(td, args->fd, &buf);
+ translate_fd_major_minor(td, args->fd, &buf);
+ if (!error)
+ error = stat64_copyout(&buf, args->statbuf);
+
+ return (error);
+}
+
+int
+linux_fstatat64(struct thread *td, struct linux_fstatat64_args *args)
+{
+ char *path;
+ int error, dfd, flag;
+ struct stat buf;
+
+ if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
+ return (EINVAL);
+ flag = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) ?
+ AT_SYMLINK_NOFOLLOW : 0;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fstatat64))
+ printf(ARGS(fstatat64, "%i, %s, %i"), args->dfd, path, args->flag);
+#endif
+
+ error = linux_kern_statat(td, flag, dfd, path, UIO_SYSSPACE, &buf);
+ if (!error)
+ error = stat64_copyout(&buf, args->statbuf);
+ LFREEPATH(path);
+
+ return (error);
+}
+
+#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
diff --git a/sys/compat/linux/linux_sysctl.c b/sys/compat/linux/linux_sysctl.c
new file mode 100644
index 0000000..9111dbe
--- /dev/null
+++ b/sys/compat/linux/linux_sysctl.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 2001 Marcel Moolenaar
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/sbuf.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_util.h>
+
+#define LINUX_CTL_KERN 1
+#define LINUX_CTL_VM 2
+#define LINUX_CTL_NET 3
+#define LINUX_CTL_PROC 4
+#define LINUX_CTL_FS 5
+#define LINUX_CTL_DEBUG 6
+#define LINUX_CTL_DEV 7
+#define LINUX_CTL_BUS 8
+
+/* CTL_KERN names */
+#define LINUX_KERN_OSTYPE 1
+#define LINUX_KERN_OSRELEASE 2
+#define LINUX_KERN_OSREV 3
+#define LINUX_KERN_VERSION 4
+
+static int
+handle_string(struct l___sysctl_args *la, char *value)
+{
+ int error;
+
+ if (la->oldval != 0) {
+ l_int len = strlen(value);
+ error = copyout(value, PTRIN(la->oldval), len + 1);
+ if (!error && la->oldlenp != 0)
+ error = copyout(&len, PTRIN(la->oldlenp), sizeof(len));
+ if (error)
+ return (error);
+ }
+
+ if (la->newval != 0)
+ return (ENOTDIR);
+
+ return (0);
+}
+
+int
+linux_sysctl(struct thread *td, struct linux_sysctl_args *args)
+{
+ struct l___sysctl_args la;
+ struct sbuf *sb;
+ l_int *mib;
+ int error, i;
+
+ error = copyin(args->args, &la, sizeof(la));
+ if (error)
+ return (error);
+
+ if (la.nlen <= 0 || la.nlen > LINUX_CTL_MAXNAME)
+ return (ENOTDIR);
+
+ mib = malloc(la.nlen * sizeof(l_int), M_TEMP, M_WAITOK);
+ error = copyin(PTRIN(la.name), mib, la.nlen * sizeof(l_int));
+ if (error) {
+ free(mib, M_TEMP);
+ return (error);
+ }
+
+ switch (mib[0]) {
+ case LINUX_CTL_KERN:
+ if (la.nlen < 2)
+ break;
+
+ switch (mib[1]) {
+ case LINUX_KERN_VERSION:
+ error = handle_string(&la, version);
+ free(mib, M_TEMP);
+ return (error);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ sb = sbuf_new(NULL, NULL, 20 + la.nlen * 5, SBUF_AUTOEXTEND);
+ if (sb == NULL) {
+ linux_msg(td, "sysctl is not implemented");
+ } else {
+ sbuf_printf(sb, "sysctl ");
+ for (i = 0; i < la.nlen; i++)
+ sbuf_printf(sb, "%c%d", (i) ? ',' : '{', mib[i]);
+ sbuf_printf(sb, "} is not implemented");
+ sbuf_finish(sb);
+ linux_msg(td, "%s", sbuf_data(sb));
+ sbuf_delete(sb);
+ }
+
+ free(mib, M_TEMP);
+ return (ENOTDIR);
+}
diff --git a/sys/compat/linux/linux_sysproto.h b/sys/compat/linux/linux_sysproto.h
new file mode 100644
index 0000000..69f1577
--- /dev/null
+++ b/sys/compat/linux/linux_sysproto.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Travis Poppe
+ * 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
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef LINUX_SYSPROTO
+#define LINUX_SYSPROTO
+
+int linux_nosys(struct thread *, struct nosys_args *);
+
+#endif
diff --git a/sys/compat/linux/linux_time.c b/sys/compat/linux/linux_time.c
new file mode 100644
index 0000000..8800d67
--- /dev/null
+++ b/sys/compat/linux/linux_time.c
@@ -0,0 +1,239 @@
+/* $NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Emmanuel Dreyfus.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+#if 0
+__KERNEL_RCSID(0, "$NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $");
+#endif
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/signal.h>
+#include <sys/stdint.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+static void native_to_linux_timespec(struct l_timespec *,
+ struct timespec *);
+static int linux_to_native_timespec(struct timespec *,
+ struct l_timespec *);
+static int linux_to_native_clockid(clockid_t *, clockid_t);
+
+static void
+native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp)
+{
+ ltp->tv_sec = ntp->tv_sec;
+ ltp->tv_nsec = ntp->tv_nsec;
+}
+
+static int
+linux_to_native_timespec(struct timespec *ntp, struct l_timespec *ltp)
+{
+ if (ltp->tv_sec < 0 || ltp->tv_nsec > (l_long)999999999L)
+ return (EINVAL);
+ ntp->tv_sec = ltp->tv_sec;
+ ntp->tv_nsec = ltp->tv_nsec;
+
+ return (0);
+}
+
+static int
+linux_to_native_clockid(clockid_t *n, clockid_t l)
+{
+ switch (l) {
+ case LINUX_CLOCK_REALTIME:
+ *n = CLOCK_REALTIME;
+ break;
+ case LINUX_CLOCK_MONOTONIC:
+ *n = CLOCK_MONOTONIC;
+ break;
+ case LINUX_CLOCK_PROCESS_CPUTIME_ID:
+ case LINUX_CLOCK_THREAD_CPUTIME_ID:
+ case LINUX_CLOCK_REALTIME_HR:
+ case LINUX_CLOCK_MONOTONIC_HR:
+ default:
+ return (EINVAL);
+ break;
+ }
+
+ return (0);
+}
+
+int
+linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args)
+{
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+ struct timespec tp;
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return (error);
+ error = kern_clock_gettime(td, nwhich, &tp);
+ if (error != 0)
+ return (error);
+ native_to_linux_timespec(&lts, &tp);
+
+ return (copyout(&lts, args->tp, sizeof lts));
+}
+
+int
+linux_clock_settime(struct thread *td, struct linux_clock_settime_args *args)
+{
+ struct timespec ts;
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return (error);
+ error = copyin(args->tp, &lts, sizeof lts);
+ if (error != 0)
+ return (error);
+ error = linux_to_native_timespec(&ts, &lts);
+ if (error != 0)
+ return (error);
+
+ return (kern_clock_settime(td, nwhich, &ts));
+}
+
+int
+linux_clock_getres(struct thread *td, struct linux_clock_getres_args *args)
+{
+ struct timespec ts;
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+
+ if (args->tp == NULL)
+ return (0);
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return (error);
+ error = kern_clock_getres(td, nwhich, &ts);
+ if (error != 0)
+ return (error);
+ native_to_linux_timespec(&lts, &ts);
+
+ return (copyout(&lts, args->tp, sizeof lts));
+}
+
+int
+linux_nanosleep(struct thread *td, struct linux_nanosleep_args *args)
+{
+ struct timespec *rmtp;
+ struct l_timespec lrqts, lrmts;
+ struct timespec rqts, rmts;
+ int error;
+
+ error = copyin(args->rqtp, &lrqts, sizeof lrqts);
+ if (error != 0)
+ return (error);
+
+ if (args->rmtp != NULL)
+ rmtp = &rmts;
+ else
+ rmtp = NULL;
+
+ error = linux_to_native_timespec(&rqts, &lrqts);
+ if (error != 0)
+ return (error);
+ error = kern_nanosleep(td, &rqts, rmtp);
+ if (error != 0)
+ return (error);
+
+ if (args->rmtp != NULL) {
+ native_to_linux_timespec(&lrmts, rmtp);
+ error = copyout(&lrmts, args->rmtp, sizeof(lrmts));
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+linux_clock_nanosleep(struct thread *td, struct linux_clock_nanosleep_args *args)
+{
+ struct timespec *rmtp;
+ struct l_timespec lrqts, lrmts;
+ struct timespec rqts, rmts;
+ int error;
+
+ if (args->flags != 0)
+ return (EINVAL); /* XXX deal with TIMER_ABSTIME */
+
+ if (args->which != LINUX_CLOCK_REALTIME)
+ return (EINVAL);
+
+ error = copyin(args->rqtp, &lrqts, sizeof lrqts);
+ if (error != 0)
+ return (error);
+
+ if (args->rmtp != NULL)
+ rmtp = &rmts;
+ else
+ rmtp = NULL;
+
+ error = linux_to_native_timespec(&rqts, &lrqts);
+ if (error != 0)
+ return (error);
+ error = kern_nanosleep(td, &rqts, rmtp);
+ if (error != 0)
+ return (error);
+
+ if (args->rmtp != NULL) {
+ native_to_linux_timespec(&lrmts, rmtp);
+ error = copyout(&lrmts, args->rmtp, sizeof lrmts );
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
diff --git a/sys/compat/linux/linux_uid16.c b/sys/compat/linux/linux_uid16.c
new file mode 100644
index 0000000..6ad674b
--- /dev/null
+++ b/sys/compat/linux/linux_uid16.c
@@ -0,0 +1,306 @@
+/*-
+ * Copyright (c) 2001 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/systm.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+#include <compat/linux/linux_util.h>
+
+DUMMY(setfsuid16);
+DUMMY(setfsgid16);
+DUMMY(getresuid16);
+DUMMY(getresgid16);
+
+#define CAST_NOCHG(x) ((x == 0xFFFF) ? -1 : x)
+
+int
+linux_chown16(struct thread *td, struct linux_chown16_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(chown16))
+ printf(ARGS(chown16, "%s, %d, %d"), path, args->uid, args->gid);
+#endif
+ error = kern_chown(td, path, UIO_SYSSPACE, CAST_NOCHG(args->uid),
+ CAST_NOCHG(args->gid));
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_lchown16(struct thread *td, struct linux_lchown16_args *args)
+{
+ char *path;
+ int error;
+
+ LCONVPATHEXIST(td, args->path, &path);
+
+#ifdef DEBUG
+ if (ldebug(lchown16))
+ printf(ARGS(lchown16, "%s, %d, %d"), path, args->uid,
+ args->gid);
+#endif
+ error = kern_lchown(td, path, UIO_SYSSPACE, CAST_NOCHG(args->uid),
+ CAST_NOCHG(args->gid));
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
+{
+ struct ucred *newcred, *oldcred;
+ l_gid16_t *linux_gidset;
+ gid_t *bsd_gidset;
+ int ngrp, error;
+ struct proc *p;
+
+#ifdef DEBUG
+ if (ldebug(setgroups16))
+ printf(ARGS(setgroups16, "%d, *"), args->gidsetsize);
+#endif
+
+ ngrp = args->gidsetsize;
+ if (ngrp < 0 || ngrp >= ngroups_max + 1)
+ return (EINVAL);
+ linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_TEMP, M_WAITOK);
+ error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t));
+ if (error)
+ return (error);
+ newcred = crget();
+ p = td->td_proc;
+ PROC_LOCK(p);
+ oldcred = crcopysafe(p, newcred);
+
+ /*
+ * cr_groups[0] holds egid. Setting the whole set from
+ * the supplied set will cause egid to be changed too.
+ * Keep cr_groups[0] unchanged to prevent that.
+ */
+
+ if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS, 0)) != 0) {
+ PROC_UNLOCK(p);
+ crfree(newcred);
+ goto out;
+ }
+
+ if (ngrp > 0) {
+ newcred->cr_ngroups = ngrp + 1;
+
+ bsd_gidset = newcred->cr_groups;
+ ngrp--;
+ while (ngrp >= 0) {
+ bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
+ ngrp--;
+ }
+ }
+ else
+ newcred->cr_ngroups = 1;
+
+ setsugid(td->td_proc);
+ p->p_ucred = newcred;
+ PROC_UNLOCK(p);
+ crfree(oldcred);
+ error = 0;
+out:
+ free(linux_gidset, M_TEMP);
+ return (error);
+}
+
+int
+linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args)
+{
+ struct ucred *cred;
+ l_gid16_t *linux_gidset;
+ gid_t *bsd_gidset;
+ int bsd_gidsetsz, ngrp, error;
+
+#ifdef DEBUG
+ if (ldebug(getgroups16))
+ printf(ARGS(getgroups16, "%d, *"), args->gidsetsize);
+#endif
+
+ cred = td->td_ucred;
+ bsd_gidset = cred->cr_groups;
+ bsd_gidsetsz = cred->cr_ngroups - 1;
+
+ /*
+ * cr_groups[0] holds egid. Returning the whole set
+ * here will cause a duplicate. Exclude cr_groups[0]
+ * to prevent that.
+ */
+
+ if ((ngrp = args->gidsetsize) == 0) {
+ td->td_retval[0] = bsd_gidsetsz;
+ return (0);
+ }
+
+ if (ngrp < bsd_gidsetsz)
+ return (EINVAL);
+
+ ngrp = 0;
+ linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
+ M_TEMP, M_WAITOK);
+ while (ngrp < bsd_gidsetsz) {
+ linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
+ ngrp++;
+ }
+
+ error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t));
+ free(linux_gidset, M_TEMP);
+ if (error)
+ return (error);
+
+ td->td_retval[0] = ngrp;
+ return (0);
+}
+
+/*
+ * The FreeBSD native getgid(2) and getuid(2) also modify td->td_retval[1]
+ * when COMPAT_43 is defined. This clobbers registers that are assumed to
+ * be preserved. The following lightweight syscalls fixes this. See also
+ * linux_getpid(2), linux_getgid(2) and linux_getuid(2) in linux_misc.c
+ *
+ * linux_getgid16() - MP SAFE
+ * linux_getuid16() - MP SAFE
+ */
+
+int
+linux_getgid16(struct thread *td, struct linux_getgid16_args *args)
+{
+
+ td->td_retval[0] = td->td_ucred->cr_rgid;
+ return (0);
+}
+
+int
+linux_getuid16(struct thread *td, struct linux_getuid16_args *args)
+{
+
+ td->td_retval[0] = td->td_ucred->cr_ruid;
+ return (0);
+}
+
+int
+linux_getegid16(struct thread *td, struct linux_getegid16_args *args)
+{
+ struct getegid_args bsd;
+
+ return (getegid(td, &bsd));
+}
+
+int
+linux_geteuid16(struct thread *td, struct linux_geteuid16_args *args)
+{
+ struct geteuid_args bsd;
+
+ return (geteuid(td, &bsd));
+}
+
+int
+linux_setgid16(struct thread *td, struct linux_setgid16_args *args)
+{
+ struct setgid_args bsd;
+
+ bsd.gid = args->gid;
+ return (setgid(td, &bsd));
+}
+
+int
+linux_setuid16(struct thread *td, struct linux_setuid16_args *args)
+{
+ struct setuid_args bsd;
+
+ bsd.uid = args->uid;
+ return (setuid(td, &bsd));
+}
+
+int
+linux_setregid16(struct thread *td, struct linux_setregid16_args *args)
+{
+ struct setregid_args bsd;
+
+ bsd.rgid = CAST_NOCHG(args->rgid);
+ bsd.egid = CAST_NOCHG(args->egid);
+ return (setregid(td, &bsd));
+}
+
+int
+linux_setreuid16(struct thread *td, struct linux_setreuid16_args *args)
+{
+ struct setreuid_args bsd;
+
+ bsd.ruid = CAST_NOCHG(args->ruid);
+ bsd.euid = CAST_NOCHG(args->euid);
+ return (setreuid(td, &bsd));
+}
+
+int
+linux_setresgid16(struct thread *td, struct linux_setresgid16_args *args)
+{
+ struct setresgid_args bsd;
+
+ bsd.rgid = CAST_NOCHG(args->rgid);
+ bsd.egid = CAST_NOCHG(args->egid);
+ bsd.sgid = CAST_NOCHG(args->sgid);
+ return (setresgid(td, &bsd));
+}
+
+int
+linux_setresuid16(struct thread *td, struct linux_setresuid16_args *args)
+{
+ struct setresuid_args bsd;
+
+ bsd.ruid = CAST_NOCHG(args->ruid);
+ bsd.euid = CAST_NOCHG(args->euid);
+ bsd.suid = CAST_NOCHG(args->suid);
+ return (setresuid(td, &bsd));
+}
diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c
new file mode 100644
index 0000000..3412c37
--- /dev/null
+++ b/sys/compat/linux/linux_util.c
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 1994 Christos Zoulas
+ * Copyright (c) 1995 Frank van der Linden
+ * Copyright (c) 1995 Scott Bartram
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: svr4_util.c,v 1.5 1995/01/22 23:44:50 christos Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/linker_set.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/syscallsubr.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+
+#include <machine/stdarg.h>
+
+#include <compat/linux/linux_util.h>
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#else
+#include <machine/../linux/linux.h>
+#endif
+
+const char linux_emul_path[] = "/compat/linux";
+
+/*
+ * Search an alternate path before passing pathname arguments on to
+ * system calls. Useful for keeping a separate 'emulation tree'.
+ *
+ * If cflag is set, we check if an attempt can be made to create the
+ * named file, i.e. we check if the directory it should be in exists.
+ */
+int
+linux_emul_convpath(td, path, pathseg, pbuf, cflag, dfd)
+ struct thread *td;
+ const char *path;
+ enum uio_seg pathseg;
+ char **pbuf;
+ int cflag;
+ int dfd;
+{
+
+ return (kern_alternate_path(td, linux_emul_path, path, pathseg, pbuf,
+ cflag, dfd));
+}
+
+void
+linux_msg(const struct thread *td, const char *fmt, ...)
+{
+ va_list ap;
+ struct proc *p;
+
+ p = td->td_proc;
+ printf("linux: pid %d (%s): ", (int)p->p_pid, p->p_comm);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+struct device_element
+{
+ TAILQ_ENTRY(device_element) list;
+ struct linux_device_handler entry;
+};
+
+static TAILQ_HEAD(, device_element) devices =
+ TAILQ_HEAD_INITIALIZER(devices);
+
+static struct linux_device_handler null_handler =
+ { "mem", "mem", "null", "null", 1, 3, 1};
+
+DATA_SET(linux_device_handler_set, null_handler);
+
+char *
+linux_driver_get_name_dev(device_t dev)
+{
+ struct device_element *de;
+ const char *device_name = device_get_name(dev);
+
+ if (device_name == NULL)
+ return NULL;
+ TAILQ_FOREACH(de, &devices, list) {
+ if (strcmp(device_name, de->entry.bsd_driver_name) == 0)
+ return (de->entry.linux_driver_name);
+ }
+
+ return NULL;
+}
+
+int
+linux_driver_get_major_minor(char *node, int *major, int *minor)
+{
+ struct device_element *de;
+
+ if (node == NULL || major == NULL || minor == NULL)
+ return 1;
+
+ if (strlen(node) > strlen("pts/") &&
+ strncmp(node, "pts/", strlen("pts/")) == 0) {
+ unsigned long devno;
+
+ /*
+ * Linux checks major and minors of the slave device
+ * to make sure it's a pty device, so let's make him
+ * believe it is.
+ */
+ devno = strtoul(node + strlen("pts/"), NULL, 10);
+ *major = 136 + (devno / 256);
+ *minor = devno % 256;
+ return 0;
+ }
+
+ TAILQ_FOREACH(de, &devices, list) {
+ if (strcmp(node, de->entry.bsd_device_name) == 0) {
+ *major = de->entry.linux_major;
+ *minor = de->entry.linux_minor;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+char *
+linux_get_char_devices()
+{
+ struct device_element *de;
+ char *temp, *string, *last;
+ char formated[256];
+ int current_size = 0, string_size = 1024;
+
+ string = malloc(string_size, M_LINUX, M_WAITOK);
+ string[0] = '\000';
+ last = "";
+ TAILQ_FOREACH(de, &devices, list) {
+ if (!de->entry.linux_char_device)
+ continue;
+ temp = string;
+ if (strcmp(last, de->entry.bsd_driver_name) != 0) {
+ last = de->entry.bsd_driver_name;
+
+ snprintf(formated, sizeof(formated), "%3d %s\n",
+ de->entry.linux_major,
+ de->entry.linux_device_name);
+ if (strlen(formated) + current_size
+ >= string_size) {
+ string_size *= 2;
+ string = malloc(string_size,
+ M_LINUX, M_WAITOK);
+ bcopy(temp, string, current_size);
+ free(temp, M_LINUX);
+ }
+ strcat(string, formated);
+ current_size = strlen(string);
+ }
+ }
+
+ return string;
+}
+
+void
+linux_free_get_char_devices(char *string)
+{
+ free(string, M_LINUX);
+}
+
+static int linux_major_starting = 200;
+
+int
+linux_device_register_handler(struct linux_device_handler *d)
+{
+ struct device_element *de;
+
+ if (d == NULL)
+ return (EINVAL);
+
+ de = malloc(sizeof(*de),
+ M_LINUX, M_WAITOK);
+ if (d->linux_major < 0) {
+ d->linux_major = linux_major_starting++;
+ }
+ bcopy(d, &de->entry, sizeof(*d));
+
+ /* Add the element to the list, sorted on span. */
+ TAILQ_INSERT_TAIL(&devices, de, list);
+
+ return (0);
+}
+
+int
+linux_device_unregister_handler(struct linux_device_handler *d)
+{
+ struct device_element *de;
+
+ if (d == NULL)
+ return (EINVAL);
+
+ TAILQ_FOREACH(de, &devices, list) {
+ if (bcmp(d, &de->entry, sizeof(*d)) == 0) {
+ TAILQ_REMOVE(&devices, de, list);
+ free(de, M_LINUX);
+ return (0);
+ }
+ }
+
+ return (EINVAL);
+}
diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h
new file mode 100644
index 0000000..13cd359
--- /dev/null
+++ b/sys/compat/linux/linux_util.h
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 1994 Christos Zoulas
+ * Copyright (c) 1995 Frank van der Linden
+ * Copyright (c) 1995 Scott Bartram
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * from: svr4_util.h,v 1.5 1994/11/18 02:54:31 christos Exp
+ * from: linux_util.h,v 1.2 1995/03/05 23:23:50 fvdl Exp
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_UTIL_H_
+#define _LINUX_UTIL_H_
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <machine/vmparam.h>
+#include <sys/exec.h>
+#include <sys/sysent.h>
+#include <sys/syslog.h>
+#include <sys/cdefs.h>
+#include <sys/uio.h>
+
+extern const char linux_emul_path[];
+
+int linux_emul_convpath(struct thread *, const char *, enum uio_seg, char **, int, int);
+
+#define LCONVPATH_AT(td, upath, pathp, i, dfd) \
+ do { \
+ int _error; \
+ \
+ _error = linux_emul_convpath(td, upath, UIO_USERSPACE, \
+ pathp, i, dfd); \
+ if (*(pathp) == NULL) \
+ return (_error); \
+ } while (0)
+
+#define LCONVPATH(td, upath, pathp, i) \
+ LCONVPATH_AT(td, upath, pathp, i, AT_FDCWD)
+
+#define LCONVPATHEXIST(td, upath, pathp) LCONVPATH(td, upath, pathp, 0)
+#define LCONVPATHEXIST_AT(td, upath, pathp, dfd) LCONVPATH_AT(td, upath, pathp, 0, dfd)
+#define LCONVPATHCREAT(td, upath, pathp) LCONVPATH(td, upath, pathp, 1)
+#define LCONVPATHCREAT_AT(td, upath, pathp, dfd) LCONVPATH_AT(td, upath, pathp, 1, dfd)
+#define LFREEPATH(path) free(path, M_TEMP)
+
+#define DUMMY(s) \
+int \
+linux_ ## s(struct thread *td, struct linux_ ## s ## _args *args) \
+{ \
+ static pid_t pid; \
+ \
+ if (pid != td->td_proc->p_pid) { \
+ linux_msg(td, "syscall %s not implemented", #s); \
+ pid = td->td_proc->p_pid; \
+ }; \
+ return (ENOSYS); \
+} \
+struct __hack
+
+void linux_msg(const struct thread *td, const char *fmt, ...)
+ __printflike(2, 3);
+
+struct linux_device_handler {
+ char *bsd_driver_name;
+ char *linux_driver_name;
+ char *bsd_device_name;
+ char *linux_device_name;
+ int linux_major;
+ int linux_minor;
+ int linux_char_device;
+};
+
+int linux_device_register_handler(struct linux_device_handler *h);
+int linux_device_unregister_handler(struct linux_device_handler *h);
+char *linux_driver_get_name_dev(device_t dev);
+int linux_driver_get_major_minor(char *node, int *major, int *minor);
+char *linux_get_char_devices(void);
+void linux_free_get_char_devices(char *string);
+
+#if defined(KTR)
+
+#define KTR_LINUX KTR_SUBSYS
+#define LINUX_CTRFMT(nm, fmt) #nm"("fmt")"
+
+#define LINUX_CTR6(f, m, p1, p2, p3, p4, p5, p6) do { \
+ if (ldebug(f)) \
+ CTR6(KTR_LINUX, LINUX_CTRFMT(f, m), \
+ p1, p2, p3, p4, p5, p6); \
+} while (0)
+
+#define LINUX_CTR(f) LINUX_CTR6(f, "", 0, 0, 0, 0, 0, 0)
+#define LINUX_CTR0(f, m) LINUX_CTR6(f, m, 0, 0, 0, 0, 0, 0)
+#define LINUX_CTR1(f, m, p1) LINUX_CTR6(f, m, p1, 0, 0, 0, 0, 0)
+#define LINUX_CTR2(f, m, p1, p2) LINUX_CTR6(f, m, p1, p2, 0, 0, 0, 0)
+#define LINUX_CTR3(f, m, p1, p2, p3) LINUX_CTR6(f, m, p1, p2, p3, 0, 0, 0)
+#define LINUX_CTR4(f, m, p1, p2, p3, p4) LINUX_CTR6(f, m, p1, p2, p3, p4, 0, 0)
+#define LINUX_CTR5(f, m, p1, p2, p3, p4, p5) LINUX_CTR6(f, m, p1, p2, p3, p4, p5, 0)
+#else
+#define LINUX_CTR(f)
+#define LINUX_CTR0(f, m)
+#define LINUX_CTR1(f, m, p1)
+#define LINUX_CTR2(f, m, p1, p2)
+#define LINUX_CTR3(f, m, p1, p2, p3)
+#define LINUX_CTR4(f, m, p1, p2, p3, p4)
+#define LINUX_CTR5(f, m, p1, p2, p3, p4, p5)
+#define LINUX_CTR6(f, m, p1, p2, p3, p4, p5, p6)
+#endif
+
+#endif /* !_LINUX_UTIL_H_ */
diff --git a/sys/compat/linux/linux_videodev.h b/sys/compat/linux/linux_videodev.h
new file mode 100644
index 0000000..f6606ae
--- /dev/null
+++ b/sys/compat/linux/linux_videodev.h
@@ -0,0 +1,367 @@
+/*
+ * This header comes from linux, but it has no license. The author
+ * (Alan Cox @ Redhat) gave explicit permissions to use it in FreeBSD.
+ * The freeBSD vendor branch for v4l gives a more detailed description
+ * about this.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __LINUX_VIDEODEV_H
+#define __LINUX_VIDEODEV_H
+
+#include <sys/types.h>
+typedef int32_t __s32;
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+
+#if 0
+#define HAVE_V4L1 1
+
+#include <linux/videodev2.h>
+#endif
+
+#define VID_TYPE_CAPTURE 1 /* Can capture */
+#define VID_TYPE_TUNER 2 /* Can tune */
+#define VID_TYPE_TELETEXT 4 /* Does teletext */
+#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
+#define VID_TYPE_CLIPPING 32 /* Can clip */
+#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
+#define VID_TYPE_SCALES 128 /* Scalable */
+#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
+#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */
+
+struct video_capability
+{
+ char name[32];
+ int type;
+ int channels; /* Num channels */
+ int audios; /* Num audio devices */
+ int maxwidth; /* Supported width */
+ int maxheight; /* And height */
+ int minwidth; /* Supported width */
+ int minheight; /* And height */
+};
+
+
+struct video_channel
+{
+ int channel;
+ char name[32];
+ int tuners;
+ __u32 flags;
+#define VIDEO_VC_TUNER 1 /* Channel has a tuner */
+#define VIDEO_VC_AUDIO 2 /* Channel has audio */
+ __u16 type;
+#define VIDEO_TYPE_TV 1
+#define VIDEO_TYPE_CAMERA 2
+ __u16 norm; /* Norm set by channel */
+};
+
+struct video_tuner
+{
+ int tuner;
+ char name[32];
+ unsigned long rangelow, rangehigh; /* Tuner range */
+ __u32 flags;
+#define VIDEO_TUNER_PAL 1
+#define VIDEO_TUNER_NTSC 2
+#define VIDEO_TUNER_SECAM 4
+#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */
+#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */
+#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */
+#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */
+#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */
+ __u16 mode; /* PAL/NTSC/SECAM/OTHER */
+#define VIDEO_MODE_PAL 0
+#define VIDEO_MODE_NTSC 1
+#define VIDEO_MODE_SECAM 2
+#define VIDEO_MODE_AUTO 3
+ __u16 signal; /* Signal strength 16bit scale */
+};
+
+struct video_picture
+{
+ __u16 brightness;
+ __u16 hue;
+ __u16 colour;
+ __u16 contrast;
+ __u16 whiteness; /* Black and white only */
+ __u16 depth; /* Capture depth */
+ __u16 palette; /* Palette in use */
+#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */
+#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */
+#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */
+#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */
+#define VIDEO_PALETTE_YUYV 8
+#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420 10
+#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */
+#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */
+};
+
+struct video_audio
+{
+ int audio; /* Audio channel */
+ __u16 volume; /* If settable */
+ __u16 bass, treble;
+ __u32 flags;
+#define VIDEO_AUDIO_MUTE 1
+#define VIDEO_AUDIO_MUTABLE 2
+#define VIDEO_AUDIO_VOLUME 4
+#define VIDEO_AUDIO_BASS 8
+#define VIDEO_AUDIO_TREBLE 16
+#define VIDEO_AUDIO_BALANCE 32
+ char name[16];
+#define VIDEO_SOUND_MONO 1
+#define VIDEO_SOUND_STEREO 2
+#define VIDEO_SOUND_LANG1 4
+#define VIDEO_SOUND_LANG2 8
+ __u16 mode;
+ __u16 balance; /* Stereo balance */
+ __u16 step; /* Step actual volume uses */
+};
+
+struct video_clip
+{
+ __s32 x,y;
+ __s32 width, height;
+ struct video_clip *next; /* For user use/driver use only */
+};
+
+struct video_window
+{
+ __u32 x,y; /* Position of window */
+ __u32 width,height; /* Its size */
+ __u32 chromakey;
+ __u32 flags;
+ struct video_clip *clips; /* Set only */
+ int clipcount;
+#define VIDEO_WINDOW_INTERLACE 1
+#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */
+#define VIDEO_CLIP_BITMAP -1
+/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
+#define VIDEO_CLIPMAP_SIZE (128 * 625)
+};
+
+struct video_capture
+{
+ __u32 x,y; /* Offsets into image */
+ __u32 width, height; /* Area to capture */
+ __u16 decimation; /* Decimation divider */
+ __u16 flags; /* Flags for capture */
+#define VIDEO_CAPTURE_ODD 0 /* Temporal */
+#define VIDEO_CAPTURE_EVEN 1
+};
+
+struct video_buffer
+{
+ void *base;
+ int height,width;
+ int depth;
+ int bytesperline;
+};
+
+struct video_mmap
+{
+ unsigned int frame; /* Frame (0 - n) for double buffer */
+ int height,width;
+ unsigned int format; /* should be VIDEO_PALETTE_* */
+};
+
+struct video_key
+{
+ __u8 key[8];
+ __u32 flags;
+};
+
+#define VIDEO_MAX_FRAME 32
+
+struct video_mbuf
+{
+ int size; /* Total memory to map */
+ int frames; /* Frames */
+ int offsets[VIDEO_MAX_FRAME];
+};
+
+#define VIDEO_NO_UNIT (-1)
+
+struct video_unit
+{
+ int video; /* Video minor */
+ int vbi; /* VBI minor */
+ int radio; /* Radio minor */
+ int audio; /* Audio minor */
+ int teletext; /* Teletext minor */
+};
+
+struct vbi_format {
+ __u32 sampling_rate; /* in Hz */
+ __u32 samples_per_line;
+ __u32 sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */
+ __s32 start[2]; /* starting line for each frame */
+ __u32 count[2]; /* count of lines for each frame */
+ __u32 flags;
+#define VBI_UNSYNC 1 /* can distingues between top/bottom field */
+#define VBI_INTERLACED 2 /* lines are interlaced */
+};
+
+/* video_info is biased towards hardware mpeg encode/decode */
+/* but it could apply generically to any hardware compressor/decompressor */
+struct video_info
+{
+ __u32 frame_count; /* frames output since decode/encode began */
+ __u32 h_size; /* current unscaled horizontal size */
+ __u32 v_size; /* current unscaled veritcal size */
+ __u32 smpte_timecode; /* current SMPTE timecode (for current GOP) */
+ __u32 picture_type; /* current picture type */
+ __u32 temporal_reference; /* current temporal reference */
+ __u8 user_data[256]; /* user data last found in compressed stream */
+ /* user_data[0] contains user data flags, user_data[1] has count */
+};
+
+/* generic structure for setting playback modes */
+struct video_play_mode
+{
+ int mode;
+ int p1;
+ int p2;
+};
+
+/* for loading microcode / fpga programming */
+struct video_code
+{
+ char loadwhat[16]; /* name or tag of file being passed */
+ int datasize;
+ __u8 *data;
+};
+
+#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */
+#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */
+#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */
+#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */
+#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */
+#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */
+#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */
+#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */
+#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */
+#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */
+#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */
+#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
+#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */
+#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */
+#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */
+#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */
+#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */
+#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */
+#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */
+#define VIDIOCGUNIT _IOR('v',21, struct video_unit) /* Get attached units */
+#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get subcapture */
+#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set subcapture */
+#define VIDIOCSPLAYMODE _IOW('v',24, struct video_play_mode) /* Set output video mode/feature */
+#define VIDIOCSWRITEMODE _IOW('v',25, int) /* Set write mode */
+#define VIDIOCGPLAYINFO _IOR('v',26, struct video_info) /* Get current playback info from hardware */
+#define VIDIOCSMICROCODE _IOW('v',27, struct video_code) /* Load microcode into hardware */
+#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */
+#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */
+
+
+#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */
+
+/* VIDIOCSWRITEMODE */
+#define VID_WRITE_MPEG_AUD 0
+#define VID_WRITE_MPEG_VID 1
+#define VID_WRITE_OSD 2
+#define VID_WRITE_TTX 3
+#define VID_WRITE_CC 4
+#define VID_WRITE_MJPEG 5
+
+/* VIDIOCSPLAYMODE */
+#define VID_PLAY_VID_OUT_MODE 0
+ /* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
+#define VID_PLAY_GENLOCK 1
+ /* p1: 0 = OFF, 1 = ON */
+ /* p2: GENLOCK FINE DELAY value */
+#define VID_PLAY_NORMAL 2
+#define VID_PLAY_PAUSE 3
+#define VID_PLAY_SINGLE_FRAME 4
+#define VID_PLAY_FAST_FORWARD 5
+#define VID_PLAY_SLOW_MOTION 6
+#define VID_PLAY_IMMEDIATE_NORMAL 7
+#define VID_PLAY_SWITCH_CHANNELS 8
+#define VID_PLAY_FREEZE_FRAME 9
+#define VID_PLAY_STILL_MODE 10
+#define VID_PLAY_MASTER_MODE 11
+ /* p1: see below */
+#define VID_PLAY_MASTER_NONE 1
+#define VID_PLAY_MASTER_VIDEO 2
+#define VID_PLAY_MASTER_AUDIO 3
+#define VID_PLAY_ACTIVE_SCANLINES 12
+ /* p1 = first active; p2 = last active */
+#define VID_PLAY_RESET 13
+#define VID_PLAY_END_MARK 14
+
+
+
+#define VID_HARDWARE_BT848 1
+#define VID_HARDWARE_QCAM_BW 2
+#define VID_HARDWARE_PMS 3
+#define VID_HARDWARE_QCAM_C 4
+#define VID_HARDWARE_PSEUDO 5
+#define VID_HARDWARE_SAA5249 6
+#define VID_HARDWARE_AZTECH 7
+#define VID_HARDWARE_SF16MI 8
+#define VID_HARDWARE_RTRACK 9
+#define VID_HARDWARE_ZOLTRIX 10
+#define VID_HARDWARE_SAA7146 11
+#define VID_HARDWARE_VIDEUM 12 /* Reserved for Winnov videum */
+#define VID_HARDWARE_RTRACK2 13
+#define VID_HARDWARE_PERMEDIA2 14 /* Reserved for Permedia2 */
+#define VID_HARDWARE_RIVA128 15 /* Reserved for RIVA 128 */
+#define VID_HARDWARE_PLANB 16 /* PowerMac motherboard video-in */
+#define VID_HARDWARE_BROADWAY 17 /* Broadway project */
+#define VID_HARDWARE_GEMTEK 18
+#define VID_HARDWARE_TYPHOON 19
+#define VID_HARDWARE_VINO 20 /* SGI Indy Vino */
+#define VID_HARDWARE_CADET 21 /* Cadet radio */
+#define VID_HARDWARE_TRUST 22 /* Trust FM Radio */
+#define VID_HARDWARE_TERRATEC 23 /* TerraTec ActiveRadio */
+#define VID_HARDWARE_CPIA 24
+#define VID_HARDWARE_ZR36120 25 /* Zoran ZR36120/ZR36125 */
+#define VID_HARDWARE_ZR36067 26 /* Zoran ZR36067/36060 */
+#define VID_HARDWARE_OV511 27
+#define VID_HARDWARE_ZR356700 28 /* Zoran 36700 series */
+#define VID_HARDWARE_W9966 29
+#define VID_HARDWARE_SE401 30 /* SE401 USB webcams */
+#define VID_HARDWARE_PWC 31 /* Philips webcams */
+#define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */
+#define VID_HARDWARE_CPIA2 33
+#define VID_HARDWARE_VICAM 34
+#define VID_HARDWARE_SF16FMR2 35
+#define VID_HARDWARE_W9968CF 36
+#define VID_HARDWARE_SAA7114H 37
+#define VID_HARDWARE_SN9C102 38
+#define VID_HARDWARE_ARV 39
+#endif /* __LINUX_VIDEODEV_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/sys/compat/linux/linux_videodev_compat.h b/sys/compat/linux/linux_videodev_compat.h
new file mode 100644
index 0000000..98034bc
--- /dev/null
+++ b/sys/compat/linux/linux_videodev_compat.h
@@ -0,0 +1,59 @@
+/*
+ * $FreeBSD$
+ */
+
+/*
+ * This file defines compatibility versions of several video structures
+ * defined in the Linux videodev.h header (linux_videodev.h). The
+ * structures defined in this file are the ones that have been determined
+ * to have 32- to 64-bit size dependencies.
+ */
+
+#ifndef _LINUX_VIDEODEV_COMPAT_H_
+#define _LINUX_VIDEODEV_COMPAT_H_
+
+struct l_video_tuner
+{
+ l_int tuner;
+#define LINUX_VIDEO_TUNER_NAME_SIZE 32
+ char name[LINUX_VIDEO_TUNER_NAME_SIZE];
+ l_ulong rangelow, rangehigh;
+ uint32_t flags;
+ uint16_t mode;
+ uint16_t signal;
+};
+
+struct l_video_clip
+{
+ int32_t x, y;
+ int32_t width, height;
+ l_uintptr_t next;
+};
+
+struct l_video_window
+{
+ uint32_t x, y;
+ uint32_t width, height;
+ uint32_t chromakey;
+ uint32_t flags;
+ l_uintptr_t clips;
+ l_int clipcount;
+};
+
+struct l_video_buffer
+{
+ l_uintptr_t base;
+ l_int height, width;
+ l_int depth;
+ l_int bytesperline;
+};
+
+struct l_video_code
+{
+#define LINUX_VIDEO_CODE_LOADWHAT_SIZE 16
+ char loadwhat[LINUX_VIDEO_CODE_LOADWHAT_SIZE];
+ l_int datasize;
+ l_uintptr_t data;
+};
+
+#endif /* !_LINUX_VIDEODEV_COMPAT_H_ */
OpenPOWER on IntegriCloud