1 | /* $NetBSD: subr_mchain.c,v 1.23 2014/11/15 18:52:45 nakayama Exp $ */ |
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: subr_mchain.c,v 1.23 2014/11/15 18:52:45 nakayama Exp $" ); |
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 | |
53 | static struct mbuf * |
54 | m_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 | |
98 | failed: |
99 | m_freem(top); |
100 | return (NULL); |
101 | } |
102 | |
103 | |
104 | /* |
105 | * Various helper functions |
106 | */ |
107 | int |
108 | m_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 | |
121 | int |
122 | mb_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 | |
134 | void |
135 | mb_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 | |
142 | void |
143 | mb_done(struct mbchain *mbp) |
144 | { |
145 | if (mbp->mb_top) { |
146 | m_freem(mbp->mb_top); |
147 | mbp->mb_top = NULL; |
148 | } |
149 | } |
150 | |
151 | struct mbuf * |
152 | mb_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 | |
161 | int |
162 | mb_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 | */ |
173 | void * |
174 | mb_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 | |
198 | int |
199 | mb_put_uint8(struct mbchain *mbp, u_int8_t x) |
200 | { |
201 | return mb_put_mem(mbp, (void *)&x, sizeof(x), MB_MSYSTEM); |
202 | } |
203 | |
204 | int |
205 | mb_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 | |
211 | int |
212 | mb_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 | |
218 | int |
219 | mb_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 | |
225 | int |
226 | mb_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 | |
232 | int |
233 | mb_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 | |
239 | int |
240 | mb_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 | |
246 | int |
247 | mb_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 | size_t srclen, dstlen; |
255 | |
256 | m = mbp->mb_cur; |
257 | mleft = mbp->mb_mleft; |
258 | |
259 | while (size > 0) { |
260 | if (mleft == 0) { |
261 | if (m->m_next == NULL) { |
262 | m = m_getm(m, size, M_WAIT, MT_DATA); |
263 | if (m == NULL) |
264 | return ENOBUFS; |
265 | } |
266 | m = m->m_next; |
267 | mleft = M_TRAILINGSPACE(m); |
268 | continue; |
269 | } |
270 | cplen = mleft > size ? size : mleft; |
271 | srclen = dstlen = cplen; |
272 | dst = mtod(m, char *) + m->m_len; |
273 | switch (type) { |
274 | case MB_MCUSTOM: |
275 | srclen = size; |
276 | dstlen = mleft; |
277 | error = mbp->mb_copy(mbp, source, dst, &srclen, &dstlen); |
278 | if (error == E2BIG) { |
279 | mleft = 0; |
280 | continue; |
281 | } |
282 | if (error) |
283 | return error; |
284 | break; |
285 | case MB_MINLINE: |
286 | for (src = source, count = cplen; count; count--) |
287 | *dst++ = *src++; |
288 | break; |
289 | case MB_MSYSTEM: |
290 | memcpy(dst, source, cplen); |
291 | break; |
292 | case MB_MUSER: |
293 | error = copyin(source, dst, cplen); |
294 | if (error) |
295 | return error; |
296 | break; |
297 | case MB_MZERO: |
298 | memset(dst, 0, cplen); |
299 | break; |
300 | } |
301 | size -= srclen; |
302 | source += srclen; |
303 | m->m_len += dstlen; |
304 | mleft -= dstlen; |
305 | mbp->mb_count += dstlen; |
306 | } |
307 | mbp->mb_cur = m; |
308 | mbp->mb_mleft = mleft; |
309 | return 0; |
310 | } |
311 | |
312 | int |
313 | mb_put_mbuf(struct mbchain *mbp, struct mbuf *m) |
314 | { |
315 | mbp->mb_cur->m_next = m; |
316 | while (m) { |
317 | mbp->mb_count += m->m_len; |
318 | if (m->m_next == NULL) |
319 | break; |
320 | m = m->m_next; |
321 | } |
322 | mbp->mb_mleft = M_TRAILINGSPACE(m); |
323 | mbp->mb_cur = m; |
324 | return 0; |
325 | } |
326 | |
327 | /* |
328 | * copies a uio scatter/gather list to an mbuf chain. |
329 | */ |
330 | int |
331 | mb_put_uio(struct mbchain *mbp, struct uio *uiop, size_t size) |
332 | { |
333 | size_t left; |
334 | int mtype, error; |
335 | |
336 | mtype = VMSPACE_IS_KERNEL_P(uiop->uio_vmspace) ? MB_MSYSTEM : MB_MUSER; |
337 | |
338 | while (size > 0 && uiop->uio_resid) { |
339 | if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) |
340 | return EFBIG; |
341 | left = uiop->uio_iov->iov_len; |
342 | if (left == 0) { |
343 | uiop->uio_iov++; |
344 | uiop->uio_iovcnt--; |
345 | continue; |
346 | } |
347 | if (left > size) |
348 | left = size; |
349 | error = mb_put_mem(mbp, uiop->uio_iov->iov_base, left, mtype); |
350 | if (error) |
351 | return error; |
352 | uiop->uio_offset += left; |
353 | uiop->uio_resid -= left; |
354 | uiop->uio_iov->iov_base = |
355 | (char *)uiop->uio_iov->iov_base + left; |
356 | uiop->uio_iov->iov_len -= left; |
357 | size -= left; |
358 | } |
359 | return 0; |
360 | } |
361 | |
362 | /* |
363 | * Routines for fetching data from an mbuf chain |
364 | */ |
365 | int |
366 | md_init(struct mdchain *mdp) |
367 | { |
368 | struct mbuf *m; |
369 | |
370 | m = m_gethdr(M_WAIT, MT_DATA); |
371 | if (m == NULL) |
372 | return ENOBUFS; |
373 | m->m_len = 0; |
374 | md_initm(mdp, m); |
375 | return 0; |
376 | } |
377 | |
378 | void |
379 | md_initm(struct mdchain *mdp, struct mbuf *m) |
380 | { |
381 | memset(mdp, 0, sizeof(*mdp)); |
382 | mdp->md_top = mdp->md_cur = m; |
383 | mdp->md_pos = mtod(m, u_char*); |
384 | } |
385 | |
386 | void |
387 | md_done(struct mdchain *mdp) |
388 | { |
389 | if (mdp->md_top) { |
390 | m_freem(mdp->md_top); |
391 | mdp->md_top = NULL; |
392 | } |
393 | } |
394 | |
395 | /* |
396 | * Append a separate mbuf chain. It is caller responsibility to prevent |
397 | * multiple calls to fetch/record routines. |
398 | */ |
399 | void |
400 | md_append_record(struct mdchain *mdp, struct mbuf *top) |
401 | { |
402 | struct mbuf *m; |
403 | |
404 | if (mdp->md_top == NULL) { |
405 | md_initm(mdp, top); |
406 | return; |
407 | } |
408 | m = mdp->md_top; |
409 | while (m->m_nextpkt) |
410 | m = m->m_nextpkt; |
411 | m->m_nextpkt = top; |
412 | top->m_nextpkt = NULL; |
413 | return; |
414 | } |
415 | |
416 | /* |
417 | * Put next record in place of existing |
418 | */ |
419 | int |
420 | md_next_record(struct mdchain *mdp) |
421 | { |
422 | struct mbuf *m; |
423 | |
424 | if (mdp->md_top == NULL) |
425 | return ENOENT; |
426 | m = mdp->md_top->m_nextpkt; |
427 | md_done(mdp); |
428 | if (m == NULL) |
429 | return ENOENT; |
430 | md_initm(mdp, m); |
431 | return 0; |
432 | } |
433 | |
434 | int |
435 | md_get_uint8(struct mdchain *mdp, u_int8_t *x) |
436 | { |
437 | return md_get_mem(mdp, x, 1, MB_MINLINE); |
438 | } |
439 | |
440 | int |
441 | md_get_uint16(struct mdchain *mdp, u_int16_t *x) |
442 | { |
443 | return md_get_mem(mdp, (void *)x, 2, MB_MINLINE); |
444 | } |
445 | |
446 | int |
447 | md_get_uint16le(struct mdchain *mdp, u_int16_t *x) |
448 | { |
449 | u_int16_t v; |
450 | int error = md_get_uint16(mdp, &v); |
451 | |
452 | *x = le16toh(v); |
453 | return error; |
454 | } |
455 | |
456 | int |
457 | md_get_uint16be(struct mdchain *mdp, u_int16_t *x) { |
458 | u_int16_t v; |
459 | int error = md_get_uint16(mdp, &v); |
460 | |
461 | *x = be16toh(v); |
462 | return error; |
463 | } |
464 | |
465 | int |
466 | md_get_uint32(struct mdchain *mdp, u_int32_t *x) |
467 | { |
468 | return md_get_mem(mdp, (void *)x, 4, MB_MINLINE); |
469 | } |
470 | |
471 | int |
472 | md_get_uint32be(struct mdchain *mdp, u_int32_t *x) |
473 | { |
474 | u_int32_t v; |
475 | int error; |
476 | |
477 | error = md_get_uint32(mdp, &v); |
478 | *x = be32toh(v); |
479 | return error; |
480 | } |
481 | |
482 | int |
483 | md_get_uint32le(struct mdchain *mdp, u_int32_t *x) |
484 | { |
485 | u_int32_t v; |
486 | int error; |
487 | |
488 | error = md_get_uint32(mdp, &v); |
489 | *x = le32toh(v); |
490 | return error; |
491 | } |
492 | |
493 | int |
494 | md_get_int64(struct mdchain *mdp, int64_t *x) |
495 | { |
496 | return md_get_mem(mdp, (void *)x, 8, MB_MINLINE); |
497 | } |
498 | |
499 | int |
500 | md_get_int64be(struct mdchain *mdp, int64_t *x) |
501 | { |
502 | int64_t v; |
503 | int error; |
504 | |
505 | error = md_get_int64(mdp, &v); |
506 | *x = be64toh(v); |
507 | return error; |
508 | } |
509 | |
510 | int |
511 | md_get_int64le(struct mdchain *mdp, int64_t *x) |
512 | { |
513 | int64_t v; |
514 | int error; |
515 | |
516 | error = md_get_int64(mdp, &v); |
517 | *x = le64toh(v); |
518 | return error; |
519 | } |
520 | |
521 | int |
522 | md_get_mem(struct mdchain *mdp, void *targetv, size_t size, int type) |
523 | { |
524 | char *target = targetv; |
525 | struct mbuf *m = mdp->md_cur; |
526 | int error; |
527 | size_t count; |
528 | u_char *s; |
529 | |
530 | while (size > 0) { |
531 | if (m == NULL) { |
532 | #ifdef MCHAIN_DEBUG |
533 | MBERROR(("incomplete copy\n" )); |
534 | #endif |
535 | return EBADRPC; |
536 | } |
537 | s = mdp->md_pos; |
538 | count = mtod(m, u_char*) + m->m_len - s; |
539 | if (count == 0) { |
540 | mdp->md_cur = m = m->m_next; |
541 | if (m) |
542 | s = mdp->md_pos = mtod(m, void *); |
543 | continue; |
544 | } |
545 | if (count > size) |
546 | count = size; |
547 | size -= count; |
548 | mdp->md_pos += count; |
549 | if (target == NULL) |
550 | continue; |
551 | switch (type) { |
552 | case MB_MUSER: |
553 | error = copyout(s, target, count); |
554 | if (error) |
555 | return error; |
556 | break; |
557 | case MB_MSYSTEM: |
558 | memcpy(target, s, count); |
559 | break; |
560 | case MB_MINLINE: |
561 | while (count--) |
562 | *target++ = *s++; |
563 | continue; |
564 | } |
565 | target += count; |
566 | } |
567 | return 0; |
568 | } |
569 | |
570 | int |
571 | md_get_mbuf(struct mdchain *mdp, int size, struct mbuf **ret) |
572 | { |
573 | struct mbuf *m = mdp->md_cur, *rm; |
574 | |
575 | rm = m_copym(m, mdp->md_pos - mtod(m, u_char*), size, M_WAIT); |
576 | if (rm == NULL) |
577 | return EBADRPC; |
578 | md_get_mem(mdp, NULL, size, MB_MZERO); |
579 | *ret = rm; |
580 | return 0; |
581 | } |
582 | |
583 | int |
584 | md_get_uio(struct mdchain *mdp, struct uio *uiop, size_t size) |
585 | { |
586 | char *uiocp; |
587 | size_t left; |
588 | int mtype, error; |
589 | |
590 | mtype = VMSPACE_IS_KERNEL_P(uiop->uio_vmspace) ? MB_MSYSTEM : MB_MUSER; |
591 | while (size > 0 && uiop->uio_resid) { |
592 | if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) |
593 | return EFBIG; |
594 | left = uiop->uio_iov->iov_len; |
595 | if (left == 0) { |
596 | uiop->uio_iov++; |
597 | uiop->uio_iovcnt--; |
598 | continue; |
599 | } |
600 | uiocp = uiop->uio_iov->iov_base; |
601 | if (left > size) |
602 | left = size; |
603 | error = md_get_mem(mdp, uiocp, left, mtype); |
604 | if (error) |
605 | return error; |
606 | uiop->uio_offset += left; |
607 | uiop->uio_resid -= left; |
608 | uiop->uio_iov->iov_base = |
609 | (char *)uiop->uio_iov->iov_base + left; |
610 | uiop->uio_iov->iov_len -= left; |
611 | size -= left; |
612 | } |
613 | return 0; |
614 | } |
615 | |