diff options
Diffstat (limited to 'sys/compat/linux')
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)§orsize, 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)§orsize, 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(§orsize, (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(<h, (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, <e, 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, <e.cdte_addr); + error = copyout(<e, (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, <v, 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(<v, 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, <v, 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(<s, &tp); + + return (copyout(<s, 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, <s, sizeof lts); + if (error != 0) + return (error); + error = linux_to_native_timespec(&ts, <s); + 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(<s, &ts); + + return (copyout(<s, 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_ */ |