/* $NetBSD: ibcs2_misc.c,v 1.69 2004/03/09 03:18:03 atatat Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * from: Header: sun_misc.c,v 1.16 93/04/07 02:46:27 torek Exp * * @(#)sun_misc.c 8.1 (Berkeley) 6/18/93 */ /* * Copyright (c) 1994, 1995, 1998 Scott Bartram * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * from: Header: sun_misc.c,v 1.16 93/04/07 02:46:27 torek Exp * * @(#)sun_misc.c 8.1 (Berkeley) 6/18/93 */ /* * IBCS2 compatibility module. * * IBCS2 system calls that are implemented differently in BSD are * handled here. */ #include __KERNEL_RCSID(0, "$NetBSD: ibcs2_misc.c,v 1.69 2004/03/09 03:18:03 atatat Exp $"); #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 #if defined(__i386__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include int ibcs2_sys_ulimit(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_ulimit_args /* { syscallarg(int) cmd; syscallarg(int) newlimit; } */ *uap = v; struct proc *p = l->l_proc; #ifdef notyet int error; struct rlimit rl; struct sys_setrlimit_args sra; #endif #define IBCS2_GETFSIZE 1 #define IBCS2_SETFSIZE 2 #define IBCS2_GETPSIZE 3 #define IBCS2_GETDTABLESIZE 4 switch (SCARG(uap, cmd)) { case IBCS2_GETFSIZE: *retval = p->p_rlimit[RLIMIT_FSIZE].rlim_cur; return 0; case IBCS2_SETFSIZE: /* XXX - fix this */ #ifdef notyet rl.rlim_cur = SCARG(uap, newlimit); SCARG(&sra, which) = RLIMIT_FSIZE; SCARG(&sra, rlp) = &rl; error = setrlimit(p, &sra, retval); if (!error) *retval = p->p_rlimit[RLIMIT_FSIZE].rlim_cur; else DPRINTF(("failed ")); return error; #else *retval = SCARG(uap, newlimit); return 0; #endif case IBCS2_GETPSIZE: *retval = p->p_rlimit[RLIMIT_RSS].rlim_cur; /* XXX */ return 0; case IBCS2_GETDTABLESIZE: SCARG(uap, cmd) = IBCS2_SC_OPEN_MAX; return ibcs2_sys_sysconf(l, uap, retval); default: return ENOSYS; } } int ibcs2_sys_waitsys(l, v, retval) struct lwp *l; void *v; register_t *retval; { #if defined(__i386__) struct ibcs2_sys_waitsys_args /* { syscallarg(int) a1; syscallarg(int) a2; syscallarg(int) a3; } */ *uap = v; #endif struct proc *p = l->l_proc; int error; struct sys_wait4_args w4; caddr_t sg; sg = stackgap_init(p, 0); SCARG(&w4, rusage) = NULL; SCARG(&w4, status) = stackgap_alloc(p, &sg, sizeof(int)); #if defined(__i386__) #define WAITPID_EFLAGS 0x8c4 /* OF, SF, ZF, PF */ if ((l->l_md.md_regs->tf_eflags & WAITPID_EFLAGS) == WAITPID_EFLAGS) { /* waitpid */ SCARG(&w4, pid) = SCARG(uap, a1); SCARG(&w4, options) = SCARG(uap, a3); } else { #endif /* wait */ SCARG(&w4, pid) = WAIT_ANY; SCARG(&w4, options) = 0; #if defined(__i386__) } #endif if ((error = sys_wait4(l, &w4, retval)) != 0) return error; return copyin((caddr_t)SCARG(&w4, status), (caddr_t)&retval[1], sizeof(int)); } int ibcs2_sys_execv(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_execv_args /* { syscallarg(const char *) path; syscallarg(char **) argp; } */ *uap = v; struct proc *p = l->l_proc; struct sys_execve_args ap; caddr_t sg; sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); SCARG(&ap, path) = SCARG(uap, path); SCARG(&ap, argp) = SCARG(uap, argp); SCARG(&ap, envp) = NULL; return sys_execve(l, &ap, retval); } int ibcs2_sys_execve(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_execve_args /* { syscallarg(const char *) path; syscallarg(char **) argp; syscallarg(char **) envp; } */ *uap = v; struct proc *p = l->l_proc; struct sys_execve_args ap; caddr_t sg; sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); SCARG(&ap, path) = SCARG(uap, path); SCARG(&ap, argp) = SCARG(uap, argp); SCARG(&ap, envp) = SCARG(uap, envp); return sys_execve(l, &ap, retval); } int ibcs2_sys_umount(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_umount_args /* { syscallarg(char *) name; } */ *uap = v; struct sys_unmount_args um; SCARG(&um, path) = SCARG(uap, name); SCARG(&um, flags) = 0; return sys_unmount(l, &um, retval); } int ibcs2_sys_mount(l, v, retval) struct lwp *l; void *v; register_t *retval; { #ifdef notyet struct ibcs2_sys_mount_args /* { syscallarg(char *) special; syscallarg(char *) dir; syscallarg(int) flags; syscallarg(int) fstype; syscallarg(char *) data; syscallarg(int) len; } */ *uap = v; int oflags = SCARG(uap, flags), nflags, error; char fsname[MFSNAMELEN]; if (oflags & (IBCS2_MS_NOSUB | IBCS2_MS_SYS5)) return (EINVAL); if ((oflags & IBCS2_MS_NEWTYPE) == 0) return (EINVAL); nflags = 0; if (oflags & IBCS2_MS_RDONLY) nflags |= MNT_RDONLY; if (oflags & IBCS2_MS_NOSUID) nflags |= MNT_NOSUID; if (oflags & IBCS2_MS_REMOUNT) nflags |= MNT_UPDATE; SCARG(uap, flags) = nflags; if (error = copyinstr((caddr_t)SCARG(uap, type), fsname, sizeof fsname, (u_int *)0)) return (error); if (strncmp(fsname, "4.2", sizeof fsname) == 0) { SCARG(uap, type) = (caddr_t)STACK_ALLOC(); if (error = copyout("ffs", SCARG(uap, type), sizeof("ffs"))) return (error); } else if (strncmp(fsname, "nfs", sizeof fsname) == 0) { struct ibcs2_nfs_args sna; struct sockaddr_in sain; struct nfs_args na; struct sockaddr sa; if (error = copyin(SCARG(uap, data), &sna, sizeof sna)) return (error); if (error = copyin(sna.addr, &sain, sizeof sain)) return (error); memcpy(&sa, &sain, sizeof sa); sa.sa_len = sizeof(sain); SCARG(uap, data) = (caddr_t)STACK_ALLOC(); na.addr = (struct sockaddr *)((int)SCARG(uap, data) + sizeof na); na.sotype = SOCK_DGRAM; na.proto = IPPROTO_UDP; na.fh = (nfsv2fh_t *)sna.fh; na.flags = sna.flags; na.wsize = sna.wsize; na.rsize = sna.rsize; na.timeo = sna.timeo; na.retrans = sna.retrans; na.hostname = sna.hostname; if (error = copyout(&sa, na.addr, sizeof sa)) return (error); if (error = copyout(&na, SCARG(uap, data), sizeof na)) return (error); } return (sys_mount(p, uap, retval)); #else return EINVAL; #endif } /* * Read iBCS2-style directory entries. We suck them into kernel space so * that they can be massaged before being copied out to user code. Like * SunOS, we squish out `empty' entries. * * This is quite ugly, but what do you expect from compatibility code? */ int ibcs2_sys_getdents(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_getdents_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(int) nbytes; } */ *uap = v; struct proc *p = l->l_proc; struct dirent *bdp; struct vnode *vp; caddr_t inp, buf; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* iBCS2-format */ int resid, ibcs2_reclen;/* iBCS2-format */ struct file *fp; struct uio auio; struct iovec aiov; struct ibcs2_dirent idb; off_t off; /* true file offset */ size_t buflen; int error, eofflag; off_t *cookiebuf = NULL, *cookie; int ncookies; /* getvnode() will use the descriptor for us */ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) { error = EBADF; goto out1; } vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) { error = EINVAL; goto out1; } buflen = min(MAXBSIZE, (size_t)SCARG(uap, nbytes)); buf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); off = fp->f_offset; 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_procp = p; auio.uio_resid = buflen; auio.uio_offset = off; /* * First we read into the malloc'ed buffer, then * we massage it into user space, one record at a time. */ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, &ncookies); if (error) goto out; inp = buf; outp = SCARG(uap, buf); resid = SCARG(uap, nbytes); if ((len = buflen - auio.uio_resid) == 0) goto eof; for (cookie = cookiebuf; len > 0; len -= reclen) { bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (reclen & 3) panic("ibcs2_getdents: bad reclen"); if ((*cookie >> 32) != 0) { compat_offseterr(vp, "ibcs2_getdents"); error = EINVAL; goto out; } if (bdp->d_fileno == 0) { inp += reclen; /* it is a hole; squish it out */ off = *cookie++; continue; } ibcs2_reclen = IBCS2_RECLEN(&idb, bdp->d_namlen); if (reclen > len || resid < ibcs2_reclen) { /* entry too big for buffer, so just stop */ outp++; break; } off = *cookie++; /* each entry points to the next */ /* * Massage in place to make a iBCS2-shaped dirent (otherwise * we have to worry about touching user memory outside of * the copyout() call). */ idb.d_ino = (ibcs2_ino_t)bdp->d_fileno; idb.d_off = (ibcs2_off_t)off; idb.d_reclen = (u_short)ibcs2_reclen; strlcpy(idb.d_name, bdp->d_name, sizeof(idb.d_name)); error = copyout(&idb, outp, ibcs2_reclen); if (error) goto out; /* advance past this real entry */ inp += reclen; /* advance output past iBCS2-shaped entry */ outp += ibcs2_reclen; resid -= ibcs2_reclen; } /* if we squished out the whole block, try again */ if (outp == SCARG(uap, buf)) goto again; fp->f_offset = off; /* update the vnode offset */ eof: *retval = SCARG(uap, nbytes) - resid; out: VOP_UNLOCK(vp, 0); if (cookiebuf) free(cookiebuf, M_TEMP); free(buf, M_TEMP); out1: FILE_UNUSE(fp, p); return (error); } int ibcs2_sys_read(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_read_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) nbytes; } */ *uap = v; struct proc *p = l->l_proc; struct dirent *bdp; struct vnode *vp; caddr_t inp, buf; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* iBCS2-format */ int resid, ibcs2_reclen;/* iBCS2-format */ struct file *fp; struct uio auio; struct iovec aiov; struct ibcs2_direct { ibcs2_ino_t ino; char name[14]; } idb; size_t buflen; int error, eofflag; size_t size; off_t *cookiebuf = NULL, *cookie; off_t off; /* true file offset */ int ncookies; /* getvnode() will use the descriptor for us */ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) { if (error == EINVAL) return sys_read(l, uap, retval); else return error; } if ((fp->f_flag & FREAD) == 0) { error = EBADF; goto out1; } vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) { FILE_UNUSE(fp, p); return sys_read(l, uap, retval); } buflen = min(MAXBSIZE, max(DEV_BSIZE, (size_t)SCARG(uap, nbytes))); buf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); off = fp->f_offset; 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_procp = p; auio.uio_resid = buflen; auio.uio_offset = off; /* * First we read into the malloc'ed buffer, then * we massage it into user space, one record at a time. */ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, &ncookies); if (error) goto out; inp = buf; outp = SCARG(uap, buf); resid = SCARG(uap, nbytes); if ((len = buflen - auio.uio_resid) == 0) goto eof; for (cookie = cookiebuf; len > 0 && resid > 0; len -= reclen) { bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (reclen & 3) panic("ibcs2_sys_read"); off = *cookie++; /* each entry points to the next */ if ((off >> 32) != 0) { error = EINVAL; goto out; } if (bdp->d_fileno == 0) { inp += reclen; /* it is a hole; squish it out */ continue; } ibcs2_reclen = 16; if (reclen > len || resid < ibcs2_reclen) { /* entry too big for buffer, so just stop */ outp++; break; } /* * Massage in place to make a iBCS2-shaped dirent (otherwise * we have to worry about touching user memory outside of * the copyout() call). * * TODO: if length(filename) > 14, then break filename into * multiple entries and set inode = 0xffff except last */ idb.ino = (bdp->d_fileno > 0xfffe) ? 0xfffe : bdp->d_fileno; (void)copystr(bdp->d_name, idb.name, 14, &size); memset(idb.name + size, 0, 14 - size); error = copyout(&idb, outp, ibcs2_reclen); if (error) goto out; /* advance past this real entry */ inp += reclen; /* advance output past iBCS2-shaped entry */ outp += ibcs2_reclen; resid -= ibcs2_reclen; } /* if we squished out the whole block, try again */ if (outp == SCARG(uap, buf)) goto again; fp->f_offset = off; /* update the vnode offset */ eof: *retval = SCARG(uap, nbytes) - resid; out: VOP_UNLOCK(vp, 0); if (cookiebuf) free(cookiebuf, M_TEMP); free(buf, M_TEMP); out1: FILE_UNUSE(fp, p); return (error); } int ibcs2_sys_mknod(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_mknod_args /* { syscallarg(const char *) path; syscallarg(int) mode; syscallarg(int) dev; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_CREAT(p, &sg, SCARG(uap, path)); if (S_ISFIFO(SCARG(uap, mode))) { struct sys_mkfifo_args ap; SCARG(&ap, path) = SCARG(uap, path); SCARG(&ap, mode) = SCARG(uap, mode); return sys_mkfifo(l, uap, retval); } else { struct sys_mknod_args ap; SCARG(&ap, path) = SCARG(uap, path); SCARG(&ap, mode) = SCARG(uap, mode); SCARG(&ap, dev) = SCARG(uap, dev); return sys_mknod(l, &ap, retval); } } int ibcs2_sys_getgroups(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_getgroups_args /* { syscallarg(int) gidsetsize; syscallarg(ibcs2_gid_t *) gidset; } */ *uap = v; struct proc *p = l->l_proc; int error, i; ibcs2_gid_t iset[NGROUPS_MAX]; gid_t nset[NGROUPS_MAX]; struct sys_getgroups_args sa; int gidsetsize; caddr_t sg = stackgap_init(p, 0); gidsetsize = SCARG(uap, gidsetsize); if (gidsetsize > NGROUPS_MAX) return EINVAL; SCARG(&sa, gidsetsize) = gidsetsize; if (gidsetsize) { SCARG(&sa, gidset) = stackgap_alloc(p, &sg, NGROUPS_MAX * sizeof(gid_t *)); } if ((error = sys_getgroups(l, &sa, retval)) != 0) return error; if (gidsetsize) { gidsetsize = retval[0]; if (gidsetsize < 0) gidsetsize = 0; error = copyin((caddr_t)SCARG(&sa, gidset), (caddr_t)nset, sizeof(gid_t) * gidsetsize); if (error) return error; for (i = 0; i < gidsetsize; i++) iset[i] = (ibcs2_gid_t)nset[i]; error = copyout((caddr_t)iset, (caddr_t)SCARG(uap, gidset), sizeof(ibcs2_gid_t) * retval[0]); } return error; } int ibcs2_sys_setgroups(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_setgroups_args /* { syscallarg(int) gidsetsize; syscallarg(ibcs2_gid_t *) gidset; } */ *uap = v; struct proc *p = l->l_proc; int error, i; ibcs2_gid_t iset[NGROUPS_MAX]; struct sys_setgroups_args sa; gid_t gp[NGROUPS_MAX], *ngid; caddr_t sg = stackgap_init(p, 0); SCARG(&sa, gidsetsize) = SCARG(uap, gidsetsize); if (SCARG(uap, gidsetsize) > NGROUPS_MAX) return EINVAL; if (SCARG(&sa, gidsetsize)) { error = copyin((caddr_t)SCARG(uap, gidset), (caddr_t)iset, sizeof(ibcs2_gid_t) * SCARG(uap, gidsetsize)); if (error) return error; } for (i = 0; i < SCARG(&sa, gidsetsize); i++) gp[i]= (gid_t)iset[i]; ngid = stackgap_alloc(p, &sg, NGROUPS_MAX * sizeof(gid_t)); error = copyout(gp, ngid, SCARG(&sa, gidsetsize) * sizeof(gid_t)); if (error) return error; SCARG(&sa, gidset) = ngid; return sys_setgroups(l, &sa, retval); } int ibcs2_sys_setuid(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_setuid_args /* { syscallarg(int) uid; } */ *uap = v; struct sys_setuid_args sa; SCARG(&sa, uid) = (uid_t)SCARG(uap, uid); return sys_setuid(l, &sa, retval); } int ibcs2_sys_setgid(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_setgid_args /* { syscallarg(int) gid; } */ *uap = v; struct sys_setgid_args sa; SCARG(&sa, gid) = (gid_t)SCARG(uap, gid); return sys_setgid(l, &sa, retval); } int xenix_sys_ftime(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct xenix_sys_ftime_args /* { syscallarg(struct xenix_timeb *) tp; } */ *uap = v; struct timeval tv; struct xenix_timeb itb; microtime(&tv); itb.time = tv.tv_sec; itb.millitm = (tv.tv_usec / 1000); /* NetBSD has no kernel notion of timezone -- fake it. */ itb.timezone = 0; itb.dstflag = 0; return copyout((caddr_t)&itb, (caddr_t)SCARG(uap, tp), xenix_timeb_len); } int ibcs2_sys_time(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_time_args /* { syscallarg(ibcs2_time_t *) tp; } */ *uap = v; struct proc *p = l->l_proc; struct timeval tv; microtime(&tv); *retval = tv.tv_sec; if (p->p_emuldata == IBCS2_EXEC_XENIX && SCARG(uap, tp)) return copyout((caddr_t)&tv.tv_sec, (caddr_t)SCARG(uap, tp), sizeof(ibcs2_time_t)); else return 0; } int ibcs2_sys_pathconf(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_pathconf_args /* { syscallarg(char *) path; syscallarg(int) name; } */ *uap = v; SCARG(uap, name)++; /* iBCS2 _PC_* defines are offset by one */ return sys_pathconf(l, uap, retval); } int ibcs2_sys_fpathconf(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_fpathconf_args /* { syscallarg(int) fd; syscallarg(int) name; } */ *uap = v; SCARG(uap, name)++; /* iBCS2 _PC_* defines are offset by one */ return sys_fpathconf(l, uap, retval); } int ibcs2_sys_sysconf(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_sysconf_args /* { syscallarg(int) name; } */ *uap = v; struct proc *p = l->l_proc; int mib[2], value, error; size_t len; struct sys_getrlimit_args ga; switch(SCARG(uap, name)) { case IBCS2_SC_ARG_MAX: mib[1] = KERN_ARGMAX; break; case IBCS2_SC_CHILD_MAX: { caddr_t sg = stackgap_init(p, 0); SCARG(&ga, which) = RLIMIT_NPROC; SCARG(&ga, rlp) = stackgap_alloc(p, &sg, sizeof(struct rlimit *)); if ((error = sys_getrlimit(l, &ga, retval)) != 0) return error; *retval = SCARG(&ga, rlp)->rlim_cur; return 0; } case IBCS2_SC_CLK_TCK: *retval = hz; return 0; case IBCS2_SC_NGROUPS_MAX: mib[1] = KERN_NGROUPS; break; case IBCS2_SC_OPEN_MAX: { caddr_t sg = stackgap_init(p, 0); SCARG(&ga, which) = RLIMIT_NOFILE; SCARG(&ga, rlp) = stackgap_alloc(p, &sg, sizeof(struct rlimit *)); if ((error = sys_getrlimit(l, &ga, retval)) != 0) return error; *retval = SCARG(&ga, rlp)->rlim_cur; return 0; } case IBCS2_SC_JOB_CONTROL: mib[1] = KERN_JOB_CONTROL; break; case IBCS2_SC_SAVED_IDS: mib[1] = KERN_SAVED_IDS; break; case IBCS2_SC_VERSION: mib[1] = KERN_POSIX1; break; case IBCS2_SC_PASS_MAX: *retval = 128; /* XXX - should we create PASS_MAX ? */ return 0; case IBCS2_SC_XOPEN_VERSION: *retval = 2; /* XXX: What should that be? */ return 0; default: return EINVAL; } mib[0] = CTL_KERN; len = sizeof(value); /* * calling into sysctl with superuser privs, but we don't mind, * 'cause we're only querying a value. */ error = old_sysctl(&mib[0], 2, &value, &len, NULL, 0, NULL); if (error) return (error); *retval = value; return 0; } int ibcs2_sys_alarm(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_alarm_args /* { syscallarg(unsigned) sec; } */ *uap = v; struct proc *p = l->l_proc; int error; struct itimerval *itp, *oitp; struct sys_setitimer_args sa; caddr_t sg = stackgap_init(p, 0); itp = stackgap_alloc(p, &sg, sizeof(*itp)); oitp = stackgap_alloc(p, &sg, sizeof(*oitp)); timerclear(&itp->it_interval); itp->it_value.tv_sec = SCARG(uap, sec); itp->it_value.tv_usec = 0; SCARG(&sa, which) = ITIMER_REAL; SCARG(&sa, itv) = itp; SCARG(&sa, oitv) = oitp; error = sys_setitimer(l, &sa, retval); if (error) return error; if (oitp->it_value.tv_usec) oitp->it_value.tv_sec++; *retval = oitp->it_value.tv_sec; return 0; } int ibcs2_sys_getmsg(l, v, retval) struct lwp *l; void *v; register_t *retval; { #ifdef notyet struct ibcs2_sys_getmsg_args /* { syscallarg(int) fd; syscallarg(struct ibcs2_stropts *) ctl; syscallarg(struct ibcs2_stropts *) dat; syscallarg(int *) flags; } */ *uap = v; #endif return 0; } int ibcs2_sys_putmsg(l, v, retval) struct lwp *l; void *v; register_t *retval; { #ifdef notyet struct ibcs2_sys_putmsg_args /* { syscallarg(int) fd; syscallarg(struct ibcs2_stropts *) ctl; syscallarg(struct ibcs2_stropts *) dat; syscallarg(int) flags; } */ *uap = v; #endif return 0; } int ibcs2_sys_times(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_times_args /* { syscallarg(struct tms *) tp; } */ *uap = v; struct proc *p = l->l_proc; int error; struct sys_getrusage_args ga; struct tms tms; struct timeval t; caddr_t sg = stackgap_init(p, 0); struct rusage *ru = stackgap_alloc(p, &sg, sizeof(*ru)); #define CONVTCK(r) (r.tv_sec * hz + r.tv_usec / (1000000 / hz)) SCARG(&ga, who) = RUSAGE_SELF; SCARG(&ga, rusage) = ru; error = sys_getrusage(l, &ga, retval); if (error) return error; tms.tms_utime = CONVTCK(ru->ru_utime); tms.tms_stime = CONVTCK(ru->ru_stime); SCARG(&ga, who) = RUSAGE_CHILDREN; error = sys_getrusage(l, &ga, retval); if (error) return error; tms.tms_cutime = CONVTCK(ru->ru_utime); tms.tms_cstime = CONVTCK(ru->ru_stime); microtime(&t); *retval = CONVTCK(t); return copyout((caddr_t)&tms, (caddr_t)SCARG(uap, tp), sizeof(struct tms)); } int ibcs2_sys_stime(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_stime_args /* { syscallarg(long *) timep; } */ *uap = v; struct proc *p = l->l_proc; int error; struct sys_settimeofday_args sa; caddr_t sg = stackgap_init(p, 0); struct timeval *tvp; tvp = stackgap_alloc(p, &sg, sizeof(*SCARG(&sa, tv))); SCARG(&sa, tzp) = NULL; error = copyin((caddr_t)SCARG(uap, timep), (void *)&tvp->tv_sec, sizeof(long)); if (error) return error; tvp->tv_usec = 0; SCARG(&sa, tv) = tvp; if ((error = sys_settimeofday(l, &sa, retval)) != 0) return EPERM; return 0; } int ibcs2_sys_utime(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_utime_args /* { syscallarg(const char *) path; syscallarg(struct ibcs2_utimbuf *) buf; } */ *uap = v; struct proc *p = l->l_proc; int error; struct sys_utimes_args sa; struct timeval *tp; caddr_t sg = stackgap_init(p, 0); tp = stackgap_alloc(p, &sg, 2 * sizeof(struct timeval *)); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); SCARG(&sa, path) = SCARG(uap, path); if (SCARG(uap, buf)) { struct ibcs2_utimbuf ubuf; error = copyin((caddr_t)SCARG(uap, buf), (caddr_t)&ubuf, sizeof(ubuf)); if (error) return error; tp[0].tv_sec = ubuf.actime; tp[0].tv_usec = 0; tp[1].tv_sec = ubuf.modtime; tp[1].tv_usec = 0; SCARG(&sa, tptr) = tp; } else SCARG(&sa, tptr) = NULL; return sys_utimes(l, &sa, retval); } int ibcs2_sys_nice(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_nice_args /* { syscallarg(int) incr; } */ *uap = v; struct proc *p = l->l_proc; struct sys_setpriority_args sa; SCARG(&sa, which) = PRIO_PROCESS; SCARG(&sa, who) = 0; SCARG(&sa, prio) = p->p_nice - NZERO + SCARG(uap, incr); if (sys_setpriority(l, &sa, retval) != 0) return EPERM; *retval = p->p_nice - NZERO; return 0; } /* * iBCS2 getpgrp, setpgrp, setsid, and setpgid */ int ibcs2_sys_pgrpsys(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_pgrpsys_args /* { syscallarg(int) type; syscallarg(caddr_t) dummy; syscallarg(int) pid; syscallarg(int) pgid; } */ *uap = v; struct proc *p = l->l_proc; switch (SCARG(uap, type)) { case 0: /* getpgrp */ *retval = p->p_pgrp->pg_id; return 0; case 1: /* setpgrp */ { struct sys_setpgid_args sa; SCARG(&sa, pid) = 0; SCARG(&sa, pgid) = 0; sys_setpgid(l, &sa, retval); *retval = p->p_pgrp->pg_id; return 0; } case 2: /* setpgid */ { struct sys_setpgid_args sa; SCARG(&sa, pid) = SCARG(uap, pid); SCARG(&sa, pgid) = SCARG(uap, pgid); return sys_setpgid(l, &sa, retval); } case 3: /* setsid */ return sys_setsid(l, NULL, retval); default: return EINVAL; } } /* * XXX - need to check for nested calls */ int ibcs2_sys_plock(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_plock_args /* { syscallarg(int) cmd; } */ *uap = v; struct proc *p = l->l_proc; #define IBCS2_UNLOCK 0 #define IBCS2_PROCLOCK 1 #define IBCS2_TEXTLOCK 2 #define IBCS2_DATALOCK 4 if (suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; switch(SCARG(uap, cmd)) { case IBCS2_UNLOCK: case IBCS2_PROCLOCK: case IBCS2_TEXTLOCK: case IBCS2_DATALOCK: return 0; /* XXX - TODO */ } return EINVAL; } int ibcs2_sys_uadmin(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_uadmin_args /* { syscallarg(int) cmd; syscallarg(int) func; syscallarg(caddr_t) data; } */ *uap = v; struct proc *p = l->l_proc; int error; #define SCO_A_REBOOT 1 #define SCO_A_SHUTDOWN 2 #define SCO_A_REMOUNT 4 #define SCO_A_CLOCK 8 #define SCO_A_SETCONFIG 128 #define SCO_A_GETDEV 130 #define SCO_AD_HALT 0 #define SCO_AD_BOOT 1 #define SCO_AD_IBOOT 2 #define SCO_AD_PWRDOWN 3 #define SCO_AD_PWRNAP 4 #define SCO_AD_PANICBOOT 1 #define SCO_AD_GETBMAJ 0 #define SCO_AD_GETCMAJ 1 /* XXX: is this the right place for this call? */ if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) return (error); switch(SCARG(uap, cmd)) { case SCO_A_REBOOT: case SCO_A_SHUTDOWN: switch(SCARG(uap, func)) { case SCO_AD_HALT: case SCO_AD_PWRDOWN: case SCO_AD_PWRNAP: cpu_reboot(RB_HALT, NULL); case SCO_AD_BOOT: case SCO_AD_IBOOT: cpu_reboot(RB_AUTOBOOT, NULL); } return EINVAL; case SCO_A_REMOUNT: case SCO_A_CLOCK: case SCO_A_SETCONFIG: return 0; case SCO_A_GETDEV: return EINVAL; /* XXX - TODO */ } return EINVAL; } int ibcs2_sys_sysfs(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_sysfs_args /* { syscallarg(int) cmd; syscallarg(caddr_t) d1; syscallarg(char *) buf; } */ *uap = v; #define IBCS2_GETFSIND 1 #define IBCS2_GETFSTYP 2 #define IBCS2_GETNFSTYP 3 switch(SCARG(uap, cmd)) { case IBCS2_GETFSIND: case IBCS2_GETFSTYP: case IBCS2_GETNFSTYP: break; } return EINVAL; /* XXX - TODO */ } int xenix_sys_rdchk(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct xenix_sys_rdchk_args /* { syscallarg(int) fd; } */ *uap = v; struct proc *p = l->l_proc; int error; struct sys_ioctl_args sa; caddr_t sg = stackgap_init(p, 0); SCARG(&sa, fd) = SCARG(uap, fd); SCARG(&sa, com) = FIONREAD; SCARG(&sa, data) = stackgap_alloc(p, &sg, sizeof(int)); if ((error = sys_ioctl(l, &sa, retval)) != 0) return error; *retval = (*((int*)SCARG(&sa, data))) ? 1 : 0; return 0; } int xenix_sys_chsize(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct xenix_sys_chsize_args /* { syscallarg(int) fd; syscallarg(long) size; } */ *uap = v; struct sys_ftruncate_args sa; SCARG(&sa, fd) = SCARG(uap, fd); SCARG(&sa, pad) = 0; SCARG(&sa, length) = SCARG(uap, size); return sys_ftruncate(l, &sa, retval); } int xenix_sys_nap(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct xenix_sys_nap_args /* { syscallarg(long) millisec; } */ *uap = v; struct proc *p = l->l_proc; int error; struct sys_nanosleep_args na; struct timespec *rqtp; struct timespec *rmtp; caddr_t sg = stackgap_init(p, 0); rqtp = stackgap_alloc(p, &sg, sizeof(struct timespec)); rmtp = stackgap_alloc(p, &sg, sizeof(struct timespec)); rqtp->tv_sec = 0; rqtp->tv_nsec = SCARG(uap, millisec) * 1000; SCARG(&na, rqtp) = rqtp; SCARG(&na, rmtp) = rmtp; if ((error = sys_nanosleep(l, &na, retval)) != 0) return error; *retval = rmtp->tv_nsec / 1000; return 0; } int ibcs2_sys_unlink(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_unlink_args /* { syscallarg(const char *) path; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); return sys_unlink(l, uap, retval); } int ibcs2_sys_chdir(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_chdir_args /* { syscallarg(const char *) path; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); return sys_chdir(l, uap, retval); } int ibcs2_sys_chmod(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_chmod_args /* { syscallarg(const char *) path; syscallarg(int) mode; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); return sys_chmod(l, uap, retval); } int ibcs2_sys_chown(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_chown_args /* { syscallarg(const char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); return sys___posix_chown(l, uap, retval); } int ibcs2_sys_rmdir(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_rmdir_args /* { syscallarg(const char *) path; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); return sys_rmdir(l, uap, retval); } int ibcs2_sys_mkdir(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_mkdir_args /* { syscallarg(const char *) path; syscallarg(int) mode; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_CREAT(p, &sg, SCARG(uap, path)); return sys_mkdir(l, uap, retval); } int ibcs2_sys_symlink(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_symlink_args /* { syscallarg(const char *) path; syscallarg(const char *) link; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, path)); CHECK_ALT_CREAT(p, &sg, SCARG(uap, link)); return sys_symlink(l, uap, retval); } int ibcs2_sys_rename(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_rename_args /* { syscallarg(const char *) from; syscallarg(const char *) to; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_EXIST(p, &sg, SCARG(uap, from)); CHECK_ALT_CREAT(p, &sg, SCARG(uap, to)); return sys___posix_rename(l, uap, retval); } int ibcs2_sys_readlink(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_readlink_args /* { syscallarg(const char *) path; syscallarg(char *) buf; syscallarg(int) count; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); CHECK_ALT_SYMLINK(p, &sg, SCARG(uap, path)); return sys_readlink(l, uap, retval); } /* * mmap compat code borrowed from svr4/svr4_misc.c */ int ibcs2_sys_mmap(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_mmap_args /* { syscallarg(ibcs2_caddr_t) addr; syscallarg(ibcs2_size_t) len; syscallarg(int) prot; syscallarg(int) flags; syscallarg(int) fd; syscallarg(ibcs2_off_t) off; } */ *uap = v; struct sys_mmap_args mm; #define _MAP_NEW 0x80000000 /* XXX why? */ if (SCARG(uap, prot) & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) return EINVAL; if (SCARG(uap, len) == 0) return EINVAL; SCARG(&mm, prot) = SCARG(uap, prot); SCARG(&mm, len) = SCARG(uap, len); SCARG(&mm, flags) = SCARG(uap, flags) & ~_MAP_NEW; SCARG(&mm, fd) = SCARG(uap, fd); SCARG(&mm, addr) = SCARG(uap, addr); SCARG(&mm, pos) = SCARG(uap, off); return sys_mmap(l, &mm, retval); } int ibcs2_sys_memcntl(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_memcntl_args /* { syscallarg(ibcs2_caddr_t) addr; syscallarg(ibcs2_size_t) len; syscallarg(int) cmd; syscallarg(ibcs2_caddr_t) arg; syscallarg(int) attr; syscallarg(int) mask; } */ *uap = v; switch (SCARG(uap, cmd)) { case IBCS2_MC_SYNC: { struct sys___msync13_args msa; SCARG(&msa, addr) = SCARG(uap, addr); SCARG(&msa, len) = SCARG(uap, len); SCARG(&msa, flags) = (int)SCARG(uap, arg); return sys___msync13(l, &msa, retval); } #ifdef IBCS2_MC_ADVISE /* supported? */ case IBCS2_MC_ADVISE: { struct sys_madvise_args maa; SCARG(&maa, addr) = SCARG(uap, addr); SCARG(&maa, len) = SCARG(uap, len); SCARG(&maa, behav) = (int)SCARG(uap, arg); return sys_madvise(l, &maa, retval); } #endif case IBCS2_MC_LOCK: case IBCS2_MC_UNLOCK: case IBCS2_MC_LOCKAS: case IBCS2_MC_UNLOCKAS: return EOPNOTSUPP; default: return ENOSYS; } } int ibcs2_sys_gettimeofday(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_gettimeofday_args /* { syscallarg(struct timeval *) tp; } */ *uap = v; if (SCARG(uap, tp)) { struct timeval atv; microtime(&atv); return copyout(&atv, SCARG(uap, tp), sizeof (atv)); } return 0; } int ibcs2_sys_settimeofday(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_settimeofday_args /* { syscallarg(struct timeval *) tp; } */ *uap = v; struct sys_settimeofday_args ap; SCARG(&ap, tv) = SCARG(uap, tp); SCARG(&ap, tzp) = NULL; return sys_settimeofday(l, &ap, retval); } int ibcs2_sys_scoinfo(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct ibcs2_sys_scoinfo_args /* { syscallarg(struct scoutsname *) bp; syscallarg(int) len; } */ *uap = v; struct proc *p = l->l_proc; caddr_t sg = stackgap_init(p, 0); struct scoutsname *utsp = stackgap_alloc(p, &sg, sizeof(struct scoutsname)); memset(utsp, 0, sizeof(struct scoutsname)); strncpy(utsp->sysname, ostype, 8); strncpy(utsp->nodename, hostname, 8); strncpy(utsp->release, osrelease, 15); strncpy(utsp->kid, "kernel id 1", 19); strncpy(utsp->machine, machine, 8); strncpy(utsp->bustype, "pci", 8); strncpy(utsp->serial, "1234", 9); utsp->origin = 0; utsp->oem = 0; strncpy(utsp->nusers, "unlim", 8); utsp->ncpu = 1; return copyout((caddr_t)utsp, (caddr_t)SCARG(uap, bp), sizeof(struct scoutsname)); } #define X_LK_UNLCK 0 #define X_LK_LOCK 1 #define X_LK_NBLCK 20 #define X_LK_RLCK 3 #define X_LK_NBRLCK 4 #define X_LK_GETLK 5 #define X_LK_SETLK 6 #define X_LK_SETLKW 7 #define X_LK_TESTLK 8 int xenix_sys_locking(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct xenix_sys_locking_args /* { syscallarg(int) fd; syscallarg(int) blk; syscallarg(int) size; } */ *uap = v; struct proc *p = l->l_proc; struct sys_fcntl_args fa; struct flock *flp; struct filedesc *fdp = p->p_fd; struct file *fp; int cmd; off_t off; caddr_t sg = stackgap_init(p, 0); switch SCARG(uap, blk) { case X_LK_GETLK: case X_LK_SETLK: case X_LK_SETLKW: return ibcs2_sys_fcntl(l, v, retval); } if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); off = fp->f_offset; flp = stackgap_alloc(p, &sg, sizeof(*flp)); flp->l_start = off; switch SCARG(uap, blk) { case X_LK_UNLCK: cmd = F_SETLK; flp->l_type = F_UNLCK; break; case X_LK_LOCK: cmd = F_SETLKW; flp->l_type = F_WRLCK; break; case X_LK_NBRLCK: cmd = F_SETLK; flp->l_type = F_RDLCK; break; case X_LK_NBLCK: cmd = F_SETLK; flp->l_type = F_WRLCK; break; default: return EINVAL; } flp->l_len = SCARG(uap, size); flp->l_whence = SEEK_SET; SCARG(&fa, fd) = SCARG(uap, fd); SCARG(&fa, cmd) = cmd; SCARG(&fa, arg) = (void *)flp; return sys_fcntl(l, &fa, retval); }