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