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 /* XXX: w/64-bit pointers, u_int not enough! */
44 static u_int xdrmbuf_getpos(XDR *);
45 static bool_t xdrmbuf_setpos(XDR *, u_int);
46 static int32_t *xdrmbuf_inline(XDR *, u_int);
47
48 static const struct xdr_ops xdrmbuf_ops = {
49 .x_getlong = xdrmbuf_getlong,
50 .x_putlong = xdrmbuf_putlong,
51 .x_getbytes = xdrmbuf_getbytes,
52 .x_putbytes = xdrmbuf_putbytes,
53 .x_getpostn = xdrmbuf_getpos,
54 .x_setpostn = xdrmbuf_setpos,
55 .x_inline = xdrmbuf_inline,
56 .x_destroy = xdrmbuf_destroy,
57 };
58
59 /*
60 * The procedure xdrmbuf_create initializes a stream descriptor for a
61 * mbuf.
62 */
63 void
xdrmbuf_create(XDR * xdrs,struct mbuf * m,enum xdr_op op)64 xdrmbuf_create(XDR *xdrs, struct mbuf *m, enum xdr_op op)
65 {
66
67 KASSERT(m != NULL, ("xdrmbuf_create with NULL mbuf chain"));
68 xdrs->x_op = op;
69 xdrs->x_ops = &xdrmbuf_ops;
70 xdrs->x_base = (char *) m;
71 if (op == XDR_ENCODE) {
72 m = m_last(m);
73 xdrs->x_private = m;
74 xdrs->x_handy = m->m_len;
75 } else {
76 xdrs->x_private = m;
77 xdrs->x_handy = 0;
78 }
79 }
80
81 void
xdrmbuf_append(XDR * xdrs,struct mbuf * madd)82 xdrmbuf_append(XDR *xdrs, struct mbuf *madd)
83 {
84 struct mbuf *m;
85
86 KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_ENCODE,
87 ("xdrmbuf_append: invalid XDR stream"));
88
89 if (m_length(madd, NULL) == 0) {
90 m_freem(madd);
91 return;
92 }
93
94 m = (struct mbuf *) xdrs->x_private;
95 m->m_next = madd;
96
97 m = m_last(madd);
98 xdrs->x_private = m;
99 xdrs->x_handy = m->m_len;
100 }
101
102 struct mbuf *
xdrmbuf_getall(XDR * xdrs)103 xdrmbuf_getall(XDR *xdrs)
104 {
105 struct mbuf *m0, *m;
106
107 KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_DECODE,
108 ("xdrmbuf_append: invalid XDR stream"));
109
110 m0 = (struct mbuf *) xdrs->x_base;
111 m = (struct mbuf *) xdrs->x_private;
112 if (m0 != m) {
113 while (m0->m_next != m)
114 m0 = m0->m_next;
115 m0->m_next = NULL;
116 xdrs->x_private = NULL;
117 } else {
118 xdrs->x_base = NULL;
119 xdrs->x_private = NULL;
120 }
121
122 if (m)
123 m_adj(m, xdrs->x_handy);
124 else
125 m = m_get(M_WAITOK, MT_DATA);
126 return (m);
127 }
128
129 static void
xdrmbuf_destroy(XDR * xdrs)130 xdrmbuf_destroy(XDR *xdrs)
131 {
132
133 if (xdrs->x_op == XDR_DECODE && xdrs->x_base) {
134 m_freem((struct mbuf *) xdrs->x_base);
135 xdrs->x_base = NULL;
136 xdrs->x_private = NULL;
137 }
138 }
139
140 static bool_t
xdrmbuf_getlong(XDR * xdrs,long * lp)141 xdrmbuf_getlong(XDR *xdrs, long *lp)
142 {
143 int32_t *p;
144 int32_t t;
145
146 p = xdrmbuf_inline(xdrs, sizeof(int32_t));
147 if (p) {
148 t = *p;
149 } else {
150 xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t));
151 }
152
153 *lp = ntohl(t);
154 return (TRUE);
155 }
156
157 static bool_t
xdrmbuf_putlong(XDR * xdrs,const long * lp)158 xdrmbuf_putlong(XDR *xdrs, const long *lp)
159 {
160 int32_t *p;
161 int32_t t = htonl(*lp);
162
163 p = xdrmbuf_inline(xdrs, sizeof(int32_t));
164 if (p) {
165 *p = t;
166 return (TRUE);
167 } else {
168 return (xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t)));
169 }
170 }
171
172 static bool_t
xdrmbuf_getbytes(XDR * xdrs,char * addr,u_int len)173 xdrmbuf_getbytes(XDR *xdrs, char *addr, u_int len)
174 {
175 struct mbuf *m = (struct mbuf *) xdrs->x_private;
176 size_t sz;
177
178 while (len > 0) {
179 /*
180 * Make sure we haven't hit the end.
181 */
182 if (!m) {
183 return (FALSE);
184 }
185
186 /*
187 * See how much we can get from this mbuf.
188 */
189 sz = m->m_len - xdrs->x_handy;
190 if (sz > len)
191 sz = len;
192 bcopy(mtod(m, const char *) + xdrs->x_handy, addr, sz);
193
194 addr += sz;
195 xdrs->x_handy += sz;
196 len -= sz;
197
198 if (xdrs->x_handy == m->m_len) {
199 m = m->m_next;
200 xdrs->x_private = (void *) m;
201 xdrs->x_handy = 0;
202 }
203 }
204
205 return (TRUE);
206 }
207
208 static bool_t
xdrmbuf_putbytes(XDR * xdrs,const char * addr,u_int len)209 xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len)
210 {
211 struct mbuf *m = (struct mbuf *) xdrs->x_private;
212 struct mbuf *n;
213 size_t sz;
214
215 while (len > 0) {
216 sz = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
217 if (sz > len)
218 sz = len;
219 bcopy(addr, mtod(m, char *) + xdrs->x_handy, sz);
220 addr += sz;
221 xdrs->x_handy += sz;
222 if (xdrs->x_handy > m->m_len)
223 m->m_len = xdrs->x_handy;
224 len -= sz;
225
226 if (xdrs->x_handy == m->m_len && M_TRAILINGSPACE(m) == 0) {
227 if (!m->m_next) {
228 if (m->m_flags & M_EXT)
229 n = m_getcl(M_WAITOK, m->m_type, 0);
230 else
231 n = m_get(M_WAITOK, m->m_type);
232 m->m_next = n;
233 }
234 m = m->m_next;
235 xdrs->x_private = (void *) m;
236 xdrs->x_handy = 0;
237 }
238 }
239
240 return (TRUE);
241 }
242
243 static u_int
xdrmbuf_getpos(XDR * xdrs)244 xdrmbuf_getpos(XDR *xdrs)
245 {
246 struct mbuf *m0 = (struct mbuf *) xdrs->x_base;
247 struct mbuf *m = (struct mbuf *) xdrs->x_private;
248 u_int pos = 0;
249
250 while (m0 && m0 != m) {
251 pos += m0->m_len;
252 m0 = m0->m_next;
253 }
254 KASSERT(m0, ("Corrupted mbuf chain"));
255
256 return (pos + xdrs->x_handy);
257 }
258
259 static bool_t
xdrmbuf_setpos(XDR * xdrs,u_int pos)260 xdrmbuf_setpos(XDR *xdrs, u_int pos)
261 {
262 struct mbuf *m = (struct mbuf *) xdrs->x_base;
263
264 while (m && pos > m->m_len) {
265 pos -= m->m_len;
266 m = m->m_next;
267 }
268 KASSERT(m, ("Corrupted mbuf chain"));
269
270 xdrs->x_private = (void *) m;
271 xdrs->x_handy = pos;
272
273 return (TRUE);
274 }
275
276 static int32_t *
xdrmbuf_inline(XDR * xdrs,u_int len)277 xdrmbuf_inline(XDR *xdrs, u_int len)
278 {
279 struct mbuf *m = (struct mbuf *) xdrs->x_private;
280 size_t available;
281 char *p;
282
283 if (!m)
284 return (0);
285 if (xdrs->x_op == XDR_ENCODE) {
286 available = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
287 } else {
288 available = m->m_len - xdrs->x_handy;
289 }
290
291 if (available >= len) {
292 p = mtod(m, char *) + xdrs->x_handy;
293 if (((uintptr_t) p) & (sizeof(int32_t) - 1))
294 return (0);
295 xdrs->x_handy += len;
296 if (xdrs->x_handy > m->m_len)
297 m->m_len = xdrs->x_handy;
298 return ((int32_t *) p);
299 }
300
301 return (0);
302 }
303