/*- * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ * Authors: Doug Rabson * Developed with Red Inc: Alfred Perlstein * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include static void xdrmbuf_destroy(XDR *); static bool_t xdrmbuf_getlong(XDR *, long *); static bool_t xdrmbuf_putlong(XDR *, const long *); static bool_t xdrmbuf_getbytes(XDR *, char *, u_int); static bool_t xdrmbuf_putbytes(XDR *, const char *, u_int); /* XXX: w/64-bit pointers, u_int not enough! */ static u_int xdrmbuf_getpos(XDR *); static bool_t xdrmbuf_setpos(XDR *, u_int); static int32_t *xdrmbuf_inline(XDR *, u_int); static const struct xdr_ops xdrmbuf_ops = { xdrmbuf_getlong, xdrmbuf_putlong, xdrmbuf_getbytes, xdrmbuf_putbytes, xdrmbuf_getpos, xdrmbuf_setpos, xdrmbuf_inline, xdrmbuf_destroy }; /* * The procedure xdrmbuf_create initializes a stream descriptor for a * mbuf. */ void xdrmbuf_create(XDR *xdrs, struct mbuf *m, enum xdr_op op) { KASSERT(m != NULL, ("xdrmbuf_create with NULL mbuf chain")); xdrs->x_op = op; xdrs->x_ops = &xdrmbuf_ops; xdrs->x_base = (char *) m; if (op == XDR_ENCODE) { m = m_last(m); xdrs->x_private = m; xdrs->x_handy = m->m_len; } else { xdrs->x_private = m; xdrs->x_handy = 0; } } void xdrmbuf_append(XDR *xdrs, struct mbuf *madd) { struct mbuf *m; KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_ENCODE, ("xdrmbuf_append: invalid XDR stream")); if (m_length(madd, NULL) == 0) { m_freem(madd); return; } m = (struct mbuf *) xdrs->x_private; m->m_next = madd; m = m_last(madd); xdrs->x_private = m; xdrs->x_handy = m->m_len; } struct mbuf * xdrmbuf_getall(XDR *xdrs) { struct mbuf *m0, *m; KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_DECODE, ("xdrmbuf_append: invalid XDR stream")); m0 = (struct mbuf *) xdrs->x_base; m = (struct mbuf *) xdrs->x_private; if (m0 != m) { while (m0->m_next != m) m0 = m0->m_next; m0->m_next = NULL; xdrs->x_private = NULL; } else { xdrs->x_base = NULL; xdrs->x_private = NULL; } if (m) m_adj(m, xdrs->x_handy); else m = m_get(M_WAITOK, MT_DATA); return (m); } static void xdrmbuf_destroy(XDR *xdrs) { if (xdrs->x_op == XDR_DECODE && xdrs->x_base) { m_freem((struct mbuf *) xdrs->x_base); xdrs->x_base = NULL; xdrs->x_private = NULL; } } static bool_t xdrmbuf_getlong(XDR *xdrs, long *lp) { int32_t *p; int32_t t; p = xdrmbuf_inline(xdrs, sizeof(int32_t)); if (p) { t = *p; } else { xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t)); } *lp = ntohl(t); return (TRUE); } static bool_t xdrmbuf_putlong(xdrs, lp) XDR *xdrs; const long *lp; { int32_t *p; int32_t t = htonl(*lp); p = xdrmbuf_inline(xdrs, sizeof(int32_t)); if (p) { *p = t; return (TRUE); } else { return (xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t))); } } static bool_t xdrmbuf_getbytes(XDR *xdrs, char *addr, u_int len) { struct mbuf *m = (struct mbuf *) xdrs->x_private; size_t sz; while (len > 0) { /* * Make sure we haven't hit the end. */ if (!m) { return (FALSE); } /* * See how much we can get from this mbuf. */ sz = m->m_len - xdrs->x_handy; if (sz > len) sz = len; bcopy(mtod(m, const char *) + xdrs->x_handy, addr, sz); addr += sz; xdrs->x_handy += sz; len -= sz; if (xdrs->x_handy == m->m_len) { m = m->m_next; xdrs->x_private = (void *) m; xdrs->x_handy = 0; } } return (TRUE); } static bool_t xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len) { struct mbuf *m = (struct mbuf *) xdrs->x_private; struct mbuf *n; size_t sz; while (len > 0) { sz = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy); if (sz > len) sz = len; bcopy(addr, mtod(m, char *) + xdrs->x_handy, sz); addr += sz; xdrs->x_handy += sz; if (xdrs->x_handy > m->m_len) m->m_len = xdrs->x_handy; len -= sz; if (xdrs->x_handy == m->m_len && M_TRAILINGSPACE(m) == 0) { if (!m->m_next) { if (m->m_flags & M_EXT) n = m_getcl(M_WAITOK, m->m_type, 0); else n = m_get(M_WAITOK, m->m_type); m->m_next = n; } m = m->m_next; xdrs->x_private = (void *) m; xdrs->x_handy = 0; } } return (TRUE); } static u_int xdrmbuf_getpos(XDR *xdrs) { struct mbuf *m0 = (struct mbuf *) xdrs->x_base; struct mbuf *m = (struct mbuf *) xdrs->x_private; u_int pos = 0; while (m0 && m0 != m) { pos += m0->m_len; m0 = m0->m_next; } KASSERT(m0, ("Corrupted mbuf chain")); return (pos + xdrs->x_handy); } static bool_t xdrmbuf_setpos(XDR *xdrs, u_int pos) { struct mbuf *m = (struct mbuf *) xdrs->x_base; while (m && pos > m->m_len) { pos -= m->m_len; m = m->m_next; } KASSERT(m, ("Corrupted mbuf chain")); xdrs->x_private = (void *) m; xdrs->x_handy = pos; return (TRUE); } static int32_t * xdrmbuf_inline(XDR *xdrs, u_int len) { struct mbuf *m = (struct mbuf *) xdrs->x_private; size_t available; char *p; if (!m) return (0); if (xdrs->x_op == XDR_ENCODE) { available = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy); } else { available = m->m_len - xdrs->x_handy; } if (available >= len) { p = mtod(m, char *) + xdrs->x_handy; if (((uintptr_t) p) & (sizeof(int32_t) - 1)) return (0); xdrs->x_handy += len; if (xdrs->x_handy > m->m_len) m->m_len = xdrs->x_handy; return ((int32_t *) p); } return (0); }