xref: /illumos-gate/usr/src/uts/common/io/strsun.c (revision de8c4a14)
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