add pathconf
[unix-history] / usr / src / sys / nfs / nfs_subs.c
index a617ce2..20e7cc1 100644 (file)
@@ -5,19 +5,9 @@
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
  *
  *
- *     @(#)nfs_subs.c  7.12 (Berkeley) %G%
+ *     @(#)nfs_subs.c  7.70 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
  * the nfs op functions. They do things like create the rpc header and
  * copy data between mbuf chains and uio lists.
  */
  * the nfs op functions. They do things like create the rpc header and
  * copy data between mbuf chains and uio lists.
  */
-#include "strings.h"
-#include "param.h"
-#include "systm.h"
-#include "user.h"
-#include "proc.h"
-#include "mount.h"
-#include "../ufs/dir.h"
-#include "time.h"
-#include "errno.h"
-#include "kernel.h"
-#include "malloc.h"
-#include "mbuf.h"
-#include "file.h"
-#include "vnode.h"
-#include "uio.h"
-#include "namei.h"
-#include "ucred.h"
-#include "map.h"
-#include "rpcv2.h"
-#include "nfsv2.h"
-#include "nfsnode.h"
-#include "nfs.h"
-#include "nfsiom.h"
-#include "xdr_subs.h"
-#include "nfsm_subs.h"
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.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 <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
 
 #define TRUE   1
 #define        FALSE   0
 
 #define TRUE   1
 #define        FALSE   0
  */
 u_long nfs_procids[NFS_NPROCS];
 u_long nfs_xdrneg1;
  */
 u_long nfs_procids[NFS_NPROCS];
 u_long nfs_xdrneg1;
-u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied,
-       rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+       rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+       rpc_auth_kerb;
 u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
 u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
-/* And other global data */
-static u_long *rpc_uidp = (u_long *)0;
-static u_long nfs_xid = 1;
-static char *rpc_unixauth;
-extern long hostid;
-extern enum vtype v_type[NFLNK+1];
-extern struct proc *nfs_iodwant[MAX_ASYNCDAEMON];
-extern struct map nfsmap[NFS_MSIZ];
 
 
-/* Function ret types */
-static char *nfs_unixauth();
-
-/*
- * Maximum number of groups passed through to NFS server.
- * For release 3.X systems, the maximum value is 8.
- * For release 4.X systems, the maximum value is 10.
- */
-int numgrps = 8;
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct queue_entry nfs_bufq;
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
 
 /*
  * Create the header for an rpc request packet
 
 /*
  * Create the header for an rpc request packet
- * The function nfs_unixauth() creates a unix style authorization string
- * and returns a ptr to it.
  * The hsiz is the size of the rest of the nfs request header.
  * (just used to decide if a cluster is a good idea)
  * The hsiz is the size of the rest of the nfs request header.
  * (just used to decide if a cluster is a good idea)
- * nb: Note that the prog, vers and procid args are already in xdr byte order
  */
  */
-struct mbuf *nfsm_reqh(prog, vers, procid, cred, hsiz, bpos, mb, retxid)
-       u_long prog;
-       u_long vers;
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+       struct vnode *vp;
        u_long procid;
        u_long procid;
-       struct ucred *cred;
        int hsiz;
        int hsiz;
-       caddr_t *bpos;
-       struct mbuf **mb;
-       u_long *retxid;
+       caddr_t *bposp;
 {
 {
-       register struct mbuf *mreq, *m;
-       register u_long *p;
-       struct mbuf *m1;
-       char *ap;
-       int asiz, siz;
-
-       NFSMGETHDR(mreq);
-       asiz = (((cred->cr_ngroups > numgrps) ? numgrps : cred->cr_ngroups)<<2);
-#ifdef FILLINHOST
-       asiz += nfsm_rndup(hostnamelen)+(9*NFSX_UNSIGNED);
-#else
-       asiz += 9*NFSX_UNSIGNED;
-#endif
+       register struct mbuf *mb;
+       register u_long *tl;
+       register caddr_t bpos;
+       struct mbuf *mb2;
+       struct nfsmount *nmp;
+       int nqflag;
 
 
-       /* If we need a lot, alloc a cluster ?? */
-       if ((asiz+hsiz+RPC_SIZ) > MHLEN)
-               NFSMCLGET(mreq, M_WAIT);
-       mreq->m_len = NFSMSIZ(mreq);
-       siz = mreq->m_len;
-       m1 = mreq;
+       MGET(mb, M_WAIT, MT_DATA);
+       if (hsiz >= MINCLSIZE)
+               MCLGET(mb, M_WAIT);
+       mb->m_len = 0;
+       bpos = mtod(mb, caddr_t);
+       
        /*
        /*
-        * Alloc enough mbufs
-        * We do it now to avoid all sleeps after the call to nfs_unixauth()
+        * For NQNFS, add lease request.
         */
         */
-       while ((asiz+RPC_SIZ) > siz) {
-               MGET(m, M_WAIT, MT_DATA);
-               m1->m_next = m;
-               m->m_len = MLEN;
-               siz += MLEN;
-               m1 = m;
-       }
-       p = mtod(mreq, u_long *);
-       *p++ = *retxid = txdr_unsigned(++nfs_xid);
-       *p++ = rpc_call;
-       *p++ = rpc_vers;
-       *p++ = prog;
-       *p++ = vers;
-       *p++ = procid;
-
-       /* Now we can call nfs_unixauth() and copy it in */
-       ap = nfs_unixauth(cred);
-       m = mreq;
-       siz = m->m_len-RPC_SIZ;
-       if (asiz <= siz) {
-               bcopy(ap, (caddr_t)p, asiz);
-               m->m_len = asiz+RPC_SIZ;
-       } else {
-               bcopy(ap, (caddr_t)p, siz);
-               ap += siz;
-               asiz -= siz;
-               while (asiz > 0) {
-                       siz = (asiz > MLEN) ? MLEN : asiz;
-                       m = m->m_next;
-                       bcopy(ap, mtod(m, caddr_t), siz);
-                       m->m_len = siz;
-                       asiz -= siz;
-                       ap += siz;
+       if (vp) {
+               nmp = VFSTONFS(vp->v_mount);
+               if (nmp->nm_flag & NFSMNT_NQNFS) {
+                       nqflag = NQNFS_NEEDLEASE(vp, procid);
+                       if (nqflag) {
+                               nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+                               *tl++ = txdr_unsigned(nqflag);
+                               *tl = txdr_unsigned(nmp->nm_leaseterm);
+                       } else {
+                               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                               *tl = 0;
+                       }
                }
        }
                }
        }
-       
        /* Finally, return values */
        /* Finally, return values */
-       *mb = m;
-       *bpos = mtod(m, caddr_t)+m->m_len;
+       *bposp = bpos;
+       return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+       mrest_len, mbp, xidp)
+       register struct ucred *cr;
+       int nqnfs;
+       int procid;
+       int auth_type;
+       int auth_len;
+       char *auth_str;
+       struct mbuf *mrest;
+       int mrest_len;
+       struct mbuf **mbp;
+       u_long *xidp;
+{
+       register struct mbuf *mb;
+       register u_long *tl;
+       register caddr_t bpos;
+       register int i;
+       struct mbuf *mreq, *mb2;
+       int siz, grpsiz, authsiz;
+
+       authsiz = nfsm_rndup(auth_len);
+       if (auth_type == RPCAUTH_NQNFS)
+               authsiz += 2 * NFSX_UNSIGNED;
+       MGETHDR(mb, M_WAIT, MT_DATA);
+       if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+               MCLGET(mb, M_WAIT);
+       } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+               MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+       } else {
+               MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+       }
+       mb->m_len = 0;
+       mreq = mb;
+       bpos = mtod(mb, caddr_t);
+
+       /*
+        * First the RPC header.
+        */
+       nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+       if (++nfs_xid == 0)
+               nfs_xid++;
+       *tl++ = *xidp = txdr_unsigned(nfs_xid);
+       *tl++ = rpc_call;
+       *tl++ = rpc_vers;
+       if (nqnfs) {
+               *tl++ = txdr_unsigned(NQNFS_PROG);
+               *tl++ = txdr_unsigned(NQNFS_VER1);
+       } else {
+               *tl++ = txdr_unsigned(NFS_PROG);
+               *tl++ = txdr_unsigned(NFS_VER2);
+       }
+       *tl++ = txdr_unsigned(procid);
+
+       /*
+        * And then the authorization cred.
+        */
+       *tl++ = txdr_unsigned(auth_type);
+       *tl = txdr_unsigned(authsiz);
+       switch (auth_type) {
+       case RPCAUTH_UNIX:
+               nfsm_build(tl, u_long *, auth_len);
+               *tl++ = 0;              /* stamp ?? */
+               *tl++ = 0;              /* NULL hostname */
+               *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_NQNFS:
+               nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+               *tl++ = txdr_unsigned(cr->cr_uid);
+               *tl = txdr_unsigned(auth_len);
+               siz = auth_len;
+               while (siz > 0) {
+                       if (M_TRAILINGSPACE(mb) == 0) {
+                               MGET(mb2, M_WAIT, MT_DATA);
+                               if (siz >= MINCLSIZE)
+                                       MCLGET(mb2, M_WAIT);
+                               mb->m_next = mb2;
+                               mb = mb2;
+                               mb->m_len = 0;
+                               bpos = mtod(mb, caddr_t);
+                       }
+                       i = min(siz, M_TRAILINGSPACE(mb));
+                       bcopy(auth_str, bpos, i);
+                       mb->m_len += i;
+                       auth_str += i;
+                       bpos += i;
+                       siz -= i;
+               }
+               if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+                       for (i = 0; i < siz; i++)
+                               *bpos++ = '\0';
+                       mb->m_len += siz;
+               }
+               break;
+       };
+       nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+       *tl++ = txdr_unsigned(RPCAUTH_NULL);
+       *tl = 0;
+       mb->m_next = mrest;
+       mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+       mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+       *mbp = mb;
        return (mreq);
 }
 
        return (mreq);
 }
 
