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