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 }