@@ -171,14 +239,15 @@ struct mbuf *nfsm_reqh(prog, vers, procid, cred, hsiz, bpos, mb, retxid)
  */
 nfsm_mbuftouio(mrep, uiop, siz, dpos)
        struct mbuf **mrep;
  */
 nfsm_mbuftouio(mrep, uiop, siz, dpos)
        struct mbuf **mrep;
-       struct uio *uiop;
+       register struct uio *uiop;
        int siz;
        caddr_t *dpos;
 {
        int siz;
        caddr_t *dpos;
 {
+       register char *mbufcp, *uiocp;
        register int xfer, left, len;
        register struct mbuf *mp;
        register int xfer, left, len;
        register struct mbuf *mp;
-       register char *mbufcp, *uiocp;
        long uiosiz, rem;
        long uiosiz, rem;
+       int error = 0;
 
        mp = *mrep;
        mbufcp = *dpos;
 
        mp = *mrep;
        mbufcp = *dpos;
@@ -186,7 +255,7 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos)
        rem = nfsm_rndup(siz)-siz;
        while (siz > 0) {
                if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
        rem = nfsm_rndup(siz)-siz;
        while (siz > 0) {
                if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
-                       return(EFBIG);
+                       return (EFBIG);
                left = uiop->uio_iov->iov_len;
                uiocp = uiop->uio_iov->iov_base;
                if (left > siz)
                left = uiop->uio_iov->iov_len;
                uiocp = uiop->uio_iov->iov_base;
                if (left > siz)
@@ -216,6 +285,7 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos)
                        len -= xfer;
                        mbufcp += xfer;
                        uiocp += xfer;
                        len -= xfer;
                        mbufcp += xfer;
                        uiocp += xfer;
+                       uiop->uio_offset += xfer;
                        uiop->uio_resid -= xfer;
                }
                if (uiop->uio_iov->iov_len <= siz) {
                        uiop->uio_resid -= xfer;
                }
                if (uiop->uio_iov->iov_len <= siz) {
@@ -227,11 +297,15 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos)
                }
                siz -= uiosiz;
        }
                }
                siz -= uiosiz;
        }
-       if (rem > 0)
-               mbufcp += rem;
        *dpos = mbufcp;
        *mrep = mp;
        *dpos = mbufcp;
        *mrep = mp;
-       return(0);
+       if (rem > 0) {
+               if (len < rem)
+                       error = nfs_adv(mrep, dpos, rem, len);
+               else
+                       *dpos += rem;
+       }
+       return (error);
 }
 
 /*
 }
 
 /*
@@ -243,50 +317,53 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
        int siz;
        caddr_t *bpos;
 {
        int siz;
        caddr_t *bpos;
 {
-       register struct mbuf *mp;
-       struct mbuf *mp2;
-       long xfer, left, uiosiz;
-       int clflg;
-       int rem, len;
-       char *cp, *uiocp;
+       register char *uiocp;
+       register struct mbuf *mp, *mp2;
+       register int xfer, left, mlen;
+       int uiosiz, clflg, rem;
+       char *cp;
 
        if (siz > MLEN)         /* or should it >= MCLBYTES ?? */
                clflg = 1;
        else
                clflg = 0;
        rem = nfsm_rndup(siz)-siz;
 
        if (siz > MLEN)         /* or should it >= MCLBYTES ?? */
                clflg = 1;
        else
                clflg = 0;
        rem = nfsm_rndup(siz)-siz;
