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