1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 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 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 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> 37 #include <sys/strsubr.h> 38 #include <sys/strsun.h> 39 #include <sys/sysmacros.h> 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 } 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; 253 size_t tail_len = stp->sd_tail; 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); 280 if ((mp = allocb_cred(offset + blocksize + tail_len, 281 CRED(), curproc->p_pid)) == NULL) { 282 *errorp = ENOMEM; 283 return (head); 284 } 285 mp->b_rptr += offset; 286 mp->b_wptr = mp->b_rptr + blocksize; 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 } 306