-       mp2 = *mq;
+       mp = mp2 = *mq;
        while (siz > 0) {
                if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
        while (siz > 0) {
                if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
-                       return(EINVAL);
+                       return (EINVAL);
                left = uiop->uio_iov->iov_len;
                uiocp = uiop->uio_iov->iov_base;
                if (left > siz)
                        left = siz;
                uiosiz = left;
                while (left > 0) {
                left = uiop->uio_iov->iov_len;
                uiocp = uiop->uio_iov->iov_base;
                if (left > siz)
                        left = siz;
                uiosiz = left;
                while (left > 0) {
-                       MGET(mp, M_WAIT, MT_DATA);
-                       if (clflg)
-                               NFSMCLGET(mp, M_WAIT);
-                       mp->m_len = NFSMSIZ(mp);
-                       mp2->m_next = mp;
-                       mp2 = mp;
-                       xfer = (left > mp->m_len) ? mp->m_len : left;
+                       mlen = M_TRAILINGSPACE(mp);
+                       if (mlen == 0) {
+                               MGET(mp, M_WAIT, MT_DATA);
+                               if (clflg)
+                                       MCLGET(mp, M_WAIT);
+                               mp->m_len = 0;
+                               mp2->m_next = mp;
+                               mp2 = mp;
+                               mlen = M_TRAILINGSPACE(mp);
+                       }
+                       xfer = (left > mlen) ? mlen : left;
 #ifdef notdef
                        /* Not Yet.. */
                        if (uiop->uio_iov->iov_op != NULL)
                                (*(uiop->uio_iov->iov_op))
 #ifdef notdef
                        /* Not Yet.. */
                        if (uiop->uio_iov->iov_op != NULL)
                                (*(uiop->uio_iov->iov_op))
-                               (uiocp, mtod(mp, caddr_t), xfer);
+                               (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
                        else
 #endif
                        if (uiop->uio_segflg == UIO_SYSSPACE)
                        else
 #endif
                        if (uiop->uio_segflg == UIO_SYSSPACE)
-                               bcopy(uiocp, mtod(mp, caddr_t), xfer);
+                               bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
                        else
                        else
-                               copyin(uiocp, mtod(mp, caddr_t), xfer);
-                       len = mp->m_len;
-                       mp->m_len = xfer;
+                               copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+                       mp->m_len += xfer;
                        left -= xfer;
                        uiocp += xfer;
                        left -= xfer;
                        uiocp += xfer;
+                       uiop->uio_offset += xfer;
                        uiop->uio_resid -= xfer;
                }
                if (uiop->uio_iov->iov_len <= siz) {
                        uiop->uio_resid -= xfer;
                }
                if (uiop->uio_iov->iov_len <= siz) {
@@ -299,7 +376,7 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
                siz -= uiosiz;
        }
        if (rem > 0) {
                siz -= uiosiz;
        }
        if (rem > 0) {
-               if (rem > (len-mp->m_len)) {
+               if (rem > M_TRAILINGSPACE(mp)) {
                        MGET(mp, M_WAIT, MT_DATA);
                        mp->m_len = 0;
                        mp2->m_next = mp;
                        MGET(mp, M_WAIT, MT_DATA);
                        mp->m_len = 0;
                        mp2->m_next = mp;
@@ -312,14 +389,16 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
        } else
                *bpos = mtod(mp, caddr_t)+mp->m_len;
        *mq = mp;
        } else
                *bpos = mtod(mp, caddr_t)+mp->m_len;
        *mq = mp;
-       return(0);
+       return (0);
 }
 
 /*
  * Help break down an mbuf chain by setting the first siz bytes contiguous
  * pointed to by returned val.
  * If Updateflg == True we can overwrite the first part of the mbuf data
 }
 
 /*
  * Help break down an mbuf chain by setting the first siz bytes contiguous
  * pointed to by returned val.
  * If Updateflg == True we can overwrite the first part of the mbuf data
- * This is used by the macros nfsm_disect and nfsm_disecton for tough
+ * (in this case it can never sleep, so it can be called from interrupt level)
+ * it may however block when Updateflg == False
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
  * cases. (The macros use the vars. dpos and dpos2)
  */
 nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
  * cases. (The macros use the vars. dpos and dpos2)
  */
 nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
@@ -338,17 +417,16 @@ nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
        while (left == 0) {
                *mdp = mp = mp->m_next;
                if (mp == NULL)
        while (left == 0) {
                *mdp = mp = mp->m_next;
                if (mp == NULL)
-                       return(EBADRPC);
+                       return (EBADRPC);
                left = mp->m_len;
                *dposp = mtod(mp, caddr_t);
        }
        if (left >= siz) {
                *cp2 = *dposp;
                *dposp += siz;
                left = mp->m_len;
                *dposp = mtod(mp, caddr_t);
        }
        if (left >= siz) {
                *cp2 = *dposp;
                *dposp += siz;
-               return(0);
        } else if (mp->m_next == NULL) {
        } else if (mp->m_next == NULL) {
-               return(EBADRPC);
-       } else if (siz > MCLBYTES) {
+               return (EBADRPC);
+       } else if (siz > MHLEN) {
                panic("nfs S too big");
        } else {
                /* Iff update, you can overwrite, else must alloc new mbuf */
                panic("nfs S too big");
        } else {
                /* Iff update, you can overwrite, else must alloc new mbuf */
@@ -361,26 +439,23 @@ nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
                        mp->m_len -= left;
                        mp = mp2;
                }
                        mp->m_len -= left;
                        mp = mp2;
                }
-               /* Alloc cluster iff we need it */
-               if (!M_HASCL(mp) && siz > NFSMSIZ(mp)) {
-                       NFSMCLGET(mp, M_WAIT);
-                       if (!M_HASCL(mp))
-                               return(ENOBUFS);
-               }
                *cp2 = p = mtod(mp, caddr_t);
                bcopy(*dposp, p, left);         /* Copy what was left */
                siz2 = siz-left;
                p += left;
                mp2 = mp->m_next;
                *cp2 = p = mtod(mp, caddr_t);
                bcopy(*dposp, p, left);         /* Copy what was left */
                siz2 = siz-left;
                p += left;
                mp2 = mp->m_next;
-               /* Loop arround copying up the siz2 bytes */
+               /* Loop around copying up the siz2 bytes */
                while (siz2 > 0) {
                        if (mp2 == NULL)
                                return (EBADRPC);
                        xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
                while (siz2 > 0) {
                        if (mp2 == NULL)
                                return (EBADRPC);
                        xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
-                       bcopy(mtod(mp2, caddr_t), p, xfer);
-                       NFSMADV(mp2, xfer);
-                       mp2->m_len -= xfer;
-                       siz2 -= xfer;
+                       if (xfer > 0) {
+                               bcopy(mtod(mp2, caddr_t), p, xfer);
+                               NFSMADV(mp2, xfer);
+                               mp2->m_len -= xfer;
+                               p += xfer;
+                               siz2 -= xfer;
+                       }
                        if (siz2 > 0)
                                mp2 = mp2->m_next;
                }
                        if (siz2 > 0)
                                mp2 = mp2->m_next;
                }
@@ -392,7 +467,7 @@ nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
 }
 
 /*
 }
 
 /*
- * Advance the position in the mbuf chain with/without freeing mbufs
+ * Advance the position in the mbuf chain.
  */
 nfs_adv(mdp, dposp, offs, left)
        struct mbuf **mdp;
  */
 nfs_adv(mdp, dposp, offs, left)
        struct mbuf **mdp;
@@ -409,12 +484,12 @@ nfs_adv(mdp, dposp, offs, left)
                offs -= s;
                m = m->m_next;
                if (m == NULL)
                offs -= s;
                m = m->m_next;
                if (m == NULL)
-                       return(EBADRPC);
+                       return (EBADRPC);
                s = m->m_len;
        }
        *mdp = m;
        *dposp = mtod(m, caddr_t)+offs;
                s = m->m_len;
        }
        *mdp = m;
        *dposp = mtod(m, caddr_t)+offs;
-       return(0);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -428,38 +503,38 @@ nfsm_strtmbuf(mb, bpos, cp, siz)
 {
        register struct mbuf *m1, *m2;
        long left, xfer, len, tlen;
 {
        register struct mbuf *m1, *m2;
        long left, xfer, len, tlen;
-       u_long *p;
+       u_long *tl;
        int putsize;
 
        putsize = 1;
        m2 = *mb;
        int putsize;
 
        putsize = 1;
        m2 = *mb;
-       left = NFSMSIZ(m2)-m2->m_len;
+       left = M_TRAILINGSPACE(m2);
        if (left > 0) {
        if (left > 0) {
-               p = ((u_long *)(*bpos));
-               *p++ = txdr_unsigned(siz);
+               tl = ((u_long *)(*bpos));
+               *tl++ = txdr_unsigned(siz);
                putsize = 0;
                left -= NFSX_UNSIGNED;
                m2->m_len += NFSX_UNSIGNED;
                if (left > 0) {
                putsize = 0;
                left -= NFSX_UNSIGNED;
                m2->m_len += NFSX_UNSIGNED;
                if (left > 0) {
-                       bcopy(cp, (caddr_t) p, left);
+                       bcopy(cp, (caddr_t) tl, left);
                        siz -= left;
                        cp += left;
                        m2->m_len += left;
                        left = 0;
                }
        }
                        siz -= left;
                        cp += left;
                        m2->m_len += left;
                        left = 0;
                }
        }
-       /* Loop arround adding mbufs */
+       /* Loop around adding mbufs */
        while (siz > 0) {
                MGET(m1, M_WAIT, MT_DATA);
                if (siz > MLEN)
        while (siz > 0) {
                MGET(m1, M_WAIT, MT_DATA);
                if (siz > MLEN)
-                       NFSMCLGET(m1, M_WAIT);
+                       MCLGET(m1, M_WAIT);
                m1->m_len = NFSMSIZ(m1);
                m2->m_next = m1;
                m2 = m1;
                m1->m_len = NFSMSIZ(m1);
                m2->m_next = m1;
                m2 = m1;
-               p = mtod(m1, u_long *);
+               tl = mtod(m1, u_long *);
                tlen = 0;
                if (putsize) {
                tlen = 0;
                if (putsize) {
-                       *p++ = txdr_unsigned(siz);
+                       *tl++ = txdr_unsigned(siz);
                        m1->m_len -= NFSX_UNSIGNED;
                        tlen = NFSX_UNSIGNED;
                        putsize = 0;
                        m1->m_len -= NFSX_UNSIGNED;
                        tlen = NFSX_UNSIGNED;
                        putsize = 0;
@@ -468,18 +543,18 @@ nfsm_strtmbuf(mb, bpos, cp, siz)
                        len = nfsm_rndup(siz);
                        xfer = siz;
                        if (xfer < len)
                        len = nfsm_rndup(siz);
                        xfer = siz;
                        if (xfer < len)
-                               *(p+(xfer>>2)) = 0;
+                               *(tl+(xfer>>2)) = 0;
                } else {
                        xfer = len = m1->m_len;
                }
                } else {
                        xfer = len = m1->m_len;
                }
-               bcopy(cp, (caddr_t) p, xfer);
+               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;
                m1->m_len = len+tlen;
                siz -= xfer;
                cp += xfer;
        }
        *mb = m1;
        *bpos = mtod(m1, caddr_t)+m1->m_len;
-       return(0);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -488,14 +563,19 @@ nfsm_strtmbuf(mb, bpos, cp, siz)
 nfs_init()
 {
        register int i;
 nfs_init()
 {
        register int i;
+       union nqsrvthead *lhp;
 
 
+       nfsrtt.pos = 0;
        rpc_vers = txdr_unsigned(RPC_VER2);
        rpc_call = txdr_unsigned(RPC_CALL);
        rpc_reply = txdr_unsigned(RPC_REPLY);
        rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
        rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
        rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
        rpc_vers = txdr_unsigned(RPC_VER2);
        rpc_call = txdr_unsigned(RPC_CALL);
        rpc_reply = txdr_unsigned(RPC_REPLY);
        rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
        rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
        rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+       rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+       rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
        rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
        rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+       rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
        nfs_vers = txdr_unsigned(NFS_VER2);
        nfs_prog = txdr_unsigned(NFS_PROG);
        nfs_true = txdr_unsigned(TRUE);
        nfs_vers = txdr_unsigned(NFS_VER2);
        nfs_prog = txdr_unsigned(NFS_PROG);
        nfs_true = txdr_unsigned(TRUE);
@@ -504,67 +584,33 @@ nfs_init()
        for (i = 0; i < NFS_NPROCS; i++)
                nfs_procids[i] = txdr_unsigned(i);
        /* Ensure async daemons disabled */
        for (i = 0; i < NFS_NPROCS; i++)
                nfs_procids[i] = txdr_unsigned(i);
        /* Ensure async daemons disabled */
-       for (i = 0; i < MAX_ASYNCDAEMON; i++)
+       for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
                nfs_iodwant[i] = (struct proc *)0;
                nfs_iodwant[i] = (struct proc *)0;
-       v_type[0] = VNON;
-       v_type[1] = VREG;
-       v_type[2] = VDIR;
-       v_type[3] = VBLK;
-       v_type[4] = VCHR;
-       v_type[5] = VLNK;
+       queue_init(&nfs_bufq);
        nfs_xdrneg1 = txdr_unsigned(-1);
        nfs_nhinit();                   /* Init the nfsnode table */
        nfs_xdrneg1 = txdr_unsigned(-1);
        nfs_nhinit();                   /* Init the nfsnode table */
-       rminit(nfsmap, (long)NFS_MAPREG, (long)1, "nfs mapreg", NFS_MSIZ);
-       /* And start timer */
-       nfs_timer();
-}
-
-/*
- * Fill in the rest of the rpc_unixauth and return it
- */
-static char *nfs_unixauth(cr)
-       register struct ucred *cr;
-{
-       register u_long *p;
-       register int i;
-       int ngr;
+       nfsrv_init(0);                  /* Init server data structures */
+       nfsrv_initcache();              /* Init the server request cache */
 
 
-       /* Maybe someday there should be a cache of AUTH_SHORT's */
-       if ((p = rpc_uidp) == NULL) {
-#ifdef FILLINHOST
-               i = nfsm_rndup(hostnamelen)+(19*NFSX_UNSIGNED);
-#else
-               i = 19*NFSX_UNSIGNED;
-#endif
-               MALLOC(p, u_long *, i, M_TEMP, M_WAITOK);
-               bzero((caddr_t)p, i);
-               rpc_unixauth = (caddr_t)p;
-               *p++ = txdr_unsigned(RPCAUTH_UNIX);
-               p++;    /* Fill in size later */
-               *p++ = hostid;
-#ifdef FILLINHOST
-               *p++ = txdr_unsigned(hostnamelen);
-               i = nfsm_rndup(hostnamelen);
-               bcopy(hostname, (caddr_t)p, hostnamelen);
-               p += (i>>2);
-#else
-               *p++ = 0;
-#endif
-               rpc_uidp = p;
+       /*
+        * Initialize the nqnfs server stuff.
+        */
+       if (nqnfsstarttime == 0) {
+               nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+                       + nqsrv_clockskew + nqsrv_writeslack;
+               NQLOADNOVRAM(nqnfsstarttime);
+               nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+               nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+               nqthead.th_head[0] = &nqthead;
+               nqthead.th_head[1] = &nqthead;
+               nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
        }
        }
-       *p++ = txdr_unsigned(cr->cr_uid);
-       *p++ = txdr_unsigned(cr->cr_groups[0]);
-       ngr = (cr->cr_ngroups > numgrps) ? numgrps : cr->cr_ngroups;
-       *p++ = txdr_unsigned(ngr);
-       for (i = 0; i < ngr; i++)
-               *p++ = txdr_unsigned(cr->cr_groups[i]);
-       /* And add the AUTH_NULL */
-       *p++ = 0;
-       *p = 0;
-       i = (((caddr_t)p)-rpc_unixauth)-12;
-       p = (u_long *)(rpc_unixauth+4);
-       *p = txdr_unsigned(i);
-       return(rpc_unixauth);
+
+       /*
+        * Initialize reply list and start timer
+        */
+       nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+       nfs_timer();
 }
 
 /*
 }
 
 /*
@@ -590,26 +636,36 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
        register struct vnode *vp = *vpp;
        register struct vattr *vap;
        register struct nfsv2_fattr *fp;
        register struct vnode *vp = *vpp;
        register struct vattr *vap;
        register struct nfsv2_fattr *fp;
-       extern struct vnodeops spec_nfsv2nodeops;
-       register struct nfsnode *np;
+       extern int (**spec_nfsv2nodeop_p)();
+       register struct nfsnode *np, *nq, **nhpp;
        register long t1;
        caddr_t dpos, cp2;
        register long t1;
        caddr_t dpos, cp2;
-       int error = 0;
+       int error = 0, isnq;
        struct mbuf *md;
        struct mbuf *md;
-       enum vtype type;
-       dev_t rdev;
-       struct timeval mtime;
+       enum vtype vtyp;
+       u_short vmode;
+       long rdev;
+       struct timespec mtime;
        struct vnode *nvp;
 
        md = *mdp;
        dpos = *dposp;
        struct vnode *nvp;
 
        md = *mdp;
        dpos = *dposp;
-       t1 = (mtod(md, caddr_t)+md->m_len)-dpos;
-       if (error = nfsm_disct(&md, &dpos, NFSX_FATTR, t1, TRUE, &cp2))
+       t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+       isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+       if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, TRUE, &cp2))
                return (error);
        fp = (struct nfsv2_fattr *)cp2;
                return (error);
        fp = (struct nfsv2_fattr *)cp2;
-       type = nfstov_type(fp->fa_type);
-       rdev = fxdr_unsigned(dev_t, fp->fa_rdev);
-       fxdr_time(&fp->fa_mtime, &mtime);
+       vtyp = nfstov_type(fp->fa_type);
+       vmode = fxdr_unsigned(u_short, fp->fa_mode);
+       if (vtyp == VNON || vtyp == VREG)
+               vtyp = IFTOVT(vmode);
+       if (isnq) {
+               rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+               fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+       } else {
+               rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+               fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+       }
        /*
         * If v_type == VNON it is a new node, so fill in the v_type,
         * n_mtime fields. Check to see if it represents a special 
        /*
         * If v_type == VNON it is a new node, so fill in the v_type,
         * n_mtime fields. Check to see if it represents a special 
@@ -619,59 +675,113 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
         */
        np = VTONFS(vp);
        if (vp->v_type == VNON) {
         */
        np = VTONFS(vp);
        if (vp->v_type == VNON) {
-               vp->v_type = type;
+               if (vtyp == VCHR && rdev == 0xffffffff)
+                       vp->v_type = vtyp = VFIFO;
+               else
+                       vp->v_type = vtyp;
+               if (vp->v_type == VFIFO) {
+#ifdef FIFO
+                       extern int (**fifo_nfsv2nodeop_p)();
+                       vp->v_op = fifo_nfsv2nodeop_p;
+#else
+                       return (EOPNOTSUPP);
+#endif /* FIFO */
+               }
                if (vp->v_type == VCHR || vp->v_type == VBLK) {
                if (vp->v_type == VCHR || vp->v_type == VBLK) {
-                       vp->v_rdev = rdev;
-                       vp->v_op = &spec_nfsv2nodeops;
-                       if (nvp = checkalias(vp, vp->v_mount)) {
+                       vp->v_op = spec_nfsv2nodeop_p;
+                       if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
                                /*
                                /*
-                                * Reinitialize aliased node.
+                                * Discard unneeded vnode, but save its nfsnode.
                                 */
                                 */
-                               np = VTONFS(nvp);
-                               np->n_vnode = nvp;
-                               np->n_flag = NLOCKED;
-                               bcopy((caddr_t)&VTONFS(vp)->n_fh,
-                                       (caddr_t)&np->n_fh, NFSX_FH);
-                               insque(np, nfs_hash(&np->n_fh));
-                               np->n_attrstamp = 0;
-                               np->n_sillyrename = (struct sillyrename *)0;
+                               if (nq = np->n_forw)
+                                       nq->n_back = np->n_back;
+                               *np->n_back = nq;
+                               nvp->v_data = vp->v_data;
+                               vp->v_data = NULL;
+                               vp->v_op = spec_vnodeop_p;
+                               vrele(vp);
+                               vgone(vp);
                                /*
                                /*
-                                * Discard unneeded vnode and update actual one
+                                * Reinitialize aliased node.
                                 */
                                 */
-                               vput(vp);
-                               *vpp = nvp;
+                               np->n_vnode = nvp;
+                               nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+                               if (nq = *nhpp)
+                                       nq->n_back = &np->n_forw;
+                               np->n_forw = nq;
+                               np->n_back = nhpp;
+                               *nhpp = np;
+                               *vpp = vp = nvp;
                        }
                }
                        }
                }
-               np->n_mtime = mtime.tv_sec;
+               np->n_mtime = mtime.ts_sec;
        }
        vap = &np->n_vattr;
        }
        vap = &np->n_vattr;
-       vap->va_type = type;
-       vap->va_mode = nfstov_mode(fp->fa_mode);
+       vap->va_type = vtyp;
+       vap->va_mode = (vmode & 07777);
        vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
        vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
        vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
        vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
        vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
        vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
-       vap->va_size = fxdr_unsigned(u_long, fp->fa_size);
-       if ((np->n_flag & NMODIFIED) == 0 || vap->va_size > np->n_size)
-               np->n_size = vap->va_size;
-       vap->va_size1 = 0;              /* OR -1 ?? */
-       vap->va_blocksize = fxdr_unsigned(long, fp->fa_blocksize);
-       vap->va_rdev = rdev;
-       vap->va_bytes = fxdr_unsigned(long, fp->fa_blocks) * vap->va_blocksize;
-       vap->va_bytes1 = 0;
-       vap->va_fsid = fxdr_unsigned(long, fp->fa_fsid);
-       vap->va_fileid = fxdr_unsigned(long, fp->fa_fileid);
-       fxdr_time(&fp->fa_atime, &vap->va_atime);
-       fxdr_time(&fp->fa_ctime, &vap->va_ctime);
+       vap->va_rdev = (dev_t)rdev;
        vap->va_mtime = mtime;
        vap->va_mtime = mtime;
-       vap->va_gen = 0;
-       vap->va_flags = 0;
+       vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+       if (isnq) {
+               fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+               vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+               fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+               vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+               fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+               vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+               fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+               vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+               fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+       } else {
+               vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+               vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+               vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+               vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+               fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+               vap->va_flags = 0;
+               vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+               vap->va_ctime.ts_nsec = 0;
+               vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+               vap->va_filerev = 0;
+       }
+       if (vap->va_size != np->n_size) {
+               if (vap->va_type == VREG) {
+                       if (np->n_flag & NMODIFIED) {
+                               if (vap->va_size < np->n_size)
+                                       vap->va_size = np->n_size;
+                               else
+                                       np->n_size = vap->va_size;
+                       } else
+                               np->n_size = vap->va_size;
+                       vnode_pager_setsize(vp, (u_long)np->n_size);
+               } else
+                       np->n_size = vap->va_size;
+       }
        np->n_attrstamp = time.tv_sec;
        *dposp = dpos;
        *mdp = md;
        if (vaper != NULL) {
                bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
        np->n_attrstamp = time.tv_sec;
        *dposp = dpos;
        *mdp = md;
        if (vaper != NULL) {
                bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
-               if ((np->n_flag & NMODIFIED) && (np->n_size > vap->va_size))
+#ifdef notdef
+               if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+               if (np->n_size > vap->va_size)
                        vaper->va_size = np->n_size;
                        vaper->va_size = np->n_size;
+#endif
+               if (np->n_flag & NCHG) {
+                       if (np->n_flag & NACC) {
+                               vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+                               vaper->va_atime.ts_nsec =
+                                   np->n_atim.tv_usec * 1000;
+                       }
+                       if (np->n_flag & NUPD) {
+                               vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+                               vaper->va_mtime.ts_nsec =
+                                   np->n_mtim.tv_usec * 1000;
+                       }
+               }
        }
        return (0);
 }
        }
        return (0);
 }
@@ -681,180 +791,163 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
  * If the cache is valid, copy contents to *vap and return 0
  * otherwise return an error
  */
  * If the cache is valid, copy contents to *vap and return 0
  * otherwise return an error
  */
-nfs_getattrcache(vp, vap)
+nfs_getattrcache(vp, vaper)
        register struct vnode *vp;
        register struct vnode *vp;
-       struct vattr *vap;
+       struct vattr *vaper;
 {
 {
-       register struct nfsnode *np;
+       register struct nfsnode *np = VTONFS(vp);
+       register struct vattr *vap;
 
 
-       np = VTONFS(vp);
-       if ((time.tv_sec-np->n_attrstamp) < NFS_ATTRTIMEO) {
-               nfsstats.attrcache_hits++;
-               bcopy((caddr_t)&np->n_vattr,(caddr_t)vap,sizeof(struct vattr));
-               if ((np->n_flag & NMODIFIED) == 0)
-                       np->n_size = vap->va_size;
-               else if (np->n_size > vap->va_size)
-                       vap->va_size = np->n_size;
-               return (0);
-       } else {
+       if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+               if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+                       nfsstats.attrcache_misses++;
+                       return (ENOENT);
+               }
+       } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
                nfsstats.attrcache_misses++;
                return (ENOENT);
        }
                nfsstats.attrcache_misses++;
                return (ENOENT);
        }
+       nfsstats.attrcache_hits++;
+       vap = &np->n_vattr;
+       if (vap->va_size != np->n_size) {
+               if (vap->va_type == VREG) {
+                       if (np->n_flag & NMODIFIED) {
+                               if (vap->va_size < np->n_size)
+                                       vap->va_size = np->n_size;
+                               else
+                                       np->n_size = vap->va_size;
+                       } else
+                               np->n_size = vap->va_size;
+                       vnode_pager_setsize(vp, (u_long)np->n_size);
+               } else
+                       np->n_size = vap->va_size;
+       }
+       bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+       if ((np->n_flag & NMODIFIED) == 0) {
+               np->n_size = vaper->va_size;
+               vnode_pager_setsize(vp, (u_long)np->n_size);
+       } else if (np->n_size > vaper->va_size)
+       if (np->n_size > vaper->va_size)
+               vaper->va_size = np->n_size;
+#endif
+       if (np->n_flag & NCHG) {
+               if (np->n_flag & NACC) {
+                       vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+                       vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+               }
+               if (np->n_flag & NUPD) {
+                       vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+                       vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+               }
+       }
+       return (0);
 }
 
 /*
 }
 
 /*
- * nfs_namei - a liitle like namei(), but for one element only
- *     essentially look up file handle, fill in ndp and call VOP_LOOKUP()
+ * Set up nameidata for a lookup() call and do it
  */
  */
