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