#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/stream.h>
#include <sys/cmn_err.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
static bool_t xdrmblk_getint32(XDR *, int32_t *);
static bool_t xdrmblk_putint32(XDR *, int32_t *);
static bool_t xdrmblk_getbytes(XDR *, caddr_t, int);
static bool_t xdrmblk_putbytes(XDR *, caddr_t, int);
static uint_t xdrmblk_getpos(XDR *);
static bool_t xdrmblk_setpos(XDR *, uint_t);
static rpc_inline_t *xdrmblk_inline(XDR *, int);
static void xdrmblk_destroy(XDR *);
static bool_t xdrmblk_control(XDR *, int, void *);
static mblk_t *xdrmblk_alloc(int);
static void xdrmblk_skip_fully_read_mblks(XDR *);
struct xdr_ops xdrmblk_ops = {
xdrmblk_getbytes,
xdrmblk_putbytes,
xdrmblk_getpos,
xdrmblk_setpos,
xdrmblk_inline,
xdrmblk_destroy,
xdrmblk_control,
xdrmblk_getint32,
xdrmblk_putint32
};
struct xdrmblk_params {
int sz;
uint_t apos;
uint_t rpos;
};
void
xdrmblk_init(XDR *xdrs, mblk_t *m, enum xdr_op op, int sz)
{
struct xdrmblk_params *p;
xdrs->x_op = op;
xdrs->x_ops = &xdrmblk_ops;
xdrs->x_base = (caddr_t)m;
xdrs->x_public = NULL;
p = kmem_alloc(sizeof (struct xdrmblk_params), KM_SLEEP);
xdrs->x_private = (caddr_t)p;
p->sz = sz;
p->apos = 0;
p->rpos = 0;
if (op == XDR_DECODE) {
xdrs->x_handy = (int)MBLKL(m);
} else {
xdrs->x_handy = (int)MBLKTAIL(m);
if (p->sz < sizeof (int32_t))
p->sz = sizeof (int32_t);
}
}
static void
xdrmblk_destroy(XDR *xdrs)
{
kmem_free(xdrs->x_private, sizeof (struct xdrmblk_params));
}
static bool_t
xdrmblk_getint32(XDR *xdrs, int32_t *int32p)
{
mblk_t *m;
struct xdrmblk_params *p;
xdrmblk_skip_fully_read_mblks(xdrs);
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)) ||
xdrs->x_handy < sizeof (int32_t)) {
while (!pullupmsg(m, sizeof (int32_t))) {
if (xmsgsize(m) < sizeof (int32_t))
return (FALSE);
delay(hz);
}
p->apos += p->rpos;
p->rpos = 0;
xdrs->x_handy = (int)MBLKL(m);
}
*int32p = ntohl(*((int32_t *)(m->b_rptr)));
m->b_rptr += sizeof (int32_t);
xdrs->x_handy -= sizeof (int32_t);
p->rpos += sizeof (int32_t);
return (TRUE);
}
static bool_t
xdrmblk_putint32(XDR *xdrs, int32_t *int32p)
{
mblk_t *m;
struct xdrmblk_params *p;
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
while (!IS_P2ALIGNED(m->b_wptr, sizeof (int32_t)) ||
xdrs->x_handy < sizeof (int32_t)) {
if (m->b_cont == NULL) {
ASSERT(p->sz >= sizeof (int32_t));
m->b_cont = xdrmblk_alloc(p->sz);
}
m = m->b_cont;
xdrs->x_base = (caddr_t)m;
p->apos += p->rpos;
p->rpos = 0;
if (m == NULL) {
xdrs->x_handy = 0;
return (FALSE);
}
xdrs->x_handy = (int)MBLKTAIL(m);
ASSERT(m->b_rptr == m->b_wptr);
ASSERT(m->b_rptr >= m->b_datap->db_base);
ASSERT(m->b_rptr < m->b_datap->db_lim);
}
*(int32_t *)m->b_wptr = htonl(*int32p);
m->b_wptr += sizeof (int32_t);
xdrs->x_handy -= sizeof (int32_t);
p->rpos += sizeof (int32_t);
ASSERT(m->b_wptr <= m->b_datap->db_lim);
return (TRUE);
}
#define XDRMBLK_BCOPY_LIMIT 16
static bool_t
xdrmblk_getbytes(XDR *xdrs, caddr_t addr, int len)
{
mblk_t *m;
struct xdrmblk_params *p;
int i;
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
while (xdrs->x_handy < len) {
if (xdrs->x_handy > 0) {
if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) {
for (i = 0; i < xdrs->x_handy; i++)
*addr++ = *m->b_rptr++;
} else {
bcopy(m->b_rptr, addr, xdrs->x_handy);
m->b_rptr += xdrs->x_handy;
addr += xdrs->x_handy;
}
len -= xdrs->x_handy;
p->rpos += xdrs->x_handy;
}
m = m->b_cont;
xdrs->x_base = (caddr_t)m;
p->apos += p->rpos;
p->rpos = 0;
if (m == NULL) {
xdrs->x_handy = 0;
return (FALSE);
}
xdrs->x_handy = (int)MBLKL(m);
}
xdrs->x_handy -= len;
p->rpos += len;
if (len < XDRMBLK_BCOPY_LIMIT) {
for (i = 0; i < len; i++)
*addr++ = *m->b_rptr++;
} else {
bcopy(m->b_rptr, addr, len);
m->b_rptr += len;
}
return (TRUE);
}
bool_t
xdrmblk_getmblk(XDR *xdrs, mblk_t **mm, uint_t *lenp)
{
mblk_t *m, *nextm;
struct xdrmblk_params *p;
int len;
uint32_t llen;
if (!xdrmblk_getint32(xdrs, (int32_t *)&llen))
return (FALSE);
*lenp = llen;
m = (mblk_t *)xdrs->x_base;
*mm = m;
len = 0;
llen = roundup(llen, BYTES_PER_XDR_UNIT);
while (m != NULL && len + (int)MBLKL(m) <= llen) {
len += (int)MBLKL(m);
m = m->b_cont;
}
if (len < llen) {
if (m == NULL) {
return (FALSE);
} else {
int tail_bytes = llen - len;
nextm = dupb(m);
if (nextm == NULL)
return (FALSE);
nextm->b_cont = m->b_cont;
m->b_cont = nextm;
m->b_wptr = m->b_rptr + tail_bytes;
nextm->b_rptr += tail_bytes;
ASSERT(nextm->b_rptr != nextm->b_wptr);
m = nextm;
}
}
xdrs->x_base = (caddr_t)m;
xdrs->x_handy = m != NULL ? MBLKL(m) : 0;
p = (struct xdrmblk_params *)xdrs->x_private;
p->apos += p->rpos + llen;
p->rpos = 0;
return (TRUE);
}
static bool_t
xdrmblk_putbytes(XDR *xdrs, caddr_t addr, int len)
{
mblk_t *m;
struct xdrmblk_params *p;
int i;
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
while (xdrs->x_handy < len) {
if (xdrs->x_handy > 0) {
if (xdrs->x_handy < XDRMBLK_BCOPY_LIMIT) {
for (i = 0; i < xdrs->x_handy; i++)
*m->b_wptr++ = *addr++;
} else {
bcopy(addr, m->b_wptr, xdrs->x_handy);
m->b_wptr += xdrs->x_handy;
addr += xdrs->x_handy;
}
len -= xdrs->x_handy;
p->rpos += xdrs->x_handy;
}
if (m->b_cont == NULL)
m->b_cont = xdrmblk_alloc(MAX(len, p->sz));
m = m->b_cont;
xdrs->x_base = (caddr_t)m;
p->apos += p->rpos;
p->rpos = 0;
if (m == NULL) {
xdrs->x_handy = 0;
return (FALSE);
}
xdrs->x_handy = (int)MBLKTAIL(m);
ASSERT(m->b_rptr == m->b_wptr);
ASSERT(m->b_rptr >= m->b_datap->db_base);
ASSERT(m->b_rptr < m->b_datap->db_lim);
}
xdrs->x_handy -= len;
p->rpos += len;
if (len < XDRMBLK_BCOPY_LIMIT) {
for (i = 0; i < len; i++)
*m->b_wptr++ = *addr++;
} else {
bcopy(addr, m->b_wptr, len);
m->b_wptr += len;
}
ASSERT(m->b_wptr <= m->b_datap->db_lim);
return (TRUE);
}
bool_t
xdrmblk_putmblk(XDR *xdrs, mblk_t *m, uint_t len)
{
int32_t llen = (int32_t)len;
if (!xdrmblk_putint32(xdrs, &llen))
return (FALSE);
return (xdrmblk_putmblk_raw(xdrs, m));
}
bool_t
xdrmblk_putmblk_raw(XDR *xdrs, mblk_t *m)
{
struct xdrmblk_params *p;
if ((DLEN(m) % BYTES_PER_XDR_UNIT) != 0)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
((mblk_t *)xdrs->x_base)->b_cont = m;
p->apos += p->rpos;
while (m->b_cont) {
p->apos += MBLKL(m);
m = m->b_cont;
}
xdrs->x_base = (caddr_t)m;
xdrs->x_handy = 0;
p->rpos = MBLKL(m);
return (TRUE);
}
static uint_t
xdrmblk_getpos(XDR *xdrs)
{
struct xdrmblk_params *p = (struct xdrmblk_params *)xdrs->x_private;
return (p->apos + p->rpos);
}
static bool_t
xdrmblk_setpos(XDR *xdrs, uint_t pos)
{
mblk_t *m;
struct xdrmblk_params *p;
p = (struct xdrmblk_params *)xdrs->x_private;
if (pos < p->apos)
return (FALSE);
if (pos > p->apos + p->rpos + xdrs->x_handy)
return (FALSE);
if (pos == p->apos + p->rpos)
return (TRUE);
m = (mblk_t *)xdrs->x_base;
ASSERT(m != NULL);
if (xdrs->x_op == XDR_DECODE)
m->b_rptr = m->b_rptr - p->rpos + (pos - p->apos);
else
m->b_wptr = m->b_wptr - p->rpos + (pos - p->apos);
xdrs->x_handy = p->rpos + xdrs->x_handy - (pos - p->apos);
p->rpos = pos - p->apos;
return (TRUE);
}
#ifdef DEBUG
static int xdrmblk_inline_hits = 0;
static int xdrmblk_inline_misses = 0;
static int do_xdrmblk_inline = 1;
#endif
static rpc_inline_t *
xdrmblk_inline(XDR *xdrs, int len)
{
rpc_inline_t *buf;
mblk_t *m;
unsigned char **mptr;
struct xdrmblk_params *p;
if (xdrs->x_op == XDR_FREE)
return (NULL);
#ifdef DEBUG
if (!do_xdrmblk_inline) {
xdrmblk_inline_misses++;
return (NULL);
}
#endif
if (xdrs->x_op == XDR_DECODE)
xdrmblk_skip_fully_read_mblks(xdrs);
if (len <= 0 || xdrs->x_handy < len) {
#ifdef DEBUG
xdrmblk_inline_misses++;
#endif
return (NULL);
}
m = (mblk_t *)xdrs->x_base;
ASSERT(m != NULL);
if (xdrs->x_op == XDR_DECODE) {
mptr = &m->b_rptr;
} else {
mptr = &m->b_wptr;
}
if (!IS_P2ALIGNED(*mptr, sizeof (int32_t)) ||
m->b_datap->db_ref != 1) {
#ifdef DEBUG
xdrmblk_inline_misses++;
#endif
return (NULL);
}
buf = (rpc_inline_t *)*mptr;
p = (struct xdrmblk_params *)xdrs->x_private;
*mptr += len;
xdrs->x_handy -= len;
p->rpos += len;
#ifdef DEBUG
xdrmblk_inline_hits++;
#endif
return (buf);
}
static bool_t
xdrmblk_control(XDR *xdrs, int request, void *info)
{
mblk_t *m;
struct xdrmblk_params *p;
int32_t *int32p;
int len;
switch (request) {
case XDR_PEEK:
xdrmblk_skip_fully_read_mblks(xdrs);
if (xdrs->x_handy < sizeof (int32_t))
return (FALSE);
m = (mblk_t *)xdrs->x_base;
ASSERT(m != NULL);
if (!IS_P2ALIGNED(m->b_rptr, sizeof (int32_t)))
return (FALSE);
int32p = (int32_t *)info;
*int32p = ntohl(*((int32_t *)(m->b_rptr)));
return (TRUE);
case XDR_SKIPBYTES:
int32p = (int32_t *)info;
len = RNDUP((int)(*int32p));
if (len < 0)
return (FALSE);
if (len == 0)
return (TRUE);
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return (FALSE);
p = (struct xdrmblk_params *)xdrs->x_private;
while (xdrs->x_handy < len) {
if (xdrs->x_handy > 0) {
m->b_rptr += xdrs->x_handy;
len -= xdrs->x_handy;
p->rpos += xdrs->x_handy;
}
m = m->b_cont;
xdrs->x_base = (caddr_t)m;
p->apos += p->rpos;
p->rpos = 0;
if (m == NULL) {
xdrs->x_handy = 0;
return (FALSE);
}
xdrs->x_handy = (int)MBLKL(m);
}
xdrs->x_handy -= len;
p->rpos += len;
m->b_rptr += len;
return (TRUE);
default:
return (FALSE);
}
}
#define HDR_SPACE 128
static mblk_t *
xdrmblk_alloc(int sz)
{
mblk_t *mp;
if (sz == 0)
return (NULL);
sz += HDR_SPACE;
while ((mp = allocb(sz, BPRI_LO)) == NULL) {
if (strwaitbuf(sz, BPRI_LO))
return (NULL);
}
mp->b_wptr += HDR_SPACE;
mp->b_rptr = mp->b_wptr;
return (mp);
}
static void
xdrmblk_skip_fully_read_mblks(XDR *xdrs)
{
mblk_t *m;
struct xdrmblk_params *p;
if (xdrs->x_handy != 0)
return;
m = (mblk_t *)xdrs->x_base;
if (m == NULL)
return;
p = (struct xdrmblk_params *)xdrs->x_private;
p->apos += p->rpos;
p->rpos = 0;
do {
m = m->b_cont;
if (m == NULL)
break;
xdrs->x_handy = (int)MBLKL(m);
} while (xdrs->x_handy == 0);
xdrs->x_base = (caddr_t)m;
}