#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
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);
static bool_t xdrmbuf_putmbuf(XDR *, struct mbuf *);
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 = {
.x_getlong = xdrmbuf_getlong,
.x_putlong = xdrmbuf_putlong,
.x_getbytes = xdrmbuf_getbytes,
.x_putbytes = xdrmbuf_putbytes,
.x_putmbuf = xdrmbuf_putmbuf,
.x_getpostn = xdrmbuf_getpos,
.x_setpostn = xdrmbuf_setpos,
.x_inline = xdrmbuf_inline,
.x_destroy = xdrmbuf_destroy,
};
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;
}
}
static bool_t
xdrmbuf_putmbuf(XDR *xdrs, struct mbuf *madd)
{
struct mbuf *m;
if (m_length(madd, NULL) == 0) {
m_freem(madd);
return (TRUE);
}
m = (struct mbuf *) xdrs->x_private;
m->m_next = madd;
m = m_last(madd);
xdrs->x_private = m;
xdrs->x_handy = m->m_len;
return (TRUE);
}
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(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) {
if (!m) {
return (FALSE);
}
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);
}