/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Solaris DDI STREAMS utility routines (PSARC/2003/648). * * Please see the appropriate section 9F manpage for documentation. */ #include #include #include #include #include #include #include #include #include void merror(queue_t *wq, mblk_t *mp, int error) { if ((mp = mexchange(wq, mp, 1, M_ERROR, -1)) == NULL) return; *mp->b_rptr = (uchar_t)error; qreply(wq, mp); } void mioc2ack(mblk_t *mp, mblk_t *dp, size_t count, int rval) { struct iocblk *iocp = (struct iocblk *)mp->b_rptr; mblk_t *odp = mp->b_cont; /* allows freemsg() to be a tail call */ DB_TYPE(mp) = M_IOCACK; iocp->ioc_count = count; iocp->ioc_error = 0; iocp->ioc_rval = rval; mp->b_cont = dp; if (dp != NULL) dp->b_wptr = dp->b_rptr + count; freemsg(odp); } void miocack(queue_t *wq, mblk_t *mp, int count, int rval) { struct iocblk *iocp = (struct iocblk *)mp->b_rptr; DB_TYPE(mp) = M_IOCACK; iocp->ioc_count = count; iocp->ioc_error = 0; iocp->ioc_rval = rval; qreply(wq, mp); } void miocnak(queue_t *wq, mblk_t *mp, int count, int error) { struct iocblk *iocp = (struct iocblk *)mp->b_rptr; DB_TYPE(mp) = M_IOCNAK; iocp->ioc_count = count; iocp->ioc_error = error; qreply(wq, mp); } mblk_t * mexchange(queue_t *wq, mblk_t *mp, size_t size, uchar_t type, int32_t primtype) { if (mp == NULL || MBLKSIZE(mp) < size || DB_REF(mp) > 1) { freemsg(mp); if ((mp = allocb(size, BPRI_LO)) == NULL) { if (wq != NULL) { if ((mp = allocb(1, BPRI_HI)) != NULL) merror(wq, mp, ENOSR); } return (NULL); } } DB_TYPE(mp) = type; mp->b_rptr = DB_BASE(mp); mp->b_wptr = mp->b_rptr + size; if (primtype >= 0) *(int32_t *)mp->b_rptr = primtype; return (mp); } size_t msgsize(mblk_t *mp) { size_t n = 0; for (; mp != NULL; mp = mp->b_cont) n += MBLKL(mp); return (n); } void mcopymsg(mblk_t *mp, void *bufp) { caddr_t dest = bufp; mblk_t *bp; size_t n; for (bp = mp; bp != NULL; bp = bp->b_cont) { n = MBLKL(bp); bcopy(bp->b_rptr, dest, n); dest += n; } freemsg(mp); } void mcopyin(mblk_t *mp, void *private, size_t size, void *useraddr) { struct copyreq *cp = (struct copyreq *)mp->b_rptr; if (useraddr != NULL) { cp->cq_addr = (caddr_t)useraddr; } else { ASSERT(DB_TYPE(mp) == M_IOCTL); ASSERT(mp->b_cont != NULL); ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT); cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; } cp->cq_flag = 0; cp->cq_size = size; cp->cq_private = (mblk_t *)private; DB_TYPE(mp) = M_COPYIN; mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); if (mp->b_cont != NULL) { freemsg(mp->b_cont); mp->b_cont = NULL; } } void mcopyout(mblk_t *mp, void *private, size_t size, void *useraddr, mblk_t *dp) { struct copyreq *cp = (struct copyreq *)mp->b_rptr; if (useraddr != NULL) cp->cq_addr = (caddr_t)useraddr; else { ASSERT(DB_TYPE(mp) == M_IOCTL); ASSERT(mp->b_cont != NULL); ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT); cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; } cp->cq_flag = 0; cp->cq_size = size; cp->cq_private = (mblk_t *)private; DB_TYPE(mp) = M_COPYOUT; mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); if (dp != NULL) { if (mp->b_cont != NULL) freemsg(mp->b_cont); mp->b_cont = dp; mp->b_cont->b_wptr = mp->b_cont->b_rptr + size; } } int miocpullup(mblk_t *iocmp, size_t size) { struct iocblk *iocp = (struct iocblk *)iocmp->b_rptr; mblk_t *datamp = iocmp->b_cont; mblk_t *newdatamp; /* * We'd like to be sure that DB_TYPE(iocmp) == M_IOCTL, but some * nitwit routines like ttycommon_ioctl() always reset the type of * legitimate M_IOCTL messages to M_IOCACK as a "courtesy" to the * caller, even when the routine does not understand the M_IOCTL. * The ttycommon_ioctl() routine does us the additional favor of * clearing ioc_count, so we cannot rely on it having a correct * size either (blissfully, ttycommon_ioctl() does not screw with * TRANSPARENT messages, so we can still sanity check for that). */ ASSERT(MBLKL(iocmp) == sizeof (struct iocblk)); if (MBLKL(iocmp) != sizeof (struct iocblk)) { cmn_err(CE_WARN, "miocpullup: passed mblk_t %p is not an ioctl" " mblk_t", (void *)iocmp); return (EINVAL); } if (iocp->ioc_count == TRANSPARENT) return (EINVAL); if (size == 0) return (0); if (datamp == NULL) return (EINVAL); if (MBLKL(datamp) >= size) return (0); newdatamp = msgpullup(datamp, size); if (newdatamp == NULL) { if (msgdsize(datamp) < size) return (EINVAL); return (ENOMEM); } iocmp->b_cont = newdatamp; freemsg(datamp); return (0); } /* Copy userdata into a new mblk_t */ mblk_t * mcopyinuio(struct stdata *stp, uio_t *uiop, ssize_t iosize, ssize_t maxblk, int *errorp) { mblk_t *head = NULL, **tail = &head; size_t offset = stp->sd_wroff; size_t tail_len = stp->sd_tail; if (iosize == INFPSZ || iosize > uiop->uio_resid) iosize = uiop->uio_resid; if (maxblk == INFPSZ) maxblk = iosize; /* Nothing to do in these cases, so we're done */ if (iosize < 0 || maxblk < 0 || (maxblk == 0 && iosize > 0)) goto done; if (stp->sd_flag & STRCOPYCACHED) uiop->uio_extflg |= UIO_COPY_CACHED; /* * We will enter the loop below if iosize is 0; it will allocate an * empty message block and call uiomove(9F) which will just return. * We could avoid that with an extra check but would only slow * down the much more likely case where iosize is larger than 0. */ do { ssize_t blocksize; mblk_t *mp; blocksize = MIN(iosize, maxblk); ASSERT(blocksize >= 0); if ((mp = allocb_cred(offset + blocksize + tail_len, CRED(), curproc->p_pid)) == NULL) { *errorp = ENOMEM; return (head); } mp->b_rptr += offset; mp->b_wptr = mp->b_rptr + blocksize; *tail = mp; tail = &mp->b_cont; /* uiomove(9F) either returns 0 or EFAULT */ if ((*errorp = uiomove(mp->b_rptr, (size_t)blocksize, UIO_WRITE, uiop)) != 0) { ASSERT(*errorp != ENOMEM); freemsg(head); return (NULL); } iosize -= blocksize; } while (iosize > 0); done: *errorp = 0; return (head); }