#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/malloc.h>
#include <sys/sysent.h>
#include <sys/syscall.h>
#include <sys/conf.h>
#include <sys/objcache.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <sys/buf2.h>
#include "rpcv2.h"
#include "nfsproto.h"
#include "nfs.h"
#include "nfsmount.h"
#include "nfsnode.h"
#include "xdr_subs.h"
#include "nfsm_subs.h"
#include "nfsrtt.h"
#include <netinet/in.h>
static u_int32_t nfs_xid = 0;
void
nfsm_reqhead(nfsm_info_t info, struct vnode *vp, u_long procid, int hsiz)
{
info->mb = m_getl(hsiz, M_WAITOK, MT_DATA, 0, NULL);
info->mb->m_len = 0;
info->mreq = info->mb;
info->bpos = mtod(info->mb, caddr_t);
}
struct mbuf *
nfsm_rpchead(struct ucred *cr, int nmflag, int procid, int auth_type,
int auth_len, char *auth_str, int verf_len, char *verf_str,
struct mbuf *mrest, int mrest_len, struct mbuf **mbp,
u_int32_t *xidp)
{
struct nfsm_info info;
struct mbuf *mb2;
u_int32_t *tl;
u_int32_t xid;
int siz, grpsiz, authsiz, dsiz;
int i;
authsiz = nfsm_rndup(auth_len);
dsiz = authsiz + 10 * NFSX_UNSIGNED;
info.mb = m_getl(dsiz, M_WAITOK, MT_DATA, M_PKTHDR, NULL);
if (dsiz < MINCLSIZE) {
if (dsiz < MHLEN)
MH_ALIGN(info.mb, dsiz);
else
MH_ALIGN(info.mb, 8 * NFSX_UNSIGNED);
}
info.mb->m_len = info.mb->m_pkthdr.len = 0;
info.mreq = info.mb;
info.bpos = mtod(info.mb, caddr_t);
tl = nfsm_build(&info, 8 * NFSX_UNSIGNED);
if (!nfs_xid)
nfs_xid = krandom();
do {
xid = atomic_fetchadd_int(&nfs_xid, 1);
} while (xid == 0);
*tl++ = *xidp = txdr_unsigned(xid);
*tl++ = rpc_call;
*tl++ = rpc_vers;
*tl++ = txdr_unsigned(NFS_PROG);
if (nmflag & NFSMNT_NFSV3)
*tl++ = txdr_unsigned(NFS_VER3);
else
*tl++ = txdr_unsigned(NFS_VER2);
if (nmflag & NFSMNT_NFSV3)
*tl++ = txdr_unsigned(procid);
else
*tl++ = txdr_unsigned(nfsv2_procid[procid]);
*tl++ = txdr_unsigned(auth_type);
*tl = txdr_unsigned(authsiz);
switch (auth_type) {
case RPCAUTH_UNIX:
tl = nfsm_build(&info, auth_len);
*tl++ = 0;
*tl++ = 0;
*tl++ = txdr_unsigned(cr->cr_uid);
*tl++ = txdr_unsigned(cr->cr_groups[0]);
grpsiz = (auth_len >> 2) - 5;
*tl++ = txdr_unsigned(grpsiz);
for (i = 1; i <= grpsiz; i++)
*tl++ = txdr_unsigned(cr->cr_groups[i]);
break;
case RPCAUTH_KERB4:
siz = auth_len;
while (siz > 0) {
if (M_TRAILINGSPACE(info.mb) == 0) {
mb2 = m_getl(siz, M_WAITOK, MT_DATA, 0, NULL);
mb2->m_len = 0;
info.mb->m_next = mb2;
info.mb = mb2;
info.bpos = mtod(info.mb, caddr_t);
}
i = min(siz, M_TRAILINGSPACE(info.mb));
bcopy(auth_str, info.bpos, i);
info.mb->m_len += i;
auth_str += i;
info.bpos += i;
siz -= i;
}
if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
for (i = 0; i < siz; i++)
*info.bpos++ = '\0';
info.mb->m_len += siz;
}
break;
}
tl = nfsm_build(&info, 2 * NFSX_UNSIGNED);
if (verf_str) {
*tl++ = txdr_unsigned(RPCAUTH_KERB4);
*tl = txdr_unsigned(verf_len);
siz = verf_len;
while (siz > 0) {
if (M_TRAILINGSPACE(info.mb) == 0) {
mb2 = m_getl(siz, M_WAITOK, MT_DATA,
0, NULL);
mb2->m_len = 0;
info.mb->m_next = mb2;
info.mb = mb2;
info.bpos = mtod(info.mb, caddr_t);
}
i = min(siz, M_TRAILINGSPACE(info.mb));
bcopy(verf_str, info.bpos, i);
info.mb->m_len += i;
verf_str += i;
info.bpos += i;
siz -= i;
}
if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
for (i = 0; i < siz; i++)
*info.bpos++ = '\0';
info.mb->m_len += siz;
}
} else {
*tl++ = txdr_unsigned(RPCAUTH_NULL);
*tl = 0;
}
info.mb->m_next = mrest;
info.mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
info.mreq->m_pkthdr.rcvif = NULL;
*mbp = info.mb;
return (info.mreq);
}
void *
nfsm_build(nfsm_info_t info, int bytes)
{
struct mbuf *mb2;
void *ptr;
if (bytes > M_TRAILINGSPACE(info->mb)) {
MGET(mb2, M_WAITOK, MT_DATA);
if (bytes > MLEN)
panic("build > MLEN");
info->mb->m_next = mb2;
info->mb = mb2;
info->mb->m_len = 0;
info->bpos = mtod(info->mb, caddr_t);
}
ptr = info->bpos;
info->mb->m_len += bytes;
info->bpos += bytes;
return (ptr);
}
void *
nfsm_dissect(nfsm_info_t info, int bytes)
{
caddr_t cp2;
void *ptr;
int error;
int n;
if (info->md == NULL) {
if (info->mrep) {
m_freem(info->mrep);
info->mrep = NULL;
}
return NULL;
}
n = mtod(info->md, caddr_t) + info->md->m_len - info->dpos;
if (bytes <= n) {
ptr = info->dpos;
info->dpos += bytes;
} else {
error = nfsm_disct(&info->md, &info->dpos, bytes, n, &cp2);
if (error) {
m_freem(info->mrep);
info->mrep = NULL;
ptr = NULL;
} else {
ptr = cp2;
}
}
return (ptr);
}
int
nfsm_fhtom(nfsm_info_t info, struct vnode *vp)
{
u_int32_t *tl;
caddr_t cp;
int error;
int n;
if (info->v3) {
n = nfsm_rndup(VTONFS(vp)->n_fhsize) + NFSX_UNSIGNED;
if (n <= M_TRAILINGSPACE(info->mb)) {
tl = nfsm_build(info, n);
*tl++ = txdr_unsigned(VTONFS(vp)->n_fhsize);
*(tl + ((n >> 2) - 2)) = 0;
bcopy((caddr_t)VTONFS(vp)->n_fhp,(caddr_t)tl,
VTONFS(vp)->n_fhsize);
error = 0;
} else if ((error = nfsm_strtmbuf(&info->mb, &info->bpos,
(caddr_t)VTONFS(vp)->n_fhp,
VTONFS(vp)->n_fhsize)) != 0) {
m_freem(info->mreq);
info->mreq = NULL;
}
} else {
cp = nfsm_build(info, NFSX_V2FH);
bcopy(VTONFS(vp)->n_fhp, cp, NFSX_V2FH);
error = 0;
}
return (error);
}
void
nfsm_srvfhtom(nfsm_info_t info, fhandle_t *fhp)
{
u_int32_t *tl;
if (info->v3) {
tl = nfsm_build(info, NFSX_UNSIGNED + NFSX_V3FH);
*tl++ = txdr_unsigned(NFSX_V3FH);
bcopy(fhp, tl, NFSX_V3FH);
} else {
tl = nfsm_build(info, NFSX_V2FH);
bcopy(fhp, tl, NFSX_V2FH);
}
}
void
nfsm_srvpostop_fh(nfsm_info_t info, fhandle_t *fhp)
{
u_int32_t *tl;
tl = nfsm_build(info, 2 * NFSX_UNSIGNED + NFSX_V3FH);
*tl++ = nfs_true;
*tl++ = txdr_unsigned(NFSX_V3FH);
bcopy(fhp, tl, NFSX_V3FH);
}
int
nfsm_mtofh(nfsm_info_t info, struct vnode *dvp, struct vnode **vpp, int *gotvpp)
{
struct nfsnode *ttnp;
nfsfh_t *ttfhp;
u_int32_t *tl;
int ttfhsize;
int error = 0;
if (info->v3) {
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return(EBADRPC);
*gotvpp = fxdr_unsigned(int, *tl);
} else {
*gotvpp = 1;
}
if (*gotvpp) {
NEGATIVEOUT(ttfhsize = nfsm_getfh(info, &ttfhp));
error = nfs_nget(dvp->v_mount, ttfhp, ttfhsize, &ttnp, NULL);
if (error) {
m_freem(info->mrep);
info->mrep = NULL;
return (error);
}
*vpp = NFSTOV(ttnp);
}
if (info->v3) {
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return (EBADRPC);
if (*gotvpp) {
*gotvpp = fxdr_unsigned(int, *tl);
} else if (fxdr_unsigned(int, *tl)) {
error = nfsm_adv(info, NFSX_V3FATTR);
if (error)
return (error);
}
}
if (*gotvpp)
error = nfsm_loadattr(info, *vpp, NULL);
nfsmout:
return (error);
}
int
nfsm_getfh(nfsm_info_t info, nfsfh_t **fhpp)
{
u_int32_t *tl;
int n;
*fhpp = NULL;
if (info->v3) {
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return(-1);
if ((n = fxdr_unsigned(int, *tl)) <= 0 || n > NFSX_V3FHMAX) {
m_freem(info->mrep);
info->mrep = NULL;
return(-1);
}
} else {
n = NFSX_V2FH;
}
*fhpp = nfsm_dissect(info, nfsm_rndup(n));
if (*fhpp == NULL)
return(-1);
return(n);
}
int
nfsm_loadattr(nfsm_info_t info, struct vnode *vp, struct vattr *vap)
{
int error;
error = nfs_loadattrcache(vp, &info->md, &info->dpos, vap, 0);
if (error) {
m_freem(info->mrep);
info->mrep = NULL;
return (error);
}
return (0);
}
int
nfsm_postop_attr(nfsm_info_t info, struct vnode *vp, int *attrp, int lflags)
{
u_int32_t *tl;
int error;
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return(EBADRPC);
*attrp = fxdr_unsigned(int, *tl);
if (*attrp) {
error = nfs_loadattrcache(vp, &info->md, &info->dpos,
NULL, lflags);
if (error) {
*attrp = 0;
m_freem(info->mrep);
info->mrep = NULL;
return (error);
}
}
return (0);
}
int
nfsm_wcc_data(nfsm_info_t info, struct vnode *vp, int *attrp)
{
u_int32_t *tl;
int error;
int ttattrf;
int ttretf = 0;
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return (EBADRPC);
if (*tl == nfs_true) {
tl = nfsm_dissect(info, 6 * NFSX_UNSIGNED);
if (tl == NULL)
return (EBADRPC);
if (*attrp) {
ttretf = (VTONFS(vp)->n_mtime ==
fxdr_unsigned(u_int32_t, *(tl + 2)));
if (ttretf == 0)
VTONFS(vp)->n_flag |= NRMODIFIED;
}
error = nfsm_postop_attr(info, vp, &ttattrf,
NFS_LATTR_NOSHRINK|NFS_LATTR_NOMTIMECHECK);
if (error)
return(error);
} else {
error = nfsm_postop_attr(info, vp, &ttattrf,
NFS_LATTR_NOSHRINK);
if (error)
return(error);
}
if (*attrp)
*attrp = ttretf;
else
*attrp = ttattrf;
return(0);
}
void
nfsm_v3attrbuild(nfsm_info_t info, struct vattr *vap, int full)
{
u_int32_t *tl;
if (vap->va_mode != (mode_t)VNOVAL) {
tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_mode);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (full && vap->va_uid != (uid_t)VNOVAL) {
tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_uid);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (full && vap->va_gid != (gid_t)VNOVAL) {
tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_gid);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (full && vap->va_size != VNOVAL) {
tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
*tl++ = nfs_true;
txdr_hyper(vap->va_size, tl);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (vap->va_atime.tv_sec != VNOVAL) {
if (vap->va_atime.tv_sec != time_second) {
tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_atime, tl);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
if (vap->va_mtime.tv_sec != VNOVAL) {
if (vap->va_mtime.tv_sec != time_second) {
tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_mtime, tl);
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
}
int
nfsm_strsiz(nfsm_info_t info, int maxlen)
{
u_int32_t *tl;
int len;
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL)
return(-1);
len = fxdr_unsigned(int32_t, *tl);
if (len < 0 || len > maxlen)
return(-1);
return (len);
}
int
nfsm_srvstrsiz(nfsm_info_t info, int maxlen, int *errorp)
{
u_int32_t *tl;
int len;
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL) {
*errorp = EBADRPC;
return(-1);
}
len = fxdr_unsigned(int32_t,*tl);
if (len > maxlen || len <= 0) {
*errorp = EBADRPC;
return(-2);
}
return(len);
}
int
nfsm_srvnamesiz(nfsm_info_t info, int *errorp)
{
u_int32_t *tl;
int len;
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL) {
*errorp = EBADRPC;
return(-1);
}
len = fxdr_unsigned(int32_t,*tl);
if (len > NFS_MAXNAMLEN)
*errorp = NFSERR_NAMETOL;
if (len <= 0)
*errorp = EBADRPC;
if (*errorp)
return(-2);
return (len);
}
int
nfsm_mtouio(nfsm_info_t info, struct uio *uiop, int len)
{
int error;
if (len > 0 &&
(error = nfsm_mbuftouio(&info->md, uiop, len, &info->dpos)) != 0) {
m_freem(info->mrep);
info->mrep = NULL;
return(error);
}
return(0);
}
int
nfsm_mtobio(nfsm_info_t info, struct bio *bio, int len)
{
int error;
if (len > 0 &&
(error = nfsm_mbuftobio(&info->md, bio, len, &info->dpos)) != 0) {
m_freem(info->mrep);
info->mrep = NULL;
return(error);
}
return (0);
}
int
nfsm_uiotom(nfsm_info_t info, struct uio *uiop, int len)
{
int error;
error = nfsm_uiotombuf(uiop, &info->mb, len, &info->bpos);
if (error) {
m_freem(info->mreq);
info->mreq = NULL;
return (error);
}
return(0);
}
int
nfsm_biotom(nfsm_info_t info, struct bio *bio, int off, int len)
{
int error;
error = nfsm_biotombuf(bio, &info->mb, off, len, &info->bpos);
if (error) {
m_freem(info->mreq);
info->mreq = NULL;
return (error);
}
return(0);
}
int
nfsm_request(nfsm_info_t info, struct vnode *vp, int procnum,
thread_t td, struct ucred *cred, int *errorp)
{
info->state = NFSM_STATE_SETUP;
info->procnum = procnum;
info->vp = vp;
info->td = td;
info->cred = cred;
info->bio = NULL;
info->nmp = VFSTONFS(vp->v_mount);
*errorp = nfs_request(info, NFSM_STATE_SETUP, NFSM_STATE_DONE);
if (*errorp) {
if ((*errorp & NFSERR_RETERR) == 0)
return(-1);
*errorp &= ~NFSERR_RETERR;
}
return(0);
}
void
nfsm_request_bio(nfsm_info_t info, struct vnode *vp, int procnum,
thread_t td, struct ucred *cred)
{
struct buf *bp;
int error;
info->state = NFSM_STATE_SETUP;
info->procnum = procnum;
info->vp = vp;
info->td = td;
info->cred = cred;
info->nmp = VFSTONFS(vp->v_mount);
error = nfs_request(info, NFSM_STATE_SETUP, NFSM_STATE_WAITREPLY);
if (error != EINPROGRESS) {
kprintf("nfsm_request_bio: early abort %d\n", error);
bp = info->bio->bio_buf;
if (error) {
bp->b_flags |= B_ERROR;
if (error == EIO)
bp->b_flags |= B_INVAL;
}
bp->b_error = error;
biodone(info->bio);
}
}
int
nfsm_strtom(nfsm_info_t info, const void *data, int len, int maxlen)
{
u_int32_t *tl;
int error;
int n;
if (len > maxlen) {
m_freem(info->mreq);
info->mreq = NULL;
return(ENAMETOOLONG);
}
n = nfsm_rndup(len) + NFSX_UNSIGNED;
if (n <= M_TRAILINGSPACE(info->mb)) {
tl = nfsm_build(info, n);
*tl++ = txdr_unsigned(len);
*(tl + ((n >> 2) - 2)) = 0;
bcopy(data, tl, len);
error = 0;
} else {
error = nfsm_strtmbuf(&info->mb, &info->bpos, data, len);
if (error) {
m_freem(info->mreq);
info->mreq = NULL;
}
}
return (error);
}
int
nfsm_reply(nfsm_info_t info,
struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
int siz, int *errorp)
{
nfsd->nd_repstat = *errorp;
if (*errorp && !(nfsd->nd_flag & ND_NFSV3))
siz = 0;
nfs_rephead(siz, nfsd, slp, *errorp, &info->mreq,
&info->mb, &info->bpos);
if (info->mrep != NULL) {
m_freem(info->mrep);
info->mrep = NULL;
}
if (*errorp && (!(nfsd->nd_flag & ND_NFSV3) || *errorp == EBADRPC)) {
*errorp = 0;
return(-1);
}
return(0);
}
void
nfsm_writereply(nfsm_info_t info,
struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
int error, int siz)
{
nfsd->nd_repstat = error;
if (error && !(info->v3))
siz = 0;
nfs_rephead(siz, nfsd, slp, error, &info->mreq, &info->mb, &info->bpos);
}
int
nfsm_adv(nfsm_info_t info, int len)
{
int error;
int n;
n = mtod(info->md, caddr_t) + info->md->m_len - info->dpos;
if (n >= len) {
info->dpos += len;
error = 0;
} else if ((error = nfs_adv(&info->md, &info->dpos, len, n)) != 0) {
m_freem(info->mrep);
info->mrep = NULL;
}
return (error);
}
int
nfsm_srvmtofh(nfsm_info_t info, struct nfsrv_descript *nfsd,
fhandle_t *fhp, int *errorp)
{
u_int32_t *tl;
int fhlen;
if (nfsd->nd_flag & ND_NFSV3) {
tl = nfsm_dissect(info, NFSX_UNSIGNED);
if (tl == NULL) {
*errorp = EBADRPC;
return(-1);
}
fhlen = fxdr_unsigned(int, *tl);
if (fhlen != 0 && fhlen != NFSX_V3FH) {
*errorp = EBADRPC;
return(-2);
}
} else {
fhlen = NFSX_V2FH;
}
if (fhlen != 0) {
tl = nfsm_dissect(info, fhlen);
if (tl == NULL) {
*errorp = EBADRPC;
return(-1);
}
bcopy(tl, fhp, fhlen);
} else {
bzero(fhp, NFSX_V3FH);
}
return(0);
}
void *
_nfsm_clget(nfsm_info_t info, struct mbuf **mp1, struct mbuf **mp2,
char **bp, char **be)
{
if (*bp >= *be) {
if (*mp1 == info->mb)
(*mp1)->m_len += *bp - info->bpos;
*mp1 = m_getcl(M_WAITOK, MT_DATA, 0);
(*mp1)->m_len = MCLBYTES;
(*mp2)->m_next = *mp1;
*mp2 = *mp1;
*bp = mtod(*mp1, caddr_t);
*be = *bp + (*mp1)->m_len;
}
return(*bp);
}
int
nfsm_srvsattr(nfsm_info_t info, struct vattr *vap)
{
u_int32_t *tl;
int error = 0;
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
if (*tl == nfs_true) {
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
vap->va_mode = nfstov_mode(*tl);
}
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
if (*tl == nfs_true) {
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
vap->va_uid = fxdr_unsigned(uid_t, *tl);
}
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
if (*tl == nfs_true) {
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
vap->va_gid = fxdr_unsigned(gid_t, *tl);
}
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
if (*tl == nfs_true) {
NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
vap->va_size = fxdr_hyper(tl);
}
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
fxdr_nfsv3time(tl, &vap->va_atime);
break;
case NFSV3SATTRTIME_TOSERVER:
getnanotime(&vap->va_atime);
break;
}
NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
fxdr_nfsv3time(tl, &vap->va_mtime);
break;
case NFSV3SATTRTIME_TOSERVER:
getnanotime(&vap->va_mtime);
break;
}
nfsmout:
return (error);
}
int
nfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
{
char *mbufcp, *uiocp;
int xfer, left, len;
struct mbuf *mp;
long uiosiz, rem;
int error = 0;
mp = *mrep;
mbufcp = *dpos;
len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
rem = nfsm_rndup(siz)-siz;
while (siz > 0) {
if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
return (EFBIG);
left = uiop->uio_iov->iov_len;
uiocp = uiop->uio_iov->iov_base;
if (left > siz)
left = siz;
uiosiz = left;
while (left > 0) {
while (len == 0) {
mp = mp->m_next;
if (mp == NULL)
return (EBADRPC);
mbufcp = mtod(mp, caddr_t);
len = mp->m_len;
}
xfer = (left > len) ? len : left;
#ifdef notdef
if (uiop->uio_iov->iov_op != NULL)
(*(uiop->uio_iov->iov_op))
(mbufcp, uiocp, xfer);
else
#endif
if (uiop->uio_segflg == UIO_SYSSPACE)
bcopy(mbufcp, uiocp, xfer);
else
copyout(mbufcp, uiocp, xfer);
left -= xfer;
len -= xfer;
mbufcp += xfer;
uiocp += xfer;
uiop->uio_offset += xfer;
uiop->uio_resid -= xfer;
}
if (uiop->uio_iov->iov_len <= siz) {
uiop->uio_iovcnt--;
uiop->uio_iov++;
} else {
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + uiosiz;
uiop->uio_iov->iov_len -= uiosiz;
}
siz -= uiosiz;
}
*dpos = mbufcp;
*mrep = mp;
if (rem > 0) {
if (len < rem)
error = nfs_adv(mrep, dpos, rem, len);
else
*dpos += rem;
}
return (error);
}
int
nfsm_mbuftobio(struct mbuf **mrep, struct bio *bio, int size, caddr_t *dpos)
{
struct buf *bp = bio->bio_buf;
char *mbufcp;
char *bio_cp;
int xfer, len;
struct mbuf *mp;
long rem;
int error = 0;
int bio_left;
mp = *mrep;
mbufcp = *dpos;
len = mtod(mp, caddr_t) + mp->m_len - mbufcp;
rem = nfsm_rndup(size) - size;
bio_left = bp->b_bcount;
bio_cp = bp->b_data;
while (size > 0) {
while (len == 0) {
mp = mp->m_next;
if (mp == NULL)
return (EBADRPC);
mbufcp = mtod(mp, caddr_t);
len = mp->m_len;
}
if ((xfer = len) > size)
xfer = size;
if (bio_left) {
if (xfer > bio_left)
xfer = bio_left;
bcopy(mbufcp, bio_cp, xfer);
} else {
return(EFBIG);
}
size -= xfer;
bio_left -= xfer;
bio_cp += xfer;
len -= xfer;
mbufcp += xfer;
}
*dpos = mbufcp;
*mrep = mp;
if (rem > 0) {
if (len < rem)
error = nfs_adv(mrep, dpos, rem, len);
else
*dpos += rem;
}
return (error);
}
int
nfsm_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos)
{
char *uiocp;
struct mbuf *mp, *mp2;
int xfer, left, mlen;
int uiosiz, rem;
boolean_t getcluster;
char *cp;
#ifdef DIAGNOSTIC
if (uiop->uio_iovcnt != 1)
panic("nfsm_uiotombuf: iovcnt != 1");
#endif
if (siz >= MINCLSIZE)
getcluster = TRUE;
else
getcluster = FALSE;
rem = nfsm_rndup(siz) - siz;
mp = mp2 = *mq;
while (siz > 0) {
left = uiop->uio_iov->iov_len;
uiocp = uiop->uio_iov->iov_base;
if (left > siz)
left = siz;
uiosiz = left;
while (left > 0) {
mlen = M_TRAILINGSPACE(mp);
if (mlen == 0) {
if (getcluster)
mp = m_getcl(M_WAITOK, MT_DATA, 0);
else
mp = m_get(M_WAITOK, MT_DATA);
mp->m_len = 0;
mp2->m_next = mp;
mp2 = mp;
mlen = M_TRAILINGSPACE(mp);
}
xfer = (left > mlen) ? mlen : left;
#ifdef notdef
if (uiop->uio_iov->iov_op != NULL)
(*(uiop->uio_iov->iov_op))
(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
else
#endif
if (uiop->uio_segflg == UIO_SYSSPACE)
bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
else
copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
mp->m_len += xfer;
left -= xfer;
uiocp += xfer;
uiop->uio_offset += xfer;
uiop->uio_resid -= xfer;
}
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + uiosiz;
uiop->uio_iov->iov_len -= uiosiz;
siz -= uiosiz;
}
if (rem > 0) {
if (rem > M_TRAILINGSPACE(mp)) {
MGET(mp, M_WAITOK, MT_DATA);
mp->m_len = 0;
mp2->m_next = mp;
}
cp = mtod(mp, caddr_t)+mp->m_len;
for (left = 0; left < rem; left++)
*cp++ = '\0';
mp->m_len += rem;
*bpos = cp;
} else
*bpos = mtod(mp, caddr_t)+mp->m_len;
*mq = mp;
return (0);
}
int
nfsm_biotombuf(struct bio *bio, struct mbuf **mq, int off,
int siz, caddr_t *bpos)
{
struct buf *bp = bio->bio_buf;
struct mbuf *mp, *mp2;
char *bio_cp;
int bio_left;
int xfer, mlen;
int rem;
boolean_t getcluster;
char *cp;
if (siz >= MINCLSIZE)
getcluster = TRUE;
else
getcluster = FALSE;
rem = nfsm_rndup(siz) - siz;
mp = mp2 = *mq;
bio_cp = bp->b_data + off;
bio_left = siz;
while (bio_left) {
mlen = M_TRAILINGSPACE(mp);
if (mlen == 0) {
if (getcluster)
mp = m_getcl(M_WAITOK, MT_DATA, 0);
else
mp = m_get(M_WAITOK, MT_DATA);
mp->m_len = 0;
mp2->m_next = mp;
mp2 = mp;
mlen = M_TRAILINGSPACE(mp);
}
xfer = (bio_left < mlen) ? bio_left : mlen;
bcopy(bio_cp, mtod(mp, caddr_t) + mp->m_len, xfer);
mp->m_len += xfer;
bio_left -= xfer;
bio_cp += xfer;
}
if (rem > 0) {
if (rem > M_TRAILINGSPACE(mp)) {
MGET(mp, M_WAITOK, MT_DATA);
mp->m_len = 0;
mp2->m_next = mp;
}
cp = mtod(mp, caddr_t) + mp->m_len;
for (mlen = 0; mlen < rem; mlen++)
*cp++ = '\0';
mp->m_len += rem;
*bpos = cp;
} else {
*bpos = mtod(mp, caddr_t) + mp->m_len;
}
*mq = mp;
return(0);
}
int
nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, caddr_t *cp2)
{
struct mbuf *mp, *mp2;
int siz2, xfer;
caddr_t p;
mp = *mdp;
while (left == 0) {
*mdp = mp = mp->m_next;
if (mp == NULL)
return (EBADRPC);
left = mp->m_len;
*dposp = mtod(mp, caddr_t);
}
if (left >= siz) {
*cp2 = *dposp;
*dposp += siz;
} else if (mp->m_next == NULL) {
return (EBADRPC);
} else if (siz > MHLEN) {
panic("nfs S too big");
} else {
MGET(mp2, M_WAITOK, MT_DATA);
mp2->m_next = mp->m_next;
mp->m_next = mp2;
mp->m_len -= left;
mp = mp2;
*cp2 = p = mtod(mp, caddr_t);
bcopy(*dposp, p, left);
siz2 = siz-left;
p += left;
mp2 = mp->m_next;
while (siz2 > 0) {
if (mp2 == NULL)
return (EBADRPC);
xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
if (xfer > 0) {
bcopy(mtod(mp2, caddr_t), p, xfer);
mp2->m_len -= xfer;
mp2->m_data += xfer;
p += xfer;
siz2 -= xfer;
}
if (siz2 > 0)
mp2 = mp2->m_next;
}
mp->m_len = siz;
*mdp = mp2;
*dposp = mtod(mp2, caddr_t);
}
return (0);
}
int
nfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
{
struct mbuf *m;
int s;
m = *mdp;
s = left;
while (s < offs) {
offs -= s;
m = m->m_next;
if (m == NULL)
return (EBADRPC);
s = m->m_len;
}
*mdp = m;
*dposp = mtod(m, caddr_t)+offs;
return (0);
}
int
nfsm_strtmbuf(struct mbuf **mb, char **bpos, const char *cp, long siz)
{
struct mbuf *m1 = NULL, *m2;
long left, xfer, len, tlen;
u_int32_t *tl;
int putsize;
putsize = 1;
m2 = *mb;
left = M_TRAILINGSPACE(m2);
if (left > 0) {
tl = ((u_int32_t *)(*bpos));
*tl++ = txdr_unsigned(siz);
putsize = 0;
left -= NFSX_UNSIGNED;
m2->m_len += NFSX_UNSIGNED;
if (left > 0) {
bcopy(cp, (caddr_t) tl, left);
siz -= left;
cp += left;
m2->m_len += left;
left = 0;
}
}
while (siz > 0) {
int msize;
m1 = m_getl(siz, M_WAITOK, MT_DATA, 0, &msize);
m1->m_len = msize;
m2->m_next = m1;
m2 = m1;
tl = mtod(m1, u_int32_t *);
tlen = 0;
if (putsize) {
*tl++ = txdr_unsigned(siz);
m1->m_len -= NFSX_UNSIGNED;
tlen = NFSX_UNSIGNED;
putsize = 0;
}
if (siz < m1->m_len) {
len = nfsm_rndup(siz);
xfer = siz;
if (xfer < len)
*(tl+(xfer>>2)) = 0;
} else {
xfer = len = m1->m_len;
}
bcopy(cp, (caddr_t) tl, xfer);
m1->m_len = len+tlen;
siz -= xfer;
cp += xfer;
}
*mb = m1;
*bpos = mtod(m1, caddr_t)+m1->m_len;
return (0);
}
void
nfsm_adj(struct mbuf *mp, int len, int nul)
{
struct mbuf *m;
int count, i;
char *cp;
count = 0;
m = mp;
for (;;) {
count += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
if (m->m_len > len) {
m->m_len -= len;
if (nul > 0) {
cp = mtod(m, caddr_t)+m->m_len-nul;
for (i = 0; i < nul; i++)
*cp++ = '\0';
}
return;
}
count -= len;
if (count < 0)
count = 0;
for (m = mp; m; m = m->m_next) {
if (m->m_len >= count) {
m->m_len = count;
if (nul > 0) {
cp = mtod(m, caddr_t)+m->m_len-nul;
for (i = 0; i < nul; i++)
*cp++ = '\0';
}
break;
}
count -= m->m_len;
}
for (m = m->m_next;m;m = m->m_next)
m->m_len = 0;
}
void
nfsm_srvwcc_data(nfsm_info_t info, struct nfsrv_descript *nfsd,
int before_ret, struct vattr *before_vap,
int after_ret, struct vattr *after_vap)
{
u_int32_t *tl;
if (before_ret) {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
tl = nfsm_build(info, 7 * NFSX_UNSIGNED);
*tl++ = nfs_true;
txdr_hyper(before_vap->va_size, tl);
tl += 2;
txdr_nfsv3time(&(before_vap->va_mtime), tl);
tl += 2;
txdr_nfsv3time(&(before_vap->va_ctime), tl);
}
nfsm_srvpostop_attr(info, nfsd, after_ret, after_vap);
}
void
nfsm_srvpostop_attr(nfsm_info_t info, struct nfsrv_descript *nfsd,
int after_ret, struct vattr *after_vap)
{
struct nfs_fattr *fp;
u_int32_t *tl;
if (after_ret) {
tl = nfsm_build(info, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
tl = nfsm_build(info, NFSX_UNSIGNED + NFSX_V3FATTR);
*tl++ = nfs_true;
fp = (struct nfs_fattr *)tl;
nfsm_srvfattr(nfsd, after_vap, fp);
}
}
void
nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
struct nfs_fattr *fp)
{
if (vap->va_nlink > 65535)
fp->fa_nlink = 65535;
else
fp->fa_nlink = txdr_unsigned(vap->va_nlink);
fp->fa_uid = txdr_unsigned(vap->va_uid);
fp->fa_gid = txdr_unsigned(vap->va_gid);
if (nfsd->nd_flag & ND_NFSV3) {
fp->fa_type = vtonfsv3_type(vap->va_type);
fp->fa_mode = vtonfsv3_mode(vap->va_mode);
txdr_hyper(vap->va_size, &fp->fa3_size);
txdr_hyper(vap->va_bytes, &fp->fa3_used);
fp->fa3_rdev.specdata1 = txdr_unsigned(vap->va_rmajor);
fp->fa3_rdev.specdata2 = txdr_unsigned(vap->va_rminor);
fp->fa3_fsid.nfsuquad[0] = 0;
fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
txdr_hyper(vap->va_fileid, &fp->fa3_fileid);
txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
} else {
fp->fa_type = vtonfsv2_type(vap->va_type);
fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
fp->fa2_size = txdr_unsigned(vap->va_size);
fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
if (vap->va_type == VFIFO)
fp->fa2_rdev = 0xffffffff;
else
fp->fa2_rdev = txdr_unsigned(makeudev(vap->va_rmajor, vap->va_rminor));
fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
}
}