stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
Erik Nordmark | de8c4a1 | 2009-02-12 08:42:06 -0800 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 7 | * |
| 8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| 9 | * or http://www.opensolaris.org/os/licensing. |
| 10 | * See the License for the specific language governing permissions |
| 11 | * and limitations under the License. |
| 12 | * |
| 13 | * When distributing Covered Code, include this CDDL HEADER in each |
| 14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| 15 | * If applicable, add the following below this CDDL HEADER, with the |
| 16 | * fields enclosed by brackets "[]" replaced with your own identifying |
| 17 | * information: Portions Copyright [yyyy] [name of copyright owner] |
| 18 | * |
| 19 | * CDDL HEADER END |
| 20 | */ |
| 21 | /* |
Erik Nordmark | de8c4a1 | 2009-02-12 08:42:06 -0800 | [diff] [blame] | 22 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 23 | * Use is subject to license terms. |
| 24 | */ |
| 25 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 26 | /* |
| 27 | * Solaris DDI STREAMS utility routines (PSARC/2003/648). |
| 28 | * |
| 29 | * Please see the appropriate section 9F manpage for documentation. |
| 30 | */ |
| 31 | |
| 32 | #include <sys/types.h> |
| 33 | #include <sys/systm.h> |
| 34 | #include <sys/errno.h> |
| 35 | #include <sys/stream.h> |
| 36 | #include <sys/stropts.h> |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 37 | #include <sys/strsubr.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 38 | #include <sys/strsun.h> |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 39 | #include <sys/sysmacros.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 40 | #include <sys/cmn_err.h> |
| 41 | |
| 42 | void |
| 43 | merror(queue_t *wq, mblk_t *mp, int error) |
| 44 | { |
| 45 | if ((mp = mexchange(wq, mp, 1, M_ERROR, -1)) == NULL) |
| 46 | return; |
| 47 | |
| 48 | *mp->b_rptr = (uchar_t)error; |
| 49 | qreply(wq, mp); |
| 50 | } |
| 51 | |
| 52 | void |
| 53 | mioc2ack(mblk_t *mp, mblk_t *dp, size_t count, int rval) |
| 54 | { |
| 55 | struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| 56 | mblk_t *odp = mp->b_cont; /* allows freemsg() to be a tail call */ |
| 57 | |
| 58 | DB_TYPE(mp) = M_IOCACK; |
| 59 | iocp->ioc_count = count; |
| 60 | iocp->ioc_error = 0; |
| 61 | iocp->ioc_rval = rval; |
| 62 | |
| 63 | mp->b_cont = dp; |
| 64 | if (dp != NULL) |
| 65 | dp->b_wptr = dp->b_rptr + count; |
| 66 | freemsg(odp); |
| 67 | } |
| 68 | |
| 69 | void |
| 70 | miocack(queue_t *wq, mblk_t *mp, int count, int rval) |
| 71 | { |
| 72 | struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| 73 | |
| 74 | DB_TYPE(mp) = M_IOCACK; |
| 75 | iocp->ioc_count = count; |
| 76 | iocp->ioc_error = 0; |
| 77 | iocp->ioc_rval = rval; |
| 78 | qreply(wq, mp); |
| 79 | } |
| 80 | |
| 81 | void |
| 82 | miocnak(queue_t *wq, mblk_t *mp, int count, int error) |
| 83 | { |
| 84 | struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| 85 | |
| 86 | DB_TYPE(mp) = M_IOCNAK; |
| 87 | iocp->ioc_count = count; |
| 88 | iocp->ioc_error = error; |
| 89 | qreply(wq, mp); |
| 90 | } |
| 91 | |
| 92 | mblk_t * |
| 93 | mexchange(queue_t *wq, mblk_t *mp, size_t size, uchar_t type, int32_t primtype) |
| 94 | { |
| 95 | if (mp == NULL || MBLKSIZE(mp) < size || DB_REF(mp) > 1) { |
| 96 | freemsg(mp); |
| 97 | if ((mp = allocb(size, BPRI_LO)) == NULL) { |
| 98 | if (wq != NULL) { |
| 99 | if ((mp = allocb(1, BPRI_HI)) != NULL) |
| 100 | merror(wq, mp, ENOSR); |
| 101 | } |
| 102 | return (NULL); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | DB_TYPE(mp) = type; |
| 107 | mp->b_rptr = DB_BASE(mp); |
| 108 | mp->b_wptr = mp->b_rptr + size; |
| 109 | if (primtype >= 0) |
| 110 | *(int32_t *)mp->b_rptr = primtype; |
| 111 | |
| 112 | return (mp); |
| 113 | } |
| 114 | |
| 115 | size_t |
| 116 | msgsize(mblk_t *mp) |
| 117 | { |
| 118 | size_t n = 0; |
| 119 | |
| 120 | for (; mp != NULL; mp = mp->b_cont) |
| 121 | n += MBLKL(mp); |
| 122 | |
| 123 | return (n); |
| 124 | } |
| 125 | |
| 126 | void |
| 127 | mcopymsg(mblk_t *mp, void *bufp) |
| 128 | { |
| 129 | caddr_t dest = bufp; |
| 130 | mblk_t *bp; |
| 131 | size_t n; |
| 132 | |
| 133 | for (bp = mp; bp != NULL; bp = bp->b_cont) { |
| 134 | n = MBLKL(bp); |
| 135 | bcopy(bp->b_rptr, dest, n); |
| 136 | dest += n; |
| 137 | } |
| 138 | |
| 139 | freemsg(mp); |
| 140 | } |
| 141 | |
| 142 | void |
| 143 | mcopyin(mblk_t *mp, void *private, size_t size, void *useraddr) |
| 144 | { |
| 145 | struct copyreq *cp = (struct copyreq *)mp->b_rptr; |
| 146 | |
| 147 | if (useraddr != NULL) { |
| 148 | cp->cq_addr = (caddr_t)useraddr; |
| 149 | } else { |
| 150 | ASSERT(DB_TYPE(mp) == M_IOCTL); |
| 151 | ASSERT(mp->b_cont != NULL); |
| 152 | ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT); |
| 153 | cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; |
| 154 | } |
| 155 | |
| 156 | cp->cq_flag = 0; |
| 157 | cp->cq_size = size; |
| 158 | cp->cq_private = (mblk_t *)private; |
| 159 | |
| 160 | DB_TYPE(mp) = M_COPYIN; |
| 161 | mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); |
| 162 | |
| 163 | if (mp->b_cont != NULL) { |
| 164 | freemsg(mp->b_cont); |
| 165 | mp->b_cont = NULL; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | void |
| 170 | mcopyout(mblk_t *mp, void *private, size_t size, void *useraddr, mblk_t *dp) |
| 171 | { |
| 172 | struct copyreq *cp = (struct copyreq *)mp->b_rptr; |
| 173 | |
| 174 | if (useraddr != NULL) |
| 175 | cp->cq_addr = (caddr_t)useraddr; |
| 176 | else { |
| 177 | ASSERT(DB_TYPE(mp) == M_IOCTL); |
| 178 | ASSERT(mp->b_cont != NULL); |
| 179 | ASSERT(((struct iocblk *)mp->b_rptr)->ioc_count == TRANSPARENT); |
| 180 | cp->cq_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; |
| 181 | } |
| 182 | |
| 183 | cp->cq_flag = 0; |
| 184 | cp->cq_size = size; |
| 185 | cp->cq_private = (mblk_t *)private; |
| 186 | |
| 187 | DB_TYPE(mp) = M_COPYOUT; |
| 188 | mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); |
| 189 | |
| 190 | if (dp != NULL) { |
| 191 | if (mp->b_cont != NULL) |
| 192 | freemsg(mp->b_cont); |
| 193 | mp->b_cont = dp; |
| 194 | mp->b_cont->b_wptr = mp->b_cont->b_rptr + size; |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | int |
| 199 | miocpullup(mblk_t *iocmp, size_t size) |
| 200 | { |
| 201 | struct iocblk *iocp = (struct iocblk *)iocmp->b_rptr; |
| 202 | mblk_t *datamp = iocmp->b_cont; |
| 203 | mblk_t *newdatamp; |
| 204 | |
| 205 | /* |
| 206 | * We'd like to be sure that DB_TYPE(iocmp) == M_IOCTL, but some |
| 207 | * nitwit routines like ttycommon_ioctl() always reset the type of |
| 208 | * legitimate M_IOCTL messages to M_IOCACK as a "courtesy" to the |
| 209 | * caller, even when the routine does not understand the M_IOCTL. |
| 210 | * The ttycommon_ioctl() routine does us the additional favor of |
| 211 | * clearing ioc_count, so we cannot rely on it having a correct |
| 212 | * size either (blissfully, ttycommon_ioctl() does not screw with |
| 213 | * TRANSPARENT messages, so we can still sanity check for that). |
| 214 | */ |
| 215 | ASSERT(MBLKL(iocmp) == sizeof (struct iocblk)); |
| 216 | if (MBLKL(iocmp) != sizeof (struct iocblk)) { |
| 217 | cmn_err(CE_WARN, "miocpullup: passed mblk_t %p is not an ioctl" |
| 218 | " mblk_t", (void *)iocmp); |
| 219 | return (EINVAL); |
| 220 | } |
| 221 | |
| 222 | if (iocp->ioc_count == TRANSPARENT) |
| 223 | return (EINVAL); |
| 224 | |
| 225 | if (size == 0) |
| 226 | return (0); |
| 227 | |
| 228 | if (datamp == NULL) |
| 229 | return (EINVAL); |
| 230 | |
| 231 | if (MBLKL(datamp) >= size) |
| 232 | return (0); |
| 233 | |
| 234 | newdatamp = msgpullup(datamp, size); |
| 235 | if (newdatamp == NULL) { |
| 236 | if (msgdsize(datamp) < size) |
| 237 | return (EINVAL); |
| 238 | return (ENOMEM); |
| 239 | } |
| 240 | |
| 241 | iocmp->b_cont = newdatamp; |
| 242 | freemsg(datamp); |
| 243 | return (0); |
| 244 | } |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 245 | |
| 246 | /* Copy userdata into a new mblk_t */ |
| 247 | mblk_t * |
| 248 | mcopyinuio(struct stdata *stp, uio_t *uiop, ssize_t iosize, |
| 249 | ssize_t maxblk, int *errorp) |
| 250 | { |
| 251 | mblk_t *head = NULL, **tail = &head; |
| 252 | size_t offset = stp->sd_wroff; |
kais | c28749e | 2005-11-12 18:58:05 -0800 | [diff] [blame] | 253 | size_t tail_len = stp->sd_tail; |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 254 | |
| 255 | if (iosize == INFPSZ || iosize > uiop->uio_resid) |
| 256 | iosize = uiop->uio_resid; |
| 257 | |
| 258 | if (maxblk == INFPSZ) |
| 259 | maxblk = iosize; |
| 260 | |
| 261 | /* Nothing to do in these cases, so we're done */ |
| 262 | if (iosize < 0 || maxblk < 0 || (maxblk == 0 && iosize > 0)) |
| 263 | goto done; |
| 264 | |
| 265 | if (stp->sd_flag & STRCOPYCACHED) |
| 266 | uiop->uio_extflg |= UIO_COPY_CACHED; |
| 267 | |
| 268 | /* |
| 269 | * We will enter the loop below if iosize is 0; it will allocate an |
| 270 | * empty message block and call uiomove(9F) which will just return. |
| 271 | * We could avoid that with an extra check but would only slow |
| 272 | * down the much more likely case where iosize is larger than 0. |
| 273 | */ |
| 274 | do { |
| 275 | ssize_t blocksize; |
| 276 | mblk_t *mp; |
| 277 | |
| 278 | blocksize = MIN(iosize, maxblk); |
| 279 | ASSERT(blocksize >= 0); |
kais | c28749e | 2005-11-12 18:58:05 -0800 | [diff] [blame] | 280 | if ((mp = allocb_cred(offset + blocksize + tail_len, |
Erik Nordmark | de8c4a1 | 2009-02-12 08:42:06 -0800 | [diff] [blame] | 281 | CRED(), curproc->p_pid)) == NULL) { |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 282 | *errorp = ENOMEM; |
| 283 | return (head); |
| 284 | } |
| 285 | mp->b_rptr += offset; |
| 286 | mp->b_wptr = mp->b_rptr + blocksize; |
masputra | ff550d0 | 2005-10-22 22:50:14 -0700 | [diff] [blame] | 287 | |
| 288 | *tail = mp; |
| 289 | tail = &mp->b_cont; |
| 290 | |
| 291 | /* uiomove(9F) either returns 0 or EFAULT */ |
| 292 | if ((*errorp = uiomove(mp->b_rptr, (size_t)blocksize, |
| 293 | UIO_WRITE, uiop)) != 0) { |
| 294 | ASSERT(*errorp != ENOMEM); |
| 295 | freemsg(head); |
| 296 | return (NULL); |
| 297 | } |
| 298 | |
| 299 | iosize -= blocksize; |
| 300 | } while (iosize > 0); |
| 301 | |
| 302 | done: |
| 303 | *errorp = 0; |
| 304 | return (head); |
| 305 | } |