-nfs_namei(ndp, fhp, len, mdp, dposp)
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
        register struct nameidata *ndp;
        fhandle_t *fhp;
        int len;
        register struct nameidata *ndp;
        fhandle_t *fhp;
        int len;
+       struct nfssvc_sock *slp;
+       struct mbuf *nam;
        struct mbuf **mdp;
        caddr_t *dposp;
        struct mbuf **mdp;
        caddr_t *dposp;
+       struct proc *p;
 {
        register int i, rem;
        register struct mbuf *md;
 {
        register int i, rem;
        register struct mbuf *md;
-       register char *cp;
-       struct vnode *dp = (struct vnode *)0;
-       int flag;
-       int docache;
-       int wantparent;
-       int lockparent;
-       int error = 0;
-
-       ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0;
-       flag = ndp->ni_nameiop & OPFLAG;
-       wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT);
-       lockparent = ndp->ni_nameiop & LOCKPARENT;
-       docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
-       if (flag == DELETE || wantparent)
-               docache = 0;
+       register char *fromcp, *tocp;
+       struct vnode *dp;
+       int error, rdonly;
+       struct componentname *cnp = &ndp->ni_cnd;
 
 
-       /* Fill in the nameidata and call lookup */
-       cp = *dposp;
+       MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+       /*
+        * Copy the name from the mbuf list to ndp->ni_pnbuf
+        * and set the various ndp fields appropriately.
+        */
+       fromcp = *dposp;
+       tocp = cnp->cn_pnbuf;
        md = *mdp;
        md = *mdp;
-       rem = mtod(md, caddr_t)+md->m_len-cp;
-       ndp->ni_hash = 0;
-       for (i = 0; i < len;) {
-               if (rem == 0) {
+       rem = mtod(md, caddr_t) + md->m_len - fromcp;
+       cnp->cn_hash = 0;
+       for (i = 0; i < len; i++) {
+               while (rem == 0) {
                        md = md->m_next;
                        md = md->m_next;
-                       if (md == NULL)
-                               return (EBADRPC);
-                       cp = mtod(md, caddr_t);
+                       if (md == NULL) {
+                               error = EBADRPC;
+                               goto out;
+                       }
+                       fromcp = mtod(md, caddr_t);
                        rem = md->m_len;
                }
                        rem = md->m_len;
                }
-               if (*cp == '\0' || *cp == '/')
-                       return (EINVAL);
-               if (*cp & 0200)
-                       if ((*cp&0377) == ('/'|0200) || flag != DELETE)
-                               return (EINVAL);
-               ndp->ni_dent.d_name[i++] = *cp;
-               ndp->ni_hash += (unsigned char)*cp * i;
-               cp++;
+               if (*fromcp == '\0' || *fromcp == '/') {
+                       error = EINVAL;
+                       goto out;
+               }
+               cnp->cn_hash += (unsigned char)*fromcp;
+               *tocp++ = *fromcp++;
                rem--;
        }
                rem--;
        }
+       *tocp = '\0';
        *mdp = md;
        *mdp = md;
+       *dposp = fromcp;
        len = nfsm_rndup(len)-len;
        len = nfsm_rndup(len)-len;
-       if (len > 0)
-               *dposp = cp+len;
-       else
-               *dposp = cp;
-       ndp->ni_namelen = i;
-       ndp->ni_dent.d_namlen = i;
-       ndp->ni_dent.d_name[i] = '\0';
-       ndp->ni_pathlen = 1;
-       ndp->ni_dirp = ndp->ni_ptr = &ndp->ni_dent.d_name[0];
-       ndp->ni_next = &ndp->ni_dent.d_name[i];
-       ndp->ni_loopcnt = 0;    /* Not actually used for now */
-       ndp->ni_endoff = 0;
-       if (docache)
-               ndp->ni_makeentry = 1;
-       else
-               ndp->ni_makeentry = 0;
-       ndp->ni_isdotdot = (i == 2 && 
-               ndp->ni_dent.d_name[1] == '.' && ndp->ni_dent.d_name[0] == '.');
-
-       if (error = nfsrv_fhtovp(fhp, TRUE, &dp, ndp->ni_cred))
-               return (error);
-       if (dp->v_type != VDIR) {
-               vput(dp);
-               return (ENOTDIR);
+       if (len > 0) {
+               if (rem >= len)
+                       *dposp += len;
+               else if (error = nfs_adv(mdp, dposp, len, rem))
+                       goto out;
        }
        }
+       ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+       cnp->cn_nameptr = cnp->cn_pnbuf;
        /*
        /*
-        * Must set current directory here to avoid confusion in namei()
-        * called from rename()
+        * Extract and set starting directory.
         */
         */
-       ndp->ni_cdir = dp;
-       ndp->ni_rdir = (struct vnode *)0;
-
+       if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+           nam, &rdonly))
+               goto out;
+       if (dp->v_type != VDIR) {
+               vrele(dp);
+               error = ENOTDIR;
+               goto out;
+       }
+       ndp->ni_startdir = dp;
+       if (rdonly)
+               cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+       else
+               cnp->cn_flags |= NOCROSSMOUNT;
        /*
        /*
-        * Handle "..":
-        * If this vnode is the root of the mounted
-        *    file system, then ignore it so can't get out
+        * And call lookup() to do the real work
         */
         */
