xref: /illumos-gate/usr/src/uts/common/io/strsun.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 	size_t tail_len = stp->sd_tail;
257 
258 	if (iosize == INFPSZ || iosize > uiop->uio_resid)
259 		iosize = uiop->uio_resid;
260 
261 	if (maxblk == INFPSZ)
262 		maxblk = iosize;
263 
264 	/* Nothing to do in these cases, so we're done */
265 	if (iosize < 0 || maxblk < 0 || (maxblk == 0 && iosize > 0))
266 		goto done;
267 
268 	if (stp->sd_flag & STRCOPYCACHED)
269 		uiop->uio_extflg |= UIO_COPY_CACHED;
270 
271 	/*
272 	 * We will enter the loop below if iosize is 0; it will allocate an
273 	 * empty message block and call uiomove(9F) which will just return.
274 	 * We could avoid that with an extra check but would only slow
275 	 * down the much more likely case where iosize is larger than 0.
276 	 */
277 	do {
278 		ssize_t blocksize;
279 		mblk_t  *mp;
280 
281 		blocksize = MIN(iosize, maxblk);
282 		ASSERT(blocksize >= 0);
283 		if ((mp = allocb_cred(offset + blocksize + tail_len,
284 		    CRED())) == NULL) {
285 			*errorp = ENOMEM;
286 			return (head);
287 		}
288 		mp->b_rptr += offset;
289 		mp->b_wptr = mp->b_rptr + blocksize;
290 		DB_CPID(mp) = curproc->p_pid;
291 
292 		*tail = mp;
293 		tail = &mp->b_cont;
294 
295 		/* uiomove(9F) either returns 0 or EFAULT */
296 		if ((*errorp = uiomove(mp->b_rptr, (size_t)blocksize,
297 		    UIO_WRITE, uiop)) != 0) {
298 			ASSERT(*errorp != ENOMEM);
299 			freemsg(head);
300 			return (NULL);
301 		}
302 
303 		iosize -= blocksize;
304 	} while (iosize > 0);
305 
306 done:
307 	*errorp = 0;
308 	return (head);
309 }
310