1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 2000, 2001 Boris Popov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Boris Popov. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * FreeBSD: src/sys/kern/subr_mchain.c,v 1.4 2002/02/21 16:23:38 bp Exp 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD$"); 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/errno.h> 43#include <sys/mbuf.h> 44#include <sys/uio.h> 45 46#include <uvm/uvm_extern.h> 47 48#include <netsmb/mchain.h> 49 50#define MBERROR(x) aprint_error x 51#define MBPANIC(x) aprint_error x 52 53static struct mbuf * 54m_getm(struct mbuf *m, size_t len, int how, int type) 55{ 56 struct mbuf *top, *tail, *mp, *mtail = NULL; 57 58 mp = m_get(how, type); 59 if (mp == NULL) 60 return (NULL); 61 else if (len > MINCLSIZE) { 62 m_clget(mp, how); 63 if ((mp->m_flags & M_EXT) == 0) { 64 m_free(mp); 65 return (NULL); 66 } 67 } 68 mp->m_len = 0; 69 len -= min(len, M_TRAILINGSPACE(mp)); 70 71 if (m != NULL) 72 for (mtail = m; mtail->m_next != NULL; mtail = mtail->m_next); 73 else 74 m = mp; 75 76 top = tail = mp; 77 while (len > 0) { 78 mp = m_get(how, type); 79 if (mp == NULL) 80 goto failed; 81 82 tail->m_next = mp; 83 tail = mp; 84 if (len > MINCLSIZE) { 85 m_clget(mp, how); 86 if ((mp->m_flags & M_EXT) == 0) 87 goto failed; 88 } 89 90 mp->m_len = 0; 91 len -= min(len, M_TRAILINGSPACE(mp)); 92 } 93 94 if (mtail != NULL) 95 mtail->m_next = top; 96 return (m); 97 98failed: 99 m_freem(top); 100 return (NULL); 101} 102 103 104/* 105 * Various helper functions 106 */ 107int 108m_fixhdr(struct mbuf *m0) 109{ 110 struct mbuf *m = m0; 111 size_t len = 0; 112 113 while (m) { 114 len += m->m_len; 115 m = m->m_next; 116 } 117 m0->m_pkthdr.len = len; 118 return len; 119} 120 121int 122mb_init(struct mbchain *mbp) 123{ 124 struct mbuf *m; 125 126 m = m_gethdr(M_WAIT, MT_DATA); 127 if (m == NULL) 128 return ENOBUFS; 129 m->m_len = 0; 130 mb_initm(mbp, m); 131 return 0; 132} 133 134void 135mb_initm(struct mbchain *mbp, struct mbuf *m) 136{ 137 memset(mbp, 0, sizeof(*mbp)); 138 mbp->mb_top = mbp->mb_cur = m; 139 mbp->mb_mleft = M_TRAILINGSPACE(m); 140} 141 142void 143mb_done(struct mbchain *mbp) 144{ 145 if (mbp->mb_top) { 146 m_freem(mbp->mb_top); 147 mbp->mb_top = NULL; 148 } 149} 150 151struct mbuf * 152mb_detach(struct mbchain *mbp) 153{ 154 struct mbuf *m; 155 156 m = mbp->mb_top; 157 mbp->mb_top = NULL; 158 return m; 159} 160 161int 162mb_fixhdr(struct mbchain *mbp) 163{ 164 return mbp->mb_top->m_pkthdr.len = m_fixhdr(mbp->mb_top); 165} 166 167/* 168 * Check if object of size 'size' fit to the current position and 169 * allocate new mbuf if not. Advance pointers and increase length of mbuf(s). 170 * Return pointer to the object placeholder or NULL if any error occurred. 171 * Note: size should be <= MLEN 172 */ 173void * 174mb_reserve(struct mbchain *mbp, size_t size) 175{ 176 struct mbuf *m, *mn; 177 void *bpos; 178 179 if (size > MLEN) 180 panic("mb_reserve: size = %zu", size); 181 m = mbp->mb_cur; 182 if (mbp->mb_mleft < size) { 183 mn = m_get(M_WAIT, MT_DATA); 184 if (mn == NULL) 185 return NULL; 186 mbp->mb_cur = m->m_next = mn; 187 m = mn; 188 m->m_len = 0; 189 mbp->mb_mleft = M_TRAILINGSPACE(m); 190 } 191 mbp->mb_mleft -= size; 192 mbp->mb_count += size; 193 bpos = mtod(m, char *) + m->m_len; 194 m->m_len += size; 195 return bpos; 196} 197 198int 199mb_put_uint8(struct mbchain *mbp, u_int8_t x) 200{ 201 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 202} 203 204int 205mb_put_uint16be(struct mbchain *mbp, u_int16_t x) 206{ 207 x = htobe16(x); 208 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 209} 210 211int 212mb_put_uint16le(struct mbchain *mbp, u_int16_t x) 213{ 214 x = htole16(x); 215 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 216} 217 218int 219mb_put_uint32be(struct mbchain *mbp, u_int32_t x) 220{ 221 x = htobe32(x); 222 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 223} 224 225int 226mb_put_uint32le(struct mbchain *mbp, u_int32_t x) 227{ 228 x = htole32(x); 229 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 230} 231 232int 233mb_put_int64be(struct mbchain *mbp, int64_t x) 234{ 235 x = htobe64(x); 236 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 237} 238 239int 240mb_put_int64le(struct mbchain *mbp, int64_t x) 241{ 242 x = htole64(x); 243 return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); 244} 245 246int 247mb_put_mem(struct mbchain *mbp, const char *source, size_t size, int type) 248{ 249 struct mbuf *m; 250 char *dst; 251 const char *src; 252 int error; 253 size_t cplen, mleft, count; 254 255 m = mbp->mb_cur; 256 mleft = mbp->mb_mleft; 257 258 while (size > 0) { 259 if (mleft == 0) { 260 if (m->m_next == NULL) { 261 m = m_getm(m, size, M_WAIT, MT_DATA); 262 if (m == NULL) 263 return ENOBUFS; 264 } 265 m = m->m_next; 266 mleft = M_TRAILINGSPACE(m); 267 continue; 268 } 269 cplen = mleft > size ? size : mleft; 270 dst = mtod(m, char *) + m->m_len; 271 switch (type) { 272 case MB_MCUSTOM: 273 error = mbp->mb_copy(mbp, source, dst, cplen); 274 if (error) 275 return error; 276 break; 277 case MB_MINLINE: 278 for (src = source, count = cplen; count; count--) 279 *dst++ = *src++; 280 break; 281 case MB_MSYSTEM: 282 memcpy(dst, source, cplen); 283 break; 284 case MB_MUSER: 285 error = copyin(source, dst, cplen); 286 if (error) 287 return error; 288 break; 289 case MB_MZERO: 290 memset(dst, 0, cplen); 291 break; 292 } 293 size -= cplen; 294 source += cplen; 295 m->m_len += cplen; 296 mleft -= cplen; 297 mbp->mb_count += cplen; 298 } 299 mbp->mb_cur = m; 300 mbp->mb_mleft = mleft; 301 return 0; 302} 303 304int 305mb_put_mbuf(struct mbchain *mbp, struct mbuf *m) 306{ 307 mbp->mb_cur->m_next = m; 308 while (m) { 309 mbp->mb_count += m->m_len; 310 if (m->m_next == NULL) 311 break; 312 m = m->m_next; 313 } 314 mbp->mb_mleft = M_TRAILINGSPACE(m); 315 mbp->mb_cur = m; 316 return 0; 317} 318 319/* 320 * copies a uio scatter/gather list to an mbuf chain. 321 */ 322int 323mb_put_uio(struct mbchain *mbp, struct uio *uiop, size_t size) 324{ 325 size_t left; 326 int mtype, error; 327 328 mtype = VMSPACE_IS_KERNEL_P(uiop->uio_vmspace) ? MB_MSYSTEM : MB_MUSER; 329 330 while (size > 0 && uiop->uio_resid) { 331 if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) 332 return EFBIG; 333 left = uiop->uio_iov->iov_len; 334 if (left == 0) { 335 uiop->uio_iov++; 336 uiop->uio_iovcnt--; 337 continue; 338 } 339 if (left > size) 340 left = size; 341 error = mb_put_mem(mbp, uiop->uio_iov->iov_base, left, mtype); 342 if (error) 343 return error; 344 uiop->uio_offset += left; 345 uiop->uio_resid -= left; 346 uiop->uio_iov->iov_base = 347 (char *)uiop->uio_iov->iov_base + left; 348 uiop->uio_iov->iov_len -= left; 349 size -= left; 350 } 351 return 0; 352} 353 354/* 355 * Routines for fetching data from an mbuf chain 356 */ 357int 358md_init(struct mdchain *mdp) 359{ 360 struct mbuf *m; 361 362 m = m_gethdr(M_WAIT, MT_DATA); 363 if (m == NULL) 364 return ENOBUFS; 365 m->m_len = 0; 366 md_initm(mdp, m); 367 return 0; 368} 369 370void 371md_initm(struct mdchain *mdp, struct mbuf *m) 372{ 373 memset(mdp, 0, sizeof(*mdp)); 374 mdp->md_top = mdp->md_cur = m; 375 mdp->md_pos = mtod(m, u_char*); 376} 377 378void 379md_done(struct mdchain *mdp) 380{ 381 if (mdp->md_top) { 382 m_freem(mdp->md_top); 383 mdp->md_top = NULL; 384 } 385} 386 387/* 388 * Append a separate mbuf chain. It is caller responsibility to prevent 389 * multiple calls to fetch/record routines. 390 */ 391void 392md_append_record(struct mdchain *mdp, struct mbuf *top) 393{ 394 struct mbuf *m; 395 396 if (mdp->md_top == NULL) { 397 md_initm(mdp, top); 398 return; 399 } 400 m = mdp->md_top; 401 while (m->m_nextpkt) 402 m = m->m_nextpkt; 403 m->m_nextpkt = top; 404 top->m_nextpkt = NULL; 405 return; 406} 407 408/* 409 * Put next record in place of existing 410 */ 411int 412md_next_record(struct mdchain *mdp) 413{ 414 struct mbuf *m; 415 416 if (mdp->md_top == NULL) 417 return ENOENT; 418 m = mdp->md_top->m_nextpkt; 419 md_done(mdp); 420 if (m == NULL) 421 return ENOENT; 422 md_initm(mdp, m); 423 return 0; 424} 425 426int 427md_get_uint8(struct mdchain *mdp, u_int8_t *x) 428{ 429 return md_get_mem(mdp, x, 1, MB_MINLINE); 430} 431 432int 433md_get_uint16(struct mdchain *mdp, u_int16_t *x) 434{ 435 return md_get_mem(mdp, (void *)x, 2, MB_MINLINE); 436} 437 438int 439md_get_uint16le(struct mdchain *mdp, u_int16_t *x) 440{ 441 u_int16_t v; 442 int error = md_get_uint16(mdp, &v); 443 444 *x = le16toh(v); 445 return error; 446} 447 448int 449md_get_uint16be(struct mdchain *mdp, u_int16_t *x) { 450 u_int16_t v; 451 int error = md_get_uint16(mdp, &v); 452 453 *x = be16toh(v); 454 return error; 455} 456 457int 458md_get_uint32(struct mdchain *mdp, u_int32_t *x) 459{ 460 return md_get_mem(mdp, (void *)x, 4, MB_MINLINE); 461} 462 463int 464md_get_uint32be(struct mdchain *mdp, u_int32_t *x) 465{ 466 u_int32_t v; 467 int error; 468 469 error = md_get_uint32(mdp, &v); 470 *x = be32toh(v); 471 return error; 472} 473 474int 475md_get_uint32le(struct mdchain *mdp, u_int32_t *x) 476{ 477 u_int32_t v; 478 int error; 479 480 error = md_get_uint32(mdp, &v); 481 *x = le32toh(v); 482 return error; 483} 484 485int 486md_get_int64(struct mdchain *mdp, int64_t *x) 487{ 488 return md_get_mem(mdp, (void *)x, 8, MB_MINLINE); 489} 490 491int 492md_get_int64be(struct mdchain *mdp, int64_t *x) 493{ 494 int64_t v; 495 int error; 496 497 error = md_get_int64(mdp, &v); 498 *x = be64toh(v); 499 return error; 500} 501 502int 503md_get_int64le(struct mdchain *mdp, int64_t *x) 504{ 505 int64_t v; 506 int error; 507 508 error = md_get_int64(mdp, &v); 509 *x = le64toh(v); 510 return error; 511} 512 513int 514md_get_mem(struct mdchain *mdp, void *targetv, size_t size, int type) 515{ 516 char *target = targetv; 517 struct mbuf *m = mdp->md_cur; 518 int error; 519 size_t count; 520 u_char *s; 521 522 while (size > 0) { 523 if (m == NULL) { 524#ifdef MCHAIN_DEBUG 525 MBERROR(("incomplete copy\n")); 526#endif 527 return EBADRPC; 528 } 529 s = mdp->md_pos; 530 count = mtod(m, u_char*) + m->m_len - s; 531 if (count == 0) { 532 mdp->md_cur = m = m->m_next; 533 if (m) 534 s = mdp->md_pos = mtod(m, void *); 535 continue; 536 } 537 if (count > size) 538 count = size; 539 size -= count; 540 mdp->md_pos += count; 541 if (target == NULL) 542 continue; 543 switch (type) { 544 case MB_MUSER: 545 error = copyout(s, target, count); 546 if (error) 547 return error; 548 break; 549 case MB_MSYSTEM: 550 memcpy(target, s, count); 551 break; 552 case MB_MINLINE: 553 while (count--) 554 *target++ = *s++; 555 continue; 556 } 557 target += count; 558 } 559 return 0; 560} 561 562int 563md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret) 564{ 565 struct mbuf *m = mdp->md_cur, *rm; 566 567 rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_WAIT); 568 if (rm == NULL) 569 return EBADRPC; 570 md_get_mem(mdp, NULL, size, MB_MZERO); 571 *ret = rm; 572 return 0; 573} 574 575int 576md_get_uio(struct mdchain *mdp, struct uio *uiop, size_t size) 577{ 578 char *uiocp; 579 size_t left; 580 int mtype, error; 581 582 mtype = VMSPACE_IS_KERNEL_P(uiop->uio_vmspace) ? MB_MSYSTEM : MB_MUSER; 583 while (size > 0 && uiop->uio_resid) { 584 if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) 585 return EFBIG; 586 left = uiop->uio_iov->iov_len; 587 if (left == 0) { 588 uiop->uio_iov++; 589 uiop->uio_iovcnt--; 590 continue; 591 } 592 uiocp = uiop->uio_iov->iov_base; 593 if (left > size) 594 left = size; 595 error = md_get_mem(mdp, uiocp, left, mtype); 596 if (error) 597 return error; 598 uiop->uio_offset += left; 599 uiop->uio_resid -= left; 600 uiop->uio_iov->iov_base = 601 (char *)uiop->uio_iov->iov_base + left; 602 uiop->uio_iov->iov_len -= left; 603 size -= left; 604 } 605 return 0; 606} 607