-       if (ndp->ni_isdotdot && (dp->v_flag & VROOT)) {
-               ndp->ni_dvp = dp;
-               ndp->ni_vp = dp;
-               VREF(dp);
-               goto nextname;
-       }
-
+       cnp->cn_proc = p;
+       if (error = lookup(ndp))
+               goto out;
        /*
        /*
-        * We now have a segment name to search for, and a directory to search.
+        * Check for encountering a symbolic link
         */
         */
-       if (error = VOP_LOOKUP(dp, ndp)) {
-               if (ndp->ni_vp != NULL)
-                       panic("leaf should be empty");
-               /*
-                * If creating and at end of pathname, then can consider
-                * allowing file to be created.
-                */
-               if (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY | M_EXRDONLY))
-                       error = EROFS;
-               if (flag == LOOKUP || flag == DELETE || error != ENOENT)
-                       goto bad;
-               /*
-                * We return with ni_vp NULL to indicate that the entry
-                * doesn't currently exist, leaving a pointer to the
-                * (possibly locked) directory inode in ndp->ni_dvp.
-                */
-               return (0);     /* should this be ENOENT? */
+       if (cnp->cn_flags & ISSYMLINK) {
+               if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+                       vput(ndp->ni_dvp);
+               else
+                       vrele(ndp->ni_dvp);
+               vput(ndp->ni_vp);
+               ndp->ni_vp = NULL;
+               error = EINVAL;
+               goto out;
        }
        }
