1177633Sdfr/*-
2177633Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3177633Sdfr * Authors: Doug Rabson <dfr@rabson.org>
4177633Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5177633Sdfr *
6177633Sdfr * Redistribution and use in source and binary forms, with or without
7177633Sdfr * modification, are permitted provided that the following conditions
8177633Sdfr * are met:
9177633Sdfr * 1. Redistributions of source code must retain the above copyright
10177633Sdfr *    notice, this list of conditions and the following disclaimer.
11177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12177633Sdfr *    notice, this list of conditions and the following disclaimer in the
13177633Sdfr *    documentation and/or other materials provided with the distribution.
14177633Sdfr *
15177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18177633Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25177633Sdfr * SUCH DAMAGE.
26177633Sdfr */
27177633Sdfr
28177633Sdfr#include <sys/cdefs.h>
29177633Sdfr__FBSDID("$FreeBSD$");
30177633Sdfr
31177633Sdfr#include <sys/param.h>
32177633Sdfr#include <sys/systm.h>
33177633Sdfr#include <sys/malloc.h>
34177633Sdfr#include <sys/mbuf.h>
35177633Sdfr
36177633Sdfr#include <rpc/types.h>
37177633Sdfr#include <rpc/xdr.h>
38177633Sdfr
39177633Sdfrstatic void xdrmbuf_destroy(XDR *);
40177633Sdfrstatic bool_t xdrmbuf_getlong(XDR *, long *);
41177633Sdfrstatic bool_t xdrmbuf_putlong(XDR *, const long *);
42177633Sdfrstatic bool_t xdrmbuf_getbytes(XDR *, char *, u_int);
43177633Sdfrstatic bool_t xdrmbuf_putbytes(XDR *, const char *, u_int);
44177633Sdfr/* XXX: w/64-bit pointers, u_int not enough! */
45177633Sdfrstatic u_int xdrmbuf_getpos(XDR *);
46177633Sdfrstatic bool_t xdrmbuf_setpos(XDR *, u_int);
47177633Sdfrstatic int32_t *xdrmbuf_inline(XDR *, u_int);
48177633Sdfr
49177633Sdfrstatic const struct	xdr_ops xdrmbuf_ops = {
50177633Sdfr	xdrmbuf_getlong,
51177633Sdfr	xdrmbuf_putlong,
52177633Sdfr	xdrmbuf_getbytes,
53177633Sdfr	xdrmbuf_putbytes,
54177633Sdfr	xdrmbuf_getpos,
55177633Sdfr	xdrmbuf_setpos,
56177633Sdfr	xdrmbuf_inline,
57177633Sdfr	xdrmbuf_destroy
58177633Sdfr};
59177633Sdfr
60177633Sdfr/*
61177633Sdfr * The procedure xdrmbuf_create initializes a stream descriptor for a
62177633Sdfr * mbuf.
63177633Sdfr */
64177633Sdfrvoid
65177633Sdfrxdrmbuf_create(XDR *xdrs, struct mbuf *m, enum xdr_op op)
66177633Sdfr{
67177633Sdfr
68184921Sdfr	KASSERT(m != NULL, ("xdrmbuf_create with NULL mbuf chain"));
69177633Sdfr	xdrs->x_op = op;
70177633Sdfr	xdrs->x_ops = &xdrmbuf_ops;
71177633Sdfr	xdrs->x_base = (char *) m;
72177633Sdfr	if (op == XDR_ENCODE) {
73177633Sdfr		m = m_last(m);
74177633Sdfr		xdrs->x_private = m;
75177633Sdfr		xdrs->x_handy = m->m_len;
76177633Sdfr	} else {
77177633Sdfr		xdrs->x_private = m;
78177633Sdfr		xdrs->x_handy = 0;
79177633Sdfr	}
80177633Sdfr}
81177633Sdfr
82184588Sdfrvoid
83184588Sdfrxdrmbuf_append(XDR *xdrs, struct mbuf *madd)
84184588Sdfr{
85184588Sdfr	struct mbuf *m;
86184588Sdfr
87184588Sdfr	KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_ENCODE,
88184588Sdfr	    ("xdrmbuf_append: invalid XDR stream"));
89184588Sdfr
90184588Sdfr	if (m_length(madd, NULL) == 0) {
91184588Sdfr		m_freem(madd);
92184588Sdfr		return;
93184588Sdfr	}
94184588Sdfr
95184588Sdfr	m = (struct mbuf *) xdrs->x_private;
96184588Sdfr	m->m_next = madd;
97184588Sdfr
98184588Sdfr	m = m_last(madd);
99184588Sdfr	xdrs->x_private = m;
100184588Sdfr	xdrs->x_handy = m->m_len;
101184588Sdfr}
102184588Sdfr
103184588Sdfrstruct mbuf *
104184588Sdfrxdrmbuf_getall(XDR *xdrs)
105184588Sdfr{
106184588Sdfr	struct mbuf *m0, *m;
107184588Sdfr
108184588Sdfr	KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_DECODE,
109184588Sdfr	    ("xdrmbuf_append: invalid XDR stream"));
110184588Sdfr
111184588Sdfr	m0 = (struct mbuf *) xdrs->x_base;
112184588Sdfr	m = (struct mbuf *) xdrs->x_private;
113184588Sdfr	if (m0 != m) {
114184588Sdfr		while (m0->m_next != m)
115184588Sdfr			m0 = m0->m_next;
116184588Sdfr		m0->m_next = NULL;
117184588Sdfr		xdrs->x_private = NULL;
118184588Sdfr	} else {
119184588Sdfr		xdrs->x_base = NULL;
120184588Sdfr		xdrs->x_private = NULL;
121184588Sdfr	}
122184588Sdfr
123184692Sdfr	if (m)
124184692Sdfr		m_adj(m, xdrs->x_handy);
125184692Sdfr	else
126248318Sglebius		m = m_get(M_WAITOK, MT_DATA);
127184588Sdfr	return (m);
128184588Sdfr}
129184588Sdfr
130177633Sdfrstatic void
131177633Sdfrxdrmbuf_destroy(XDR *xdrs)
132177633Sdfr{
133177633Sdfr
134177633Sdfr	if (xdrs->x_op == XDR_DECODE && xdrs->x_base) {
135177633Sdfr		m_freem((struct mbuf *) xdrs->x_base);
136177633Sdfr		xdrs->x_base = NULL;
137177633Sdfr		xdrs->x_private = NULL;
138177633Sdfr	}
139177633Sdfr}
140177633Sdfr
141177633Sdfrstatic bool_t
142177633Sdfrxdrmbuf_getlong(XDR *xdrs, long *lp)
143177633Sdfr{
144184588Sdfr	int32_t *p;
145177633Sdfr	int32_t t;
146177633Sdfr
147184588Sdfr	p = xdrmbuf_inline(xdrs, sizeof(int32_t));
148184588Sdfr	if (p) {
149184588Sdfr		t = *p;
150184588Sdfr	} else {
151184588Sdfr		xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t));
152184588Sdfr	}
153184588Sdfr
154177633Sdfr	*lp = ntohl(t);
155177633Sdfr	return (TRUE);
156177633Sdfr}
157177633Sdfr
158177633Sdfrstatic bool_t
159177633Sdfrxdrmbuf_putlong(xdrs, lp)
160177633Sdfr	XDR *xdrs;
161177633Sdfr	const long *lp;
162177633Sdfr{
163184588Sdfr	int32_t *p;
164177633Sdfr	int32_t t = htonl(*lp);
165177633Sdfr
166184588Sdfr	p = xdrmbuf_inline(xdrs, sizeof(int32_t));
167184588Sdfr	if (p) {
168184588Sdfr		*p = t;
169184588Sdfr		return (TRUE);
170184588Sdfr	} else {
171184588Sdfr		return (xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t)));
172184588Sdfr	}
173177633Sdfr}
174177633Sdfr
175177633Sdfrstatic bool_t
176177633Sdfrxdrmbuf_getbytes(XDR *xdrs, char *addr, u_int len)
177177633Sdfr{
178177633Sdfr	struct mbuf *m = (struct mbuf *) xdrs->x_private;
179177633Sdfr	size_t sz;
180177633Sdfr
181177633Sdfr	while (len > 0) {
182177633Sdfr		/*
183177633Sdfr		 * Make sure we haven't hit the end.
184177633Sdfr		 */
185177633Sdfr		if (!m) {
186177633Sdfr			return (FALSE);
187177633Sdfr		}
188177633Sdfr
189177633Sdfr		/*
190177633Sdfr		 * See how much we can get from this mbuf.
191177633Sdfr		 */
192177633Sdfr		sz = m->m_len - xdrs->x_handy;
193177633Sdfr		if (sz > len)
194177633Sdfr			sz = len;
195184588Sdfr		bcopy(mtod(m, const char *) + xdrs->x_handy, addr, sz);
196177633Sdfr
197177633Sdfr		addr += sz;
198177633Sdfr		xdrs->x_handy += sz;
199177633Sdfr		len -= sz;
200177633Sdfr
201177633Sdfr		if (xdrs->x_handy == m->m_len) {
202177633Sdfr			m = m->m_next;
203177633Sdfr			xdrs->x_private = (void *) m;
204177633Sdfr			xdrs->x_handy = 0;
205177633Sdfr		}
206177633Sdfr	}
207177633Sdfr
208177633Sdfr	return (TRUE);
209177633Sdfr}
210177633Sdfr
211177633Sdfrstatic bool_t
212177633Sdfrxdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len)
213177633Sdfr{
214177633Sdfr	struct mbuf *m = (struct mbuf *) xdrs->x_private;
215177633Sdfr	struct mbuf *n;
216177633Sdfr	size_t sz;
217177633Sdfr
218177633Sdfr	while (len > 0) {
219177633Sdfr		sz = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
220177633Sdfr		if (sz > len)
221177633Sdfr			sz = len;
222184588Sdfr		bcopy(addr, mtod(m, char *) + xdrs->x_handy, sz);
223177633Sdfr		addr += sz;
224177633Sdfr		xdrs->x_handy += sz;
225177633Sdfr		if (xdrs->x_handy > m->m_len)
226177633Sdfr			m->m_len = xdrs->x_handy;
227177633Sdfr		len -= sz;
228177633Sdfr
229177633Sdfr		if (xdrs->x_handy == m->m_len && M_TRAILINGSPACE(m) == 0) {
230177633Sdfr			if (!m->m_next) {
231184588Sdfr				if (m->m_flags & M_EXT)
232248318Sglebius					n = m_getcl(M_WAITOK, m->m_type, 0);
233248318Sglebius				else
234248318Sglebius					n = m_get(M_WAITOK, m->m_type);
235177633Sdfr				m->m_next = n;
236177633Sdfr			}
237177633Sdfr			m = m->m_next;
238177633Sdfr			xdrs->x_private = (void *) m;
239177633Sdfr			xdrs->x_handy = 0;
240177633Sdfr		}
241177633Sdfr	}
242177633Sdfr
243177633Sdfr	return (TRUE);
244177633Sdfr}
245177633Sdfr
246177633Sdfrstatic u_int
247177633Sdfrxdrmbuf_getpos(XDR *xdrs)
248177633Sdfr{
249177633Sdfr	struct mbuf *m0 = (struct mbuf *) xdrs->x_base;
250177633Sdfr	struct mbuf *m = (struct mbuf *) xdrs->x_private;
251177633Sdfr	u_int pos = 0;
252177633Sdfr
253177633Sdfr	while (m0 && m0 != m) {
254177633Sdfr		pos += m0->m_len;
255177633Sdfr		m0 = m0->m_next;
256177633Sdfr	}
257177633Sdfr	KASSERT(m0, ("Corrupted mbuf chain"));
258177633Sdfr
259177633Sdfr	return (pos + xdrs->x_handy);
260177633Sdfr}
261177633Sdfr
262177633Sdfrstatic bool_t
263177633Sdfrxdrmbuf_setpos(XDR *xdrs, u_int pos)
264177633Sdfr{
265177633Sdfr	struct mbuf *m = (struct mbuf *) xdrs->x_base;
266177633Sdfr
267177633Sdfr	while (m && pos > m->m_len) {
268177633Sdfr		pos -= m->m_len;
269177633Sdfr		m = m->m_next;
270177633Sdfr	}
271177633Sdfr	KASSERT(m, ("Corrupted mbuf chain"));
272177633Sdfr
273177633Sdfr	xdrs->x_private = (void *) m;
274177633Sdfr	xdrs->x_handy = pos;
275177633Sdfr
276177633Sdfr	return (TRUE);
277177633Sdfr}
278177633Sdfr
279177633Sdfrstatic int32_t *
280177633Sdfrxdrmbuf_inline(XDR *xdrs, u_int len)
281177633Sdfr{
282177633Sdfr	struct mbuf *m = (struct mbuf *) xdrs->x_private;
283177633Sdfr	size_t available;
284177633Sdfr	char *p;
285177633Sdfr
286196149Srmacklem	if (!m)
287196149Srmacklem		return (0);
288177633Sdfr	if (xdrs->x_op == XDR_ENCODE) {
289177633Sdfr		available = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
290177633Sdfr	} else {
291177633Sdfr		available = m->m_len - xdrs->x_handy;
292177633Sdfr	}
293177633Sdfr
294177633Sdfr	if (available >= len) {
295177633Sdfr		p = mtod(m, char *) + xdrs->x_handy;
296177633Sdfr		if (((uintptr_t) p) & (sizeof(int32_t) - 1))
297177633Sdfr			return (0);
298177633Sdfr		xdrs->x_handy += len;
299177633Sdfr		if (xdrs->x_handy > m->m_len)
300177633Sdfr			m->m_len = xdrs->x_handy;
301177633Sdfr		return ((int32_t *) p);
302177633Sdfr	}
303177633Sdfr
304177633Sdfr	return (0);
305177633Sdfr}
306