/*
 * copyright (c) 2004
 * the regents of the university of michigan
 * all rights reserved
 * 
 * permission is granted to use, copy, create derivative works and redistribute
 * this software and such derivative works for any purpose, so long as the name
 * of the university of michigan is not used in any advertising or publicity
 * pertaining to the use or distribution of this software without specific,
 * written prior authorization.  if the above copyright notice or any other
 * identification of the university of michigan is included in any copy of any
 * portion of this software, then the disclaimer below must also be included.
 * 
 * this software is provided as is, without representation from the university
 * of michigan as to its fitness for any purpose, and without warranty by the
 * university of michigan of any kind, either express or implied, including
 * without limitation the implied warranties of merchantability and fitness for
 * a particular purpose. the regents of the university of michigan shall not be
 * liable for any damages, including special, indirect, incidental, or
 * consequential damages, with respect to any claim arising out of or in
 * connection with the use of the software, even if it has been or is hereafter
 * advised of the possibility of such damages.
 */

#include <sys/cdefs.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>

#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/malloc.h>
#include <sys/syscall.h>
#ifdef __FreeBSD__
#include <sys/bio.h>
#endif
#include <sys/buf.h>

#include <nfs4client/nfs4_glue.h>

#include <rpcx/rpcclnt.h>
#include <nfsx/rpcv2.h>
#include <nfsx/nfsproto.h>
#include <nfsxclient/nfs.h>
#include <nfs4client/nfs4.h>
#include <nfsxclient/nfsnode.h>
#include <nfsxclient/nfsm_subs.h>
#include <nfsx/xdr_subs.h>

#ifndef __FreeBSD__
void *
nfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos)
{
        int t1;
        char *cp2;
        void *ret;

        t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
        if (t1 >= s) {
                ret = *dpos;
                *dpos += s;
                return ret;
        }
#ifdef __APPLE__ 		/* XXX - OpenBSD */
	t1 = nfsm_disct(md, dpos, s, t1, &cp2);
#else
        cp2 = nfsm_disct(md, dpos, s, t1);
#endif /* __APPLE__ */
        return cp2;
}

void *
nfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos)
{
        struct mbuf *mb2;
        void *ret;

        if (s > M_TRAILINGSPACE(*mb)) {
                MGET(mb2, M_TRYWAIT, MT_DATA);
                if (s > MLEN)
                        panic("build > MLEN");
                (*mb)->m_next = mb2;
                *mb = mb2;
                (*mb)->m_len = 0;
                *bpos = mtod(*mb, caddr_t);
        }
        ret = *bpos;
        (*mb)->m_len += s;
        *bpos += s;
        return ret;
}

int
nfsm_fhtom_xx(struct vnode *v, int v3, struct mbuf **mb, caddr_t *bpos)
{
        u_int32_t *tl;
        int t1;
        caddr_t cp;

        if (v3) {
                t1 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED;
                if (t1 < M_TRAILINGSPACE(*mb)) {
                        tl = nfsm_build_xx(t1, mb, bpos);
                        *tl++ = txdr_unsigned(VTONFS(v)->n_fhsize);
                        *(tl + ((t1 >> 2) - 2)) = 0;
                        bcopy(VTONFS(v)->n_fhp, tl, VTONFS(v)->n_fhsize);
                } else {
#ifdef __FreeBSD__
                        t1 = nfsm_strtmbuf(mb, bpos,
                            (const char *)VTONFS(v)->n_fhp,
                            VTONFS(v)->n_fhsize);
#else
                        t1 = nfsm_strtmbuf(mb, bpos,
                            (char *)VTONFS(v)->n_fhp, VTONFS(v)->n_fhsize);
#endif /* __FreeBSD__ */
                        if (t1 != 0)
                                return t1;
                }
        } else {
                cp = nfsm_build_xx(NFSX_V2FH, mb, bpos);
                bcopy(VTONFS(v)->n_fhp, cp, NFSX_V2FH);
        }
        return 0;
}

#if 0				/* MARIUS: not needed yet */
int
nfsm_postop_attr_xx(struct vnode **v, int *f, struct mbuf **md,
    caddr_t *dpos)
{
        u_int32_t *tl;
        int t1;
	struct nfsnode *np;
	uint64_t xid;

        struct vnode *ttvp = *v;
        tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
        if (tl == NULL)
                return EBADRPC;
        *f = fxdr_unsigned(int, *tl);
        if (*f != 0) {
		/* XXX np = VTONFS(vp);*/
	  	np = VTONFS(ttvp);
		xid = np->n_xid;
                t1 = nfs_loadattrcache(&ttvp, md, dpos, NULL, 1, &xid);
                if (t1 != 0) {
                        *f = 0;
                        return t1;
                }
                *v = ttvp;
        }
        return 0;
}
#endif

int
nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
{
        int t1;

        t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
        if (t1 >= s) {
                *dpos += s;
                return 0;
        }
        t1 = nfs_adv(md, dpos, s, t1);
        if (t1)
                return t1;
        return 0;
}


int     
nfsm_strtom_xx(const char *a, int s, int m, struct mbuf **mb, caddr_t *bpos)
{               
	u_int32_t *tl;
	int t1;
                
	if (s > m)
		return ENAMETOOLONG;
	t1 = nfsm_rndup(s) + NFSX_UNSIGNED;
	if (t1 <= M_TRAILINGSPACE(*mb)) {
		tl = nfsm_build_xx(t1, mb, bpos);
		*tl++ = txdr_unsigned(s);
		*(tl + ((t1 >> 2) - 2)) = 0;
		bcopy(a, tl, s);
	} else {        
		t1 = nfsm_strtmbuf(mb, bpos, (char *)a, s);
		if (t1 != 0)
			return t1;
	}
	return 0;
} 

#if defined(__APPLE__)
/* XXX from OpenBSD's mbuf.h */
/*
 * Copy data from a buffer back into the indicated mbuf chain,
 * starting "off" bytes from the beginning, extending the mbuf
 * chain if necessary. The mbuf needs to be properly initialized
 * including the setting of m_len.
 */
void				/* XXXMARIUS: name collision */
m_copyback_nfsx(m0, off, len, cp)
	struct	mbuf *m0;
	register int off;
	register int len;
	const void *cp;
{
	register int mlen;
	register struct mbuf *m = m0, *n;
	int totlen = 0;

	if (m0 == 0)
		return;
	while (off > (mlen = m->m_len)) {
		off -= mlen;
		totlen += mlen;
		if (m->m_next == 0) {
			n = m_getclr(M_DONTWAIT, m->m_type);
			if (n == 0)
				goto out;
			n->m_len = min(MLEN, len + off);
			m->m_next = n;
		}
		m = m->m_next;
	}
	while (len > 0) {
		mlen = min (m->m_len - off, len);
		bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
		cp += mlen;
		len -= mlen;
		mlen += off;
		off = 0;
		totlen += mlen;
		if (len == 0)
			break;
		if (m->m_next == 0) {
			n = m_get(M_DONTWAIT, m->m_type);
			if (n == 0)
				break;
			n->m_len = min(MLEN, len);
			m->m_next = n;
		}
		m = m->m_next;
	}
out:	if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
		m->m_pkthdr.len = totlen;
}
#endif /* __APPLE__ */
#endif /* __FreeBSD__ */
