xref: /freebsd/sys/xdr/xdr_mbuf.c (revision 66612e673652fce27f83402b7871fcd58447c5a0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5  * Authors: Doug Rabson <dfr@rabson.org>
6  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/mbuf.h>
34 
35 #include <rpc/types.h>
36 #include <rpc/xdr.h>
37 
38 static void xdrmbuf_destroy(XDR *);
39 static bool_t xdrmbuf_getlong(XDR *, long *);
40 static bool_t xdrmbuf_putlong(XDR *, const long *);
41 static bool_t xdrmbuf_getbytes(XDR *, char *, u_int);
42 static bool_t xdrmbuf_putbytes(XDR *, const char *, u_int);
43 static bool_t xdrmbuf_putmbuf(XDR *, struct mbuf *);
44 /* XXX: w/64-bit pointers, u_int not enough! */
45 static u_int xdrmbuf_getpos(XDR *);
46 static bool_t xdrmbuf_setpos(XDR *, u_int);
47 static int32_t *xdrmbuf_inline(XDR *, u_int);
48 
49 static const struct	xdr_ops xdrmbuf_ops = {
50 	.x_getlong =	xdrmbuf_getlong,
51 	.x_putlong =	xdrmbuf_putlong,
52 	.x_getbytes =	xdrmbuf_getbytes,
53 	.x_putbytes =	xdrmbuf_putbytes,
54 	.x_putmbuf =	xdrmbuf_putmbuf,
55 	.x_getpostn =	xdrmbuf_getpos,
56 	.x_setpostn =	xdrmbuf_setpos,
57 	.x_inline =	xdrmbuf_inline,
58 	.x_destroy =	xdrmbuf_destroy,
59 };
60 
61 /*
62  * The procedure xdrmbuf_create initializes a stream descriptor for a
63  * mbuf.
64  */
65 void
66 xdrmbuf_create(XDR *xdrs, struct mbuf *m, enum xdr_op op)
67 {
68 
69 	KASSERT(m != NULL, ("xdrmbuf_create with NULL mbuf chain"));
70 	xdrs->x_op = op;
71 	xdrs->x_ops = &xdrmbuf_ops;
72 	xdrs->x_base = (char *) m;
73 	if (op == XDR_ENCODE) {
74 		m = m_last(m);
75 		xdrs->x_private = m;
76 		xdrs->x_handy = m->m_len;
77 	} else {
78 		xdrs->x_private = m;
79 		xdrs->x_handy = 0;
80 	}
81 }
82 
83 /*
84  * Append mbuf.  Always succeds and we own mbuf.
85  */
86 static bool_t
87 xdrmbuf_putmbuf(XDR *xdrs, struct mbuf *madd)
88 {
89 	struct mbuf *m;
90 
91 	if (m_length(madd, NULL) == 0) {
92 		m_freem(madd);
93 		return (TRUE);
94 	}
95 
96 	m = (struct mbuf *) xdrs->x_private;
97 	m->m_next = madd;
98 
99 	m = m_last(madd);
100 	xdrs->x_private = m;
101 	xdrs->x_handy = m->m_len;
102 
103 	return (TRUE);
104 }
105 
106 struct mbuf *
107 xdrmbuf_getall(XDR *xdrs)
108 {
109 	struct mbuf *m0, *m;
110 
111 	KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_DECODE,
112 	    ("xdrmbuf_append: invalid XDR stream"));
113 
114 	m0 = (struct mbuf *) xdrs->x_base;
115 	m = (struct mbuf *) xdrs->x_private;
116 	if (m0 != m) {
117 		while (m0->m_next != m)
118 			m0 = m0->m_next;
119 		m0->m_next = NULL;
120 		xdrs->x_private = NULL;
121 	} else {
122 		xdrs->x_base = NULL;
123 		xdrs->x_private = NULL;
124 	}
125 
126 	if (m)
127 		m_adj(m, xdrs->x_handy);
128 	else
129 		m = m_get(M_WAITOK, MT_DATA);
130 	return (m);
131 }
132 
133 static void
134 xdrmbuf_destroy(XDR *xdrs)
135 {
136 
137 	if (xdrs->x_op == XDR_DECODE && xdrs->x_base) {
138 		m_freem((struct mbuf *) xdrs->x_base);
139 		xdrs->x_base = NULL;
140 		xdrs->x_private = NULL;
141 	}
142 }
143 
144 static bool_t
145 xdrmbuf_getlong(XDR *xdrs, long *lp)
146 {
147 	int32_t *p;
148 	int32_t t;
149 
150 	p = xdrmbuf_inline(xdrs, sizeof(int32_t));
151 	if (p) {
152 		t = *p;
153 	} else {
154 		xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t));
155 	}
156 
157 	*lp = ntohl(t);
158 	return (TRUE);
159 }
160 
161 static bool_t
162 xdrmbuf_putlong(XDR *xdrs, const long *lp)
163 {
164 	int32_t *p;
165 	int32_t t = htonl(*lp);
166 
167 	p = xdrmbuf_inline(xdrs, sizeof(int32_t));
168 	if (p) {
169 		*p = t;
170 		return (TRUE);
171 	} else {
172 		return (xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t)));
173 	}
174 }
175 
176 static bool_t
177 xdrmbuf_getbytes(XDR *xdrs, char *addr, u_int len)
178 {
179 	struct mbuf *m = (struct mbuf *) xdrs->x_private;
180 	size_t sz;
181 
182 	while (len > 0) {
183 		/*
184 		 * Make sure we haven't hit the end.
185 		 */
186 		if (!m) {
187 			return (FALSE);
188 		}
189 
190 		/*
191 		 * See how much we can get from this mbuf.
192 		 */
193 		sz = m->m_len - xdrs->x_handy;
194 		if (sz > len)
195 			sz = len;
196 		bcopy(mtod(m, const char *) + xdrs->x_handy, addr, sz);
197 
198 		addr += sz;
199 		xdrs->x_handy += sz;
200 		len -= sz;
201 
202 		if (xdrs->x_handy == m->m_len) {
203 			m = m->m_next;
204 			xdrs->x_private = (void *) m;
205 			xdrs->x_handy = 0;
206 		}
207 	}
208 
209 	return (TRUE);
210 }
211 
212 static bool_t
213 xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len)
214 {
215 	struct mbuf *m = (struct mbuf *) xdrs->x_private;
216 	struct mbuf *n;
217 	size_t sz;
218 
219 	while (len > 0) {
220 		sz = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
221 		if (sz > len)
222 			sz = len;
223 		bcopy(addr, mtod(m, char *) + xdrs->x_handy, sz);
224 		addr += sz;
225 		xdrs->x_handy += sz;
226 		if (xdrs->x_handy > m->m_len)
227 			m->m_len = xdrs->x_handy;
228 		len -= sz;
229 
230 		if (xdrs->x_handy == m->m_len && M_TRAILINGSPACE(m) == 0) {
231 			if (!m->m_next) {
232 				if (m->m_flags & M_EXT)
233 					n = m_getcl(M_WAITOK, m->m_type, 0);
234 				else
235 					n = m_get(M_WAITOK, m->m_type);
236 				m->m_next = n;
237 			}
238 			m = m->m_next;
239 			xdrs->x_private = (void *) m;
240 			xdrs->x_handy = 0;
241 		}
242 	}
243 
244 	return (TRUE);
245 }
246 
247 static u_int
248 xdrmbuf_getpos(XDR *xdrs)
249 {
250 	struct mbuf *m0 = (struct mbuf *) xdrs->x_base;
251 	struct mbuf *m = (struct mbuf *) xdrs->x_private;
252 	u_int pos = 0;
253 
254 	while (m0 && m0 != m) {
255 		pos += m0->m_len;
256 		m0 = m0->m_next;
257 	}
258 	KASSERT(m0, ("Corrupted mbuf chain"));
259 
260 	return (pos + xdrs->x_handy);
261 }
262 
263 static bool_t
264 xdrmbuf_setpos(XDR *xdrs, u_int pos)
265 {
266 	struct mbuf *m = (struct mbuf *) xdrs->x_base;
267 
268 	while (m && pos > m->m_len) {
269 		pos -= m->m_len;
270 		m = m->m_next;
271 	}
272 	KASSERT(m, ("Corrupted mbuf chain"));
273 
274 	xdrs->x_private = (void *) m;
275 	xdrs->x_handy = pos;
276 
277 	return (TRUE);
278 }
279 
280 static int32_t *
281 xdrmbuf_inline(XDR *xdrs, u_int len)
282 {
283 	struct mbuf *m = (struct mbuf *) xdrs->x_private;
284 	size_t available;
285 	char *p;
286 
287 	if (!m)
288 		return (0);
289 	if (xdrs->x_op == XDR_ENCODE) {
290 		available = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
291 	} else {
292 		available = m->m_len - xdrs->x_handy;
293 	}
294 
295 	if (available >= len) {
296 		p = mtod(m, char *) + xdrs->x_handy;
297 		if (((uintptr_t) p) & (sizeof(int32_t) - 1))
298 			return (0);
299 		xdrs->x_handy += len;
300 		if (xdrs->x_handy > m->m_len)
301 			m->m_len = xdrs->x_handy;
302 		return ((int32_t *) p);
303 	}
304 
305 	return (0);
306 }
307