Bring in a merge of Rick Macklem's NFSv3 code from Lite2 trunk
authorfvdl <fvdl@NetBSD.org>
Sun, 18 Feb 1996 11:53:36 +0000
branchtrunk
changeset 17853 0e1dad7a56b9
parent 17852 e00c6bd7ce15
child 17854 4f1bc83ab792
Bring in a merge of Rick Macklem's NFSv3 code from Lite2
sys/nfs/krpc_subr.c
sys/nfs/nfs.h
sys/nfs/nfs_bio.c
sys/nfs/nfs_boot.c
sys/nfs/nfs_node.c
sys/nfs/nfs_nqlease.c
sys/nfs/nfs_serv.c
sys/nfs/nfs_socket.c
sys/nfs/nfs_srvcache.c
sys/nfs/nfs_subs.c
sys/nfs/nfs_syscalls.c
sys/nfs/nfs_var.h
sys/nfs/nfs_vfsops.c
sys/nfs/nfs_vnops.c
sys/nfs/nfsdiskless.h
sys/nfs/nfsm_subs.h
sys/nfs/nfsmount.h
sys/nfs/nfsnode.h
sys/nfs/nfsproto.h
sys/nfs/nfsrtt.h
sys/nfs/nfsrvcache.h
sys/nfs/nfsv2.h
sys/nfs/nqnfs.h
sys/nfs/rpcv2.h
sys/nfs/xdr_subs.h
--- a/sys/nfs/krpc_subr.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/krpc_subr.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: krpc_subr.c,v 1.11 1995/12/19 23:07:19 cgd Exp $	*/
+/*	$NetBSD: krpc_subr.c,v 1.12 1996/02/18 11:53:36 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1995 Gordon Ross, Adam Glass
@@ -97,8 +97,8 @@
 
 struct rpc_reply {
 	u_int32_t rp_xid;		/* request transaction id */
-	int32_t   rp_direction;		/* call direction (1) */
-	int32_t   rp_astatus;		/* accept status (0: accepted) */
+	int32_t  rp_direction;		/* call direction (1) */
+	int32_t  rp_astatus;		/* accept status (0: accepted) */
 	union {
 		u_int32_t rpu_errno;
 		struct {
--- a/sys/nfs/nfs.h	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs.h	Sun Feb 18 11:53:36 1996 +0000
@@ -1,7 +1,6 @@
-/*	$NetBSD: nfs.h,v 1.9 1995/12/19 23:07:21 cgd Exp $	*/
-
+/*	$NetBSD: nfs.h,v 1.10 1996/02/18 11:53:38 fvdl Exp $	*/
 /*
- * Copyright (c) 1989, 1993
+ * Copyright (c) 1989, 1993, 1995
  *	The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -35,34 +34,93 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs.h	8.1 (Berkeley) 6/10/93
+ *	@(#)nfs.h	8.4 (Berkeley) 5/1/95
  */
 
+#ifndef _NFS_NFS_H_
+#define _NFS_NFS_H_
+
 /*
  * Tunable constants for nfs
  */
 
 #define	NFS_MAXIOVEC	34
-#define NFS_HZ		25		/* Ticks per second for NFS timeouts */
-#define	NFS_TIMEO	(1*NFS_HZ)	/* Default timeout = 1 second */
-#define	NFS_MINTIMEO	(1*NFS_HZ)	/* Min timeout to use */
-#define	NFS_MAXTIMEO	(60*NFS_HZ)	/* Max timeout to backoff to */
-#define	NFS_MINIDEMTIMEO (5*NFS_HZ)	/* Min timeout for non-idempotent ops*/
+#define NFS_TICKINTVL	5		/* Desired time for a tick (msec) */
+#define NFS_HZ		(hz / nfs_ticks) /* Ticks/sec */
+#define	NFS_TIMEO	(1 * NFS_HZ)	/* Default timeout = 1 second */
+#define	NFS_MINTIMEO	(1 * NFS_HZ)	/* Min timeout to use */
+#define	NFS_MAXTIMEO	(60 * NFS_HZ)	/* Max timeout to backoff to */
+#define	NFS_MINIDEMTIMEO (5 * NFS_HZ)	/* Min timeout for non-idempotent ops*/
 #define	NFS_MAXREXMIT	100		/* Stop counting after this many */
 #define	NFS_MAXWINDOW	1024		/* Max number of outstanding requests */
 #define	NFS_RETRANS	10		/* Num of retrans for soft mounts */
 #define	NFS_MAXGRPS	16		/* Max. size of groups list */
+#ifndef NFS_MINATTRTIMO
 #define	NFS_MINATTRTIMO 5		/* Attribute cache timeout in sec */
+#endif
+#ifndef NFS_MAXATTRTIMO
 #define	NFS_MAXATTRTIMO 60
+#endif
 #define	NFS_WSIZE	8192		/* Def. write data size <= 8192 */
 #define	NFS_RSIZE	8192		/* Def. read data size <= 8192 */
+#define NFS_READDIRSIZE	8192		/* Def. readdir size */
 #define	NFS_DEFRAHEAD	1		/* Def. read ahead # blocks */
 #define	NFS_MAXRAHEAD	4		/* Max. read ahead # blocks */
-#define	NFS_MAXREADDIR	NFS_MAXDATA	/* Max. size of directory read */
 #define	NFS_MAXUIDHASH	64		/* Max. # of hashed uid entries/mp */
-#define	NFS_MAXASYNCDAEMON 20	/* Max. number async_daemons runable */
-#define	NFS_DIRBLKSIZ	1024		/* Size of an NFS directory block */
+#define	NFS_MAXASYNCDAEMON 	20	/* Max. number async_daemons runable */
+#define NFS_MAXGATHERDELAY	100	/* Max. write gather delay (msec) */
+#ifndef NFS_GATHERDELAY
+#define NFS_GATHERDELAY		10	/* Default write gather delay (msec) */
+#endif
+/*
+ * Ideally, NFS_DIRBLKSIZ should be bigger, but I've seen servers with
+ * broken NFS/ethernet drivers that won't work with anything bigger (Linux..)
+ */
+#define	NFS_DIRBLKSIZ	1024		/* Must be a multiple of DIRBLKSIZ */
+#define NFS_READDIRBLKSIZ	512	/* Size of read dir blocks. XXX */
+
+/*
+ * Oddballs
+ */
 #define	NMOD(a)		((a) % nfs_asyncdaemons)
+#define NFS_CMPFH(n, f, s) \
+	((n)->n_fhsize == (s) && !bcmp((caddr_t)(n)->n_fhp, (caddr_t)(f), (s)))
+#define NFS_ISV3(v)	(VFSTONFS((v)->v_mount)->nm_flag & NFSMNT_NFSV3)
+#define NFS_SRVMAXDATA(n) \
+		(((n)->nd_flag & ND_NFSV3) ? (((n)->nd_nam2) ? \
+		 NFS_MAXDGRAMDATA : NFS_MAXDATA) : NFS_V2MAXDATA)
+
+/*
+ * sys/malloc.h needs M_NFSDIROFF, M_NFSRVDESC and M_NFSBIGFH added.
+ * The VA_EXCLUSIVE flag should be added for va_vaflags and set for an
+ * exclusive create.
+ */
+#ifndef M_NFSRVDESC
+#define M_NFSRVDESC	M_TEMP
+#endif
+#ifndef M_NFSDIROFF
+#define M_NFSDIROFF	M_TEMP
+#endif
+#ifndef M_NFSBIGFH
+#define M_NFSBIGFH	M_TEMP
+#endif
+#ifndef VA_EXCLUSIVE
+#define VA_EXCLUSIVE	0
+#endif
+
+/*
+ * The B_INVAFTERWRITE flag should be set to whatever is required by the
+ * buffer cache code to say "Invalidate the block after it is written back".
+ */
+#define	B_INVAFTERWRITE	B_INVAL
+
+/*
+ * The IO_METASYNC flag should be implemented for local file systems.
+ * (Until then, it is nothin at all.)
+ */
+#ifndef IO_METASYNC
+#define IO_METASYNC	0
+#endif
 
 /*
  * Set the attribute timeout based on how recently the file has been modified.
@@ -74,12 +132,26 @@
 	  (time.tv_sec - (np)->n_mtime) / 10))
 
 /*
+ * Expected allocation sizes for major data structures. If the actual size
+ * of the structure exceeds these sizes, then malloc() will be allocating
+ * almost twice the memory required. This is used in nfs_init() to warn
+ * the sysadmin that the size of a structure should be reduced.
+ * (These sizes are always a power of 2. If the kernel malloc() changes
+ *  to one that does not allocate space in powers of 2 size, then this all
+ *  becomes bunk!)
+ */
+#define NFS_NODEALLOC	256
+#define NFS_MNTALLOC	512
+#define NFS_SVCALLOC	256
+#define NFS_UIDALLOC	128
+
+/*
  * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
  * should ever try and use it.
  */
 struct nfsd_args {
 	int	sock;		/* Socket to serve */
-	caddr_t	name;		/* Client address for connection based sockets */
+	caddr_t	name;		/* Client addr for connection based sockets */
 	int	namelen;	/* Length of name */
 };
 
@@ -89,7 +161,12 @@
 	u_int32_t	nsd_haddr;	/* Ip address of client */
 	struct ucred	nsd_cr;		/* Cred. uid maps to */
 	int		nsd_authlen;	/* Length of auth string (ret) */
-	char		*nsd_authstr;	/* Auth string (ret) */
+	u_char		*nsd_authstr;	/* Auth string (ret) */
+	int		nsd_verflen;	/* and the verfier */
+	u_char		*nsd_verfstr;
+	struct timeval	nsd_timestamp;	/* timestamp from verifier */
+	u_int32_t	nsd_ttl;	/* credential ttl (sec) */
+	NFSKERBKEY_T	nsd_key;	/* Session key */
 };
 
 struct nfsd_cargs {
@@ -97,7 +174,10 @@
 	uid_t		ncd_authuid;	/* Effective uid */
 	int		ncd_authtype;	/* Type of authenticator */
 	int		ncd_authlen;	/* Length of authenticator string */
-	char		*ncd_authstr;	/* Authenticator string */
+	u_char		*ncd_authstr;	/* Authenticator string */
+	int		ncd_verflen;	/* and the verifier */
+	u_char		*ncd_verfstr;
+	NFSKERBKEY_T	ncd_key;	/* Session key */
 };
 
 /*
@@ -136,6 +216,7 @@
 	int	srvnqnfs_leases;
 	int	srvnqnfs_maxleases;
 	int	srvnqnfs_getleases;
+	int	srvvop_writes;
 };
 
 /*
@@ -150,6 +231,16 @@
 #define	NFSSVC_MNTD	0x100
 
 /*
+ * fs.nfs sysctl(3) identifiers
+ */
+#define NFS_NFSSTATS	1		/* struct: struct nfsstats */
+
+#define FS_NFS_NAMES { \
+		       { 0, 0 }, \
+		       { "nfsstats", CTLTYPE_STRUCT }, \
+}
+
+/*
  * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
  * What should be in this set is open to debate, but I believe that since
  * I/O system calls on ufs are never interrupted by signals the set should
@@ -158,6 +249,9 @@
  * by them and break.
  */
 #ifdef _KERNEL
+
+struct uio; struct buf; struct vattr; struct nameidata;	/* XXX */
+
 #define	NFSINT_SIGMASK	(sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
 			 sigmask(SIGHUP)|sigmask(SIGQUIT))
 
@@ -205,17 +299,29 @@
 #define	R_MUSTRESEND	0x40		/* Must resend request */
 #define	R_GETONEREP	0x80		/* Probe for one reply only */
 
-struct nfsstats nfsstats;
-
 /*
  * A list of nfssvc_sock structures is maintained with all the sockets
  * that require service by the nfsd.
  * The nfsuid structs hang off of the nfssvc_sock structs in both lru
  * and uid hash lists.
  */
-#define	NUIDHASHSIZ	32
+#ifndef NFS_UIDHASHSIZ
+#define	NFS_UIDHASHSIZ	29	/* Tune the size of nfssvc_sock with this */
+#endif
 #define	NUIDHASH(sock, uid) \
-	(&(sock)->ns_uidhashtbl[(uid) & (sock)->ns_uidhash])
+	(&(sock)->ns_uidhashtbl[(uid) % NFS_UIDHASHSIZ])
+#ifndef NFS_WDELAYHASHSIZ
+#define	NFS_WDELAYHASHSIZ 16	/* and with this */
+#endif
+#define	NWDELAYHASH(sock, f) \
+	(&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ])
+#ifndef NFS_MUIDHASHSIZ
+#define NFS_MUIDHASHSIZ	67	/* Tune the size of nfsmount with this */
+#endif
+#define	NMUIDHASH(nmp, uid) \
+	(&(nmp)->nm_uidhashtbl[(uid) % NFS_MUIDHASHSIZ])
+#define	NFSNOHASH(fhsum) \
+	(&nfsnodehashtbl[(fhsum) & nfsnodehash])
 
 /*
  * Network address hash list element
@@ -229,35 +335,41 @@
 	TAILQ_ENTRY(nfsuid) nu_lru;	/* LRU chain */
 	LIST_ENTRY(nfsuid) nu_hash;	/* Hash list */
 	int		nu_flag;	/* Flags */
-	uid_t		nu_uid;		/* Uid mapped by this entry */
 	union nethostaddr nu_haddr;	/* Host addr. for dgram sockets */
 	struct ucred	nu_cr;		/* Cred uid mapped to */
+	int		nu_expire;	/* Expiry time (sec) */
+	struct timeval	nu_timestamp;	/* Kerb. timestamp */
+	u_int32_t	nu_nickname;	/* Nickname on server */
+	NFSKERBKEY_T	nu_key;		/* and session key */
 };
 
 #define	nu_inetaddr	nu_haddr.had_inetaddr
 #define	nu_nam		nu_haddr.had_nam
 /* Bits for nu_flag */
 #define	NU_INETADDR	0x1
+#define NU_NAM		0x2
+#define NU_NETFAM(u)	(((u)->nu_flag & NU_INETADDR) ? AF_INET : AF_ISO)
 
 struct nfssvc_sock {
 	TAILQ_ENTRY(nfssvc_sock) ns_chain;	/* List of all nfssvc_sock's */
 	TAILQ_HEAD(, nfsuid) ns_uidlruhead;
-	LIST_HEAD(, nfsuid) *ns_uidhashtbl;
-	u_long		ns_uidhash;
-
-	int		ns_flag;
-	u_int32_t	ns_sref;
 	struct file	*ns_fp;
 	struct socket	*ns_so;
-	int		ns_solock;
 	struct mbuf	*ns_nam;
-	int		ns_cc;
 	struct mbuf	*ns_raw;
 	struct mbuf	*ns_rawend;
-	int		ns_reclen;
 	struct mbuf	*ns_rec;
 	struct mbuf	*ns_recend;
+	struct mbuf	*ns_frag;
+	int		ns_flag;
+	int		ns_solock;
+	int		ns_cc;
+	int		ns_reclen;
 	int		ns_numuids;
+	u_int32_t	ns_sref;
+	LIST_HEAD(, nfsrv_descript) ns_tq;	/* Write gather lists */
+	LIST_HEAD(, nfsuid) ns_uidhashtbl[NFS_UIDHASHSIZ];
+	LIST_HEAD(nfsrvw_delayhash, nfsrv_descript) ns_wdelayhashtbl[NFS_WDELAYHASHSIZ];
 };
 
 /* Bits for "ns_flag" */
@@ -266,6 +378,7 @@
 #define	SLP_NEEDQ	0x04
 #define	SLP_DISCONN	0x08
 #define	SLP_GETSTREAM	0x10
+#define	SLP_LASTFRAG	0x20
 #define SLP_ALLFLAGS	0xff
 
 TAILQ_HEAD(, nfssvc_sock) nfssvc_sockhead;
@@ -277,32 +390,79 @@
  * One of these structures is allocated for each nfsd.
  */
 struct nfsd {
-	TAILQ_ENTRY(nfsd) nd_chain;	/* List of all nfsd's */
-	int		nd_flag;	/* NFSD_ flags */
-	struct nfssvc_sock *nd_slp;	/* Current socket */
-	struct mbuf	*nd_nam;	/* Client addr for datagram req. */
-	struct mbuf	*nd_mrep;	/* Req. mbuf list */
-	struct mbuf	*nd_md;
-	caddr_t		nd_dpos;	/* Position in list */
-	int		nd_procnum;	/* RPC procedure number */
-	u_int32_t	nd_retxid;	/* RPC xid */
-	int		nd_repstat;	/* Reply status value */
-	struct ucred	nd_cr;		/* Credentials for req. */
-	int		nd_nqlflag;	/* Leasing flag */
-	int		nd_duration;	/* Lease duration */
-	int		nd_authlen;	/* Authenticator len */
-	u_char		nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
-	struct proc	*nd_procp;	/* Proc ptr */
+	TAILQ_ENTRY(nfsd) nfsd_chain;	/* List of all nfsd's */
+	int		nfsd_flag;	/* NFSD_ flags */
+	struct nfssvc_sock *nfsd_slp;	/* Current socket */
+	int		nfsd_authlen;	/* Authenticator len */
+	u_char		nfsd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+	int		nfsd_verflen;	/* and the Verifier */
+	u_char		nfsd_verfstr[RPCVERF_MAXSIZ];
+	struct proc	*nfsd_procp;	/* Proc ptr */
+	struct nfsrv_descript *nfsd_nd;	/* Associated nfsrv_descript */
 };
 
-/* Bits for "nd_flag" */
+/* Bits for "nfsd_flag" */
 #define	NFSD_WAITING	0x01
 #define	NFSD_REQINPROG	0x02
 #define	NFSD_NEEDAUTH	0x04
 #define	NFSD_AUTHFAIL	0x08
 
+/*
+ * This structure is used by the server for describing each request.
+ * Some fields are used only when write request gathering is performed.
+ */
+struct nfsrv_descript {
+	u_quad_t		nd_time;	/* Write deadline (usec) */
+	off_t			nd_off;		/* Start byte offset */
+	off_t			nd_eoff;	/* and end byte offset */
+	LIST_ENTRY(nfsrv_descript) nd_hash;	/* Hash list */
+	LIST_ENTRY(nfsrv_descript) nd_tq;		/* and timer list */
+	LIST_HEAD(,nfsrv_descript) nd_coalesce;	/* coalesced writes */
+	struct mbuf		*nd_mrep;	/* Request mbuf list */
+	struct mbuf		*nd_md;		/* Current dissect mbuf */
+	struct mbuf		*nd_mreq;	/* Reply mbuf list */
+	struct mbuf		*nd_nam;	/* and socket addr */
+	struct mbuf		*nd_nam2;	/* return socket addr */
+	caddr_t			nd_dpos;	/* Current dissect pos */
+	int			nd_procnum;	/* RPC # */
+	int			nd_stable;	/* storage type */
+	int			nd_flag;	/* nd_flag */
+	int			nd_len;		/* Length of this write */
+	int			nd_repstat;	/* Reply status */
+	u_int32_t		nd_retxid;	/* Reply xid */
+	u_int32_t		nd_duration;	/* Lease duration */
+	struct timeval		nd_starttime;	/* Time RPC initiated */
+	fhandle_t		nd_fh;		/* File handle */
+	struct ucred		nd_cr;		/* Credentials */
+};
+
+/* Bits for "nd_flag" */
+#define	ND_READ		LEASE_READ
+#define ND_WRITE	LEASE_WRITE
+#define ND_CHECK	0x04
+#define ND_LEASE	(ND_READ | ND_WRITE | ND_CHECK)
+#define ND_NFSV3	0x08
+#define ND_NQNFS	0x10
+#define ND_KERBNICK	0x20
+#define ND_KERBFULL	0x40
+#define ND_KERBAUTH	(ND_KERBNICK | ND_KERBFULL)
+
 TAILQ_HEAD(, nfsd) nfsd_head;
 int nfsd_head_flag;
 #define	NFSD_CHECKSLP	0x01
 
+/*
+ * These macros compare nfsrv_descript structures.
+ */
+#define NFSW_CONTIG(o, n) \
+		((o)->nd_eoff >= (n)->nd_off && \
+		 !bcmp((caddr_t)&(o)->nd_fh, (caddr_t)&(n)->nd_fh, NFSX_V3FH))
+
+#define NFSW_SAMECRED(o, n) \
+	(((o)->nd_flag & ND_KERBAUTH) == ((n)->nd_flag & ND_KERBAUTH) && \
+ 	 !bcmp((caddr_t)&(o)->nd_cr, (caddr_t)&(n)->nd_cr, \
+		sizeof (struct ucred)))
+
 #endif	/* _KERNEL */
+
+#endif /* _NFS_NFS_H */
--- a/sys/nfs/nfs_bio.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_bio.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_bio.c,v 1.23 1996/02/09 21:48:19 christos Exp $	*/
+/*	$NetBSD: nfs_bio.c,v 1.24 1996/02/18 11:53:39 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,12 +35,14 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_bio.c	8.5 (Berkeley) 1/4/94
+ *	@(#)nfs_bio.c	8.9 (Berkeley) 3/30/95
  */
 
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/resourcevar.h>
+#include <sys/signalvar.h>
 #include <sys/proc.h>
 #include <sys/buf.h>
 #include <sys/vnode.h>
@@ -48,20 +50,20 @@
 #include <sys/mount.h>
 #include <sys/kernel.h>
 #include <sys/namei.h>
-#include <sys/signalvar.h>
 
 #include <vm/vm.h>
 
-#include <nfs/nfsnode.h>
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nqnfs.h>
+#include <nfs/nfsnode.h>
 #include <nfs/nfs_var.h>
 
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 extern int nfs_numasync;
+extern struct nfsstats nfsstats;
 
 /*
  * Vnode op for read using bio
@@ -75,29 +77,27 @@
 	struct ucred *cred;
 {
 	register struct nfsnode *np = VTONFS(vp);
-	register int biosize, diff;
+	register int biosize, diff, i;
 	struct buf *bp = NULL, *rabp;
 	struct vattr vattr;
 	struct proc *p;
-	struct nfsmount *nmp;
+	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
 	daddr_t lbn, bn, rabn;
 	caddr_t baddr;
 	int got_buf = 0, nra, error = 0, n = 0, on = 0, not_readin;
 
-#ifdef lint
-	ioflag = ioflag;
-#endif /* lint */
 #ifdef DIAGNOSTIC
 	if (uio->uio_rw != UIO_READ)
 		panic("nfs_read mode");
 #endif
 	if (uio->uio_resid == 0)
 		return (0);
-	if (uio->uio_offset < 0 && vp->v_type != VDIR)
+	if (uio->uio_offset < 0)
 		return (EINVAL);
-	nmp = VFSTONFS(vp->v_mount);
+	p = uio->uio_procp;
+	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
+		(void)nfs_fsinfo(nmp, vp, cred, p);
 	biosize = nmp->nm_rsize;
-	p = uio->uio_procp;
 	/*
 	 * For nfs, cache consistency can only be maintained approximately.
 	 * Although RFC1094 does not specify the criteria, the following is
@@ -110,8 +110,6 @@
 	 * server, so flush all of the file's data out of the cache.
 	 * Then force a getattr rpc to ensure that you have up to date
 	 * attributes.
-	 * The mount flag NFSMNT_MYWRITE says "Assume that my writes are
-	 * the ones changing the modify time.
 	 * NB: This implies that cache data can be read when up to
 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
 	 * attributes this could be forced by setting n_attrstamp to 0 before
@@ -119,23 +117,26 @@
 	 */
 	if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) {
 		if (np->n_flag & NMODIFIED) {
-			if ((nmp->nm_flag & NFSMNT_MYWRITE) == 0 ||
-			     vp->v_type != VREG) {
+			if (vp->v_type != VREG) {
+				if (vp->v_type != VDIR)
+					panic("nfs: bioread, not dir");
+				nfs_invaldir(vp);
 				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
 				if (error)
 					return (error);
 			}
 			np->n_attrstamp = 0;
-			np->n_direofoffset = 0;
 			error = VOP_GETATTR(vp, &vattr, cred, p);
 			if (error)
 				return (error);
 			np->n_mtime = vattr.va_mtime.tv_sec;
 		} else {
-			if ((error = VOP_GETATTR(vp, &vattr, cred, p)) != 0)
+			error = VOP_GETATTR(vp, &vattr, cred, p);
+			if (error)
 				return (error);
 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
-				np->n_direofoffset = 0;
+				if (vp->v_type == VDIR)
+					nfs_invaldir(vp);
 				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
 				if (error)
 					return (error);
@@ -149,27 +150,24 @@
 	     * Get a valid lease. If cached data is stale, flush it.
 	     */
 	    if (nmp->nm_flag & NFSMNT_NQNFS) {
-		if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+		if (NQNFS_CKINVALID(vp, np, ND_READ)) {
 		    do {
-			error = nqnfs_getlease(vp, NQL_READ, cred, p);
+			error = nqnfs_getlease(vp, ND_READ, cred, p);
 		    } while (error == NQNFS_EXPIRED);
 		    if (error)
 			return (error);
 		    if (np->n_lrev != np->n_brev ||
 			(np->n_flag & NQNFSNONCACHE) ||
 			((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) {
-			if (vp->v_type == VDIR) {
-			    np->n_direofoffset = 0;
-			    cache_purge(vp);
-			}
+			if (vp->v_type == VDIR)
+			    nfs_invaldir(vp);
 			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
 			if (error)
 			    return (error);
 			np->n_brev = np->n_lrev;
 		    }
 		} else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) {
-		    np->n_direofoffset = 0;
-		    cache_purge(vp);
+		    nfs_invaldir(vp);
 		    error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
 		    if (error)
 			return (error);
@@ -178,38 +176,29 @@
 	    if (np->n_flag & NQNFSNONCACHE) {
 		switch (vp->v_type) {
 		case VREG:
-			error = nfs_readrpc(vp, uio, cred);
-			break;
+			return (nfs_readrpc(vp, uio, cred));
 		case VLNK:
-			error = nfs_readlinkrpc(vp, uio, cred);
-			break;
+			return (nfs_readlinkrpc(vp, uio, cred));
 		case VDIR:
-			error = nfs_readdirrpc(vp, uio, cred);
 			break;
-		case VCHR:
-		case VSOCK:
-		case VFIFO:
-		case VBAD:
-		case VNON:
-		case VBLK:
-			break;
+		default:
+			printf(" NQNFSNONCACHE: type %x unexpected\n",	
+				vp->v_type);
 		};
-		return (error);
 	    }
 	    baddr = (caddr_t)0;
 	    switch (vp->v_type) {
 	    case VREG:
 		nfsstats.biocache_reads++;
 		lbn = uio->uio_offset / biosize;
-		on = uio->uio_offset & (biosize-1);
+		on = uio->uio_offset & (biosize - 1);
 		bn = lbn * (biosize / DEV_BSIZE);
 		not_readin = 1;
 
 		/*
 		 * Start the read ahead(s), as required.
 		 */
-		if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
-		    lbn == vp->v_lastr + 1) {
+		if (nfs_numasync > 0 && nmp->nm_readahead > 0) {
 		    for (nra = 0; nra < nmp->nm_readahead &&
 			(lbn + 1 + nra) * biosize < np->n_size; nra++) {
 			rabn = (lbn + 1 + nra) * (biosize / DEV_BSIZE);
@@ -267,7 +256,7 @@
 					return (EINTR);
 				    got_buf = 1;
 				}
-				bp->b_flags |= B_INVAL;
+				bp->b_flags |= B_INVAFTERWRITE;
 				if (bp->b_dirtyend > 0) {
 				    if ((bp->b_flags & B_DELWRI) == 0)
 					panic("nfsbioread");
@@ -290,7 +279,8 @@
 			return (EINTR);
 		if ((bp->b_flags & B_DONE) == 0) {
 			bp->b_flags |= B_READ;
-			if ((error = nfs_doio(bp, cred, p)) != 0) {
+			error = nfs_doio(bp, cred, p);
+			if (error) {
 				brelse(bp);
 				return (error);
 			}
@@ -300,31 +290,56 @@
 		on = 0;
 		break;
 	    case VDIR:
-		if (uio->uio_resid < NFS_DIRBLKSIZ)
+		if (uio->uio_resid < NFS_READDIRBLKSIZ)
 			return (0);
 		nfsstats.biocache_readdirs++;
-		bn = (daddr_t)uio->uio_offset;
-		bp = nfs_getcacheblk(vp, bn, NFS_DIRBLKSIZ, p);
+		lbn = uio->uio_offset / NFS_DIRBLKSIZ;
+		on = uio->uio_offset & (NFS_DIRBLKSIZ - 1);
+		bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, p);
 		if (!bp)
-			return (EINTR);
+		    return (EINTR);
 		if ((bp->b_flags & B_DONE) == 0) {
-			bp->b_flags |= B_READ;
-			if ((error = nfs_doio(bp, cred, p)) != 0) {
-				brelse(bp);
-				return (error);
+		    bp->b_flags |= B_READ;
+		    error = nfs_doio(bp, cred, p);
+		    if (error) {
+			brelse(bp);
+			while (error == NFSERR_BAD_COOKIE) {
+			    nfs_invaldir(vp);
+			    error = nfs_vinvalbuf(vp, 0, cred, p, 1);
+			    /*
+			     * Yuck! The directory has been modified on the
+			     * server. The only way to get the block is by
+			     * reading from the beginning to get all the
+			     * offset cookies.
+			     */
+			    for (i = 0; i <= lbn && !error; i++) {
+				bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, p);
+				if (!bp)
+				    return (EINTR);
+				if ((bp->b_flags & B_DONE) == 0) {
+				    bp->b_flags |= B_READ;
+				    error = nfs_doio(bp, cred, p);
+				    if (error)
+					brelse(bp);
+				}
+			    }
 			}
+			if (error)
+			    return (error);
+		    }
 		}
 
 		/*
 		 * If not eof and read aheads are enabled, start one.
 		 * (You need the current block first, so that you have the
-		 *  directory offset cookie of the next block.
+		 *  directory offset cookie of the next block.)
 		 */
-		rabn = bp->b_blkno;
 		if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
-		    rabn != 0 && rabn != np->n_direofoffset &&
-		    !incore(vp, rabn)) {
-			rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p);
+		    (np->n_direofoffset == 0 ||
+		    (lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) &&
+		    !(np->n_flag & NQNFSNONCACHE) &&
+		    !incore(vp, lbn + 1)) {
+			rabp = nfs_getcacheblk(vp, lbn + 1, NFS_DIRBLKSIZ, p);
 			if (rabp) {
 			    if ((rabp->b_flags & (B_DONE | B_DELWRI)) == 0) {
 				rabp->b_flags |= (B_READ | B_ASYNC);
@@ -336,16 +351,11 @@
 				brelse(rabp);
 			}
 		}
-		on = 0;
-		n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
+		n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on);
 		got_buf = 1;
 		break;
-	    case VBAD:
-	    case VSOCK:
-	    case VCHR:
-	    case VBLK:
-	    case VNON:
-	    case VFIFO:
+	    default:
+		printf(" nfsbioread: type %x unexpected\n",vp->v_type);
 		break;
 	    };
 
@@ -355,21 +365,18 @@
 		error = uiomove(baddr + on, (int)n, uio);
 	    }
 	    switch (vp->v_type) {
+	    case VREG:
+		break;
 	    case VLNK:
 		n = 0;
 		break;
 	    case VDIR:
-		uio->uio_offset = bp->b_blkno;
+		if (np->n_flag & NQNFSNONCACHE)
+			bp->b_flags |= B_INVAL;
 		break;
-	    case VREG:
-	    case VBAD:
-	    case VFIFO:
-	    case VSOCK:
-	    case VCHR:
-	    case VBLK:
-	    case VNON:
-		break;
-	    };
+	    default:
+		printf(" nfsbioread: type %x unexpected\n",vp->v_type);
+	    }
 	    if (got_buf)
 		brelse(bp);
 	} while (error == 0 && uio->uio_resid > 0 && n > 0);
@@ -384,7 +391,7 @@
 	void *v;
 {
 	struct vop_write_args /* {
-		struct vnode a_vp;
+		struct vnode *a_vp;
 		struct uio *a_uio;
 		int  a_ioflag;
 		struct ucred *a_cred;
@@ -398,9 +405,9 @@
 	int ioflag = ap->a_ioflag;
 	struct buf *bp;
 	struct vattr vattr;
-	struct nfsmount *nmp;
+	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
 	daddr_t lbn, bn;
-	int n, on, error = 0;
+	int n, on, error = 0, iomode, must_commit;
 
 #ifdef DIAGNOSTIC
 	if (uio->uio_rw != UIO_WRITE)
@@ -414,6 +421,8 @@
 		np->n_flag &= ~NWRITEERR;
 		return (np->n_error);
 	}
+	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
+		(void)nfs_fsinfo(nmp, vp, cred, p);
 	if (ioflag & (IO_APPEND | IO_SYNC)) {
 		if (np->n_flag & NMODIFIED) {
 			np->n_attrstamp = 0;
@@ -429,7 +438,6 @@
 			uio->uio_offset = np->n_size;
 		}
 	}
-	nmp = VFSTONFS(vp->v_mount);
 	if (uio->uio_offset < 0)
 		return (EINVAL);
 	if (uio->uio_resid == 0)
@@ -458,12 +466,11 @@
 
 		/*
 		 * Check for a valid write lease.
-		 * If non-cachable, just do the rpc
 		 */
 		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-		    NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
 			do {
-				error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
 			} while (error == NQNFS_EXPIRED);
 			if (error)
 				return (error);
@@ -475,8 +482,13 @@
 				np->n_brev = np->n_lrev;
 			}
 		}
-		if (np->n_flag & NQNFSNONCACHE)
-			return (nfs_writerpc(vp, uio, cred, ioflag));
+		if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) {
+		    iomode = NFSV3WRITE_FILESYNC;
+		    error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
+		    if (must_commit)
+			nfs_clearcommit(vp->v_mount);
+		    return (error);
+		}
 		nfsstats.biocache_writes++;
 		lbn = uio->uio_offset / biosize;
 		on = uio->uio_offset & (biosize-1);
@@ -514,9 +526,9 @@
 		 * In case getblk() and/or bwrite() delayed us.
 		 */
 		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-		    NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
 			do {
-				error = nqnfs_getlease(vp, NQL_WRITE, cred, p);
+				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
 			} while (error == NQNFS_EXPIRED);
 			if (error) {
 				brelse(bp);
@@ -545,7 +557,6 @@
 			bp->b_dirtyoff = on;
 			bp->b_dirtyend = on + n;
 		}
-#ifndef notdef
 		if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
 		    bp->b_validoff > bp->b_dirtyend) {
 			bp->b_validoff = bp->b_dirtyoff;
@@ -554,26 +565,27 @@
 			bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
 			bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
 		}
-#else
-		bp->b_validoff = bp->b_dirtyoff;
-		bp->b_validend = bp->b_dirtyend;
-#endif
-		if (ioflag & IO_APPEND)
-			bp->b_flags |= B_APPENDWRITE;
-
 		/*
 		 * If the lease is non-cachable or IO_SYNC do bwrite().
 		 */
 		if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
 			bp->b_proc = p;
-			if ((error = VOP_BWRITE(bp)) != 0)
+			error = VOP_BWRITE(bp);
+			if (error)
 				return (error);
+			if (np->n_flag & NQNFSNONCACHE) {
+				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+				if (error)
+					return (error);
+			}
 		} else if ((n + on) == biosize &&
 			(nmp->nm_flag & NFSMNT_NQNFS) == 0) {
 			bp->b_proc = (struct proc *)0;
-			bawrite(bp);
-		} else
+			bp->b_flags |= B_ASYNC;
+			(void)nfs_writebp(bp, 0);
+		} else {
 			bdwrite(bp);
+		}
 	} while (uio->uio_resid > 0 && n > 0);
 	return (0);
 }
@@ -689,6 +701,7 @@
 				bp->b_rcred = cred;
 			}
 		} else {
+			bp->b_flags |= B_WRITEINPROG;
 			if (bp->b_wcred == NOCRED && cred != NOCRED) {
 				crhold(cred);
 				bp->b_wcred = cred;
@@ -700,7 +713,25 @@
 		wakeup((caddr_t)&nfs_iodwant[i]);
 		return (0);
 	    }
-	return (EIO);
+
+	/*
+	 * If it is a read or a write already marked B_WRITEINPROG or B_NOCACHE
+	 * return EIO so the process will call nfs_doio() and do it
+	 * synchronously.
+	 */
+	if (bp->b_flags & (B_READ | B_WRITEINPROG | B_NOCACHE))
+		return (EIO);
+
+	/*
+	 * Just turn the async write into a delayed write, instead of
+	 * doing in synchronously. Hopefully, at least one of the nfsiods
+	 * is currently doing a write for this file and will pick up the
+	 * delayed writes before going back to sleep.
+	 */
+	bp->b_flags |= B_DELWRI;
+	reassignbuf(bp, bp->b_vp);
+	biodone(bp);
+	return (0);
 }
 
 /*
@@ -717,7 +748,7 @@
 	register struct vnode *vp;
 	struct nfsnode *np;
 	struct nfsmount *nmp;
-	int error = 0, diff, len;
+	int error = 0, diff, len, iomode, must_commit = 0;
 	struct uio uio;
 	struct iovec io;
 
@@ -740,15 +771,16 @@
 	    io.iov_len = uiop->uio_resid = bp->b_bcount;
 	    /* mapping was done by vmapbuf() */
 	    io.iov_base = bp->b_data;
-	    uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
+	    uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
 	    if (bp->b_flags & B_READ) {
 		uiop->uio_rw = UIO_READ;
 		nfsstats.read_physios++;
 		error = nfs_readrpc(vp, uiop, cr);
 	    } else {
+		iomode = NFSV3WRITE_DATASYNC;
 		uiop->uio_rw = UIO_WRITE;
 		nfsstats.write_physios++;
-		error = nfs_writerpc(vp, uiop, cr, 0);
+		error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
 	    }
 	    if (error) {
 		bp->b_flags |= B_ERROR;
@@ -760,7 +792,7 @@
 	    uiop->uio_rw = UIO_READ;
 	    switch (vp->v_type) {
 	    case VREG:
-		uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
+		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
 		nfsstats.read_bios++;
 		error = nfs_readrpc(vp, uiop, cr);
 		if (!error) {
@@ -773,7 +805,7 @@
 			 * Just zero fill the rest of the valid area.
 			 */
 			diff = bp->b_bcount - uiop->uio_resid;
-			len = np->n_size - (bp->b_blkno * DEV_BSIZE
+			len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE
 				+ diff);
 			if (len > 0) {
 			    len = min(len, uiop->uio_resid);
@@ -786,7 +818,7 @@
 		}
 		if (p && (vp->v_flag & VTEXT) &&
 			(((nmp->nm_flag & NFSMNT_NQNFS) &&
-			  NQNFS_CKINVALID(vp, np, NQL_READ) &&
+			  NQNFS_CKINVALID(vp, np, ND_READ) &&
 			  np->n_lrev != np->n_brev) ||
 			 (!(nmp->nm_flag & NFSMNT_NQNFS) &&
 			  np->n_mtime != np->n_vattr.va_mtime.tv_sec))) {
@@ -796,28 +828,24 @@
 		}
 		break;
 	    case VLNK:
-		uiop->uio_offset = 0;
+		uiop->uio_offset = (off_t)0;
 		nfsstats.readlink_bios++;
 		error = nfs_readlinkrpc(vp, uiop, cr);
 		break;
 	    case VDIR:
-		uiop->uio_offset = bp->b_lblkno;
 		nfsstats.readdir_bios++;
-		if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS)
-		    error = nfs_readdirlookrpc(vp, uiop, cr);
-		else
-		    error = nfs_readdirrpc(vp, uiop, cr);
-		/*
-		 * Save offset cookie in b_blkno.
-		 */
-		bp->b_blkno = uiop->uio_offset;
+		uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ;
+		if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
+			error = nfs_readdirplusrpc(vp, uiop, cr);
+			if (error == NFSERR_NOTSUPP)
+				nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
+		}
+		if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
+			error = nfs_readdirrpc(vp, uiop, cr);
 		break;
-	    case VNON:
-	    case VBLK:
-	    case VCHR:
-	    case VFIFO:
-	    case VBAD:
-	    case VSOCK:
+	    default:
+		printf("nfs_doio:  type %x unexpected\n",vp->v_type);
+		break;
 	    };
 	    if (error) {
 		bp->b_flags |= B_ERROR;
@@ -826,16 +854,26 @@
 	} else {
 	    io.iov_len = uiop->uio_resid = bp->b_dirtyend
 		- bp->b_dirtyoff;
-	    uiop->uio_offset = (bp->b_blkno * DEV_BSIZE)
+	    uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE
 		+ bp->b_dirtyoff;
 	    io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
 	    uiop->uio_rw = UIO_WRITE;
 	    nfsstats.write_bios++;
-	    if (bp->b_flags & B_APPENDWRITE)
-		error = nfs_writerpc(vp, uiop, cr, IO_APPEND);
+	    if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC)
+		iomode = NFSV3WRITE_UNSTABLE;
 	    else
-		error = nfs_writerpc(vp, uiop, cr, 0);
-	    bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE);
+		iomode = NFSV3WRITE_FILESYNC;
+	    bp->b_flags |= B_WRITEINPROG;
+#ifdef fvdl_debug
+	    printf("nfs_doio(%x): bp %x doff %d dend %d\n", 
+		vp, bp, bp->b_dirtyoff, bp->b_dirtyend);
+#endif
+	    error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
+	    if (!error && iomode == NFSV3WRITE_UNSTABLE)
+		bp->b_flags |= B_NEEDCOMMIT;
+	    else
+		bp->b_flags &= ~B_NEEDCOMMIT;
+	    bp->b_flags &= ~B_WRITEINPROG;
 
 	    /*
 	     * For an interrupted write, the buffer is still valid and the
@@ -843,9 +881,13 @@
 	     * B_ERROR and report the interruption by setting B_EINTR. For
 	     * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt
 	     * is essentially a noop.
+	     * For the case of a V3 write rpc not being committed to stable
+	     * storage, the block is still dirty and requires either a commit
+	     * rpc or another write rpc with iomode == NFSV3WRITE_FILESYNC
+	     * before the block is reused. This is indicated by setting the
+	     * B_DELWRI and B_NEEDCOMMIT flags.
 	     */
-	    if (error == EINTR) {
-		bp->b_flags &= ~B_INVAL;
+	    if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
 		bp->b_flags |= B_DELWRI;
 
 		/*
@@ -867,6 +909,8 @@
 	    }
 	}
 	bp->b_resid = uiop->uio_resid;
+	if (must_commit)
+		nfs_clearcommit(vp->v_mount);
 	biodone(bp);
 	return (error);
 }
--- a/sys/nfs/nfs_boot.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_boot.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_boot.c,v 1.24 1996/02/16 15:18:19 gwr Exp $ */
+/*	$NetBSD: nfs_boot.c,v 1.25 1996/02/18 11:53:41 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1995 Adam Glass, Gordon Ross
@@ -46,7 +46,7 @@
 #include <netinet/if_ether.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsdiskless.h>
 #include <nfs/krpc.h>
@@ -104,10 +104,6 @@
 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
 	u_char *fh));
 
-/* other helpers */
-static void get_path_and_handle __P((struct sockaddr_in *bpsin,
-	char *key, struct nfs_dlmount *ndmntp));
-
 char	*nfsbootdevname;
 
 /*
@@ -172,7 +168,7 @@
 	 */
 	if ((error = revarpwhoami(&my_ip, ifp)) != 0)
 		panic("revarp failed, error=%d", error);
-	printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr));
+	printf("nfs_boot: client_addr=0x%x\n", (u_int32_t)ntohl(my_ip.s_addr));
 
 	/*
 	 * Do enough of ifconfig(8) so that the chosen interface
@@ -208,7 +204,7 @@
 	if (error)
 		panic("nfs_boot: bootparam whoami, error=%d", error);
 	printf("nfs_boot: server_addr=0x%x\n",
-		   ntohl(bp_sin.sin_addr.s_addr));
+		   (u_int32_t)ntohl(bp_sin.sin_addr.s_addr));
 	printf("nfs_boot: hostname=%s\n", hostname);
 
 #ifdef	NFS_BOOT_GATEWAY
@@ -271,8 +267,7 @@
 	 * Get server:pathname for "key" (root or swap)
 	 * using RPC to bootparam/getfile
 	 */
-	error = bp_getfile(bpsin, key, sin,
-	    ndmntp->ndm_host, pathname);
+	error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname);
 	if (error)
 		panic("nfs_boot: bootparam get %s: %d", key, error);
 
@@ -513,7 +508,7 @@
 	/* The RPC structures */
 	struct rdata {
 		u_int32_t errno;
-		u_int8_t  fh[NFS_FHSIZE];
+		u_int8_t  fh[NFSX_V2FH];
 	} *rdata;
 	struct mbuf *m;
 	int error;
@@ -525,7 +520,7 @@
 
 	m = xdr_string_encode(path, strlen(path));
 	if (m == NULL)
-		return (ENOMEM);
+		return ENOMEM;
 
 	/* Do RPC to mountd. */
 	error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
@@ -542,14 +537,14 @@
 	if (error)
 		goto out;
 
-	/* Have errno==0, so the fh must be there. */
+	 /* Have errno==0, so the fh must be there. */
 	if (m->m_len < sizeof(*rdata)) {
 		m = m_pullup(m, sizeof(*rdata));
 		if (m == NULL)
 			goto bad;
 		rdata = mtod(m, struct rdata *);
 	}
-	bcopy(rdata->fh, fhp, NFS_FHSIZE);
+	bcopy(rdata->fh, fhp, NFSX_V2FH);
 	goto out;
 
 bad:
--- a/sys/nfs/nfs_node.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_node.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_node.c,v 1.15 1996/02/09 21:48:24 christos Exp $	*/
+/*	$NetBSD: nfs_node.c,v 1.16 1996/02/18 11:53:42 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,9 +35,10 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_node.c	8.2 (Berkeley) 12/30/93
+ *	@(#)nfs_node.c	8.6 (Berkeley) 5/22/95
  */
 
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
@@ -48,15 +49,13 @@
 #include <sys/malloc.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nqnfs.h>
 #include <nfs/nfs_var.h>
 
-#define	NFSNOHASH(fhsum) \
-	(&nfsnodehashtbl[(fhsum) & nfsnodehash])
 LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
 u_long nfsnodehash;
 
@@ -77,19 +76,20 @@
 /*
  * Compute an entry in the NFS hash table structure
  */
-struct nfsnodehashhead *
-nfs_hash(fhp)
-	register nfsv2fh_t *fhp;
+u_long
+nfs_hash(fhp, fhsize)
+	register nfsfh_t *fhp;
+	int fhsize;
 {
 	register u_char *fhpp;
 	register u_long fhsum;
-	int i;
+	register int i;
 
 	fhpp = &fhp->fh_bytes[0];
 	fhsum = 0;
-	for (i = 0; i < NFSX_FH; i++)
+	for (i = 0; i < fhsize; i++)
 		fhsum += *fhpp++;
-	return (NFSNOHASH(fhsum));
+	return (fhsum);
 }
 
 /*
@@ -99,25 +99,34 @@
  * nfsnode structure is returned.
  */
 int
-nfs_nget(mntp, fhp, npp)
+nfs_nget(mntp, fhp, fhsize, npp)
 	struct mount *mntp;
-	register nfsv2fh_t *fhp;
+	register nfsfh_t *fhp;
+	int fhsize;
 	struct nfsnode **npp;
 {
+#ifdef Lite2_integrated
+	struct proc *p = curproc;	/* XXX */
+#endif
 	register struct nfsnode *np;
 	struct nfsnodehashhead *nhpp;
 	register struct vnode *vp;
+	extern int (**nfsv2_vnodeop_p)__P((void *));
 	struct vnode *nvp;
 	int error;
 
-	nhpp = nfs_hash(fhp);
+	nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
 loop:
 	for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
-		if (mntp != NFSTOV(np)->v_mount ||
-		    bcmp((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH))
+		if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
+		    bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize))
 			continue;
 		vp = NFSTOV(np);
+#ifdef Lite2_integrated
+		if (vget(vp, LK_EXCLUSIVE, p))
+#else
 		if (vget(vp, 1))
+#endif
 			goto loop;
 		*npp = np;
 		return(0);
@@ -129,26 +138,19 @@
 	}
 	vp = nvp;
 	MALLOC(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
+	bzero((caddr_t)np, sizeof *np);
 	vp->v_data = np;
 	np->n_vnode = vp;
 	/*
 	 * Insert the nfsnode in the hash queue for its new file handle
 	 */
-	np->n_flag = 0;
 	LIST_INSERT_HEAD(nhpp, np, n_hash);
-	bcopy((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH);
-	np->n_attrstamp = 0;
-	np->n_direofoffset = 0;
-	np->n_sillyrename = (struct sillyrename *)0;
-	np->n_size = 0;
-	np->n_mtime = 0;
-	np->n_lockf = 0;
-	if (VFSTONFS(mntp)->nm_flag & NFSMNT_NQNFS) {
-		np->n_brev = 0;
-		np->n_lrev = 0;
-		np->n_expiry = (time_t)0;
-		np->n_timer.cqe_next = (struct nfsnode *)0;
-	}
+	if (fhsize > NFS_SMALLFH) {
+		MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK);
+	} else
+		np->n_fhp = &np->n_fh;
+	bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
+	np->n_fhsize = fhsize;
 	*npp = np;
 	return (0);
 }
@@ -159,6 +161,9 @@
 {
 	struct vop_inactive_args /* {
 		struct vnode *a_vp;
+#ifdef Lite2_integrated
+		struct proc *a_p;
+#endif
 	} */ *ap = v;
 	register struct nfsnode *np;
 	register struct sillyrename *sp;
@@ -168,7 +173,10 @@
 	np = VTONFS(ap->a_vp);
 	if (prtactive && ap->a_vp->v_usecount != 0)
 		vprint("nfs_inactive: pushing active", ap->a_vp);
-	sp = np->n_sillyrename;
+	if (ap->a_vp->v_type != VDIR)
+		sp = np->n_sillyrename;
+	else
+		sp = (struct sillyrename *)0;
 	np->n_sillyrename = (struct sillyrename *)0;
 	if (sp) {
 		/*
@@ -178,12 +186,13 @@
 		nfs_removeit(sp);
 		crfree(sp->s_cred);
 		vrele(sp->s_dvp);
-#ifdef SILLYSEPARATE
-		free((caddr_t)sp, M_NFSREQ);
-#endif
+		FREE((caddr_t)sp, M_NFSREQ);
 	}
 	np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
 		NQNFSNONCACHE | NQNFSWRITE);
+#ifdef Lite2_integrated
+	VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
+#endif
 	return (0);
 }
 
@@ -200,10 +209,12 @@
 	register struct vnode *vp = ap->a_vp;
 	register struct nfsnode *np = VTONFS(vp);
 	register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+	register struct nfsdmap *dp, *dp2;
 	extern int prtactive;
 
 	if (prtactive && vp->v_usecount != 0)
 		vprint("nfs_reclaim: pushing active", vp);
+
 	LIST_REMOVE(np, n_hash);
 
 	/*
@@ -212,12 +223,31 @@
 	if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) {
 		CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
 	}
+
+	/*
+	 * Free up any directory cookie structures and
+	 * large file handle structures that might be associated with
+	 * this nfs node.
+	 */
+	if (vp->v_type == VDIR) {
+		dp = np->n_cookies.lh_first;
+		while (dp) {
+			dp2 = dp;
+			dp = dp->ndm_list.le_next;
+			FREE((caddr_t)dp2, M_NFSDIROFF);
+		}
+	}
+	if (np->n_fhsize > NFS_SMALLFH) {
+		FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
+	}
+
 	cache_purge(vp);
 	FREE(vp->v_data, M_NFSNODE);
 	vp->v_data = (void *)0;
 	return (0);
 }
 
+#ifndef Lite2_integrated
 /*
  * Lock an nfsnode
  */
@@ -237,7 +267,7 @@
 	 */
 	while (vp->v_flag & VXLOCK) {
 		vp->v_flag |= VXWANT;
-		sleep((caddr_t)vp, PINOD);
+		(void) tsleep((caddr_t)vp, PINOD, "nfslck", 0);
 	}
 	if (vp->v_tag == VT_NON)
 		return (ENOENT);
@@ -271,9 +301,9 @@
 		struct vnode *a_vp;
 	} */ *ap = v;
 #endif
-
 	return (0);
 }
+#endif /* Lite2_integrated */
 
 /*
  * Nfs abort op, called after namei() when a CREATE/DELETE isn't actually
--- a/sys/nfs/nfs_nqlease.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_nqlease.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_nqlease.c,v 1.12 1996/02/09 21:48:26 christos Exp $	*/
+/*	$NetBSD: nfs_nqlease.c,v 1.13 1996/02/18 11:53:43 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1992, 1993
@@ -35,7 +35,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_nqlease.c	8.5 (Berkeley) 8/18/94
+ *	@(#)nfs_nqlease.c	8.9 (Berkeley) 5/20/95
  */
 
 /*
@@ -67,7 +67,7 @@
 
 #include <netinet/in.h>
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/xdr_subs.h>
@@ -77,7 +77,6 @@
 #include <nfs/nfs_var.h>
 
 time_t nqnfsstarttime = (time_t)0;
-u_int32_t nqnfs_prog, nqnfs_vers;
 int nqsrv_clockskew = NQ_CLOCKSKEW;
 int nqsrv_writeslack = NQ_WRITESLACK;
 int nqsrv_maxlease = NQ_MAXLEASE;
@@ -88,14 +87,14 @@
  */
 int nqnfs_piggy[NFS_NPROCS] = {
 	0,
-	NQL_READ,
-	NQL_WRITE,
+	0,
+	ND_WRITE,
+	ND_READ,
 	0,
-	NQL_READ,
-	NQL_READ,
-	NQL_READ,
+	ND_READ,
+	ND_READ,
+	ND_WRITE,
 	0,
-	NQL_WRITE,
 	0,
 	0,
 	0,
@@ -103,24 +102,30 @@
 	0,
 	0,
 	0,
-	NQL_READ,
+	ND_READ,
+	ND_READ,
 	0,
-	NQL_READ,
+	0,
+	0,
+	0,
 	0,
 	0,
 	0,
 	0,
 };
 
-extern nfstype nfs_type[9];
+extern nfstype nfsv2_type[9];
+extern nfstype nfsv3_type[9];
 extern struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
 extern int nfsd_waiting;
+extern struct nfsstats nfsstats;
+
 
 #define TRUE	1
 #define	FALSE	0
 
 /*
- * Get or check for a lease for "vp", based on NQL_CHECK flag.
+ * Get or check for a lease for "vp", based on ND_CHECK flag.
  * The rules are as follows:
  * - if a current non-caching lease, reply non-caching
  * - if a current lease for same host only, extend lease
@@ -143,11 +148,12 @@
  *     queue yet. (Ditto for the splsoftclock() and splx(s) calls)
  */
 int
-nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
+nqsrv_getlease(vp, duration, flags, slp, procp, nam, cachablep, frev, cred)
 	struct vnode *vp;
-	u_int *duration;
+	u_int32_t *duration;
 	int flags;
-	struct nfsd *nd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
 	struct mbuf *nam;
 	int *cachablep;
 	u_quad_t *frev;
@@ -166,19 +172,21 @@
 		return (0);
 	if (*duration > nqsrv_maxlease)
 		*duration = nqsrv_maxlease;
-	if ((error = VOP_GETATTR(vp, &vattr, cred, nd->nd_procp)) != 0)
+	error = VOP_GETATTR(vp, &vattr, cred, procp);
+	if (error)
 		return (error);
 	*frev = vattr.va_filerev;
 	s = splsoftclock();
 	tlp = vp->v_lease;
-	if ((flags & NQL_CHECK) == 0)
+	if ((flags & ND_CHECK) == 0)
 		nfsstats.srvnqnfs_getleases++;
 	if (tlp == 0) {
 		/*
 		 * Find the lease by searching the hash list.
 		 */
 		fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
-		if ((error = VFS_VPTOFH(vp, &fh.fh_fid)) != 0) {
+		VFS_VPTOFH(vp, &fh.fh_fid);
+		if (error) {
 			splx(s);
 			return (error);
 		}
@@ -199,12 +207,12 @@
 	if (lp != 0) {
 		if ((lp->lc_flag & LC_NONCACHABLE) ||
 		    (lp->lc_morehosts == (struct nqm *)0 &&
-		     nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host)))
+		     nqsrv_cmpnam(slp, nam, &lp->lc_host)))
 			goto doreply;
-		if ((flags & NQL_READ) && (lp->lc_flag & LC_WRITE) == 0) {
-			if (flags & NQL_CHECK)
+		if ((flags & ND_READ) && (lp->lc_flag & LC_WRITE) == 0) {
+			if (flags & ND_CHECK)
 				goto doreply;
-			if (nqsrv_cmpnam(nd->nd_slp, nam, &lp->lc_host))
+			if (nqsrv_cmpnam(slp, nam, &lp->lc_host))
 				goto doreply;
 			i = 0;
 			if (lp->lc_morehosts) {
@@ -216,7 +224,7 @@
 				ok = 0;
 			}
 			while (ok && (lph->lph_flag & LC_VALID)) {
-				if (nqsrv_cmpnam(nd->nd_slp, nam, lph))
+				if (nqsrv_cmpnam(slp, nam, lph))
 					goto doreply;
 				if (++i == LC_MOREHOSTSIZ) {
 					i = 0;
@@ -236,12 +244,12 @@
 				bzero((caddr_t)*lphp, sizeof (struct nqm));
 				lph = (*lphp)->lpm_hosts;
 			}
-			nqsrv_addhost(lph, nd->nd_slp, nam);
+			nqsrv_addhost(lph, slp, nam);
 			nqsrv_unlocklease(lp);
 		} else {
 			lp->lc_flag |= LC_NONCACHABLE;
 			nqsrv_locklease(lp);
-			nqsrv_send_eviction(vp, lp, nd->nd_slp, nam, cred);
+			nqsrv_send_eviction(vp, lp, slp, nam, cred);
 			nqsrv_waitfor_expiry(lp);
 			nqsrv_unlocklease(lp);
 		}
@@ -249,20 +257,20 @@
 		/*
 		 * Update the lease and return
 		 */
-		if ((flags & NQL_CHECK) == 0)
+		if ((flags & ND_CHECK) == 0)
 			nqsrv_instimeq(lp, *duration);
 		if (lp->lc_flag & LC_NONCACHABLE)
 			*cachablep = 0;
 		else {
 			*cachablep = 1;
-			if (flags & NQL_WRITE)
+			if (flags & ND_WRITE)
 				lp->lc_flag |= LC_WRITTEN;
 		}
 		splx(s);
 		return (0);
 	}
 	splx(s);
-	if (flags & NQL_CHECK)
+	if (flags & ND_CHECK)
 		return (0);
 
 	/*
@@ -279,13 +287,15 @@
 	}
 	MALLOC(lp, struct nqlease *, sizeof (struct nqlease), M_NQLEASE, M_WAITOK);
 	bzero((caddr_t)lp, sizeof (struct nqlease));
-	if (flags & NQL_WRITE)
+	if (flags & ND_WRITE)
 		lp->lc_flag |= (LC_WRITE | LC_WRITTEN);
-	nqsrv_addhost(&lp->lc_host, nd->nd_slp, nam);
+	nqsrv_addhost(&lp->lc_host, slp, nam);
 	lp->lc_vp = vp;
 	lp->lc_fsid = fh.fh_fsid;
 	bcopy(fh.fh_fid.fid_data, lp->lc_fiddata,
 	    fh.fh_fid.fid_len - sizeof (int32_t));
+	if(!lpp)
+		panic("nfs_nqlease.c: Phoney lpp");
 	LIST_INSERT_HEAD(lpp, lp, lc_hash);
 	vp->v_lease = lp;
 	s = splsoftclock();
@@ -302,7 +312,7 @@
  * Just set up args and let nqsrv_getlease() do the rest.
  */
 int
-lease_check(v)
+nqnfs_vop_lease_check(v)
 	void *v;
 {
 	struct vop_lease_args /* {
@@ -311,15 +321,13 @@
 		struct ucred *a_cred;
 		int a_flag;
 	} */ *ap = v;
-	int duration = 0, cache;
-	struct nfsd nfsd;
+	u_int32_t duration = 0;
+	int cache;
 	u_quad_t frev;
 
-	nfsd.nd_slp = NQLOCALSLP;
-	nfsd.nd_procp = ap->a_p;
-	(void) nqsrv_getlease(ap->a_vp, &duration, NQL_CHECK | ap->a_flag,
-		&nfsd, (struct mbuf *)0, &cache, &frev, ap->a_cred);
-	return 0;
+	(void) nqsrv_getlease(ap->a_vp, &duration, ND_CHECK | ap->a_flag,
+	    NQLOCALSLP, ap->a_p, (struct mbuf *)0, &cache, &frev, ap->a_cred);
+	return (0);
 }
 
 /*
@@ -356,7 +364,7 @@
 void
 nqsrv_instimeq(lp, duration)
 	register struct nqlease *lp;
-	u_long duration;
+	u_int32_t duration;
 {
 	register struct nqlease *tlp;
 	time_t newexpiry;
@@ -374,8 +382,10 @@
 	tlp = nqtimerhead.cqh_last;
 	while (tlp != (void *)&nqtimerhead && tlp->lc_expiry > newexpiry)
 		tlp = tlp->lc_timer.cqe_prev;
+#ifdef HASNVRAM
 	if (tlp == nqtimerhead.cqh_last)
 		NQSTORENOVRAM(newexpiry);
+#endif /* HASNVRAM */
 	if (tlp == (void *)&nqtimerhead) {
 		CIRCLEQ_INSERT_HEAD(&nqtimerhead, lp, lc_timer);
 	} else {
@@ -478,9 +488,9 @@
 			else
 				solockp = (int *)0;
 			nfsm_reqhead((struct vnode *)0, NQNFSPROC_EVICTED,
-				NFSX_FH);
-			nfsm_build(cp, caddr_t, NFSX_FH);
-			bzero(cp, NFSX_FH);
+				NFSX_V3FH);
+			nfsm_build(cp, caddr_t, NFSX_V3FH);
+			bzero(cp, NFSX_V3FH);
 			fhp = (fhandle_t *)cp;
 			fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
 			VFS_VPTOFH(vp, &fhp->fh_fid);
@@ -494,9 +504,10 @@
 				printf("mbuf siz=%d\n",siz);
 				panic("Bad nfs svc reply");
 			}
-			m = nfsm_rpchead(cred, TRUE, NQNFSPROC_EVICTED,
-				RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
-				mreq, siz, &mheadend, &xid);
+			m = nfsm_rpchead(cred, (NFSMNT_NFSV3 | NFSMNT_NQNFS),
+				NQNFSPROC_EVICTED,
+				RPCAUTH_UNIX, 5 * NFSX_UNSIGNED, (char *)0,
+				0, (char *)0, mreq, siz, &mheadend, &xid);
 			/*
 			 * For stream protocols, prepend a Sun RPC
 			 * Record Mark.
@@ -670,17 +681,20 @@
  * do the real work.
  */
 int
-nqnfsrv_getlease(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nqnfsrv_getlease(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	register struct nfsv2_fattr *fp;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register struct nfs_fattr *fp;
 	struct vattr va;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register u_int32_t *tl;
 	register int32_t t1;
@@ -693,28 +707,29 @@
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_dissect(tl, u_int32_t *, 2*NFSX_UNSIGNED);
+	nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
 	flags = fxdr_unsigned(int, *tl++);
 	nfsd->nd_duration = fxdr_unsigned(int, *tl);
-	error = nfsrv_fhtovp(fhp,
-			     TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly,
+		(nfsd->nd_flag & ND_KERBAUTH));
 	if (error)
 		nfsm_reply(0);
-	if (rdonly && flags == NQL_WRITE) {
+	if (rdonly && flags == ND_WRITE) {
+		vput(vp);
 		error = EROFS;
 		nfsm_reply(0);
 	}
-	(void) nqsrv_getlease(vp, &nfsd->nd_duration, flags, nfsd,
+	(void) nqsrv_getlease(vp, &nfsd->nd_duration, flags, slp, procp,
 		nam, &cache, &frev, cred);
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
+	error = VOP_GETATTR(vp, &va, cred, procp);
 	vput(vp);
-	nfsm_reply(NFSX_NQFATTR + 4*NFSX_UNSIGNED);
-	nfsm_build(tl, u_int32_t *, 4*NFSX_UNSIGNED);
+	nfsm_reply(NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
+	nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
 	*tl++ = txdr_unsigned(cache);
 	*tl++ = txdr_unsigned(nfsd->nd_duration);
 	txdr_hyper(&frev, tl);
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_NQFATTR);
-	nfsm_srvfillattr;
+	nfsm_build(fp, struct nfs_fattr *, NFSX_V3FATTR);
+	nfsm_srvfillattr(&va, fp);
 	nfsm_srvdone;
 }
 
@@ -723,23 +738,27 @@
  * client. Find the entry and expire it.
  */
 int
-nqnfsrv_vacated(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nqnfsrv_vacated(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
 	register struct nqlease *lp;
 	register struct nqhost *lph;
 	struct nqlease *tlp = (struct nqlease *)0;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	struct nqm *lphnext;
-	int error = 0, i, len, ok, gotit = 0;
-	char *cp2;
+	struct mbuf *mreq, *mb;
+	int error = 0, i, len, ok, gotit = 0, cache = 0;
+	char *cp2, *bpos;
+	u_quad_t frev;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
@@ -765,7 +784,7 @@
 		lphnext = lp->lc_morehosts;
 		ok = 1;
 		while (ok && (lph->lph_flag & LC_VALID)) {
-			if (nqsrv_cmpnam(nfsd->nd_slp, nam, lph)) {
+			if (nqsrv_cmpnam(slp, nam, lph)) {
 				lph->lph_flag |= LC_VACATED;
 				gotit++;
 				break;
@@ -805,7 +824,7 @@
 {
 	register u_int32_t *tl;
 	register caddr_t cp;
-	register int32_t t1;
+	register int32_t t1, t2;
 	register struct nfsnode *np;
 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
 	caddr_t bpos, dpos, cp2;
@@ -816,16 +835,16 @@
 	u_quad_t frev;
 	
 	nfsstats.rpccnt[NQNFSPROC_GETLEASE]++;
-	mb = mreq = nfsm_reqh(vp, NQNFSPROC_GETLEASE, NFSX_FH+2*NFSX_UNSIGNED,
+	mb = mreq = nfsm_reqh(vp, NQNFSPROC_GETLEASE, NFSX_V3FH+2*NFSX_UNSIGNED,
 		 &bpos);
-	nfsm_fhtom(vp);
-	nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
+	nfsm_fhtom(vp, 1);
+	nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
 	*tl++ = txdr_unsigned(rwflag);
 	*tl = txdr_unsigned(nmp->nm_leaseterm);
 	reqtime = time.tv_sec;
 	nfsm_request(vp, NQNFSPROC_GETLEASE, p, cred);
 	np = VTONFS(vp);
-	nfsm_dissect(tl, u_int32_t *, 4*NFSX_UNSIGNED);
+	nfsm_dissect(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
 	cachable = fxdr_unsigned(int, *tl++);
 	reqtime += fxdr_unsigned(int, *tl++);
 	if (reqtime > time.tv_sec) {
@@ -849,6 +868,8 @@
 	register caddr_t cp;
 	register struct mbuf *m;
 	register int i;
+	register u_int32_t *tl;
+	register int32_t t2;
 	caddr_t bpos;
 	u_int32_t xid;
 	int error = 0;
@@ -858,17 +879,17 @@
 	
 	nmp = VFSTONFS(vp->v_mount);
 	nfsstats.rpccnt[NQNFSPROC_VACATED]++;
-	nfsm_reqhead(vp, NQNFSPROC_VACATED, NFSX_FH);
-	nfsm_fhtom(vp);
+	nfsm_reqhead(vp, NQNFSPROC_VACATED, NFSX_V3FH);
+	nfsm_fhtom(vp, 1);
 	m = mreq;
 	i = 0;
 	while (m) {
 		i += m->m_len;
 		m = m->m_next;
 	}
-	m = nfsm_rpchead(cred, TRUE, NQNFSPROC_VACATED,
-		RPCAUTH_UNIX, 5*NFSX_UNSIGNED, (char *)0,
-		mreq, i, &mheadend, &xid);
+	m = nfsm_rpchead(cred, nmp->nm_flag, NQNFSPROC_VACATED,
+		RPCAUTH_UNIX, 5 * NFSX_UNSIGNED, (char *)0,
+		0, (char *)0, mreq, i, &mheadend, &xid);
 	if (nmp->nm_sotype == SOCK_STREAM) {
 		M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
 		*mtod(m, u_int32_t *) = htonl(0x80000000 | (m->m_pkthdr.len -
@@ -881,6 +902,7 @@
 	(void) nfs_send(nmp->nm_so, nmp->nm_nam, m, &myrep);
 	if (nmp->nm_soflags & PR_CONNREQUIRED)
 		nfs_sndunlock(&nmp->nm_flag);
+nfsmout:
 	return (error);
 }
 
@@ -896,28 +918,37 @@
 	register struct vnode *vp;
 	register u_int32_t *tl;
 	register int32_t t1;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct nfsnode *np;
-	struct nfsd nd;
-	int error;
-	char *cp2;
+	struct nfsd tnfsd;
+	struct nfssvc_sock *slp;
+	struct nfsrv_descript ndesc;
+	register struct nfsrv_descript *nfsd = &ndesc;
+	struct mbuf **mrq = (struct mbuf **)0, *mb, *mreq;
+	int error = 0, cache = 0;
+	char *cp2, *bpos;
+	u_quad_t frev;
 
-	nd.nd_mrep = mrep;
-	nd.nd_md = md;
-	nd.nd_dpos = dpos;
-	if ((error = nfs_getreq(&nd, FALSE)) != 0)
+#ifndef nolint
+	slp = NULL;
+#endif
+	nfsd->nd_mrep = mrep;
+	nfsd->nd_md = md;
+	nfsd->nd_dpos = dpos;
+	error = nfs_getreq(nfsd, &tnfsd, FALSE);
+	if (error)
 		return (error);
-	md = nd.nd_md;
-	dpos = nd.nd_dpos;
-	if (nd.nd_procnum != NQNFSPROC_EVICTED) {
+	md = nfsd->nd_md;
+	dpos = nfsd->nd_dpos;
+	if (nfsd->nd_procnum != NQNFSPROC_EVICTED) {
 		m_freem(mrep);
 		return (EPERM);
 	}
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
 	m_freem(mrep);
-	error = nfs_nget(nmp->nm_mountp, (nfsv2fh_t *) fhp, &np);
+	error = nfs_nget(nmp->nm_mountp, (nfsfh_t *)fhp, NFSX_V3FH, &np);
 	if (error)
 		return (error);
 	vp = NFSTOV(np);
@@ -952,35 +983,39 @@
 	register struct nfsnode *np;
 	struct vnode *vp;
 	struct nfsreq myrep;
+	struct nfsuid *nuidp, *nnuidp;
 	int error = 0, vpid;
 
 	/*
 	 * First initialize some variables
 	 */
-	nqnfs_prog = txdr_unsigned(NQNFS_PROG);
-	nqnfs_vers = txdr_unsigned(NQNFS_VER1);
 
 	/*
 	 * If an authorization string is being passed in, get it.
 	 */
 	if ((flag & NFSSVC_GOTAUTH) &&
-		(nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT)) == 0) {
-		if (nmp->nm_flag & NFSMNT_HASAUTH)
-			panic("cld kerb");
-		if ((flag & NFSSVC_AUTHINFAIL) == 0) {
-			if (ncd->ncd_authlen <= RPCAUTH_MAXSIZ &&
-				copyin(ncd->ncd_authstr, nmp->nm_authstr,
-				ncd->ncd_authlen) == 0) {
-				nmp->nm_authtype = ncd->ncd_authtype;
-				nmp->nm_authlen = ncd->ncd_authlen;
-			} else
-				nmp->nm_flag |= NFSMNT_AUTHERR;
+	    (nmp->nm_flag & (NFSMNT_WAITAUTH | NFSMNT_DISMNT)) == 0) {
+	    if (nmp->nm_flag & NFSMNT_HASAUTH)
+		panic("cld kerb");
+	    if ((flag & NFSSVC_AUTHINFAIL) == 0) {
+		if (ncd->ncd_authlen <= nmp->nm_authlen &&
+		    ncd->ncd_verflen <= nmp->nm_verflen &&
+		    !copyin(ncd->ncd_authstr,nmp->nm_authstr,ncd->ncd_authlen)&&
+		    !copyin(ncd->ncd_verfstr,nmp->nm_verfstr,ncd->ncd_verflen)){
+		    nmp->nm_authtype = ncd->ncd_authtype;
+		    nmp->nm_authlen = ncd->ncd_authlen;
+		    nmp->nm_verflen = ncd->ncd_verflen;
+#ifdef NFSKERB
+		    nmp->nm_key = ncd->ncd_key;
+#endif
 		} else
-			nmp->nm_flag |= NFSMNT_AUTHERR;
-		nmp->nm_flag |= NFSMNT_HASAUTH;
-		wakeup((caddr_t)&nmp->nm_authlen);
+		    nmp->nm_flag |= NFSMNT_AUTHERR;
+	    } else
+		nmp->nm_flag |= NFSMNT_AUTHERR;
+	    nmp->nm_flag |= NFSMNT_HASAUTH;
+	    wakeup((caddr_t)&nmp->nm_authlen);
 	} else
-		nmp->nm_flag |= NFSMNT_WAITAUTH;
+	    nmp->nm_flag |= NFSMNT_WAITAUTH;
 
 	/*
 	 * Loop every second updating queue until there is a termination sig.
@@ -1008,15 +1043,15 @@
 		while (np != (void *)&nmp->nm_timerhead &&
 		       (nmp->nm_flag & NFSMNT_DISMINPROG) == 0) {
 			vp = NFSTOV(np);
-if (strncmp(&vp->v_mount->mnt_stat.f_fstypename[0], MOUNT_NFS, MFSNAMELEN))
-	panic("trash2");
 			vpid = vp->v_id;
 			if (np->n_expiry < time.tv_sec) {
+#ifdef Lite2_integrated
+			   if (vget(vp, LK_EXCLUSIVE, p) == 0) {
+#else
 			   if (vget(vp, 1) == 0) {
+#endif
 			     nmp->nm_inprog = vp;
 			     if (vpid == vp->v_id) {
-if (strncmp(&vp->v_mount->mnt_stat.f_fstypename[0], MOUNT_NFS, MFSNAMELEN))
-	panic("trash3");
 				CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
 				np->n_timer.cqe_next = 0;
 				if ((np->n_flag & (NMODIFIED | NQNFSEVICTED))
@@ -1039,11 +1074,14 @@
 			} else if ((np->n_expiry - NQ_RENEWAL) < time.tv_sec) {
 			    if ((np->n_flag & (NQNFSWRITE | NQNFSNONCACHE))
 				 == NQNFSWRITE && vp->v_dirtyblkhd.lh_first &&
+#ifdef Lite2_integrated
+				 vget(vp, LK_EXCLUSIVE, p) == 0) {
+#else
 				 vget(vp, 1) == 0) {
+#endif
 				 nmp->nm_inprog = vp;
-if (strncmp(&vp->v_mount->mnt_stat.f_fstypename[0], MOUNT_NFS, MFSNAMELEN)) panic("trash4");
 				 if (vpid == vp->v_id &&
-				     nqnfs_getlease(vp, NQL_WRITE, cred, p)==0)
+				     nqnfs_getlease(vp, ND_WRITE, cred, p)==0)
 					np->n_brev = np->n_lrev;
 				 vrele(vp);
 				 nmp->nm_inprog = NULLVP;
@@ -1078,6 +1116,16 @@
 			(void) dounmount(nmp->nm_mountp, MNT_FORCE, p);
 	    }
 	}
+
+	/*
+	 * Finally, we can free up the mount structure.
+	 */
+	for (nuidp = nmp->nm_uidlruhead.tqh_first; nuidp != 0; nuidp = nnuidp) {
+		nnuidp = nuidp->nu_lru.tqe_next;
+		LIST_REMOVE(nuidp, nu_hash);
+		TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru);
+		free((caddr_t)nuidp, M_NFSUID);
+	}
 	free((caddr_t)nmp, M_NFSMNT);
 	if (error == EWOULDBLOCK)
 		error = 0;
@@ -1099,9 +1147,9 @@
 
 	if (np->n_timer.cqe_next != 0) {
 		CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
-		if (rwflag == NQL_WRITE)
+		if (rwflag == ND_WRITE)
 			np->n_flag |= NQNFSWRITE;
-	} else if (rwflag == NQL_READ)
+	} else if (rwflag == ND_READ)
 		np->n_flag &= ~NQNFSWRITE;
 	else
 		np->n_flag |= NQNFSWRITE;
@@ -1127,7 +1175,7 @@
  * Called from the settimeofday() syscall.
  */
 void
-lease_updatetime(deltat)
+nqnfs_lease_updatetime(deltat)
 	register int deltat;
 {
 	register struct nqlease *lp;
@@ -1135,6 +1183,10 @@
 	struct mount *mp;
 	struct nfsmount *nmp;
 	int s;
+#ifdef Lite2_integrated
+	struct proc *p = curproc;	/* XXX */
+	struct mount *nxtmp;
+#endif
 
 	if (nqnfsstarttime != 0)
 		nqnfsstarttime += deltat;
@@ -1148,6 +1200,30 @@
 	 * Search the mount list for all nqnfs mounts and do their timer
 	 * queues.
 	 */
+#ifdef Lite2_integrated
+	simple_lock(&mountlist_slock);
+	for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nxtmp) {
+		if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) {
+			nxtmp = mp->mnt_list.cqe_next;
+			continue;
+		}
+		if (!strncmp(&mp->mnt_stat.f_fstypename[0], MOUNT_NFS,
+		    MFSNAMELEN)) {
+			nmp = VFSTONFS(mp);
+			if (nmp->nm_flag & NFSMNT_NQNFS) {
+				for (np = nmp->nm_timerhead.cqh_first;
+				    np != (void *)&nmp->nm_timerhead;
+				    np = np->n_timer.cqe_next) {
+					np->n_expiry += deltat;
+				}
+			}
+		}
+		simple_lock(&mountlist_slock);
+		nxtmp = mp->mnt_list.cqe_next;
+		vfs_unbusy(mp, p);
+	}
+	simple_unlock(&mountlist_slock);
+#else /* Lite2_integrated */
 	for (mp = mountlist.cqh_first; mp != (void *)&mountlist;
 	     mp = mp->mnt_list.cqe_next) {
 		if (!strncmp(&mp->mnt_stat.f_fstypename[0], MOUNT_NFS,
@@ -1162,6 +1238,7 @@
 			}
 		}
 	}
+#endif
 }
 
 /*
--- a/sys/nfs/nfs_serv.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_serv.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_serv.c,v 1.22 1996/02/09 21:48:27 christos Exp $	*/
+/*	$NetBSD: nfs_serv.c,v 1.23 1996/02/18 11:53:45 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,11 +35,11 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_serv.c	8.4 (Berkeley) 6/4/94
+ *	@(#)nfs_serv.c	8.7 (Berkeley) 5/14/95
  */
 
 /*
- * nfs version 2 server calls to vnode ops
+ * nfs version 2 and 3 server calls to vnode ops
  * - these routines generally have 3 phases
  *   1 - break down and validate rpc request in mbuf list
  *   2 - do the vnode ops for the request
@@ -54,6 +54,8 @@
  *	returning an error from the server function implies a fatal error
  *	such as a badly constructed rpc request that should be dropped without
  *	a reply.
+ *	For Version 3, nfsm_reply() does not return for the error case, since
+ *	most version 3 rpcs return more than the status for error cases.
  */
 
 #include <sys/param.h>
@@ -63,13 +65,17 @@
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
 #include <sys/mbuf.h>
 #include <sys/dirent.h>
 #include <sys/stat.h>
+#include <sys/kernel.h>
+#include <ufs/ufs/dir.h>
 
 #include <vm/vm.h>
 
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/rpcv2.h>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
@@ -77,54 +83,77 @@
 #include <nfs/nqnfs.h>
 #include <nfs/nfs_var.h>
 
-/* Defs */
-#define	TRUE	1
-#define	FALSE	0
-
 /* Global vars */
-extern u_long nfs_procids[NFS_NPROCS];
 extern u_int32_t nfs_xdrneg1;
 extern u_int32_t nfs_false, nfs_true;
-nfstype nfs_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
-		      NFCHR, NFNON };
+extern enum vtype nv3tov_type[8];
+extern struct nfsstats nfsstats;
+extern nfstype nfsv2_type[9];
+extern nfstype nfsv3_type[9];
+int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000;
 
 /*
- * nqnfs access service
+ * nfs v3 access service
  */
 int
-nqnfsrv_access(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv3_access(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache = 0, mode = 0;
+	int error = 0, rdonly, cache = 0, getret;
 	char *cp2;
-	struct mbuf *mb, *mreq;
+	struct mbuf *mb, *mreq, *mb2;
+	struct vattr va;
+	u_long testmode, nfsmode;
 	u_quad_t frev;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	if (*tl++ == nfs_true)
-		mode |= VREAD;
-	if (*tl++ == nfs_true)
-		mode |= VWRITE;
-	if (*tl == nfs_true)
-		mode |= VEXEC;
-	error = nfsrv_access(vp, mode, cred, rdonly, nfsd->nd_procp);
+	nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly,
+	    (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(1, (struct vattr *)0);
+		return (0);
+	}
+	nfsmode = fxdr_unsigned(u_int32_t, *tl);
+	if ((nfsmode & NFSV3ACCESS_READ) &&
+		nfsrv_access(vp, VREAD, cred, rdonly, procp))
+		nfsmode &= ~NFSV3ACCESS_READ;
+	if (vp->v_type == VDIR)
+		testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND |
+			NFSV3ACCESS_DELETE);
+	else
+		testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
+	if ((nfsmode & testmode) &&
+		nfsrv_access(vp, VWRITE, cred, rdonly, procp))
+		nfsmode &= ~testmode;
+	if (vp->v_type == VDIR)
+		testmode = NFSV3ACCESS_LOOKUP;
+	else
+		testmode = NFSV3ACCESS_EXECUTE;
+	if ((nfsmode & testmode) &&
+		nfsrv_access(vp, VEXEC, cred, rdonly, procp))
+		nfsmode &= ~testmode;
+	getret = VOP_GETATTR(vp, &va, cred, procp);
 	vput(vp);
-	nfsm_reply(0);
+	nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED);
+	nfsm_srvpostop_attr(getret, &va);
+	nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(nfsmode);
 	nfsm_srvdone;
 }
 
@@ -132,17 +161,20 @@
  * nfs getattr service
  */
 int
-nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_getattr(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	register struct nfsv2_fattr *fp;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register struct nfs_fattr *fp;
 	struct vattr va;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register u_int32_t *tl;
 	register int32_t t1;
@@ -154,15 +186,20 @@
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly,
+	    (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
 		nfsm_reply(0);
-	nqsrv_getl(vp, NQL_READ);
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
+		return (0);
+	}
+	nqsrv_getl(vp, ND_READ);
+	error = VOP_GETATTR(vp, &va, cred, procp);
 	vput(vp);
-	nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
+	nfsm_reply(NFSX_FATTR(nfsd->nd_flag & ND_NFSV3));
+	if (error)
+		return (0);
+	nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(nfsd->nd_flag & ND_NFSV3));
+	nfsm_srvfillattr(&va, fp);
 	nfsm_srvdone;
 }
 
@@ -170,68 +207,97 @@
  * nfs setattr service
  */
 int
-nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_setattr(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	struct vattr va;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	struct vattr va, preat;
 	register struct nfsv2_sattr *sp;
-	register struct nfsv2_fattr *fp;
+	register struct nfs_fattr *fp;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache;
+	int error = 0, rdonly, cache, preat_ret = 1, postat_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0;
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
-	u_quad_t frev, frev2;
+	u_quad_t frev;
+	struct timespec guard;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_dissect(sp, struct nfsv2_sattr *,
-		     NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	nqsrv_getl(vp, NQL_WRITE);
 	VATTR_NULL(&va);
-	/*
-	 * Nah nah nah nah na nah
-	 * There is a bug in the Sun client that puts 0xffff in the mode
-	 * field of sattr when it should put in 0xffffffff. The u_short
-	 * doesn't sign extend.
-	 * --> check the low order 2 bytes for 0xffff
-	 */
-	if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
-		va.va_mode = nfstov_mode(sp->sa_mode);
-	if (sp->sa_uid != nfs_xdrneg1)
-		va.va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
-	if (sp->sa_gid != nfs_xdrneg1)
-		va.va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
-	if (nfsd->nd_nqlflag == NQL_NOVAL) {
-		if (sp->sa_nfssize != nfs_xdrneg1)
-			va.va_size = fxdr_unsigned(u_quad_t, sp->sa_nfssize);
-		if (sp->sa_nfsatime.nfs_sec != nfs_xdrneg1) {
+	if (v3) {
+		nfsm_srvsattr(&va);
+		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+		gcheck = fxdr_unsigned(int, *tl);
+		if (gcheck) {
+			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			fxdr_nfsv3time(tl, &guard);
+		}
+	} else {
+		nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+		/*
+		 * Nah nah nah nah na nah
+		 * There is a bug in the Sun client that puts 0xffff in the mode
+		 * field of sattr when it should put in 0xffffffff. The u_short
+		 * doesn't sign extend.
+		 * --> check the low order 2 bytes for 0xffff
+		 */
+		if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
+			va.va_mode = nfstov_mode(sp->sa_mode);
+		if (sp->sa_uid != nfs_xdrneg1)
+			va.va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
+		if (sp->sa_gid != nfs_xdrneg1)
+			va.va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
+		if (sp->sa_size != nfs_xdrneg1)
+			va.va_size = fxdr_unsigned(u_quad_t, sp->sa_size);
+		if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) {
 #ifdef notyet
-			fxdr_nfstime(&sp->sa_nfsatime, &va.va_atime);
+			fxdr_nfsv2time(&sp->sa_atime, &va.va_atime);
 #else
 			va.va_atime.tv_sec =
-				fxdr_unsigned(int32_t, sp->sa_nfsatime.nfs_sec);
+				fxdr_unsigned(u_int32_t,sp->sa_atime.nfsv2_sec);
 			va.va_atime.tv_nsec = 0;
 #endif
 		}
-		if (sp->sa_nfsmtime.nfs_sec != nfs_xdrneg1)
-			fxdr_nfstime(&sp->sa_nfsmtime, &va.va_mtime);
-	} else {
-		fxdr_hyper(&sp->sa_nqsize, &va.va_size);
-		fxdr_nqtime(&sp->sa_nqatime, &va.va_atime);
-		fxdr_nqtime(&sp->sa_nqmtime, &va.va_mtime);
-		va.va_flags = fxdr_unsigned(u_int32_t, sp->sa_nqflags);
+		if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1)
+			fxdr_nfsv2time(&sp->sa_mtime, &va.va_mtime);
+
+	}
+
+	/*
+	 * Now that we have all the fields, lets do it.
+	 */
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly,
+	    (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &va);
+		return (0);
+	}
+	nqsrv_getl(vp, ND_WRITE);
+	if (v3) {
+		error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp);
+		if (!error && gcheck &&
+			(preat.va_ctime.tv_sec != guard.tv_sec ||
+			 preat.va_ctime.tv_nsec != guard.tv_nsec))
+			error = NFSERR_NOT_SYNC;
+		if (error) {
+			vput(vp);
+			nfsm_reply(NFSX_WCCDATA(v3));
+			nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &va);
+			return (0);
+		}
 	}
 
 	/*
@@ -248,22 +314,22 @@
 			error = EISDIR;
 			goto out;
 		} else if ((error = nfsrv_access(vp, VWRITE, cred, rdonly,
-						 nfsd->nd_procp)) != 0)
+			procp)) != 0)
 			goto out;
 	}
-	if ((error = VOP_SETATTR(vp, &va, cred, nfsd->nd_procp)) != 0) {
-		vput(vp);
-		nfsm_reply(0);
-	}
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
+	error = VOP_SETATTR(vp, &va, cred, procp);
+	postat_ret = VOP_GETATTR(vp, &va, cred, procp);
+	if (!error)
+		error = postat_ret;
 out:
 	vput(vp);
-	nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 2*NFSX_UNSIGNED);
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
-	if (nfsd->nd_nqlflag != NQL_NOVAL) {
-		nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-		txdr_hyper(&frev2, tl);
+	nfsm_reply(NFSX_WCCORFATTR(v3));
+	if (v3) {
+		nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &va);
+		return (0);
+	} else {
+		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+		nfsm_srvfillattr(&va, fp);
 	}
 	nfsm_srvdone;
 }
@@ -272,73 +338,74 @@
  * nfs lookup rpc
  */
 int
-nfsrv_lookup(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_lookup(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	register struct nfsv2_fattr *fp;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register struct nfs_fattr *fp;
 	struct nameidata nd;
-	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vnode *vp, *dirp;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	register caddr_t cp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, cache, duration2, cache2, len;
+	int error = 0, cache, len, dirattr_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
-	struct vattr va;
-	u_quad_t frev, frev2;
+	struct vattr va, dirattr;
+	u_quad_t frev;
 
 	fhp = &nfh.fh_generic;
-	duration2 = 0;
-	if (nfsd->nd_nqlflag != NQL_NOVAL) {
-		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-		duration2 = fxdr_unsigned(int, *tl);
-	}
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = LOOKUP;
 	nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART;
-	if ((error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			       nfsd->nd_procp)) != 0)
-		nfsm_reply(0);
-	nqsrv_getl(nd.ni_startdir, NQL_READ);
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred,
+				procp);
+		vrele(dirp);
+	}
+	if (error) {
+		nfsm_reply(NFSX_POSTOPATTR(v3));
+		nfsm_srvpostop_attr(dirattr_ret, &dirattr);
+		return (0);
+	}
+	nqsrv_getl(nd.ni_startdir, ND_READ);
 	vrele(nd.ni_startdir);
 	FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
 	vp = nd.ni_vp;
 	bzero((caddr_t)fhp, sizeof(nfh));
 	fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
-	if ((error = VFS_VPTOFH(vp, &fhp->fh_fid)) != 0) {
-		vput(vp);
-		nfsm_reply(0);
-	}
-	if (duration2)
-		(void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd,
-			nam, &cache2, &frev2, cred);
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
+	error = VFS_VPTOFH(vp, &fhp->fh_fid);
+	if (!error)
+		error = VOP_GETATTR(vp, &va, cred, procp);
 	vput(vp);
-	nfsm_reply(NFSX_FH + NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 5*NFSX_UNSIGNED);
-	if (nfsd->nd_nqlflag != NQL_NOVAL) {
-		if (duration2) {
-			nfsm_build(tl, u_int32_t *, 5*NFSX_UNSIGNED);
-			*tl++ = txdr_unsigned(NQL_READ);
-			*tl++ = txdr_unsigned(cache2);
-			*tl++ = txdr_unsigned(duration2);
-			txdr_hyper(&frev2, tl);
-		} else {
-			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
-			*tl = 0;
-		}
+	nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3));
+	if (error) {
+		nfsm_srvpostop_attr(dirattr_ret, &dirattr);
+		return (0);
 	}
-	nfsm_srvfhtom(fhp);
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
+	nfsm_srvfhtom(fhp, v3);
+	if (v3) {
+		nfsm_srvpostop_attr(0, &va);
+		nfsm_srvpostop_attr(dirattr_ret, &dirattr);
+	} else {
+		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+		nfsm_srvfillattr(&va, fp);
+	}
 	nfsm_srvdone;
 }
 
@@ -346,24 +413,29 @@
  * nfs readlink service
  */
 int
-nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_readlink(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
 	register struct iovec *ivp = iv;
 	register struct mbuf *mp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache, i, tlen, len;
+	int error = 0, rdonly, cache, i, tlen, len, getret;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mb2, *mp2 = NULL, *mp3 = NULL, *mreq;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vattr attr;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct uio io, *uiop = &io;
 	u_quad_t frev;
@@ -399,22 +471,34 @@
 	uiop->uio_rw = UIO_READ;
 	uiop->uio_segflg = UIO_SYSSPACE;
 	uiop->uio_procp = (struct proc *)0;
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
 	if (error) {
 		m_freem(mp3);
-		nfsm_reply(0);
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(1, (struct vattr *)0);
+		return (0);
 	}
 	if (vp->v_type != VLNK) {
-		error = EINVAL;
+		if (v3)
+			error = EINVAL;
+		else
+			error = ENXIO;
 		goto out;
 	}
-	nqsrv_getl(vp, NQL_READ);
+	nqsrv_getl(vp, ND_READ);
 	error = VOP_READLINK(vp, uiop, cred);
 out:
+	getret = VOP_GETATTR(vp, &attr, cred, procp);
 	vput(vp);
 	if (error)
 		m_freem(mp3);
-	nfsm_reply(NFSX_UNSIGNED);
+	nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED);
+	if (v3) {
+		nfsm_srvpostop_attr(getret, &attr);
+		if (error)
+			return (0);
+	}
 	if (uiop->uio_resid > 0) {
 		len -= uiop->uio_resid;
 		tlen = nfsm_rndup(len);
@@ -430,26 +514,31 @@
  * nfs read service
  */
 int
-nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_read(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register struct iovec *iv;
 	struct iovec *iv2;
 	register struct mbuf *m;
-	register struct nfsv2_fattr *fp;
+	register struct nfs_fattr *fp;
 	register u_int32_t *tl;
 	register int32_t t1;
+	register int i;
 	caddr_t bpos;
-	int error = 0, rdonly, cache, i, cnt, len, left, siz, tlen;
+	int error = 0, rdonly, cache, cnt, len, left, siz, tlen, getret;
+	int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen;
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
 	struct mbuf *m2;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct uio io, *uiop = &io;
 	struct vattr va;
@@ -458,43 +547,58 @@
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	if (nfsd->nd_nqlflag == NQL_NOVAL) {
-		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-		off = (off_t)fxdr_unsigned(u_int32_t, *tl);
-	} else {
+	if (v3) {
 		nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
 		fxdr_hyper(tl, &off);
+	} else {
+		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+		off = (off_t)fxdr_unsigned(u_int32_t, *tl);
 	}
-	nfsm_srvstrsiz(cnt, NFS_MAXDATA);
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	if (vp->v_type != VREG) {
-		error = (vp->v_type == VDIR) ? EISDIR : EACCES;
-		vput(vp);
-		nfsm_reply(0);
+	nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd));
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(1, (struct vattr *)0);
+		return (0);
 	}
-	nqsrv_getl(vp, NQL_READ);
-	error = nfsrv_access(vp, VREAD, cred, rdonly, nfsd->nd_procp);
-	if (error)
-		goto out;
-	error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp);
-	if (error)
-		goto out;
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
-
+	if (vp->v_type != VREG) {
+		if (v3)
+			error = EINVAL;
+		else
+			error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+	}
+	if (!error) {
+	    nqsrv_getl(vp, ND_READ);
+	    if ((error = nfsrv_access(vp, VREAD, cred, rdonly, procp)) != 0)
+		error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
+	}
+	getret = VOP_GETATTR(vp, &va, cred, procp);
+	if (!error)
+		error = getret;
 	if (error) {
-out:
 		vput(vp);
-		nfsm_reply(0);
+		nfsm_reply(NFSX_POSTOPATTR(v3));
+		nfsm_srvpostop_attr(getret, &va);
+		return (0);
 	}
 	if (off >= va.va_size)
 		cnt = 0;
-	else if ((off + cnt) > va.va_size)
+	else if ((off + reqlen) > va.va_size)
 		cnt = nfsm_rndup(va.va_size - off);
-	nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL)+NFSX_UNSIGNED+nfsm_rndup(cnt));
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+	else
+		cnt = reqlen;
+	nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt));
+	if (v3) {
+		nfsm_build(tl, u_int32_t *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
+		*tl++ = nfs_true;
+		fp = (struct nfs_fattr *)tl;
+		tl += (NFSX_V3FATTR / sizeof (u_int32_t));
+	} else {
+		nfsm_build(tl, u_int32_t *, NFSX_V2FATTR + NFSX_UNSIGNED);
+		fp = (struct nfs_fattr *)tl;
+		tl += (NFSX_V2FATTR / sizeof (u_int32_t));
+	}
 	len = left = cnt;
 	if (cnt > 0) {
 		/*
@@ -502,19 +606,11 @@
 		 */
 		i = 0;
 		m = m2 = mb;
-		MALLOC(iv, struct iovec *,
-		       ((NFS_MAXDATA+MLEN-1)/MLEN) * sizeof (struct iovec),
-		       M_TEMP, M_WAITOK);
-		iv2 = iv;
 		while (left > 0) {
 			siz = min(M_TRAILINGSPACE(m), left);
 			if (siz > 0) {
-				m->m_len += siz;
-				iv->iov_base = bpos;
-				iv->iov_len = siz;
-				iv++;
+				left -= siz;
 				i++;
-				left -= siz;
 			}
 			if (left > 0) {
 				MGET(m, M_WAIT, MT_DATA);
@@ -522,10 +618,28 @@
 				m->m_len = 0;
 				m2->m_next = m;
 				m2 = m;
-				bpos = mtod(m, caddr_t);
 			}
 		}
-		uiop->uio_iov = iv2;
+		MALLOC(iv, struct iovec *, i * sizeof (struct iovec),
+		       M_TEMP, M_WAITOK);
+		uiop->uio_iov = iv2 = iv;
+		m = mb;
+		left = cnt;
+		i = 0;
+		while (left > 0) {
+			if (m == NULL)
+				panic("nfsrv_read iov");
+			siz = min(M_TRAILINGSPACE(m), left);
+			if (siz > 0) {
+				iv->iov_base = mtod(m, caddr_t) + m->m_len;
+				iv->iov_len = siz;
+				m->m_len += siz;
+				left -= siz;
+				iv++;
+				i++;
+			}
+			m = m->m_next;
+		}
 		uiop->uio_iovcnt = i;
 		uiop->uio_offset = off;
 		uiop->uio_resid = cnt;
@@ -534,19 +648,30 @@
 		error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
 		off = uiop->uio_offset;
 		FREE((caddr_t)iv2, M_TEMP);
-		if (error || (error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp))) {
+		if (error || (getret = VOP_GETATTR(vp, &va, cred, procp)) != 0){
+			if (!error)
+				error = getret;
 			m_freem(mreq);
 			vput(vp);
-			nfsm_reply(0);
+			nfsm_reply(NFSX_POSTOPATTR(v3));
+			nfsm_srvpostop_attr(getret, &va);
+			return (0);
 		}
 	} else
 		uiop->uio_resid = 0;
 	vput(vp);
-	nfsm_srvfillattr;
+	nfsm_srvfillattr(&va, fp);
 	len -= uiop->uio_resid;
 	tlen = nfsm_rndup(len);
 	if (cnt != tlen || tlen != len)
-		nfsm_adj(mb, cnt-tlen, tlen-len);
+		nfsm_adj(mb, cnt - tlen, tlen - len);
+	if (v3) {
+		*tl++ = txdr_unsigned(len);
+		if (len < reqlen)
+			*tl++ = nfs_true;
+		else
+			*tl++ = nfs_false;
+	}
 	*tl = txdr_unsigned(len);
 	nfsm_srvdone;
 }
@@ -555,223 +680,685 @@
  * nfs write service
  */
 int
-nfsrv_write(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_write(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register struct iovec *ivp;
+	register int i, cnt;
 	register struct mbuf *mp;
-	register struct nfsv2_fattr *fp;
-	struct iovec iv[NFS_MAXIOVEC];
-	struct vattr va;
+	register struct nfs_fattr *fp;
+	struct iovec *iv;
+	struct vattr va, forat;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache, siz, len, xfer;
-	int ioflags = IO_SYNC | IO_NODELOCKED;
+	int error = 0, rdonly, cache, len, forat_ret = 1;
+	int ioflags, aftat_ret = 1, retlen, zeroing, adjust;
+	int stable = NFSV3WRITE_FILESYNC;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct uio io, *uiop = &io;
 	off_t off;
 	u_quad_t frev;
 
+	if (mrep == NULL) {
+		*mrq = NULL;
+		return (0);
+	}
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_dissect(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
-	if (nfsd->nd_nqlflag == NQL_NOVAL) {
+	if (v3) {
+		nfsm_dissect(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+		fxdr_hyper(tl, &off);
+		tl += 3;
+		stable = fxdr_unsigned(int, *tl++);
+	} else {
+		nfsm_dissect(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
 		off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
 		tl += 2;
-	} else {
-		fxdr_hyper(tl, &off);
-		tl += 2;
-		if (fxdr_unsigned(u_int32_t, *tl++))
-			ioflags |= IO_APPEND;
 	}
-	len = fxdr_unsigned(int32_t, *tl);
-	if (len > NFS_MAXDATA || len <= 0) {
-		error = EBADRPC;
-		nfsm_reply(0);
-	}
-	if (dpos == (mtod(md, caddr_t)+md->m_len)) {
-		mp = md->m_next;
-		if (mp == NULL) {
-			error = EBADRPC;
-			nfsm_reply(0);
+	retlen = len = fxdr_unsigned(int32_t, *tl);
+	cnt = i = 0;
+
+	/*
+	 * For NFS Version 2, it is not obvious what a write of zero length
+	 * should do, but I might as well be consistent with Version 3,
+	 * which is to return ok so long as there are no permission problems.
+	 */
+	if (len > 0) {
+	    zeroing = 1;
+	    mp = mrep;
+	    while (mp) {
+		if (mp == md) {
+			zeroing = 0;
+			adjust = dpos - mtod(mp, caddr_t);
+			mp->m_len -= adjust;
+			if (mp->m_len > 0 && adjust > 0)
+				NFSMADV(mp, adjust);
 		}
-	} else {
-		mp = md;
-		siz = dpos-mtod(mp, caddr_t);
-		mp->m_len -= siz;
-		NFSMADV(mp, siz);
+		if (zeroing)
+			mp->m_len = 0;
+		else if (mp->m_len > 0) {
+			i += mp->m_len;
+			if (i > len) {
+				mp->m_len -= (i - len);
+				zeroing	= 1;
+			}
+			if (mp->m_len > 0)
+				cnt++;
+		}
+		mp = mp->m_next;
+	    }
+	}
+	if (len > NFS_MAXDATA || len < 0 || i < len) {
+		error = EIO;
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+		return (0);
 	}
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+		return (0);
+	}
+	if (v3)
+		forat_ret = VOP_GETATTR(vp, &forat, cred, procp);
 	if (vp->v_type != VREG) {
-		error = (vp->v_type == VDIR) ? EISDIR : EACCES;
-		vput(vp);
-		nfsm_reply(0);
+		if (v3)
+			error = EINVAL;
+		else
+			error = (vp->v_type == VDIR) ? EISDIR : EACCES;
 	}
-	nqsrv_getl(vp, NQL_WRITE);
-	error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp);
+	if (!error) {
+		nqsrv_getl(vp, ND_WRITE);
+		error = nfsrv_access(vp, VWRITE, cred, rdonly, procp);
+	}
 	if (error) {
 		vput(vp);
-		nfsm_reply(0);
+		nfsm_reply(NFSX_WCCDATA(v3));
+		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+		return (0);
 	}
-	uiop->uio_resid = 0;
-	uiop->uio_rw = UIO_WRITE;
-	uiop->uio_segflg = UIO_SYSSPACE;
-	uiop->uio_procp = (struct proc *)0;
-	/*
-	 * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
-	 * loop until done.
-	 */
-	while (len > 0 && uiop->uio_resid == 0) {
-		ivp = iv;
-		siz = 0;
-		uiop->uio_iov = ivp;
-		uiop->uio_iovcnt = 0;
-		uiop->uio_offset = off;
-		while (len > 0 && uiop->uio_iovcnt < NFS_MAXIOVEC && mp != NULL) {
+
+	if (len > 0) {
+	    MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP,
+		M_WAITOK);
+	    uiop->uio_iov = iv = ivp;
+	    uiop->uio_iovcnt = cnt;
+	    mp = mrep;
+	    while (mp) {
+		if (mp->m_len > 0) {
 			ivp->iov_base = mtod(mp, caddr_t);
-			if (len < mp->m_len)
-				ivp->iov_len = xfer = len;
-			else
-				ivp->iov_len = xfer = mp->m_len;
-#ifdef notdef
-			/* Not Yet .. */
-			if (M_HASCL(mp) && (((u_long)ivp->iov_base) & CLOFSET) == 0)
-				ivp->iov_op = NULL;	/* what should it be ?? */
-			else
-				ivp->iov_op = NULL;
-#endif
-			uiop->uio_iovcnt++;
+			ivp->iov_len = mp->m_len;
 			ivp++;
-			len -= xfer;
-			siz += xfer;
-			mp = mp->m_next;
 		}
-		if (len > 0 && mp == NULL) {
-			error = EBADRPC;
-			vput(vp);
-			nfsm_reply(0);
-		}
-		uiop->uio_resid = siz;
-		if ((error = VOP_WRITE(vp, uiop, ioflags, cred)) != 0) {
-			vput(vp);
-			nfsm_reply(0);
-		}
-		off = uiop->uio_offset;
+		mp = mp->m_next;
+	    }
+
+	    /*
+	     * XXX
+	     * The IO_METASYNC flag indicates that all metadata (and not just
+	     * enough to ensure data integrity) mus be written to stable storage
+	     * synchronously.
+	     * (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
+	     */
+	    if (stable == NFSV3WRITE_UNSTABLE)
+		ioflags = IO_NODELOCKED;
+	    else if (stable == NFSV3WRITE_DATASYNC)
+		ioflags = (IO_SYNC | IO_NODELOCKED);
+	    else
+		ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
+	    uiop->uio_resid = len;
+	    uiop->uio_rw = UIO_WRITE;
+	    uiop->uio_segflg = UIO_SYSSPACE;
+	    uiop->uio_procp = (struct proc *)0;
+	    uiop->uio_offset = off;
+	    error = VOP_WRITE(vp, uiop, ioflags, cred);
+	    nfsstats.srvvop_writes++;
+	    FREE((caddr_t)iv, M_TEMP);
 	}
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
+	aftat_ret = VOP_GETATTR(vp, &va, cred, procp);
 	vput(vp);
-	nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
-	if (nfsd->nd_nqlflag != NQL_NOVAL) {
-		nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-		txdr_hyper(&va.va_filerev, tl);
+	if (!error)
+		error = aftat_ret;
+	nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) +
+		2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3));
+	if (v3) {
+		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+		if (error)
+			return (0);
+		nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+		*tl++ = txdr_unsigned(retlen);
+		if (stable == NFSV3WRITE_UNSTABLE)
+			*tl++ = txdr_unsigned(stable);
+		else
+			*tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC);
+		/*
+		 * Actually, there is no need to txdr these fields,
+		 * but it may make the values more human readable,
+		 * for debugging purposes.
+		 */
+		*tl++ = txdr_unsigned(boottime.tv_sec);
+		*tl = txdr_unsigned(boottime.tv_usec);
+	} else {
+		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+		nfsm_srvfillattr(&va, fp);
 	}
 	nfsm_srvdone;
 }
 
 /*
+ * NFS write service with write gathering support. Called when
+ * nfsrvw_procrastinate > 0.
+ * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
+ * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
+ * Jan. 1994.
+ */
+int
+nfsrv_writegather(ndp, slp, procp, mrq)
+	struct nfsrv_descript **ndp;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	register struct iovec *ivp;
+	register struct mbuf *mp;
+	register struct nfsrv_descript *wp, *nfsd, *owp, *swp;
+	register struct nfs_fattr *fp;
+	register int i = 0;
+	struct iovec *iov;
+	struct nfsrvw_delayhash *wpp;
+	struct ucred *cred;
+	struct vattr va, forat;
+	register u_int32_t *tl;
+	register int32_t t1;
+	caddr_t bpos, dpos;
+	int error = 0, rdonly, cache, len = 0, forat_ret = 1;
+	int ioflags, aftat_ret = 1, s, adjust, v3, zeroing;
+	char *cp2;
+	struct mbuf *mb, *mb2, *mreq, *mrep, *md;
+	struct vnode *vp;
+	struct uio io, *uiop = &io;
+	u_quad_t frev, cur_usec;
+
+	*mrq = NULL;
+	if (*ndp) {
+	    nfsd = *ndp;
+	    *ndp = NULL;
+	    mrep = nfsd->nd_mrep;
+	    md = nfsd->nd_md;
+	    dpos = nfsd->nd_dpos;
+	    cred = &nfsd->nd_cr;
+	    v3 = (nfsd->nd_flag & ND_NFSV3);
+	    LIST_INIT(&nfsd->nd_coalesce);
+	    nfsd->nd_mreq = NULL;
+	    nfsd->nd_stable = NFSV3WRITE_FILESYNC;
+	    cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec;
+	    nfsd->nd_time = cur_usec + nfsrvw_procrastinate;
+    
+	    /*
+	     * Now, get the write header..
+	     */
+	    nfsm_srvmtofh(&nfsd->nd_fh);
+	    if (v3) {
+		nfsm_dissect(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+		fxdr_hyper(tl, &nfsd->nd_off);
+		tl += 3;
+		nfsd->nd_stable = fxdr_unsigned(int, *tl++);
+	    } else {
+		nfsm_dissect(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+		nfsd->nd_off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
+		tl += 2;
+	    }
+	    len = fxdr_unsigned(int32_t, *tl);
+	    nfsd->nd_len = len;
+	    nfsd->nd_eoff = nfsd->nd_off + len;
+    
+	    /*
+	     * Trim the header out of the mbuf list and trim off any trailing
+	     * junk so that the mbuf list has only the write data.
+	     */
+	    zeroing = 1;
+	    i = 0;
+	    mp = mrep;
+	    while (mp) {
+		if (mp == md) {
+		    zeroing = 0;
+		    adjust = dpos - mtod(mp, caddr_t);
+		    mp->m_len -= adjust;
+		    if (mp->m_len > 0 && adjust > 0)
+			NFSMADV(mp, adjust);
+		}
+		if (zeroing)
+		    mp->m_len = 0;
+		else {
+		    i += mp->m_len;
+		    if (i > len) {
+			mp->m_len -= (i - len);
+			zeroing = 1;
+		    }
+		}
+		mp = mp->m_next;
+	    }
+	    if (len > NFS_MAXDATA || len < 0  || i < len) {
+nfsmout:
+		m_freem(mrep);
+		error = EIO;
+		nfsm_writereply(2 * NFSX_UNSIGNED, v3);
+		if (v3)
+		    nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+		nfsd->nd_mreq = mreq;
+		nfsd->nd_mrep = NULL;
+		nfsd->nd_time = 0;
+	    }
+    
+	    /*
+	     * Add this entry to the hash and time queues.
+	     */
+	    s = splsoftclock();
+	    owp = NULL;
+	    wp = slp->ns_tq.lh_first;
+	    while (wp && wp->nd_time < nfsd->nd_time) {
+		owp = wp;
+		wp = wp->nd_tq.le_next;
+	    }
+	    if (owp) {
+		LIST_INSERT_AFTER(owp, nfsd, nd_tq);
+	    } else {
+		LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
+	    }
+	    if (nfsd->nd_mrep) {
+		wpp = NWDELAYHASH(slp, nfsd->nd_fh.fh_fid.fid_data);
+		owp = NULL;
+		wp = wpp->lh_first;
+		while (wp &&
+		    bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) {
+		    owp = wp;
+		    wp = wp->nd_hash.le_next;
+		}
+		while (wp && wp->nd_off < nfsd->nd_off &&
+		    !bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) {
+		    owp = wp;
+		    wp = wp->nd_hash.le_next;
+		}
+		if (owp) {
+		    LIST_INSERT_AFTER(owp, nfsd, nd_hash);
+
+		    /*
+		     * Search the hash list for overlapping entries and
+		     * coalesce.
+		     */
+		    for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) {
+			wp = nfsd->nd_hash.le_next;
+			if (NFSW_SAMECRED(owp, nfsd))
+			    nfsrvw_coalesce(owp, nfsd);
+		    }
+		} else {
+		    LIST_INSERT_HEAD(wpp, nfsd, nd_hash);
+		}
+	    }
+	    splx(s);
+	}
+    
+	/*
+	 * Now, do VOP_WRITE()s for any one(s) that need to be done now
+	 * and generate the associated reply mbuf list(s).
+	 */
+loop1:
+	cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec;
+	s = splsoftclock();
+	for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) {
+		owp = nfsd->nd_tq.le_next;
+		if (nfsd->nd_time > cur_usec)
+		    break;
+		if (nfsd->nd_mreq)
+		    continue;
+		LIST_REMOVE(nfsd, nd_tq);
+		LIST_REMOVE(nfsd, nd_hash);
+		splx(s);
+		mrep = nfsd->nd_mrep;
+		nfsd->nd_mrep = NULL;
+		cred = &nfsd->nd_cr;
+		v3 = (nfsd->nd_flag & ND_NFSV3);
+		forat_ret = aftat_ret = 1;
+		error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp, 
+		    nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+		if (!error) {
+		    if (v3)
+			forat_ret = VOP_GETATTR(vp, &forat, cred, procp);
+		    if (vp->v_type != VREG) {
+			if (v3)
+			    error = EINVAL;
+			else
+			    error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+		    }
+		} else
+		    vp = NULL;
+		if (!error) {
+		    nqsrv_getl(vp, ND_WRITE);
+		    error = nfsrv_access(vp, VWRITE, cred, rdonly, procp);
+		}
+    
+		if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE)
+		    ioflags = IO_NODELOCKED;
+		else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC)
+		    ioflags = (IO_SYNC | IO_NODELOCKED);
+		else
+		    ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
+		uiop->uio_rw = UIO_WRITE;
+		uiop->uio_segflg = UIO_SYSSPACE;
+		uiop->uio_procp = (struct proc *)0;
+		uiop->uio_offset = nfsd->nd_off;
+		uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off;
+		if (uiop->uio_resid > 0) {
+		    mp = mrep;
+		    i = 0;
+		    while (mp) {
+			if (mp->m_len > 0)
+			    i++;
+			mp = mp->m_next;
+		    }
+		    uiop->uio_iovcnt = i;
+		    MALLOC(iov, struct iovec *, i * sizeof (struct iovec), 
+			M_TEMP, M_WAITOK);
+		    uiop->uio_iov = ivp = iov;
+		    mp = mrep;
+		    while (mp) {
+			if (mp->m_len > 0) {
+			    ivp->iov_base = mtod(mp, caddr_t);
+			    ivp->iov_len = mp->m_len;
+			    ivp++;
+			}
+			mp = mp->m_next;
+		    }
+		    if (!error) {
+			error = VOP_WRITE(vp, uiop, ioflags, cred);
+			nfsstats.srvvop_writes++;
+		    }
+		    FREE((caddr_t)iov, M_TEMP);
+		}
+		m_freem(mrep);
+		if (vp) {
+		    aftat_ret = VOP_GETATTR(vp, &va, cred, procp);
+		    vput(vp);
+		}
+
+		/*
+		 * Loop around generating replies for all write rpcs that have
+		 * now been completed.
+		 */
+		swp = nfsd;
+		do {
+		    if (error) {
+			nfsm_writereply(NFSX_WCCDATA(v3), v3);
+			if (v3) {
+			    nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+			}
+		    } else {
+			nfsm_writereply(NFSX_PREOPATTR(v3) +
+			    NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED +
+			    NFSX_WRITEVERF(v3), v3);
+			if (v3) {
+			    nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
+			    nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+			    *tl++ = txdr_unsigned(nfsd->nd_len);
+			    *tl++ = txdr_unsigned(swp->nd_stable);
+			    /*
+			     * Actually, there is no need to txdr these fields,
+			     * but it may make the values more human readable,
+			     * for debugging purposes.
+			     */
+			    *tl++ = txdr_unsigned(boottime.tv_sec);
+			    *tl = txdr_unsigned(boottime.tv_usec);
+			} else {
+			    nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+			    nfsm_srvfillattr(&va, fp);
+			}
+		    }
+		    nfsd->nd_mreq = mreq;
+		    if (nfsd->nd_mrep)
+			panic("nfsrv_write: nd_mrep not free");
+
+		    /*
+		     * Done. Put it at the head of the timer queue so that
+		     * the final phase can return the reply.
+		     */
+		    s = splsoftclock();
+		    if (nfsd != swp) {
+			nfsd->nd_time = 0;
+			LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
+		    }
+		    nfsd = swp->nd_coalesce.lh_first;
+		    if (nfsd) {
+			LIST_REMOVE(nfsd, nd_tq);
+		    }
+		    splx(s);
+		} while (nfsd);
+		s = splsoftclock();
+		swp->nd_time = 0;
+		LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq);
+		splx(s);
+		goto loop1;
+	}
+	splx(s);
+
+	/*
+	 * Search for a reply to return.
+	 */
+	s = splsoftclock();
+	for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next)
+		if (nfsd->nd_mreq) {
+		    LIST_REMOVE(nfsd, nd_tq);
+		    *mrq = nfsd->nd_mreq;
+		    *ndp = nfsd;
+		    break;
+		}
+	splx(s);
+	return (0);
+}
+
+/*
+ * Coalesce the write request nfsd into owp. To do this we must:
+ * - remove nfsd from the queues
+ * - merge nfsd->nd_mrep into owp->nd_mrep
+ * - update the nd_eoff and nd_stable for owp
+ * - put nfsd on owp's nd_coalesce list
+ * NB: Must be called at splsoftclock().
+ */
+void
+nfsrvw_coalesce(owp, nfsd)
+        register struct nfsrv_descript *owp;
+        register struct nfsrv_descript *nfsd;
+{
+        register int overlap;
+        register struct mbuf *mp;
+
+        LIST_REMOVE(nfsd, nd_hash);
+        LIST_REMOVE(nfsd, nd_tq);
+        if (owp->nd_eoff < nfsd->nd_eoff) {
+            overlap = owp->nd_eoff - nfsd->nd_off;
+            if (overlap < 0)
+                panic("nfsrv_coalesce: bad off");
+            if (overlap > 0)
+                m_adj(nfsd->nd_mrep, overlap);
+            mp = owp->nd_mrep;
+            while (mp->m_next)
+                mp = mp->m_next;
+            mp->m_next = nfsd->nd_mrep;
+            owp->nd_eoff = nfsd->nd_eoff;
+        } else
+            m_freem(nfsd->nd_mrep);
+        nfsd->nd_mrep = NULL;
+        if (nfsd->nd_stable == NFSV3WRITE_FILESYNC)
+            owp->nd_stable = NFSV3WRITE_FILESYNC;
+        else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC &&
+            owp->nd_stable == NFSV3WRITE_UNSTABLE)
+            owp->nd_stable = NFSV3WRITE_DATASYNC;
+        LIST_INSERT_HEAD(&owp->nd_coalesce, nfsd, nd_tq);
+}
+
+/*
  * nfs create service
  * now does a truncate to 0 length via. setattr if it already exists
  */
 int
-nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_create(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	register struct nfsv2_fattr *fp;
-	struct vattr va;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register struct nfs_fattr *fp;
+	struct vattr va, dirfor, diraft;
 	register struct nfsv2_sattr *sp;
 	register u_int32_t *tl;
 	struct nameidata nd;
 	register caddr_t cp;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdev, cache, len, tsize;
+	int error = 0, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1;
+	int rdev = 0;
+	int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0;
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
-	struct vnode *vp = NULL;
-	nfsv2fh_t nfh;
+	struct vnode *vp = NULL, *dirp = NULL;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
-	u_quad_t frev;
+	u_quad_t frev, tempsize;
+	u_char cverf[NFSX_V3CREATEVERF];
 
 	nd.ni_cnd.cn_nameiop = 0;
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = CREATE;
 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
-	error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
-	if (error)
-		nfsm_reply(0);
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
+		else {
+			vrele(dirp);
+			dirp = (struct vnode *)0;
+		}
+	}
+	if (error) {
+		nfsm_reply(NFSX_WCCDATA(v3));
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		if (dirp)
+			vrele(dirp);
+		return (0);
+	}
 	VATTR_NULL(&va);
-	nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+	if (v3) {
+		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+		how = fxdr_unsigned(int, *tl);
+		switch (how) {
+		case NFSV3CREATE_GUARDED:
+			if (nd.ni_vp) {
+				error = EEXIST;
+				break;
+			}
+		case NFSV3CREATE_UNCHECKED:
+			nfsm_srvsattr(&va);
+			break;
+		case NFSV3CREATE_EXCLUSIVE:
+			nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF);
+			bcopy(cp, cverf, NFSX_V3CREATEVERF);
+			exclusive_flag = 1;
+			if (nd.ni_vp == NULL)
+				va.va_mode = 0;
+			break;
+		};
+		va.va_type = VREG;
+	} else {
+		nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+		va.va_type = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
+		if (va.va_type == VNON)
+			va.va_type = VREG;
+		va.va_mode = nfstov_mode(sp->sa_mode);
+		switch (va.va_type) {
+		case VREG:
+			tsize = fxdr_unsigned(int32_t, sp->sa_size);
+			if (tsize != -1)
+				va.va_size = (u_quad_t)tsize;
+			break;
+		case VCHR:
+		case VBLK:
+		case VFIFO:
+			rdev = fxdr_unsigned(int32_t, sp->sa_size);
+			break;
+		default:
+			break;
+		};
+	}
+
 	/*
 	 * Iff doesn't exist, create it
 	 * otherwise just truncate to 0 length
 	 *   should I set the mode too ??
 	 */
 	if (nd.ni_vp == NULL) {
-		va.va_type = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
-		if (va.va_type == VNON)
-			va.va_type = VREG;
-		va.va_mode = nfstov_mode(sp->sa_mode);
-		if (nfsd->nd_nqlflag == NQL_NOVAL)
-			rdev = fxdr_unsigned(int32_t, sp->sa_nfssize);
-		else
-			rdev = fxdr_unsigned(int32_t, sp->sa_nqrdev);
 		if (va.va_type == VREG || va.va_type == VSOCK) {
 			vrele(nd.ni_startdir);
-			nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-			error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd,
-					   &va);
-			if (error)
-				nfsm_reply(0);
-			FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+			nqsrv_getl(nd.ni_dvp, ND_WRITE);
+			error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va);
+			if (!error) {
+				FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+				if (exclusive_flag) {
+					exclusive_flag = 0;
+					VATTR_NULL(&va);
+					bcopy(cverf, (caddr_t)&va.va_atime,
+						NFSX_V3CREATEVERF);
+					error = VOP_SETATTR(nd.ni_vp, &va, cred,
+						procp);
+				}
+			}
 		} else if (va.va_type == VCHR || va.va_type == VBLK ||
 			va.va_type == VFIFO) {
 			if (va.va_type == VCHR && rdev == 0xffffffff)
 				va.va_type = VFIFO;
-			if (va.va_type == VFIFO) {
-#ifndef FIFO
+			error = suser(cred, (u_short *)0);
+			if (error) {
+				vrele(nd.ni_startdir);
+				free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
 				VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 				vput(nd.ni_dvp);
-				error = ENXIO;
-				goto out;
-#endif /* FIFO */
-			} else if ((error = suser(cred, (u_short *)0)) != 0) {
-				VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
-				vput(nd.ni_dvp);
-				goto out;
+				nfsm_reply(0);
+				return (error);
 			} else
 				va.va_rdev = (dev_t)rdev;
-			nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+			nqsrv_getl(nd.ni_dvp, ND_WRITE);
 			error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd,
-					  &va);
+			    &va);
 			if (error) {
 				vrele(nd.ni_startdir);
 				nfsm_reply(0);
 			}
 			nd.ni_cnd.cn_nameiop = LOOKUP;
 			nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
-			nd.ni_cnd.cn_proc = nfsd->nd_procp;
-			nd.ni_cnd.cn_cred = nfsd->nd_procp->p_ucred;
+			nd.ni_cnd.cn_proc = procp;
+			nd.ni_cnd.cn_cred = cred;
 			if ((error = lookup(&nd)) != 0) {
 				free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
 				nfsm_reply(0);
@@ -785,10 +1372,11 @@
 				nfsm_reply(0);
 			}
 		} else {
+			vrele(nd.ni_startdir);
+			free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
 			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 			vput(nd.ni_dvp);
 			error = ENXIO;
-			goto out;
 		}
 		vp = nd.ni_vp;
 	} else {
@@ -800,46 +1388,56 @@
 		else
 			vput(nd.ni_dvp);
 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
-		if (nfsd->nd_nqlflag == NQL_NOVAL) {
-			tsize = fxdr_unsigned(int32_t, sp->sa_nfssize);
-			if (tsize != -1)
-				va.va_size = (u_quad_t)tsize;
-			else
-				va.va_size = -1;
-		} else
-			fxdr_hyper(&sp->sa_nqsize, &va.va_size);
 		if (va.va_size != -1) {
 			error = nfsrv_access(vp, VWRITE, cred,
-					     (nd.ni_cnd.cn_flags & RDONLY),
-					     nfsd->nd_procp);
-			if (error) {
-				vput(vp);
-				nfsm_reply(0);
+			    (nd.ni_cnd.cn_flags & RDONLY), procp);
+			if (!error) {
+				nqsrv_getl(vp, ND_WRITE);
+				tempsize = va.va_size;
+				VATTR_NULL(&va);
+				va.va_size = tempsize;
+				error = VOP_SETATTR(vp, &va, cred,
+					 procp);
 			}
-			nqsrv_getl(vp, NQL_WRITE);
-			error = VOP_SETATTR(vp, &va, cred, nfsd->nd_procp);
-			if (error) {
+			if (error)
 				vput(vp);
-				nfsm_reply(0);
-			}
 		}
 	}
-	bzero((caddr_t)fhp, sizeof(nfh));
-	fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
-	if ((error = VFS_VPTOFH(vp, &fhp->fh_fid)) != 0) {
+	if (!error) {
+		bzero((caddr_t)fhp, sizeof(nfh));
+		fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+		error = VFS_VPTOFH(vp, &fhp->fh_fid);
+		if (!error)
+			error = VOP_GETATTR(vp, &va, cred, procp);
 		vput(vp);
-		nfsm_reply(0);
+	}
+	if (v3) {
+		if (exclusive_flag && !error &&
+			bcmp(cverf, (caddr_t)&va.va_atime, NFSX_V3CREATEVERF))
+			error = EEXIST;
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
 	}
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
-	vput(vp);
-	nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfhtom(fhp);
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
-	return (error);
+	nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3));
+	if (v3) {
+		if (!error) {
+			nfsm_srvpostop_fh(fhp);
+			nfsm_srvpostop_attr(0, &va);
+		}
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+	} else {
+		nfsm_srvfhtom(fhp, v3);
+		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+		nfsm_srvfillattr(&va, fp);
+	}
+	return (0);
 nfsmout:
-	if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags)
+	if (dirp)
+		vrele(dirp);
+	if (nd.ni_cnd.cn_nameiop) {
 		vrele(nd.ni_startdir);
+		free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI);
+	}
 	VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 	if (nd.ni_dvp == nd.ni_vp)
 		vrele(nd.ni_dvp);
@@ -848,68 +1446,241 @@
 	if (nd.ni_vp)
 		vput(nd.ni_vp);
 	return (error);
+}
 
+/*
+ * nfs v3 mknod service
+ */
+int
+nfsrv_mknod(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	struct vattr va, dirfor, diraft;
+	register u_int32_t *tl;
+	struct nameidata nd;
+	register int32_t t1;
+	caddr_t bpos;
+	int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1;
+	u_int32_t major, minor;
+	enum vtype vtyp;
+	char *cp2;
+	struct mbuf *mb, *mb2, *mreq;
+	struct vnode *vp, *dirp = (struct vnode *)0;
+	nfsfh_t nfh;
+	fhandle_t *fhp;
+	u_quad_t frev;
+
+	nd.ni_cnd.cn_nameiop = 0;
+	fhp = &nfh.fh_generic;
+	nfsm_srvmtofh(fhp);
+	nfsm_srvnamesiz(len);
+	nd.ni_cnd.cn_cred = cred;
+	nd.ni_cnd.cn_nameiop = CREATE;
+	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp)
+		dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp);
+	if (error) {
+		nfsm_reply(NFSX_WCCDATA(1));
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		if (dirp)
+			vrele(dirp);
+		return (0);
+	}
+	nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+	vtyp = nfsv3tov_type(*tl);
+	if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) {
+		vrele(nd.ni_startdir);
+		free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI);
+		error = NFSERR_BADTYPE;
+		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+		vput(nd.ni_dvp);
+		goto out;
+	}
+	VATTR_NULL(&va);
+	nfsm_srvsattr(&va);
+	if (vtyp == VCHR || vtyp == VBLK) {
+		nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+		major = fxdr_unsigned(u_int32_t, *tl++);
+		minor = fxdr_unsigned(u_int32_t, *tl);
+		va.va_rdev = makedev(major, minor);
+	}
+
+	/*
+	 * Iff doesn't exist, create it.
+	 */
+	if (nd.ni_vp) {
+		vrele(nd.ni_startdir);
+		free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI);
+		error = EEXIST;
+		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+		vput(nd.ni_dvp);
+		goto out;
+	}
+	va.va_type = vtyp;
+	if (vtyp == VSOCK) {
+		vrele(nd.ni_startdir);
+		nqsrv_getl(nd.ni_dvp, ND_WRITE);
+		error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va);
+		if (!error)
+			FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+	} else {
+		error = suser(cred, (u_short *)0);
+		if (error) {
+			vrele(nd.ni_startdir);
+			free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI);
+			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+			vput(nd.ni_dvp);
+			goto out;
+		}
+		nqsrv_getl(nd.ni_dvp, ND_WRITE);
+		error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va);
+		if (error) {
+			vrele(nd.ni_startdir);
+			goto out;
+		}
+		nd.ni_cnd.cn_nameiop = LOOKUP;
+		nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
+		nd.ni_cnd.cn_proc = procp;
+		nd.ni_cnd.cn_cred = procp->p_ucred;
+		error = lookup(&nd);
+		FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+		if (error)
+			goto out;
+		if (nd.ni_cnd.cn_flags & ISSYMLINK) {
+			vrele(nd.ni_dvp);
+			vput(nd.ni_vp);
+			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+			error = EINVAL;
+		}
+	}
 out:
-	vrele(nd.ni_startdir);
-	free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
-	nfsm_reply(0);
-	return 0;
+	vp = nd.ni_vp;
+	if (!error) {
+		bzero((caddr_t)fhp, sizeof(nfh));
+		fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+		error = VFS_VPTOFH(vp, &fhp->fh_fid);
+		if (!error)
+			error = VOP_GETATTR(vp, &va, cred, procp);
+		vput(vp);
+	}
+	diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+	vrele(dirp);
+	nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1));
+	if (!error) {
+		nfsm_srvpostop_fh(fhp);
+		nfsm_srvpostop_attr(0, &va);
+	}
+	nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+	return (0);
+nfsmout:
+	if (dirp)
+		vrele(dirp);
+	if (nd.ni_cnd.cn_nameiop) {
+		vrele(nd.ni_startdir);
+		free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI);
+	}
+	VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+	if (nd.ni_dvp == nd.ni_vp)
+		vrele(nd.ni_dvp);
+	else
+		vput(nd.ni_dvp);
+	if (nd.ni_vp)
+		vput(nd.ni_vp);
+	return (error);
 }
 
 /*
  * nfs remove service
  */
 int
-nfsrv_remove(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_remove(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	struct nameidata nd;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, cache, len;
+	int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mreq;
-	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vnode *vp, *dirp;
+	struct vattr dirfor, diraft;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	u_quad_t frev;
 
+#ifndef nolint
+	vp = (struct vnode *)0;
+#endif
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = DELETE;
 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
-	error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
-	if (error)
-		nfsm_reply(0);
-	vp = nd.ni_vp;
-	/*
-	 * The root of a mounted filesystem cannot be deleted.
-	 */
-	if (vp->v_flag & VROOT) {
-		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
-		if (nd.ni_dvp == vp)
-			vrele(nd.ni_dvp);
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
 		else
-			vput(nd.ni_dvp);
-		vput(vp);
-		error = EBUSY;
-		goto out;
+			vrele(dirp);
 	}
-	if (vp->v_flag & VTEXT)
-		(void) vnode_pager_uncache(vp);
-	nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-	nqsrv_getl(vp, NQL_WRITE);
-	error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+	if (!error) {
+		vp = nd.ni_vp;
+		if (vp->v_type == VDIR &&
+		    (error = suser(cred, (u_short *)0)) != 0)
+			goto out;
+		/*
+		 * The root of a mounted filesystem cannot be deleted.
+		 */
+		if (vp->v_flag & VROOT) {
+			error = EBUSY;
+			goto out;
+		}
+		if (vp->v_flag & VTEXT)
+			(void) vnode_pager_uncache(vp);
 out:
-	nfsm_reply(0);
+		if (!error) {
+			nqsrv_getl(nd.ni_dvp, ND_WRITE);
+			nqsrv_getl(vp, ND_WRITE);
+			error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+		} else {
+			VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+			if (nd.ni_dvp == vp)
+				vrele(nd.ni_dvp);
+			else
+				vput(nd.ni_dvp);
+			vput(vp);
+		}
+	}
+	if (dirp && v3) {
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
+	}
+	nfsm_reply(NFSX_WCCDATA(v3));
+	if (v3) {
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		return (0);
+	}
 	nfsm_srvdone;
 }
 
@@ -917,32 +1688,42 @@
  * nfs rename service
  */
 int
-nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_rename(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, cache, len, len2;
+	int error = 0, cache, len, len2, fdirfor_ret = 1, fdiraft_ret = 1;
+	int tdirfor_ret = 1, tdiraft_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mreq;
 	struct nameidata fromnd, tond;
-	struct vnode *fvp = NULL, *tvp, *tdvp;
-	nfsv2fh_t fnfh, tnfh;
+	struct vnode *fvp, *tvp, *tdvp, *fdirp = (struct vnode *)0;
+	struct vnode *tdirp = (struct vnode *)0;
+	struct vattr fdirfor, fdiraft, tdirfor, tdiraft;
+	nfsfh_t fnfh, tnfh;
 	fhandle_t *ffhp, *tfhp;
 	u_quad_t frev;
 	uid_t saved_uid;
 
+#ifndef nolint
+	fvp = (struct vnode *)0;
+#endif
 	ffhp = &fnfh.fh_generic;
 	tfhp = &tnfh.fh_generic;
 	fromnd.ni_cnd.cn_nameiop = 0;
 	tond.ni_cnd.cn_nameiop = 0;
 	nfsm_srvmtofh(ffhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	/*
 	 * Remember our original uid so that we can reset cr_uid before
 	 * the second nfs_namei() call, in case it is remapped.
@@ -951,10 +1732,25 @@
 	fromnd.ni_cnd.cn_cred = cred;
 	fromnd.ni_cnd.cn_nameiop = DELETE;
 	fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
-	error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md,
-			  &dpos, nfsd->nd_procp);
-	if (error)
-		nfsm_reply(0);
+	error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md,
+		&dpos, &fdirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (fdirp) {
+		if (v3)
+			fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred,
+				procp);
+		else {
+			vrele(fdirp);
+			fdirp = (struct vnode *)0;
+		}
+	}
+	if (error) {
+		nfsm_reply(2 * NFSX_WCCDATA(v3));
+		nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
+		nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
+		if (fdirp)
+			vrele(fdirp);
+		return (0);
+	}
 	fvp = fromnd.ni_vp;
 	nfsm_srvmtofh(tfhp);
 	nfsm_strsiz(len2, NFS_MAXNAMLEN);
@@ -962,8 +1758,17 @@
 	tond.ni_cnd.cn_cred = cred;
 	tond.ni_cnd.cn_nameiop = RENAME;
 	tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
-	error = nfs_namei(&tond, tfhp, len2, nfsd->nd_slp, nam, &md,
-			  &dpos, nfsd->nd_procp);
+	error = nfs_namei(&tond, tfhp, len2, slp, nam, &md,
+		&dpos, &tdirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (tdirp) {
+		if (v3)
+			tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred,
+				procp);
+		else {
+			vrele(tdirp);
+			tdirp = (struct vnode *)0;
+		}
+	}
 	if (error) {
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
 		vrele(fromnd.ni_dvp);
@@ -974,27 +1779,45 @@
 	tvp = tond.ni_vp;
 	if (tvp != NULL) {
 		if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
-			error = EISDIR;
+			if (v3)
+				error = EEXIST;
+			else
+				error = EISDIR;
 			goto out;
 		} else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
-			error = ENOTDIR;
+			if (v3)
+				error = EEXIST;
+			else
+				error = ENOTDIR;
 			goto out;
 		}
 		if (tvp->v_type == VDIR && tvp->v_mountedhere) {
-			error = EXDEV;
+			if (v3)
+				error = EXDEV;
+			else
+				error = ENOTEMPTY;
 			goto out;
 		}
 	}
 	if (fvp->v_type == VDIR && fvp->v_mountedhere) {
-		error = EBUSY;
+		if (v3)
+			error = EXDEV;
+		else
+			error = ENOTEMPTY;
 		goto out;
 	}
 	if (fvp->v_mount != tdvp->v_mount) {
-		error = EXDEV;
+		if (v3)
+			error = EXDEV;
+		else
+			error = ENOTEMPTY;
 		goto out;
 	}
 	if (fvp == tdvp)
-		error = EINVAL;
+		if (v3)
+			error = EINVAL;
+		else
+			error = ENOTEMPTY;
 	/*
 	 * If source is the same as the destination (that is the
 	 * same vnode with the same name in the same directory),
@@ -1007,10 +1830,10 @@
 		error = -1;
 out:
 	if (!error) {
-		nqsrv_getl(fromnd.ni_dvp, NQL_WRITE);
-		nqsrv_getl(tdvp, NQL_WRITE);
+		nqsrv_getl(fromnd.ni_dvp, ND_WRITE);
+		nqsrv_getl(tdvp, ND_WRITE);
 		if (tvp)
-			nqsrv_getl(tvp, NQL_WRITE);
+			nqsrv_getl(tvp, ND_WRITE);
 		error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
 				   tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
 	} else {
@@ -1024,21 +1847,39 @@
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
 		vrele(fromnd.ni_dvp);
 		vrele(fvp);
+		if (error == -1)
+			error = 0;
 	}
 	vrele(tond.ni_startdir);
 	FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
 out1:
+	if (fdirp) {
+		fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, procp);
+		vrele(fdirp);
+	}
+	if (tdirp) {
+		tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, procp);
+		vrele(tdirp);
+	}
 	vrele(fromnd.ni_startdir);
 	FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
-	nfsm_reply(0);
-	return (error);
+	nfsm_reply(2 * NFSX_WCCDATA(v3));
+	if (v3) {
+		nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
+		nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
+	}
+	return (0);
 
 nfsmout:
-	if (tond.ni_cnd.cn_nameiop || tond.ni_cnd.cn_flags) {
+	if (fdirp)
+		vrele(fdirp);
+	if (tdirp)
+		vrele(tdirp);
+	if (tond.ni_cnd.cn_nameiop) {
 		vrele(tond.ni_startdir);
 		FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
 	}
-	if (fromnd.ni_cnd.cn_nameiop || fromnd.ni_cnd.cn_flags) {
+	if (fromnd.ni_cnd.cn_nameiop) {
 		vrele(fromnd.ni_startdir);
 		FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
@@ -1052,22 +1893,27 @@
  * nfs link service
  */
 int
-nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_link(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	struct nameidata nd;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache, len;
+	int error = 0, rdonly, cache, len, dirfor_ret = 1, diraft_ret = 1;
+	int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mreq;
-	struct vnode *vp;
-	nfsv2fh_t nfh, dnfh;
+	struct vnode *vp, *xp, *dirp = (struct vnode *)0;
+	struct vattr dirfor, diraft, at;
+	nfsfh_t nfh, dnfh;
 	fhandle_t *fhp, *dfhp;
 	u_quad_t frev;
 
@@ -1075,33 +1921,69 @@
 	dfhp = &dnfh.fh_generic;
 	nfsm_srvmtofh(fhp);
 	nfsm_srvmtofh(dfhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
-	error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
+	nfsm_srvnamesiz(len);
+	error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
+		nfsm_srvpostop_attr(getret, &at);
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		return (0);
+	}
+	if (vp->v_type == VDIR && (error = suser(cred, (u_short *)0)) != 0)
+		goto out1;
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = CREATE;
 	nd.ni_cnd.cn_flags = LOCKPARENT;
-	error = nfs_namei(&nd, dfhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
+	error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
+		else {
+			vrele(dirp);
+			dirp = (struct vnode *)0;
+		}
+	}
 	if (error)
+		goto out1;
+	xp = nd.ni_vp;
+	if (xp != NULL) {
+		error = EEXIST;
 		goto out;
-	if (nd.ni_vp) {
+	}
+	xp = nd.ni_dvp;
+	if (vp->v_mount != xp->v_mount)
+		error = EXDEV;
+out:
+	if (!error) {
+		nqsrv_getl(vp, ND_WRITE);
+		nqsrv_getl(xp, ND_WRITE);
+		error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+	} else {
 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 		if (nd.ni_dvp == nd.ni_vp)
 			vrele(nd.ni_dvp);
 		else
 			vput(nd.ni_dvp);
-		vrele(nd.ni_vp);
-		error = EEXIST;
-		goto out;
+		if (nd.ni_vp)
+			vrele(nd.ni_vp);
 	}
-	nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-	nqsrv_getl(vp, NQL_WRITE);
-	error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
-out:
+out1:
+	if (v3)
+		getret = VOP_GETATTR(vp, &at, cred, procp);
+	if (dirp) {
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
+	}
 	vrele(vp);
-	nfsm_reply(0);
+	nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
+	if (v3) {
+		nfsm_srvpostop_attr(getret, &at);
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		return (0);
+	}
 	nfsm_srvdone;
 }
 
@@ -1109,39 +1991,55 @@
  * nfs symbolic link service
  */
 int
-nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_symlink(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	struct vattr va;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	struct vattr va, dirfor, diraft;
 	struct nameidata nd;
 	register u_int32_t *tl;
 	register int32_t t1;
 	struct nfsv2_sattr *sp;
-	caddr_t bpos;
+	char *bpos, *pathcp = NULL, *cp2;
 	struct uio io;
 	struct iovec iv;
-	int error = 0, cache, len, len2;
-	char *pathcp, *cp2;
-	struct mbuf *mb, *mreq;
-	nfsv2fh_t nfh;
+	int error = 0, cache, len, len2, dirfor_ret = 1, diraft_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
+	struct mbuf *mb, *mreq, *mb2;
+	struct vnode *dirp = (struct vnode *)0;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	u_quad_t frev;
 
-	pathcp = (char *)0;
+	nd.ni_cnd.cn_nameiop = 0;
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = CREATE;
-	nd.ni_cnd.cn_flags = LOCKPARENT;
-	error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
+	nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART;
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
+		else {
+			vrele(dirp);
+			dirp = (struct vnode *)0;
+		}
+	}
 	if (error)
 		goto out;
+	VATTR_NULL(&va);
+	if (v3)
+		nfsm_srvsattr(&va);
 	nfsm_strsiz(len2, NFS_MAXPATHLEN);
 	MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
 	iv.iov_base = pathcp;
@@ -1154,9 +2052,14 @@
 	io.uio_rw = UIO_READ;
 	io.uio_procp = (struct proc *)0;
 	nfsm_mtouio(&io, len2);
-	nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+	if (!v3) {
+		nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+		va.va_mode = fxdr_unsigned(u_int16_t, sp->sa_mode);
+	}
 	*(pathcp + len2) = '\0';
 	if (nd.ni_vp) {
+		vrele(nd.ni_startdir);
+		free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 		if (nd.ni_dvp == nd.ni_vp)
 			vrele(nd.ni_dvp);
@@ -1166,16 +2069,54 @@
 		error = EEXIST;
 		goto out;
 	}
-	VATTR_NULL(&va);
-	va.va_mode = fxdr_unsigned(u_int16_t, sp->sa_mode);
-	nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+	nqsrv_getl(nd.ni_dvp, ND_WRITE);
 	error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va, pathcp);
+	if (error)
+		vrele(nd.ni_startdir);
+	else {
+	    if (v3) {
+		nd.ni_cnd.cn_nameiop = LOOKUP;
+		nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART | FOLLOW);
+		nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
+		nd.ni_cnd.cn_proc = procp;
+		nd.ni_cnd.cn_cred = cred;
+		error = lookup(&nd);
+		if (!error) {
+			bzero((caddr_t)fhp, sizeof(nfh));
+			fhp->fh_fsid = nd.ni_vp->v_mount->mnt_stat.f_fsid;
+			error = VFS_VPTOFH(nd.ni_vp, &fhp->fh_fid);
+			if (!error)
+				error = VOP_GETATTR(nd.ni_vp, &va, cred,
+					procp);
+			vput(nd.ni_vp);
+		}
+	    } else
+		vrele(nd.ni_startdir);
+	    FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+	}
 out:
 	if (pathcp)
 		FREE(pathcp, M_TEMP);
-	nfsm_reply(0);
-	return (error);
+	if (dirp) {
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
+	}
+	nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
+	if (v3) {
+		if (!error) {
+			nfsm_srvpostop_fh(fhp);
+			nfsm_srvpostop_attr(0, &va);
+		}
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+	}
+	return (0);
 nfsmout:
+	if (nd.ni_cnd.cn_nameiop) {
+		vrele(nd.ni_startdir);
+		free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+	}
+	if (dirp)
+		vrele(dirp);
 	VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 	if (nd.ni_dvp == nd.ni_vp)
 		vrele(nd.ni_dvp);
@@ -1192,42 +2133,64 @@
  * nfs mkdir service
  */
 int
-nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_mkdir(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
-	struct vattr va;
-	register struct nfsv2_fattr *fp;
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	struct vattr va, dirfor, diraft;
+	register struct nfs_fattr *fp;
 	struct nameidata nd;
 	register caddr_t cp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, cache, len;
+	int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
-	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vnode *vp, *dirp = (struct vnode *)0;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	u_quad_t frev;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = CREATE;
 	nd.ni_cnd.cn_flags = LOCKPARENT;
-	error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
-	if (error)
-		nfsm_reply(0);
-	nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
+		else {
+			vrele(dirp);
+			dirp = (struct vnode *)0;
+		}
+	}
+	if (error) {
+		nfsm_reply(NFSX_WCCDATA(v3));
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		if (dirp)
+			vrele(dirp);
+		return (0);
+	}
 	VATTR_NULL(&va);
+	if (v3) {
+		nfsm_srvsattr(&va);
+	} else {
+		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+		va.va_mode = nfstov_mode(*tl++);
+	}
 	va.va_type = VDIR;
-	va.va_mode = nfstov_mode(*tl++);
 	vp = nd.ni_vp;
 	if (vp != NULL) {
 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1237,26 +2200,40 @@
 			vput(nd.ni_dvp);
 		vrele(vp);
 		error = EEXIST;
-		nfsm_reply(0);
+		goto out;
 	}
-	nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-	if ((error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va)) != 0)
-		nfsm_reply(0);
-	vp = nd.ni_vp;
-	bzero((caddr_t)fhp, sizeof(nfh));
-	fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
-	if ((error = VFS_VPTOFH(vp, &fhp->fh_fid)) != 0) {
+	nqsrv_getl(nd.ni_dvp, ND_WRITE);
+	error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &va);
+	if (!error) {
+		vp = nd.ni_vp;
+		bzero((caddr_t)fhp, sizeof(nfh));
+		fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+		error = VFS_VPTOFH(vp, &fhp->fh_fid);
+		if (!error)
+			error = VOP_GETATTR(vp, &va, cred, procp);
 		vput(vp);
-		nfsm_reply(0);
+	}
+out:
+	if (dirp) {
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
 	}
-	error = VOP_GETATTR(vp, &va, cred, nfsd->nd_procp);
-	vput(vp);
-	nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfhtom(fhp);
-	nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-	nfsm_srvfillattr;
-	return (error);
+	nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
+	if (v3) {
+		if (!error) {
+			nfsm_srvpostop_fh(fhp);
+			nfsm_srvpostop_attr(0, &va);
+		}
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+	} else {
+		nfsm_srvfhtom(fhp, v3);
+		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+		nfsm_srvfillattr(&va, fp);
+	}
+	return (0);
 nfsmout:
+	if (dirp)
+		vrele(dirp);
 	VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
 	if (nd.ni_dvp == nd.ni_vp)
 		vrele(nd.ni_dvp);
@@ -1271,35 +2248,54 @@
  * nfs rmdir service
  */
 int
-nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_rmdir(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, cache, len;
+	int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
 	char *cp2;
 	struct mbuf *mb, *mreq;
-	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vnode *vp, *dirp = (struct vnode *)0;
+	struct vattr dirfor, diraft;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct nameidata nd;
 	u_quad_t frev;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+	nfsm_srvnamesiz(len);
 	nd.ni_cnd.cn_cred = cred;
 	nd.ni_cnd.cn_nameiop = DELETE;
 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
-	error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-			  nfsd->nd_procp);
-	if (error)
-		nfsm_reply(0);
+	error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+		&dirp, procp, (nfsd->nd_flag & ND_KERBAUTH));
+	if (dirp) {
+		if (v3)
+			dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
+				procp);
+		else {
+			vrele(dirp);
+			dirp = (struct vnode *)0;
+		}
+	}
+	if (error) {
+		nfsm_reply(NFSX_WCCDATA(v3));
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		if (dirp)
+			vrele(dirp);
+		return (0);
+	}
 	vp = nd.ni_vp;
 	if (vp->v_type != VDIR) {
 		error = ENOTDIR;
@@ -1319,8 +2315,8 @@
 		error = EBUSY;
 out:
 	if (!error) {
-		nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-		nqsrv_getl(vp, NQL_WRITE);
+		nqsrv_getl(nd.ni_dvp, ND_WRITE);
+		nqsrv_getl(vp, ND_WRITE);
 		error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
 	} else {
 		VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1330,7 +2326,15 @@
 			vput(nd.ni_dvp);
 		vput(vp);
 	}
-	nfsm_reply(0);
+	if (dirp) {
+		diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+		vrele(dirp);
+	}
+	nfsm_reply(NFSX_WCCDATA(v3));
+	if (v3) {
+		nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+		return (0);
+	}
 	nfsm_srvdone;
 }
 
@@ -1354,7 +2358,7 @@
  *	than requested, but this may not apply to all filesystems. For
  *	example, client NFS does not { although it is never remote mounted
  *	anyhow }
- *     The alternate call nqnfsrv_readdirlook() does lookups as well.
+ *     The alternate call nfsrv_readdirplus() does lookups as well.
  * PS: The NFS protocol spec. does not clarify what the "count" byte
  *	argument is a count of.. just name strings and file id's or the
  *	entire reply rpc or ...
@@ -1364,24 +2368,28 @@
  *	"entry" structures, but are in the rpc.
  */
 struct flrep {
-	u_int32_t fl_cachable;
-	u_int32_t fl_duration;
-	u_int32_t fl_frev[2];
-	nfsv2fh_t fl_nfh;
-	u_int32_t fl_fattr[NFSX_NQFATTR / sizeof (u_int32_t)];
+	nfsuint64 fl_off;
+	u_int32_t fl_postopok;
+	u_int32_t fl_fattr[NFSX_V3FATTR / sizeof (u_int32_t)];
+	u_int32_t fl_fhok;
+	u_int32_t fl_fhsize;
+	u_int32_t fl_nfh[NFSX_V3FH / sizeof (u_int32_t)];
 };
 
 int
-nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_readdir(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register char *bp, *be;
 	register struct mbuf *mp;
-	register struct dirent *dp = NULL;
+	register struct dirent *dp;
 	register caddr_t cp;
 	register u_int32_t *tl;
 	register int32_t t1;
@@ -1389,39 +2397,71 @@
 	struct mbuf *mb, *mb2, *mreq, *mp2;
 	char *cpos, *cend, *cp2, *rbuf;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vattr at;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
 	struct uio io;
 	struct iovec iv;
-	int len, nlen, rem, xfer, tsiz, i, error = 0;
-	int siz, cnt, fullsiz, eofflag, rdonly, cache;
-	u_quad_t frev;
-	u_long off, *cookiebuf, *cookie;
-	int ncookies;
+	int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
+	int siz, cnt, fullsiz, eofflag, rdonly, cache, ncookies;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
+	u_quad_t frev, off, toff, verf;
+	u_long *cookies = NULL, *cookiep;
 
 	fhp = &nfh.fh_generic;
 	nfsm_srvmtofh(fhp);
-	nfsm_dissect(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-	off = fxdr_unsigned(u_int32_t, *tl++);
+	if (v3) {
+		nfsm_dissect(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+		fxdr_hyper(tl, &toff);
+		tl += 2;
+		fxdr_hyper(tl, &verf);
+		tl += 2;
+	} else {
+		nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+		toff = fxdr_unsigned(u_quad_t, *tl++);
+	}
+	off = toff;
 	cnt = fxdr_unsigned(int, *tl);
-	siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
-	if (cnt > NFS_MAXREADDIR)
-		siz = NFS_MAXREADDIR;
+	siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
+	xfer = NFS_SRVMAXDATA(nfsd);
+	if (siz > xfer)
+		siz = xfer;
 	fullsiz = siz;
-	ncookies = siz / 16;	/* Guess at the number of cookies needed. */
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	nqsrv_getl(vp, NQL_READ);
-	error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+	nqsrv_getl(vp, ND_READ);
+	if (v3) {
+		error = getret = VOP_GETATTR(vp, &at, cred, procp);
+#ifdef NFS3_STRICTVERF
+		/*
+		 * XXX This check is too strict for Solaris 2.5 clients.
+		 */
+		if (!error && toff && verf != at.va_filerev)
+			error = NFSERR_BAD_COOKIE;
+#endif
+	}
+	if (!error)
+		error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
 	if (error) {
 		vput(vp);
-		nfsm_reply(0);
+		nfsm_reply(NFSX_POSTOPATTR(v3));
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
 	}
+#ifdef Lite2_integrated
+	VOP_UNLOCK(vp, 0, procp);
+#else
 	VOP_UNLOCK(vp);
+#endif
 	MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
-	MALLOC(cookiebuf, u_long *, ncookies * sizeof(*cookiebuf), M_TEMP,
-	    M_WAITOK);
+	ncookies = siz / (5 * NFSX_UNSIGNED); /*7 for V3, but it's an est. so*/
+	MALLOC(cookies, u_long *, ncookies * sizeof (u_long *), M_TEMP,
+		M_WAITOK);
 again:
 	iv.iov_base = rbuf;
 	iv.iov_len = fullsiz;
@@ -1432,14 +2472,37 @@
 	io.uio_segflg = UIO_SYSSPACE;
 	io.uio_rw = UIO_READ;
 	io.uio_procp = (struct proc *)0;
-	error = VOP_READDIR(vp, &io, cred, &eofflag, cookiebuf, ncookies);
-	cookie = cookiebuf;
+	eofflag = 0;
+#ifdef Lite2_integrated
+	VOP_LOCK(vp, 0, procp);
+#else
+	VOP_LOCK(vp);
+#endif
+
+	error = VOP_READDIR(vp, &io, cred, &eofflag, cookies, ncookies);
+
 	off = (off_t)io.uio_offset;
+	if (!cookies && !error)
+		error = NFSERR_PERM;
+	if (v3) {
+		getret = VOP_GETATTR(vp, &at, cred, procp);
+		if (!error)
+			error = getret;
+	}
+
+#ifdef Lite2_integrated
+	VOP_UNLOCK(vp, 0, procp);
+#else
+	VOP_UNLOCK(vp);
+#endif
 	if (error) {
 		vrele(vp);
-		free((caddr_t)cookiebuf, M_TEMP);
 		free((caddr_t)rbuf, M_TEMP);
-		nfsm_reply(0);
+		if (cookies)
+			free((caddr_t)cookies, M_TEMP);
+		nfsm_reply(NFSX_POSTOPATTR(v3));
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
 	}
 	if (io.uio_resid) {
 		siz -= io.uio_resid;
@@ -1450,12 +2513,19 @@
 		 */
 		if (siz == 0) {
 			vrele(vp);
-			nfsm_reply(2*NFSX_UNSIGNED);
-			nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
+			nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) +
+				2 * NFSX_UNSIGNED);
+			if (v3) {
+				nfsm_srvpostop_attr(getret, &at);
+				nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+				txdr_hyper(&at.va_filerev, tl);
+				tl += 2;
+			} else
+				nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
 			*tl++ = nfs_false;
 			*tl = nfs_true;
-			FREE((caddr_t)cookiebuf, M_TEMP);
 			FREE((caddr_t)rbuf, M_TEMP);
+			FREE((caddr_t)cookies, M_TEMP);
 			return (0);
 		}
 	}
@@ -1466,31 +2536,40 @@
 	 */
 	cpos = rbuf;
 	cend = rbuf + siz;
-	while (cpos < cend) {
+	dp = (struct dirent *)cpos;
+	cookiep = cookies;
+
+	while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) {
+		cpos += dp->d_reclen;
 		dp = (struct dirent *)cpos;
-		if (dp->d_fileno == 0) {
-			cpos += dp->d_reclen;
-			cookie++;
-		} else
-			break;
+		cookiep++;
+		ncookies--;
 	}
-	if (cpos >= cend) {
+	if (cpos >= cend || ncookies == 0) {
+		toff = off;
 		siz = fullsiz;
 		goto again;
 	}
 
-	len = 3*NFSX_UNSIGNED;	/* paranoia, probably can be 0 */
-	nfsm_reply(siz);
+	len = 3 * NFSX_UNSIGNED;	/* paranoia, probably can be 0 */
+	nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz);
+	if (v3) {
+		nfsm_srvpostop_attr(getret, &at);
+		nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+		txdr_hyper(&at.va_filerev, tl);
+	}
 	mp = mp2 = mb;
 	bp = bpos;
 	be = bp + M_TRAILINGSPACE(mp);
 
 	/* Loop through the records and build reply */
-	while (cpos < cend) {
+	while (cpos < cend && ncookies > 0) {
 		if (dp->d_fileno != 0) {
 			nlen = dp->d_namlen;
 			rem = nfsm_rndup(nlen)-nlen;
-			len += (4*NFSX_UNSIGNED + nlen + rem);
+			len += (4 * NFSX_UNSIGNED + nlen + rem);
+			if (v3)
+				len += 2 * NFSX_UNSIGNED;
 			if (len > cnt) {
 				eofflag = 0;
 				break;
@@ -1502,243 +2581,10 @@
 			nfsm_clget;
 			*tl = nfs_true;
 			bp += NFSX_UNSIGNED;
-			nfsm_clget;
-			*tl = txdr_unsigned(dp->d_fileno);
-			bp += NFSX_UNSIGNED;
-			nfsm_clget;
-			*tl = txdr_unsigned(nlen);
-			bp += NFSX_UNSIGNED;
-	
-			/* And loop around copying the name */
-			xfer = nlen;
-			cp = dp->d_name;
-			while (xfer > 0) {
+			if (v3) {
 				nfsm_clget;
-				if ((bp+xfer) > be)
-					tsiz = be-bp;
-				else
-					tsiz = xfer;
-				bcopy(cp, bp, tsiz);
-				bp += tsiz;
-				xfer -= tsiz;
-				if (xfer > 0)
-					cp += tsiz;
-			}
-			/* And null pad to a int32_t boundary */
-			for (i = 0; i < rem; i++)
-				*bp++ = '\0';
-			nfsm_clget;
-	
-			/* Finish off the record */
-			*tl = txdr_unsigned(*cookie);
-			bp += NFSX_UNSIGNED;
-		}
-		cpos += dp->d_reclen;
-		dp = (struct dirent *)cpos;
-		cookie++;
-	}
-	vrele(vp);
-	nfsm_clget;
-	*tl = nfs_false;
-	bp += NFSX_UNSIGNED;
-	nfsm_clget;
-	if (eofflag)
-		*tl = nfs_true;
-	else
-		*tl = nfs_false;
-	bp += NFSX_UNSIGNED;
-	if (mp != mb) {
-		if (bp < be)
-			mp->m_len = bp - mtod(mp, caddr_t);
-	} else
-		mp->m_len += bp - bpos;
-	FREE(cookiebuf, M_TEMP);
-	FREE(rbuf, M_TEMP);
-	nfsm_srvdone;
-}
-
-int
-nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
-{
-	register char *bp, *be;
-	register struct mbuf *mp;
-	register struct dirent *dp = NULL;
-	register caddr_t cp;
-	register u_int32_t *tl;
-	register int32_t t1;
-	caddr_t bpos;
-	struct mbuf *mb, *mb2, *mreq, *mp2;
-	char *cpos, *cend, *cp2, *rbuf;
-	struct vnode *vp, *nvp;
-	struct flrep fl;
-	nfsv2fh_t nfh;
-	fhandle_t *fhp;
-	struct uio io;
-	struct iovec iv;
-	struct vattr va;
-	struct nfsv2_fattr *fp;
-	int len, nlen, rem, xfer, tsiz, i, error = 0, duration2, cache2;
-	int siz, cnt, fullsiz, eofflag, rdonly, cache;
-	u_quad_t frev, frev2;
-	u_long off, *cookiebuf, *cookie;
-	int ncookies;
-
-	fhp = &nfh.fh_generic;
-	nfsm_srvmtofh(fhp);
-	nfsm_dissect(tl, u_int32_t *, 3*NFSX_UNSIGNED);
-	off = fxdr_unsigned(u_int32_t, *tl++);
-	cnt = fxdr_unsigned(int, *tl++);
-	duration2 = fxdr_unsigned(int, *tl);
-	siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
-	if (cnt > NFS_MAXREADDIR)
-		siz = NFS_MAXREADDIR;
-	fullsiz = siz;
-	ncookies = siz / 16;	/* Guess at the number of cookies needed. */
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	nqsrv_getl(vp, NQL_READ);
-	error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp);
-	if (error) {
-		vput(vp);
-		nfsm_reply(0);
-	}
-	VOP_UNLOCK(vp);
-	MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
-	MALLOC(cookiebuf, u_long *, ncookies * sizeof(*cookiebuf), M_TEMP,
-	    M_WAITOK);
-again:
-	iv.iov_base = rbuf;
-	iv.iov_len = fullsiz;
-	io.uio_iov = &iv;
-	io.uio_iovcnt = 1;
-	io.uio_offset = (off_t)off;
-	io.uio_resid = fullsiz;
-	io.uio_segflg = UIO_SYSSPACE;
-	io.uio_rw = UIO_READ;
-	io.uio_procp = (struct proc *)0;
-	error = VOP_READDIR(vp, &io, cred, &eofflag, cookiebuf, ncookies);
-	cookie = cookiebuf;
-	off = (u_long)io.uio_offset;
-	if (error) {
-		vrele(vp);
-		free((caddr_t)cookiebuf, M_TEMP);
-		free((caddr_t)rbuf, M_TEMP);
-		nfsm_reply(0);
-	}
-	if (io.uio_resid) {
-		siz -= io.uio_resid;
-
-		/*
-		 * If nothing read, return eof
-		 * rpc reply
-		 */
-		if (siz == 0) {
-			vrele(vp);
-			nfsm_reply(2 * NFSX_UNSIGNED);
-			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-			*tl++ = nfs_false;
-			*tl = nfs_true;
-			FREE((caddr_t)cookiebuf, M_TEMP);
-			FREE((caddr_t)rbuf, M_TEMP);
-			return (0);
-		}
-	}
-
-	/*
-	 * Check for degenerate cases of nothing useful read.
-	 * If so go try again
-	 */
-	cpos = rbuf;
-	cend = rbuf + siz;
-	while (cpos < cend) {
-		dp = (struct dirent *)cpos;
-		if (dp->d_fileno == 0) {
-			cpos += dp->d_reclen;
-			cookie++;
-		} else
-			break;
-	}
-	if (cpos >= cend) {
-		siz = fullsiz;
-		goto again;
-	}
-
-	len = 3 * NFSX_UNSIGNED;	/* paranoia, probably can be 0 */
-	nfsm_reply(siz);
-	mp = mp2 = mb;
-	bp = bpos;
-	be = bp + M_TRAILINGSPACE(mp);
-
-	/* Loop through the records and build reply */
-	while (cpos < cend) {
-		if (dp->d_fileno != 0) {
-			nlen = dp->d_namlen;
-			rem = nfsm_rndup(nlen)-nlen;
-	
-			/*
-			 * For readdir_and_lookup get the vnode using
-			 * the file number.
-			 */
-			if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
-				goto invalid;
-			bzero((caddr_t)&fl.fl_nfh, sizeof (nfsv2fh_t));
-			fl.fl_nfh.fh_generic.fh_fsid =
-				nvp->v_mount->mnt_stat.f_fsid;
-			if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) {
-				vput(nvp);
-				goto invalid;
-			}
-			if (duration2) {
-				(void) nqsrv_getlease(nvp, &duration2, NQL_READ,
-					nfsd, nam, &cache2, &frev2, cred);
-				fl.fl_duration = txdr_unsigned(duration2);
-				fl.fl_cachable = txdr_unsigned(cache2);
-				txdr_hyper(&frev2, fl.fl_frev);
-			} else
-				fl.fl_duration = 0;
-			if (VOP_GETATTR(nvp, &va, cred, nfsd->nd_procp)) {
-				vput(nvp);
-				goto invalid;
-			}
-			vput(nvp);
-			fp = (struct nfsv2_fattr *)&fl.fl_fattr;
-			nfsm_srvfillattr;
-			len += (4*NFSX_UNSIGNED + nlen + rem + NFSX_FH
-				+ NFSX_NQFATTR);
-			if (len > cnt) {
-				eofflag = 0;
-				break;
-			}
-			/*
-			 * Build the directory record xdr from
-			 * the dirent entry.
-			 */
-			nfsm_clget;
-			*tl = nfs_true;
-			bp += NFSX_UNSIGNED;
-
-			/*
-			 * For readdir_and_lookup copy the stuff out.
-			 */
-			xfer = sizeof (struct flrep);
-			cp = (caddr_t)&fl;
-			while (xfer > 0) {
-				nfsm_clget;
-				if ((bp+xfer) > be)
-					tsiz = be-bp;
-				else
-					tsiz = xfer;
-				bcopy(cp, bp, tsiz);
-				bp += tsiz;
-				xfer -= tsiz;
-				if (xfer > 0)
-					cp += tsiz;
+				*tl = 0;
+				bp += NFSX_UNSIGNED;
 			}
 			nfsm_clget;
 			*tl = txdr_unsigned(dp->d_fileno);
@@ -1762,19 +2608,24 @@
 				if (xfer > 0)
 					cp += tsiz;
 			}
-			/* And null pad to a int32_t boundary */
+			/* And null pad to an int32_t boundary */
 			for (i = 0; i < rem; i++)
 				*bp++ = '\0';
 			nfsm_clget;
 	
 			/* Finish off the record */
-			*tl = txdr_unsigned(*cookie);
+			if (v3) {
+				*tl = 0;
+				bp += NFSX_UNSIGNED;
+				nfsm_clget;
+			}
+			*tl = txdr_unsigned(*cookiep);
 			bp += NFSX_UNSIGNED;
 		}
-invalid:
 		cpos += dp->d_reclen;
 		dp = (struct dirent *)cpos;
-		cookie++;
+		cookiep++;
+		ncookies--;
 	}
 	vrele(vp);
 	nfsm_clget;
@@ -1791,8 +2642,373 @@
 			mp->m_len = bp - mtod(mp, caddr_t);
 	} else
 		mp->m_len += bp - bpos;
-	FREE(cookiebuf, M_TEMP);
-	FREE(rbuf, M_TEMP);
+	FREE((caddr_t)rbuf, M_TEMP);
+	FREE((caddr_t)cookies, M_TEMP);
+	nfsm_srvdone;
+}
+
+int
+nfsrv_readdirplus(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register char *bp, *be;
+	register struct mbuf *mp;
+	register struct dirent *dp;
+	register caddr_t cp;
+	register u_int32_t *tl;
+	register int32_t t1;
+	caddr_t bpos;
+	struct mbuf *mb, *mb2, *mreq, *mp2;
+	char *cpos, *cend, *cp2, *rbuf;
+	struct vnode *vp, *nvp;
+	struct flrep fl;
+	nfsfh_t nfh;
+	fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh;
+	struct uio io;
+	struct iovec iv;
+	struct vattr va, at, *vap = &va;
+	struct nfs_fattr *fp;
+	int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
+	int siz, cnt, fullsiz, eofflag, rdonly, cache, dirlen, ncookies;
+	u_quad_t frev, off, toff, verf;
+	u_long *cookies = NULL, *cookiep;
+
+	fhp = &nfh.fh_generic;
+	nfsm_srvmtofh(fhp);
+	nfsm_dissect(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
+	fxdr_hyper(tl, &toff);
+	tl += 2;
+	fxdr_hyper(tl, &verf);
+	tl += 2;
+	siz = fxdr_unsigned(int, *tl++);
+	cnt = fxdr_unsigned(int, *tl);
+	off = toff;
+	siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
+	xfer = NFS_SRVMAXDATA(nfsd);
+	if (siz > xfer)
+		siz = xfer;
+	fullsiz = siz;
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+	error = getret = VOP_GETATTR(vp, &at, cred, procp);
+#ifdef NFS3_STRICTVERF
+	/*
+	 * XXX This check is too strict for Solaris 2.5 clients.
+	 */
+	if (!error && toff && verf != at.va_filerev)
+		error = NFSERR_BAD_COOKIE;
+#endif
+	if (!error) {
+		nqsrv_getl(vp, ND_READ);
+		error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
+	}
+	if (error) {
+		vput(vp);
+		nfsm_reply(NFSX_V3POSTOPATTR);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+#ifdef Lite2_integrated
+	VOP_UNLOCK(vp, 0, procp);
+#else
+	VOP_UNLOCK(vp);
+#endif
+
+	MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+	ncookies = siz / (7 * NFSX_UNSIGNED);
+	MALLOC(cookies, u_long *, ncookies * sizeof (u_long *), M_TEMP,
+		M_WAITOK);
+again:
+	iv.iov_base = rbuf;
+	iv.iov_len = fullsiz;
+	io.uio_iov = &iv;
+	io.uio_iovcnt = 1;
+	io.uio_offset = (off_t)off;
+	io.uio_resid = fullsiz;
+	io.uio_segflg = UIO_SYSSPACE;
+	io.uio_rw = UIO_READ;
+	io.uio_procp = (struct proc *)0;
+	eofflag = 0;
+
+#ifdef Lite2_integrated
+	VOP_LOCK(vp, 0, procp);
+#else
+	VOP_LOCK(vp);
+#endif
+	error = VOP_READDIR(vp, &io, cred, &eofflag, cookies, ncookies);
+
+	off = (u_quad_t)io.uio_offset;
+	getret = VOP_GETATTR(vp, &at, cred, procp);
+
+#ifdef Lite2_integrated
+	VOP_UNLOCK(vp, 0, procp);
+#else
+	VOP_UNLOCK(vp);
+#endif
+	if (!cookies && !error)
+		error = NFSERR_PERM;
+	if (!error)
+		error = getret;
+	if (error) {
+		vrele(vp);
+		if (cookies)
+			free((caddr_t)cookies, M_TEMP);
+		free((caddr_t)rbuf, M_TEMP);
+		nfsm_reply(NFSX_V3POSTOPATTR);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+	if (io.uio_resid) {
+		siz -= io.uio_resid;
+
+		/*
+		 * If nothing read, return eof
+		 * rpc reply
+		 */
+		if (siz == 0) {
+			vrele(vp);
+			nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF +
+				2 * NFSX_UNSIGNED);
+			nfsm_srvpostop_attr(getret, &at);
+			nfsm_build(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+			txdr_hyper(&at.va_filerev, tl);
+			tl += 2;
+			*tl++ = nfs_false;
+			*tl = nfs_true;
+			FREE((caddr_t)cookies, M_TEMP);
+			FREE((caddr_t)rbuf, M_TEMP);
+			return (0);
+		}
+	}
+
+	/*
+	 * Check for degenerate cases of nothing useful read.
+	 * If so go try again
+	 */
+	cpos = rbuf;
+	cend = rbuf + siz;
+	dp = (struct dirent *)cpos;
+	cookiep = cookies;
+
+	while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) {
+		cpos += dp->d_reclen;
+		dp = (struct dirent *)cpos;
+		cookiep++;
+		ncookies--;
+	}
+	if (cpos >= cend || ncookies == 0) {
+		toff = off;
+		siz = fullsiz;
+		goto again;
+	}
+
+	dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
+	nfsm_reply(cnt);
+	nfsm_srvpostop_attr(getret, &at);
+	nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+	txdr_hyper(&at.va_filerev, tl);
+	mp = mp2 = mb;
+	bp = bpos;
+	be = bp + M_TRAILINGSPACE(mp);
+
+	/* Loop through the records and build reply */
+	while (cpos < cend && ncookies > 0) {
+		if (dp->d_fileno != 0) {
+			nlen = dp->d_namlen;
+			rem = nfsm_rndup(nlen)-nlen;
+	
+			/*
+			 * For readdir_and_lookup get the vnode using
+			 * the file number.
+			 */
+			if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
+				goto invalid;
+			bzero((caddr_t)nfhp, NFSX_V3FH);
+			nfhp->fh_fsid =
+				nvp->v_mount->mnt_stat.f_fsid;
+			if (VFS_VPTOFH(nvp, &nfhp->fh_fid)) {
+				vput(nvp);
+				goto invalid;
+			}
+			if (VOP_GETATTR(nvp, vap, cred, procp)) {
+				vput(nvp);
+				goto invalid;
+			}
+			vput(nvp);
+
+			/*
+			 * If either the dircount or maxcount will be
+			 * exceeded, get out now. Both of these lengths
+			 * are calculated conservatively, including all
+			 * XDR overheads.
+			 */
+			len += (7 * NFSX_UNSIGNED + nlen + rem + NFSX_V3FH +
+				NFSX_V3POSTOPATTR);
+			dirlen += (6 * NFSX_UNSIGNED + nlen + rem);
+			if (len > cnt || dirlen > fullsiz) {
+				eofflag = 0;
+				break;
+			}
+
+			/*
+			 * Build the directory record xdr from
+			 * the dirent entry.
+			 */
+			fp = (struct nfs_fattr *)&fl.fl_fattr;
+			nfsm_srvfillattr(vap, fp);
+			fl.fl_fhsize = txdr_unsigned(NFSX_V3FH);
+			fl.fl_fhok = nfs_true;
+			fl.fl_postopok = nfs_true;
+			fl.fl_off.nfsuquad[0] = 0;
+			fl.fl_off.nfsuquad[1] = txdr_unsigned(*cookiep);
+
+			nfsm_clget;
+			*tl = nfs_true;
+			bp += NFSX_UNSIGNED;
+			nfsm_clget;
+			*tl = 0;
+			bp += NFSX_UNSIGNED;
+			nfsm_clget;
+			*tl = txdr_unsigned(dp->d_fileno);
+			bp += NFSX_UNSIGNED;
+			nfsm_clget;
+			*tl = txdr_unsigned(nlen);
+			bp += NFSX_UNSIGNED;
+	
+			/* And loop around copying the name */
+			xfer = nlen;
+			cp = dp->d_name;
+			while (xfer > 0) {
+				nfsm_clget;
+				if ((bp + xfer) > be)
+					tsiz = be - bp;
+				else
+					tsiz = xfer;
+				bcopy(cp, bp, tsiz);
+				bp += tsiz;
+				xfer -= tsiz;
+				if (xfer > 0)
+					cp += tsiz;
+			}
+			/* And null pad to an int32_t boundary */
+			for (i = 0; i < rem; i++)
+				*bp++ = '\0';
+	
+			/*
+			 * Now copy the flrep structure out.
+			 */
+			xfer = sizeof (struct flrep);
+			cp = (caddr_t)&fl;
+			while (xfer > 0) {
+				nfsm_clget;
+				if ((bp + xfer) > be)
+					tsiz = be - bp;
+				else
+					tsiz = xfer;
+				bcopy(cp, bp, tsiz);
+				bp += tsiz;
+				xfer -= tsiz;
+				if (xfer > 0)
+					cp += tsiz;
+			}
+		}
+invalid:
+		cpos += dp->d_reclen;
+		dp = (struct dirent *)cpos;
+		cookiep++;
+		ncookies--;
+	}
+	vrele(vp);
+	nfsm_clget;
+	*tl = nfs_false;
+	bp += NFSX_UNSIGNED;
+	nfsm_clget;
+	if (eofflag)
+		*tl = nfs_true;
+	else
+		*tl = nfs_false;
+	bp += NFSX_UNSIGNED;
+	if (mp != mb) {
+		if (bp < be)
+			mp->m_len = bp - mtod(mp, caddr_t);
+	} else
+		mp->m_len += bp - bpos;
+	FREE((caddr_t)cookies, M_TEMP);
+	FREE((caddr_t)rbuf, M_TEMP);
+	nfsm_srvdone;
+}
+
+/*
+ * nfs commit service
+ */
+int
+nfsrv_commit(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	struct vattr bfor, aft;
+	struct vnode *vp;
+	nfsfh_t nfh;
+	fhandle_t *fhp;
+	register u_int32_t *tl;
+	register int32_t t1;
+	caddr_t bpos;
+	int error = 0, rdonly, for_ret = 1, aft_ret = 1, cnt, cache;
+	char *cp2;
+	struct mbuf *mb, *mb2, *mreq;
+	u_quad_t frev, off;
+
+#ifndef nolint
+	cache = 0;
+#endif
+	fhp = &nfh.fh_generic;
+	nfsm_srvmtofh(fhp);
+	nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+
+	/*
+	 * XXX At this time VOP_FSYNC() does not accept offset and byte
+	 * count parameters, so these arguments are useless (someday maybe).
+	 */
+	fxdr_hyper(tl, &off);
+	tl += 2;
+	cnt = fxdr_unsigned(int, *tl);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(2 * NFSX_UNSIGNED);
+		nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
+		return (0);
+	}
+	for_ret = VOP_GETATTR(vp, &bfor, cred, procp);
+	error = VOP_FSYNC(vp, cred, MNT_WAIT, procp);
+	aft_ret = VOP_GETATTR(vp, &aft, cred, procp);
+	vput(vp);
+	nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
+	nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
+	if (!error) {
+		nfsm_build(tl, u_int32_t *, NFSX_V3WRITEVERF);
+		*tl++ = txdr_unsigned(boottime.tv_sec);
+		*tl = txdr_unsigned(boottime.tv_usec);
+	} else
+		return (0);
 	nfsm_srvdone;
 }
 
@@ -1800,47 +3016,219 @@
  * nfs statfs service
  */
 int
-nfsrv_statfs(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_statfs(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
 	register struct statfs *sf;
-	register struct nfsv2_statfs *sfp;
+	register struct nfs_statfs *sfp;
 	register u_int32_t *tl;
 	register int32_t t1;
 	caddr_t bpos;
-	int error = 0, rdonly, cache = 0, isnq;
+	int error = 0, rdonly, cache, getret = 1;
+	int v3 = (nfsd->nd_flag & ND_NFSV3);
+	char *cp2;
+	struct mbuf *mb, *mb2, *mreq;
+	struct vnode *vp;
+	struct vattr at;
+	nfsfh_t nfh;
+	fhandle_t *fhp;
+	struct statfs statfs;
+	u_quad_t frev, tval;
+
+#ifndef nolint
+	cache = 0;
+#endif
+	fhp = &nfh.fh_generic;
+	nfsm_srvmtofh(fhp);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+	sf = &statfs;
+	error = VFS_STATFS(vp->v_mount, sf, procp);
+	getret = VOP_GETATTR(vp, &at, cred, procp);
+	vput(vp);
+	nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3));
+	if (v3)
+		nfsm_srvpostop_attr(getret, &at);
+	if (error)
+		return (0);
+	nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
+	if (v3) {
+		tval = (u_quad_t)sf->f_blocks;
+		tval *= (u_quad_t)sf->f_bsize;
+		txdr_hyper(&tval, &sfp->sf_tbytes);
+		tval = (u_quad_t)sf->f_bfree;
+		tval *= (u_quad_t)sf->f_bsize;
+		txdr_hyper(&tval, &sfp->sf_fbytes);
+		tval = (u_quad_t)sf->f_bavail;
+		tval *= (u_quad_t)sf->f_bsize;
+		txdr_hyper(&tval, &sfp->sf_abytes);
+		sfp->sf_tfiles.nfsuquad[0] = 0;
+		sfp->sf_tfiles.nfsuquad[1] = txdr_unsigned(sf->f_files);
+		sfp->sf_ffiles.nfsuquad[0] = 0;
+		sfp->sf_ffiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree);
+		sfp->sf_afiles.nfsuquad[0] = 0;
+		sfp->sf_afiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree);
+		sfp->sf_invarsec = 0;
+	} else {
+		sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
+		sfp->sf_bsize = txdr_unsigned(sf->f_bsize);
+		sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
+		sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
+		sfp->sf_bavail = txdr_unsigned(sf->f_bavail);
+	}
+	nfsm_srvdone;
+}
+
+/*
+ * nfs fsinfo service
+ */
+int
+nfsrv_fsinfo(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register u_int32_t *tl;
+	register struct nfsv3_fsinfo *sip;
+	register int32_t t1;
+	caddr_t bpos;
+	int error = 0, rdonly, cache, getret = 1, pref;
 	char *cp2;
 	struct mbuf *mb, *mb2, *mreq;
 	struct vnode *vp;
-	nfsv2fh_t nfh;
+	struct vattr at;
+	nfsfh_t nfh;
 	fhandle_t *fhp;
-	struct statfs statfs;
 	u_quad_t frev;
 
+#ifndef nolint
+	cache = 0;
+#endif
 	fhp = &nfh.fh_generic;
-	isnq = (nfsd->nd_nqlflag != NQL_NOVAL);
 	nfsm_srvmtofh(fhp);
-	error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly);
-	if (error)
-		nfsm_reply(0);
-	sf = &statfs;
-	error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
+	}
+	getret = VOP_GETATTR(vp, &at, cred, procp);
 	vput(vp);
-	nfsm_reply(NFSX_STATFS(isnq));
-	nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
-	sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
-	sfp->sf_bsize = txdr_unsigned(sf->f_bsize);
-	sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
-	sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
-	sfp->sf_bavail = txdr_unsigned(sf->f_bavail);
-	if (isnq) {
-		sfp->sf_files = txdr_unsigned(sf->f_files);
-		sfp->sf_ffree = txdr_unsigned(sf->f_ffree);
+	nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
+	nfsm_srvpostop_attr(getret, &at);
+	nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
+
+	/*
+	 * XXX
+	 * There should be file system VFS OP(s) to get this information.
+	 * For now, assume ufs.
+	 */
+	if (slp->ns_so->so_type == SOCK_DGRAM)
+		pref = NFS_MAXDGRAMDATA;
+	else
+		pref = NFS_MAXDATA;
+	sip->fs_rtmax = txdr_unsigned(NFS_MAXDATA);
+	sip->fs_rtpref = txdr_unsigned(pref);
+	sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE);
+	sip->fs_wtmax = txdr_unsigned(NFS_MAXDATA);
+	sip->fs_wtpref = txdr_unsigned(pref);
+	sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE);
+	sip->fs_dtpref = txdr_unsigned(pref);
+	sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff;
+	sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff;
+	sip->fs_timedelta.nfsv3_sec = 0;
+	sip->fs_timedelta.nfsv3_nsec = txdr_unsigned(1);
+	sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK |
+		NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS |
+		NFSV3FSINFO_CANSETTIME);
+	nfsm_srvdone;
+}
+
+/*
+ * nfs pathconf service
+ */
+int
+nfsrv_pathconf(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
+{
+	struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md;
+	struct mbuf *nam = nfsd->nd_nam;
+	caddr_t dpos = nfsd->nd_dpos;
+	struct ucred *cred = &nfsd->nd_cr;
+	register u_int32_t *tl;
+	register struct nfsv3_pathconf *pc;
+	register int32_t t1;
+	caddr_t bpos;
+	int error = 0, rdonly, cache, getret = 1, linkmax, namemax;
+	int chownres, notrunc;
+	char *cp2;
+	struct mbuf *mb, *mb2, *mreq;
+	struct vnode *vp;
+	struct vattr at;
+	nfsfh_t nfh;
+	fhandle_t *fhp;
+	u_quad_t frev;
+
+#ifndef nolint
+	cache = 0;
+#endif
+	fhp = &nfh.fh_generic;
+	nfsm_srvmtofh(fhp);
+	error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH));
+	if (error) {
+		nfsm_reply(NFSX_UNSIGNED);
+		nfsm_srvpostop_attr(getret, &at);
+		return (0);
 	}
+	error = VOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax);
+	if (!error)
+		error = VOP_PATHCONF(vp, _PC_NAME_MAX, &namemax);
+	if (!error)
+		error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres);
+	if (!error)
+		error = VOP_PATHCONF(vp, _PC_NO_TRUNC, &notrunc);
+	getret = VOP_GETATTR(vp, &at, cred, procp);
+	vput(vp);
+	nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
+	nfsm_srvpostop_attr(getret, &at);
+	if (error)
+		return (0);
+	nfsm_build(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
+
+	pc->pc_linkmax = txdr_unsigned(linkmax);
+	pc->pc_namemax = txdr_unsigned(namemax);
+	pc->pc_notrunc = txdr_unsigned(notrunc);
+	pc->pc_chownrestricted = txdr_unsigned(chownres);
+
+	/*
+	 * These should probably be supported by VOP_PATHCONF(), but
+	 * until msdosfs is exportable (why would you want to?), the
+	 * Unix defaults should be ok.
+	 */
+	pc->pc_caseinsensitive = nfs_false;
+	pc->pc_casepreserving = nfs_true;
 	nfsm_srvdone;
 }
 
@@ -1849,20 +3237,20 @@
  */
 /* ARGSUSED */
 int
-nfsrv_null(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_null(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep;
 	caddr_t bpos;
-	int error = VNOVAL, cache = 0;
+	int error = NFSERR_RETVOID, cache = 0;
 	struct mbuf *mb, *mreq;
 	u_quad_t frev;
 
 	nfsm_reply(0);
-	return (error);
+	return (0);
 }
 
 /*
@@ -1870,13 +3258,13 @@
  */
 /* ARGSUSED */
 int
-nfsrv_noop(nfsd, mrep, md, dpos, cred, nam, mrq)
-	struct nfsd *nfsd;
-	struct mbuf *mrep, *md;
-	caddr_t dpos;
-	struct ucred *cred;
-	struct mbuf *nam, **mrq;
+nfsrv_noop(nfsd, slp, procp, mrq)
+	struct nfsrv_descript *nfsd;
+	struct nfssvc_sock *slp;
+	struct proc *procp;
+	struct mbuf **mrq;
 {
+	struct mbuf *mrep = nfsd->nd_mrep;
 	caddr_t bpos;
 	int error, cache = 0;
 	struct mbuf *mb, *mreq;
@@ -1887,7 +3275,7 @@
 	else
 		error = EPROCUNAVAIL;
 	nfsm_reply(0);
-	return (error);
+	return (0);
 }
 
 /*
@@ -1919,14 +3307,11 @@
 		 */
 		if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
 			switch (vp->v_type) {
-			case VREG: case VDIR: case VLNK:
+			case VREG:
+			case VDIR:
+			case VLNK:
 				return (EROFS);
-			case VNON:
-			case VBLK:
-			case VCHR:
-			case VSOCK:
-			case VFIFO:
-			case VBAD:
+			default:
 				break;
 			}
 		}
@@ -1938,9 +3323,10 @@
 		if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
 			return (ETXTBSY);
 	}
-	if ((error = VOP_GETATTR(vp, &vattr, cred, p)) != 0)
+	error = VOP_GETATTR(vp, &vattr, cred, p);
+	if (error)
 		return (error);
-	if ((error = VOP_ACCESS(vp, flags, cred, p)) &&
+	if ((error = VOP_ACCESS(vp, flags, cred, p)) != 0 &&
 	    cred->cr_uid != vattr.va_uid)
 		return (error);
 	return (0);
--- a/sys/nfs/nfs_socket.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_socket.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: nfs_socket.c,v 1.23 1996/02/09 21:48:29 christos Exp $	*/
+/*	$NetBSD: nfs_socket.c,v 1.24 1996/02/18 11:53:48 fvdl Exp $	*/
 
 /*
- * Copyright (c) 1989, 1991, 1993
+ * Copyright (c) 1989, 1991, 1993, 1995
  *	The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -35,7 +35,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_socket.c	8.3 (Berkeley) 1/12/94
+ *	@(#)nfs_socket.c	8.5 (Berkeley) 3/30/95
  */
 
 /*
@@ -59,8 +59,9 @@
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
@@ -96,36 +97,13 @@
  * External data, mostly RPC constants in XDR form
  */
 extern u_int32_t rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers,
-	rpc_auth_unix, rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+	rpc_auth_unix, rpc_msgaccepted, rpc_call, rpc_autherr,
 	rpc_auth_kerb;
-extern u_int32_t nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern u_int32_t nfs_prog, nqnfs_prog;
 extern time_t nqnfsstarttime;
-extern int nonidempotent[NFS_NPROCS];
-
-/*
- * Maps errno values to nfs error numbers.
- * Use NFSERR_IO as the catch all for ones not specifically defined in
- * RFC 1094.
- */
-static int nfsrv_errmap[ELAST] = {
-  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
-  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_NAMETOL,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
-  NFSERR_IO,
-};
+extern struct nfsstats nfsstats;
+extern int nfsv3_procid[NFS_NPROCS];
+extern int nfs_ticks;
 
 /*
  * Defines which timer to use for the procnum.
@@ -136,7 +114,8 @@
  * 4 - write
  */
 static int proct[NFS_NPROCS] = {
-	0, 1, 0, 0, 2, 3, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0,
+	0, 1, 0, 2, 1, 3, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0,
+	0, 0, 0,
 };
 
 /*
@@ -176,8 +155,8 @@
 
 	nmp->nm_so = (struct socket *)0;
 	saddr = mtod(nmp->nm_nam, struct sockaddr *);
-	error = socreate(saddr->sa_family,
-			 &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto);
+	error = socreate(saddr->sa_family, &nmp->nm_so, nmp->nm_sotype, 
+		nmp->nm_soproto);
 	if (error)
 		goto bad;
 	so = nmp->nm_so;
@@ -212,7 +191,8 @@
 			goto bad;
 		}
 	} else {
-		if ((error = soconnect(so, nmp->nm_nam)) != 0)
+		error = soconnect(so, nmp->nm_nam);
+		if (error)
 			goto bad;
 
 		/*
@@ -226,7 +206,7 @@
 				"nfscon", 2 * hz);
 			if ((so->so_state & SS_ISCONNECTING) &&
 			    so->so_error == 0 && rep &&
-			    (error = nfs_sigintr(nmp, rep, rep->r_procp))) {
+			    (error = nfs_sigintr(nmp, rep, rep->r_procp)) != 0){
 				so->so_state &= ~SS_ISCONNECTING;
 				splx(s);
 				goto bad;
@@ -273,7 +253,8 @@
 		rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR +
 		    sizeof (u_int32_t)) * 2;
 	}
-	if ((error = soreserve(so, sndreserve, rcvreserve)) != 0)
+	error = soreserve(so, sndreserve, rcvreserve);
+	if (error)
 		goto bad;
 	so->so_rcv.sb_flags |= SB_NOINTR;
 	so->so_snd.sb_flags |= SB_NOINTR;
@@ -459,7 +440,8 @@
 	 * until we have an entire rpc request/reply.
 	 */
 	if (sotype != SOCK_DGRAM) {
-		if ((error = nfs_sndlock(&rep->r_nmp->nm_flag, rep)) != 0)
+		error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+		if (error)
 			return (error);
 tryagain:
 		/*
@@ -475,8 +457,10 @@
 			nfs_sndunlock(&rep->r_nmp->nm_flag);
 			return (EINTR);
 		}
-		if ((so = rep->r_nmp->nm_so) == NULL) {
-			if ((error = nfs_reconnect(rep)) != 0) {
+		so = rep->r_nmp->nm_so;
+		if (!so) {
+			error = nfs_reconnect(rep); 
+			if (error) {
 				nfs_sndunlock(&rep->r_nmp->nm_flag);
 				return (error);
 			}
@@ -488,7 +472,7 @@
 			error = nfs_send(so, rep->r_nmp->nm_nam, m, rep);
 			if (error) {
 				if (error == EINTR || error == ERESTART ||
-				    (error = nfs_reconnect(rep))) {
+				    (error = nfs_reconnect(rep)) != 0) {
 					nfs_sndunlock(&rep->r_nmp->nm_flag);
 					return (error);
 				}
@@ -657,7 +641,8 @@
 		 * Also necessary for connection based protocols to avoid
 		 * race conditions during a reconnect.
 		 */
-		if ((error = nfs_rcvlock(myrep)) != 0)
+		error = nfs_rcvlock(myrep);
+		if (error)
 			return (error);
 		/* Already received, bye bye */
 		if (myrep->r_mrep != NULL) {
@@ -819,13 +804,16 @@
 	struct nfsmount *nmp;
 	struct mbuf *md, *mheadend;
 	struct nfsnode *np;
+	char nickv[RPCX_NICKVERF];
 	time_t reqtime, waituntil;
 	caddr_t dpos, cp2;
 	int t1, nqlflag, cachable, s, error = 0, mrest_len, auth_len, auth_type;
 	int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0, failed_auth = 0;
+	int verf_len, verf_type;
 	u_int32_t xid;
 	u_quad_t frev;
-	char *auth_str;
+	char *auth_str, *verf_str;
+	NFSKERBKEY_T key;		/* save session key */
 
 	nmp = VFSTONFS(vp->v_mount);
 	MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
@@ -845,19 +833,21 @@
 	 * Get the RPC header with authorization.
 	 */
 kerbauth:
-	auth_str = (char *)0;
+	verf_str = auth_str = (char *)0;
 	if (nmp->nm_flag & NFSMNT_KERB) {
-		if (failed_auth) {
-			error = nfs_getauth(nmp, rep, cred, &auth_type,
-				&auth_str, &auth_len);
+		verf_str = nickv;
+		verf_len = sizeof (nickv);
+		auth_type = RPCAUTH_KERB4;
+		bzero((caddr_t)key, sizeof (key));
+		if (failed_auth || nfs_getnickauth(nmp, cred, &auth_str,
+			&auth_len, verf_str, verf_len)) {
+			error = nfs_getauth(nmp, rep, cred, &auth_str,
+				&auth_len, verf_str, &verf_len, key);
 			if (error) {
 				free((caddr_t)rep, M_NFSREQ);
 				m_freem(mrest);
 				return (error);
 			}
-		} else {
-			auth_type = RPCAUTH_UNIX;
-			auth_len = 5 * NFSX_UNSIGNED;
 		}
 	} else {
 		auth_type = RPCAUTH_UNIX;
@@ -865,8 +855,8 @@
 			nmp->nm_numgrps : cred->cr_ngroups) << 2) +
 			5 * NFSX_UNSIGNED;
 	}
-	m = nfsm_rpchead(cred, (nmp->nm_flag & NFSMNT_NQNFS), procnum,
-	     auth_type, auth_len, auth_str, mrest, mrest_len, &mheadend, &xid);
+	m = nfsm_rpchead(cred, nmp->nm_flag, procnum, auth_type, auth_len,
+	     auth_str, verf_len, verf_str, mrest, mrest_len, &mheadend, &xid);
 	if (auth_str)
 		free(auth_str, M_TEMP);
 
@@ -972,12 +962,12 @@
 	/*
 	 * break down the rpc header and check if ok
 	 */
-	nfsm_dissect(tl, u_int32_t *, 3*NFSX_UNSIGNED);
+	nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
 	if (*tl++ == rpc_msgdenied) {
 		if (*tl == rpc_mismatch)
 			error = EOPNOTSUPP;
 		else if ((nmp->nm_flag & NFSMNT_KERB) && *tl++ == rpc_autherr) {
-			if (*tl == rpc_rejectedcred && failed_auth == 0) {
+			if (!failed_auth) {
 				failed_auth++;
 				mheadend->m_next = (struct mbuf *)0;
 				m_freem(mrep);
@@ -994,22 +984,25 @@
 	}
 
 	/*
-	 * skip over the auth_verf, someday we may want to cache auth_short's
-	 * for nfs_reqhead(), but for now just dump it
+	 * Grab any Kerberos verifier, otherwise just throw it away.
 	 */
-	if (*++tl != 0) {
-		i = nfsm_rndup(fxdr_unsigned(int32_t, *tl));
-		nfsm_adv(i);
-	}
+	verf_type = fxdr_unsigned(int, *tl++);
+	i = fxdr_unsigned(int32_t, *tl);
+	if ((nmp->nm_flag & NFSMNT_KERB) && verf_type == RPCAUTH_KERB4) {
+		error = nfs_savenickauth(nmp, cred, i, key, &md, &dpos, mrep);
+		if (error)
+			goto nfsmout;
+	} else if (i > 0)
+		nfsm_adv(nfsm_rndup(i));
 	nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
 	/* 0 == ok */
 	if (*tl == 0) {
 		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
 		if (*tl != 0) {
 			error = fxdr_unsigned(int, *tl);
-			m_freem(mrep);
-			if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-			    error == NQNFS_TRYLATER) {
+			if ((nmp->nm_flag & NFSMNT_NFSV3) &&
+				error == NFSERR_TRYLATER) {
+				m_freem(mrep);
 				error = 0;
 				waituntil = time.tv_sec + trylater_delay;
 				while (time.tv_sec < waituntil)
@@ -1027,6 +1020,13 @@
 			 */
 			if (error == ESTALE)
 				cache_purge(vp);
+			if (nmp->nm_flag & NFSMNT_NFSV3) {
+				*mrp = mrep;
+				*mdp = md;
+				*dposp = dpos;
+				error |= NFSERR_RETERR;
+			} else
+				m_freem(mrep);
 			m_freem(rep->r_mreq);
 			free((caddr_t)rep, M_NFSREQ);
 			return (error);
@@ -1058,10 +1058,10 @@
 		return (0);
 	}
 	m_freem(mrep);
+	error = EPROTONOSUPPORT;
+nfsmout:
 	m_freem(rep->r_mreq);
 	free((caddr_t)rep, M_NFSREQ);
-	error = EPROTONOSUPPORT;
-nfsmout:
 	return (error);
 }
 #endif /* NFSCLIENT */
@@ -1071,9 +1071,10 @@
  * siz arg. is used to decide if adding a cluster is worthwhile
  */
 int
-nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+nfs_rephead(siz, nd, slp, err, cache, frev, mrq, mbp, bposp)
 	int siz;
-	struct nfsd *nd;
+	struct nfsrv_descript *nd;
+	struct nfssvc_sock *slp;
 	int err;
 	int cache;
 	u_quad_t *frev;
@@ -1098,47 +1099,97 @@
 	} else
 		mreq->m_data += max_hdr;
 	tl = mtod(mreq, u_int32_t *);
-	mreq->m_len = 6*NFSX_UNSIGNED;
-	bpos = ((caddr_t)tl)+mreq->m_len;
+	mreq->m_len = 6 * NFSX_UNSIGNED;
+	bpos = ((caddr_t)tl) + mreq->m_len;
 	*tl++ = txdr_unsigned(nd->nd_retxid);
 	*tl++ = rpc_reply;
-	if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+	if (err == ERPCMISMATCH || (err & NFSERR_AUTHERR)) {
 		*tl++ = rpc_msgdenied;
-		if (err == NQNFS_AUTHERR) {
+		if (err & NFSERR_AUTHERR) {
 			*tl++ = rpc_autherr;
-			*tl = rpc_rejectedcred;
+			*tl = txdr_unsigned(err & ~NFSERR_AUTHERR);
 			mreq->m_len -= NFSX_UNSIGNED;
 			bpos -= NFSX_UNSIGNED;
 		} else {
 			*tl++ = rpc_mismatch;
-			*tl++ = txdr_unsigned(2);
-			*tl = txdr_unsigned(2);
+			*tl++ = txdr_unsigned(RPC_VER2);
+			*tl = txdr_unsigned(RPC_VER2);
 		}
 	} else {
 		*tl++ = rpc_msgaccepted;
-		*tl++ = 0;
-		*tl++ = 0;
+
+		/*
+		 * For Kerberos authentication, we must send the nickname
+		 * verifier back, otherwise just RPCAUTH_NULL.
+		 */
+		if (nd->nd_flag & ND_KERBFULL) {
+		    register struct nfsuid *nuidp;
+		    struct timeval ktvin, ktvout;
+
+		    for (nuidp = NUIDHASH(slp, nd->nd_cr.cr_uid)->lh_first;
+			nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
+			if (nuidp->nu_cr.cr_uid == nd->nd_cr.cr_uid &&
+			    (!nd->nd_nam2 || netaddr_match(NU_NETFAM(nuidp),
+			     &nuidp->nu_haddr, nd->nd_nam2)))
+			    break;
+		    }
+		    if (nuidp) {
+			ktvin.tv_sec =
+			    txdr_unsigned(nuidp->nu_timestamp.tv_sec - 1);
+			ktvin.tv_usec =
+			    txdr_unsigned(nuidp->nu_timestamp.tv_usec);
+
+			/*
+			 * Encrypt the timestamp in ecb mode using the
+			 * session key.
+			 */
+#ifdef NFSKERB
+			XXX
+#endif
+
+			*tl++ = rpc_auth_kerb;
+			*tl++ = txdr_unsigned(3 * NFSX_UNSIGNED);
+			*tl = ktvout.tv_sec;
+			nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+			*tl++ = ktvout.tv_usec;
+			*tl++ = txdr_unsigned(nuidp->nu_cr.cr_uid);
+		    } else {
+			*tl++ = 0;
+			*tl++ = 0;
+		    }
+		} else {
+			*tl++ = 0;
+			*tl++ = 0;
+		}
 		switch (err) {
 		case EPROGUNAVAIL:
 			*tl = txdr_unsigned(RPC_PROGUNAVAIL);
 			break;
 		case EPROGMISMATCH:
 			*tl = txdr_unsigned(RPC_PROGMISMATCH);
-			nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-			*tl++ = txdr_unsigned(2);
-			*tl = txdr_unsigned(2);	/* someday 3 */
+			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			if (nd->nd_flag & ND_NQNFS) {
+				*tl++ = txdr_unsigned(3);
+				*tl = txdr_unsigned(3);
+			} else {
+				*tl++ = txdr_unsigned(2);
+				*tl = txdr_unsigned(3);
+			}
 			break;
 		case EPROCUNAVAIL:
 			*tl = txdr_unsigned(RPC_PROCUNAVAIL);
 			break;
+		case EBADRPC:
+			*tl = txdr_unsigned(RPC_GARBAGE);
+			break;
 		default:
 			*tl = 0;
-			if (err != VNOVAL) {
+			if (err != NFSERR_RETVOID) {
 				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
 				if (err)
-					*tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+				    *tl = txdr_unsigned(nfsrv_errmap(nd, err));
 				else
-					*tl = 0;
+				    *tl = 0;
 			}
 			break;
 		};
@@ -1147,16 +1198,14 @@
 	/*
 	 * For nqnfs, piggyback lease as requested.
 	 */
-	if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
-		if (nd->nd_nqlflag) {
-			nfsm_build(tl, u_int32_t *, 5*NFSX_UNSIGNED);
-			*tl++ = txdr_unsigned(nd->nd_nqlflag);
+	if ((nd->nd_flag & ND_NQNFS) && err == 0) {
+		if (nd->nd_flag & ND_LEASE) {
+			nfsm_build(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+			*tl++ = txdr_unsigned(nd->nd_flag & ND_LEASE);
 			*tl++ = txdr_unsigned(cache);
 			*tl++ = txdr_unsigned(nd->nd_duration);
 			txdr_hyper(frev, tl);
 		} else {
-			if (nd->nd_nqlflag != 0)
-				panic("nqreph");
 			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
 			*tl = 0;
 		}
@@ -1164,7 +1213,7 @@
 	*mrq = mreq;
 	*mbp = mb;
 	*bposp = bpos;
-	if (err != 0 && err != VNOVAL)
+	if (err != 0 && err != NFSERR_RETVOID)
 		nfsstats.srvrpc_errs++;
 	return (0);
 }
@@ -1177,17 +1226,19 @@
  */
 void
 nfs_timer(arg)
-	void *arg;
+	void *arg;	/* never used */
 {
 	register struct nfsreq *rep;
 	register struct mbuf *m;
 	register struct socket *so;
 	register struct nfsmount *nmp;
 	register int timeo;
+	register struct nfssvc_sock *slp;
 #ifdef NFSSERVER
 	static long lasttime = 0;
 #endif
 	int s, error;
+	u_quad_t cur_usec;
 
 	s = splsoftnet();
 	for (rep = nfs_reqq.tqh_first; rep != 0; rep = rep->r_chain.tqe_next) {
@@ -1285,9 +1336,20 @@
 		lasttime = time.tv_sec;
 		nqnfs_serverd();
 	}
+
+	/*
+	 * Scan the write gathering queues for writes that need to be
+	 * completed now.
+	 */
+	cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec;
+	for (slp = nfssvc_sockhead.tqh_first; slp != 0;
+	    slp = slp->ns_chain.tqe_next) {
+	    if (slp->ns_tq.lh_first && slp->ns_tq.lh_first->nd_time<=cur_usec)
+		nfsrv_wakenfsd(slp);
+	}
 #endif /* NFSSERVER */
 	splx(s);
-	timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+	timeout(nfs_timer, (void *)0, nfs_ticks);
 }
 
 /*
@@ -1503,8 +1565,9 @@
  * - fill in the cred struct.
  */
 int
-nfs_getreq(nd, has_header)
-	register struct nfsd *nd;
+nfs_getreq(nd, nfsd, has_header)
+	register struct nfsrv_descript *nd;
+	struct nfsd *nfsd;
 	int has_header;
 {
 	register int len, i;
@@ -1512,57 +1575,66 @@
 	register int32_t t1;
 	struct uio uio;
 	struct iovec iov;
-	caddr_t dpos, cp2;
+	caddr_t dpos, cp2, cp;
 	u_int32_t nfsvers, auth_type;
-	int error = 0, nqnfs = 0;
+	uid_t nickuid;
+	int error = 0, nqnfs = 0, ticklen;
 	struct mbuf *mrep, *md;
+	register struct nfsuid *nuidp;
+	struct timeval tvin, tvout;
 
 	mrep = nd->nd_mrep;
 	md = nd->nd_md;
 	dpos = nd->nd_dpos;
 	if (has_header) {
-		nfsm_dissect(tl, u_int32_t *, 10*NFSX_UNSIGNED);
-		nd->nd_retxid = fxdr_unsigned(u_int32_t , *tl++);
+		nfsm_dissect(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
+		nd->nd_retxid = fxdr_unsigned(u_int32_t, *tl++);
 		if (*tl++ != rpc_call) {
 			m_freem(mrep);
 			return (EBADRPC);
 		}
-	} else {
-		nfsm_dissect(tl, u_int32_t *, 8*NFSX_UNSIGNED);
-	}
+	} else
+		nfsm_dissect(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
 	nd->nd_repstat = 0;
+	nd->nd_flag = 0;
 	if (*tl++ != rpc_vers) {
 		nd->nd_repstat = ERPCMISMATCH;
 		nd->nd_procnum = NFSPROC_NOOP;
 		return (0);
 	}
-	nfsvers = nfs_vers;
 	if (*tl != nfs_prog) {
-		if (*tl == nqnfs_prog) {
+		if (*tl == nqnfs_prog)
 			nqnfs++;
-			nfsvers = nqnfs_vers;
-		} else {
+		else {
 			nd->nd_repstat = EPROGUNAVAIL;
 			nd->nd_procnum = NFSPROC_NOOP;
 			return (0);
 		}
 	}
 	tl++;
-	if (*tl++ != nfsvers) {
+	nfsvers = fxdr_unsigned(u_int32_t, *tl++);
+	if (((nfsvers < NFS_VER2 || nfsvers > NFS_VER3) && !nqnfs) ||
+		(nfsvers != NQNFS_VER3 && nqnfs)) {
 		nd->nd_repstat = EPROGMISMATCH;
 		nd->nd_procnum = NFSPROC_NOOP;
 		return (0);
 	}
-	nd->nd_procnum = fxdr_unsigned(u_int32_t , *tl++);
+	if (nqnfs)
+		nd->nd_flag = (ND_NFSV3 | ND_NQNFS);
+	else if (nfsvers == NFS_VER3)
+		nd->nd_flag = ND_NFSV3;
+	nd->nd_procnum = fxdr_unsigned(u_int32_t, *tl++);
 	if (nd->nd_procnum == NFSPROC_NULL)
 		return (0);
 	if (nd->nd_procnum >= NFS_NPROCS ||
-		(!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
-		(*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+		(!nqnfs && nd->nd_procnum >= NQNFSPROC_GETLEASE) ||
+		(!nd->nd_flag && nd->nd_procnum > NFSV2PROC_STATFS)) {
 		nd->nd_repstat = EPROCUNAVAIL;
 		nd->nd_procnum = NFSPROC_NOOP;
 		return (0);
 	}
+	if ((nd->nd_flag & ND_NFSV3) == 0)
+		nd->nd_procnum = nfsv3_procid[nd->nd_procnum];
 	auth_type = *tl++;
 	len = fxdr_unsigned(int, *tl++);
 	if (len < 0 || len > RPCAUTH_MAXSIZ) {
@@ -1570,6 +1642,7 @@
 		return (EBADRPC);
 	}
 
+	nd->nd_flag &= ~ND_KERBAUTH;
 	/*
 	 * Handle auth_unix or auth_kerb.
 	 */
@@ -1580,7 +1653,9 @@
 			return (EBADRPC);
 		}
 		nfsm_adv(nfsm_rndup(len));
-		nfsm_dissect(tl, u_int32_t *, 3*NFSX_UNSIGNED);
+		nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+		bzero((caddr_t)&nd->nd_cr, sizeof (struct ucred));
+		nd->nd_cr.cr_ref = 1;
 		nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
 		nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
 		len = fxdr_unsigned(int, *tl);
@@ -1588,45 +1663,124 @@
 			m_freem(mrep);
 			return (EBADRPC);
 		}
-		nfsm_dissect(tl, u_int32_t *, (len + 2)*NFSX_UNSIGNED);
+		nfsm_dissect(tl, u_int32_t *, (len + 2) * NFSX_UNSIGNED);
 		for (i = 0; i < len; i++)
-			if (i < NGROUPS)
-				nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
-			else
-				tl++;
+		    if (i < NGROUPS)
+			nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+		    else
+			tl++;
 		nd->nd_cr.cr_ngroups = (len > NGROUPS) ? NGROUPS : len;
-	} else if (auth_type == rpc_auth_kerb) {
-		nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
-		nd->nd_authlen = fxdr_unsigned(int, *tl);
-		uio.uio_resid = nfsm_rndup(nd->nd_authlen);
-		if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+		if (nd->nd_cr.cr_ngroups > 1)
+		    nfsrvw_sort(nd->nd_cr.cr_groups, nd->nd_cr.cr_ngroups);
+		len = fxdr_unsigned(int, *++tl);
+		if (len < 0 || len > RPCAUTH_MAXSIZ) {
 			m_freem(mrep);
 			return (EBADRPC);
 		}
-		uio.uio_offset = 0;
-		uio.uio_iov = &iov;
-		uio.uio_iovcnt = 1;
-		uio.uio_segflg = UIO_SYSSPACE;
-		iov.iov_base = (caddr_t)nd->nd_authstr;
-		iov.iov_len = RPCAUTH_MAXSIZ;
-		nfsm_mtouio(&uio, uio.uio_resid);
-		nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-		nd->nd_flag |= NFSD_NEEDAUTH;
-	}
+		if (len > 0)
+			nfsm_adv(nfsm_rndup(len));
+	} else if (auth_type == rpc_auth_kerb) {
+		switch (fxdr_unsigned(int, *tl++)) {
+		case RPCAKN_FULLNAME:
+			ticklen = fxdr_unsigned(int, *tl);
+			*((u_int32_t *)nfsd->nfsd_authstr) = *tl;
+			uio.uio_resid = nfsm_rndup(ticklen) + NFSX_UNSIGNED;
+			nfsd->nfsd_authlen = uio.uio_resid + NFSX_UNSIGNED;
+			if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+				m_freem(mrep);
+				return (EBADRPC);
+			}
+			uio.uio_offset = 0;
+			uio.uio_iov = &iov;
+			uio.uio_iovcnt = 1;
+			uio.uio_segflg = UIO_SYSSPACE;
+			iov.iov_base = (caddr_t)&nfsd->nfsd_authstr[4];
+			iov.iov_len = RPCAUTH_MAXSIZ - 4;
+			nfsm_mtouio(&uio, uio.uio_resid);
+			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			if (*tl++ != rpc_auth_kerb ||
+				fxdr_unsigned(int, *tl) != 4 * NFSX_UNSIGNED) {
+				printf("Bad kerb verifier\n");
+				nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+			nfsm_dissect(cp, caddr_t, 4 * NFSX_UNSIGNED);
+			tl = (u_int32_t *)cp;
+			if (fxdr_unsigned(int, *tl) != RPCAKN_FULLNAME) {
+				printf("Not fullname kerb verifier\n");
+				nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+			cp += NFSX_UNSIGNED;
+			bcopy(cp, nfsd->nfsd_verfstr, 3 * NFSX_UNSIGNED);
+			nfsd->nfsd_verflen = 3 * NFSX_UNSIGNED;
+			nd->nd_flag |= ND_KERBFULL;
+			nfsd->nfsd_flag |= NFSD_NEEDAUTH;
+			break;
+		case RPCAKN_NICKNAME:
+			if (len != 2 * NFSX_UNSIGNED) {
+				printf("Kerb nickname short\n");
+				nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADCRED);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+			nickuid = fxdr_unsigned(uid_t, *tl);
+			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			if (*tl++ != rpc_auth_kerb ||
+				fxdr_unsigned(int, *tl) != 3 * NFSX_UNSIGNED) {
+				printf("Kerb nick verifier bad\n");
+				nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+			nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+			tvin.tv_sec = *tl++;
+			tvin.tv_usec = *tl;
 
-	/*
-	 * Do we have any use for the verifier.
-	 * According to the "Remote Procedure Call Protocol Spec." it
-	 * should be AUTH_NULL, but some clients make it AUTH_UNIX?
-	 * For now, just skip over it
-	 */
-	len = fxdr_unsigned(int, *++tl);
-	if (len < 0 || len > RPCAUTH_MAXSIZ) {
-		m_freem(mrep);
-		return (EBADRPC);
-	}
-	if (len > 0) {
-		nfsm_adv(nfsm_rndup(len));
+			for (nuidp = NUIDHASH(nfsd->nfsd_slp,nickuid)->lh_first;
+			    nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
+				if (nuidp->nu_cr.cr_uid == nickuid &&
+				    (!nd->nd_nam2 ||
+				     netaddr_match(NU_NETFAM(nuidp),
+				      &nuidp->nu_haddr, nd->nd_nam2)))
+					break;
+			}
+			if (!nuidp) {
+				nd->nd_repstat =
+					(NFSERR_AUTHERR|AUTH_REJECTCRED);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+
+			/*
+			 * Now, decrypt the timestamp using the session key
+			 * and validate it.
+			 */
+#ifdef NFSKERB
+			XXX
+#endif
+
+			tvout.tv_sec = fxdr_unsigned(long, tvout.tv_sec);
+			tvout.tv_usec = fxdr_unsigned(long, tvout.tv_usec);
+			if (nuidp->nu_expire < time.tv_sec ||
+			    nuidp->nu_timestamp.tv_sec > tvout.tv_sec ||
+			    (nuidp->nu_timestamp.tv_sec == tvout.tv_sec &&
+			     nuidp->nu_timestamp.tv_usec > tvout.tv_usec)) {
+				nuidp->nu_expire = 0;
+				nd->nd_repstat =
+				    (NFSERR_AUTHERR|AUTH_REJECTVERF);
+				nd->nd_procnum = NFSPROC_NOOP;
+				return (0);
+			}
+			nfsrv_setcred(&nuidp->nu_cr, &nd->nd_cr);
+			nd->nd_flag |= ND_KERBNICK;
+		};
+	} else {
+		nd->nd_repstat = (NFSERR_AUTHERR | AUTH_REJECTCRED);
+		nd->nd_procnum = NFSPROC_NOOP;
+		return (0);
 	}
 
 	/*
@@ -1634,16 +1788,14 @@
 	 */
 	if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
 		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-		nd->nd_nqlflag = fxdr_unsigned(int, *tl);
-		if (nd->nd_nqlflag) {
+		nd->nd_flag |= fxdr_unsigned(int, *tl);
+		if (nd->nd_flag & ND_LEASE) {
 			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-			nd->nd_duration = fxdr_unsigned(int, *tl);
+			nd->nd_duration = fxdr_unsigned(u_int32_t, *tl);
 		} else
 			nd->nd_duration = NQ_MINLEASE;
-	} else {
-		nd->nd_nqlflag = NQL_NOVAL;
+	} else
 		nd->nd_duration = NQ_MINLEASE;
-	}
 	nd->nd_md = md;
 	nd->nd_dpos = dpos;
 	return (0);
@@ -1651,7 +1803,7 @@
 	return (error);
 }
 
-void
+int
 nfs_msg(p, server, msg)
 	struct proc *p;
 	char *server, *msg;
@@ -1664,35 +1816,39 @@
 		tpr = NULL;
 	tprintf(tpr, "nfs server %s: %s\n", server, msg);
 	tprintf_close(tpr);
+	return (0);
 }
 
 #ifdef NFSSERVER
-int (*nfsrv_procs[NFS_NPROCS]) __P((struct nfsd *, struct mbuf *, struct mbuf *,
-				    caddr_t, struct ucred *, struct mbuf *,
+int (*nfsrv3_procs[NFS_NPROCS]) __P((struct nfsrv_descript *,
+				    struct nfssvc_sock *, struct proc *,
 				    struct mbuf **)) = {
 	nfsrv_null,
 	nfsrv_getattr,
 	nfsrv_setattr,
-	nfsrv_noop,
 	nfsrv_lookup,
+	nfsrv3_access,
 	nfsrv_readlink,
 	nfsrv_read,
-	nfsrv_noop,
 	nfsrv_write,
 	nfsrv_create,
+	nfsrv_mkdir,
+	nfsrv_symlink,
+	nfsrv_mknod,
 	nfsrv_remove,
+	nfsrv_rmdir,
 	nfsrv_rename,
 	nfsrv_link,
-	nfsrv_symlink,
-	nfsrv_mkdir,
-	nfsrv_rmdir,
 	nfsrv_readdir,
+	nfsrv_readdirplus,
 	nfsrv_statfs,
-	nqnfsrv_readdirlook,
+	nfsrv_fsinfo,
+	nfsrv_pathconf,
+	nfsrv_commit,
 	nqnfsrv_getlease,
 	nqnfsrv_vacated,
 	nfsrv_noop,
-	nqnfsrv_access,
+	nfsrv_noop
 };
 
 /*
@@ -1763,7 +1919,8 @@
 		/*
 		 * Now try and parse record(s) out of the raw stream data.
 		 */
-		if ((error = nfsrv_getstream(slp, waitflag)) != 0) {
+		error = nfsrv_getstream(slp, waitflag);
+		if (error) {
 			if (error == EPERM)
 				slp->ns_flag |= SLP_DISCONN;
 			else
@@ -1818,11 +1975,11 @@
 	register struct nfssvc_sock *slp;
 	int waitflag;
 {
-	register struct mbuf *m;
+	register struct mbuf *m, **mpp;
 	register char *cp1, *cp2;
 	register int len;
 	struct mbuf *om, *m2, *recm = NULL;
-	u_long recmark;
+	u_int32_t recmark;
 
 	if (slp->ns_flag & SLP_GETSTREAM)
 		panic("nfs getstream");
@@ -1852,7 +2009,12 @@
 			}
 		}
 		slp->ns_cc -= NFSX_UNSIGNED;
-		slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+		recmark = ntohl(recmark);
+		slp->ns_reclen = recmark & ~0x80000000;
+		if (recmark & 0x80000000)
+			slp->ns_flag |= SLP_LASTFRAG;
+		else
+			slp->ns_flag &= ~SLP_LASTFRAG;
 		if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
 			slp->ns_flag &= ~SLP_GETSTREAM;
 			return (EPERM);
@@ -1906,12 +2068,23 @@
 		slp->ns_flag &= ~SLP_GETSTREAM;
 		return (0);
 	    }
-	    nfs_realign(recm, 10 * NFSX_UNSIGNED);
-	    if (slp->ns_recend)
-		slp->ns_recend->m_nextpkt = recm;
-	    else
-		slp->ns_rec = recm;
-	    slp->ns_recend = recm;
+
+	    /*
+	     * Accumulate the fragments into a record.
+	     */
+	    mpp = &slp->ns_frag;
+	    while (*mpp)
+		mpp = &((*mpp)->m_next);
+	    *mpp = recm;
+	    if (slp->ns_flag & SLP_LASTFRAG) {
+		nfs_realign(slp->ns_frag, 10 * NFSX_UNSIGNED);
+		if (slp->ns_recend)
+		    slp->ns_recend->m_nextpkt = slp->ns_frag;
+		else
+		    slp->ns_rec = slp->ns_frag;
+		slp->ns_recend = slp->ns_frag;
+		slp->ns_frag = (struct mbuf *)0;
+	    }
 	}
 }
 
@@ -1919,36 +2092,47 @@
  * Parse an RPC header.
  */
 int
-nfsrv_dorec(slp, nd)
+nfsrv_dorec(slp, nfsd, ndp)
 	register struct nfssvc_sock *slp;
-	register struct nfsd *nd;
+	struct nfsd *nfsd;
+	struct nfsrv_descript **ndp;
 {
-	register struct mbuf *m;
+	register struct mbuf *m, *nam;
+	register struct nfsrv_descript *nd;
 	int error;
 
+	*ndp = NULL;
 	if ((slp->ns_flag & SLP_VALID) == 0 ||
 	    (m = slp->ns_rec) == (struct mbuf *)0)
 		return (ENOBUFS);
-	if ((slp->ns_rec = m->m_nextpkt) != NULL)
+	slp->ns_rec = m->m_nextpkt;
+	if (slp->ns_rec)
 		m->m_nextpkt = (struct mbuf *)0;
 	else
 		slp->ns_recend = (struct mbuf *)0;
 	if (m->m_type == MT_SONAME) {
-		nd->nd_nam = m;
-		nd->nd_md = nd->nd_mrep = m->m_next;
-		m->m_next = (struct mbuf *)0;
-	} else {
-		nd->nd_nam = (struct mbuf *)0;
-		nd->nd_md = nd->nd_mrep = m;
-	}
-	nd->nd_dpos = mtod(nd->nd_md, caddr_t);
-	if ((error = nfs_getreq(nd, TRUE)) != 0) {
-		m_freem(nd->nd_nam);
+		nam = m;
+		m = m->m_next;
+		nam->m_next = NULL;
+	} else
+		nam = NULL;
+	MALLOC(nd, struct nfsrv_descript *, sizeof (struct nfsrv_descript),
+		M_NFSRVDESC, M_WAITOK);
+	nd->nd_md = nd->nd_mrep = m;
+	nd->nd_nam2 = nam;
+	nd->nd_dpos = mtod(m, caddr_t);
+	error = nfs_getreq(nd, nfsd, TRUE);
+	if (error) {
+		m_freem(nam);
+		free((caddr_t)nd, M_NFSRVDESC);
 		return (error);
 	}
+	*ndp = nd;
+	nfsd->nfsd_nd = nd;
 	return (0);
 }
 
+
 /*
  * Search for a sleeping nfsd and wake it up.
  * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
@@ -1962,13 +2146,13 @@
 
 	if ((slp->ns_flag & SLP_VALID) == 0)
 		return;
-	for (nd = nfsd_head.tqh_first; nd != 0; nd = nd->nd_chain.tqe_next) {
-		if (nd->nd_flag & NFSD_WAITING) {
-			nd->nd_flag &= ~NFSD_WAITING;
-			if (nd->nd_slp)
+	for (nd = nfsd_head.tqh_first; nd != 0; nd = nd->nfsd_chain.tqe_next) {
+		if (nd->nfsd_flag & NFSD_WAITING) {
+			nd->nfsd_flag &= ~NFSD_WAITING;
+			if (nd->nfsd_slp)
 				panic("nfsd wakeup");
 			slp->ns_sref++;
-			nd->nd_slp = slp;
+			nd->nfsd_slp = slp;
 			wakeup((caddr_t)nd);
 			return;
 		}
--- a/sys/nfs/nfs_srvcache.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_srvcache.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_srvcache.c,v 1.11 1996/02/09 21:48:32 christos Exp $	*/
+/*	$NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,7 +35,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_srvcache.c	8.2 (Berkeley) 8/18/94
+ *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
  */
 
 /*
@@ -60,12 +60,14 @@
 #endif
 #include <nfs/nfsm_subs.h>
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsrvcache.h>
 #include <nfs/nqnfs.h>
 #include <nfs/nfs_var.h>
 
+extern struct nfsstats nfsstats;
+extern int nfsv2_procid[NFS_NPROCS];
 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
 
 #define	NFSRCHASH(xid) \
@@ -91,7 +93,7 @@
 	FALSE,
 	FALSE,
 	FALSE,
-	FALSE,
+	TRUE,
 	TRUE,
 	TRUE,
 	TRUE,
@@ -107,10 +109,13 @@
 	FALSE,
 	FALSE,
 	FALSE,
+	FALSE,
+	FALSE,
+	FALSE,
 };
 
 /* True iff the rpc reply is an nfs status ONLY! */
-static int repliesstatus[NFS_NPROCS] = {
+static int nfsv2_repstat[NFS_NPROCS] = {
 	FALSE,
 	FALSE,
 	FALSE,
@@ -129,11 +134,6 @@
 	TRUE,
 	FALSE,
 	FALSE,
-	FALSE,
-	FALSE,
-	FALSE,
-	FALSE,
-	TRUE,
 };
 
 /*
@@ -162,9 +162,9 @@
  * Update/add new request at end of lru list
  */
 int
-nfsrv_getcache(nam, nd, repp)
-	struct mbuf *nam;
-	register struct nfsd *nd;
+nfsrv_getcache(nd, slp, repp)
+	register struct nfsrv_descript *nd;
+	struct nfssvc_sock *slp;
 	struct mbuf **repp;
 {
 	register struct nfsrvcache *rp;
@@ -173,13 +173,17 @@
 	caddr_t bpos;
 	int ret;
 
-	if (nd->nd_nqlflag != NQL_NOVAL)
+	/*
+	 * Don't cache recent requests for reliable transport protocols.
+	 * (Maybe we should for the case of a reconnect, but..)
+	 */
+	if (!nd->nd_nam2)
 		return (RC_DOIT);
 loop:
 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
 	    rp = rp->rc_hash.le_next) {
 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
-		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
 			if ((rp->rc_flag & RC_LOCKED) != 0) {
 				rp->rc_flag |= RC_WANTED;
 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
@@ -198,7 +202,7 @@
 				ret = RC_DROPIT;
 			} else if (rp->rc_flag & RC_REPSTATUS) {
 				nfsstats.srvcache_nonidemdonehits++;
-				nfs_rephead(0, nd, rp->rc_status,
+				nfs_rephead(0, nd, slp, rp->rc_status,
 				   0, (u_quad_t *)0, repp, &mb, &bpos);
 				ret = RC_REPLY;
 			} else if (rp->rc_flag & RC_REPMBUF) {
@@ -245,7 +249,7 @@
 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
 	rp->rc_state = RC_INPROG;
 	rp->rc_xid = nd->nd_retxid;
-	saddr = mtod(nam, struct sockaddr_in *);
+	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
 	switch (saddr->sin_family) {
 	case AF_INET:
 		rp->rc_flag |= RC_INETADDR;
@@ -254,7 +258,7 @@
 	case AF_ISO:
 	default:
 		rp->rc_flag |= RC_NAM;
-		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
 		break;
 	};
 	rp->rc_proc = nd->nd_procnum;
@@ -271,21 +275,20 @@
  * Update a request cache entry after the rpc has been done
  */
 void
-nfsrv_updatecache(nam, nd, repvalid, repmbuf)
-	struct mbuf *nam;
-	register struct nfsd *nd;
+nfsrv_updatecache(nd, repvalid, repmbuf)
+	register struct nfsrv_descript *nd;
 	int repvalid;
 	struct mbuf *repmbuf;
 {
 	register struct nfsrvcache *rp;
 
-	if (nd->nd_nqlflag != NQL_NOVAL)
+	if (!nd->nd_nam2)
 		return;
 loop:
 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
 	    rp = rp->rc_hash.le_next) {
 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
-		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
 			if ((rp->rc_flag & RC_LOCKED) != 0) {
 				rp->rc_flag |= RC_WANTED;
 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
@@ -298,7 +301,8 @@
 			 * the reply for non-idempotent rpc's.
 			 */
 			if (repvalid && nonidempotent[nd->nd_procnum]) {
-				if (repliesstatus[nd->nd_procnum]) {
+				if ((nd->nd_flag & ND_NFSV3) == 0 &&
+				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
 					rp->rc_status = nd->nd_repstat;
 					rp->rc_flag |= RC_REPSTATUS;
 				} else {
--- a/sys/nfs/nfs_subs.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_subs.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_subs.c,v 1.24 1996/02/09 21:48:34 christos Exp $	*/
+/*	$NetBSD: nfs_subs.c,v 1.25 1996/02/18 11:53:51 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,9 +35,10 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_subs.c	8.3 (Berkeley) 1/4/94
+ *	@(#)nfs_subs.c	8.8 (Berkeley) 5/22/95
  */
 
+
 /*
  * These functions support the macros and help fiddle mbuf chains for
  * the nfs op functions. They do things like create the rpc header and
@@ -53,9 +54,12 @@
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
@@ -74,34 +78,461 @@
 #include <netiso/iso.h>
 #endif
 
-#define TRUE	1
-#define	FALSE	0
-
 /*
  * Data items converted to xdr at startup, since they are constant
  * This is kinda hokey, but may save a little time doing byte swaps
  */
-u_long nfs_procids[NFS_NPROCS];
 u_int32_t nfs_xdrneg1;
 u_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
-	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted,
 	rpc_auth_kerb;
-u_int32_t nfs_vers, nfs_prog, nfs_true, nfs_false;
+u_int32_t nfs_prog, nqnfs_prog, nfs_true, nfs_false;
 
 /* And other global data */
 static u_int32_t nfs_xid = 0;
-enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+		      NFCHR, NFNON };
+nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
+		      NFFIFO, NFNON };
+enum vtype nv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
+enum vtype nv3tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
+int nfs_ticks;
+
+/*
+ * Mapping of old NFS Version 2 RPC numbers to generic numbers.
+ */
+int nfsv3_procid[NFS_NPROCS] = {
+	NFSPROC_NULL,
+	NFSPROC_GETATTR,
+	NFSPROC_SETATTR,
+	NFSPROC_NOOP,
+	NFSPROC_LOOKUP,
+	NFSPROC_READLINK,
+	NFSPROC_READ,
+	NFSPROC_NOOP,
+	NFSPROC_WRITE,
+	NFSPROC_CREATE,
+	NFSPROC_REMOVE,
+	NFSPROC_RENAME,
+	NFSPROC_LINK,
+	NFSPROC_SYMLINK,
+	NFSPROC_MKDIR,
+	NFSPROC_RMDIR,
+	NFSPROC_READDIR,
+	NFSPROC_FSSTAT,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP,
+	NFSPROC_NOOP
+};
+
+/*
+ * and the reverse mapping from generic to Version 2 procedure numbers
+ */
+int nfsv2_procid[NFS_NPROCS] = {
+	NFSV2PROC_NULL,
+	NFSV2PROC_GETATTR,
+	NFSV2PROC_SETATTR,
+	NFSV2PROC_LOOKUP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_READLINK,
+	NFSV2PROC_READ,
+	NFSV2PROC_WRITE,
+	NFSV2PROC_CREATE,
+	NFSV2PROC_MKDIR,
+	NFSV2PROC_SYMLINK,
+	NFSV2PROC_CREATE,
+	NFSV2PROC_REMOVE,
+	NFSV2PROC_RMDIR,
+	NFSV2PROC_RENAME,
+	NFSV2PROC_LINK,
+	NFSV2PROC_READDIR,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_STATFS,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+	NFSV2PROC_NOOP,
+};
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Use NFSERR_IO as the catch all for ones not specifically defined in
+ * RFC 1094.
+ */
+static u_char nfsrv_v2errmap[ELAST] = {
+  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
+  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_NAMETOL,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
+  NFSERR_IO,
+};
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Although it is not obvious whether or not NFS clients really care if
+ * a returned error value is in the specified list for the procedure, the
+ * safest thing to do is filter them appropriately. For Version 2, the
+ * X/Open XNFS document is the only specification that defines error values
+ * for each RPC (The RFC simply lists all possible error values for all RPCs),
+ * so I have decided to not do this for Version 2.
+ * The first entry is the default error return and the rest are the valid
+ * errors for that RPC in increasing numeric order.
+ */
+static short nfsv3err_null[] = {
+	0,
+	0,
+};
+
+static short nfsv3err_getattr[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_setattr[] = {
+	NFSERR_IO,
+	NFSERR_PERM,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_INVAL,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOT_SYNC,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_lookup[] = {
+	NFSERR_IO,
+	NFSERR_NOENT,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_NOTDIR,
+	NFSERR_NAMETOL,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_access[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_readlink[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_INVAL,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_read[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_NXIO,
+	NFSERR_ACCES,
+	NFSERR_INVAL,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_write[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_INVAL,
+	NFSERR_FBIG,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_create[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_NOTDIR,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_mkdir[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_NOTDIR,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_symlink[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_NOTDIR,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_mknod[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_NOTDIR,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	NFSERR_BADTYPE,
+	0,
+};
+
+static short nfsv3err_remove[] = {
+	NFSERR_IO,
+	NFSERR_NOENT,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_NOTDIR,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_rmdir[] = {
+	NFSERR_IO,
+	NFSERR_NOENT,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_NOTDIR,
+	NFSERR_INVAL,
+	NFSERR_ROFS,
+	NFSERR_NAMETOL,
+	NFSERR_NOTEMPTY,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_rename[] = {
+	NFSERR_IO,
+	NFSERR_NOENT,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_XDEV,
+	NFSERR_NOTDIR,
+	NFSERR_ISDIR,
+	NFSERR_INVAL,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_MLINK,
+	NFSERR_NAMETOL,
+	NFSERR_NOTEMPTY,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_link[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_EXIST,
+	NFSERR_XDEV,
+	NFSERR_NOTDIR,
+	NFSERR_INVAL,
+	NFSERR_NOSPC,
+	NFSERR_ROFS,
+	NFSERR_MLINK,
+	NFSERR_NAMETOL,
+	NFSERR_DQUOT,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_NOTSUPP,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_readdir[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_NOTDIR,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_BAD_COOKIE,
+	NFSERR_TOOSMALL,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_readdirplus[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_ACCES,
+	NFSERR_NOTDIR,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_BAD_COOKIE,
+	NFSERR_NOTSUPP,
+	NFSERR_TOOSMALL,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_fsstat[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_fsinfo[] = {
+	NFSERR_STALE,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_pathconf[] = {
+	NFSERR_STALE,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short nfsv3err_commit[] = {
+	NFSERR_IO,
+	NFSERR_IO,
+	NFSERR_STALE,
+	NFSERR_BADHANDLE,
+	NFSERR_SERVERFAULT,
+	0,
+};
+
+static short *nfsrv_v3errmap[] = {
+	nfsv3err_null,
+	nfsv3err_getattr,
+	nfsv3err_setattr,
+	nfsv3err_lookup,
+	nfsv3err_access,
+	nfsv3err_readlink,
+	nfsv3err_read,
+	nfsv3err_write,
+	nfsv3err_create,
+	nfsv3err_mkdir,
+	nfsv3err_symlink,
+	nfsv3err_mknod,
+	nfsv3err_remove,
+	nfsv3err_rmdir,
+	nfsv3err_rename,
+	nfsv3err_link,
+	nfsv3err_readdir,
+	nfsv3err_readdirplus,
+	nfsv3err_fsstat,
+	nfsv3err_fsinfo,
+	nfsv3err_pathconf,
+	nfsv3err_commit,
+};
+
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
-extern int nqnfs_piggy[NFS_NPROCS];
 extern struct nfsrtt nfsrtt;
 extern time_t nqnfsstarttime;
-extern u_int32_t nqnfs_prog, nqnfs_vers;
 extern int nqsrv_clockskew;
 extern int nqsrv_writeslack;
 extern int nqsrv_maxlease;
+extern struct nfsstats nfsstats;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern nfstype nfsv2_type[9];
+extern nfstype nfsv3_type[9];
+extern struct nfsnodehashhead *nfsnodehashtbl;
+extern u_long nfsnodehash;
 
 LIST_HEAD(nfsnodehashhead, nfsnode);
-extern struct nfsnodehashhead *nfs_hash __P((nfsv2fh_t *));
 
 /*
  * Create the header for an rpc request packet
@@ -157,14 +588,16 @@
  * 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)
+nfsm_rpchead(cr, nmflag, procid, auth_type, auth_len, auth_str, verf_len,
+	verf_str, mrest, mrest_len, mbp, xidp)
 	register struct ucred *cr;
-	int nqnfs;
+	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;
@@ -178,15 +611,13 @@
 	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) {
+	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 if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) {
+		MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED);
 	} else {
-		MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+		MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
 	}
 	mb->m_len = 0;
 	mreq = mb;
@@ -195,20 +626,26 @@
 	/*
 	 * First the RPC header.
 	 */
-	nfsm_build(tl, u_int32_t *, 8*NFSX_UNSIGNED);
+	nfsm_build(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
 	if (++nfs_xid == 0)
 		nfs_xid++;
 	*tl++ = *xidp = txdr_unsigned(nfs_xid);
 	*tl++ = rpc_call;
 	*tl++ = rpc_vers;
-	if (nqnfs) {
+	if (nmflag & NFSMNT_NQNFS) {
 		*tl++ = txdr_unsigned(NQNFS_PROG);
-		*tl++ = txdr_unsigned(NQNFS_VER1);
+		*tl++ = txdr_unsigned(NQNFS_VER3);
 	} else {
 		*tl++ = txdr_unsigned(NFS_PROG);
-		*tl++ = txdr_unsigned(NFS_VER2);
+		if (nmflag & NFSMNT_NFSV3)
+			*tl++ = txdr_unsigned(NFS_VER3);
+		else
+			*tl++ = txdr_unsigned(NFS_VER2);
 	}
-	*tl++ = txdr_unsigned(procid);
+	if (nmflag & NFSMNT_NFSV3)
+		*tl++ = txdr_unsigned(procid);
+	else
+		*tl++ = txdr_unsigned(nfsv2_procid[procid]);
 
 	/*
 	 * And then the authorization cred.
@@ -227,10 +664,7 @@
 		for (i = 0; i < grpsiz; i++)
 			*tl++ = txdr_unsigned(cr->cr_groups[i]);
 		break;
-	case RPCAUTH_NQNFS:
-		nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-		*tl++ = txdr_unsigned(cr->cr_uid);
-		*tl = txdr_unsigned(auth_len);
+	case RPCAUTH_KERB4:
 		siz = auth_len;
 		while (siz > 0) {
 			if (M_TRAILINGSPACE(mb) == 0) {
@@ -256,11 +690,43 @@
 		}
 		break;
 	};
-	nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-	*tl++ = txdr_unsigned(RPCAUTH_NULL);
-	*tl = 0;
+
+	/*
+	 * And the verifier...
+	 */
+	nfsm_build(tl, u_int32_t *, 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(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(verf_str, bpos, i);
+			mb->m_len += i;
+			verf_str += i;
+			bpos += i;
+			siz -= i;
+		}
+		if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
+			for (i = 0; i < siz; i++)
+				*bpos++ = '\0';
+			mb->m_len += siz;
+		}
+	} else {
+		*tl++ = txdr_unsigned(RPCAUTH_NULL);
+		*tl = 0;
+	}
 	mb->m_next = mrest;
-	mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+	mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
 	mreq->m_pkthdr.rcvif = (struct ifnet *)0;
 	*mbp = mb;
 	return (mreq);
@@ -593,6 +1059,28 @@
 {
 	register int i;
 
+#if !defined(alpha) && defined(DIAGNOSTIC)
+	/*
+	 * Check to see if major data structures haven't bloated.
+	 */
+	if (sizeof (struct nfsnode) > NFS_NODEALLOC) {
+		printf("struct nfsnode bloated (> %dbytes)\n", NFS_NODEALLOC);
+		printf("Try reducing NFS_SMALLFH\n");
+	}
+	if (sizeof (struct nfsmount) > NFS_MNTALLOC) {
+		printf("struct nfsmount bloated (> %dbytes)\n", NFS_MNTALLOC);
+		printf("Try reducing NFS_MUIDHASHSIZ\n");
+	}
+	if (sizeof (struct nfssvc_sock) > NFS_SVCALLOC) {
+		printf("struct nfssvc_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
+		printf("Try reducing NFS_UIDHASHSIZ\n");
+	}
+	if (sizeof (struct nfsuid) > NFS_UIDALLOC) {
+		printf("struct nfsuid bloated (> %dbytes)\n",NFS_UIDALLOC);
+		printf("Try unionizing the nu_nickname and nu_flag fields\n");
+	}
+#endif
+
 	nfsrtt.pos = 0;
 	rpc_vers = txdr_unsigned(RPC_VER2);
 	rpc_call = txdr_unsigned(RPC_CALL);
@@ -601,17 +1089,16 @@
 	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_kerb = txdr_unsigned(RPCAUTH_NQNFS);
-	nfs_vers = txdr_unsigned(NFS_VER2);
+	rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
 	nfs_prog = txdr_unsigned(NFS_PROG);
+	nqnfs_prog = txdr_unsigned(NQNFS_PROG);
 	nfs_true = txdr_unsigned(TRUE);
 	nfs_false = txdr_unsigned(FALSE);
 	nfs_xdrneg1 = txdr_unsigned(-1);
-	/* Loop thru nfs procids */
-	for (i = 0; i < NFS_NPROCS; i++)
-		nfs_procids[i] = txdr_unsigned(i);
+	nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
+	if (nfs_ticks < 1)
+		nfs_ticks = 1;
 #ifdef NFSCLIENT
 	/* Ensure async daemons disabled */
 	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
@@ -631,8 +1118,6 @@
 		nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
 			+ nqsrv_clockskew + nqsrv_writeslack;
 		NQLOADNOVRAM(nqnfsstarttime);
-		nqnfs_prog = txdr_unsigned(NQNFS_PROG);
-		nqnfs_vers = txdr_unsigned(NQNFS_VER1);
 		CIRCLEQ_INIT(&nqtimerhead);
 		nqfhhashtbl = hashinit(NQLCHSZ, M_NQLEASE, &nqfhhash);
 	}
@@ -668,39 +1153,47 @@
 {
 	register struct vnode *vp = *vpp;
 	register struct vattr *vap;
-	register struct nfsv2_fattr *fp;
+	register struct nfs_fattr *fp;
 	extern int (**spec_nfsv2nodeop_p) __P((void *));
 	register struct nfsnode *np;
-	register struct nfsnodehashhead *nhpp;
 	register int32_t t1;
-	caddr_t dpos, cp2;
-	int error = 0, isnq;
+	caddr_t cp2;
+	int error = 0;
+	int32_t rdev;
 	struct mbuf *md;
 	enum vtype vtyp;
 	u_short vmode;
-	long rdev;
 	struct timespec mtime;
 	struct vnode *nvp;
+	int v3 = NFS_ISV3(vp);
 
 	md = *mdp;
-	dpos = *dposp;
-	t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
-	isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
-	error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2);
+	t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
+	error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2);
 	if (error)
 		return (error);
-	fp = (struct nfsv2_fattr *)cp2;
-	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(int32_t, fp->fa_nqrdev);
-		fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+	fp = (struct nfs_fattr *)cp2;
+	if (v3) {
+		vtyp = nfsv3tov_type(fp->fa_type);
+		vmode = fxdr_unsigned(u_short, fp->fa_mode);
+		rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
+			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
+		fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
 	} else {
-		rdev = fxdr_unsigned(int32_t, fp->fa_nfsrdev);
-		fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+		vtyp = nfsv2tov_type(fp->fa_type);
+		vmode = fxdr_unsigned(u_short, fp->fa_mode);
+		if (vtyp == VNON || vtyp == VREG)
+			vtyp = IFTOVT(vmode);
+		rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
+		fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
+
+		/*
+		 * Really ugly NFSv2 kludge.
+		 */
+		if (vtyp == VCHR && rdev == 0xffffffff)
+			vtyp = VFIFO;
 	}
+
 	/*
 	 * 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 
@@ -710,17 +1203,10 @@
 	 */
 	np = VTONFS(vp);
 	if (vp->v_type == VNON) {
-		if (vtyp == VCHR && rdev == 0xffffffff)
-			vp->v_type = vtyp = VFIFO;
-		else
-			vp->v_type = vtyp;
+		vp->v_type = vtyp;
 		if (vp->v_type == VFIFO) {
-#ifdef FIFO
 			extern int (**fifo_nfsv2nodeop_p) __P((void *));
 			vp->v_op = fifo_nfsv2nodeop_p;
-#else
-			return (EOPNOTSUPP);
-#endif /* FIFO */
 		}
 		if (vp->v_type == VCHR || vp->v_type == VBLK) {
 			vp->v_op = spec_nfsv2nodeop_p;
@@ -728,8 +1214,13 @@
 			if (nvp) {
 				/*
 				 * Discard unneeded vnode, but save its nfsnode.
+				 * Since the nfsnode does not have a lock, its
+				 * vnode lock has to be carried over.
 				 */
-				LIST_REMOVE(np, n_hash);
+#ifdef Lite2_integrated
+				nvp->v_vnlock = vp->v_vnlock;
+				vp->v_vnlock = NULL;
+#endif
 				nvp->v_data = vp->v_data;
 				vp->v_data = NULL;
 				vp->v_op = spec_vnodeop_p;
@@ -739,8 +1230,6 @@
 				 * Reinitialize aliased node.
 				 */
 				np->n_vnode = nvp;
-				nhpp = nfs_hash(&np->n_fh);
-				LIST_INSERT_HEAD(nhpp, np, n_hash);
 				*vpp = vp = nvp;
 			}
 		}
@@ -749,31 +1238,37 @@
 	vap = &np->n_vattr;
 	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_rdev = (dev_t)rdev;
 	vap->va_mtime = mtime;
 	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(int32_t, fp->fa_nqblocksize);
-		fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
-		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa_nqfileid);
-		fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
-		vap->va_flags = fxdr_unsigned(u_int32_t, fp->fa_nqflags);
-		fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
-		vap->va_gen = fxdr_unsigned(u_int32_t, fp->fa_nqgen);
-		fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+	if (v3) {
+		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);
+		fxdr_hyper(&fp->fa3_size, &vap->va_size);
+		vap->va_blocksize = NFS_FABLKSIZE;
+		fxdr_hyper(&fp->fa3_used, &vap->va_bytes);
+		vap->va_fileid = fxdr_unsigned(int32_t,
+		    fp->fa3_fileid.nfsuquad[1]);
+		fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
+		fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
+		vap->va_flags = 0;
+		vap->va_filerev = 0;
 	} else {
-		vap->va_size = fxdr_unsigned(u_int32_t, fp->fa_nfssize);
-		vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa_nfsblocksize);
-		vap->va_bytes = fxdr_unsigned(int32_t, fp->fa_nfsblocks) * NFS_FABLKSIZE;
-		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa_nfsfileid);
-		fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+		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_int32_t, fp->fa2_size);
+		vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
+		vap->va_bytes = fxdr_unsigned(int32_t, fp->fa2_blocks)
+		    * NFS_FABLKSIZE;
+		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
+		fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
 		vap->va_flags = 0;
-		fxdr_nfstime(&fp->fa_nfsctime, &vap->va_ctime);
-		vap->va_gen = 0;
+		vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
+		    fp->fa2_ctime.nfsv2_sec);
+		vap->va_ctime.tv_nsec = 0;
+		vap->va_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
 		vap->va_filerev = 0;
 	}
 	if (vap->va_size != np->n_size) {
@@ -790,26 +1285,13 @@
 			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));
-#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;
-#endif
 		if (np->n_flag & NCHG) {
-			if (np->n_flag & NACC) {
-				vaper->va_atime.tv_sec = np->n_atim.tv_sec;
-				vaper->va_atime.tv_nsec =
-				    np->n_atim.tv_usec * 1000;
-			}
-			if (np->n_flag & NUPD) {
-				vaper->va_mtime.tv_sec = np->n_mtim.tv_sec;
-				vaper->va_mtime.tv_nsec =
-				    np->n_mtim.tv_usec * 1000;
-			}
+			if (np->n_flag & NACC)
+				vaper->va_atime = np->n_atim;
+			if (np->n_flag & NUPD)
+				vaper->va_mtime = np->n_mtim;
 		}
 	}
 	return (0);
@@ -828,12 +1310,7 @@
 	register struct nfsnode *np = VTONFS(vp);
 	register struct vattr *vap;
 
-	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)) {
+	if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
 		nfsstats.attrcache_misses++;
 		return (ENOENT);
 	}
@@ -853,33 +1330,21 @@
 			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.tv_sec = np->n_atim.tv_sec;
-			vaper->va_atime.tv_nsec = np->n_atim.tv_usec * 1000;
-		}
-		if (np->n_flag & NUPD) {
-			vaper->va_mtime.tv_sec = np->n_mtim.tv_sec;
-			vaper->va_mtime.tv_nsec = np->n_mtim.tv_usec * 1000;
-		}
+		if (np->n_flag & NACC)
+			vaper->va_atime = np->n_atim;
+		if (np->n_flag & NUPD)
+			vaper->va_mtime = np->n_mtim;
 	}
 	return (0);
 }
-#endif
+#endif /* NFSCLIENT */
 
 /*
  * Set up nameidata for a lookup() call and do it
  */
 int
-nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
 	register struct nameidata *ndp;
 	fhandle_t *fhp;
 	int len;
@@ -887,7 +1352,9 @@
 	struct mbuf *nam;
 	struct mbuf **mdp;
 	caddr_t *dposp;
+	struct vnode **retdirp;
 	struct proc *p;
+	int kerbflag;
 {
 	register int i, rem;
 	register struct mbuf *md;
@@ -896,6 +1363,7 @@
 	int error, rdonly;
 	struct componentname *cnp = &ndp->ni_cnd;
 
+	*retdirp = (struct vnode *)0;
 	MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
 	/*
 	 * Copy the name from the mbuf list to ndp->ni_pnbuf
@@ -916,7 +1384,7 @@
 			rem = md->m_len;
 		}
 		if (*fromcp == '\0' || *fromcp == '/') {
-			error = EINVAL;
+			error = EACCES;
 			goto out;
 		}
 		*tocp++ = *fromcp++;
@@ -938,7 +1406,7 @@
 	 * Extract and set starting directory.
 	 */
 	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
-			     nam, &rdonly);
+	    nam, &rdonly, kerbflag);
 	if (error)
 		goto out;
 	if (dp->v_type != VDIR) {
@@ -946,6 +1414,8 @@
 		error = ENOTDIR;
 		goto out;
 	}
+	VREF(dp);
+	*retdirp = dp;
 	ndp->ni_startdir = dp;
 	if (rdonly)
 		cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
@@ -955,7 +1425,8 @@
 	 * And call lookup() to do the real work
 	 */
 	cnp->cn_proc = p;
-	if ((error = lookup(ndp)) != 0)
+	error = lookup(ndp);
+	if (error)
 		goto out;
 	/*
 	 * Check for encountering a symbolic link
@@ -1040,11 +1511,114 @@
 		}
 		count -= m->m_len;
 	}
-	while ((m = m->m_next) != NULL)
+	for (m = m->m_next;m;m = m->m_next)
 		m->m_len = 0;
 }
 
 /*
+ * Make these functions instead of macros, so that the kernel text size
+ * doesn't get too big...
+ */
+void
+nfsm_srvwcc(nfsd, before_ret, before_vap, after_ret, after_vap, mbp, bposp)
+	struct nfsrv_descript *nfsd;
+	int before_ret;
+	register struct vattr *before_vap;
+	int after_ret;
+	struct vattr *after_vap;
+	struct mbuf **mbp;
+	char **bposp;
+{
+	register struct mbuf *mb = *mbp, *mb2;
+	register char *bpos = *bposp;
+	register u_int32_t *tl;
+
+	if (before_ret) {
+		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+		*tl = nfs_false;
+	} else {
+		nfsm_build(tl, u_int32_t *, 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);
+	}
+	*bposp = bpos;
+	*mbp = mb;
+	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
+}
+
+void
+nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp)
+	struct nfsrv_descript *nfsd;
+	int after_ret;
+	struct vattr *after_vap;
+	struct mbuf **mbp;
+	char **bposp;
+{
+	register struct mbuf *mb = *mbp, *mb2;
+	register char *bpos = *bposp;
+	register u_int32_t *tl;
+	register struct nfs_fattr *fp;
+
+	if (after_ret) {
+		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+		*tl = nfs_false;
+	} else {
+		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
+		*tl++ = nfs_true;
+		fp = (struct nfs_fattr *)tl;
+		nfsm_srvfattr(nfsd, after_vap, fp);
+	}
+	*mbp = mb;
+	*bposp = bpos;
+}
+
+void
+nfsm_srvfattr(nfsd, vap, fp)
+	register struct nfsrv_descript *nfsd;
+	register struct vattr *vap;
+	register struct nfs_fattr *fp;
+{
+
+	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(major(vap->va_rdev));
+		fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
+		fp->fa3_fsid.nfsuquad[0] = 0;
+		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
+		fp->fa3_fileid.nfsuquad[0] = 0;
+		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_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(vap->va_rdev);
+		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);
+	}
+}
+
+/*
  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
  * 	- look up fsid in mount list (if not found ret error)
  *	- get vp and export rights by calling VFS_FHTOVP()
@@ -1052,7 +1626,7 @@
  *	- if not lockflag unlock it with VOP_UNLOCK()
  */
 int
-nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
 	fhandle_t *fhp;
 	int lockflag;
 	struct vnode **vpp;
@@ -1060,15 +1634,23 @@
 	struct nfssvc_sock *slp;
 	struct mbuf *nam;
 	int *rdonlyp;
+	int kerbflag;
 {
+#ifdef Lite2_integrated
+	struct proc *p = curproc;	/* XXX */
+#endif
 	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)
+#ifdef Lite2_integrated
+	mp = vfs_getvfs(&fhp->fh_fsid);
+#else
+	mp = getvfs(&fhp->fh_fsid);
+#endif
+	if (!mp)
 		return (ESTALE);
 	error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon);
 	if (error)
@@ -1077,20 +1659,13 @@
 	 * Check/setup credentials.
 	 */
 	if (exflags & MNT_EXKERB) {
-		for (uidp = NUIDHASH(slp, cred->cr_uid)->lh_first; uidp != 0;
-		    uidp = uidp->nu_hash.le_next) {
-			if (uidp->nu_uid == cred->cr_uid)
-				break;
-		}
-		if (uidp == 0) {
+		if (!kerbflag) {
 			vput(*vpp);
-			return (NQNFS_AUTHERR);
+			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
 		}
-		cred->cr_uid = uidp->nu_cr.cr_uid;
-		cred->cr_gid = uidp->nu_cr.cr_gid;
-		for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
-			cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
-		cred->cr_ngroups = i;
+	} else if (kerbflag) {
+		vput(*vpp);
+		return (NFSERR_AUTHERR | AUTH_TOOWEAK);
 	} else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
 		cred->cr_uid = credanon->cr_uid;
 		cred->cr_gid = credanon->cr_gid;
@@ -1103,7 +1678,11 @@
 	else
 		*rdonlyp = 0;
 	if (!lockflag)
+#ifdef Lite2_integrated
+		VOP_UNLOCK(*vpp, 0, p);
+#else
 		VOP_UNLOCK(*vpp);
+#endif
 	return (0);
 }
 
@@ -1149,3 +1728,184 @@
 	};
 	return (0);
 }
+
+static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
+/*
+ * This function finds the directory cookie that corresponds to the
+ * logical byte offset given.
+ */
+nfsuint64 *
+nfs_getcookie(np, off, add)
+	register struct nfsnode *np;
+	off_t off;
+	int add;
+{
+	register struct nfsdmap *dp, *dp2;
+	register int pos;
+
+	pos = off / NFS_DIRBLKSIZ;
+	if (pos == 0) {
+#ifdef DIAGNOSTIC
+		if (add)
+			panic("nfs getcookie add at 0");
+#endif
+		return (&nfs_nullcookie);
+	}
+	pos--;
+	dp = np->n_cookies.lh_first;
+	if (!dp) {
+		if (add) {
+			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
+				M_NFSDIROFF, M_WAITOK);
+			dp->ndm_eocookie = 0;
+			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
+		} else
+			return ((nfsuint64 *)0);
+	}
+	while (pos >= NFSNUMCOOKIES) {
+		pos -= NFSNUMCOOKIES;
+		if (dp->ndm_list.le_next) {
+			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
+				pos >= dp->ndm_eocookie)
+				return ((nfsuint64 *)0);
+			dp = dp->ndm_list.le_next;
+		} else if (add) {
+			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
+				M_NFSDIROFF, M_WAITOK);
+			dp2->ndm_eocookie = 0;
+			LIST_INSERT_AFTER(dp, dp2, ndm_list);
+			dp = dp2;
+		} else
+			return ((nfsuint64 *)0);
+	}
+	if (pos >= dp->ndm_eocookie) {
+		if (add)
+			dp->ndm_eocookie = pos + 1;
+		else
+			return ((nfsuint64 *)0);
+	}
+	return (&dp->ndm_cookies[pos]);
+}
+
+/*
+ * Invalidate cached directory information, except for the actual directory
+ * blocks (which are invalidated separately).
+ * Done mainly to avoid the use of stale offset cookies.
+ */
+void
+nfs_invaldir(vp)
+	register struct vnode *vp;
+{
+	register struct nfsnode *np = VTONFS(vp);
+
+#ifdef DIAGNOSTIC
+	if (vp->v_type != VDIR)
+		panic("nfs: invaldir not dir");
+#endif
+	np->n_direofoffset = 0;
+	np->n_cookieverf.nfsuquad[0] = 0;
+	np->n_cookieverf.nfsuquad[1] = 0;
+	if (np->n_cookies.lh_first)
+		np->n_cookies.lh_first->ndm_eocookie = 0;
+}
+
+/*
+ * The write verifier has changed (probably due to a server reboot), so all
+ * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
+ * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
+ * flag. Once done the new write verifier can be set for the mount point.
+ */
+void
+nfs_clearcommit(mp)
+	struct mount *mp;
+{
+	register struct vnode *vp, *nvp;
+	register struct buf *bp, *nbp;
+	int s;
+
+	s = splbio();
+loop:
+	for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
+		if (vp->v_mount != mp)	/* Paranoia */
+			goto loop;
+		nvp = vp->v_mntvnodes.le_next;
+		for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+			nbp = bp->b_vnbufs.le_next;
+			if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
+				== (B_DELWRI | B_NEEDCOMMIT))
+				bp->b_flags &= ~B_NEEDCOMMIT;
+		}
+	}
+	splx(s);
+}
+
+/*
+ * Map errnos to NFS error numbers. For Version 3 also filter out error
+ * numbers not specified for the associated procedure.
+ */
+int
+nfsrv_errmap(nd, err)
+	struct nfsrv_descript *nd;
+	register int err;
+{
+	register short *defaulterrp, *errp;
+
+	if (nd->nd_flag & ND_NFSV3) {
+	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
+		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
+		while (*++errp) {
+			if (*errp == err)
+				return (err);
+			else if (*errp > err)
+				break;
+		}
+		return ((int)*defaulterrp);
+	    } else
+		return (err & 0xffff);
+	}
+	if (err <= ELAST)
+		return ((int)nfsrv_v2errmap[err - 1]);
+	return (NFSERR_IO);
+}
+
+/*
+ * Sort the group list in increasing numerical order.
+ * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
+ *  that used to be here.)
+ */
+void
+nfsrvw_sort(list, num)
+        register gid_t *list;
+        register int num;
+{
+	register int i, j;
+	gid_t v;
+
+	/* Insertion sort. */
+	for (i = 1; i < num; i++) {
+		v = list[i];
+		/* find correct slot for value v, moving others up */
+		for (j = i; --j >= 0 && v < list[j];)
+			list[j + 1] = list[j];
+		list[j + 1] = v;
+	}
+}
+
+/*
+ * copy credentials making sure that the result can be compared with bcmp().
+ */
+void
+nfsrv_setcred(incred, outcred)
+	register struct ucred *incred, *outcred;
+{
+	register int i;
+
+	bzero((caddr_t)outcred, sizeof (struct ucred));
+	outcred->cr_ref = 1;
+	outcred->cr_uid = incred->cr_uid;
+	outcred->cr_gid = incred->cr_gid;
+	outcred->cr_ngroups = incred->cr_ngroups;
+	for (i = 0; i < incred->cr_ngroups; i++)
+		outcred->cr_groups[i] = incred->cr_groups[i];
+	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
+}
--- a/sys/nfs/nfs_syscalls.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_syscalls.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_syscalls.c,v 1.18 1996/02/09 21:48:36 christos Exp $	*/
+/*	$NetBSD: nfs_syscalls.c,v 1.19 1996/02/18 11:53:52 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,7 +35,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_syscalls.c	8.3 (Berkeley) 1/4/94
+ *	@(#)nfs_syscalls.c	8.5 (Berkeley) 3/30/95
  */
 
 #include <sys/param.h>
@@ -65,9 +65,11 @@
 #ifdef ISO
 #include <netiso/iso.h>
 #endif
+#include <nfs/xdr_subs.h>
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
+#include <nfs/nfsm_subs.h>
 #include <nfs/nfsrvcache.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nfsnode.h>
@@ -75,17 +77,19 @@
 #include <nfs/nfsrtt.h>
 #include <nfs/nfs_var.h>
 
+void	nfsrv_zapsock	__P((struct nfssvc_sock *));
+
 /* Global defs. */
-extern u_int32_t nfs_prog, nfs_vers;
-extern int32_t (*nfsrv_procs[NFS_NPROCS]) __P((struct nfsd *, struct mbuf *,
-					       struct mbuf *, caddr_t,
-					       struct ucred *, struct mbuf *,
-					       struct mbuf **));
+extern int32_t (*nfsrv3_procs[NFS_NPROCS]) __P((struct nfsrv_descript *,
+						struct nfssvc_sock *,
+						struct proc *, struct mbuf **));
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 extern int nfs_numasync;
 extern time_t nqnfsstarttime;
 extern int nqsrv_writeslack;
 extern int nfsrtton;
+extern struct nfsstats nfsstats;
+extern int nfsrvw_procrastinate;
 struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
 int nuidhash_max = NFS_MAXUIDHASH;
 int nfsd_waiting = 0;
@@ -100,10 +104,11 @@
 #define	FALSE	0
 
 static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
+
 #ifdef NFSSERVER
-static void nfsd_rt __P((struct timeval *, int, struct nfsd *, struct mbuf *,
-			 int));
+static void nfsd_rt __P((int, struct nfsrv_descript *, int));
 #endif
+
 /*
  * NFS server system calls
  * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
@@ -130,11 +135,13 @@
 	/*
 	 * Must be super user
 	 */
-	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
+	error = suser(p->p_ucred, &p->p_acflag);
+	if(error)
 		return (error);
 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
 	    SCARG(uap, fname), p);
-	if ((error = namei(&nd)) != 0)
+	error = namei(&nd);
+	if (error)
 		return (error);
 	vp = nd.ni_vp;
 	bzero((caddr_t)&fh, sizeof(fh));
@@ -148,7 +155,7 @@
 }
 
 /*
- * Nfs server psuedo system call for the nfsd's
+ * Nfs server pseudo system call for the nfsd's
  * Based on the flag value it either:
  * - adds a socket to the selection list
  * - remains in the kernel as an nfsd
@@ -167,6 +174,9 @@
 	struct nameidata nd;
 	struct nfsmount *nmp;
 	int error;
+#ifdef NFSCLIENT
+	struct nfsd_cargs ncd;
+#endif
 #ifdef NFSSERVER
 	struct file *fp;
 	struct mbuf *nam;
@@ -176,33 +186,34 @@
 	struct nfssvc_sock *slp;
 	struct nfsuid *nuidp;
 #endif
-	struct nfsd_cargs ncd;
 
 	/*
 	 * Must be super user
 	 */
-	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
+	error = suser(p->p_ucred, &p->p_acflag);
+	if(error)
 		return (error);
 	while (nfssvc_sockhead_flag & SLP_INIT) {
-		nfssvc_sockhead_flag |= SLP_WANTINIT;
+		 nfssvc_sockhead_flag |= SLP_WANTINIT;
 		(void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
 	}
 	if (SCARG(uap, flag) & NFSSVC_BIOD) {
-#ifndef NFSCLIENT
+#ifdef NFSCLIENT
+		error = nfssvc_iod(p);
+#else
 		error = ENOSYS;
-#else /* !NFSCLIENT */
-		error = nfssvc_iod(p);
-#endif /* !NFSCLIENT */
+#endif
 	} else if (SCARG(uap, flag) & NFSSVC_MNTD) {
 #ifndef NFSCLIENT
 		error = ENOSYS;
-#else /* !NFSCLIENT */
+#else
 		error = copyin(SCARG(uap, argp), (caddr_t)&ncd, sizeof (ncd));
 		if (error)
 			return (error);
 		NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
 			ncd.ncd_dirp, p);
-		if ((error = namei(&nd)) != 0)
+		error = namei(&nd);
+		if (error)
 			return (error);
 		if ((nd.ni_vp->v_flag & VROOT) == 0)
 			error = EINVAL;
@@ -211,21 +222,22 @@
 		if (error)
 			return (error);
 		if ((nmp->nm_flag & NFSMNT_MNTD) &&
-		    (SCARG(uap, flag) & NFSSVC_GOTAUTH) == 0)
+			(SCARG(uap, flag) & NFSSVC_GOTAUTH) == 0)
 			return (0);
 		nmp->nm_flag |= NFSMNT_MNTD;
 		error = nqnfs_clientd(nmp, p->p_ucred, &ncd, SCARG(uap, flag),
-		    SCARG(uap, argp), p);
-#endif /* !NFSCLIENT */
+			SCARG(uap, argp), p);
+#endif /* NFSCLIENT */
 	} else if (SCARG(uap, flag) & NFSSVC_ADDSOCK) {
 #ifndef NFSSERVER
 		error = ENOSYS;
-#else /* !NFSSERVER */
+#else
 		error = copyin(SCARG(uap, argp), (caddr_t)&nfsdarg,
-			       sizeof(nfsdarg));
+		    sizeof(nfsdarg));
 		if (error)
 			return (error);
-		if ((error = getsock(p->p_fd, nfsdarg.sock, &fp)) != 0)
+		error = getsock(p->p_fd, nfsdarg.sock, &fp);
+		if (error)
 			return (error);
 		/*
 		 * Get the client address for connected sockets.
@@ -234,7 +246,7 @@
 			nam = (struct mbuf *)0;
 		else {
 			error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
-					 MT_SONAME);
+				MT_SONAME);
 			if (error)
 				return (error);
 		}
@@ -243,25 +255,31 @@
 	} else {
 #ifndef NFSSERVER
 		error = ENOSYS;
-#else /* !NFSSERVER */
+#else
 		error = copyin(SCARG(uap, argp), (caddr_t)nsd, sizeof (*nsd));
 		if (error)
 			return (error);
 		if ((SCARG(uap, flag) & NFSSVC_AUTHIN) &&
-		    (nfsd = nsd->nsd_nfsd) &&
-		    (nfsd->nd_slp->ns_flag & SLP_VALID)) {
-			slp = nfsd->nd_slp;
+		    ((nfsd = nsd->nsd_nfsd)) != NULL &&
+		    (nfsd->nfsd_slp->ns_flag & SLP_VALID)) {
+			slp = nfsd->nfsd_slp;
 
 			/*
 			 * First check to see if another nfsd has already
 			 * added this credential.
 			 */
-			for (nuidp = NUIDHASH(slp, nsd->nsd_uid)->lh_first;
+			for (nuidp = NUIDHASH(slp,nsd->nsd_cr.cr_uid)->lh_first;
 			    nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
-				if (nuidp->nu_uid == nsd->nsd_uid)
+				if (nuidp->nu_cr.cr_uid == nsd->nsd_cr.cr_uid &&
+				    (!nfsd->nfsd_nd->nd_nam2 ||
+				     netaddr_match(NU_NETFAM(nuidp),
+				     &nuidp->nu_haddr, nfsd->nfsd_nd->nd_nam2)))
 					break;
 			}
-			if (nuidp == 0) {
+			if (nuidp) {
+			    nfsrv_setcred(&nuidp->nu_cr,&nfsd->nfsd_nd->nd_cr);
+			    nfsd->nfsd_nd->nd_flag |= ND_KERBFULL;
+			} else {
 			    /*
 			     * Nope, so we will.
 			     */
@@ -281,22 +299,54 @@
 				    LIST_REMOVE(nuidp, nu_hash);
 				    TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp,
 					nu_lru);
+				    if (nuidp->nu_flag & NU_NAM)
+					m_freem(nuidp->nu_nam);
 			        }
+				nuidp->nu_flag = 0;
 				nuidp->nu_cr = nsd->nsd_cr;
 				if (nuidp->nu_cr.cr_ngroups > NGROUPS)
-					nuidp->nu_cr.cr_ngroups = NGROUPS;
+				    nuidp->nu_cr.cr_ngroups = NGROUPS;
 				nuidp->nu_cr.cr_ref = 1;
-				nuidp->nu_uid = nsd->nsd_uid;
+				nuidp->nu_timestamp = nsd->nsd_timestamp;
+				nuidp->nu_expire = time.tv_sec + nsd->nsd_ttl;
+				/*
+				 * and save the session key in nu_key.
+				 */
+				bcopy(nsd->nsd_key, nuidp->nu_key,
+				    sizeof (nsd->nsd_key));
+				if (nfsd->nfsd_nd->nd_nam2) {
+				    struct sockaddr_in *saddr;
+
+				    saddr = mtod(nfsd->nfsd_nd->nd_nam2,
+					 struct sockaddr_in *);
+				    switch (saddr->sin_family) {
+				    case AF_INET:
+					nuidp->nu_flag |= NU_INETADDR;
+					nuidp->nu_inetaddr =
+					     saddr->sin_addr.s_addr;
+					break;
+				    case AF_ISO:
+				    default:
+					nuidp->nu_flag |= NU_NAM;
+					nuidp->nu_nam = m_copym(
+					    nfsd->nfsd_nd->nd_nam2, 0,
+					     M_COPYALL, M_WAIT);
+					break;
+				    };
+				}
 				TAILQ_INSERT_TAIL(&slp->ns_uidlruhead, nuidp,
-				    nu_lru);
+					nu_lru);
 				LIST_INSERT_HEAD(NUIDHASH(slp, nsd->nsd_uid),
-				    nuidp, nu_hash);
+					nuidp, nu_hash);
+				nfsrv_setcred(&nuidp->nu_cr,
+				    &nfsd->nfsd_nd->nd_cr);
+				nfsd->nfsd_nd->nd_flag |= ND_KERBFULL;
 			    }
 			}
 		}
 		if ((SCARG(uap, flag) & NFSSVC_AUTHINFAIL) &&
 		    (nfsd = nsd->nsd_nfsd))
-			nfsd->nd_flag |= NFSD_AUTHFAIL;
+			nfsd->nfsd_flag |= NFSD_AUTHFAIL;
 		error = nfssvc_nfsd(nsd, SCARG(uap, argp), p);
 #endif /* !NFSSERVER */
 	}
@@ -342,10 +392,11 @@
 #endif /* ISO */
 	}
 	if (so->so_type == SOCK_STREAM)
-		siz = NFS_MAXPACKET + sizeof (u_int32_t);
+		siz = NFS_MAXPACKET + sizeof (u_long);
 	else
 		siz = NFS_MAXPACKET;
-	if ((error = soreserve(so, siz, siz)) != 0) {
+	error = soreserve(so, siz, siz); 
+	if (error) {
 		m_freem(mynam);
 		return (error);
 	}
@@ -357,15 +408,15 @@
 	 */
 	if (so->so_type == SOCK_STREAM) {
 		MGET(m, M_WAIT, MT_SOOPTS);
-		*mtod(m, int *) = 1;
-		m->m_len = sizeof(int);
+		*mtod(m, int32_t *) = 1;
+		m->m_len = sizeof(int32_t);
 		sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
 	}
 	if (so->so_proto->pr_domain->dom_family == AF_INET &&
 	    so->so_proto->pr_protocol == IPPROTO_TCP) {
 		MGET(m, M_WAIT, MT_SOOPTS);
-		*mtod(m, int *) = 1;
-		m->m_len = sizeof(int);
+		*mtod(m, int32_t *) = 1;
+		m->m_len = sizeof(int32_t);
 		sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
 	}
 	so->so_rcv.sb_flags &= ~SB_NOINTR;
@@ -378,8 +429,6 @@
 		slp = (struct nfssvc_sock *)
 			malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
 		bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
-		slp->ns_uidhashtbl =
-		    hashinit(NUIDHASHSIZ, M_NFSSVC, &slp->ns_uidhash);
 		TAILQ_INIT(&slp->ns_uidlruhead);
 		TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain);
 	}
@@ -406,44 +455,46 @@
 	caddr_t argp;
 	struct proc *p;
 {
-	register struct mbuf *m, *nam2;
+	register struct mbuf *m;
 	register int siz;
 	register struct nfssvc_sock *slp;
 	register struct socket *so;
 	register int *solockp;
-	struct nfsd *nd = nsd->nsd_nfsd;
-	struct mbuf *mreq, *nam;
-	struct timeval starttime;
-	struct nfsuid *uidp;
-	int error = 0, cacherep, s;
-	int sotype;
+	struct nfsd *nfsd = nsd->nsd_nfsd;
+	struct nfsrv_descript *nd = NULL;
+	struct mbuf *mreq;
+	int error = 0, cacherep, s, sotype, writes_todo;
+	u_quad_t cur_usec;
 
+#ifndef nolint
+	cacherep = RC_DOIT;
+	writes_todo = 0;
+#endif
 	s = splsoftnet();
-	if (nd == (struct nfsd *)0) {
-		nsd->nsd_nfsd = nd = (struct nfsd *)
+	if (nfsd == (struct nfsd *)0) {
+		nsd->nsd_nfsd = nfsd = (struct nfsd *)
 			malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
-		bzero((caddr_t)nd, sizeof (struct nfsd));
-		nd->nd_procp = p;
-		nd->nd_cr.cr_ref = 1;
-		TAILQ_INSERT_TAIL(&nfsd_head, nd, nd_chain);
-		nd->nd_nqlflag = NQL_NOVAL;
+		bzero((caddr_t)nfsd, sizeof (struct nfsd));
+		nfsd->nfsd_procp = p;
+		TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain);
 		nfs_numnfsd++;
 	}
 	/*
 	 * Loop getting rpc requests until SIGKILL.
 	 */
 	for (;;) {
-		if ((nd->nd_flag & NFSD_REQINPROG) == 0) {
-			while (nd->nd_slp == (struct nfssvc_sock *)0 &&
+		if ((nfsd->nfsd_flag & NFSD_REQINPROG) == 0) {
+			while (nfsd->nfsd_slp == (struct nfssvc_sock *)0 &&
 			    (nfsd_head_flag & NFSD_CHECKSLP) == 0) {
-				nd->nd_flag |= NFSD_WAITING;
+				nfsd->nfsd_flag |= NFSD_WAITING;
 				nfsd_waiting++;
-				error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0);
+				error = tsleep((caddr_t)nfsd, PSOCK | PCATCH,
+				    "nfsd", 0);
 				nfsd_waiting--;
 				if (error)
 					goto done;
 			}
-			if (nd->nd_slp == (struct nfssvc_sock *)0 &&
+			if (nfsd->nfsd_slp == (struct nfssvc_sock *)0 &&
 			    (nfsd_head_flag & NFSD_CHECKSLP) != 0) {
 				for (slp = nfssvc_sockhead.tqh_first; slp != 0;
 				    slp = slp->ns_chain.tqe_next) {
@@ -451,14 +502,14 @@
 					== (SLP_VALID | SLP_DOREC)) {
 					    slp->ns_flag &= ~SLP_DOREC;
 					    slp->ns_sref++;
-					    nd->nd_slp = slp;
+					    nfsd->nfsd_slp = slp;
 					    break;
 				    }
 				}
 				if (slp == 0)
 					nfsd_head_flag &= ~NFSD_CHECKSLP;
 			}
-			if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0)
+			if ((slp = nfsd->nfsd_slp) == (struct nfssvc_sock *)0)
 				continue;
 			if (slp->ns_flag & SLP_VALID) {
 				if (slp->ns_flag & SLP_DISCONN)
@@ -471,85 +522,78 @@
 						M_WAIT);
 					nfs_sndunlock(&slp->ns_solock);
 				}
-				error = nfsrv_dorec(slp, nd);
-				nd->nd_flag |= NFSD_REQINPROG;
+				error = nfsrv_dorec(slp, nfsd, &nd);
+				cur_usec = (u_quad_t)time.tv_sec * 1000000 +
+					(u_quad_t)time.tv_usec;
+				if (error && slp->ns_tq.lh_first &&
+				    slp->ns_tq.lh_first->nd_time <= cur_usec) {
+					error = 0;
+					cacherep = RC_DOIT;
+					writes_todo = 1;
+				} else
+					writes_todo = 0;
+				nfsd->nfsd_flag |= NFSD_REQINPROG;
 			}
 		} else {
 			error = 0;
-			slp = nd->nd_slp;
+			slp = nfsd->nfsd_slp;
 		}
 		if (error || (slp->ns_flag & SLP_VALID) == 0) {
-			nd->nd_slp = (struct nfssvc_sock *)0;
-			nd->nd_flag &= ~NFSD_REQINPROG;
+			if (nd) {
+				free((caddr_t)nd, M_NFSRVDESC);
+				nd = NULL;
+			}
+			nfsd->nfsd_slp = (struct nfssvc_sock *)0;
+			nfsd->nfsd_flag &= ~NFSD_REQINPROG;
 			nfsrv_slpderef(slp);
 			continue;
 		}
 		splx(s);
 		so = slp->ns_so;
 		sotype = so->so_type;
-		starttime = time;
 		if (so->so_proto->pr_flags & PR_CONNREQUIRED)
 			solockp = &slp->ns_solock;
 		else
 			solockp = (int *)0;
-		/*
-		 * nam == nam2 for connectionless protocols such as UDP
-		 * nam2 == NULL for connection based protocols to disable
-		 *    recent request caching.
-		 */
-		if ((nam2 = nd->nd_nam) != NULL) {
-			nam = nam2;
-			cacherep = RC_CHECKIT;
-		} else {
-			nam = slp->ns_nam;
-			cacherep = RC_DOIT;
-		}
-
-		/*
-		 * Check to see if authorization is needed.
-		 */
-		if (nd->nd_flag & NFSD_NEEDAUTH) {
-			static int logauth = 0;
+		if (nd) {
+		    nd->nd_starttime = time;
+		    if (nd->nd_nam2)
+			nd->nd_nam = nd->nd_nam2;
+		    else
+			nd->nd_nam = slp->ns_nam;
 
-			nd->nd_flag &= ~NFSD_NEEDAUTH;
-			/*
-			 * Check for a mapping already installed.
-			 */
-			for (uidp = NUIDHASH(slp, nd->nd_cr.cr_uid)->lh_first;
-			    uidp != 0; uidp = uidp->nu_hash.le_next) {
-				if (uidp->nu_uid == nd->nd_cr.cr_uid)
-					break;
-			}
-			if (uidp == 0) {
-			    nsd->nsd_uid = nd->nd_cr.cr_uid;
-			    if (nam2 && logauth++ == 0)
-				log(LOG_WARNING, "Kerberized NFS using UDP\n");
-			    nsd->nsd_haddr =
-			      mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
-			    nsd->nsd_authlen = nd->nd_authlen;
-			    if (copyout(nd->nd_authstr, nsd->nsd_authstr,
-				nd->nd_authlen) == 0 &&
-				copyout((caddr_t)nsd, argp, sizeof (*nsd)) == 0)
-				return (ENEEDAUTH);
-			    cacherep = RC_DROPIT;
-			}
-		}
-		if (cacherep == RC_CHECKIT)
-			cacherep = nfsrv_getcache(nam2, nd, &mreq);
+		    /*
+		     * Check to see if authorization is needed.
+		     */
+		    if (nfsd->nfsd_flag & NFSD_NEEDAUTH) {
+			nfsd->nfsd_flag &= ~NFSD_NEEDAUTH;
+			nsd->nsd_haddr = mtod(nd->nd_nam,
+			    struct sockaddr_in *)->sin_addr.s_addr;
+			nsd->nsd_authlen = nfsd->nfsd_authlen;
+			nsd->nsd_verflen = nfsd->nfsd_verflen;
+			if (!copyout(nfsd->nfsd_authstr,nsd->nsd_authstr,
+				nfsd->nfsd_authlen) &&
+			    !copyout(nfsd->nfsd_verfstr, nsd->nsd_verfstr,
+				nfsd->nfsd_verflen) &&
+			    !copyout((caddr_t)nsd, argp, sizeof (*nsd)))
+			    return (ENEEDAUTH);
+			cacherep = RC_DROPIT;
+		    } else
+			cacherep = nfsrv_getcache(nd, slp, &mreq);
 
-		/*
-		 * Check for just starting up for NQNFS and send
-		 * fake "try again later" replies to the NQNFS clients.
-		 */
-		if (notstarted && nqnfsstarttime <= time.tv_sec) {
+		    /*
+		     * Check for just starting up for NQNFS and send
+		     * fake "try again later" replies to the NQNFS clients.
+		     */
+		    if (notstarted && nqnfsstarttime <= time.tv_sec) {
 			if (modify_flag) {
 				nqnfsstarttime = time.tv_sec + nqsrv_writeslack;
 				modify_flag = 0;
 			} else
 				notstarted = 0;
-		}
-		if (notstarted) {
-			if (nd->nd_nqlflag == NQL_NOVAL)
+		    }
+		    if (notstarted) {
+			if ((nd->nd_flag & ND_NQNFS) == 0)
 				cacherep = RC_DROPIT;
 			else if (nd->nd_procnum != NFSPROC_WRITE) {
 				nd->nd_procnum = NFSPROC_NOOP;
@@ -557,36 +601,42 @@
 				cacherep = RC_DOIT;
 			} else
 				modify_flag = 1;
-		} else if (nd->nd_flag & NFSD_AUTHFAIL) {
-			nd->nd_flag &= ~NFSD_AUTHFAIL;
+		    } else if (nfsd->nfsd_flag & NFSD_AUTHFAIL) {
+			nfsd->nfsd_flag &= ~NFSD_AUTHFAIL;
 			nd->nd_procnum = NFSPROC_NOOP;
-			nd->nd_repstat = NQNFS_AUTHERR;
+			nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
 			cacherep = RC_DOIT;
+		    }
 		}
 
-		switch (cacherep) {
-		case RC_DOIT:
-			error = (*(nfsrv_procs[nd->nd_procnum]))(nd,
-				nd->nd_mrep, nd->nd_md, nd->nd_dpos, &nd->nd_cr,
-				nam, &mreq);
-			if (nd->nd_cr.cr_ref != 1) {
-				printf("nfssvc cref=%d\n", nd->nd_cr.cr_ref);
-				panic("nfssvc cref");
-			}
+		/*
+		 * Loop to get all the write rpc relies that have been
+		 * gathered together.
+		 */
+		do {
+		    switch (cacherep) {
+		    case RC_DOIT:
+			if (writes_todo || (nd->nd_procnum == NFSPROC_WRITE &&
+			    nfsrvw_procrastinate > 0 && !notstarted))
+			    error = nfsrv_writegather(&nd, slp,
+				nfsd->nfsd_procp, &mreq);
+			else
+			    error = (*(nfsrv3_procs[nd->nd_procnum]))(nd,
+				slp, nfsd->nfsd_procp, &mreq);
+			if (mreq == NULL)
+				break;
 			if (error) {
 				if (nd->nd_procnum != NQNFSPROC_VACATED)
 					nfsstats.srv_errs++;
-				if (nam2) {
-					nfsrv_updatecache(nam2, nd, FALSE, mreq);
-					m_freem(nam2);
-				}
+				nfsrv_updatecache(nd, FALSE, mreq);
+				if (nd->nd_nam2)
+					m_freem(nd->nd_nam2);
 				break;
 			}
 			nfsstats.srvrpccnt[nd->nd_procnum]++;
-			if (nam2)
-				nfsrv_updatecache(nam2, nd, TRUE, mreq);
+			nfsrv_updatecache(nd, TRUE, mreq);
 			nd->nd_mrep = (struct mbuf *)0;
-		case RC_REPLY:
+		    case RC_REPLY:
 			m = mreq;
 			siz = 0;
 			while (m) {
@@ -611,15 +661,15 @@
 			if (solockp)
 				(void) nfs_sndlock(solockp, (struct nfsreq *)0);
 			if (slp->ns_flag & SLP_VALID)
-			    error = nfs_send(so, nam2, m, (struct nfsreq *)0);
+			    error = nfs_send(so, nd->nd_nam2, m, NULL);
 			else {
 			    error = EPIPE;
 			    m_freem(m);
 			}
 			if (nfsrtton)
-				nfsd_rt(&starttime, sotype, nd, nam, cacherep);
-			if (nam2)
-				MFREE(nam2, m);
+				nfsd_rt(sotype, nd, cacherep);
+			if (nd->nd_nam2)
+				MFREE(nd->nd_nam2, m);
 			if (nd->nd_mrep)
 				m_freem(nd->nd_mrep);
 			if (error == EPIPE)
@@ -627,29 +677,50 @@
 			if (solockp)
 				nfs_sndunlock(solockp);
 			if (error == EINTR || error == ERESTART) {
+				free((caddr_t)nd, M_NFSRVDESC);
 				nfsrv_slpderef(slp);
 				s = splsoftnet();
 				goto done;
 			}
 			break;
-		case RC_DROPIT:
+		    case RC_DROPIT:
 			if (nfsrtton)
-				nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+				nfsd_rt(sotype, nd, cacherep);
 			m_freem(nd->nd_mrep);
-			m_freem(nam2);
+			m_freem(nd->nd_nam2);
 			break;
-		};
+		    };
+		    if (nd) {
+			FREE((caddr_t)nd, M_NFSRVDESC);
+			nd = NULL;
+		    }
+
+		    /*
+		     * Check to see if there are outstanding writes that
+		     * need to be serviced.
+		     */
+		    cur_usec = (u_quad_t)time.tv_sec * 1000000 +
+			(u_quad_t)time.tv_usec;
+		    s = splsoftclock();
+		    if (slp->ns_tq.lh_first &&
+			slp->ns_tq.lh_first->nd_time <= cur_usec) {
+			cacherep = RC_DOIT;
+			writes_todo = 1;
+		    } else
+			writes_todo = 0;
+		    splx(s);
+		} while (writes_todo);
 		s = splsoftnet();
-		if (nfsrv_dorec(slp, nd)) {
-			nd->nd_flag &= ~NFSD_REQINPROG;
-			nd->nd_slp = (struct nfssvc_sock *)0;
+		if (nfsrv_dorec(slp, nfsd, &nd)) {
+			nfsd->nfsd_flag &= ~NFSD_REQINPROG;
+			nfsd->nfsd_slp = NULL;
 			nfsrv_slpderef(slp);
 		}
 	}
 done:
-	TAILQ_REMOVE(&nfsd_head, nd, nd_chain);
+	TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain);
 	splx(s);
-	free((caddr_t)nd, M_NFSD);
+	free((caddr_t)nfsd, M_NFSD);
 	nsd->nsd_nfsd = (struct nfsd *)0;
 	if (--nfs_numnfsd == 0)
 		nfsrv_init(TRUE);	/* Reinitialize everything */
@@ -668,12 +739,15 @@
 	register struct nfssvc_sock *slp;
 {
 	register struct nfsuid *nuidp, *nnuidp;
+	register struct nfsrv_descript *nwp, *nnwp;
 	struct socket *so;
 	struct file *fp;
 	struct mbuf *m;
+	int s;
 
 	slp->ns_flag &= ~SLP_ALLFLAGS;
-	if ((fp = slp->ns_fp) != NULL) {
+	fp = slp->ns_fp;
+	if (fp) {
 		slp->ns_fp = (struct file *)0;
 		so = slp->ns_so;
 		so->so_upcall = NULL;
@@ -688,8 +762,18 @@
 			nnuidp = nuidp->nu_lru.tqe_next;
 			LIST_REMOVE(nuidp, nu_hash);
 			TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru);
+			if (nuidp->nu_flag & NU_NAM)
+				m_freem(nuidp->nu_nam);
 			free((caddr_t)nuidp, M_NFSUID);
 		}
+		s = splsoftclock();
+		for (nwp = slp->ns_tq.lh_first; nwp; nwp = nnwp) {
+			nnwp = nwp->nd_tq.le_next;
+			LIST_REMOVE(nwp, nd_tq);
+			free((caddr_t)nwp, M_NFSRVDESC);
+		}
+		LIST_INIT(&slp->ns_tq);
+		splx(s);
 	}
 }
 
@@ -745,16 +829,12 @@
 	nfs_udpsock = (struct nfssvc_sock *)
 	    malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
 	bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
-	nfs_udpsock->ns_uidhashtbl =
-	    hashinit(NUIDHASHSIZ, M_NFSSVC, &nfs_udpsock->ns_uidhash);
 	TAILQ_INIT(&nfs_udpsock->ns_uidlruhead);
 	TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain);
 
 	nfs_cltpsock = (struct nfssvc_sock *)
 	    malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
 	bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
-	nfs_cltpsock->ns_uidhashtbl =
-	    hashinit(NUIDHASHSIZ, M_NFSSVC, &nfs_cltpsock->ns_uidhash);
 	TAILQ_INIT(&nfs_cltpsock->ns_uidlruhead);
 	TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain);
 }
@@ -763,11 +843,9 @@
  * Add entries to the server monitor log.
  */
 static void
-nfsd_rt(startp, sotype, nd, nam, cacherep)
-	struct timeval *startp;
+nfsd_rt(sotype, nd, cacherep)
 	int sotype;
-	register struct nfsd *nd;
-	struct mbuf *nam;
+	register struct nfsrv_descript *nd;
 	int cacherep;
 {
 	register struct drt *rt;
@@ -781,15 +859,17 @@
 		rt->flag = DRT_CACHEDROP;
 	if (sotype == SOCK_STREAM)
 		rt->flag |= DRT_TCP;
-	if (nd->nd_nqlflag != NQL_NOVAL)
+	if (nd->nd_flag & ND_NQNFS)
 		rt->flag |= DRT_NQNFS;
+	else if (nd->nd_flag & ND_NFSV3)
+		rt->flag |= DRT_NFSV3;
 	rt->proc = nd->nd_procnum;
-	if (mtod(nam, struct sockaddr *)->sa_family == AF_INET)
-		rt->ipadr = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+	if (mtod(nd->nd_nam, struct sockaddr *)->sa_family == AF_INET)
+	    rt->ipadr = mtod(nd->nd_nam, struct sockaddr_in *)->sin_addr.s_addr;
 	else
-		rt->ipadr = INADDR_ANY;
-	rt->resptime = ((time.tv_sec - startp->tv_sec) * 1000000) +
-		(time.tv_usec - startp->tv_usec);
+	    rt->ipadr = INADDR_ANY;
+	rt->resptime = ((time.tv_sec - nd->nd_starttime.tv_sec) * 1000000) +
+		(time.tv_usec - nd->nd_starttime.tv_usec);
 	rt->tstamp = time;
 	nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
 }
@@ -805,9 +885,10 @@
 nfssvc_iod(p)
 	struct proc *p;
 {
-	register struct buf *bp;
+	register struct buf *bp, *nbp;
 	register int i, myiod;
-	int error = 0;
+	struct vnode *vp;
+	int error = 0, s;
 
 	/*
 	 * Assign my position or return error if too many already running
@@ -826,39 +907,70 @@
 	 * Just loop around doin our stuff until SIGKILL
 	 */
 	for (;;) {
-		while (nfs_bufq.tqh_first == NULL && error == 0) {
-			nfs_iodwant[myiod] = p;
-			error = tsleep((caddr_t)&nfs_iodwant[myiod],
-				PWAIT | PCATCH, "nfsidl", 0);
-		}
-		while ((bp = nfs_bufq.tqh_first) != NULL) {
-			/* Take one off the front of the list */
-			TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
-			if (bp->b_flags & B_READ)
-			    (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
-			else
-			    (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
-		}
-		if (error) {
-			nfs_asyncdaemon[myiod] = 0;
-			nfs_numasync--;
-			return (error);
-		}
+	    while (nfs_bufq.tqh_first == NULL && error == 0) {
+		nfs_iodwant[myiod] = p;
+		error = tsleep((caddr_t)&nfs_iodwant[myiod],
+			PWAIT | PCATCH, "nfsidl", 0);
+	    }
+	    while ((bp = nfs_bufq.tqh_first) != NULL) {
+		/* Take one off the front of the list */
+		TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
+		if (bp->b_flags & B_READ)
+		    (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
+		else do {
+		    /*
+		     * Look for a delayed write for the same vnode, so I can do 
+		     * it now. We must grab it before calling nfs_doio() to
+		     * avoid any risk of the vnode getting vclean()'d while
+		     * we are doing the write rpc.
+		     */
+		    vp = bp->b_vp;
+		    s = splbio();
+		    for (nbp = vp->v_dirtyblkhd.lh_first; nbp;
+			nbp = nbp->b_vnbufs.le_next) {
+			if ((nbp->b_flags &
+			    (B_BUSY|B_DELWRI|B_NEEDCOMMIT|B_NOCACHE))!=B_DELWRI)
+			    continue;
+			bremfree(nbp);
+			nbp->b_flags |= (B_BUSY|B_ASYNC);
+			break;
+		    }
+		    splx(s);
+		    /*
+		     * For the delayed write, do the first part of nfs_bwrite()
+		     * up to, but not including nfs_strategy().
+		     */
+		    if (nbp) {
+			nbp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
+			reassignbuf(nbp, nbp->b_vp);
+			nbp->b_vp->v_numoutput++;
+		    }
+		    (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
+		} while ((bp = nbp) != NULL);
+	    }
+	    if (error) {
+		nfs_asyncdaemon[myiod] = 0;
+		nfs_numasync--;
+		return (error);
+	    }
 	}
 }
 
+
 /*
  * Get an authorization string for the uid by having the mount_nfs sitting
  * on this mount point porpous out of the kernel and do it.
  */
 int
-nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key)
 	register struct nfsmount *nmp;
 	struct nfsreq *rep;
 	struct ucred *cred;
-	int *auth_type;
 	char **auth_str;
 	int *auth_len;
+	char *verf_str;
+	int *verf_len;
+	NFSKERBKEY_T key;		/* return session key */
 {
 	int error = 0;
 
@@ -874,6 +986,9 @@
 	}
 	nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
 	nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
+	nmp->nm_authlen = RPCAUTH_MAXSIZ;
+	nmp->nm_verfstr = verf_str;
+	nmp->nm_verflen = *verf_len;
 	nmp->nm_authuid = cred->cr_uid;
 	wakeup((caddr_t)&nmp->nm_authstr);
 
@@ -892,8 +1007,9 @@
 	if (error)
 		free((caddr_t)*auth_str, M_TEMP);
 	else {
-		*auth_type = nmp->nm_authtype;
 		*auth_len = nmp->nm_authlen;
+		*verf_len = nmp->nm_verflen;
+		bcopy((caddr_t)nmp->nm_key, (caddr_t)key, sizeof (key));
 	}
 	nmp->nm_flag &= ~NFSMNT_HASAUTH;
 	nmp->nm_flag |= NFSMNT_WAITAUTH;
@@ -903,4 +1019,145 @@
 	}
 	return (error);
 }
+
+/*
+ * Get a nickname authenticator and verifier.
+ */
+int
+nfs_getnickauth(nmp, cred, auth_str, auth_len, verf_str, verf_len)
+	struct nfsmount *nmp;
+	struct ucred *cred;
+	char **auth_str;
+	int *auth_len;
+	char *verf_str;
+	int verf_len;
+{
+	register struct nfsuid *nuidp;
+	register u_int32_t *nickp, *verfp;
+	struct timeval ktvin, ktvout;
+
+#ifdef DIAGNOSTIC
+	if (verf_len < (4 * NFSX_UNSIGNED))
+		panic("nfs_getnickauth verf too small");
+#endif
+	for (nuidp = NMUIDHASH(nmp, cred->cr_uid)->lh_first;
+	    nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
+		if (nuidp->nu_cr.cr_uid == cred->cr_uid)
+			break;
+	}
+	if (!nuidp || nuidp->nu_expire < time.tv_sec)
+		return (EACCES);
+
+	/*
+	 * Move to the end of the lru list (end of lru == most recently used).
+	 */
+	TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru);
+	TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru);
+
+	nickp = (u_int32_t *)malloc(2 * NFSX_UNSIGNED, M_TEMP, M_WAITOK);
+	*nickp++ = txdr_unsigned(RPCAKN_NICKNAME);
+	*nickp = txdr_unsigned(nuidp->nu_nickname);
+	*auth_str = (char *)nickp;
+	*auth_len = 2 * NFSX_UNSIGNED;
+
+	/*
+	 * Now we must encrypt the verifier and package it up.
+	 */
+	verfp = (u_int32_t *)verf_str;
+	*verfp++ = txdr_unsigned(RPCAKN_NICKNAME);
+	if (time.tv_sec > nuidp->nu_timestamp.tv_sec ||
+	    (time.tv_sec == nuidp->nu_timestamp.tv_sec &&
+	     time.tv_usec > nuidp->nu_timestamp.tv_usec))
+		nuidp->nu_timestamp = time;
+	else
+		nuidp->nu_timestamp.tv_usec++;
+	ktvin.tv_sec = txdr_unsigned(nuidp->nu_timestamp.tv_sec);
+	ktvin.tv_usec = txdr_unsigned(nuidp->nu_timestamp.tv_usec);
+
+	/*
+	 * Now encrypt the timestamp verifier in ecb mode using the session
+	 * key.
+	 */
+#ifdef NFSKERB
+	XXX
+#endif
+
+	*verfp++ = ktvout.tv_sec;
+	*verfp++ = ktvout.tv_usec;
+	*verfp = 0;
+	return (0);
+}
+
+/*
+ * Save the current nickname in a hash list entry on the mount point.
+ */
+int
+nfs_savenickauth(nmp, cred, len, key, mdp, dposp, mrep)
+	register struct nfsmount *nmp;
+	struct ucred *cred;
+	int len;
+	NFSKERBKEY_T key;
+	struct mbuf **mdp;
+	char **dposp;
+	struct mbuf *mrep;
+{
+	register struct nfsuid *nuidp;
+	register u_int32_t *tl;
+	register int32_t t1;
+	struct mbuf *md = *mdp;
+	struct timeval ktvin, ktvout;
+	u_int32_t nick;
+	char *dpos = *dposp, *cp2;
+	int deltasec, error = 0;
+
+	if (len == (3 * NFSX_UNSIGNED)) {
+		nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+		ktvin.tv_sec = *tl++;
+		ktvin.tv_usec = *tl++;
+		nick = fxdr_unsigned(u_int32_t, *tl);
+
+		/*
+		 * Decrypt the timestamp in ecb mode.
+		 */
+#ifdef NFSKERB
+		XXX
+#endif
+		ktvout.tv_sec = fxdr_unsigned(long, ktvout.tv_sec);
+		ktvout.tv_usec = fxdr_unsigned(long, ktvout.tv_usec);
+		deltasec = time.tv_sec - ktvout.tv_sec;
+		if (deltasec < 0)
+			deltasec = -deltasec;
+		/*
+		 * If ok, add it to the hash list for the mount point.
+		 */
+		if (deltasec <= NFS_KERBCLOCKSKEW) {
+			if (nmp->nm_numuids < nuidhash_max) {
+				nmp->nm_numuids++;
+				nuidp = (struct nfsuid *)
+				   malloc(sizeof (struct nfsuid), M_NFSUID,
+					M_WAITOK);
+			} else {
+				nuidp = nmp->nm_uidlruhead.tqh_first;
+				LIST_REMOVE(nuidp, nu_hash);
+				TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp,
+					nu_lru);
+			}
+			nuidp->nu_flag = 0;
+			nuidp->nu_cr.cr_uid = cred->cr_uid;
+			nuidp->nu_expire = time.tv_sec + NFS_KERBTTL;
+			nuidp->nu_timestamp = ktvout;
+			nuidp->nu_nickname = nick;
+			bcopy(key, nuidp->nu_key, sizeof (key));
+			TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp,
+				nu_lru);
+			LIST_INSERT_HEAD(NMUIDHASH(nmp, cred->cr_uid),
+				nuidp, nu_hash);
+		}
+	} else
+		nfsm_adv(nfsm_rndup(len));
+nfsmout:
+	*mdp = md;
+	*dposp = dpos;
+	return (error);
+}
 #endif /* NFSCLIENT */
--- a/sys/nfs/nfs_var.h	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_var.h	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_var.h,v 1.2 1996/02/13 17:06:52 christos Exp $	*/
+/*	$NetBSD: nfs_var.h,v 1.3 1996/02/18 11:53:54 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1996 Christos Zoulas.  All rights reserved.
@@ -29,6 +29,10 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/*
+ * XXX needs <nfs/rpcv2.h> and <nfs/nfs.h> because of typedefs
+ */
+
 struct vnode;
 struct uio;
 struct ucred;
@@ -53,11 +57,12 @@
 struct sillyrename;
 struct componentname;
 struct nfsd_srvargs;
-struct nfsd_cargs;
-
+struct nfsrv_descript;
+struct nfs_fattr;
 
 /* nfs_bio.c */
 int nfs_bioread __P((struct vnode *, struct uio *, int, struct ucred *));
+int nfs_write __P((void *));
 struct buf *nfs_getcacheblk __P((struct vnode *, daddr_t, int, struct proc *));
 int nfs_vinvalbuf __P((struct vnode *, int, struct ucred *, struct proc *,
 		       int));
@@ -70,91 +75,157 @@
 
 /* nfs_node.c */
 void nfs_nhinit __P((void));
-struct nfsnodehashhead *nfs_hash __P((nfsv2fh_t *));
-int nfs_nget __P((struct mount *, nfsv2fh_t *, struct nfsnode **));
+u_long nfs_hash __P((nfsfh_t *, int));
+int nfs_nget __P((struct mount *, nfsfh_t *, int, struct nfsnode **));
+int nfs_inactive __P((void *));
+int nfs_reclaim __P((void *));
+int nfs_lock __P((void *));
+int nfs_unlock __P((void *));
+int nfs_islocked __P((void *));
+int nfs_abortop __P((void *));
 
 /* nfs_vnops.c */
 int nfs_null __P((struct vnode *, struct ucred *, struct proc *));
+int nfs_access __P((void *));
+int nfs_open __P((void *));
+int nfs_close __P((void *));
+int nfs_getattr __P((void *));
+int nfs_setattr __P((void *));
+int nfs_setattrrpc __P((struct vnode *, struct vattr *, struct ucred *,
+			struct proc *));
+int nfs_lookup __P((void *));
+int nfs_read __P((void *));
+int nfs_readlink __P((void *));
 int nfs_readlinkrpc __P((struct vnode *, struct uio *, struct ucred *));
 int nfs_readrpc __P((struct vnode *, struct uio *, struct ucred *));
-int nfs_writerpc __P((struct vnode *, struct uio *, struct ucred *, int));
+int nfs_writerpc __P((struct vnode *, struct uio *, struct ucred *, int *,
+		      int *));
+int nfs_mknodrpc __P((struct vnode *, struct vnode **, struct componentname *,
+		      struct vattr *));
+int nfs_mknod __P((void *));
+int nfs_create __P((void *));
+int nfs_remove __P((void *));
 int nfs_removeit __P((struct sillyrename *));
+int nfs_removerpc __P((struct vnode *, char *, int, struct ucred *,
+		       struct proc *));
+int nfs_rename __P((void *));
 int nfs_renameit __P((struct vnode *, struct componentname *,
 		      struct sillyrename *));
+int nfs_renamerpc __P((struct vnode *, char *, int, struct vnode *, char *, int,
+		       struct ucred *, struct proc *));
+int nfs_link __P((void *));
+int nfs_symlink __P((void *));
+int nfs_mkdir __P((void *));
+int nfs_rmdir __P((void *));
+int nfs_readdir __P((void *));
 int nfs_readdirrpc __P((struct vnode *, struct uio *, struct ucred *));
-int nfs_readdirlookrpc __P((struct vnode *, struct uio *, struct ucred *));
+int nfs_readdirplusrpc __P((struct vnode *, struct uio *, struct ucred *));
 int nfs_sillyrename __P((struct vnode *, struct vnode *,
 			 struct componentname *));
-int nfs_lookitup __P((struct sillyrename *, nfsv2fh_t *, struct proc *));
+int nfs_lookitup __P((struct vnode *, char *, int, struct ucred *,
+		      struct proc *, struct nfsnode **));
+int nfs_commit __P((struct vnode *, u_quad_t, int, struct ucred *,
+		    struct proc *));
+int nfs_bmap __P((void *));
+int nfs_strategy __P((void *));
+int nfs_mmap __P((void *));
+int nfs_fsync __P((void *));
+int nfs_flush __P((struct vnode *, struct ucred *, int, struct proc *, int));
+int nfs_pathconf __P((void *));
+int nfs_advlock __P((void *));
+int nfs_print __P((void *));
+int nfs_blkatoff __P((void *));
+int nfs_valloc __P((void *));
+int nfs_vfree __P((void *));
+int nfs_truncate __P((void *));
+int nfs_update __P((void *));
+int nfs_bwrite __P((void *));
+int nfs_writebp __P((struct buf *, int));
+int nfsspec_access __P((void *));
+int nfsspec_read __P((void *));
+int nfsspec_write __P((void *));
+int nfsspec_close __P((void *));
+int nfsfifo_read __P((void *));
+int nfsfifo_write __P((void *));
+int nfsfifo_close __P((void *));
 
 /* nfs_nqlease.c */
-int nqsrv_getlease __P((struct vnode *, u_int *, int, struct nfsd *,
-			struct mbuf *, int *, u_quad_t *, struct ucred *));
-int lease_check __P((void *));
+void nqnfs_lease_updatetime __P((int));
+void nqnfs_clientlease __P((struct nfsmount *, struct nfsnode *, int, int,
+			    time_t, u_quad_t));
+void nqsrv_locklease __P((struct nqlease *));
+void nqsrv_unlocklease __P((struct nqlease *));
+int nqsrv_getlease __P((struct vnode *, u_int32_t *, int, struct nfssvc_sock *,
+			struct proc *, struct mbuf *, int *, u_quad_t *,
+			struct ucred *));
+int nqnfs_vop_lease_check __P((void *));
 void nqsrv_addhost __P((struct nqhost *, struct nfssvc_sock *, struct mbuf *));
-void nqsrv_instimeq __P((struct nqlease *, u_long));
+void nqsrv_instimeq __P((struct nqlease *, u_int32_t));
 int nqsrv_cmpnam __P((struct nfssvc_sock *, struct mbuf *, struct nqhost *));
 void nqsrv_send_eviction __P((struct vnode *, struct nqlease *,
 			      struct nfssvc_sock *, struct mbuf *,
 			      struct ucred *));
 void nqsrv_waitfor_expiry __P((struct nqlease *));
 void nqnfs_serverd __P((void));
-int nqnfsrv_getlease __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-			  struct ucred *, struct mbuf *, struct mbuf **));
-int nqnfsrv_vacated __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-			 struct ucred *, struct mbuf *, struct mbuf **));
+int nqnfsrv_getlease __P((struct nfsrv_descript *, struct nfssvc_sock *,
+			  struct proc *, struct mbuf **));
+int nqnfsrv_vacated __P((struct nfsrv_descript *, struct nfssvc_sock *,
+			 struct proc *, struct mbuf **));
 int nqnfs_getlease __P((struct vnode *, int, struct ucred *, struct proc *));
 int nqnfs_vacated __P((struct vnode *, struct ucred *));
 int nqnfs_callback __P((struct nfsmount *, struct mbuf *, struct mbuf *,
-		 	caddr_t));
-int nqnfs_clientd __P((struct nfsmount *, struct ucred *, struct nfsd_cargs *,
-	               int, caddr_t, struct proc *));
-void nqnfs_clientlease __P((struct nfsmount *, struct nfsnode *, int, int ,
-			    time_t, u_quad_t));
-void lease_updatetime __P((int));
-void nqsrv_locklease __P((struct nqlease *));
-void nqsrv_unlocklease __P((struct nqlease *));
+			caddr_t));
 
 /* nfs_serv.c */
-int nqnfsrv_access __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-			struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_getattr __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		       struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_setattr __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		       struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_lookup __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		      struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_readlink __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-			struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_read __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		    struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_write __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		     struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_create __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		      struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_remove __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		      struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_rename __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		      struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_link __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		    struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_symlink __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		       struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_mkdir __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		     struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_rmdir __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		     struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_readdir __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		       struct ucred *, struct mbuf *, struct mbuf **));
-int nqnfsrv_readdirlook __P((struct nfsd *, struct mbuf *, struct mbuf *,
-			     caddr_t, struct ucred *, struct mbuf *,
-			     struct mbuf **));
-int nfsrv_statfs __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		      struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_null __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		    struct ucred *, struct mbuf *, struct mbuf **));
-int nfsrv_noop __P((struct nfsd *, struct mbuf *, struct mbuf *, caddr_t,
-		    struct ucred *, struct mbuf *, struct mbuf **));
+int nfsrv3_access __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		       struct proc *, struct mbuf **));
+int nfsrv_getattr __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		       struct proc *, struct mbuf **));
+int nfsrv_setattr __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		       struct proc *, struct mbuf **));
+int nfsrv_lookup __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_readlink __P((struct nfsrv_descript *, struct nfssvc_sock *,
+			struct proc *, struct mbuf **));
+int nfsrv_read __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		    struct proc *, struct mbuf **));
+int nfsrv_write __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		     struct proc *, struct mbuf **));
+int nfsrv_writegather __P((struct nfsrv_descript **, struct nfssvc_sock *,
+			   struct proc *, struct mbuf **));
+void nfsrvw_coalesce __P((struct nfsrv_descript *, struct nfsrv_descript *));
+int nfsrv_create __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_mknod __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		     struct proc *, struct mbuf **));
+int nfsrv_remove __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_rename __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_link __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		    struct proc *, struct mbuf **));
+int nfsrv_symlink __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		       struct proc *, struct mbuf **));
+int nfsrv_mkdir __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		     struct proc *, struct mbuf **));
+int nfsrv_rmdir __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		     struct proc *, struct mbuf **));
+int nfsrv_readdir __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		       struct proc *, struct mbuf **));
+int nfsrv_readdirplus __P((struct nfsrv_descript *, struct nfssvc_sock *,
+			   struct proc *, struct mbuf **));
+int nfsrv_commit __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_statfs __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_fsinfo __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		      struct proc *, struct mbuf **));
+int nfsrv_pathconf __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		        struct proc *, struct mbuf **));
+int nfsrv_null __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		    struct proc *, struct mbuf **));
+int nfsrv_noop __P((struct nfsrv_descript *, struct nfssvc_sock *,
+		    struct proc *, struct mbuf **));
 int nfsrv_access __P((struct vnode *, int, struct ucred *, int, struct proc *));
 
 /* nfs_socket.c */
@@ -168,8 +239,8 @@
 int nfs_request __P((struct vnode *, struct mbuf *, int, struct proc *,
 		     struct ucred *, struct mbuf **, struct mbuf **,
 		     caddr_t *));
-int nfs_rephead __P((int, struct nfsd *, int, int, u_quad_t *, struct mbuf **,
-		     struct mbuf **, caddr_t *));
+int nfs_rephead __P((int, struct nfsrv_descript *, struct nfssvc_sock *,
+		     int, int, u_quad_t *, struct mbuf **, struct mbuf **,			     caddr_t *));
 void nfs_timer __P((void *));
 int nfs_sigintr __P((struct nfsmount *, struct nfsreq *, struct proc *));
 int nfs_sndlock __P((int *, struct nfsreq *));
@@ -177,40 +248,58 @@
 int nfs_rcvlock __P((struct nfsreq *));
 void nfs_rcvunlock __P((int *));
 void nfs_realign __P((struct mbuf *, int));
-int nfs_getreq __P((struct nfsd *, int));
-void nfs_msg __P((struct proc *, char *, char *));
+int nfs_getreq __P((struct nfsrv_descript *, struct nfsd *, int));
+int nfs_msg __P((struct proc *, char *, char *));
 void nfsrv_rcv __P((struct socket *, caddr_t, int));
 int nfsrv_getstream __P((struct nfssvc_sock *, int));
-int nfsrv_dorec __P((struct nfssvc_sock *, struct nfsd *));
+int nfsrv_dorec __P((struct nfssvc_sock *, struct nfsd *,
+		     struct nfsrv_descript **));
 void nfsrv_wakenfsd __P((struct nfssvc_sock *));
 
 /* nfs_srvcache.c */
-void nfsrv_initcache __P((void));
-int nfsrv_getcache __P((struct mbuf *, struct nfsd *, struct mbuf **));
-void nfsrv_updatecache __P((struct mbuf *, struct nfsd *, int, struct mbuf *));
+void nfsrv_initcache __P((void ));
+int nfsrv_getcache __P((struct nfsrv_descript *, struct nfssvc_sock *,
+			struct mbuf **));
+void nfsrv_updatecache __P((struct nfsrv_descript *, int, struct mbuf *));
 void nfsrv_cleancache __P((void));
 
 /* nfs_subs.c */
 struct mbuf *nfsm_reqh __P((struct vnode *, u_long, int, caddr_t *));
-struct mbuf *nfsm_rpchead __P((struct ucred *, int, int, int, int, char *,
-			       struct mbuf *, int, struct mbuf **,
+struct mbuf *nfsm_rpchead __P((struct ucred *, int, int, int, int, char *, int,
+			       char *, struct mbuf *, int, struct mbuf **,
 			       u_int32_t *));
 int nfsm_mbuftouio __P((struct mbuf **, struct uio *, int, caddr_t *));
 int nfsm_uiotombuf __P((struct uio *, struct mbuf **, int, caddr_t *));
 int nfsm_disct __P((struct mbuf **, caddr_t *, int, int, caddr_t *));
 int nfs_adv __P((struct mbuf **, caddr_t *, int, int));
 int nfsm_strtmbuf __P((struct mbuf **, char **, char *, long));
+void nfs_init __P((void));
 int nfs_loadattrcache __P((struct vnode **, struct mbuf **, caddr_t *,
 			   struct vattr *));
 int nfs_getattrcache __P((struct vnode *, struct vattr *));
 int nfs_namei __P((struct nameidata *, fhandle_t *, int, struct nfssvc_sock *,
-		   struct mbuf *, struct mbuf **, caddr_t *, struct proc *));
+		   struct mbuf *, struct mbuf **, caddr_t *, struct vnode **,
+		   struct proc *, int));
 void nfsm_adj __P((struct mbuf *, int, int));
+void nfsm_srvwcc __P((struct nfsrv_descript *, int, struct vattr *, int,
+		      struct vattr *, struct mbuf **, char **));
+void nfsm_srvpostopattr __P((struct nfsrv_descript *, int, struct vattr *,
+			     struct mbuf **, char **));
+void nfsm_srvfattr __P((struct nfsrv_descript *, struct vattr *,
+			struct nfs_fattr *));
 int nfsrv_fhtovp __P((fhandle_t *, int, struct vnode **, struct ucred *,
-		      struct nfssvc_sock *, struct mbuf *, int *));
+		      struct nfssvc_sock *, struct mbuf *, int *, int));
 int netaddr_match __P((int, union nethostaddr *, struct mbuf *));
+nfsuint64 *nfs_getcookie __P((struct nfsnode *, off_t off, int));
+void nfs_invaldir __P((struct vnode *));
+void nfs_clearcommit __P((struct mount *));
+int nfsrv_errmap __P((struct nfsrv_descript *, int));
+void nfsrvw_sort __P((gid_t *, int));
+void nfsrv_setcred __P((struct ucred *, struct ucred *));
 
 /* nfs_syscalls.c */
+int sys_getfh __P((struct proc *, void *, register_t *));
+int sys_nfssvc __P((struct proc *, void *, register_t *));
 int nfssvc_addsock __P((struct file *, struct mbuf *));
 int nfssvc_nfsd __P((struct nfsd_srvargs *, caddr_t, struct proc *));
 void nfsrv_zapsock __P((struct nfssvc_sock *));
@@ -218,5 +307,8 @@
 void nfsrv_init __P((int));
 int nfssvc_iod __P((struct proc *));
 int nfs_getauth __P((struct nfsmount *, struct nfsreq *, struct ucred *,
-		     int *, char **, int *));
-
+		     char **, int *, char *, int *, NFSKERBKEY_T));
+int nfs_getnickauth __P((struct nfsmount *, struct ucred *, char **, int *,
+			 char *, int));
+int nfs_savenickauth __P((struct nfsmount *, struct ucred *, int, NFSKERBKEY_T,
+			  struct mbuf **, char **, struct mbuf *));
--- a/sys/nfs/nfs_vfsops.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_vfsops.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: nfs_vfsops.c,v 1.42 1996/02/13 17:53:35 gwr Exp $	*/
+/*	$NetBSD: nfs_vfsops.c,v 1.43 1996/02/18 11:53:56 fvdl Exp $	*/
 
 /*
- * Copyright (c) 1989, 1993
+ * Copyright (c) 1989, 1993, 1995
  *	The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -35,7 +35,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_vfsops.c	8.3 (Berkeley) 1/4/94
+ *	@(#)nfs_vfsops.c	8.12 (Berkeley) 5/20/95
  */
 
 #include <sys/param.h>
@@ -58,16 +58,24 @@
 #include <netinet/in.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
 #include <nfs/nfsmount.h>
-#include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nfsdiskless.h>
 #include <nfs/nqnfs.h>
 #include <nfs/nfs_var.h>
 
+struct nfsstats nfsstats;
+extern int nfs_ticks;
+
+#ifdef notyet
+static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
+		      struct proc *);
+#endif
+
 /*
  * nfs vfs operations.
  */
@@ -84,11 +92,13 @@
 	nfs_fhtovp,
 	nfs_vptofh,
 	nfs_init,
+#ifdef notyet
+	nfs_sysctl
+#endif
 };
 
-extern u_long nfs_procids[NFS_NPROCS];
-extern u_long nfs_prog, nfs_vers;
-void nfs_disconnect __P((struct nfsmount *));
+extern u_int32_t nfs_procids[NFS_NPROCS];
+extern u_int32_t nfs_prog, nfs_vers;
 
 static struct mount *
 nfs_mount_diskless __P((struct nfs_dlmount *, char *, int, struct vnode **));
@@ -106,43 +116,61 @@
 	struct proc *p;
 {
 	register struct vnode *vp;
-	register struct nfsv2_statfs *sfp;
+	register struct nfs_statfs *sfp;
 	register caddr_t cp;
-	register int32_t t1;
+	register u_int32_t *tl;
+	register int32_t t1, t2;
 	caddr_t bpos, dpos, cp2;
-	int error = 0, isnq;
+	struct nfsmount *nmp = VFSTONFS(mp);
+	int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-	struct nfsmount *nmp;
 	struct ucred *cred;
 	struct nfsnode *np;
+	u_quad_t tquad;
 
-	nmp = VFSTONFS(mp);
-	isnq = (nmp->nm_flag & NFSMNT_NQNFS);
-	if ((error = nfs_nget(mp, &nmp->nm_fh, &np)) != 0)
+#ifndef nolint
+	sfp = (struct nfs_statfs *)0;
+#endif
+	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+	if (error)
 		return (error);
 	vp = NFSTOV(np);
-	nfsstats.rpccnt[NFSPROC_STATFS]++;
 	cred = crget();
 	cred->cr_ngroups = 0;
-	nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH);
-	nfsm_fhtom(vp);
-	nfsm_request(vp, NFSPROC_STATFS, p, cred);
-	nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+	if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
+		(void)nfs_fsinfo(nmp, vp, cred, p);
+	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
+	nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3));
+	nfsm_fhtom(vp, v3);
+	nfsm_request(vp, NFSPROC_FSSTAT, p, cred);
+	if (v3)
+		nfsm_postop_attr(vp, retattr);
+	if (!error)
+		nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
 #ifdef COMPAT_09
 	sbp->f_type = 2;
 #else
 	sbp->f_type = 0;
 #endif
 	sbp->f_flags = nmp->nm_flag;
-	sbp->f_iosize = NFS_MAXDGRAMDATA;
-	sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
-	sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
-	sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
-	sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
-	if (isnq) {
-		sbp->f_files = fxdr_unsigned(int32_t, sfp->sf_files);
-		sbp->f_ffree = fxdr_unsigned(int32_t, sfp->sf_ffree);
+	sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize);
+	if (v3) {
+		sbp->f_bsize = NFS_FABLKSIZE;
+		fxdr_hyper(&sfp->sf_tbytes, &tquad);
+		sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
+		fxdr_hyper(&sfp->sf_fbytes, &tquad);
+		sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
+		fxdr_hyper(&sfp->sf_abytes, &tquad);
+		sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
+		sbp->f_files = (fxdr_unsigned(int32_t,
+		    sfp->sf_tfiles.nfsuquad[1]) & 0x7fffffff);
+		sbp->f_ffree = (fxdr_unsigned(int32_t,
+		    sfp->sf_ffiles.nfsuquad[1]) & 0x7fffffff);
 	} else {
+		sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
+		sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
+		sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
+		sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
 		sbp->f_files = 0;
 		sbp->f_ffree = 0;
 	}
@@ -158,6 +186,66 @@
 }
 
 /*
+ * nfs version 3 fsinfo rpc call
+ */
+int
+nfs_fsinfo(nmp, vp, cred, p)
+	register struct nfsmount *nmp;
+	register struct vnode *vp;
+	struct ucred *cred;
+	struct proc *p;
+{
+	register struct nfsv3_fsinfo *fsp;
+	register caddr_t cp;
+	register int32_t t1, t2;
+	register u_int32_t *tl, pref, max;
+	caddr_t bpos, dpos, cp2;
+	int error = 0, retattr;
+	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+
+	nfsstats.rpccnt[NFSPROC_FSINFO]++;
+	nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1));
+	nfsm_fhtom(vp, 1);
+	nfsm_request(vp, NFSPROC_FSINFO, p, cred);
+	nfsm_postop_attr(vp, retattr);
+	if (!error) {
+		nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
+		pref = fxdr_unsigned(u_int32_t, fsp->fs_wtpref);
+		if (pref < nmp->nm_wsize)
+			nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
+				~(NFS_FABLKSIZE - 1);
+		max = fxdr_unsigned(u_int32_t, fsp->fs_wtmax);
+		if (max < nmp->nm_wsize) {
+			nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
+			if (nmp->nm_wsize == 0)
+				nmp->nm_wsize = max;
+		}
+		pref = fxdr_unsigned(u_int32_t, fsp->fs_rtpref);
+		if (pref < nmp->nm_rsize)
+			nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
+				~(NFS_FABLKSIZE - 1);
+		max = fxdr_unsigned(u_int32_t, fsp->fs_rtmax);
+		if (max < nmp->nm_rsize) {
+			nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
+			if (nmp->nm_rsize == 0)
+				nmp->nm_rsize = max;
+		}
+		pref = fxdr_unsigned(u_int32_t, fsp->fs_dtpref);
+		if (pref < nmp->nm_readdirsize)
+			nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) &
+				~(NFS_DIRBLKSIZ - 1);
+		if (max < nmp->nm_readdirsize) {
+			nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
+			if (nmp->nm_readdirsize == 0)
+				nmp->nm_readdirsize = max;
+		}
+		nmp->nm_flag |= NFSMNT_GOTFSINFO;
+	}
+	nfsm_reqdone;
+	return (error);
+}
+
+/*
  * Mount a remote root fs via. NFS.  It goes like this:
  * - Call nfs_boot_init() to fill in the nfs_diskless struct
  *   (using RARP, bootparam RPC, mountd RPC)
@@ -204,12 +292,20 @@
 	/*
 	 * Link it into the mount list.
 	 */
+#ifdef Lite2_integrated
+	simple_lock(&mountlist_slock);
+	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+	simple_unlock(&mountlist_slock);
+	rootvp = vp;
+	vfs_unbusy(mp, procp);
+#else
 	if (vfs_lock(mp))
 		panic("nfs_mountroot: vfs_lock");
 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
 	mp->mnt_vnodecovered = NULLVP;
 	vfs_unlock(mp);
 	rootvp = vp;
+#endif
 
 	/* Get root attributes (for the time). */
 	error = VOP_GETATTR(vp, &attr, procp->p_ucred, procp);
@@ -220,6 +316,11 @@
 #endif
 	inittodr(n);
 
+	/* 
+	 * XXX splnet, so networks will receive...
+	 */
+	splnet();
+
 #ifdef notyet
 	/* Set up swap credentials. */
 	proc0.p_ucred->cr_uid = ntohl(nd.swap_ucred.cr_uid);
@@ -251,6 +352,9 @@
 	 */
 	nfs_boot_getfh(&nd.nd_boot, "swap", &nd.nd_swap);
 	mp = nfs_mount_diskless(&nd.nd_swap, "/swap", 0, &vp);
+#ifdef Lite2_integrated
+	vfs_unbusy(mp, procp);
+#endif
 	printf("swap on %s\n", nd.nd_swap.ndm_host);
 
 	/*
@@ -291,12 +395,16 @@
 	struct mbuf *m;
 	int error;
 
+#ifdef Lite2_integrated
+	vfs_rootmountalloc("nfs", mntname, &mp);
+#else
 	/* Create the mount point. */
 	mp = (struct mount *)malloc((u_long)sizeof(struct mount),
 	    M_MOUNT, M_WAITOK);
 	if (mp == NULL)
 		panic("nfs_mountroot: malloc mount for %s", mntname);
 	bzero((char *)mp, (u_long)sizeof(struct mount));
+#endif
 	mp->mnt_op = &nfs_vfsops;
 	mp->mnt_flag = mntflag;
 
@@ -305,7 +413,8 @@
 	args.addr     = (struct sockaddr *)&ndmntp->ndm_saddr;
 	args.addrlen  = args.addr->sa_len;
 	args.sotype   = SOCK_DGRAM;
-	args.fh       = (nfsv2fh_t *)ndmntp->ndm_fh;
+	args.fh       = ndmntp->ndm_fh;
+	args.fhsize   = NFSX_V2FH;
 	args.hostname = ndmntp->ndm_host;
 	args.flags    = NFSMNT_RESVPORT;
 
@@ -344,6 +453,7 @@
 {
 	int s;
 	int adjsock;
+	int maxio;
 
 	s = splsoftnet();
 
@@ -370,17 +480,25 @@
 			nmp->nm_retry = NFS_MAXREXMIT;
 	}
 
+	if (argp->flags & NFSMNT_NFSV3) {
+		if (argp->sotype == SOCK_DGRAM)
+			maxio = NFS_MAXDGRAMDATA;
+		else
+			maxio = NFS_MAXDATA;
+	} else
+		maxio = NFS_V2MAXDATA;
+
 	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
 		int osize = nmp->nm_wsize;
 		nmp->nm_wsize = argp->wsize;
 		/* Round down to multiple of blocksize */
-		nmp->nm_wsize &= ~0x1ff;
+		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
 		if (nmp->nm_wsize <= 0)
-			nmp->nm_wsize = 512;
-		else if (nmp->nm_wsize > NFS_MAXDATA)
-			nmp->nm_wsize = NFS_MAXDATA;
+			nmp->nm_wsize = NFS_FABLKSIZE;
 		adjsock |= (nmp->nm_wsize != osize);
 	}
+	if (nmp->nm_wsize > maxio)
+		nmp->nm_wsize = maxio;
 	if (nmp->nm_wsize > MAXBSIZE)
 		nmp->nm_wsize = MAXBSIZE;
 
@@ -388,16 +506,26 @@
 		int osize = nmp->nm_rsize;
 		nmp->nm_rsize = argp->rsize;
 		/* Round down to multiple of blocksize */
-		nmp->nm_rsize &= ~0x1ff;
+		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
 		if (nmp->nm_rsize <= 0)
-			nmp->nm_rsize = 512;
-		else if (nmp->nm_rsize > NFS_MAXDATA)
-			nmp->nm_rsize = NFS_MAXDATA;
+			nmp->nm_rsize = NFS_FABLKSIZE;
 		adjsock |= (nmp->nm_rsize != osize);
 	}
+	if (nmp->nm_rsize > maxio)
+		nmp->nm_rsize = maxio;
 	if (nmp->nm_rsize > MAXBSIZE)
 		nmp->nm_rsize = MAXBSIZE;
 
+	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
+		nmp->nm_readdirsize = argp->readdirsize;
+		/* Round down to multiple of blocksize */
+		nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
+		if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
+			nmp->nm_readdirsize = NFS_DIRBLKSIZ;
+	}
+	if (nmp->nm_readdirsize > maxio)
+		nmp->nm_readdirsize = maxio;
+
 	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
 		argp->maxgrouplist <= NFS_MAXGRPS)
 		nmp->nm_numgrps = argp->maxgrouplist;
@@ -446,11 +574,13 @@
 	struct vnode *vp;
 	char pth[MNAMELEN], hst[MNAMELEN];
 	size_t len;
-	nfsv2fh_t nfh;
+	u_char nfh[NFSX_V3FHMAX];
 
 	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
 	if (error)
 		return (error);
+	if (args.version != NFS_ARGSVERSION)
+		return (EPROGMISMATCH);
 	if (mp->mnt_flag & MNT_UPDATE) {
 		register struct nfsmount *nmp = VFSTONFS(mp);
 
@@ -459,21 +589,22 @@
 		nfs_decode_args(nmp, &args);
 		return (0);
 	}
-	error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t));
+	error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
 	if (error)
 		return (error);
-	if ((error = copyinstr(path, pth, MNAMELEN-1, &len)) != 0)
+	error = copyinstr(path, pth, MNAMELEN-1, &len);
+	if (error)
 		return (error);
 	bzero(&pth[len], MNAMELEN - len);
-	if ((error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) != 0)
+	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
+	if (error)
 		return (error);
 	bzero(&hst[len], MNAMELEN - len);
 	/* sockargs() call must be after above copyin() calls */
-	error = sockargs(&nam, (caddr_t)args.addr,
-			 args.addrlen, MT_SONAME);
+	error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
 	if (error)
 		return (error);
-	args.fh = &nfh;
+	args.fh = nfh;
 	error = mountnfs(&args, mp, nam, pth, hst, &vp);
 	return (error);
 }
@@ -503,14 +634,14 @@
 		    M_NFSMNT, M_WAITOK);
 		bzero((caddr_t)nmp, sizeof (struct nfsmount));
 		mp->mnt_data = (qaddr_t)nmp;
+		TAILQ_INIT(&nmp->nm_uidlruhead);
 	}
+#ifdef Lite2_integrated
+	vfs_getnewfsid(mp, makefstype(MOUNT_NFS));
+#else
 	getnewfsid(mp, makefstype(MOUNT_NFS));
+#endif
 	nmp->nm_mountp = mp;
-	if ((argp->flags & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) ==
-		(NFSMNT_NQNFS | NFSMNT_MYWRITE)) {
-		error = EPERM;
-		goto bad;
-	}
 	if (argp->flags & NFSMNT_NQNFS)
 		/*
 		 * We have to set mnt_maxsymlink to a non-zero value so
@@ -523,13 +654,15 @@
 	nmp->nm_retry = NFS_RETRANS;
 	nmp->nm_wsize = NFS_WSIZE;
 	nmp->nm_rsize = NFS_RSIZE;
+	nmp->nm_readdirsize = NFS_READDIRSIZE;
 	nmp->nm_numgrps = NFS_MAXGRPS;
 	nmp->nm_readahead = NFS_DEFRAHEAD;
 	nmp->nm_leaseterm = NQ_DEFLEASE;
 	nmp->nm_deadthresh = NQ_DEADTHRESH;
 	CIRCLEQ_INIT(&nmp->nm_timerhead);
 	nmp->nm_inprog = NULLVP;
-	bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t));
+	nmp->nm_fhsize = argp->fhsize;
+	bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
 #ifdef COMPAT_09
 	mp->mnt_stat.f_type = 2;
 #else
@@ -568,7 +701,8 @@
 	 * this problem, because one can identify root inodes by their
 	 * number == ROOTINO (2).
 	 */
-	if ((error = nfs_nget(mp, &nmp->nm_fh, &np)) != 0)
+	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+	if (error)
 		goto bad;
 	*vpp = NFSTOV(np);
 
@@ -611,7 +745,8 @@
 	 * the remote root.  See comment in mountnfs().  The VFS unmount()
 	 * has done vput on this vnode, otherwise we would get deadlock!
 	 */
-	if ((error = nfs_nget(mp, &nmp->nm_fh, &np)) != 0)
+	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+	if (error)
 		return(error);
 	vp = NFSTOV(np);
 	if (vp->v_usecount > 2) {
@@ -625,7 +760,8 @@
 	nmp->nm_flag |= NFSMNT_DISMINPROG;
 	while (nmp->nm_inprog != NULLVP)
 		(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
-	if ((error = vflush(mp, vp, flags)) != 0) {
+	error = vflush(mp, vp, flags);
+	if (error) {
 		vput(vp);
 		nmp->nm_flag &= ~NFSMNT_DISMINPROG;
 		return (error);
@@ -666,7 +802,8 @@
 	int error;
 
 	nmp = VFSTONFS(mp);
-	if ((error = nfs_nget(mp, &nmp->nm_fh, &np)) != 0)
+	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+	if (error)
 		return (error);
 	vp = NFSTOV(np);
 	vp->v_type = VDIR;
@@ -706,9 +843,14 @@
 			goto loop;
 		if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
 			continue;
+#ifdef Lite2_integrated
+		if (vget(vp, LK_EXCLUSIVE, p))
+#else
 		if (vget(vp, 1))
+#endif
 			goto loop;
-		if ((error = VOP_FSYNC(vp, cred, waitfor, p)) != 0)
+		error = VOP_FSYNC(vp, cred, waitfor, p);
+		if (error)
 			allerror = error;
 		vput(vp);
 	}
@@ -730,6 +872,52 @@
 	return (EOPNOTSUPP);
 }
 
+#ifdef notyet
+/*
+ * Do that sysctl thang...
+ */
+static int
+nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
+	   size_t newlen, struct proc *p)
+{
+	int rv;
+
+	/*
+	 * All names at this level are terminal.
+	 */
+	if(namelen > 1)
+		return ENOTDIR;	/* overloaded */
+
+	switch(name[0]) {
+	case NFS_NFSSTATS:
+		if(!oldp) {
+			*oldlenp = sizeof nfsstats;
+			return 0;
+		}
+
+		if(*oldlenp < sizeof nfsstats) {
+			*oldlenp = sizeof nfsstats;
+			return ENOMEM;
+		}
+
+		rv = copyout(&nfsstats, oldp, sizeof nfsstats);
+		if(rv) return rv;
+
+		if(newp && newlen != sizeof nfsstats)
+			return EINVAL;
+
+		if(newp) {
+			return copyin(newp, &nfsstats, sizeof nfsstats);
+		}
+		return 0;
+
+	default:
+		return EOPNOTSUPP;
+	}
+}
+#endif
+
+
 /*
  * At this point, this should never happen
  */
--- a/sys/nfs/nfs_vnops.c	Sun Feb 18 09:10:15 1996 +0000
+++ b/sys/nfs/nfs_vnops.c	Sun Feb 18 11:53:36 1996 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_vnops.c,v 1.58 1996/02/09 21:48:41 christos Exp $	*/
+/*	$NetBSD: nfs_vnops.c,v 1.59 1996/02/18 11:53:58 fvdl Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,17 +35,20 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)nfs_vnops.c	8.10 (Berkeley) 8/11/94
+ *	@(#)nfs_vnops.c	8.16 (Berkeley) 5/27/95
  */
 
+
 /*
- * vnode op calls for sun nfs version 2
+ * vnode op calls for Sun NFS version 2 and 3
  */
 
 #include <sys/param.h>
 #include <sys/proc.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/proc.h>
 #include <sys/mount.h>
 #include <sys/buf.h>
 #include <sys/malloc.h>
@@ -53,8 +56,8 @@
 #include <sys/conf.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
-#include <sys/map.h>
 #include <sys/dirent.h>
+#include <sys/fcntl.h>
 #include <sys/lockf.h>
 
 #include <vm/vm.h>
@@ -63,7 +66,7 @@
 #include <miscfs/fifofs/fifo.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsmount.h>
@@ -72,6 +75,10 @@
 #include <nfs/nqnfs.h>
 #include <nfs/nfs_var.h>
 
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
 /* Defs */
 #define	TRUE	1
 #define	FALSE	0
@@ -95,6 +102,9 @@
 	{ &vop_lease_desc, nfs_lease_check },	/* lease */
 	{ &vop_ioctl_desc, nfs_ioctl },		/* ioctl */
 	{ &vop_select_desc, nfs_select },	/* select */
+#ifdef Lite2_integrated
+	{ &vop_revoke_desc, nfs_revoke },	/* revoke */
+#endif
 	{ &vop_mmap_desc, nfs_mmap },		/* mmap */
 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
 	{ &vop_seek_desc, nfs_seek },		/* seek */
@@ -123,7 +133,7 @@
 	{ &vop_vfree_desc, nfs_vfree },		/* vfree */
 	{ &vop_truncate_desc, nfs_truncate },	/* truncate */
 	{ &vop_update_desc, nfs_update },	/* update */
-	{ &vop_bwrite_desc, vn_bwrite },
+	{ &vop_bwrite_desc, nfs_bwrite },
 	{ (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
 };
 struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
@@ -148,6 +158,9 @@
 	{ &vop_lease_desc, spec_lease_check },	/* lease */
 	{ &vop_ioctl_desc, spec_ioctl },	/* ioctl */
 	{ &vop_select_desc, spec_select },	/* select */
+#ifdef Lite2_integrated
+	{ &vop_revoke_desc, spec_revoke },	/* revoke */
+#endif
 	{ &vop_mmap_desc, spec_mmap },		/* mmap */
 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
 	{ &vop_seek_desc, spec_seek },		/* seek */
@@ -182,7 +195,6 @@
 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
 	{ &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
 
-#ifdef FIFO
 int (**fifo_nfsv2nodeop_p) __P((void *));
 struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
 	{ &vop_default_desc, vn_default_error },
@@ -199,6 +211,9 @@
 	{ &vop_lease_desc, fifo_lease_check },	/* lease */
 	{ &vop_ioctl_desc, fifo_ioctl },	/* ioctl */
 	{ &vop_select_desc, fifo_select },	/* select */
+#ifdef Lite2_integrated
+	{ &vop_revoke_desc, fifo_revoke },	/* revoke */
+#endif
 	{ &vop_mmap_desc, fifo_mmap },		/* mmap */
 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
 	{ &vop_seek_desc, fifo_seek },		/* seek */
@@ -232,14 +247,14 @@
 };
 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
 	{ &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
-#endif /* FIFO */
 
 /*
  * Global variables
  */
-extern u_int32_t nfs_procids[NFS_NPROCS];
+extern u_int32_t nfs_true, nfs_false;
 extern u_int32_t nfs_xdrneg1;
-extern u_int32_t nfs_prog, nfs_vers, nfs_true, nfs_false;
+extern struct nfsstats nfsstats;
+extern nfstype nfsv3_type[9];
 struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 int nfs_numasync = 0;
 #define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
@@ -265,9 +280,9 @@
 
 /*
  * nfs access vnode op.
- * For nfs, just return ok. File accesses may fail later.
- * For nqnfs, use the access rpc to check accessibility. If file modes are
- * changed on the server, accesses might still fail later.
+ * For nfs version 2, just return ok. File accesses may fail later.
+ * For nfs version 3, use the access rpc to check accessibility. If file modes
+ * are changed on the server, accesses might still fail later.
  */
 int
 nfs_access(v)
@@ -282,36 +297,71 @@
 	register struct vnode *vp = ap->a_vp;
 	register u_int32_t *tl;
 	register caddr_t cp;
-	caddr_t bpos, dpos;
-	int error = 0;
+	register int32_t t1, t2;
+	caddr_t bpos, dpos, cp2;
+	int error = 0, attrflag;
 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+	u_int32_t mode, rmode;
+	int v3 = NFS_ISV3(vp);
 
 	/*
-	 * For nqnfs, do an access rpc, otherwise you are stuck emulating
+	 * Disallow write attempts on filesystems mounted read-only;
+	 * unless the file is a socket, fifo, or a block or character
+	 * device resident on the filesystem.
+	 */
+	if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+		switch (vp->v_type) {
+		case VREG:
+		case VDIR:
+		case VLNK:
+			return (EROFS);
+		default:
+			break;
+		}
+	}
+	/*
+	 * For nfs v3, do an access rpc, otherwise you are stuck emulating
 	 * ufs_access() locally using the vattr. This may not be correct,
 	 * since the server may apply other access criteria such as
 	 * client uid-->server uid mapping that we do not know about, but
 	 * this is better than just returning anything that is lying about
 	 * in the cache.
 	 */
-	if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
-		nfsstats.rpccnt[NQNFSPROC_ACCESS]++;
-		nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED);
-		nfsm_fhtom(vp);
-		nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+	if (v3) {
+		nfsstats.rpccnt[NFSPROC_ACCESS]++;
+		nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED);
+		nfsm_fhtom(vp, v3);
+		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
 		if (ap->a_mode & VREAD)
-			*tl++ = nfs_true;
+			mode = NFSV3ACCESS_READ;
 		else
-			*tl++ = nfs_false;
-		if (ap->a_mode & VWRITE)
-			*tl++ = nfs_true;
-		else
-			*tl++ = nfs_false;
-		if (ap->a_mode & VEXEC)
-			*tl = nfs_true;
-		else
-			*tl = nfs_false;
-		nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred);
+			mode = 0;
+		if (vp->v_type == VDIR) {
+			if (ap->a_mode & VWRITE)
+				mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND |
+					 NFSV3ACCESS_DELETE);
+			if (ap->a_mode & VEXEC)
+				mode |= NFSV3ACCESS_LOOKUP;
+		} else {
+			if (ap->a_mode & VWRITE)
+				mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
+			if (ap->a_mode & VEXEC)
+				mode |= NFSV3ACCESS_EXECUTE;
+		}
+		*tl = txdr_unsigned(mode);
+		nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred);
+		nfsm_postop_attr(vp, attrflag);
+		if (!error) {
+			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+			rmode = fxdr_unsigned(u_int32_t, *tl);
+			/*
+			 * The NFS V3 spec does not clarify whether or not
+			 * the returned access bits can be a superset of
+			 * the ones requested, so...
+			 */
+			if ((rmode & mode) != mode)
+				error = EACCES;
+		}
 		nfsm_reqdone;
 		return (error);
 	} else
@@ -342,16 +392,20 @@
 	struct vattr vattr;
 	int error;
 
-	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
+	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) {
+#ifdef DIAGNOSTIC
+		printf("open eacces vtyp=%d\n",vp->v_type);
+#endif
 		return (EACCES);
-	if (vp->v_flag & VTEXT) {
-	    /*
-	     * Get a valid lease. If cached data is stale, flush it.
-	     */
-	    if (nmp->nm_flag & NFSMNT_NQNFS) {
-		if (NQNFS_CKINVALID(vp, np, NQL_READ)) {
+	}
+	/*
+	 * Get a valid lease. If cached data is stale, flush it.
+	 */
+	if (nmp->nm_flag & NFSMNT_NQNFS) {
+		if (NQNFS_CKINVALID(vp, np, ND_READ)) {
 		    do {
-			error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p);
+			error = nqnfs_getlease(vp, ND_READ, ap->a_cred,
+			    ap->a_p);
 		    } while (error == NQNFS_EXPIRED);
 		    if (error)
 			return (error);
@@ -364,14 +418,15 @@
 			np->n_brev = np->n_lrev;
 		    }
 		}
-	    } else {
+	} else {
 		if (np->n_flag & NMODIFIED) {
 			if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
 				ap->a_p, 1)) == EINTR)
 				return (error);
 			(void) vnode_pager_uncache(vp);
 			np->n_attrstamp = 0;
-			np->n_direofoffset = 0;
+			if (vp->v_type == VDIR)
+				np->n_direofoffset = 0;
 			error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
 			if (error)
 				return (error);
@@ -381,7 +436,8 @@
 			if (error)
 				return (error);
 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
-				np->n_direofoffset = 0;
+				if (vp->v_type == VDIR)
+					np->n_direofoffset = 0;
 				if ((error = nfs_vinvalbuf(vp, V_SAVE,
 					ap->a_cred, ap->a_p, 1)) == EINTR)
 					return (error);
@@ -389,15 +445,41 @@
 				np->n_mtime = vattr.va_mtime.tv_sec;
 			}
 		}
-	    }
-	} else if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+	}
+	if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
 		np->n_attrstamp = 0; /* For Open/Close consistency */
 	return (0);
 }
 
 /*
  * nfs close vnode op
- * For reg files, invalidate any buffer cache entries.
+ * What an NFS client should do upon close after writing is a debatable issue.
+ * Most NFS clients push delayed writes to the server upon close, basically for
+ * two reasons:
+ * 1 - So that any write errors may be reported back to the client process
+ *     doing the close system call. By far the two most likely errors are
+ *     NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
+ * 2 - To put a worst case upper bound on cache inconsistency between
+ *     multiple clients for the file.
+ * There is also a consistency problem for Version 2 of the protocol w.r.t.
+ * not being able to tell if other clients are writing a file concurrently,
+ * since there is no way of knowing if the changed modify time in the reply
+ * is only due to the write for this client.
+ * (NFS Version 3 provides weak cache consistency data in the reply that
+ *  should be sufficient to detect and handle this case.)
+ *
+ * The current code does the following:
+ * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
+ * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
+ *                     or commit them (this satisfies 1 and 2 except for the
+ *                     case where the server crashes after this close but
+ *                     before the commit RPC, which is felt to be "good
+ *                     enough". Changing the last argument to nfs_flush() to
+ *                     a 1 would force a commit operation, if it is felt a
+ *                     commit is necessary now.
+ * for NQNFS         - do nothing now, since 2 is dealt with via leases and
+ *                     1 should be dealt with via an fsync() system call for
+ *                     cases where write errors are important.
  */
 /* ARGSUSED */
 int
@@ -418,7 +500,11 @@
 	if (vp->v_type == VREG) {
 	    if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
 		(np->n_flag & NMODIFIED)) {
-		error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
+		if (NFS_ISV3(vp)) {
+		    error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0);
+		    np->n_flag &= ~NMODIFIED;
+		} else
+		    error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
 		np->n_attrstamp = 0;
 	    }
 	    if (np->n_flag & NWRITEERR) {
@@ -445,9 +531,12 @@
 	register struct vnode *vp = ap->a_vp;
 	register struct nfsnode *np = VTONFS(vp);
 	register caddr_t cp;
+	register u_int32_t *tl;
+	register int32_t t1, t2;
 	caddr_t bpos, dpos;
 	int error = 0;
 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+	int v3 = NFS_ISV3(vp);
 	
 	/*
 	 * Update local times for special files.
@@ -460,10 +549,11 @@
 	if (nfs_getattrcache(vp, ap->a_vap) == 0)
 		return (0);
 	nfsstats.rpccnt[NFSPROC_GETATTR]++;
-	nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH);
-	nfsm_fhtom(vp);
+	nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3));
+	nfsm_fhtom(vp, v3);
 	nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred);
-	nfsm_loadattr(vp, ap->a_vap);
+	if (!error)
+		nfsm_loadattr(vp, ap->a_vap);
 	nfsm_reqdone;
 	return (error);
 }
@@ -482,92 +572,63 @@
 		struct ucred *a_cred;
 		struct proc *a_p;
 	} */ *ap = v;
-	register struct nfsv2_sattr *sp;
-	register caddr_t cp;
-	register int32_t t1;
-	caddr_t bpos, dpos, cp2;
-	u_int32_t *tl;
-	int error = 0, isnq;
-	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
 	register struct vnode *vp = ap->a_vp;
 	register struct nfsnode *np = VTONFS(vp);
 	register struct vattr *vap = ap->a_vap;
-	u_quad_t frev, tsize = 0;
+	int error = 0;
+	u_quad_t tsize = 0;
 
+	/*
+	 * Disallow write attempts if the filesystem is mounted read-only.
+	 */
+  	if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
+	    vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
+	    vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
+	    (vp->v_mount->mnt_flag & MNT_RDONLY))
+		return (EROFS);
 	if (vap->va_size != VNOVAL) {
-		switch (vp->v_type) {
-		case VDIR:
-			return (EISDIR);
-		case VCHR:
-		case VBLK:
+ 		switch (vp->v_type) {
+ 		case VDIR:
+ 			return (EISDIR);
+ 		case VCHR:
+ 		case VBLK:
+ 		case VSOCK:
+ 		case VFIFO:
 			if (vap->va_mtime.tv_sec == VNOVAL &&
 			    vap->va_atime.tv_sec == VNOVAL &&
 			    vap->va_mode == (u_short)VNOVAL &&
-			    vap->va_uid == VNOVAL &&
-			    vap->va_gid == VNOVAL)
+			    vap->va_uid == (uid_t)VNOVAL &&
+			    vap->va_gid == (gid_t)VNOVAL)
 				return (0);
-			vap->va_size = VNOVAL;
-			break;
-		default:
-			if (np->n_flag & NMODIFIED) {
-				if (vap->va_size == 0)
-					error = nfs_vinvalbuf(vp, 0,
-						ap->a_cred, ap->a_p, 1);
-				else
-					error = nfs_vinvalbuf(vp, V_SAVE,
-						ap->a_cred, ap->a_p, 1);
-				if (error)
-					return (error);
-			}
-			tsize = np->n_size;
-			np->n_size = np->n_vattr.va_size = vap->va_size;
-			vnode_pager_setsize(vp, (u_long)np->n_size);
-		}
-	} else if ((vap->va_mtime.tv_sec != VNOVAL ||
-	    vap->va_atime.tv_sec != VNOVAL) && (np->n_flag & NMODIFIED)) {
-		error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
-		if (error == EINTR)
-			return (error);
-	}
-	nfsstats.rpccnt[NFSPROC_SETATTR]++;
-	isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
-	nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq));
-	nfsm_fhtom(vp);
-	nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
-	if (vap->va_mode == (u_short)VNOVAL)
-		sp->sa_mode = nfs_xdrneg1;
-	else
-		sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode);
-	if (vap->va_uid == VNOVAL)
-		sp->sa_uid = nfs_xdrneg1;
-	else
-		sp->sa_uid = txdr_unsigned(vap->va_uid);
-	if (vap->va_gid == VNOVAL)
-		sp->sa_gid = nfs_xdrneg1;
-	else
-		sp->sa_gid = txdr_unsigned(vap->va_gid);
-	if (isnq) {
-		txdr_hyper(&vap->va_size, &sp->sa_nqsize);
-		txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
-		txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
-		sp->sa_nqflags = txdr_unsigned(vap->va_flags);
-		sp->sa_nqrdev = nfs_xdrneg1;
-	} else {
-		sp->sa_nfssize = txdr_unsigned(vap->va_size);
-		txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
-		txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
-	}
-	nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred);
-	nfsm_loadattr(vp, (struct vattr *)0);
-	if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) &&
-	    NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
-		nfsm_dissect(tl, u_int32_t *, 2*NFSX_UNSIGNED);
-		fxdr_hyper(tl, &frev);
-		if (frev > np->n_brev)
-			np->n_brev = frev;
-	}
-	nfsm_reqdone;
-	if (error) {
+ 			vap->va_size = VNOVAL;
+ 			break;
+ 		default:
+			/*
+			 * Disallow write attempts if the filesystem is
+			 * mounted read-only.
+			 */
+			if (vp->v_mount->mnt_flag & MNT_RDONLY)
+				return (EROFS);
+ 			if (vap->va_size == 0)
+ 				error = nfs_vinvalbuf(vp, 0,
+ 				     ap->a_cred, ap->a_p, 1);
+			else
+				error = nfs_vinvalbuf(vp, V_SAVE,
+				     ap->a_cred, ap->a_p, 1);
+			if (error)
+				return (error);
+ 			tsize = np->n_size;
+ 			np->n_size = np->n_vattr.va_size = vap->va_size;
+ 			vnode_pager_setsize(vp, (u_long)np->n_size);
+  		};
+  	} else if ((vap->va_mtime.tv_sec != VNOVAL ||
+		vap->va_atime.tv_sec != VNOVAL) &&
+		vp->v_type == VREG &&
+  		(error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+		 ap->a_p, 1)) == EINTR)
+		return (error);
+	error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p);
+	if (error && vap->va_size != VNOVAL) {
 		np->n_size = np->n_vattr.va_size = tsize;
 		vnode_pager_setsize(vp, (u_long)np->n_size);
 	}
@@ -575,6 +636,116 @@
 }
 
 /*
+ * Do an nfs setattr rpc.
+ */
+int
+nfs_setattrrpc(vp, vap, cred, procp)
+	register struct vnode *vp;
+	register struct vattr *vap;
+	struct ucred *cred;
+	struct proc *procp;
+{
+	register struct nfsv2_sattr *sp;
+	register caddr_t cp;
+	register int32_t t1, t2;
+	caddr_t bpos, dpos, cp2;
+	u_int32_t *tl;
+	int error = 0, wccflag = NFSV3_WCCRATTR;
+	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+	int v3 = NFS_ISV3(vp);
+
+	nfsstats.rpccnt[NFSPROC_SETATTR]++;
+	nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3));
+	nfsm_fhtom(vp, v3);
+	if (v3) {
+		if (vap->va_mode != (u_short)VNOVAL) {
+			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			*tl++ = nfs_true;
+			*tl = txdr_unsigned(vap->va_mode);
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = nfs_false;
+		}
+		if (vap->va_uid != (uid_t)VNOVAL) {
+			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			*tl++ = nfs_true;
+			*tl = txdr_unsigned(vap->va_uid);
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = nfs_false;
+		}
+		if (vap->va_gid != (gid_t)VNOVAL) {
+			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+			*tl++ = nfs_true;
+			*tl = txdr_unsigned(vap->va_gid);
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = nfs_false;
+		}
+		if (vap->va_size != VNOVAL) {
+			nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+			*tl++ = nfs_true;
+			txdr_hyper(&vap->va_size, tl);
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = nfs_false;
+		}
+		if (vap->va_atime.tv_sec != VNOVAL) {
+			if (vap->va_atime.tv_sec != time.tv_sec) {
+				nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
+				txdr_nfsv3time(&vap->va_atime, tl);
+			} else {
+				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
+			}
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
+		}
+		if (vap->va_mtime.tv_sec != VNOVAL) {
+			if (vap->va_mtime.tv_sec != time.tv_sec) {
+				nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
+				txdr_nfsv3time(&vap->va_atime, tl);
+			} else {
+				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
+			}
+		} else {
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
+		}
+		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+		*tl = nfs_false;
+	} else {
+		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+		if (vap->va_mode == (u_short)VNOVAL)
+			sp->sa_mode = nfs_xdrneg1;
+		else
+			sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode);
+		if (vap->va_uid == (uid_t)VNOVAL)
+			sp->sa_uid = nfs_xdrneg1;
+		else
+			sp->sa_uid = txdr_unsigned(vap->va_uid);
+		if (vap->va_gid == (gid_t)VNOVAL)
+			sp->sa_gid = nfs_xdrneg1;