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