1 /*
2  * Copyright (c) 2000, 2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $
33  */
34 /*
35  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
36  * Use is subject to license terms.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/errno.h>
44 #include <sys/uio.h>
45 #include <sys/types.h>
46 #include <sys/stream.h>
47 #include <sys/strsun.h>
48 #include <sys/strsubr.h>
49 #include <sys/cmn_err.h>
50 
51 #ifdef APPLE
52 #include <sys/smb_apple.h>
53 #else
54 #include <netsmb/smb_osdep.h>
55 #endif
56 
57 #include <netsmb/mchain.h>
58 
59 #include <netsmb/smb.h>
60 #include <netsmb/smb_conn.h>
61 #include <netsmb/smb_subr.h>
62 
63 /* BEGIN CSTYLED */
64 /*
65  * BSD-style mbufs, vs SysV-style mblks:
66  * One big difference: the mbuf payload is:
67  *   m_data ... (m_data + m_len)
68  * In Unix STREAMS, the mblk payload is:
69  *   b_rptr ... b_wptr
70  *
71  * Here are some handy conversion notes:
72  *
73  * struct mbuf                     struct mblk
74  *   m->m_next                       m->b_cont
75  *   m->m_nextpkt                    m->b_next
76  *   m->m_data                       m->b_rptr
77  *   m->m_len                        MBLKL(m)
78  *   m->m_dat[]                      m->b_datap->db_base
79  *   &m->m_dat[MLEN]                 m->b_datap->db_lim
80  *   M_TRAILINGSPACE(m)              MBLKTAIL(m)
81  *   m_freem(m)                      freemsg(m)
82  *
83  * Note that mbufs chains also have a special "packet" header,
84  * which has the length of the whole message.  In STREAMS one
85  * typically just calls msgdsize(m) to get that.
86  */
87 /* END CSTYLED */
88 
89 
90 /*
91  *
92  * MODULE_VERSION(libmchain, 1);
93  */
94 
95 #ifdef __GNUC__
96 #define	MBERROR(format, args...) printf("%s(%d): "format, \
97 				    __FUNCTION__, __LINE__, ## args)
98 #define	MBPANIC(format, args...) printf("%s(%d): "format, \
99 				    __FUNCTION__, __LINE__, ## args)
100 #else
101 #define	MBERROR(...) \
102 	smb_errmsg(CE_NOTE, __func__, __VA_ARGS__)
103 #define	MBPANIC(...) \
104 	smb_errmsg(CE_PANIC, __func__, __VA_ARGS__)
105 #endif
106 
107 /*
108  * MLEN: The smallest mblk we'll allocate.
109  *
110  * There's more to MLEN than you might think.
111  * Some ethernet drivers may send each mblk as a
112  * separate frame, so we want MLEN at least 1K.
113  * We could have used 1K here, but that might
114  * hurt transports that support larger frames.
115  * 4K fits nicely in 3 Ethernet frames (3 * 1500)
116  * leaving about 500 bytes for protocol headers.
117  *
118  * XXX: Would Ethernet drivers be happier
119  * (more efficient) if we used 1K here?
120  */
121 #define	MLEN	4096
122 
123 
124 /*
125  * Some UIO routines.
126  * Taken from Darwin Sourcecs.
127  */
128 
129 /*
130  * uio_isuserspace - return non zero value if the address space
131  * flag is for a user address space (could be 32 or 64 bit).
132  */
133 int
134 uio_isuserspace(uio_t *a_uio)
135 {
136 	if (a_uio->uio_segflg == UIO_USERSPACE) {
137 		return (1);
138 	}
139 	return (0);
140 }
141 
142 /*
143  * uio_curriovbase - return the base address of the current iovec associated
144  *      with the given uio_t.  May return 0.
145  */
146 caddr_t
147 uio_curriovbase(uio_t *a_uio)
148 {
149 	if (a_uio->uio_iovcnt < 1) {
150 		return (0);
151 	}
152 	return ((caddr_t)((uintptr_t)a_uio->uio_iov->iov_base));
153 }
154 
155 /*
156  * uio_curriovlen - return the length value of the current iovec associated
157  *      with the given uio_t.
158  */
159 size_t
160 uio_curriovlen(uio_t *a_uio)
161 {
162 	if (a_uio->uio_iovcnt < 1) {
163 		return (0);
164 	}
165 	return ((size_t)a_uio->uio_iov->iov_len);
166 }
167 
168 
169 /*
170  * uio_update - update the given uio_t for a_count of completed IO.
171  *      This call decrements the current iovec length and residual IO value
172  *      and increments the current iovec base address and offset value.
173  *      If the current iovec length is 0 then advance to the next
174  *      iovec (if any).
175  *      If the a_count passed in is 0, than only do the advancement
176  *      over any 0 length iovec's.
177  */
178 void
179 uio_update(uio_t *a_uio, size_t a_count)
180 {
181 	if (a_uio->uio_iovcnt < 1) {
182 		return;
183 	}
184 
185 	/*
186 	 * if a_count == 0, then we are asking to skip over
187 	 * any empty iovs
188 	 */
189 	if (a_count) {
190 		if (a_count > a_uio->uio_iov->iov_len) {
191 			a_uio->uio_iov->iov_base += a_uio->uio_iov->iov_len;
192 			a_uio->uio_iov->iov_len = 0;
193 		} else {
194 			a_uio->uio_iov->iov_base += a_count;
195 			a_uio->uio_iov->iov_len -= a_count;
196 		}
197 		if (a_uio->uio_resid < 0) {
198 			a_uio->uio_resid = 0;
199 		}
200 		if (a_count > (size_t)a_uio->uio_resid) {
201 			a_uio->uio_offset += a_uio->uio_resid;
202 			a_uio->uio_resid = 0;
203 		} else {
204 			a_uio->uio_offset += a_count;
205 			a_uio->uio_resid -= a_count;
206 		}
207 	}
208 	/*
209 	 * advance to next iovec if current one is totally consumed
210 	 */
211 	while (a_uio->uio_iovcnt > 0 && a_uio->uio_iov->iov_len == 0) {
212 		a_uio->uio_iovcnt--;
213 		if (a_uio->uio_iovcnt > 0) {
214 			a_uio->uio_iov++;
215 		}
216 	}
217 }
218 
219 
220 /*ARGSUSED*/
221 mblk_t *
222 m_getblk(int size, int type)
223 {
224 	mblk_t *mblk;
225 	int error;
226 
227 	/* Make size at least MLEN. */
228 	if (size < MLEN)
229 		size = MLEN;
230 	mblk = allocb_wait(size, BPRI_LO, STR_NOSIG, &error);
231 	ASSERT(mblk);
232 	return (mblk);
233 }
234 
235 void
236 mb_done(struct mbchain *mbp)
237 {
238 	if (mbp->mb_top) {
239 		freemsg(mbp->mb_top);
240 		mbp->mb_top = NULL;
241 	}
242 	/* Avoid dangling references */
243 	mbp->mb_cur = NULL;
244 }
245 
246 unsigned int
247 m_length(mblk_t *mblk)
248 {
249 	uint64_t diff;
250 
251 	diff = (uintptr_t)mblk->b_datap->db_lim -
252 	    (uintptr_t)mblk->b_datap->db_base;
253 	ASSERT(diff == (uint64_t)((unsigned int)diff));
254 	return ((unsigned int)diff);
255 }
256 
257 void
258 mb_initm(struct mbchain *mbp, mblk_t *m)
259 {
260 	bzero(mbp, sizeof (*mbp));
261 	mbp->mb_top = mbp->mb_cur = m;
262 }
263 
264 
265 int
266 mb_init(struct mbchain *mbp)
267 {
268 	mblk_t *mblk;
269 
270 	mblk = m_getblk(MLEN, 1);
271 	if (mblk == NULL) {
272 		return (ENOSR);
273 	}
274 
275 	/*
276 	 * Leave room in this first mblk so we can
277 	 * prepend a 4-byte NetBIOS header.
278 	 * See smb_nbst_send()
279 	 */
280 	mblk->b_wptr += 4;
281 	mblk->b_rptr = mblk->b_wptr;
282 
283 	mb_initm(mbp, mblk);
284 	return (0);
285 }
286 
287 
288 /*
289  * mb_detach() function returns the value of mbp->mb_top field
290  * and sets its * value to NULL.
291  */
292 
293 mblk_t *
294 mb_detach(struct mbchain *mbp)
295 {
296 	mblk_t *m;
297 
298 	m = mbp->mb_top;
299 	mbp->mb_top = mbp->mb_cur = NULL;
300 	return (m);
301 }
302 
303 /*
304  * Returns the length of the mblk_t data.
305  *
306  */
307 int
308 m_fixhdr(mblk_t *m0)
309 {
310 	size_t dsz;
311 
312 	dsz = msgdsize(m0);
313 	return ((int)dsz);
314 }
315 
316 /*
317  * BSD code set the message header length here, and
318  * returned the length.  We don't have that field, so
319  * just return the message length.
320  */
321 int
322 mb_fixhdr(struct mbchain *mbp)
323 {
324 	return (m_fixhdr(mbp->mb_top));
325 }
326 
327 
328 /*
329  * Check if object of size 'size' fit to the current position and
330  * allocate new mbuf if not. Advance pointers and increase len. of mbuf(s).
331  * Return pointer to the object placeholder or NULL if any error occured.
332  * Note: size should be <= MLEN
333  */
334 void *
335 mb_reserve(struct mbchain *mbp, int size)
336 {
337 	mblk_t *m, *mn;
338 	void *bpos;
339 
340 	m = mbp->mb_cur;
341 	/*
342 	 * If the requested size is more than the space left.
343 	 * Allocate and appenad a new mblk.
344 	 */
345 	/*LINTED*/
346 	if (MBLKTAIL(m) < size) {
347 		mn = m_getblk(size, 1);
348 		if (mn == NULL)
349 			return (NULL);
350 		mbp->mb_cur = m->b_cont = mn;
351 		m = mn;
352 	}
353 	/*
354 	 * If 'size' bytes fits into the buffer, then
355 	 * 1. increment the write pointer to the size.
356 	 * 2. return the position from where the memory is reserved.
357 	 */
358 	bpos = m->b_wptr;
359 	m->b_wptr += size;
360 	mbp->mb_count += size;
361 	return (bpos);
362 }
363 
364 /*
365  * All mb_put_*() functions perform an actual copy of the data into mbuf
366  * chain. Functions which have le or be suffixes will perform conversion to
367  * the little- or big-endian data formats.
368  * XXX: Assumes total data length in previous mblks is EVEN.
369  * XXX: Might need to compute the offset from mb_top instead.
370  */
371 int
372 mb_put_padbyte(struct mbchain *mbp)
373 {
374 	caddr_t dst;
375 	char x = 0;
376 
377 	dst = (caddr_t)mbp->mb_cur->b_wptr;
378 
379 	/* only add padding if address is odd */
380 	if ((long)dst & 1)
381 		return (mb_put_mem(mbp, (caddr_t)&x, 1, MB_MSYSTEM));
382 	else
383 		return (0);
384 }
385 
386 int
387 mb_put_uint8(struct mbchain *mbp, u_int8_t x)
388 {
389 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
390 }
391 
392 int
393 mb_put_uint16be(struct mbchain *mbp, u_int16_t x)
394 {
395 	x = htobes(x);
396 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
397 }
398 
399 int
400 mb_put_uint16le(struct mbchain *mbp, u_int16_t x)
401 {
402 	x = htoles(x);
403 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
404 }
405 
406 int
407 mb_put_uint32be(struct mbchain *mbp, u_int32_t x)
408 {
409 	x = htobel(x);
410 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
411 }
412 
413 int
414 mb_put_uint32le(struct mbchain *mbp, u_int32_t x)
415 {
416 	x = htolel(x);
417 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
418 }
419 
420 int
421 mb_put_uint64be(struct mbchain *mbp, u_int64_t x)
422 {
423 	x = htobeq(x);
424 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
425 }
426 
427 int
428 mb_put_uint64le(struct mbchain *mbp, u_int64_t x)
429 {
430 	x = htoleq(x);
431 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
432 }
433 
434 /*
435  * mb_put_mem() function copies size bytes of data specified by the source
436  * argument to an mbuf chain.  The type argument specifies the method used
437  * to perform a copy
438  */
439 int
440 mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type)
441 {
442 	mblk_t *m, *n;
443 	caddr_t dst;
444 	c_caddr_t src;
445 	int cplen, error, mleft, count;
446 	uint64_t diff;
447 
448 	m = mbp->mb_cur;
449 
450 	/*LINTED*/
451 	diff = MBLKTAIL(m);
452 	ASSERT(diff == (uint64_t)((int)diff));
453 	mleft = (int)diff;
454 
455 	while (size > 0) {
456 		if (mleft == 0) {
457 			if (m->b_cont == NULL) {
458 				/*
459 				 * Changed m_getm() to m_getblk()
460 				 * with the requested size, so we
461 				 * don't need m_getm() anymore.
462 				 */
463 				n = m_getblk(size, 1);
464 				if (n == NULL)
465 					return (ENOBUFS);
466 				m->b_cont = n;
467 			}
468 			m = m->b_cont;
469 			/*LINTED*/
470 			diff = MBLKTAIL(m);
471 			ASSERT(diff == (uint64_t)((int)diff));
472 			mleft = (int)diff;
473 			continue;
474 		}
475 		cplen = mleft > size ? size : mleft;
476 		dst = (caddr_t)m->b_wptr;
477 		switch (type) {
478 		case MB_MINLINE:
479 			for (src = source, count = cplen; count; count--)
480 				*dst++ = *src++;
481 			break;
482 		case MB_MSYSTEM:
483 			/*
484 			 * Try copying the raw bytes instead of using bcopy()
485 			 */
486 			bcopy(source, dst, cplen);
487 			break;
488 		case MB_MUSER:
489 			error = copyin((void *)source, dst, cplen);
490 			if (error)
491 				return (error);
492 			break;
493 		case MB_MZERO:
494 			bzero(dst, cplen);
495 			break;
496 		}
497 		size -= cplen;
498 		source += cplen;
499 		mleft -= cplen;
500 		m->b_wptr += cplen;
501 		mbp->mb_count += cplen;
502 	}
503 	mbp->mb_cur = m;
504 	return (0);
505 }
506 
507 /*
508  * Append an mblk to the chain.
509  */
510 int
511 mb_put_mbuf(struct mbchain *mbp, mblk_t *m)
512 {
513 	mblk_t *mb;
514 
515 	/* See: linkb(9f) */
516 	for (mb = mbp->mb_cur; mb->b_cont; mb = mb->b_cont)
517 		;
518 	mb->b_cont = m;
519 	mbp->mb_cur = m;
520 	mbp->mb_count += msgdsize(m);
521 
522 	return (0);
523 }
524 
525 /*
526  * copies a uio scatter/gather list to an mbuf chain.
527  */
528 int
529 mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size)
530 {
531 	int left;
532 	int mtype, error;
533 
534 	mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
535 
536 	while (size > 0 && uiop->uio_resid) {
537 		if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) ==
538 		    USER_ADDR_NULL)
539 			return (EFBIG);
540 		left = uio_curriovlen(uiop);
541 		if (left > size)
542 			left = size;
543 		error = mb_put_mem(mbp, CAST_DOWN(caddr_t,
544 		    uio_curriovbase(uiop)), left, mtype);
545 		if (error)
546 			return (error);
547 		uio_update(uiop, left);
548 		size -= left;
549 	}
550 	return (0);
551 }
552 
553 /*
554  * Routines for fetching data from an mbuf chain
555  */
556 int
557 md_init(struct mdchain *mdp)
558 {
559 	mblk_t *m;
560 
561 	m = m_getblk(MLEN, 1);
562 	if (m == NULL)
563 		return (ENOBUFS);
564 	md_initm(mdp, m);
565 	return (0);
566 }
567 
568 void
569 md_initm(struct mdchain *mdp, mblk_t *m)
570 {
571 	bzero(mdp, sizeof (*mdp));
572 	mdp->md_top = mdp->md_cur = m;
573 	mdp->md_pos = m->b_rptr;
574 }
575 
576 void
577 md_done(struct mdchain *mdp)
578 {
579 	mblk_t *m;
580 
581 	/*
582 	 * Deal with the fact that we can error out of
583 	 * smb_t2_reply or smb_nt_reply without using up
584 	 * all the "records" added by md_append_record().
585 	 */
586 	while ((m = mdp->md_top) != NULL) {
587 		mdp->md_top = m->b_next;
588 		m->b_next = NULL;
589 		freemsg(m);
590 	}
591 	/* Avoid dangling references */
592 	mdp->md_cur = NULL;
593 	mdp->md_pos = NULL;
594 }
595 
596 /*
597  * Append a new message (separate mbuf chain).
598  * It is caller responsibility to prevent
599  * multiple calls to fetch/record routines.
600  * XXX: Note (mis)use of mblk->b_next here.
601  */
602 void
603 md_append_record(struct mdchain *mdp, mblk_t *top)
604 {
605 	mblk_t *m;
606 
607 	top->b_next = NULL;
608 	if (mdp->md_top == NULL) {
609 		md_initm(mdp, top);
610 		return;
611 	}
612 	m = mdp->md_top;
613 	/* Get to last message (not b_cont chain) */
614 	while (m->b_next)
615 		m = m->b_next;
616 	m->b_next = top;
617 }
618 
619 /*
620  * Advance mdp->md_top to the next message.
621  * XXX: Note (mis)use of mblk->b_next here.
622  */
623 int
624 md_next_record(struct mdchain *mdp)
625 {
626 	mblk_t *m;
627 
628 	if (mdp->md_top == NULL)
629 		return (ENOENT);
630 	/* Get to next message (not b_cont chain) */
631 	m = mdp->md_top->b_next;
632 	mdp->md_top->b_next = NULL;
633 	md_done(mdp);
634 	if (m == NULL)
635 		return (ENOENT);
636 	md_initm(mdp, m);
637 	return (0);
638 }
639 
640 int
641 md_get_uint8(struct mdchain *mdp, u_int8_t *x)
642 {
643 	return (md_get_mem(mdp, (char *)x, 1, MB_MINLINE));
644 }
645 
646 int
647 md_get_uint16(struct mdchain *mdp, u_int16_t *x)
648 {
649 	return (md_get_mem(mdp, (char *)x, 2, MB_MINLINE));
650 }
651 
652 int
653 md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
654 {
655 	u_int16_t v;
656 	int error = md_get_uint16(mdp, &v);
657 
658 	if (x)
659 		*x = letohs(v);
660 	return (error);
661 }
662 
663 int
664 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
665 	u_int16_t v;
666 	int error = md_get_uint16(mdp, &v);
667 
668 	if (x)
669 		*x = betohs(v);
670 	return (error);
671 }
672 
673 int
674 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
675 {
676 	return (md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE));
677 }
678 
679 int
680 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
681 {
682 	u_int32_t v;
683 	int error;
684 
685 	error = md_get_uint32(mdp, &v);
686 	if (x)
687 		*x = betohl(v);
688 	return (error);
689 }
690 
691 int
692 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
693 {
694 	u_int32_t v;
695 	int error;
696 
697 	error = md_get_uint32(mdp, &v);
698 	if (x)
699 		*x = letohl(v);
700 	return (error);
701 }
702 
703 int
704 md_get_uint64(struct mdchain *mdp, u_int64_t *x)
705 {
706 	return (md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE));
707 }
708 
709 int
710 md_get_uint64be(struct mdchain *mdp, u_int64_t *x)
711 {
712 	u_int64_t v;
713 	int error;
714 
715 	error = md_get_uint64(mdp, &v);
716 	if (x)
717 		*x = betohq(v);
718 	return (error);
719 }
720 
721 int
722 md_get_uint64le(struct mdchain *mdp, u_int64_t *x)
723 {
724 	u_int64_t v;
725 	int error;
726 
727 	error = md_get_uint64(mdp, &v);
728 	if (x)
729 		*x = letohq(v);
730 	return (error);
731 }
732 
733 int
734 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
735 {
736 	mblk_t *m = mdp->md_cur;
737 	int error;
738 	int count;
739 	unsigned char *s;
740 	uint64_t diff;
741 
742 	while (size > 0) {
743 		if (m == NULL) {
744 			SMBSDEBUG("incomplete copy\n");
745 			return (EBADRPC);
746 		}
747 
748 		/*
749 		 * Offset in the current MBUF.
750 		 */
751 		s = mdp->md_pos;
752 		ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
753 
754 		/* Data remaining. */
755 		diff = (uintptr_t)m->b_wptr - (uintptr_t)s;
756 		ASSERT(diff == (uint64_t)((int)diff));
757 		count = (int)diff;
758 
759 		/*
760 		 * Check if the no. of bytes remaining is less than
761 		 * the bytes requested.
762 		 */
763 		if (count == 0) {
764 			m = m->b_cont;
765 			if (m) {
766 				mdp->md_cur = m;
767 				mdp->md_pos = s = m->b_rptr;
768 			}
769 			continue;
770 		}
771 		if (count > size)
772 			count = size;
773 		size -= count;
774 		mdp->md_pos += count;
775 		if (target == NULL)
776 			continue;
777 		switch (type) {
778 		case MB_MUSER:
779 			error = copyout(s, (void *)target, count);
780 			if (error)
781 				return (error);
782 			break;
783 		case MB_MSYSTEM:
784 			bcopy(s, target, count);
785 			break;
786 		case MB_MINLINE:
787 			while (count--)
788 				*target++ = *s++;
789 			continue;
790 		}
791 		target += count;
792 	}
793 	return (0);
794 }
795 
796 /*
797  * Get the next SIZE bytes as a separate mblk.
798  */
799 int
800 md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret)
801 {
802 	mblk_t *m, *rm;
803 
804 	unsigned char *s;
805 	uint64_t diff;
806 	int off;
807 
808 	/*
809 	 * Offset in the current MBUF.
810 	 */
811 	m = mdp->md_cur;
812 	s = mdp->md_pos;
813 	ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
814 	diff = (uintptr_t)s - (uintptr_t)m->b_rptr;
815 	ASSERT(diff == (uint64_t)((int)diff));
816 	off = (int)diff;
817 
818 	rm = m_copym(m, off, size, M_WAITOK);
819 	if (rm == NULL)
820 		return (EBADRPC);
821 
822 	*ret = rm;
823 	return (0);
824 }
825 
826 int
827 md_get_uio(struct mdchain *mdp, uio_t *uiop, int size)
828 {
829 	size_t left;
830 	int mtype, error;
831 
832 	mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
833 	while (size > 0 && uiop->uio_resid) {
834 		if (uiop->uio_iovcnt <= 0 ||
835 		    uio_curriovbase(uiop) == USER_ADDR_NULL)
836 			return (EFBIG);
837 		left = uio_curriovlen(uiop);
838 		if (left > size)
839 			left = size;
840 		error = md_get_mem(mdp, CAST_DOWN(caddr_t,
841 		    uio_curriovbase(uiop)), left, mtype);
842 		if (error)
843 			return (error);
844 		uio_update(uiop, left);
845 		size -= left;
846 	}
847 	return (0);
848 }
849 
850 /*
851  * Additions for Solaris
852  */
853 
854 /*
855  * concatenate mblk chain n to m.
856  * go till end of data in m.
857  * then add the link of b_cont to n.
858  * See: linkb(9f)
859  */
860 
861 void m_cat(
862 	mblk_t *m,
863 	mblk_t *n)
864 {
865 	if (!n)
866 		return;
867 	while (m->b_cont) {
868 		m = m->b_cont;
869 	}
870 	m->b_cont = n;
871 }
872 
873 /*ARGSUSED*/
874 mblk_t *
875 m_copym(mblk_t *m, int off, int len, int wait)
876 {
877 	mblk_t *n;
878 	size_t dsz;
879 	ssize_t adj;
880 
881 	dsz = msgdsize(m);
882 	if (len == M_COPYALL) {
883 		if (off > dsz)
884 			return (0);
885 	} else {
886 		if ((off + len) > dsz)
887 			return (0);
888 	}
889 
890 	if ((n = dupmsg(m)) == NULL)
891 		return (0);
892 
893 	/* trim from head */
894 	adj = off;
895 	if (!adjmsg(n, adj)) {
896 		freemsg(n);
897 		return (0);
898 	}
899 
900 	/* trim from tail */
901 	if (len != M_COPYALL) {
902 		dsz = msgdsize(n);
903 		ASSERT(len <= dsz);
904 		if (len < dsz) {
905 			adj = (ssize_t)len - (ssize_t)dsz;
906 			ASSERT(adj < 0);
907 			adjmsg(n, adj);
908 		}
909 	}
910 
911 	return (n);
912 }
913 
914 /*
915  * Get "rqlen" contiguous bytes into the first mblk of a chain.
916  */
917 mblk_t *
918 m_pullup(
919 	mblk_t *m,
920 	int rqlen)
921 {
922 	ptrdiff_t diff;
923 
924 	/*LINTED*/
925 	diff = MBLKL(m);
926 	ASSERT(diff == (ptrdiff_t)((int)diff));
927 	if ((int)diff < rqlen) {
928 		/* This should be rare. */
929 		if (!pullupmsg(m, rqlen)) {
930 			SMBSDEBUG("pullupmsg failed!\n");
931 			freemsg(m);
932 			return (NULL);
933 		}
934 	}
935 	return (m);
936 }
937 
938 
939 /*
940  * m_split : split the mblk from the offset(len0) to the end.
941  * Partition an mbuf chain in two pieces, returning the tail --
942  * all but the first len0 bytes.  In case of failure, it returns NULL and
943  * attempts to restore the chain to its original state.
944  * Similar to dupmsg() + adjmsg() on Solaris.
945  */
946 /*ARGSUSED*/
947 mblk_t *
948 m_split(
949 	mblk_t *m0,
950 	int len0,
951 	int wait)
952 {
953 	mblk_t *m, *n;
954 	int mbl, len = len0;
955 	ptrdiff_t	diff;
956 
957 #if 0 /* If life were simple, this would be: */
958 	for (m = m0; m && len > MBLKL(m); m = m->b_cont)
959 		len -= MBLKL(m);
960 #else /* but with LP64 and picky lint we have: */
961 	for (m = m0; m; m = m->b_cont) {
962 		/*LINTED*/
963 		diff = MBLKL(m);
964 		ASSERT(diff == (ptrdiff_t)((int)diff));
965 		mbl = (int)diff;
966 		if (len <= mbl)
967 			break;
968 		len -= mbl;
969 	}
970 #endif
971 
972 	if (m == 0)
973 		return (0);
974 
975 	/* This is the one to split (dupb, adjust) */
976 	if ((n = dupb(m)) == 0)
977 		return (0);
978 
979 	/*LINTED*/
980 	ASSERT(len <= MBLKL(m));
981 
982 	m->b_wptr = m->b_rptr + len;
983 	n->b_rptr += len;
984 
985 	/* Move any b_cont (tail) to the new head. */
986 	n->b_cont = m->b_cont;
987 	m->b_cont = NULL;
988 
989 	return (n);
990 }
991