-
-       dp = ndp->ni_vp;
-
-nextname:
-       ndp->ni_ptr = ndp->ni_next;
        /*
        /*
-        * Check for read-only file systems
+        * Check for saved name request
         */
         */
-       if (flag == DELETE || flag == RENAME) {
-               /*
-                * Disallow directory write attempts on read-only
-                * file systems.
-                */
-               if ((dp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)) ||
-                   (wantparent && (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)))) {
-                       error = EROFS;
-                       goto bad2;
-               }
+       if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+               cnp->cn_flags |= HASBUF;
+               return (0);
        }
        }
-
-       if (!wantparent)
-               vrele(ndp->ni_dvp);
-
-       if ((ndp->ni_nameiop & LOCKLEAF) == 0)
-               VOP_UNLOCK(dp);
-       return (0);
-
-bad2:
-       if (lockparent)
-               VOP_UNLOCK(ndp->ni_dvp);
-       vrele(ndp->ni_dvp);
-bad:
-       vput(dp);
-       ndp->ni_vp = NULL;
+out:
+       FREE(cnp->cn_pnbuf, M_NAMEI);
        return (error);
 }
 
        return (error);
 }
 
@@ -862,6 +955,7 @@ bad:
  * A fiddled version of m_adj() that ensures null fill to a long
  * boundary and only trims off the back end
  */
  * A fiddled version of m_adj() that ensures null fill to a long
  * boundary and only trims off the back end
  */
