/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * STREAMS system calls. */ int getmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *flagsp); int putmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int flags); int getpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *prip, int *flagsp); int putpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int pri, int flags); static int msgio(int fdes, struct strbuf *ctl, struct strbuf *data, int *rval, int mode, unsigned char *prip, int *flagsp); int getmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *flagsp) { int error; int localflags; int realflags = 0; unsigned char pri = 0; int rv = 0; /* * Convert between old flags (localflags) and new flags (realflags). */ if (copyin(flagsp, &localflags, sizeof (*flagsp))) return (set_errno(EFAULT)); switch (localflags) { case 0: realflags = MSG_ANY; break; case RS_HIPRI: realflags = MSG_HIPRI; break; default: return (set_errno(EINVAL)); } if ((error = msgio(fdes, ctl, data, &rv, FREAD, &pri, &realflags)) == 0) { /* * massage realflags based on localflags. */ if (realflags == MSG_HIPRI) localflags = RS_HIPRI; else localflags = 0; if (copyout(&localflags, flagsp, sizeof (*flagsp))) error = EFAULT; } if (error != 0) return (set_errno(error)); return (rv); } int putmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int flags) { unsigned char pri = 0; int realflags; int error; int rv = 0; switch (flags) { case RS_HIPRI: realflags = MSG_HIPRI; break; case (RS_HIPRI|MSG_XPG4): realflags = MSG_HIPRI|MSG_XPG4; break; case MSG_XPG4: realflags = MSG_BAND|MSG_XPG4; break; case 0: realflags = MSG_BAND; break; default: return (set_errno(EINVAL)); } error = msgio(fdes, ctl, data, &rv, FWRITE, &pri, &realflags); if (error != 0) return (set_errno(error)); return (rv); } int getpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *prip, int *flagsp) { int error; int flags; int intpri; unsigned char pri; int rv = 0; if (copyin(flagsp, &flags, sizeof (flags))) return (set_errno(EFAULT)); if (copyin(prip, &intpri, sizeof (intpri))) return (set_errno(EFAULT)); if ((intpri > 255) || (intpri < 0)) return (set_errno(EINVAL)); pri = (unsigned char)intpri; error = msgio(fdes, ctl, data, &rv, FREAD, &pri, &flags); if (error != 0) return (set_errno(error)); if (copyout(&flags, flagsp, sizeof (flags))) return (set_errno(EFAULT)); intpri = (int)pri; if (copyout(&intpri, prip, sizeof (intpri))) return (set_errno(EFAULT)); return (rv); } int putpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int intpri, int flags) { unsigned char pri; int rv = 0; int error; if ((intpri > 255) || (intpri < 0)) return (set_errno(EINVAL)); pri = (unsigned char)intpri; error = msgio(fdes, ctl, data, &rv, FWRITE, &pri, &flags); if (error != 0) return (set_errno(error)); return (rv); } /* * Common code for getmsg and putmsg calls: check permissions, * copy in args, do preliminary setup, and switch to * appropriate stream routine. */ static int msgio(int fdes, struct strbuf *ctl, struct strbuf *data, int *rval, int mode, unsigned char *prip, int *flagsp) { file_t *fp; vnode_t *vp; struct strbuf msgctl, msgdata; int error; int flag; klwp_t *lwp = ttolwp(curthread); rval_t rv; if ((fp = getf(fdes)) == NULL) return (EBADF); if ((fp->f_flag & mode) == 0) { releasef(fdes); return (EBADF); } vp = fp->f_vnode; if (vp->v_type == VFIFO) { if (vp->v_stream) { /* * must use sd_vnode, could be named pipe */ (void) fifo_vfastoff(vp->v_stream->sd_vnode); } else { releasef(fdes); return (ENOSTR); } } else if ((vp->v_type != VCHR && vp->v_type != VSOCK) || vp->v_stream == NULL) { releasef(fdes); return (ENOSTR); } if ((ctl != NULL) && copyin(ctl, &msgctl, sizeof (struct strbuf))) { releasef(fdes); return (EFAULT); } if ((data != NULL) && copyin(data, &msgdata, sizeof (struct strbuf))) { releasef(fdes); return (EFAULT); } if (mode == FREAD) { if (ctl == NULL) msgctl.maxlen = -1; if (data == NULL) msgdata.maxlen = -1; flag = fp->f_flag; rv.r_val1 = 0; if (vp->v_type == VSOCK) { error = sock_getmsg(vp, &msgctl, &msgdata, prip, flagsp, flag, &rv); } else { error = strgetmsg(vp, &msgctl, &msgdata, prip, flagsp, flag, &rv); } *rval = rv.r_val1; if (error != 0) { releasef(fdes); return (error); } if (lwp != NULL) lwp->lwp_ru.msgrcv++; if (((ctl != NULL) && copyout(&msgctl, ctl, sizeof (struct strbuf))) || ((data != NULL) && copyout(&msgdata, data, sizeof (struct strbuf)))) { releasef(fdes); return (EFAULT); } releasef(fdes); return (0); } /* * FWRITE case */ if (ctl == NULL) msgctl.len = -1; if (data == NULL) msgdata.len = -1; flag = fp->f_flag; if (vp->v_type == VSOCK) { error = sock_putmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag); } else { error = strputmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag); } releasef(fdes); if (error == 0 && lwp != NULL) lwp->lwp_ru.msgsnd++; return (error); } #if defined(_LP64) && defined(_SYSCALL32) static int msgio32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int *rval, int mode, unsigned char *prip, int *flagsp); int getmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t *flagsp) { int error; int32_t localflags; int realflags = 0; unsigned char pri = 0; int rv = 0; /* * Convert between old flags (localflags) and new flags (realflags). */ if (copyin(flagsp, &localflags, sizeof (*flagsp))) return (set_errno(EFAULT)); switch (localflags) { case 0: realflags = MSG_ANY; break; case RS_HIPRI: realflags = MSG_HIPRI; break; default: return (set_errno(EINVAL)); } if ((error = msgio32(fdes, ctl, data, &rv, FREAD, &pri, &realflags)) == 0) { /* * massage realflags based on localflags. */ if (realflags == MSG_HIPRI) localflags = RS_HIPRI; else localflags = 0; if (copyout(&localflags, flagsp, sizeof (*flagsp))) error = EFAULT; } if (error != 0) return (set_errno(error)); return (rv); } int putmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t flags) { unsigned char pri = 0; int realflags; int error; int rv = 0; switch (flags) { case RS_HIPRI: realflags = MSG_HIPRI; break; case (RS_HIPRI|MSG_XPG4): realflags = MSG_HIPRI|MSG_XPG4; break; case MSG_XPG4: realflags = MSG_BAND|MSG_XPG4; break; case 0: realflags = MSG_BAND; break; default: return (set_errno(EINVAL)); } error = msgio32(fdes, ctl, data, &rv, FWRITE, &pri, &realflags); if (error != 0) return (set_errno(error)); return (rv); } int getpmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t *prip, int32_t *flagsp) { int error; int32_t flags; int32_t intpri; unsigned char pri; int rv = 0; if (copyin(flagsp, &flags, sizeof (*flagsp))) return (set_errno(EFAULT)); if (copyin(prip, &intpri, sizeof (intpri))) return (set_errno(EFAULT)); if ((intpri > 255) || (intpri < 0)) return (set_errno(EINVAL)); pri = (unsigned char)intpri; error = msgio32(fdes, ctl, data, &rv, FREAD, &pri, &flags); if (error != 0) return (set_errno(error)); if (copyout(&flags, flagsp, sizeof (flags))) return (set_errno(EFAULT)); intpri = (int)pri; if (copyout(&intpri, prip, sizeof (intpri))) return (set_errno(EFAULT)); return (rv); } int putpmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t intpri, int32_t flags) { unsigned char pri; int rv = 0; int error; if ((intpri > 255) || (intpri < 0)) return (set_errno(EINVAL)); pri = (unsigned char)intpri; error = msgio32(fdes, ctl, data, &rv, FWRITE, &pri, &flags); if (error != 0) return (set_errno(error)); return (rv); } /* * Common code for getmsg and putmsg calls: check permissions, * copy in args, do preliminary setup, and switch to * appropriate stream routine. */ static int msgio32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int *rval, int mode, unsigned char *prip, int *flagsp) { file_t *fp; vnode_t *vp; struct strbuf32 msgctl32, msgdata32; struct strbuf msgctl, msgdata; int error; int flag; klwp_t *lwp = ttolwp(curthread); rval_t rv; if ((fp = getf(fdes)) == NULL) return (EBADF); if ((fp->f_flag & mode) == 0) { releasef(fdes); return (EBADF); } vp = fp->f_vnode; if (vp->v_type == VFIFO) { if (vp->v_stream) { /* * must use sd_vnode, could be named pipe */ (void) fifo_vfastoff(vp->v_stream->sd_vnode); } else { releasef(fdes); return (ENOSTR); } } else if ((vp->v_type != VCHR && vp->v_type != VSOCK) || vp->v_stream == NULL) { releasef(fdes); return (ENOSTR); } if (ctl != NULL) { if (copyin(ctl, &msgctl32, sizeof (msgctl32))) { releasef(fdes); return (EFAULT); } msgctl.len = msgctl32.len; msgctl.maxlen = msgctl32.maxlen; msgctl.buf = (caddr_t)(uintptr_t)msgctl32.buf; } if (data != NULL) { if (copyin(data, &msgdata32, sizeof (msgdata32))) { releasef(fdes); return (EFAULT); } msgdata.len = msgdata32.len; msgdata.maxlen = msgdata32.maxlen; msgdata.buf = (caddr_t)(uintptr_t)msgdata32.buf; } if (mode == FREAD) { if (ctl == NULL) msgctl.maxlen = -1; if (data == NULL) msgdata.maxlen = -1; flag = fp->f_flag; rv.r_val1 = 0; if (vp->v_type == VSOCK) { error = sock_getmsg(vp, &msgctl, &msgdata, prip, flagsp, flag, &rv); } else { error = strgetmsg(vp, &msgctl, &msgdata, prip, flagsp, flag, &rv); } *rval = rv.r_val1; if (error != 0) { releasef(fdes); return (error); } if (lwp != NULL) lwp->lwp_ru.msgrcv++; if (ctl != NULL) { /* XX64 - range check */ msgctl32.len = msgctl.len; msgctl32.maxlen = msgctl.maxlen; msgctl32.buf = (caddr32_t)(uintptr_t)msgctl.buf; if (copyout(&msgctl32, ctl, sizeof (msgctl32))) { releasef(fdes); return (EFAULT); } } if (data != NULL) { /* XX64 - range check */ msgdata32.len = msgdata.len; msgdata32.maxlen = msgdata.maxlen; msgdata32.buf = (caddr32_t)(uintptr_t)msgdata.buf; if (copyout(&msgdata32, data, sizeof (msgdata32))) { releasef(fdes); return (EFAULT); } } releasef(fdes); return (0); } /* * FWRITE case */ if (ctl == NULL) msgctl.len = -1; if (data == NULL) msgdata.len = -1; flag = fp->f_flag; if (vp->v_type == VSOCK) { error = sock_putmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag); } else { error = strputmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag); } releasef(fdes); if (error == 0 && lwp != NULL) lwp->lwp_ru.msgsnd++; return (error); } #endif /* _LP64 && _SYSCALL32 */