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