+void
 nfsm_adj(mp, len, nul)
        struct mbuf *mp;
        register int len;
 nfsm_adj(mp, len, nul)
        struct mbuf *mp;
        register int len;
@@ -922,28 +1016,100 @@ nfsm_adj(mp, len, nul)
 /*
  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
  *     - look up fsid in mount list (if not found ret error)
 /*
  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
  *     - look up fsid in mount list (if not found ret error)
- *     - check that it is exported
- *     - get vp by calling VFS_FHTOVP() macro
+ *     - get vp and export rights by calling VFS_FHTOVP()
+ *     - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
  *     - if not lockflag unlock it with VOP_UNLOCK()
  *     - if not lockflag unlock it with VOP_UNLOCK()
- *     - if cred->cr_uid == 0 set it to m_exroot
  */
  */
-nfsrv_fhtovp(fhp, lockflag, vpp, cred)
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
        fhandle_t *fhp;
        int lockflag;
        struct vnode **vpp;
        struct ucred *cred;
        fhandle_t *fhp;
        int lockflag;
        struct vnode **vpp;
        struct ucred *cred;
+       struct nfssvc_sock *slp;
+       struct mbuf *nam;
+       int *rdonlyp;
 {
        register struct mount *mp;
 {
        register struct mount *mp;
+       register struct nfsuid *uidp;
+       register int i;
+       struct ucred *credanon;
+       int error, exflags;
 
 
+       *vpp = (struct vnode *)0;
        if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
                return (ESTALE);
        if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
                return (ESTALE);
-       if ((mp->m_flag & M_EXPORTED) == 0)
-               return (EACCES);
-       if (VFS_FHTOVP(mp, &fhp->fh_fid, vpp))
-               return (ESTALE);
-       if (cred->cr_uid == 0)
-               cred->cr_uid = mp->m_exroot;
+       if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+               return (error);
+       /*
+        * Check/setup credentials.
+        */
+       if (exflags & MNT_EXKERB) {
+               uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+               while (uidp) {
+                       if (uidp->nu_uid == cred->cr_uid)
+                               break;
+                       uidp = uidp->nu_hnext;
+               }
+               if (uidp) {
+                       cred->cr_uid = uidp->nu_cr.cr_uid;
+                       for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+                               cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+               } else {
+                       vput(*vpp);
+                       return (NQNFS_AUTHERR);
+               }
+       } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+               cred->cr_uid = credanon->cr_uid;
+               for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+                       cred->cr_groups[i] = credanon->cr_groups[i];
+       }
+       if (exflags & MNT_EXRDONLY)
+               *rdonlyp = 1;
+       else
+               *rdonlyp = 0;
        if (!lockflag)
                VOP_UNLOCK(*vpp);
        return (0);
 }
        if (!lockflag)
                VOP_UNLOCK(*vpp);
        return (0);
 }
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+netaddr_match(family, haddr, nam)
+       int family;
+       union nethostaddr *haddr;
+       struct mbuf *nam;
+{
+       register struct sockaddr_in *inetaddr;
+
+       switch (family) {
+       case AF_INET:
+               inetaddr = mtod(nam, struct sockaddr_in *);
+               if (inetaddr->sin_family == AF_INET &&
+                   inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+                       return (1);
+               break;
+#ifdef ISO
+       case AF_ISO:
+           {
+               register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+               isoaddr1 = mtod(nam, struct sockaddr_iso *);
+               isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+               if (isoaddr1->siso_family == AF_ISO &&
+                   isoaddr1->siso_nlen > 0 &&
+                   isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+                   SAME_ISOADDR(isoaddr1, isoaddr2))
+                       return (1);
+               break;
+           }
+#endif /* ISO */
+       default:
+               break;
+       };
+       return (0);
+}