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