Index: head/sys/amd64/linux32/linux32_sysvec.c =================================================================== --- head/sys/amd64/linux32/linux32_sysvec.c (revision 156873) +++ head/sys/amd64/linux32/linux32_sysvec.c (revision 156874) @@ -1,1097 +1,1098 @@ /*- * Copyright (c) 2004 Tim J. Robbins * Copyright (c) 2003 Peter Wemm * Copyright (c) 2002 Doug Rabson * Copyright (c) 1998-1999 Andrew Gallatin * Copyright (c) 1994-1996 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #ifndef COMPAT_IA32 #error "Unable to compile Linux-emulator due to missing COMPAT_IA32 option!" #endif #define __ELF_WORD_SIZE 32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_VERSION(linux, 1); MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); #define AUXARGS_ENTRY_32(pos, id, val) \ do { \ suword32(pos++, id); \ suword32(pos++, val); \ } while (0) #if BYTE_ORDER == LITTLE_ENDIAN #define SHELLMAGIC 0x2123 /* #! */ #else #define SHELLMAGIC 0x2321 #endif /* * Allow the sendsig functions to use the ldebug() facility * even though they are not syscalls themselves. Map them * to syscall 0. This is slightly less bogus than using * ldebug(sigreturn). */ #define LINUX_SYS_linux_rt_sendsig 0 #define LINUX_SYS_linux_sendsig 0 extern char linux_sigcode[]; extern int linux_szsigcode; extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL]; SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); static int elf_linux_fixup(register_t **stack_base, struct image_params *iparams); static register_t *linux_copyout_strings(struct image_params *imgp); static void linux_prepsyscall(struct trapframe *tf, int *args, u_int *code, caddr_t *params); static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask); static void exec_linux_setregs(struct thread *td, u_long entry, u_long stack, u_long ps_strings); static void linux32_fixlimits(struct proc *p); /* * Linux syscalls return negative errno's, we do positive and map them */ static int bsd_to_linux_errno[ELAST + 1] = { -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, -6, -6, -43, -42, -75, -6, -84 }; int bsd_to_linux_signal[LINUX_SIGTBLSZ] = { LINUX_SIGHUP, LINUX_SIGINT, LINUX_SIGQUIT, LINUX_SIGILL, LINUX_SIGTRAP, LINUX_SIGABRT, 0, LINUX_SIGFPE, LINUX_SIGKILL, LINUX_SIGBUS, LINUX_SIGSEGV, LINUX_SIGSYS, LINUX_SIGPIPE, LINUX_SIGALRM, LINUX_SIGTERM, LINUX_SIGURG, LINUX_SIGSTOP, LINUX_SIGTSTP, LINUX_SIGCONT, LINUX_SIGCHLD, LINUX_SIGTTIN, LINUX_SIGTTOU, LINUX_SIGIO, LINUX_SIGXCPU, LINUX_SIGXFSZ, LINUX_SIGVTALRM, LINUX_SIGPROF, LINUX_SIGWINCH, 0, LINUX_SIGUSR1, LINUX_SIGUSR2 }; int linux_to_bsd_signal[LINUX_SIGTBLSZ] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGBUS, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGURG, SIGSYS }; #define LINUX_T_UNKNOWN 255 static int _bsd_to_linux_trapcode[] = { LINUX_T_UNKNOWN, /* 0 */ 6, /* 1 T_PRIVINFLT */ LINUX_T_UNKNOWN, /* 2 */ 3, /* 3 T_BPTFLT */ LINUX_T_UNKNOWN, /* 4 */ LINUX_T_UNKNOWN, /* 5 */ 16, /* 6 T_ARITHTRAP */ 254, /* 7 T_ASTFLT */ LINUX_T_UNKNOWN, /* 8 */ 13, /* 9 T_PROTFLT */ 1, /* 10 T_TRCTRAP */ LINUX_T_UNKNOWN, /* 11 */ 14, /* 12 T_PAGEFLT */ LINUX_T_UNKNOWN, /* 13 */ 17, /* 14 T_ALIGNFLT */ LINUX_T_UNKNOWN, /* 15 */ LINUX_T_UNKNOWN, /* 16 */ LINUX_T_UNKNOWN, /* 17 */ 0, /* 18 T_DIVIDE */ 2, /* 19 T_NMI */ 4, /* 20 T_OFLOW */ 5, /* 21 T_BOUND */ 7, /* 22 T_DNA */ 8, /* 23 T_DOUBLEFLT */ 9, /* 24 T_FPOPFLT */ 10, /* 25 T_TSSFLT */ 11, /* 26 T_SEGNPFLT */ 12, /* 27 T_STKFLT */ 18, /* 28 T_MCHK */ 19, /* 29 T_XMMFLT */ 15 /* 30 T_RESERVED */ }; #define bsd_to_linux_trapcode(code) \ ((code)td_proc == imgp->proc && (curthread->td_proc->p_flag & P_SA) == 0, ("unsafe elf_linux_fixup(), should be curproc")); base = (Elf32_Addr *)*stack_base; args = (Elf32_Auxargs *)imgp->auxargs; pos = base + (imgp->args->argc + imgp->args->envc + 2); if (args->trace) AUXARGS_ENTRY_32(pos, AT_DEBUG, 1); if (args->execfd != -1) AUXARGS_ENTRY_32(pos, AT_EXECFD, args->execfd); AUXARGS_ENTRY_32(pos, AT_PHDR, args->phdr); AUXARGS_ENTRY_32(pos, AT_PHENT, args->phent); AUXARGS_ENTRY_32(pos, AT_PHNUM, args->phnum); AUXARGS_ENTRY_32(pos, AT_PAGESZ, args->pagesz); AUXARGS_ENTRY_32(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY_32(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY_32(pos, AT_BASE, args->base); AUXARGS_ENTRY_32(pos, AT_UID, imgp->proc->p_ucred->cr_ruid); AUXARGS_ENTRY_32(pos, AT_EUID, imgp->proc->p_ucred->cr_svuid); AUXARGS_ENTRY_32(pos, AT_GID, imgp->proc->p_ucred->cr_rgid); AUXARGS_ENTRY_32(pos, AT_EGID, imgp->proc->p_ucred->cr_svgid); AUXARGS_ENTRY_32(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); imgp->auxargs = NULL; base--; suword32(base, (uint32_t)imgp->args->argc); *stack_base = (register_t *)base; return 0; } extern int _ucodesel, _ucode32sel, _udatasel; extern unsigned long linux_sznonrtsigcode; static void linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct thread *td = curthread; struct proc *p = td->td_proc; struct sigacts *psp; struct trapframe *regs; struct l_rt_sigframe *fp, frame; int oonstack; int sig; int code; sig = ksi->ksi_signo; code = ksi->ksi_code; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); regs = td->td_frame; oonstack = sigonstack(regs->tf_rsp); #ifdef DEBUG if (ldebug(rt_sendsig)) printf(ARGS(rt_sendsig, "%p, %d, %p, %u"), catcher, sig, (void*)mask, code); #endif /* * Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct l_rt_sigframe *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct l_rt_sigframe)); } else fp = (struct l_rt_sigframe *)regs->tf_rsp - 1; mtx_unlock(&psp->ps_mtx); /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) if (sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; bzero(&frame, sizeof(frame)); frame.sf_handler = PTROUT(catcher); frame.sf_sig = sig; frame.sf_siginfo = PTROUT(&fp->sf_si); frame.sf_ucontext = PTROUT(&fp->sf_sc); /* Fill in POSIX parts */ frame.sf_si.lsi_signo = sig; frame.sf_si.lsi_code = code; frame.sf_si.lsi_addr = PTROUT(ksi->ksi_addr); /* * Build the signal context to be used by sigreturn. */ frame.sf_sc.uc_flags = 0; /* XXX ??? */ frame.sf_sc.uc_link = 0; /* XXX ??? */ frame.sf_sc.uc_stack.ss_sp = PTROUT(td->td_sigstk.ss_sp); frame.sf_sc.uc_stack.ss_size = td->td_sigstk.ss_size; frame.sf_sc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? LINUX_SS_ONSTACK : 0) : LINUX_SS_DISABLE; PROC_UNLOCK(p); bsd_to_linux_sigset(mask, &frame.sf_sc.uc_sigmask); frame.sf_sc.uc_mcontext.sc_mask = frame.sf_sc.uc_sigmask.__bits[0]; frame.sf_sc.uc_mcontext.sc_gs = rgs(); frame.sf_sc.uc_mcontext.sc_fs = rfs(); __asm __volatile("movl %%es,%0" : "=rm" (frame.sf_sc.uc_mcontext.sc_es)); __asm __volatile("movl %%ds,%0" : "=rm" (frame.sf_sc.uc_mcontext.sc_ds)); frame.sf_sc.uc_mcontext.sc_edi = regs->tf_rdi; frame.sf_sc.uc_mcontext.sc_esi = regs->tf_rsi; frame.sf_sc.uc_mcontext.sc_ebp = regs->tf_rbp; frame.sf_sc.uc_mcontext.sc_ebx = regs->tf_rbx; frame.sf_sc.uc_mcontext.sc_edx = regs->tf_rdx; frame.sf_sc.uc_mcontext.sc_ecx = regs->tf_rcx; frame.sf_sc.uc_mcontext.sc_eax = regs->tf_rax; frame.sf_sc.uc_mcontext.sc_eip = regs->tf_rip; frame.sf_sc.uc_mcontext.sc_cs = regs->tf_cs; frame.sf_sc.uc_mcontext.sc_eflags = regs->tf_rflags; frame.sf_sc.uc_mcontext.sc_esp_at_signal = regs->tf_rsp; frame.sf_sc.uc_mcontext.sc_ss = regs->tf_ss; frame.sf_sc.uc_mcontext.sc_err = regs->tf_err; frame.sf_sc.uc_mcontext.sc_trapno = bsd_to_linux_trapcode(code); #ifdef DEBUG if (ldebug(rt_sendsig)) printf(LMSG("rt_sendsig flags: 0x%x, sp: %p, ss: 0x%lx, mask: 0x%x"), frame.sf_sc.uc_stack.ss_flags, td->td_sigstk.ss_sp, td->td_sigstk.ss_size, frame.sf_sc.uc_mcontext.sc_mask); #endif if (copyout(&frame, fp, sizeof(frame)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG if (ldebug(rt_sendsig)) printf(LMSG("rt_sendsig: bad stack %p, oonstack=%x"), fp, oonstack); #endif PROC_LOCK(p); sigexit(td, SIGILL); } /* * Build context to run handler in. */ regs->tf_rsp = PTROUT(fp); regs->tf_rip = LINUX32_PS_STRINGS - *(p->p_sysent->sv_szsigcode) + linux_sznonrtsigcode; regs->tf_rflags &= ~PSL_T; regs->tf_cs = _ucode32sel; regs->tf_ss = _udatasel; load_ds(_udatasel); td->td_pcb->pcb_ds = _udatasel; load_es(_udatasel); td->td_pcb->pcb_es = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * in u. to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct thread *td = curthread; struct proc *p = td->td_proc; struct sigacts *psp; struct trapframe *regs; struct l_sigframe *fp, frame; l_sigset_t lmask; int oonstack, i; int sig, code; sig = ksi->ksi_signo; code = ksi->ksi_code; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ linux_rt_sendsig(catcher, ksi, mask); return; } regs = td->td_frame; oonstack = sigonstack(regs->tf_rsp); #ifdef DEBUG if (ldebug(sendsig)) printf(ARGS(sendsig, "%p, %d, %p, %u"), catcher, sig, (void*)mask, code); #endif /* * Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct l_sigframe *)(td->td_sigstk.ss_sp + td->td_sigstk.ss_size - sizeof(struct l_sigframe)); } else fp = (struct l_sigframe *)regs->tf_rsp - 1; mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); /* * Build the argument list for the signal handler. */ if (p->p_sysent->sv_sigtbl) if (sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; bzero(&frame, sizeof(frame)); frame.sf_handler = PTROUT(catcher); frame.sf_sig = sig; bsd_to_linux_sigset(mask, &lmask); /* * Build the signal context to be used by sigreturn. */ frame.sf_sc.sc_mask = lmask.__bits[0]; frame.sf_sc.sc_gs = rgs(); frame.sf_sc.sc_fs = rfs(); __asm __volatile("movl %%es,%0" : "=rm" (frame.sf_sc.sc_es)); __asm __volatile("movl %%ds,%0" : "=rm" (frame.sf_sc.sc_ds)); frame.sf_sc.sc_edi = regs->tf_rdi; frame.sf_sc.sc_esi = regs->tf_rsi; frame.sf_sc.sc_ebp = regs->tf_rbp; frame.sf_sc.sc_ebx = regs->tf_rbx; frame.sf_sc.sc_edx = regs->tf_rdx; frame.sf_sc.sc_ecx = regs->tf_rcx; frame.sf_sc.sc_eax = regs->tf_rax; frame.sf_sc.sc_eip = regs->tf_rip; frame.sf_sc.sc_cs = regs->tf_cs; frame.sf_sc.sc_eflags = regs->tf_rflags; frame.sf_sc.sc_esp_at_signal = regs->tf_rsp; frame.sf_sc.sc_ss = regs->tf_ss; frame.sf_sc.sc_err = regs->tf_err; frame.sf_sc.sc_trapno = bsd_to_linux_trapcode(code); for (i = 0; i < (LINUX_NSIG_WORDS-1); i++) frame.sf_extramask[i] = lmask.__bits[i+1]; if (copyout(&frame, fp, sizeof(frame)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); sigexit(td, SIGILL); } /* * Build context to run handler in. */ regs->tf_rsp = PTROUT(fp); regs->tf_rip = LINUX32_PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_rflags &= ~PSL_T; regs->tf_cs = _ucode32sel; regs->tf_ss = _udatasel; load_ds(_udatasel); td->td_pcb->pcb_ds = _udatasel; load_es(_udatasel); td->td_pcb->pcb_es = _udatasel; PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * psl to gain improper privileges or to cause * a machine fault. */ int linux_sigreturn(struct thread *td, struct linux_sigreturn_args *args) { struct proc *p = td->td_proc; struct l_sigframe frame; struct trapframe *regs; l_sigset_t lmask; int eflags, i; ksiginfo_t ksi; regs = td->td_frame; #ifdef DEBUG if (ldebug(sigreturn)) printf(ARGS(sigreturn, "%p"), (void *)args->sfp); #endif /* * The trampoline code hands us the sigframe. * It is unsafe to keep track of it ourselves, in the event that a * program jumps out of a signal handler. */ if (copyin(args->sfp, &frame, sizeof(frame)) != 0) return (EFAULT); /* * Check for security violations. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) eflags = frame.sf_sc.sc_eflags; /* * XXX do allow users to change the privileged flag PSL_RF. The * cpu sets PSL_RF in tf_eflags for faults. Debuggers should * sometimes set it there too. tf_eflags is kept in the signal * context during signal handling and there is no other place * to remember it, so the PSL_RF bit may be corrupted by the * signal handler without us knowing. Corruption of the PSL_RF * bit at worst causes one more or one less debugger trap, so * allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs->tf_rflags & ~PSL_RF)) return(EINVAL); /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(frame.sf_sc.sc_cs)) { ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_OBJERR; ksi.ksi_trapno = T_PROTFLT; ksi.ksi_addr = (void *)regs->tf_rip; trapsignal(td, &ksi); return(EINVAL); } lmask.__bits[0] = frame.sf_sc.sc_mask; for (i = 0; i < (LINUX_NSIG_WORDS-1); i++) lmask.__bits[i+1] = frame.sf_extramask[i]; PROC_LOCK(p); linux_to_bsd_sigset(&lmask, &td->td_sigmask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); /* * Restore signal context. */ /* Selectors were restored by the trampoline. */ regs->tf_rdi = frame.sf_sc.sc_edi; regs->tf_rsi = frame.sf_sc.sc_esi; regs->tf_rbp = frame.sf_sc.sc_ebp; regs->tf_rbx = frame.sf_sc.sc_ebx; regs->tf_rdx = frame.sf_sc.sc_edx; regs->tf_rcx = frame.sf_sc.sc_ecx; regs->tf_rax = frame.sf_sc.sc_eax; regs->tf_rip = frame.sf_sc.sc_eip; regs->tf_cs = frame.sf_sc.sc_cs; regs->tf_rflags = eflags; regs->tf_rsp = frame.sf_sc.sc_esp_at_signal; regs->tf_ss = frame.sf_sc.sc_ss; return (EJUSTRETURN); } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by rt_sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * psl to gain improper privileges or to cause * a machine fault. */ int linux_rt_sigreturn(struct thread *td, struct linux_rt_sigreturn_args *args) { struct proc *p = td->td_proc; struct l_ucontext uc; struct l_sigcontext *context; l_stack_t *lss; stack_t ss; struct trapframe *regs; int eflags; ksiginfo_t ksi; regs = td->td_frame; #ifdef DEBUG if (ldebug(rt_sigreturn)) printf(ARGS(rt_sigreturn, "%p"), (void *)args->ucp); #endif /* * The trampoline code hands us the ucontext. * It is unsafe to keep track of it ourselves, in the event that a * program jumps out of a signal handler. */ if (copyin(args->ucp, &uc, sizeof(uc)) != 0) return (EFAULT); context = &uc.uc_mcontext; /* * Check for security violations. */ #define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) eflags = context->sc_eflags; /* * XXX do allow users to change the privileged flag PSL_RF. The * cpu sets PSL_RF in tf_eflags for faults. Debuggers should * sometimes set it there too. tf_eflags is kept in the signal * context during signal handling and there is no other place * to remember it, so the PSL_RF bit may be corrupted by the * signal handler without us knowing. Corruption of the PSL_RF * bit at worst causes one more or one less debugger trap, so * allowing it is fairly harmless. */ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs->tf_rflags & ~PSL_RF)) return(EINVAL); /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) if (!CS_SECURE(context->sc_cs)) { ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_OBJERR; ksi.ksi_trapno = T_PROTFLT; ksi.ksi_addr = (void *)regs->tf_rip; trapsignal(td, &ksi); return(EINVAL); } PROC_LOCK(p); linux_to_bsd_sigset(&uc.uc_sigmask, &td->td_sigmask); SIG_CANTMASK(td->td_sigmask); signotify(td); PROC_UNLOCK(p); /* * Restore signal context */ /* Selectors were restored by the trampoline. */ regs->tf_rdi = context->sc_edi; regs->tf_rsi = context->sc_esi; regs->tf_rbp = context->sc_ebp; regs->tf_rbx = context->sc_ebx; regs->tf_rdx = context->sc_edx; regs->tf_rcx = context->sc_ecx; regs->tf_rax = context->sc_eax; regs->tf_rip = context->sc_eip; regs->tf_cs = context->sc_cs; regs->tf_rflags = eflags; regs->tf_rsp = context->sc_esp_at_signal; regs->tf_ss = context->sc_ss; /* * call sigaltstack & ignore results.. */ lss = &uc.uc_stack; ss.ss_sp = PTRIN(lss->ss_sp); ss.ss_size = lss->ss_size; ss.ss_flags = linux_to_bsd_sigaltstack(lss->ss_flags); #ifdef DEBUG if (ldebug(rt_sigreturn)) printf(LMSG("rt_sigret flags: 0x%x, sp: %p, ss: 0x%lx, mask: 0x%x"), ss.ss_flags, ss.ss_sp, ss.ss_size, context->sc_mask); #endif (void)kern_sigaltstack(td, &ss, NULL); return (EJUSTRETURN); } /* * MPSAFE */ static void linux_prepsyscall(struct trapframe *tf, int *args, u_int *code, caddr_t *params) { args[0] = tf->tf_rbx; args[1] = tf->tf_rcx; args[2] = tf->tf_rdx; args[3] = tf->tf_rsi; args[4] = tf->tf_rdi; args[5] = tf->tf_rbp; /* Unconfirmed */ *params = NULL; /* no copyin */ } /* * If a linux binary is exec'ing something, try this image activator * first. We override standard shell script execution in order to * be able to modify the interpreter path. We only do this if a linux * binary is doing the exec, so we do not create an EXEC module for it. */ static int exec_linux_imgact_try(struct image_params *iparams); static int exec_linux_imgact_try(struct image_params *imgp) { const char *head = (const char *)imgp->image_header; char *rpath; int error = -1, len; /* * The interpreter for shell scripts run from a linux binary needs * to be located in /compat/linux if possible in order to recursively * maintain linux path emulation. */ if (((const short *)head)[0] == SHELLMAGIC) { /* * Run our normal shell image activator. If it succeeds attempt * to use the alternate path for the interpreter. If an alternate * path is found, use our stringspace to store it. */ if ((error = exec_shell_imgact(imgp)) == 0) { linux_emul_convpath(FIRST_THREAD_IN_PROC(imgp->proc), imgp->interpreter_name, UIO_SYSSPACE, &rpath, 0); if (rpath != NULL) { len = strlen(rpath) + 1; if (len <= MAXSHELLCMDLEN) { memcpy(imgp->interpreter_name, rpath, len); } free(rpath, M_TEMP); } } } return(error); } /* * Clear registers on exec * XXX copied from ia32_signal.c. */ static void exec_linux_setregs(td, entry, stack, ps_strings) struct thread *td; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; wrmsr(MSR_FSBASE, 0); wrmsr(MSR_KGSBASE, 0); /* User value while we're in the kernel */ pcb->pcb_fsbase = 0; pcb->pcb_gsbase = 0; load_ds(_udatasel); load_es(_udatasel); load_fs(_udatasel); load_gs(0); pcb->pcb_ds = _udatasel; pcb->pcb_es = _udatasel; pcb->pcb_fs = _udatasel; pcb->pcb_gs = 0; bzero((char *)regs, sizeof(struct trapframe)); regs->tf_rip = entry; regs->tf_rsp = stack; regs->tf_rflags = PSL_USER | (regs->tf_rflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_cs = _ucode32sel; regs->tf_rbx = ps_strings; load_cr0(rcr0() | CR0_MP | CR0_TS); fpstate_drop(td); /* Return via doreti so that we can change to a different %cs */ pcb->pcb_flags |= PCB_FULLCTX; td->td_retval[1] = 0; } /* * XXX copied from ia32_sysvec.c. */ static register_t * linux_copyout_strings(struct image_params *imgp) { int argc, envc; u_int32_t *vectp; char *stringp, *destp; u_int32_t *stack_base; struct linux32_ps_strings *arginfo; int sigcodesz; /* * Calculate string base and vector table pointers. * Also deal with signal trampoline code for this exec type. */ arginfo = (struct linux32_ps_strings *)LINUX32_PS_STRINGS; sigcodesz = *(imgp->proc->p_sysent->sv_szsigcode); destp = (caddr_t)arginfo - sigcodesz - SPARE_USRSPACE - roundup((ARG_MAX - imgp->args->stringspace), sizeof(char *)); /* * install sigcode */ if (sigcodesz) copyout(imgp->proc->p_sysent->sv_sigcode, ((caddr_t)arginfo - sigcodesz), szsigcode); /* * If we have a valid auxargs ptr, prepare some room * on the stack. */ if (imgp->auxargs) { /* * 'AT_COUNT*2' is size for the ELF Auxargs data. This is for * lower compatibility. */ imgp->auxarg_size = (imgp->auxarg_size) ? imgp->auxarg_size : (AT_COUNT * 2); /* * The '+ 2' is for the null pointers at the end of each of * the arg and env vector sets,and imgp->auxarg_size is room * for argument of Runtime loader. */ vectp = (u_int32_t *) (destp - (imgp->args->argc + imgp->args->envc + 2 + imgp->auxarg_size) * sizeof(u_int32_t)); } else /* * The '+ 2' is for the null pointers at the end of each of * the arg and env vector sets */ vectp = (u_int32_t *) (destp - (imgp->args->argc + imgp->args->envc + 2) * sizeof(u_int32_t)); /* * vectp also becomes our initial stack base */ stack_base = vectp; stringp = imgp->args->begin_argv; argc = imgp->args->argc; envc = imgp->args->envc; /* * Copy out strings - arguments and environment. */ copyout(stringp, destp, ARG_MAX - imgp->args->stringspace); /* * Fill in "ps_strings" struct for ps, w, etc. */ suword32(&arginfo->ps_argvstr, (u_int32_t)(intptr_t)vectp); suword32(&arginfo->ps_nargvstr, argc); /* * Fill in argument portion of vector table. */ for (; argc > 0; --argc) { suword32(vectp++, (u_int32_t)(intptr_t)destp); while (*stringp++ != 0) destp++; destp++; } /* a null vector table pointer separates the argp's from the envp's */ suword32(vectp++, 0); suword32(&arginfo->ps_envstr, (u_int32_t)(intptr_t)vectp); suword32(&arginfo->ps_nenvstr, envc); /* * Fill in environment portion of vector table. */ for (; envc > 0; --envc) { suword32(vectp++, (u_int32_t)(intptr_t)destp); while (*stringp++ != 0) destp++; destp++; } /* end of vector table is a null pointer */ suword32(vectp, 0); return ((register_t *)stack_base); } SYSCTL_NODE(_compat, OID_AUTO, linux32, CTLFLAG_RW, 0, "32-bit Linux emulation"); static u_long linux32_maxdsiz = LINUX32_MAXDSIZ; SYSCTL_ULONG(_compat_linux32, OID_AUTO, maxdsiz, CTLFLAG_RW, &linux32_maxdsiz, 0, ""); static u_long linux32_maxssiz = LINUX32_MAXSSIZ; SYSCTL_ULONG(_compat_linux32, OID_AUTO, maxssiz, CTLFLAG_RW, &linux32_maxssiz, 0, ""); static u_long linux32_maxvmem = LINUX32_MAXVMEM; SYSCTL_ULONG(_compat_linux32, OID_AUTO, maxvmem, CTLFLAG_RW, &linux32_maxvmem, 0, ""); /* * XXX copied from ia32_sysvec.c. */ static void linux32_fixlimits(struct proc *p) { struct plimit *oldlim, *newlim; if (linux32_maxdsiz == 0 && linux32_maxssiz == 0 && linux32_maxvmem == 0) return; newlim = lim_alloc(); PROC_LOCK(p); oldlim = p->p_limit; lim_copy(newlim, oldlim); if (linux32_maxdsiz != 0) { if (newlim->pl_rlimit[RLIMIT_DATA].rlim_cur > linux32_maxdsiz) newlim->pl_rlimit[RLIMIT_DATA].rlim_cur = linux32_maxdsiz; if (newlim->pl_rlimit[RLIMIT_DATA].rlim_max > linux32_maxdsiz) newlim->pl_rlimit[RLIMIT_DATA].rlim_max = linux32_maxdsiz; } if (linux32_maxssiz != 0) { if (newlim->pl_rlimit[RLIMIT_STACK].rlim_cur > linux32_maxssiz) newlim->pl_rlimit[RLIMIT_STACK].rlim_cur = linux32_maxssiz; if (newlim->pl_rlimit[RLIMIT_STACK].rlim_max > linux32_maxssiz) newlim->pl_rlimit[RLIMIT_STACK].rlim_max = linux32_maxssiz; } if (linux32_maxvmem != 0) { if (newlim->pl_rlimit[RLIMIT_VMEM].rlim_cur > linux32_maxvmem) newlim->pl_rlimit[RLIMIT_VMEM].rlim_cur = linux32_maxvmem; if (newlim->pl_rlimit[RLIMIT_VMEM].rlim_max > linux32_maxvmem) newlim->pl_rlimit[RLIMIT_VMEM].rlim_max = linux32_maxvmem; } p->p_limit = newlim; PROC_UNLOCK(p); lim_free(oldlim); } struct sysentvec elf_linux_sysvec = { LINUX_SYS_MAXSYSCALL, linux_sysent, 0xff, LINUX_SIGTBLSZ, bsd_to_linux_signal, ELAST + 1, bsd_to_linux_errno, translate_traps, elf_linux_fixup, linux_sendsig, linux_sigcode, &linux_szsigcode, linux_prepsyscall, "Linux ELF32", elf32_coredump, exec_linux_imgact_try, LINUX_MINSIGSTKSZ, PAGE_SIZE, VM_MIN_ADDRESS, LINUX32_USRSTACK, LINUX32_USRSTACK, LINUX32_PS_STRINGS, VM_PROT_ALL, linux_copyout_strings, exec_linux_setregs, linux32_fixlimits }; static Elf32_Brandinfo linux_brand = { ELFOSABI_LINUX, EM_386, "Linux", "/compat/linux", "/lib/ld-linux.so.1", &elf_linux_sysvec, NULL, BI_CAN_EXEC_DYN, }; static Elf32_Brandinfo linux_glibc2brand = { ELFOSABI_LINUX, EM_386, "Linux", "/compat/linux", "/lib/ld-linux.so.2", &elf_linux_sysvec, NULL, BI_CAN_EXEC_DYN, }; Elf32_Brandinfo *linux_brandlist[] = { &linux_brand, &linux_glibc2brand, NULL }; static int linux_elf_modevent(module_t mod, int type, void *data) { Elf32_Brandinfo **brandinfo; int error; struct linux_ioctl_handler **lihp; error = 0; switch(type) { case MOD_LOAD: for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf32_insert_brand_entry(*brandinfo) < 0) error = EINVAL; if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_register_handler(*lihp); if (bootverbose) printf("Linux ELF exec handler installed\n"); } else printf("cannot insert Linux ELF brand handler\n"); break; case MOD_UNLOAD: for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf32_brand_inuse(*brandinfo)) error = EBUSY; if (error == 0) { for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf32_remove_brand_entry(*brandinfo) < 0) error = EINVAL; } if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_unregister_handler(*lihp); if (bootverbose) printf("Linux ELF exec handler removed\n"); } else printf("Could not deinstall ELF interpreter entry\n"); break; default: break; } return error; } static moduledata_t linux_elf_mod = { "linuxelf", linux_elf_modevent, 0 }; DECLARE_MODULE(linuxelf, linux_elf_mod, SI_SUB_EXEC, SI_ORDER_ANY); Index: head/sys/amd64/linux32/syscalls.master =================================================================== --- head/sys/amd64/linux32/syscalls.master (revision 156873) +++ head/sys/amd64/linux32/syscalls.master (revision 156874) @@ -1,430 +1,431 @@ $FreeBSD$ ; @(#)syscalls.master 8.1 (Berkeley) 7/19/93 ; System call name/number master file (or rather, slave, from LINUX). ; Processed to create linux_sysent.c, linux_proto.h and linux_syscall.h. ; Columns: number type nargs name alt{name,tag,rtyp}/comments ; number system call number, must be in order ; audit the audit event associated with the system call ; A value of AUE_NULL means no auditing, but it also means that ; there is no audit event for the call at this time. For the ; case where the event exists, but we don't want auditing, the ; event should be #defined to AUE_NULL in audit_kevents.h. ; type one of STD, OBSOL, UNIMPL ; name psuedo-prototype of syscall routine ; If one of the following alts is different, then all appear: ; altname name of system call if different ; alttag name of args struct tag if different from [o]`name'"_args" ; altrtyp return type if not int (bogus - syscalls always return int) ; for UNIMPL/OBSOL, name continues with comments ; types: ; STD always included ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only +#include "opt_compat.h" #include #include #include #include #include #include ; Isn't pretty, but there seems to be no other way to trap nosys #define nosys linux_nosys ; #ifdef's, etc. may be included, and are copied to the output files. 0 AUE_NULL UNIMPL setup 1 AUE_NULL MNOPROTO { void sys_exit(int rval); } exit \ sys_exit_args void 2 AUE_NULL MSTD { int linux_fork(void); } 3 AUE_NULL MNOPROTO { int read(int fd, char *buf, \ u_int nbyte); } 4 AUE_NULL MNOPROTO { int write(int fd, char *buf, \ u_int nbyte); } 5 AUE_NULL MSTD { int linux_open(char *path, l_int flags, \ l_int mode); } 6 AUE_NULL MNOPROTO { int close(int fd); } 7 AUE_NULL MSTD { int linux_waitpid(l_pid_t pid, \ l_int *status, l_int options); } 8 AUE_NULL MSTD { int linux_creat(char *path, l_int mode); } 9 AUE_NULL MSTD { int linux_link(char *path, char *to); } 10 AUE_NULL MSTD { int linux_unlink(char *path); } 11 AUE_NULL MSTD { int linux_execve(char *path, char **argp, \ char **envp); } 12 AUE_NULL MSTD { int linux_chdir(char *path); } 13 AUE_NULL MSTD { int linux_time(l_time_t *tm); } 14 AUE_NULL MSTD { int linux_mknod(char *path, l_int mode, \ l_dev_t dev); } 15 AUE_NULL MSTD { int linux_chmod(char *path, \ l_mode_t mode); } 16 AUE_NULL MSTD { int linux_lchown16(char *path, \ l_uid16_t uid, l_gid16_t gid); } 17 AUE_NULL UNIMPL break 18 AUE_NULL UNIMPL stat 19 AUE_NULL MSTD { int linux_lseek(l_uint fdes, l_off_t off, \ l_int whence); } 20 AUE_NULL MSTD { int linux_getpid(void); } 21 AUE_NULL STD { int linux_mount(char *specialfile, \ char *dir, char *filesystemtype, \ l_ulong rwflag, void *data); } 22 AUE_NULL STD { int linux_oldumount(char *path); } 23 AUE_NULL MSTD { int linux_setuid16(l_uid16_t uid); } 24 AUE_NULL MSTD { int linux_getuid16(void); } 25 AUE_NULL MSTD { int linux_stime(void); } 26 AUE_NULL MSTD { int linux_ptrace(l_long req, l_long pid, \ l_long addr, l_long data); } 27 AUE_NULL MSTD { int linux_alarm(l_uint secs); } 28 AUE_NULL UNIMPL fstat 29 AUE_NULL MSTD { int linux_pause(void); } 30 AUE_NULL MSTD { int linux_utime(char *fname, \ struct l_utimbuf *times); } 31 AUE_NULL UNIMPL stty 32 AUE_NULL UNIMPL gtty 33 AUE_NULL MSTD { int linux_access(char *path, l_int flags); } 34 AUE_NULL MSTD { int linux_nice(l_int inc); } 35 AUE_NULL UNIMPL ftime 36 AUE_NULL MNOPROTO { int sync(void); } 37 AUE_NULL MSTD { int linux_kill(l_int pid, l_int signum); } 38 AUE_NULL MSTD { int linux_rename(char *from, char *to); } 39 AUE_NULL MSTD { int linux_mkdir(char *path, l_int mode); } 40 AUE_NULL MSTD { int linux_rmdir(char *path); } 41 AUE_NULL MNOPROTO { int dup(u_int fd); } 42 AUE_NULL MSTD { int linux_pipe(l_ulong *pipefds); } 43 AUE_NULL MSTD { int linux_times(struct l_times_argv *buf); } 44 AUE_NULL UNIMPL prof 45 AUE_NULL STD { int linux_brk(l_ulong dsend); } 46 AUE_NULL MSTD { int linux_setgid16(l_gid16_t gid); } 47 AUE_NULL MSTD { int linux_getgid16(void); } 48 AUE_NULL MSTD { int linux_signal(l_int sig, \ l_handler_t handler); } 49 AUE_NULL MSTD { int linux_geteuid16(void); } 50 AUE_NULL MSTD { int linux_getegid16(void); } 51 AUE_NULL MNOPROTO { int acct(char *path); } 52 AUE_NULL STD { int linux_umount(char *path, l_int flags); } 53 AUE_NULL UNIMPL lock 54 AUE_NULL STD { int linux_ioctl(l_uint fd, l_uint cmd, \ uintptr_t arg); } 55 AUE_NULL MSTD { int linux_fcntl(l_uint fd, l_uint cmd, \ uintptr_t arg); } 56 AUE_NULL UNIMPL mpx 57 AUE_NULL MNOPROTO { int setpgid(int pid, int pgid); } 58 AUE_NULL UNIMPL ulimit 59 AUE_NULL MSTD { int linux_olduname(void); } 60 AUE_NULL MNOPROTO { int umask(int newmask); } 61 AUE_NULL MNOPROTO { int chroot(char *path); } 62 AUE_NULL MSTD { int linux_ustat(l_dev_t dev, \ struct l_ustat *ubuf); } 63 AUE_NULL MNOPROTO { int dup2(u_int from, u_int to); } 64 AUE_NULL MNOPROTO { int getppid(void); } 65 AUE_NULL MNOPROTO { int getpgrp(void); } 66 AUE_NULL MNOPROTO { int setsid(void); } 67 AUE_NULL MSTD { int linux_sigaction(l_int sig, \ l_osigaction_t *nsa, \ l_osigaction_t *osa); } 68 AUE_NULL MSTD { int linux_sgetmask(void); } 69 AUE_NULL MSTD { int linux_ssetmask(l_osigset_t mask); } 70 AUE_NULL MSTD { int linux_setreuid16(l_uid16_t ruid, \ l_uid16_t euid); } 71 AUE_NULL MSTD { int linux_setregid16(l_gid16_t rgid, \ l_gid16_t egid); } 72 AUE_NULL MSTD { int linux_sigsuspend(l_int hist0, \ l_int hist1, l_osigset_t mask); } 73 AUE_NULL MSTD { int linux_sigpending(l_osigset_t *mask); } 74 AUE_NULL MSTD { int linux_sethostname(char *hostname, \ u_int len); } 75 AUE_NULL MSTD { int linux_setrlimit(l_uint resource, \ struct l_rlimit *rlim); } 76 AUE_NULL MSTD { int linux_old_getrlimit(l_uint resource, \ struct l_rlimit *rlim); } 77 AUE_NULL MSTD { int linux_getrusage(int who, \ struct l_rusage *rusage); } 78 AUE_NULL MSTD { int linux_gettimeofday( \ struct l_timeval *tp, \ struct timezone *tzp); } 79 AUE_NULL MSTD { int linux_settimeofday( \ struct l_timeval *tp, \ struct timezone *tzp); } 80 AUE_NULL MSTD { int linux_getgroups16(l_uint gidsetsize, \ l_gid16_t *gidset); } 81 AUE_NULL MSTD { int linux_setgroups16(l_uint gidsetsize, \ l_gid16_t *gidset); } 82 AUE_NULL MSTD { int linux_old_select( \ struct l_old_select_argv *ptr); } 83 AUE_NULL MSTD { int linux_symlink(char *path, \ char *to); } 84 AUE_NULL MSTD { int linux_stat(char *path, struct l_stat *up); } 85 AUE_NULL MSTD { int linux_readlink(char *name, \ char *buf, l_int count); } 86 AUE_NULL UNIMPL linux_uselib 87 AUE_NULL MNOPROTO { int swapon(char *name); } 88 AUE_NULL MSTD { int linux_reboot(l_int magic1, \ l_int magic2, l_uint cmd, \ void *arg); } 89 AUE_NULL STD { int linux_readdir(l_uint fd, \ struct l_dirent *dent, l_uint count); } 90 AUE_NULL MSTD { int linux_mmap(struct l_mmap_argv *ptr); } 91 AUE_NULL MNOPROTO { int munmap(caddr_t addr, int len); } 92 AUE_NULL MSTD { int linux_truncate(char *path, \ l_ulong length); } 93 AUE_NULL MSTD { int linux_ftruncate(int fd, long length); } 94 AUE_NULL MNOPROTO { int fchmod(int fd, int mode); } 95 AUE_NULL MNOPROTO { int fchown(int fd, int uid, int gid); } 96 AUE_NULL MSTD { int linux_getpriority(int which, int who); } 97 AUE_NULL MNOPROTO { int setpriority(int which, int who, \ int prio); } 98 AUE_NULL UNIMPL profil 99 AUE_NULL MSTD { int linux_statfs(char *path, \ struct l_statfs_buf *buf); } 100 AUE_NULL MSTD { int linux_fstatfs(l_uint fd, \ struct l_statfs_buf *buf); } 101 AUE_NULL UNIMPL ioperm 102 AUE_NULL MSTD { int linux_socketcall(l_int what, \ l_ulong args); } 103 AUE_NULL MSTD { int linux_syslog(l_int type, char *buf, \ l_int len); } 104 AUE_NULL MSTD { int linux_setitimer(l_int which, \ struct l_itimerval *itv, \ struct l_itimerval *oitv); } 105 AUE_NULL MSTD { int linux_getitimer(l_int which, \ struct l_itimerval *itv); } 106 AUE_NULL MSTD { int linux_newstat(char *path, \ struct l_newstat *buf); } 107 AUE_NULL MSTD { int linux_newlstat(char *path, \ struct l_newstat *buf); } 108 AUE_NULL MSTD { int linux_newfstat(l_uint fd, \ struct l_newstat *buf); } 109 AUE_NULL MSTD { int linux_uname(void); } 110 AUE_NULL UNIMPL iopl 111 AUE_NULL MSTD { int linux_vhangup(void); } 112 AUE_NULL UNIMPL idle 113 AUE_NULL UNIMPL vm86old 114 AUE_NULL MSTD { int linux_wait4(l_pid_t pid, \ l_uint *status, l_int options, \ struct l_rusage *rusage); } 115 AUE_NULL MSTD { int linux_swapoff(void); } 116 AUE_NULL MSTD { int linux_sysinfo(struct l_sysinfo *info); } 117 AUE_NULL STD { int linux_ipc(l_uint what, l_int arg1, \ l_int arg2, l_int arg3, void *ptr, \ l_long arg5); } 118 AUE_NULL MNOPROTO { int fsync(int fd); } 119 AUE_NULL MSTD { int linux_sigreturn( \ struct l_sigframe *sfp); } 120 AUE_NULL MSTD { int linux_clone(l_int flags, \ void *stack); } 121 AUE_NULL MNOPROTO { int setdomainname(char *name, int len); } 122 AUE_NULL MSTD { int linux_newuname( \ struct l_new_utsname *buf); } 123 AUE_NULL UNIMPL modify_ldt 124 AUE_NULL MSTD { int linux_adjtimex(void); } 125 AUE_NULL MSTD { int linux_mprotect(caddr_t addr, int len, \ int prot); } 126 AUE_NULL MSTD { int linux_sigprocmask(l_int how, \ l_osigset_t *mask, l_osigset_t *omask); } 127 AUE_NULL MSTD { int linux_create_module(void); } 128 AUE_NULL MSTD { int linux_init_module(void); } 129 AUE_NULL MSTD { int linux_delete_module(void); } 130 AUE_NULL MSTD { int linux_get_kernel_syms(void); } 131 AUE_NULL MSTD { int linux_quotactl(void); } 132 AUE_NULL MNOPROTO { int getpgid(int pid); } 133 AUE_NULL MNOPROTO { int fchdir(int fd); } 134 AUE_NULL MSTD { int linux_bdflush(void); } 135 AUE_NULL MSTD { int linux_sysfs(l_int option, \ l_ulong arg1, l_ulong arg2); } 136 AUE_NULL MSTD { int linux_personality(l_ulong per); } 137 AUE_NULL UNIMPL afs_syscall 138 AUE_NULL MSTD { int linux_setfsuid16(l_uid16_t uid); } 139 AUE_NULL MSTD { int linux_setfsgid16(l_gid16_t gid); } 140 AUE_NULL MSTD { int linux_llseek(l_int fd, l_ulong ohigh, \ l_ulong olow, l_loff_t *res, \ l_uint whence); } 141 AUE_NULL STD { int linux_getdents(l_uint fd, void *dent, \ l_uint count); } 142 AUE_NULL MSTD { int linux_select(l_int nfds, \ l_fd_set *readfds, l_fd_set *writefds, \ l_fd_set *exceptfds, \ struct l_timeval *timeout); } 143 AUE_NULL MNOPROTO { int flock(int fd, int how); } 144 AUE_NULL MSTD { int linux_msync(l_ulong addr, \ l_size_t len, l_int fl); } 145 AUE_NULL MSTD { int linux_readv(int fd, \ struct iovec32 *iovp, u_int iovcnt); } 146 AUE_NULL MSTD { int linux_writev(int fd, \ struct iovec32 *iovp, u_int iovcnt); } 147 AUE_NULL MSTD { int linux_getsid(l_pid_t pid); } 148 AUE_NULL MSTD { int linux_fdatasync(l_uint fd); } 149 AUE_NULL MSTD { int linux_sysctl( \ struct l___sysctl_args *args); } 150 AUE_NULL MNOPROTO { int mlock(const void *addr, size_t len); } 151 AUE_NULL MNOPROTO { int munlock(const void *addr, \ size_t len); } 152 AUE_NULL MNOPROTO { int mlockall(int how); } 153 AUE_NULL MNOPROTO { int munlockall(void); } 154 AUE_NULL MNOPROTO { int sched_setparam(pid_t pid, \ const struct sched_param *param); } 155 AUE_NULL MNOPROTO { int sched_getparam(pid_t pid, \ struct sched_param *param); } 156 AUE_NULL MSTD { int linux_sched_setscheduler(l_pid_t pid, \ l_int policy, \ struct l_sched_param *param); } 157 AUE_NULL MSTD { int linux_sched_getscheduler(l_pid_t pid); } 158 AUE_NULL MNOPROTO { int sched_yield(void); } 159 AUE_NULL MSTD { int linux_sched_get_priority_max( \ l_int policy); } 160 AUE_NULL MSTD { int linux_sched_get_priority_min( \ l_int policy); } 161 AUE_NULL MSTD { int linux_sched_rr_get_interval( \ l_pid_t pid, \ struct l_timespec *interval); } 162 AUE_NULL MSTD { int linux_nanosleep( \ const struct l_timespec *rqtp, \ struct l_timespec *rmtp); } 163 AUE_NULL MSTD { int linux_mremap(l_ulong addr, \ l_ulong old_len, l_ulong new_len, \ l_ulong flags, l_ulong new_addr); } 164 AUE_NULL MSTD { int linux_setresuid16(l_uid16_t ruid, \ l_uid16_t euid, l_uid16_t suid); } 165 AUE_NULL MSTD { int linux_getresuid16(l_uid16_t *ruid, \ l_uid16_t *euid, l_uid16_t *suid); } 166 AUE_NULL UNIMPL vm86 167 AUE_NULL MSTD { int linux_query_module(void); } 168 AUE_NULL MNOPROTO { int poll(struct pollfd*, \ unsigned int nfds, int timeout); } 169 AUE_NULL MSTD { int linux_nfsservctl(void); } 170 AUE_NULL MSTD { int linux_setresgid16(l_gid16_t rgid, \ l_gid16_t egid, l_gid16_t sgid); } 171 AUE_NULL MSTD { int linux_getresgid16(l_gid16_t *rgid, \ l_gid16_t *egid, l_gid16_t *sgid); } 172 AUE_NULL MSTD { int linux_prctl(void); } 173 AUE_NULL MSTD { int linux_rt_sigreturn( \ struct l_ucontext *ucp); } 174 AUE_NULL MSTD { int linux_rt_sigaction(l_int sig, \ l_sigaction_t *act, l_sigaction_t *oact, \ l_size_t sigsetsize); } 175 AUE_NULL MSTD { int linux_rt_sigprocmask(l_int how, \ l_sigset_t *mask, l_sigset_t *omask, \ l_size_t sigsetsize); } 176 AUE_NULL MSTD { int linux_rt_sigpending(void); } 177 AUE_NULL MSTD { int linux_rt_sigtimedwait(void); } 178 AUE_NULL MSTD { int linux_rt_sigqueueinfo(void); } 179 AUE_NULL MSTD { int linux_rt_sigsuspend( \ l_sigset_t *newset, \ l_size_t sigsetsize); } 180 AUE_NULL MSTD { int linux_pread(l_uint fd, char *buf, \ l_size_t nbyte, l_loff_t offset); } 181 AUE_NULL MSTD { int linux_pwrite(l_uint fd, char *buf, \ l_size_t nbyte, l_loff_t offset); } 182 AUE_NULL MSTD { int linux_chown16(char *path, \ l_uid16_t uid, l_gid16_t gid); } 183 AUE_NULL MSTD { int linux_getcwd(char *buf, \ l_ulong bufsize); } 184 AUE_NULL MSTD { int linux_capget(void); } 185 AUE_NULL MSTD { int linux_capset(void); } 186 AUE_NULL MSTD { int linux_sigaltstack(l_stack_t *uss, \ l_stack_t *uoss); } 187 AUE_NULL MSTD { int linux_sendfile(void); } 188 AUE_NULL UNIMPL getpmsg 189 AUE_NULL UNIMPL putpmsg 190 AUE_NULL MSTD { int linux_vfork(void); } 191 AUE_NULL MSTD { int linux_getrlimit(l_uint resource, \ struct l_rlimit *rlim); } 192 AUE_NULL MSTD { int linux_mmap2(l_ulong addr, l_ulong len, \ l_ulong prot, l_ulong flags, l_ulong fd, \ l_ulong pgoff); } 193 AUE_NULL MSTD { int linux_truncate64(char *path, \ l_loff_t length); } 194 AUE_NULL MSTD { int linux_ftruncate64(l_uint fd, \ l_loff_t length); } 195 AUE_NULL MSTD { int linux_stat64(char *filename, \ struct l_stat64 *statbuf, l_long flags); } 196 AUE_NULL MSTD { int linux_lstat64(char *filename, \ struct l_stat64 *statbuf, l_long flags); } 197 AUE_NULL MSTD { int linux_fstat64(l_ulong fd, \ struct l_stat64 *statbuf, l_long flags); } 198 AUE_NULL MSTD { int linux_lchown(char *path, l_uid_t uid, \ l_gid_t gid); } 199 AUE_NULL MSTD { int linux_getuid(void); } 200 AUE_NULL MSTD { int linux_getgid(void); } 201 AUE_NULL MNOPROTO { int geteuid(void); } 202 AUE_NULL MNOPROTO { int getegid(void); } 203 AUE_NULL MNOPROTO { int setreuid(uid_t ruid, uid_t euid); } 204 AUE_NULL MNOPROTO { int setregid(gid_t rgid, gid_t egid); } 205 AUE_NULL MSTD { int linux_getgroups(l_int gidsetsize, \ l_gid_t *grouplist); } 206 AUE_NULL MSTD { int linux_setgroups(l_int gidsetsize, \ l_gid_t *grouplist); } 207 AUE_NULL NODEF fchown fchown fchown_args int 208 AUE_NULL MNOPROTO { int setresuid(uid_t ruid, uid_t euid, \ uid_t suid); } 209 AUE_NULL MNOPROTO { int getresuid(uid_t *ruid, uid_t *euid, \ uid_t *suid); } 210 AUE_NULL MNOPROTO { int setresgid(gid_t rgid, gid_t egid, \ gid_t sgid); } 211 AUE_NULL MNOPROTO { int getresgid(gid_t *rgid, gid_t *egid, \ gid_t *sgid); } 212 AUE_NULL MSTD { int linux_chown(char *path, l_uid_t uid, \ l_gid_t gid); } 213 AUE_NULL MNOPROTO { int setuid(uid_t uid); } 214 AUE_NULL MNOPROTO { int setgid(gid_t gid); } 215 AUE_NULL MSTD { int linux_setfsuid(l_uid_t uid); } 216 AUE_NULL MSTD { int linux_setfsgid(l_gid_t gid); } 217 AUE_NULL MSTD { int linux_pivot_root(char *new_root, \ char *put_old); } 218 AUE_NULL MSTD { int linux_mincore(l_ulong start, \ l_size_t len, u_char *vec); } 219 AUE_NULL MNOPROTO { int madvise(void *addr, size_t len, \ int behav); } 220 AUE_NULL STD { int linux_getdents64(l_uint fd, \ void *dirent, l_uint count); } 221 AUE_NULL MSTD { int linux_fcntl64(l_uint fd, l_uint cmd, \ uintptr_t arg); } 222 AUE_NULL UNIMPL 223 AUE_NULL UNIMPL 224 AUE_NULL MNOPROTO { int linux_getpid(void); } gettid \ linux_getpid_args void 225 AUE_NULL UNIMPL linux_readahead 226 AUE_NULL MSTD { int linux_setxattr(void); } 227 AUE_NULL MSTD { int linux_lsetxattr(void); } 228 AUE_NULL MSTD { int linux_fsetxattr(void); } 229 AUE_NULL MSTD { int linux_getxattr(void); } 230 AUE_NULL MSTD { int linux_lgetxattr(void); } 231 AUE_NULL MSTD { int linux_fgetxattr(void); } 232 AUE_NULL MSTD { int linux_listxattr(void); } 233 AUE_NULL MSTD { int linux_llistxattr(void); } 234 AUE_NULL MSTD { int linux_flistxattr(void); } 235 AUE_NULL MSTD { int linux_removexattr(void); } 236 AUE_NULL MSTD { int linux_lremovexattr(void); } 237 AUE_NULL MSTD { int linux_fremovexattr(void); } 238 AUE_NULL UNIMPL linux_tkill 239 AUE_NULL UNIMPL linux_sendfile64 240 AUE_NULL UNIMPL linux_futex 241 AUE_NULL UNIMPL linux_sched_setaffinity 242 AUE_NULL UNIMPL linux_sched_getaffinity 243 AUE_NULL UNIMPL linux_set_thread_area 244 AUE_NULL UNIMPL linux_get_thread_area 245 AUE_NULL UNIMPL linux_io_setup 246 AUE_NULL UNIMPL linux_io_destroy 247 AUE_NULL UNIMPL linux_io_getevents 248 AUE_NULL UNIMPL linux_io_submit 249 AUE_NULL UNIMPL linux_io_cancel 250 AUE_NULL MSTD { int linux_fadvise64(void); } 251 AUE_NULL UNIMPL 252 AUE_NULL MNOPROTO { void sys_exit(int rval); } exit_group \ sys_exit_args void 253 AUE_NULL UNIMPL linux_lookup_dcookie 254 AUE_NULL UNIMPL linux_epoll_create 255 AUE_NULL UNIMPL linux_epoll_ctl 256 AUE_NULL UNIMPL linux_epoll_wait 257 AUE_NULL UNIMPL linux_remap_file_pages 258 AUE_NULL UNIMPL linux_set_tid_address 259 AUE_NULL UNIMPL linux_timer_create 260 AUE_NULL UNIMPL linux_timer_settime 261 AUE_NULL UNIMPL linux_timer_gettime 262 AUE_NULL UNIMPL linux_timer_getoverrun 263 AUE_NULL UNIMPL linux_timer_delete 264 AUE_NULL UNIMPL linux_clock_settime 265 AUE_NULL UNIMPL linux_clock_gettime 266 AUE_NULL UNIMPL linux_clock_getres 267 AUE_NULL UNIMPL linux_clock_nanosleep Index: head/sys/compat/linux/linux_file.c =================================================================== --- head/sys/compat/linux/linux_file.c (revision 156873) +++ head/sys/compat/linux/linux_file.c (revision 156874) @@ -1,1177 +1,1178 @@ /*- * 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #ifndef __alpha__ 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); } #endif /*!__alpha__*/ int linux_open(struct thread *td, struct linux_open_args *args) { struct proc *p = td->td_proc; char *path; int bsd_flags, error; 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 bsd_flags = 0; if (args->flags & LINUX_O_RDONLY) bsd_flags |= O_RDONLY; if (args->flags & LINUX_O_WRONLY) bsd_flags |= O_WRONLY; if (args->flags & LINUX_O_RDWR) bsd_flags |= O_RDWR; if (args->flags & LINUX_O_NDELAY) bsd_flags |= O_NONBLOCK; if (args->flags & LINUX_O_APPEND) bsd_flags |= O_APPEND; if (args->flags & LINUX_O_SYNC) bsd_flags |= O_FSYNC; if (args->flags & LINUX_O_NONBLOCK) bsd_flags |= O_NONBLOCK; if (args->flags & LINUX_FASYNC) bsd_flags |= O_ASYNC; if (args->flags & LINUX_O_CREAT) bsd_flags |= O_CREAT; if (args->flags & LINUX_O_TRUNC) bsd_flags |= O_TRUNC; if (args->flags & LINUX_O_EXCL) bsd_flags |= O_EXCL; if (args->flags & LINUX_O_NOCTTY) bsd_flags |= O_NOCTTY; error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, args->mode); PROC_LOCK(p); if (!error && !(bsd_flags & O_NOCTTY) && SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) { struct file *fp; PROC_UNLOCK(p); error = fget(td, td->td_retval[0], &fp); if (!error) { if (fp->f_type == DTYPE_VNODE) fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0, td->td_ucred, td); fdrop(fp, td); } } else { PROC_UNLOCK(p); #ifdef DEBUG if (ldebug(open)) printf(LMSG("open returns error %d"), error); #endif } LFREEPATH(path); return error; } 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; } #ifndef __alpha__ 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; } #endif /*!__alpha__*/ #ifndef __alpha__ 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); } #endif /*!__alpha__*/ /* * 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_long 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]; }; #define LINUX_RECLEN(de,namlen) \ ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1)) #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 */ 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; 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; if (vp->v_type != VDIR) { fdrop(fp, td); return (EINVAL); } nbytes = args->count; if (nbytes == 1) { /* readdir(2) case. Always struct dirent. */ if (is64bit) { fdrop(fp, td); return (EINVAL); } nbytes = sizeof(linux_dirent); justone = 1; } else justone = 0; off = fp->f_offset; buflen = max(LINUX_DIRBLKSIZ, nbytes); buflen = min(buflen, MAXBSIZE); buf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); again: 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_check_vnode_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_RECLEN(&linux_dirent64, bdp->d_namlen) : LINUX_RECLEN(&linux_dirent, bdp->d_namlen); if (reclen > len || resid < linuxreclen) { outp++; break; } if (justone) { /* readdir(2) case. */ linux_dirent.d_ino = (l_long)bdp->d_fileno; linux_dirent.d_off = (l_off_t)linuxreclen; linux_dirent.d_reclen = (l_ushort)bdp->d_namlen; strcpy(linux_dirent.d_name, bdp->d_name); error = copyout(&linux_dirent, outp, linuxreclen); } else { if (is64bit) { 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; strcpy(linux_dirent64.d_name, bdp->d_name); error = copyout(&linux_dirent64, outp, linuxreclen); } else { 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; strcpy(linux_dirent.d_name, bdp->d_name); 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) goto again; 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, td); fdrop(fp, td); free(buf, 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; 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_unlink(struct thread *td, struct linux_unlink_args *args) { char *path; int error; LCONVPATHEXIST(td, args->path, &path); #ifdef DEBUG if (ldebug(unlink)) printf(ARGS(unlink, "%s"), path); #endif error = kern_unlink(td, path, UIO_SYSSPACE); 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_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_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); 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_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); 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_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_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_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.pad = 0; 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); 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); } #ifndef __alpha__ 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); } #endif /*!__alpha__*/ int linux_pread(td, uap) struct thread *td; struct linux_pread_args *uap; { struct pread_args bsd; bsd.fd = uap->fd; bsd.buf = uap->buf; bsd.nbyte = uap->nbyte; bsd.offset = uap->offset; return pread(td, &bsd); } 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 { 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 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; } 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; } 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) */ #if defined(__alpha__) #define linux_fcntl64_args linux_fcntl_args #endif 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_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); } Index: head/sys/compat/linux/linux_getcwd.c =================================================================== --- head/sys/compat/linux/linux_getcwd.c (revision 156873) +++ head/sys/compat/linux/linux_getcwd.c (revision 156874) @@ -1,475 +1,476 @@ /* $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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX only for DIRBLKSIZ */ #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include 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, td); 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_EXCLUSIVE; /* * At this point, lvp is locked and will be unlocked by the lookup. * On successful return, *uvpp will be locked */ #ifdef MAC error = mac_check_vnode_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_check_vnode_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; int perms = 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, td); 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, perms, td->td_ucred, td); if (error) goto out; perms = 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, td); 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 printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid, 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); } Index: head/sys/compat/linux/linux_ioctl.c =================================================================== --- head/sys/compat/linux/linux_ioctl.c (revision 156873) +++ head/sys/compat/linux/linux_ioctl.c (revision 156874) @@ -1,2682 +1,2684 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "opt_compat.h" + #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include #include 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_special; 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 }; 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); 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); /* * 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; #ifdef __alpha__ unsigned char c_cc[LINUX_NCCS]; unsigned char c_line; unsigned int c_ispeed; unsigned int c_ospeed; #else unsigned char c_line; unsigned char c_cc[LINUX_NCCS]; #endif }; struct linux_winsize { unsigned short ws_row, ws_col; unsigned short ws_xpixel, ws_ypixel; }; 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; ic_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 & OXTABS) 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; ic_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; ic_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; ic_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; ic_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 |= OXTABS; 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; ic_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; ic_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; ic_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; #ifdef __alpha__ lio->c_cc[LINUX__VINTR] = lios.c_cc[LINUX_VINTR]; lio->c_cc[LINUX__VQUIT] = lios.c_cc[LINUX_VQUIT]; lio->c_cc[LINUX__VERASE] = lios.c_cc[LINUX_VERASE]; lio->c_cc[LINUX__VKILL] = lios.c_cc[LINUX_VKILL]; lio->c_cc[LINUX__VEOF] = lios.c_cc[(lios.c_lflag & ICANON) ? LINUX_VEOF : LINUX_VMIN]; lio->c_cc[LINUX__VEOL] = lios.c_cc[(lios.c_lflag & ICANON) ? LINUX_VEOL : LINUX_VTIME]; lio->c_cc[LINUX__VEOL2] = lios.c_cc[LINUX_VEOL2]; lio->c_cc[LINUX__VSWTC] = lios.c_cc[LINUX_VSWTC]; #else memcpy(lio->c_cc, lios.c_cc, LINUX_NCC); #endif } 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; #ifdef __alpha__ for (i=0; ic_cc[LINUX__VINTR]; lios.c_cc[LINUX_VQUIT] = lio->c_cc[LINUX__VQUIT]; lios.c_cc[LINUX_VERASE] = lio->c_cc[LINUX__VERASE]; lios.c_cc[LINUX_VKILL] = lio->c_cc[LINUX__VKILL]; lios.c_cc[LINUX_VEOL2] = lio->c_cc[LINUX__VEOL2]; lios.c_cc[LINUX_VSWTC] = lio->c_cc[LINUX__VSWTC]; lios.c_cc[(lio->c_lflag & ICANON) ? LINUX_VEOF : LINUX_VMIN] = lio->c_cc[LINUX__VEOF]; lios.c_cc[(lio->c_lflag & ICANON) ? LINUX_VEOL : LINUX_VTIME] = lio->c_cc[LINUX__VEOL]; #else for (i=LINUX_NCC; ic_cc, LINUX_NCC); #endif 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; } /* LINUX_TIOCPKT */ 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; } 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; error = copyin((void *)args->arg, &lds, sizeof(lds)); if (error) break; error = linux_to_bsd_dvd_struct(&lds, &bds); if (error) break; error = fo_ioctl(fp, DVDIOCREADSTRUCTURE, (caddr_t)&bds, td->td_ucred, td); if (error) break; error = bsd_to_linux_dvd_struct(&bds, &lds); if (error) break; error = copyout(&lds, (void *)args->arg, sizeof(lds)); 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; } /* 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)); break; } 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)); break; } 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_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; /* 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; IFNET_RLOCK(); TAILQ_FOREACH(ifscan, &ifnet, if_link) { if (ifscan == ifp) { IFNET_RUNLOCK(); return (snprintf(buffer, buflen, "eth%d", ethno)); } if (IFP_IS_ETH(ifscan)) ethno++; } IFNET_RUNLOCK(); 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(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; IFNET_RLOCK(); TAILQ_FOREACH(ifp, &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(); 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; /* handle the 'request buffer size' case */ if (ifc.ifc_buf == PTROUT(NULL)) { ifc.ifc_len = 0; TAILQ_FOREACH(ifp, &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); } } error = copyout(&ifc, uifc, sizeof(ifc)); return (error); } if (ifc.ifc_len <= 0) 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(); /* could sleep XXX */ TAILQ_FOREACH(ifp, &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_overflowed(sb)) 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_overflowed(sb)) 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); 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: /* 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: /* 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(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; /* * 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); } /* * 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); } /* * 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; TAILQ_FOREACH(he, &handlers, list) { if (cmd >= he->low && cmd <= he->high) { error = (*he->func)(td, args); if (error != ENOIOCTL) { fdrop(fp, td); return (error); } } } 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. */ TAILQ_FOREACH(he, &handlers, list) { if (he->func == h->func) break; } if (he == NULL) { MALLOC(he, struct handler_element *, 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); return (0); } } TAILQ_INSERT_TAIL(&handlers, he, list); return (0); } int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h) { struct handler_element *he; if (h == NULL || h->func == NULL) return (EINVAL); TAILQ_FOREACH(he, &handlers, list) { if (he->func == h->func) { TAILQ_REMOVE(&handlers, he, list); FREE(he, M_LINUX); return (0); } } return (EINVAL); } Index: head/sys/compat/linux/linux_ipc.c =================================================================== --- head/sys/compat/linux/linux_ipc.c (revision 156873) +++ head/sys/compat/linux/linux_ipc.c (revision 156874) @@ -1,819 +1,821 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include +#include "opt_compat.h" + #ifdef COMPAT_LINUX32 #include #include #include #else #include #include #include #endif #include #include 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; }; 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; /* this goes (yet) SOS */ bsp->shm_internal = PTRIN(lsp->private3); } 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); lsp->shm_segsz = bsp->shm_segsz; lsp->shm_lpid = bsp->shm_lpid; lsp->shm_cpid = bsp->shm_cpid; lsp->shm_nattch = bsp->shm_nattch; lsp->shm_atime = bsp->shm_atime; lsp->shm_dtime = bsp->shm_dtime; lsp->shm_ctime = bsp->shm_ctime; /* this goes (yet) SOS */ lsp->private3 = PTROUT(bsp->shm_internal); } 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; 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 = (struct sembuf *)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 __semctl_args /* { int semid; int semnum; int cmd; union semun *arg; } */ bsd_args; struct l_seminfo linux_seminfo; int error; union semun *unptr; caddr_t sg; sg = stackgap_init(); /* Make sure the arg parameter can be copied in. */ unptr = stackgap_alloc(&sg, sizeof(union semun)); bcopy(&args->arg, unptr, sizeof(union semun)); bsd_args.semid = args->semid; bsd_args.semnum = args->semnum; bsd_args.arg = unptr; switch (args->cmd & ~LINUX_IPC_64) { case LINUX_IPC_RMID: bsd_args.cmd = IPC_RMID; break; case LINUX_GETNCNT: bsd_args.cmd = GETNCNT; break; case LINUX_GETPID: bsd_args.cmd = GETPID; break; case LINUX_GETVAL: bsd_args.cmd = GETVAL; break; case LINUX_GETZCNT: bsd_args.cmd = GETZCNT; break; case LINUX_SETVAL: bsd_args.cmd = SETVAL; break; case LINUX_IPC_SET: bsd_args.cmd = IPC_SET; error = linux_semid_pullup(args->cmd & LINUX_IPC_64, &linux_semid, (caddr_t)PTRIN(args->arg.buf)); if (error) return (error); unptr->buf = stackgap_alloc(&sg, sizeof(struct semid_ds)); linux_to_bsd_semid_ds(&linux_semid, unptr->buf); return __semctl(td, &bsd_args); case LINUX_IPC_STAT: case LINUX_SEM_STAT: if((args->cmd & ~LINUX_IPC_64) == LINUX_IPC_STAT) bsd_args.cmd = IPC_STAT; else bsd_args.cmd = SEM_STAT; unptr->buf = stackgap_alloc(&sg, sizeof(struct semid_ds)); error = __semctl(td, &bsd_args); if (error) return error; td->td_retval[0] = (bsd_args.cmd == SEM_STAT) ? IXSEQ_TO_IPCID(bsd_args.semid, unptr->buf->sem_perm) : 0; bsd_to_linux_semid_ds(unptr->buf, &linux_semid); return (linux_semid_pushdown(args->cmd & LINUX_IPC_64, &linux_semid, (caddr_t)PTRIN(args->arg.buf))); 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: /* FALLTHROUGH */ case LINUX_SETALL: /* FALLTHROUGH */ default: linux_msg(td, "ipc type %d is not implemented", args->cmd & ~LINUX_IPC_64); return EINVAL; } return __semctl(td, &bsd_args); } int linux_msgsnd(struct thread *td, struct linux_msgsnd_args *args) { struct msgsnd_args /* { int msqid; void *msgp; size_t msgsz; int msgflg; } */ bsd_args; bsd_args.msqid = args->msqid; bsd_args.msgp = PTRIN(args->msgp); bsd_args.msgsz = args->msgsz; bsd_args.msgflg = args->msgflg; return msgsnd(td, &bsd_args); } int linux_msgrcv(struct thread *td, struct linux_msgrcv_args *args) { struct msgrcv_args /* { int msqid; void *msgp; size_t msgsz; long msgtyp; int msgflg; } */ bsd_args; bsd_args.msqid = args->msqid; bsd_args.msgp = PTRIN(args->msgp); bsd_args.msgsz = args->msgsz; bsd_args.msgtyp = args->msgtyp; bsd_args.msgflg = args->msgflg; return msgrcv(td, &bsd_args); } 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; error = linux_msqid_pullup(args->cmd & LINUX_IPC_64, &linux_msqid, (caddr_t)PTRIN(args->buf)); if (error != 0) return (error); bsd_cmd = args->cmd & ~LINUX_IPC_64; if (bsd_cmd == LINUX_IPC_SET) linux_to_bsd_msqid_ds(&linux_msqid, &bsd_msqid); 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, (caddr_t)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; size_t bufsz; 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, &bufsz); if (error) return error; bsd_to_linux_shminfo(&bsd_shminfo, &linux_shminfo); return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64, &linux_shminfo, (caddr_t)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, &bufsz); if (error) return error; bsd_to_linux_shm_info(&bsd_shm_info, &linux_shm_info); return copyout(&linux_shm_info, (caddr_t)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, &bufsz); if (error) return error; bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid); return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)PTRIN(args->buf))); case LINUX_SHM_STAT: /* Perform shmctl wanting removed segments lookup */ error = kern_shmctl(td, args->shmid, IPC_STAT, (void *)&bsd_shmid, &bufsz); if (error) return error; bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid); return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)PTRIN(args->buf))); case LINUX_IPC_SET: error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)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, &bufsz); case LINUX_IPC_RMID: { void *buf; if (args->buf == 0) buf = NULL; else { error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)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, &bufsz); } case LINUX_SHM_LOCK: case LINUX_SHM_UNLOCK: default: linux_msg(td, "ipc typ=%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); Index: head/sys/compat/linux/linux_mib.c =================================================================== --- head/sys/compat/linux/linux_mib.c (revision 156873) +++ head/sys/compat/linux/linux_mib.c (revision 156874) @@ -1,353 +1,355 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include +#include "opt_compat.h" + #ifdef COMPAT_LINUX32 #include #else #include #endif #include struct linux_prison { char pr_osname[LINUX_MAX_UTSNAME]; char pr_osrelease[LINUX_MAX_UTSNAME]; int pr_oss_version; }; SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, "Linux mode"); static struct mtx osname_lock; MTX_SYSINIT(linux_osname, &osname_lock, "linux osname", MTX_DEF); static char linux_osname[LINUX_MAX_UTSNAME] = "Linux"; 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, 0, 0, linux_sysctl_osname, "A", "Linux kernel OS name"); static char linux_osrelease[LINUX_MAX_UTSNAME] = "2.4.2"; 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, 0, 0, linux_sysctl_osrelease, "A", "Linux kernel OS release"); static int linux_oss_version = 0x030600; 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, 0, 0, linux_sysctl_oss_version, "I", "Linux OSS version"); /* * Returns holding the prison mutex if return non-NULL. */ static struct prison * linux_get_prison(struct thread *td) { register struct prison *pr; register struct linux_prison *lpr; KASSERT(td == curthread, ("linux_get_prison() called on !curthread")); if (!jailed(td->td_ucred)) return (NULL); pr = td->td_ucred->cr_prison; mtx_lock(&pr->pr_mtx); if (pr->pr_linux == NULL) { /* * If we don't have a linux prison structure yet, allocate * one. We have to handle the race where another thread * could be adding a linux prison to this process already. */ mtx_unlock(&pr->pr_mtx); lpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK | M_ZERO); mtx_lock(&pr->pr_mtx); if (pr->pr_linux == NULL) pr->pr_linux = lpr; else free(lpr, M_PRISON); } return (pr); } void linux_get_osname(struct thread *td, char *dst) { register struct prison *pr; register struct linux_prison *lpr; pr = td->td_ucred->cr_prison; if (pr != NULL) { mtx_lock(&pr->pr_mtx); if (pr->pr_linux != NULL) { lpr = (struct linux_prison *)pr->pr_linux; if (lpr->pr_osname[0]) { bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); mtx_unlock(&pr->pr_mtx); return; } } mtx_unlock(&pr->pr_mtx); } mtx_lock(&osname_lock); bcopy(linux_osname, dst, LINUX_MAX_UTSNAME); mtx_unlock(&osname_lock); } int linux_set_osname(struct thread *td, char *osname) { struct prison *pr; struct linux_prison *lpr; pr = linux_get_prison(td); if (pr != NULL) { lpr = (struct linux_prison *)pr->pr_linux; strcpy(lpr->pr_osname, osname); mtx_unlock(&pr->pr_mtx); } else { mtx_lock(&osname_lock); strcpy(linux_osname, osname); mtx_unlock(&osname_lock); } return (0); } void linux_get_osrelease(struct thread *td, char *dst) { register struct prison *pr; struct linux_prison *lpr; pr = td->td_ucred->cr_prison; if (pr != NULL) { mtx_lock(&pr->pr_mtx); if (pr->pr_linux != NULL) { lpr = (struct linux_prison *)pr->pr_linux; if (lpr->pr_osrelease[0]) { bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME); mtx_unlock(&pr->pr_mtx); return; } } mtx_unlock(&pr->pr_mtx); } mtx_lock(&osname_lock); bcopy(linux_osrelease, dst, LINUX_MAX_UTSNAME); mtx_unlock(&osname_lock); } int linux_set_osrelease(struct thread *td, char *osrelease) { struct prison *pr; struct linux_prison *lpr; pr = linux_get_prison(td); if (pr != NULL) { lpr = (struct linux_prison *)pr->pr_linux; strcpy(lpr->pr_osrelease, osrelease); mtx_unlock(&pr->pr_mtx); } else { mtx_lock(&osname_lock); strcpy(linux_osrelease, osrelease); mtx_unlock(&osname_lock); } return (0); } int linux_get_oss_version(struct thread *td) { register struct prison *pr; register struct linux_prison *lpr; int version; pr = td->td_ucred->cr_prison; if (pr != NULL) { mtx_lock(&pr->pr_mtx); if (pr->pr_linux != NULL) { lpr = (struct linux_prison *)pr->pr_linux; if (lpr->pr_oss_version) { version = lpr->pr_oss_version; mtx_unlock(&pr->pr_mtx); return (version); } } mtx_unlock(&pr->pr_mtx); } mtx_lock(&osname_lock); version = linux_oss_version; mtx_unlock(&osname_lock); return (version); } int linux_set_oss_version(struct thread *td, int oss_version) { struct prison *pr; struct linux_prison *lpr; pr = linux_get_prison(td); if (pr != NULL) { lpr = (struct linux_prison *)pr->pr_linux; lpr->pr_oss_version = oss_version; mtx_unlock(&pr->pr_mtx); } else { mtx_lock(&osname_lock); linux_oss_version = oss_version; mtx_unlock(&osname_lock); } return (0); } #ifdef DEBUG 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=.<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 */ Index: head/sys/compat/linux/linux_misc.c =================================================================== --- head/sys/compat/linux/linux_misc.c (revision 156873) +++ head/sys/compat/linux/linux_misc.c (revision 156874) @@ -1,1418 +1,1419 @@ /*- * 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_mac.h" #include #include #include #if defined(__i386__) || defined(__alpha__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include #ifdef __i386__ #include #endif #ifdef __alpha__ #define BSD_TO_LINUX_SIGNAL(sig) (sig) #else #define BSD_TO_LINUX_SIGNAL(sig) \ (((sig) <= LINUX_SIGTBLSZ) ? bsd_to_linux_signal[_SIG_IDX(sig)] : sig) #endif #ifndef __alpha__ 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, -1 }; #endif /*!__alpha__*/ 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_ulong totalbig; l_ulong freebig; l_uint mem_unit; char _f[6]; /* Pads structure to 64 bytes */ }; #ifndef __alpha__ 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)); } #endif /*!__alpha__*/ #ifndef __alpha__ int linux_alarm(struct thread *td, struct linux_alarm_args *args) { struct itimerval it, old_it; int error; #ifdef DEBUG if (ldebug(alarm)) printf(ARGS(alarm, "%u"), args->secs); #endif if (args->secs > 100000000) return (EINVAL); it.it_value.tv_sec = (long)args->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); } #endif /*!__alpha__*/ 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__) || defined(__alpha__) 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; LCONVPATHEXIST(td, args->library, &library); #ifdef DEBUG if (ldebug(uselib)) printf(ARGS(uselib, "%s"), library); #endif a_out = NULL; locked = 0; vp = NULL; /* * XXX: This code should make use of vn_open(), rather than doing * all this stuff itself. */ NDINIT(&ni, LOOKUP, ISOPEN|FOLLOW|LOCKLEAF, UIO_SYSSPACE, library, td); error = namei(&ni); LFREEPATH(library); if (error) goto cleanup; vp = ni.ni_vp; /* * XXX - This looks like a bogus check. A LOCKLEAF namei should not * succeed without returning a vnode. */ if (vp == NULL) { error = ENOEXEC; /* ?? */ goto cleanup; } NDFREE(&ni, NDF_ONLY_PNBUF); /* * From here on down, we have a locked vnode that must be unlocked. */ locked++; /* Writable? */ if (vp->v_writecount) { error = ETXTBSY; goto cleanup; } /* Executable? */ error = VOP_GETATTR(vp, &attr, td->td_ucred, td); if (error) goto cleanup; if ((vp->v_mount->mnt_flag & MNT_NOEXEC) || ((attr.va_mode & 0111) == 0) || (attr.va_type != VREG)) { 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. */ #ifdef MAC error = mac_check_vnode_open(td->td_ucred, vp, FREAD); if (error) goto cleanup; #endif error = VOP_OPEN(vp, FREAD, td->td_ucred, td, -1); 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); /* * Lock no longer needed */ locked = 0; VOP_UNLOCK(vp, 0, td); 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)) { PROC_UNLOCK(td->td_proc); error = ENOMEM; goto cleanup; } PROC_UNLOCK(td->td_proc); mp_fixme("Unlocked vflags access."); /* prevent more writers */ vp->v_vflag |= VV_TEXT; /* * 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, td); /* 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__ || __alpha__ */ 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); #ifdef DEBUG if (ldebug(select)) printf(LMSG("real select returns %d"), error); #endif if (error) { /* * See fs/select.c in the Linux kernel. Without this, * Maelstrom doesn't work. */ if (error == ERESTART) error = EINTR; 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 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); } #ifndef __alpha__ 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; } #endif /*!__alpha__*/ struct l_times_argv { l_long tms_utime; l_long tms_stime; l_long tms_cutime; l_long tms_cstime; }; #ifdef __alpha__ #define CLK_TCK 1024 /* Linux uses 1024 on alpha */ #else #define CLK_TCK 100 /* Linux uses 100 */ #endif #define CONVTCK(r) (r.tv_sec * CLK_TCK + r.tv_usec / (1000000 / CLK_TCK)) 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 p = td->td_proc; PROC_LOCK(p); calcru(p, &utime, &stime); 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); 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; } #ifdef __i386__ { const char *class; switch (cpu_class) { case CPUCLASS_686: class = "i686"; break; case CPUCLASS_586: class = "i586"; break; case CPUCLASS_486: class = "i486"; break; default: class = "i386"; } strlcpy(utsname.machine, class, LINUX_MAX_UTSNAME); } #elif defined(__amd64__) /* XXX: Linux can change 'personality'. */ #ifdef COMPAT_LINUX32 strlcpy(utsname.machine, "i686", LINUX_MAX_UTSNAME); #else strlcpy(utsname.machine, "x86_64", LINUX_MAX_UTSNAME); #endif /* COMPAT_LINUX32 */ #else /* something other than i386 or amd64 - assume we and Linux agree */ strlcpy(utsname.machine, machine, LINUX_MAX_UTSNAME); #endif /* __i386__ */ strlcpy(utsname.domainname, domainname, 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); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ #define __WCLONE 0x80000000 #ifndef __alpha__ int linux_waitpid(struct thread *td, struct linux_waitpid_args *args) { int error, options, tmpstat; #ifdef DEBUG if (ldebug(waitpid)) printf(ARGS(waitpid, "%d, %p, %d"), args->pid, (void *)args->status, args->options); #endif options = (args->options & (WNOHANG | WUNTRACED)); /* WLINUXCLONE should be equal to __WCLONE, but we make sure */ if (args->options & __WCLONE) options |= WLINUXCLONE; error = kern_wait(td, args->pid, &tmpstat, options, NULL); if (error) return error; if (args->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); return copyout(&tmpstat, args->status, sizeof(int)); } return 0; } #endif /*!__alpha__*/ int linux_wait4(struct thread *td, struct linux_wait4_args *args) { int error, options, tmpstat; struct rusage ru, *rup; struct proc *p; #ifdef DEBUG if (ldebug(wait4)) printf(ARGS(wait4, "%d, %p, %d, %p"), args->pid, (void *)args->status, args->options, (void *)args->rusage); #endif options = (args->options & (WNOHANG | WUNTRACED)); /* WLINUXCLONE should be equal to __WCLONE, but we make sure */ if (args->options & __WCLONE) options |= WLINUXCLONE; if (args->rusage != NULL) rup = &ru; else rup = NULL; error = kern_wait(td, args->pid, &tmpstat, options, rup); if (error) return error; p = td->td_proc; PROC_LOCK(p); sigqueue_delete(&p->p_sigqueue, SIGCHLD); PROC_UNLOCK(p); if (args->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, args->status, sizeof(int)); } if (args->rusage != NULL && error == 0) error = copyout(&ru, args->rusage, sizeof(ru)); return (error); } 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 if (args->mode & S_IFIFO) error = kern_mkfifo(td, path, UIO_SYSSPACE, args->mode); else error = kern_mknod(td, path, UIO_SYSSPACE, args->mode, args->dev); 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 #ifndef __alpha__ if (args->per != 0) return EINVAL; #endif /* 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))); } #ifndef __alpha__ 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); } #endif /*!__alpha__*/ int linux_setgroups(struct thread *td, struct linux_setgroups_args *args) { struct ucred *newcred, *oldcred; l_gid_t linux_gidset[NGROUPS]; gid_t *bsd_gidset; int ngrp, error; struct proc *p; ngrp = args->gidsetsize; if (ngrp < 0 || ngrp >= NGROUPS) return (EINVAL); error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t)); if (error) return (error); newcred = crget(); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; /* * 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 = suser_cred(oldcred, SUSER_ALLOWJAIL)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); 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); return (0); } int linux_getgroups(struct thread *td, struct linux_getgroups_args *args) { struct ucred *cred; l_gid_t linux_gidset[NGROUPS]; 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; while (ngrp < bsd_gidsetsz) { linux_gidset[ngrp] = bsd_gidset[ngrp + 1]; ngrp++; } if ((error = copyout(linux_gidset, args->grouplist, ngrp * sizeof(l_gid_t)))) return (error); td->td_retval[0] = ngrp; return (0); } #ifndef __alpha__ 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))); } #endif /*!__alpha__*/ 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 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->cmd == REBOOT_CAD_ON || args->cmd == REBOOT_CAD_OFF) return (0); bsd_args.opt = (args->cmd == REBOOT_HALT) ? RB_HALT : 0; return (reboot(td, &bsd_args)); } #ifndef __alpha__ /* * The FreeBSD native getpid(2), getgid(2) and getuid(2) also modify * td->td_retval[1] when COMPAT_43 is defined. This * globbers 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) { td->td_retval[0] = td->td_proc->p_pid; return (0); } int linux_getgid(struct thread *td, struct linux_getgid_args *args) { td->td_retval[0] = td->td_ucred->cr_rgid; return (0); } int linux_getuid(struct thread *td, struct linux_getuid_args *args) { td->td_retval[0] = td->td_ucred->cr_ruid; return (0); } #endif /*!__alpha__*/ int linux_getsid(struct thread *td, struct linux_getsid_args *args) { struct getsid_args bsd; 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; 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) { struct proc *p = td->td_proc; int name[2]; int error; name[0] = CTL_KERN; name[1] = KERN_HOSTNAME; if ((error = suser_cred(p->p_ucred, SUSER_ALLOWJAIL))) return (error); return (userland_sysctl(td, name, 2, 0, 0, 0, args->hostname, args->len, 0, 0)); } Index: head/sys/compat/linux/linux_signal.c =================================================================== --- head/sys/compat/linux/linux_signal.c (revision 156873) +++ head/sys/compat/linux/linux_signal.c (revision 156874) @@ -1,437 +1,439 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include "opt_compat.h" + #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include 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)) { #ifdef __alpha__ b = _SIG_IDX(l); #else b = linux_to_bsd_signal[_SIG_IDX(l)]; #endif 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)) { #ifdef __alpha__ l = _SIG_IDX(b); #else l = bsd_to_linux_signal[_SIG_IDX(b)]; #endif 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 <= 0 || linux_sig > LINUX_NSIG) return (EINVAL); osa = (linux_osa != NULL) ? &oact : NULL; if (linux_nsa != NULL) { nsa = &act; linux_to_bsd_sigaction(linux_nsa, nsa); } else nsa = NULL; #ifndef __alpha__ if (linux_sig <= LINUX_SIGTBLSZ) sig = linux_to_bsd_signal[_SIG_IDX(linux_sig)]; else #endif 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); } #ifndef __alpha__ 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); } #endif /*!__alpha__*/ 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); } #ifndef __alpha__ 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); } #endif /*!__alpha__*/ 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); } #ifndef __alpha__ 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))); } #endif /*!__alpha__*/ 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 (args->signum < 0 || args->signum > LINUX_NSIG) return EINVAL; #ifndef __alpha__ if (args->signum > 0 && args->signum <= LINUX_SIGTBLSZ) tmp.signum = linux_to_bsd_signal[_SIG_IDX(args->signum)]; else #endif tmp.signum = args->signum; tmp.pid = args->pid; return (kill(td, &tmp)); } Index: head/sys/compat/linux/linux_socket.c =================================================================== --- head/sys/compat/linux/linux_socket.c (revision 156873) +++ head/sys/compat/linux/linux_socket.c (revision 156874) @@ -1,1261 +1,1262 @@ /*- * 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 __FBSDID("$FreeBSD$"); /* XXX we use functions that might not exist. */ +#include "opt_compat.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #endif #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include 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 MALLOC(kosa, struct osockaddr *, alloclen, mtype, M_WAITOK); if ((error = copyin(osa, kosa, *osalen))) goto out; bdom = linux_to_bsd_domain(kosa->sa_family); if (bdom == -1) { error = EINVAL; 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); 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); } #ifndef __alpha__ 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); } 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_sendit(struct thread *td, int s, struct msghdr *mp, int flags, enum uio_seg segflg) { struct mbuf *control; 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; if (mp->msg_control != NULL) { struct cmsghdr *cmsg; if (mp->msg_controllen < sizeof(struct cmsghdr)) { error = EINVAL; goto bad; } error = sockargs(&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); if (error) goto bad; cmsg = mtod(control, struct cmsghdr *); cmsg->cmsg_level = linux_to_bsd_sockopt_level(cmsg->cmsg_level); } else control = NULL; error = kern_sendit(td, s, mp, linux_to_bsd_msg_flags(flags), control, segflg); bad: 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, 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 linux_socket_args linux_args; struct socket_args /* { int domain; int type; int protocol; } */ bsd_args; int error; int retval_socket; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.protocol = linux_args.protocol; bsd_args.type = linux_args.type; bsd_args.domain = linux_to_bsd_domain(linux_args.domain); if (bsd_args.domain == -1) return (EINVAL); retval_socket = socket(td, &bsd_args); if (bsd_args.type == SOCK_RAW && (bsd_args.protocol == IPPROTO_RAW || bsd_args.protocol == 0) && bsd_args.domain == AF_INET && retval_socket >= 0) { /* 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 if the sysctl value is set to 1. */ if (bsd_args.domain == PF_INET6 && retval_socket >= 0 #ifndef KLD_MODULE /* * XXX: Avoid undefined symbol error with an IPv4 only * kernel. */ && ip6_v6only #endif ) { 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 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 linux_bind_args linux_args; struct sockaddr *sa; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); error = linux_getsockaddr(&sa, PTRIN(linux_args.name), linux_args.namelen); if (error) return (error); return (kern_bind(td, linux_args.s, sa)); } struct linux_connect_args { int s; l_uintptr_t name; int namelen; }; int linux_connect(struct thread *, struct linux_connect_args *); #endif /* !__alpha__*/ int linux_connect(struct thread *td, struct linux_connect_args *args) { struct linux_connect_args linux_args; struct socket *so; struct sockaddr *sa; u_int fflag; int error; #ifdef __alpha__ bcopy(args, &linux_args, sizeof(linux_args)); #else if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); #endif /* __alpha__ */ error = linux_getsockaddr(&sa, (struct osockaddr *)PTRIN(linux_args.name), linux_args.namelen); if (error) return (error); error = kern_connect(td, linux_args.s, sa); 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. */ NET_LOCK_GIANT(); error = fgetsock(td, linux_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); } NET_UNLOCK_GIANT(); return (error); } #ifndef __alpha__ struct linux_listen_args { int s; int backlog; }; static int linux_listen(struct thread *td, struct linux_listen_args *args) { struct linux_listen_args linux_args; struct listen_args /* { int s; int backlog; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.backlog = linux_args.backlog; return (listen(td, &bsd_args)); } 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) { struct linux_accept_args linux_args; struct accept_args /* { int s; struct sockaddr * __restrict name; socklen_t * __restrict anamelen; } */ bsd_args; struct close_args /* { int fd; } */ c_args; int error, fd; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; /* XXX: */ bsd_args.name = (struct sockaddr * __restrict)PTRIN(linux_args.addr); bsd_args.anamelen = PTRIN(linux_args.namelen);/* XXX */ error = accept(td, &bsd_args); bsd_to_linux_sockaddr((struct sockaddr *)bsd_args.name); if (error) return (error); if (linux_args.addr) { error = linux_sa_put(PTRIN(linux_args.addr)); if (error) { c_args.fd = td->td_retval[0]; (void)close(td, &c_args); 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. * Ignore any errors, because we already have an open fd. */ fd = td->td_retval[0]; (void)kern_fcntl(td, fd, F_SETFL, 0); td->td_retval[0] = fd; return (0); } 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 linux_getsockname_args linux_args; struct getsockname_args /* { int fdes; struct sockaddr * __restrict asa; socklen_t * __restrict alen; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.fdes = linux_args.s; /* XXX: */ bsd_args.asa = (struct sockaddr * __restrict)PTRIN(linux_args.addr); bsd_args.alen = PTRIN(linux_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(linux_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 linux_getpeername_args linux_args; struct getpeername_args /* { int fdes; caddr_t asa; int *alen; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.fdes = linux_args.s; bsd_args.asa = (struct sockaddr *)PTRIN(linux_args.addr); bsd_args.alen = (int *)PTRIN(linux_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(linux_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 linux_socketpair_args linux_args; struct socketpair_args /* { int domain; int type; int protocol; int *rsv; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.domain = linux_to_bsd_domain(linux_args.domain); if (bsd_args.domain == -1) return (EINVAL); bsd_args.type = linux_args.type; bsd_args.protocol = linux_args.protocol; bsd_args.rsv = (int *)PTRIN(linux_args.rsv); return (socketpair(td, &bsd_args)); } 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 linux_send_args linux_args; struct sendto_args /* { int s; caddr_t buf; int len; int flags; caddr_t to; int tolen; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.buf = (caddr_t)PTRIN(linux_args.msg); bsd_args.len = linux_args.len; bsd_args.flags = linux_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 linux_recv_args linux_args; struct recvfrom_args /* { int s; caddr_t buf; int len; int flags; struct sockaddr *from; socklen_t fromlenaddr; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.buf = (caddr_t)PTRIN(linux_args.msg); bsd_args.len = linux_args.len; bsd_args.flags = linux_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 linux_sendto_args linux_args; struct msghdr msg; struct iovec aiov; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); if (linux_check_hdrincl(td, linux_args.s) == 0) /* IP_HDRINCL set, tweak the packet before sending */ return (linux_sendto_hdrincl(td, &linux_args)); 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.iov_base = PTRIN(linux_args.msg); aiov.iov_len = linux_args.len; error = linux_sendit(td, linux_args.s, &msg, linux_args.flags, 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 linux_recvfrom_args linux_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(args, &linux_args, sizeof(linux_args)))) return (error); if ((error = copyin(PTRIN(linux_args.fromlen), &len, sizeof(size_t)))) return (error); bsd_args.s = linux_args.s; bsd_args.buf = PTRIN(linux_args.buf); bsd_args.len = linux_args.len; bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags); /* XXX: */ bsd_args.from = (struct sockaddr * __restrict)PTRIN(linux_args.from); bsd_args.fromlenaddr = PTRIN(linux_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 (linux_args.from) { error = linux_sa_put((struct osockaddr *) PTRIN(linux_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 linux_sendmsg_args linux_args; struct msghdr msg; struct iovec *iov; int error; /* XXXTJR sendmsg is broken on amd64 */ error = copyin(args, &linux_args, sizeof(linux_args)); if (error) return (error); error = copyin(PTRIN(linux_args.msg), &msg, sizeof(msg)); if (error) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_iov = iov; msg.msg_flags = 0; error = linux_sendit(td, linux_args.s, &msg, linux_args.flags, UIO_USERSPACE); free(iov, M_IOV); 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 linux_recvmsg_args linux_args; struct recvmsg_args /* { int s; struct msghdr *msg; int flags; } */ bsd_args; struct msghdr msg; struct cmsghdr *cmsg; int error; /* XXXTJR recvmsg is broken on amd64 */ if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.msg = PTRIN(linux_args.msg); bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags); if (msg.msg_name) { linux_to_bsd_sockaddr((struct sockaddr *)msg.msg_name, msg.msg_namelen); error = recvmsg(td, &bsd_args); bsd_to_linux_sockaddr((struct sockaddr *)msg.msg_name); } else error = recvmsg(td, &bsd_args); if (error) return (error); if (bsd_args.msg->msg_control != NULL && bsd_args.msg->msg_controllen > 0) { cmsg = (struct cmsghdr*)bsd_args.msg->msg_control; cmsg->cmsg_level = bsd_to_linux_sockopt_level(cmsg->cmsg_level); } error = copyin(PTRIN(linux_args.msg), &msg, sizeof(msg)); if (error) return (error); if (msg.msg_name && msg.msg_namelen > 2) error = linux_sa_put(msg.msg_name); return (error); } struct linux_shutdown_args { int s; int how; }; static int linux_shutdown(struct thread *td, struct linux_shutdown_args *args) { struct linux_shutdown_args linux_args; struct shutdown_args /* { int s; int how; } */ bsd_args; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.how = linux_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 linux_setsockopt_args linux_args; struct setsockopt_args /* { int s; int level; int name; caddr_t val; int valsize; } */ bsd_args; int error, name; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level); switch (bsd_args.level) { case SOL_SOCKET: name = linux_to_bsd_so_sockopt(linux_args.optname); break; case IPPROTO_IP: name = linux_to_bsd_ip_sockopt(linux_args.optname); break; case IPPROTO_TCP: /* Linux TCP option values match BSD's */ name = linux_args.optname; break; default: name = -1; break; } if (name == -1) return (EINVAL); bsd_args.name = name; bsd_args.val = PTRIN(linux_args.optval); bsd_args.valsize = linux_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 linux_getsockopt_args linux_args; struct getsockopt_args /* { int s; int level; int name; caddr_t val; int *avalsize; } */ bsd_args; int error, name; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); bsd_args.s = linux_args.s; bsd_args.level = linux_to_bsd_sockopt_level(linux_args.level); switch (bsd_args.level) { case SOL_SOCKET: name = linux_to_bsd_so_sockopt(linux_args.optname); break; case IPPROTO_IP: name = linux_to_bsd_ip_sockopt(linux_args.optname); break; case IPPROTO_TCP: /* Linux TCP option values match BSD's */ name = linux_args.optname; break; default: name = -1; break; } if (name == -1) return (EINVAL); bsd_args.name = name; bsd_args.val = PTRIN(linux_args.optval); bsd_args.avalsize = PTRIN(linux_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); } int linux_socketcall(struct thread *td, struct linux_socketcall_args *args) { void *arg = (void *)(intptr_t)args->args; 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)); } uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what); return (ENOSYS); } #endif /*!__alpha__*/ Index: head/sys/compat/linux/linux_stats.c =================================================================== --- head/sys/compat/linux/linux_stats.c (revision 156873) +++ head/sys/compat/linux/linux_stats.c (revision 156874) @@ -1,476 +1,477 @@ /*- * 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include /* * 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 = uminor(buf->st_dev) & 0xf; tbuf.st_rdev = buf->st_rdev & 0xff; } dev_relthread(dev); } } } #endif static int newstat_copyout(struct stat *buf, void *ubuf) { struct l_newstat tbuf; bzero(&tbuf, sizeof(tbuf)); tbuf.st_dev = uminor(buf->st_dev) | (umajor(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_atime = buf->st_atime; tbuf.st_mtime = buf->st_mtime; tbuf.st_ctime = buf->st_ctime; 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 = kern_stat(td, path, UIO_SYSSPACE, &buf); if (!error && strlen(path) > strlen("/dev/pts/") && !strncmp(path, "/dev/pts/", strlen("/dev/pts/")) && path[9] >= '0' && path[9] <= '9') { /* * 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. */ buf.st_rdev = (136 << 8); } 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 = 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); 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_atime = buf->st_atime; lbuf.st_mtime = buf->st_mtime; lbuf.st_ctime = buf->st_ctime; 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; int error; #ifdef DEBUG if (ldebug(stat)) printf(ARGS(stat, "%s, *"), args->path); #endif error = kern_stat(td, args->path, UIO_SYSSPACE, &buf); if (error) return (error); return(stat_copyout(&buf, args->up)); } int linux_lstat(struct thread *td, struct linux_lstat_args *args) { struct stat buf; int error; #ifdef DEBUG if (ldebug(lstat)) printf(ARGS(lstat, "%s, *"), args->path); #endif error = kern_lstat(td, args->path, UIO_SYSSPACE, &buf); if (error) return (error); 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; LCONVPATHEXIST(td, args->path, &path); #ifdef DEBUG if (ldebug(statfs)) printf(ARGS(statfs, "%s, *"), path); #endif error = kern_statfs(td, path, UIO_SYSSPACE, &bsd_statfs); LFREEPATH(path); if (error) return (error); bsd_to_linux_statfs(&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 = uminor(buf->st_dev) | (umajor(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_atime = buf->st_atime; lbuf.st_mtime = buf->st_mtime; lbuf.st_ctime = buf->st_ctime; 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 = kern_stat(td, filename, UIO_SYSSPACE, &buf); if (!error && strlen(filename) > strlen("/dev/pts/") && !strncmp(filename, "/dev/pts/", strlen("/dev/pts/")) && filename[9] >= '0' && filename[9] <= '9') { /* * Linux checks major and minors of the slave device to make * sure it's a pty deivce, so let's make him believe it is. */ buf.st_rdev = (136 << 8); } 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 = 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); if (!error) error = stat64_copyout(&buf, args->statbuf); return (error); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ Index: head/sys/compat/linux/linux_sysctl.c =================================================================== --- head/sys/compat/linux/linux_sysctl.c (revision 156873) +++ head/sys/compat/linux/linux_sysctl.c (revision 156874) @@ -1,141 +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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #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); } Index: head/sys/compat/linux/linux_uid16.c =================================================================== --- head/sys/compat/linux/linux_uid16.c (revision 156873) +++ head/sys/compat/linux/linux_uid16.c (revision 156874) @@ -1,297 +1,299 @@ /*- * 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include 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[NGROUPS]; 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) return (EINVAL); 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 = p->p_ucred; /* * 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 = suser_cred(oldcred, SUSER_ALLOWJAIL)) != 0) { PROC_UNLOCK(p); crfree(newcred); return (error); } crcopy(newcred, oldcred); 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); return (0); } int linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args) { struct ucred *cred; l_gid16_t linux_gidset[NGROUPS]; 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; while (ngrp < bsd_gidsetsz) { linux_gidset[ngrp] = bsd_gidset[ngrp + 1]; ngrp++; } error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t)); 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 globbers 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)); }