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 }