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
merror(queue_t * wq,mblk_t * mp,int error)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
mioc2ack(mblk_t * mp,mblk_t * dp,size_t count,int rval)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
miocack(queue_t * wq,mblk_t * mp,int count,int rval)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
miocnak(queue_t * wq,mblk_t * mp,int count,int error)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 *
mexchange(queue_t * wq,mblk_t * mp,size_t size,uchar_t type,int32_t primtype)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
msgsize(mblk_t * mp)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
mcopymsg(mblk_t * mp,void * bufp)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
mcopyin(mblk_t * mp,void * private,size_t size,void * useraddr)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
mcopyout(mblk_t * mp,void * private,size_t size,void * useraddr,mblk_t * dp)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
miocpullup(mblk_t * iocmp,size_t size)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 *
mcopyinuio(struct stdata * stp,uio_t * uiop,ssize_t iosize,ssize_t maxblk,int * errorp)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