xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c (revision 968633ad8faee931821fd6b656eb0d96d4b186c0)
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 	if (MBLKTAIL(m) < size) {
346 		mn = m_getblk(size, 1);
347 		if (mn == NULL)
348 			return (NULL);
349 		mbp->mb_cur = m->b_cont = mn;
350 		m = mn;
351 	}
352 	/*
353 	 * If 'size' bytes fits into the buffer, then
354 	 * 1. increment the write pointer to the size.
355 	 * 2. return the position from where the memory is reserved.
356 	 */
357 	bpos = m->b_wptr;
358 	m->b_wptr += size;
359 	mbp->mb_count += size;
360 	return (bpos);
361 }
362 
363 /*
364  * All mb_put_*() functions perform an actual copy of the data into mbuf
365  * chain. Functions which have le or be suffixes will perform conversion to
366  * the little- or big-endian data formats.
367  * XXX: Assumes total data length in previous mblks is EVEN.
368  * XXX: Might need to compute the offset from mb_top instead.
369  */
370 int
371 mb_put_padbyte(struct mbchain *mbp)
372 {
373 	caddr_t dst;
374 	char x = 0;
375 
376 	dst = (caddr_t)mbp->mb_cur->b_wptr;
377 
378 	/* only add padding if address is odd */
379 	if ((long)dst & 1)
380 		return (mb_put_mem(mbp, (caddr_t)&x, 1, MB_MSYSTEM));
381 	else
382 		return (0);
383 }
384 
385 int
386 mb_put_uint8(struct mbchain *mbp, u_int8_t x)
387 {
388 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
389 }
390 
391 int
392 mb_put_uint16be(struct mbchain *mbp, u_int16_t x)
393 {
394 	x = htobes(x);
395 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
396 }
397 
398 int
399 mb_put_uint16le(struct mbchain *mbp, u_int16_t x)
400 {
401 	x = htoles(x);
402 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
403 }
404 
405 int
406 mb_put_uint32be(struct mbchain *mbp, u_int32_t x)
407 {
408 	x = htobel(x);
409 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
410 }
411 
412 int
413 mb_put_uint32le(struct mbchain *mbp, u_int32_t x)
414 {
415 	x = htolel(x);
416 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
417 }
418 
419 int
420 mb_put_uint64be(struct mbchain *mbp, u_int64_t x)
421 {
422 	x = htobeq(x);
423 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
424 }
425 
426 int
427 mb_put_uint64le(struct mbchain *mbp, u_int64_t x)
428 {
429 	x = htoleq(x);
430 	return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
431 }
432 
433 /*
434  * mb_put_mem() function copies size bytes of data specified by the source
435  * argument to an mbuf chain.  The type argument specifies the method used
436  * to perform a copy
437  */
438 int
439 mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type)
440 {
441 	mblk_t *m, *n;
442 	caddr_t dst;
443 	c_caddr_t src;
444 	int cplen, error, mleft, count;
445 	uint64_t diff;
446 
447 	m = mbp->mb_cur;
448 
449 	diff = MBLKTAIL(m);
450 	ASSERT(diff == (uint64_t)((int)diff));
451 	mleft = (int)diff;
452 
453 	while (size > 0) {
454 		if (mleft == 0) {
455 			if (m->b_cont == NULL) {
456 				/*
457 				 * Changed m_getm() to m_getblk()
458 				 * with the requested size, so we
459 				 * don't need m_getm() anymore.
460 				 */
461 				n = m_getblk(size, 1);
462 				if (n == NULL)
463 					return (ENOBUFS);
464 				m->b_cont = n;
465 			}
466 			m = m->b_cont;
467 			diff = MBLKTAIL(m);
468 			ASSERT(diff == (uint64_t)((int)diff));
469 			mleft = (int)diff;
470 			continue;
471 		}
472 		cplen = mleft > size ? size : mleft;
473 		dst = (caddr_t)m->b_wptr;
474 		switch (type) {
475 		case MB_MINLINE:
476 			for (src = source, count = cplen; count; count--)
477 				*dst++ = *src++;
478 			break;
479 		case MB_MSYSTEM:
480 			/*
481 			 * Try copying the raw bytes instead of using bcopy()
482 			 */
483 			bcopy(source, dst, cplen);
484 			break;
485 		case MB_MUSER:
486 			error = copyin((void *)source, dst, cplen);
487 			if (error)
488 				return (error);
489 			break;
490 		case MB_MZERO:
491 			bzero(dst, cplen);
492 			break;
493 		}
494 		size -= cplen;
495 		source += cplen;
496 		mleft -= cplen;
497 		m->b_wptr += cplen;
498 		mbp->mb_count += cplen;
499 	}
500 	mbp->mb_cur = m;
501 	return (0);
502 }
503 
504 /*
505  * Append an mblk to the chain.
506  */
507 int
508 mb_put_mbuf(struct mbchain *mbp, mblk_t *m)
509 {
510 	mblk_t *mb;
511 
512 	/* See: linkb(9f) */
513 	for (mb = mbp->mb_cur; mb->b_cont; mb = mb->b_cont)
514 		;
515 	mb->b_cont = m;
516 	mbp->mb_cur = m;
517 	mbp->mb_count += msgdsize(m);
518 
519 	return (0);
520 }
521 
522 /*
523  * copies a uio scatter/gather list to an mbuf chain.
524  */
525 int
526 mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size)
527 {
528 	int left;
529 	int mtype, error;
530 
531 	mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
532 
533 	while (size > 0 && uiop->uio_resid) {
534 		if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) ==
535 		    USER_ADDR_NULL)
536 			return (EFBIG);
537 		left = uio_curriovlen(uiop);
538 		if (left > size)
539 			left = size;
540 		error = mb_put_mem(mbp, CAST_DOWN(caddr_t,
541 		    uio_curriovbase(uiop)), left, mtype);
542 		if (error)
543 			return (error);
544 		uio_update(uiop, left);
545 		size -= left;
546 	}
547 	return (0);
548 }
549 
550 /*
551  * Routines for fetching data from an mbuf chain
552  */
553 int
554 md_init(struct mdchain *mdp)
555 {
556 	mblk_t *m;
557 
558 	m = m_getblk(MLEN, 1);
559 	if (m == NULL)
560 		return (ENOBUFS);
561 	md_initm(mdp, m);
562 	return (0);
563 }
564 
565 void
566 md_initm(struct mdchain *mdp, mblk_t *m)
567 {
568 	bzero(mdp, sizeof (*mdp));
569 	mdp->md_top = mdp->md_cur = m;
570 	mdp->md_pos = m->b_rptr;
571 }
572 
573 void
574 md_done(struct mdchain *mdp)
575 {
576 	mblk_t *m;
577 
578 	/*
579 	 * Deal with the fact that we can error out of
580 	 * smb_t2_reply or smb_nt_reply without using up
581 	 * all the "records" added by md_append_record().
582 	 */
583 	while ((m = mdp->md_top) != NULL) {
584 		mdp->md_top = m->b_next;
585 		m->b_next = NULL;
586 		freemsg(m);
587 	}
588 	/* Avoid dangling references */
589 	mdp->md_cur = NULL;
590 	mdp->md_pos = NULL;
591 }
592 
593 /*
594  * Append a new message (separate mbuf chain).
595  * It is caller responsibility to prevent
596  * multiple calls to fetch/record routines.
597  * XXX: Note (mis)use of mblk->b_next here.
598  */
599 void
600 md_append_record(struct mdchain *mdp, mblk_t *top)
601 {
602 	mblk_t *m;
603 
604 	top->b_next = NULL;
605 	if (mdp->md_top == NULL) {
606 		md_initm(mdp, top);
607 		return;
608 	}
609 	m = mdp->md_top;
610 	/* Get to last message (not b_cont chain) */
611 	while (m->b_next)
612 		m = m->b_next;
613 	m->b_next = top;
614 }
615 
616 /*
617  * Advance mdp->md_top to the next message.
618  * XXX: Note (mis)use of mblk->b_next here.
619  */
620 int
621 md_next_record(struct mdchain *mdp)
622 {
623 	mblk_t *m;
624 
625 	if (mdp->md_top == NULL)
626 		return (ENOENT);
627 	/* Get to next message (not b_cont chain) */
628 	m = mdp->md_top->b_next;
629 	mdp->md_top->b_next = NULL;
630 	md_done(mdp);
631 	if (m == NULL)
632 		return (ENOENT);
633 	md_initm(mdp, m);
634 	return (0);
635 }
636 
637 int
638 md_get_uint8(struct mdchain *mdp, u_int8_t *x)
639 {
640 	return (md_get_mem(mdp, (char *)x, 1, MB_MINLINE));
641 }
642 
643 int
644 md_get_uint16(struct mdchain *mdp, u_int16_t *x)
645 {
646 	return (md_get_mem(mdp, (char *)x, 2, MB_MINLINE));
647 }
648 
649 int
650 md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
651 {
652 	u_int16_t v;
653 	int error = md_get_uint16(mdp, &v);
654 
655 	if (x)
656 		*x = letohs(v);
657 	return (error);
658 }
659 
660 int
661 md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
662 	u_int16_t v;
663 	int error = md_get_uint16(mdp, &v);
664 
665 	if (x)
666 		*x = betohs(v);
667 	return (error);
668 }
669 
670 int
671 md_get_uint32(struct mdchain *mdp, u_int32_t *x)
672 {
673 	return (md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE));
674 }
675 
676 int
677 md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
678 {
679 	u_int32_t v;
680 	int error;
681 
682 	error = md_get_uint32(mdp, &v);
683 	if (x)
684 		*x = betohl(v);
685 	return (error);
686 }
687 
688 int
689 md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
690 {
691 	u_int32_t v;
692 	int error;
693 
694 	error = md_get_uint32(mdp, &v);
695 	if (x)
696 		*x = letohl(v);
697 	return (error);
698 }
699 
700 int
701 md_get_uint64(struct mdchain *mdp, u_int64_t *x)
702 {
703 	return (md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE));
704 }
705 
706 int
707 md_get_uint64be(struct mdchain *mdp, u_int64_t *x)
708 {
709 	u_int64_t v;
710 	int error;
711 
712 	error = md_get_uint64(mdp, &v);
713 	if (x)
714 		*x = betohq(v);
715 	return (error);
716 }
717 
718 int
719 md_get_uint64le(struct mdchain *mdp, u_int64_t *x)
720 {
721 	u_int64_t v;
722 	int error;
723 
724 	error = md_get_uint64(mdp, &v);
725 	if (x)
726 		*x = letohq(v);
727 	return (error);
728 }
729 
730 int
731 md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
732 {
733 	mblk_t *m = mdp->md_cur;
734 	int error;
735 	int count;
736 	unsigned char *s;
737 	uint64_t diff;
738 
739 	while (size > 0) {
740 		if (m == NULL) {
741 			SMBSDEBUG("incomplete copy\n");
742 			return (EBADRPC);
743 		}
744 
745 		/*
746 		 * Offset in the current MBUF.
747 		 */
748 		s = mdp->md_pos;
749 		ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
750 
751 		/* Data remaining. */
752 		diff = (uintptr_t)m->b_wptr - (uintptr_t)s;
753 		ASSERT(diff == (uint64_t)((int)diff));
754 		count = (int)diff;
755 
756 		/*
757 		 * Check if the no. of bytes remaining is less than
758 		 * the bytes requested.
759 		 */
760 		if (count == 0) {
761 			m = m->b_cont;
762 			if (m) {
763 				mdp->md_cur = m;
764 				mdp->md_pos = s = m->b_rptr;
765 			}
766 			continue;
767 		}
768 		if (count > size)
769 			count = size;
770 		size -= count;
771 		mdp->md_pos += count;
772 		if (target == NULL)
773 			continue;
774 		switch (type) {
775 		case MB_MUSER:
776 			error = copyout(s, (void *)target, count);
777 			if (error)
778 				return (error);
779 			break;
780 		case MB_MSYSTEM:
781 			bcopy(s, target, count);
782 			break;
783 		case MB_MINLINE:
784 			while (count--)
785 				*target++ = *s++;
786 			continue;
787 		}
788 		target += count;
789 	}
790 	return (0);
791 }
792 
793 /*
794  * Get the next SIZE bytes as a separate mblk.
795  */
796 int
797 md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret)
798 {
799 	mblk_t *m, *rm;
800 
801 	unsigned char *s;
802 	uint64_t diff;
803 	int off;
804 
805 	/*
806 	 * Offset in the current MBUF.
807 	 */
808 	m = mdp->md_cur;
809 	s = mdp->md_pos;
810 	ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
811 	diff = (uintptr_t)s - (uintptr_t)m->b_rptr;
812 	ASSERT(diff == (uint64_t)((int)diff));
813 	off = (int)diff;
814 
815 	rm = m_copym(m, off, size, M_WAITOK);
816 	if (rm == NULL)
817 		return (EBADRPC);
818 
819 	*ret = rm;
820 	return (0);
821 }
822 
823 int
824 md_get_uio(struct mdchain *mdp, uio_t *uiop, int size)
825 {
826 	size_t left;
827 	int mtype, error;
828 
829 	mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
830 	while (size > 0 && uiop->uio_resid) {
831 		if (uiop->uio_iovcnt <= 0 ||
832 		    uio_curriovbase(uiop) == USER_ADDR_NULL)
833 			return (EFBIG);
834 		left = uio_curriovlen(uiop);
835 		if (left > size)
836 			left = size;
837 		error = md_get_mem(mdp, CAST_DOWN(caddr_t,
838 		    uio_curriovbase(uiop)), left, mtype);
839 		if (error)
840 			return (error);
841 		uio_update(uiop, left);
842 		size -= left;
843 	}
844 	return (0);
845 }
846 
847 /*
848  * Additions for Solaris
849  */
850 
851 /*
852  * concatenate mblk chain n to m.
853  * go till end of data in m.
854  * then add the link of b_cont to n.
855  * See: linkb(9f)
856  */
857 
858 void m_cat(
859 	mblk_t *m,
860 	mblk_t *n)
861 {
862 	if (!n)
863 		return;
864 	while (m->b_cont) {
865 		m = m->b_cont;
866 	}
867 	m->b_cont = n;
868 }
869 
870 /*ARGSUSED*/
871 mblk_t *
872 m_copym(mblk_t *m, int off, int len, int wait)
873 {
874 	mblk_t *n;
875 	size_t dsz;
876 	ssize_t adj;
877 
878 	dsz = msgdsize(m);
879 	if (len == M_COPYALL) {
880 		if (off > dsz)
881 			return (0);
882 	} else {
883 		if ((off + len) > dsz)
884 			return (0);
885 	}
886 
887 	if ((n = dupmsg(m)) == NULL)
888 		return (0);
889 
890 	/* trim from head */
891 	adj = off;
892 	if (!adjmsg(n, adj)) {
893 		freemsg(n);
894 		return (0);
895 	}
896 
897 	/* trim from tail */
898 	if (len != M_COPYALL) {
899 		dsz = msgdsize(n);
900 		ASSERT(len <= dsz);
901 		if (len < dsz) {
902 			adj = (ssize_t)len - (ssize_t)dsz;
903 			ASSERT(adj < 0);
904 			adjmsg(n, adj);
905 		}
906 	}
907 
908 	return (n);
909 }
910 
911 /*
912  * Get "rqlen" contiguous bytes into the first mblk of a chain.
913  */
914 mblk_t *
915 m_pullup(
916 	mblk_t *m,
917 	int rqlen)
918 {
919 	ptrdiff_t diff;
920 
921 	diff = MBLKL(m);
922 	ASSERT(diff == (ptrdiff_t)((int)diff));
923 	if ((int)diff < rqlen) {
924 		/* This should be rare. */
925 		if (!pullupmsg(m, rqlen)) {
926 			SMBSDEBUG("pullupmsg failed!\n");
927 			freemsg(m);
928 			return (NULL);
929 		}
930 	}
931 	return (m);
932 }
933 
934 
935 /*
936  * m_split : split the mblk from the offset(len0) to the end.
937  * Partition an mbuf chain in two pieces, returning the tail --
938  * all but the first len0 bytes.  In case of failure, it returns NULL and
939  * attempts to restore the chain to its original state.
940  * Similar to dupmsg() + adjmsg() on Solaris.
941  */
942 /*ARGSUSED*/
943 mblk_t *
944 m_split(
945 	mblk_t *m0,
946 	int len0,
947 	int wait)
948 {
949 	mblk_t *m, *n;
950 	int mbl, len = len0;
951 	ptrdiff_t	diff;
952 
953 #if 0 /* If life were simple, this would be: */
954 	for (m = m0; m && len > MBLKL(m); m = m->b_cont)
955 		len -= MBLKL(m);
956 #else /* but with LP64 and picky lint we have: */
957 	for (m = m0; m; m = m->b_cont) {
958 		diff = MBLKL(m);
959 		ASSERT(diff == (ptrdiff_t)((int)diff));
960 		mbl = (int)diff;
961 		if (len <= mbl)
962 			break;
963 		len -= mbl;
964 	}
965 #endif
966 
967 	if (m == 0)
968 		return (0);
969 
970 	/* This is the one to split (dupb, adjust) */
971 	if ((n = dupb(m)) == 0)
972 		return (0);
973 
974 	ASSERT(len <= MBLKL(m));
975 
976 	m->b_wptr = m->b_rptr + len;
977 	n->b_rptr += len;
978 
979 	/* Move any b_cont (tail) to the new head. */
980 	n->b_cont = m->b_cont;
981 	m->b_cont = NULL;
982 
983 	return (n);
984 }
985