massive update to incorporate version 3 protocol from Rick Macklem
authorKirk McKusick <mckusick@ucbvax.Berkeley.EDU>
Fri, 31 Mar 1995 02:27:00 +0000 (18:27 -0800)
committerKirk McKusick <mckusick@ucbvax.Berkeley.EDU>
Fri, 31 Mar 1995 02:27:00 +0000 (18:27 -0800)
SCCS-vsn: sys/nfs/xdr_subs.h 8.3
SCCS-vsn: sys/nfs/nfsnode.h 8.8
SCCS-vsn: sys/nfs/nfsrvcache.h 8.3
SCCS-vsn: sys/nfs/nfsm_subs.h 8.2
SCCS-vsn: sys/nfs/nfsmount.h 8.3
SCCS-vsn: sys/nfs/rpcv2.h 8.2
SCCS-vsn: sys/nfs/nfs.h 8.3
SCCS-vsn: sys/nfs/nfs_subs.c 8.6
SCCS-vsn: sys/nfs/nfs_serv.c 8.6
SCCS-vsn: sys/nfs/nfs_srvcache.c 8.3
SCCS-vsn: sys/nfs/nfs_node.c 8.4
SCCS-vsn: sys/nfs/nfs_syscalls.c 8.5
SCCS-vsn: sys/nfs/nfs_vfsops.c 8.5
SCCS-vsn: sys/nfs/nfs_vnops.c 8.14
SCCS-vsn: sys/nfs/nfs_socket.c 8.5
SCCS-vsn: sys/nfs/nfsdiskless.h 8.2
SCCS-vsn: sys/nfs/nfs_bio.c 8.9
SCCS-vsn: sys/nfs/nfs_nqlease.c 8.6
SCCS-vsn: sys/nfs/nfsrtt.h 8.2
SCCS-vsn: sys/nfs/nqnfs.h 8.3
SCCS-vsn: sys/nfs/nfsproto.h 8.2

21 files changed:
usr/src/sys/nfs/nfs.h
usr/src/sys/nfs/nfs_bio.c
usr/src/sys/nfs/nfs_node.c
usr/src/sys/nfs/nfs_nqlease.c
usr/src/sys/nfs/nfs_serv.c
usr/src/sys/nfs/nfs_socket.c
usr/src/sys/nfs/nfs_srvcache.c
usr/src/sys/nfs/nfs_subs.c
usr/src/sys/nfs/nfs_syscalls.c
usr/src/sys/nfs/nfs_vfsops.c
usr/src/sys/nfs/nfs_vnops.c
usr/src/sys/nfs/nfsdiskless.h
usr/src/sys/nfs/nfsm_subs.h
usr/src/sys/nfs/nfsmount.h
usr/src/sys/nfs/nfsnode.h
usr/src/sys/nfs/nfsproto.h
usr/src/sys/nfs/nfsrtt.h
usr/src/sys/nfs/nfsrvcache.h
usr/src/sys/nfs/nqnfs.h
usr/src/sys/nfs/rpcv2.h
usr/src/sys/nfs/xdr_subs.h

index 835a14f..65805d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * 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
  *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -7,34 +7,71 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs.h       8.2 (Berkeley) %G%
+ *     @(#)nfs.h       8.3 (Berkeley) %G%
  */
 
  */
 
+#ifndef _NFS_NFS_H_
+#define _NFS_NFS_H_
+
 /*
  * Tunable constants for nfs
  */
 
 #define        NFS_MAXIOVEC    34
 /*
  * 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 */
 #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 */
 #define        NFS_MINATTRTIMO 5               /* Attribute cache timeout in sec */
+#endif
+#ifndef NFS_MAXATTRTIMO
 #define        NFS_MAXATTRTIMO 60
 #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_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_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_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
+#define        NFS_DIRBLKSIZ   4096            /* Must be a multiple of DIRBLKSIZ */
+
+/*
+ * Oddballs
+ */
 #define        NMOD(a)         ((a) % nfs_asyncdaemons)
 #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)
+
+/*
+ * XXX
+ * 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.
 
 /*
  * Set the attribute timeout based on how recently the file has been modified.
         ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
          (time.tv_sec - (np)->n_mtime) / 10))
 
         ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
          (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
+
+/*
+ * Arguments to mount NFS
+ */
+struct nfs_args {
+       struct sockaddr *addr;          /* file server address */
+       int             addrlen;        /* length of address */
+       int             sotype;         /* Socket type */
+       int             proto;          /* and Protocol */
+       u_char          *fh;            /* File handle to be mounted */
+       int             fhsize;         /* Size, in bytes, of fh */
+       int             flags;          /* flags */
+       int             wsize;          /* write size in bytes */
+       int             rsize;          /* read size in bytes */
+       int             readdirsize;    /* readdir size in bytes */
+       int             timeo;          /* initial timeout in .1 secs */
+       int             retrans;        /* times to retry send */
+       int             maxgrouplist;   /* Max. size of group list */
+       int             readahead;      /* # of blocks to readahead */
+       int             leaseterm;      /* Term (sec) of lease */
+       int             deadthresh;     /* Retrans threshold */
+       char            *hostname;      /* server's name */
+};
+
+/*
+ * NFS mount option flags
+ */
+#define        NFSMNT_SOFT             0x00000001  /* soft mount (hard is default) */
+#define        NFSMNT_WSIZE            0x00000002  /* set write size */
+#define        NFSMNT_RSIZE            0x00000004  /* set read size */
+#define        NFSMNT_TIMEO            0x00000008  /* set initial timeout */
+#define        NFSMNT_RETRANS          0x00000010  /* set number of request retries */
+#define        NFSMNT_MAXGRPS          0x00000020  /* set maximum grouplist size */
+#define        NFSMNT_INT              0x00000040  /* allow interrupts on hard mount */
+#define        NFSMNT_NOCONN           0x00000080  /* Don't Connect the socket */
+#define        NFSMNT_NQNFS            0x00000100  /* Use Nqnfs protocol */
+#define        NFSMNT_NFSV3            0x00000200  /* Use NFS Version 3 protocol */
+#define        NFSMNT_KERB             0x00000400  /* Use Kerberos authentication */
+#define        NFSMNT_DUMBTIMR         0x00000800  /* Don't estimate rtt dynamically */
+#define        NFSMNT_LEASETERM        0x00001000  /* set lease term (nqnfs) */
+#define        NFSMNT_READAHEAD        0x00002000  /* set read ahead */
+#define        NFSMNT_DEADTHRESH       0x00004000  /* set dead server retry thresh */
+#define        NFSMNT_RESVPORT         0x00008000  /* Allocate a reserved port */
+#define        NFSMNT_RDIRPLUS         0x00010000  /* Use Readdirplus for V3 */
+#define        NFSMNT_READDIRSIZE      0x00020000  /* Set readdir size */
+#define        NFSMNT_INTERNAL         0xfffc0000  /* Bits set internally */
+#define NFSMNT_HASWRITEVERF    0x00040000  /* Has write verifier for V3 */
+#define NFSMNT_GOTPATHCONF     0x00080000  /* Got the V3 pathconf info */
+#define NFSMNT_GOTFSINFO       0x00100000  /* Got the V3 fsinfo */
+#define        NFSMNT_MNTD             0x00200000  /* Mnt server for mnt point */
+#define        NFSMNT_DISMINPROG       0x00400000  /* Dismount in progress */
+#define        NFSMNT_DISMNT           0x00800000  /* Dismounted */
+#define        NFSMNT_SNDLOCK          0x01000000  /* Send socket lock */
+#define        NFSMNT_WANTSND          0x02000000  /* Want above */
+#define        NFSMNT_RCVLOCK          0x04000000  /* Rcv socket lock */
+#define        NFSMNT_WANTRCV          0x08000000  /* Want above */
+#define        NFSMNT_WAITAUTH         0x10000000  /* Wait for authentication */
+#define        NFSMNT_HASAUTH          0x20000000  /* Has authenticator */
+#define        NFSMNT_WANTAUTH         0x40000000  /* Wants an authenticator */
+#define        NFSMNT_AUTHERR          0x80000000  /* Authentication error */
+
 /*
  * 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 */
 /*
  * 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 */
 };
 
        int     namelen;        /* Length of name */
 };
 
@@ -61,7 +172,12 @@ struct nfsd_srvargs {
        u_long          nsd_haddr;      /* Ip address of client */
        struct ucred    nsd_cr;         /* Cred. uid maps to */
        int             nsd_authlen;    /* Length of auth string (ret) */
        u_long          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_long          nsd_ttl;        /* credential ttl (sec) */
+       NFSKERBKEY_T    nsd_key;        /* Session key */
 };
 
 struct nfsd_cargs {
 };
 
 struct nfsd_cargs {
@@ -69,7 +185,10 @@ struct nfsd_cargs {
        uid_t           ncd_authuid;    /* Effective uid */
        int             ncd_authtype;   /* Type of authenticator */
        int             ncd_authlen;    /* Length of authenticator string */
        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 */
 };
 
 /*
 };
 
 /*
@@ -108,6 +227,7 @@ struct nfsstats {
        int     srvnqnfs_leases;
        int     srvnqnfs_maxleases;
        int     srvnqnfs_getleases;
        int     srvnqnfs_leases;
        int     srvnqnfs_maxleases;
        int     srvnqnfs_getleases;
+       int     srvvop_writes;
 };
 
 /*
 };
 
 /*
@@ -121,6 +241,16 @@ struct nfsstats {
 #define        NFSSVC_AUTHINFAIL 0x080
 #define        NFSSVC_MNTD     0x100
 
 #define        NFSSVC_AUTHINFAIL 0x080
 #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
 /*
  * 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
@@ -130,6 +260,9 @@ struct nfsstats {
  * by them and break.
  */
 #ifdef KERNEL
  * 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))
 
 #define        NFSINT_SIGMASK  (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
                         sigmask(SIGHUP)|sigmask(SIGQUIT))
 
@@ -165,7 +298,7 @@ struct nfsreq {
 /*
  * Queue head for nfsreq's
  */
 /*
  * Queue head for nfsreq's
  */
-TAILQ_HEAD(nfsreqs, nfsreq) nfs_reqq;
+TAILQ_HEAD(, nfsreq) nfs_reqq;
 
 /* Flag values for r_flags */
 #define R_TIMING       0x01            /* timing request (in mntp) */
 
 /* Flag values for r_flags */
 #define R_TIMING       0x01            /* timing request (in mntp) */
@@ -177,17 +310,29 @@ TAILQ_HEAD(nfsreqs, nfsreq) nfs_reqq;
 #define        R_MUSTRESEND    0x40            /* Must resend request */
 #define        R_GETONEREP     0x80            /* Probe for one reply only */
 
 #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.
  */
 /*
  * 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) \
 #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
 
 /*
  * Network address hash list element
@@ -201,35 +346,41 @@ struct nfsuid {
        TAILQ_ENTRY(nfsuid) nu_lru;     /* LRU chain */
        LIST_ENTRY(nfsuid) nu_hash;     /* Hash list */
        int             nu_flag;        /* Flags */
        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 */
        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_long          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_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 */
 
 struct nfssvc_sock {
        TAILQ_ENTRY(nfssvc_sock) ns_chain;      /* List of all nfssvc_sock's */
-       TAILQ_HEAD(nfsuidlru, nfsuid) ns_uidlruhead;
-       LIST_HEAD(nfsuidhash, nfsuid) *ns_uidhashtbl;
-       u_long          ns_uidhash;
-
-       int             ns_flag;
-       u_long          ns_sref;
+       TAILQ_HEAD(, nfsuid) ns_uidlruhead;
        struct file     *ns_fp;
        struct socket   *ns_so;
        struct file     *ns_fp;
        struct socket   *ns_so;
-       int             ns_solock;
        struct mbuf     *ns_nam;
        struct mbuf     *ns_nam;
-       int             ns_cc;
        struct mbuf     *ns_raw;
        struct mbuf     *ns_rawend;
        struct mbuf     *ns_raw;
        struct mbuf     *ns_rawend;
-       int             ns_reclen;
        struct mbuf     *ns_rec;
        struct mbuf     *ns_recend;
        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;
        int             ns_numuids;
+       u_long          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" */
 };
 
 /* Bits for "ns_flag" */
@@ -238,9 +389,10 @@ struct nfssvc_sock {
 #define        SLP_NEEDQ       0x04
 #define        SLP_DISCONN     0x08
 #define        SLP_GETSTREAM   0x10
 #define        SLP_NEEDQ       0x04
 #define        SLP_DISCONN     0x08
 #define        SLP_GETSTREAM   0x10
+#define        SLP_LASTFRAG    0x20
 #define SLP_ALLFLAGS   0xff
 
 #define SLP_ALLFLAGS   0xff
 
-TAILQ_HEAD(nfssvc_socks, nfssvc_sock) nfssvc_sockhead;
+TAILQ_HEAD(, nfssvc_sock) nfssvc_sockhead;
 int nfssvc_sockhead_flag;
 #define        SLP_INIT        0x01
 #define        SLP_WANTINIT    0x02
 int nfssvc_sockhead_flag;
 #define        SLP_INIT        0x01
 #define        SLP_WANTINIT    0x02
@@ -249,32 +401,138 @@ int nfssvc_sockhead_flag;
  * One of these structures is allocated for each nfsd.
  */
 struct nfsd {
  * 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_long          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
 
 #define        NFSD_WAITING    0x01
 #define        NFSD_REQINPROG  0x02
 #define        NFSD_NEEDAUTH   0x04
 #define        NFSD_AUTHFAIL   0x08
 
-TAILQ_HEAD(nfsds, nfsd) nfsd_head;
+/*
+ * 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_long                  nd_retxid;      /* Reply xid */
+       u_long                  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
 
 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)))
+
+int    nfs_reply __P((struct nfsreq *));
+int    nfs_getreq __P((struct nfsrv_descript *,struct nfsd *,int));
+int    nfs_send __P((struct socket *,struct mbuf *,struct mbuf *,struct nfsreq *));
+int    nfs_rephead __P((int,struct nfsrv_descript *,struct nfssvc_sock *,int,int,u_quad_t *,struct mbuf **,struct mbuf **,caddr_t *));
+int    nfs_sndlock __P((int *,struct nfsreq *));
+int    nfs_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *));
+int    nfs_vinvalbuf __P((struct vnode *,int,struct ucred *,struct proc *,int));
+int    nfs_readrpc __P((struct vnode *,struct uio *,struct ucred *));
+int    nfs_writerpc __P((struct vnode *,struct uio *,struct ucred *,int *,int *));
+int    nfs_readdirrpc __P((register struct vnode *,struct uio *,struct ucred *));
+int    nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *));
+int    nfs_asyncio __P((struct buf *,struct ucred *));
+int    nfs_doio __P((struct buf *,struct ucred *,struct proc *));
+int    nfs_readlinkrpc __P((struct vnode *,struct uio *,struct ucred *));
+int    nfs_sigintr __P((struct nfsmount *,struct nfsreq *r,struct proc *));
+int    nfs_readdirplusrpc __P((struct vnode *,register struct uio *,struct ucred *));
+int    nfsm_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *));
+void   nfsm_srvfattr __P((struct nfsrv_descript *,struct vattr *,struct nfs_fattr *));
+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 **));
+int    nfsrv_fhtovp __P((fhandle_t *,int,struct vnode **,struct ucred *,struct nfssvc_sock *,struct mbuf *,int *,int));
+int    nfsrv_access __P((struct vnode *,int,struct ucred *,int,struct proc *));
+int    netaddr_match __P((int,union nethostaddr *,struct mbuf *));
+int    nfs_request __P((struct vnode *,struct mbuf *,int,struct proc *,struct ucred *,struct mbuf **,struct mbuf **,caddr_t *));
+int    nfs_loadattrcache __P((struct vnode **,struct mbuf **,caddr_t *,struct vattr *));
+int    nfs_namei __P((struct nameidata *,fhandle_t *,int,struct nfssvc_sock *,struct mbuf *,struct mbuf **,caddr_t *,struct vnode **,struct proc *,int));
+void   nfsm_adj __P((struct mbuf *,int,int));
+int    nfsm_mbuftouio __P((struct mbuf **,struct uio *,int,caddr_t *));
+void   nfsrv_initcache __P((void));
+int    nfs_rcvlock __P((struct nfsreq *));
+int    nfs_getauth __P((struct nfsmount *,struct nfsreq *,struct ucred *,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 *));
+int    nfs_msg __P((struct proc *,char *,char *));
+int    nfs_adv __P((struct mbuf **,caddr_t *,int,int));
+int    nfsrv_getstream __P((struct nfssvc_sock *,int));
+void   nfs_nhinit __P((void));
+void   nfs_timer __P((void*));
+u_long nfs_hash __P((nfsfh_t *,int));
+int    nfssvc_iod __P((struct proc *));
+int    nfssvc_nfsd __P((struct nfsd_srvargs *,caddr_t,struct proc *));
+int    nfssvc_addsock __P((struct file *,struct mbuf *));
+int    nfsrv_dorec __P((struct nfssvc_sock *,struct nfsd *,struct nfsrv_descript **));
+int    nfsrv_getcache __P((struct nfsrv_descript *,struct nfssvc_sock *,struct mbuf **));
+void   nfsrv_updatecache __P((struct nfsrv_descript *,int,struct mbuf *));
+int    mountnfs __P((struct nfs_args *,struct mount *,struct mbuf *,char *,char *,struct vnode **));
+int    nfs_connect __P((struct nfsmount *,struct nfsreq *));
+int    nfs_getattrcache __P((struct vnode *,struct vattr *));
+int    nfsm_strtmbuf __P((struct mbuf **,char **,char *,long));
+int    nfs_bioread __P((struct vnode *,struct uio *,int,struct ucred *));
+int    nfsm_uiotombuf __P((struct uio *,struct mbuf **,int,caddr_t *));
+void   nfsrv_init __P((int));
+void   nfs_clearcommit __P((struct mount *));
+int    nfsrv_errmap __P((struct nfsrv_descript *, int));
+void   nfsrvw_coalesce __P((struct nfsrv_descript *,struct nfsrv_descript *));
+void   nfsrvw_sort __P((gid_t [],int));
+void   nfsrv_setcred __P((struct ucred *,struct ucred *));
+int    nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int));
+int    nfs_writebp __P((struct buf *,int));
 #endif /* KERNEL */
 #endif /* KERNEL */
+
+#endif
index ab833ef..28c9323 100644 (file)
@@ -7,12 +7,14 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_bio.c   8.8 (Berkeley) %G%
+ *     @(#)nfs_bio.c   8.9 (Berkeley) %G%
  */
 
  */
 
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/resourcevar.h>
 #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>
 #include <sys/proc.h>
 #include <sys/buf.h>
 #include <sys/vnode.h>
 
 #include <vm/vm.h>
 
 
 #include <vm/vm.h>
 
-#include <nfs/nfsnode.h>
 #include <nfs/rpcv2.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/nfs.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nqnfs.h>
+#include <nfs/nfsnode.h>
 
 
-struct buf *incore(), *nfs_getcacheblk();
+struct buf *nfs_getcacheblk();
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 extern int nfs_numasync;
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 extern int nfs_numasync;
+extern struct nfsstats nfsstats;
 
 /*
  * Vnode op for read using bio
  * Any similarity to readip() is purely coincidental
  */
 
 /*
  * Vnode op for read using bio
  * Any similarity to readip() is purely coincidental
  */
+int
 nfs_bioread(vp, uio, ioflag, cred)
        register struct vnode *vp;
        register struct uio *uio;
 nfs_bioread(vp, uio, ioflag, cred)
        register struct vnode *vp;
        register struct uio *uio;
@@ -44,29 +48,28 @@ nfs_bioread(vp, uio, ioflag, cred)
        struct ucred *cred;
 {
        register struct nfsnode *np = VTONFS(vp);
        struct ucred *cred;
 {
        register struct nfsnode *np = VTONFS(vp);
-       register int biosize, diff;
-       struct buf *bp, *rabp;
+       register int biosize, diff, i;
+       struct buf *bp = 0, *rabp;
        struct vattr vattr;
        struct proc *p;
        struct vattr vattr;
        struct proc *p;
-       struct nfsmount *nmp;
-       daddr_t lbn, bn, rabn;
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       daddr_t lbn, bn, bn2, rabn;
        caddr_t baddr;
        caddr_t baddr;
-       int got_buf, nra, error = 0, n, on, not_readin;
+       int got_buf = 0, nra, error = 0, n = 0, on = 0, not_readin;
+       nfsquad_t tquad;
 
 
-#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);
 #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);
                return (EINVAL);
-       nmp = VFSTONFS(vp->v_mount);
-       biosize = nmp->nm_rsize;
        p = uio->uio_procp;
        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;
        /*
         * For nfs, cache consistency can only be maintained approximately.
         * Although RFC1094 does not specify the criteria, the following is
        /*
         * For nfs, cache consistency can only be maintained approximately.
         * Although RFC1094 does not specify the criteria, the following is
@@ -79,8 +82,6 @@ nfs_bioread(vp, uio, ioflag, cred)
         * 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.
         * 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
         * 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
@@ -88,22 +89,28 @@ nfs_bioread(vp, uio, ioflag, cred)
         */
        if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) {
                if (np->n_flag & NMODIFIED) {
         */
        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 (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                       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;
                                        return (error);
                        }
                        np->n_attrstamp = 0;
-                       np->n_direofoffset = 0;
-                       if (error = VOP_GETATTR(vp, &vattr, cred, p))
+                       error = VOP_GETATTR(vp, &vattr, cred, p);
+                       if (error)
                                return (error);
                        np->n_mtime = vattr.va_mtime.ts_sec;
                } else {
                                return (error);
                        np->n_mtime = vattr.va_mtime.ts_sec;
                } else {
-                       if (error = VOP_GETATTR(vp, &vattr, cred, p))
+                       error = VOP_GETATTR(vp, &vattr, cred, p);
+                       if (error)
                                return (error);
                        if (np->n_mtime != vattr.va_mtime.ts_sec) {
                                return (error);
                        if (np->n_mtime != vattr.va_mtime.ts_sec) {
-                               np->n_direofoffset = 0;
-                               if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                               if (vp->v_type == VDIR)
+                                       nfs_invaldir(vp);
+                               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                               if (error)
                                        return (error);
                                np->n_mtime = vattr.va_mtime.ts_sec;
                        }
                                        return (error);
                                np->n_mtime = vattr.va_mtime.ts_sec;
                        }
@@ -115,58 +122,55 @@ nfs_bioread(vp, uio, ioflag, cred)
             * Get a valid lease. If cached data is stale, flush it.
             */
            if (nmp->nm_flag & NFSMNT_NQNFS) {
             * 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 {
                    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)) {
                    } 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 (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                       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)) {
                            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);
-                   if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                   nfs_invaldir(vp);
+                   error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                   if (error)
                        return (error);
                }
            }
            if (np->n_flag & NQNFSNONCACHE) {
                switch (vp->v_type) {
                case VREG:
                        return (error);
                }
            }
            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:
                case VLNK:
-                       error = nfs_readlinkrpc(vp, uio, cred);
-                       break;
+                       return (nfs_readlinkrpc(vp, uio, cred));
                case VDIR:
                case VDIR:
-                       error = nfs_readdirrpc(vp, uio, cred);
                        break;
                        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;
            }
            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.
                 */
                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);
                    for (nra = 0; nra < nmp->nm_readahead &&
                        (lbn + 1 + nra) * biosize < np->n_size; nra++) {
                        rabn = (lbn + 1 + nra) * (biosize / DEV_BSIZE);
@@ -205,7 +209,8 @@ again:
                        if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
                                bp->b_flags |= B_READ;
                                not_readin = 0;
                        if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
                                bp->b_flags |= B_READ;
                                not_readin = 0;
-                               if (error = nfs_doio(bp, cred, p)) {
+                               error = nfs_doio(bp, cred, p);
+                               if (error) {
                                    brelse(bp);
                                    return (error);
                                }
                                    brelse(bp);
                                    return (error);
                                }
@@ -223,7 +228,7 @@ again:
                                        return (EINTR);
                                    got_buf = 1;
                                }
                                        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");
                                if (bp->b_dirtyend > 0) {
                                    if ((bp->b_flags & B_DELWRI) == 0)
                                        panic("nfsbioread");
@@ -246,7 +251,8 @@ again:
                        return (EINTR);
                if ((bp->b_flags & B_DONE) == 0) {
                        bp->b_flags |= B_READ;
                        return (EINTR);
                if ((bp->b_flags & B_DONE) == 0) {
                        bp->b_flags |= B_READ;
-                       if (error = nfs_doio(bp, cred, p)) {
+                       error = nfs_doio(bp, cred, p);
+                       if (error) {
                                brelse(bp);
                                return (error);
                        }
                                brelse(bp);
                                return (error);
                        }
@@ -257,28 +263,53 @@ again:
                break;
            case VDIR:
                nfsstats.biocache_readdirs++;
                break;
            case VDIR:
                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)
                if (!bp)
-                       return (EINTR);
+                   return (EINTR);
                if ((bp->b_flags & B_DONE) == 0) {
                if ((bp->b_flags & B_DONE) == 0) {
-                       bp->b_flags |= B_READ;
-                       if (error = nfs_doio(bp, cred, p)) {
-                               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
                }
 
                /*
                 * 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 &&
                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);
                        if (rabp) {
                            if ((rabp->b_flags & (B_DONE | B_DELWRI)) == 0) {
                                rabp->b_flags |= (B_READ | B_ASYNC);
@@ -290,10 +321,12 @@ again:
                                brelse(rabp);
                        }
                }
                                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;
                got_buf = 1;
                break;
+           default:
+               printf(" nfsbioread: type %x unexpected\n",vp->v_type);
+               break;
            };
 
            if (n > 0) {
            };
 
            if (n > 0) {
@@ -303,16 +336,17 @@ again:
            }
            switch (vp->v_type) {
            case VREG:
            }
            switch (vp->v_type) {
            case VREG:
-               if (n + on == biosize || uio->uio_offset == np->n_size)
-                       bp->b_flags |= B_AGE;
                break;
            case VLNK:
                n = 0;
                break;
            case VDIR:
                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;
                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);
            if (got_buf)
                brelse(bp);
        } while (error == 0 && uio->uio_resid > 0 && n > 0);
@@ -322,6 +356,7 @@ again:
 /*
  * Vnode op for write using bio
  */
 /*
  * Vnode op for write using bio
  */
+int
 nfs_write(ap)
        struct vop_write_args /* {
                struct vnode *a_vp;
 nfs_write(ap)
        struct vop_write_args /* {
                struct vnode *a_vp;
@@ -339,9 +374,9 @@ nfs_write(ap)
        int ioflag = ap->a_ioflag;
        struct buf *bp;
        struct vattr vattr;
        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;
        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)
 
 #ifdef DIAGNOSTIC
        if (uio->uio_rw != UIO_WRITE)
@@ -355,20 +390,23 @@ nfs_write(ap)
                np->n_flag &= ~NWRITEERR;
                return (np->n_error);
        }
                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;
        if (ioflag & (IO_APPEND | IO_SYNC)) {
                if (np->n_flag & NMODIFIED) {
                        np->n_attrstamp = 0;
-                       if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                       error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                       if (error)
                                return (error);
                }
                if (ioflag & IO_APPEND) {
                        np->n_attrstamp = 0;
                                return (error);
                }
                if (ioflag & IO_APPEND) {
                        np->n_attrstamp = 0;
-                       if (error = VOP_GETATTR(vp, &vattr, cred, p))
+                       error = VOP_GETATTR(vp, &vattr, cred, p);
+                       if (error)
                                return (error);
                        uio->uio_offset = np->n_size;
                }
        }
                                return (error);
                        uio->uio_offset = np->n_size;
                }
        }
-       nmp = VFSTONFS(vp->v_mount);
        if (uio->uio_offset < 0)
                return (EINVAL);
        if (uio->uio_resid == 0)
        if (uio->uio_offset < 0)
                return (EINVAL);
        if (uio->uio_resid == 0)
@@ -397,24 +435,29 @@ nfs_write(ap)
 
                /*
                 * Check for a valid write lease.
 
                /*
                 * Check for a valid write lease.
-                * If non-cachable, just do the rpc
                 */
                if ((nmp->nm_flag & NFSMNT_NQNFS) &&
                 */
                if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-                   NQNFS_CKINVALID(vp, np, NQL_WRITE)) {
+                   NQNFS_CKINVALID(vp, np, ND_WRITE)) {
                        do {
                        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);
                        if (np->n_lrev != np->n_brev ||
                            (np->n_flag & NQNFSNONCACHE)) {
                        } while (error == NQNFS_EXPIRED);
                        if (error)
                                return (error);
                        if (np->n_lrev != np->n_brev ||
                            (np->n_flag & NQNFSNONCACHE)) {
-                               if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                               if (error)
                                        return (error);
                                np->n_brev = np->n_lrev;
                        }
                }
                                        return (error);
                                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);
                nfsstats.biocache_writes++;
                lbn = uio->uio_offset / biosize;
                on = uio->uio_offset & (biosize-1);
@@ -452,9 +495,9 @@ again:
                 * In case getblk() and/or bwrite() delayed us.
                 */
                if ((nmp->nm_flag & NFSMNT_NQNFS) &&
                 * 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 {
                        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);
                        } while (error == NQNFS_EXPIRED);
                        if (error) {
                                brelse(bp);
@@ -463,13 +506,15 @@ again:
                        if (np->n_lrev != np->n_brev ||
                            (np->n_flag & NQNFSNONCACHE)) {
                                brelse(bp);
                        if (np->n_lrev != np->n_brev ||
                            (np->n_flag & NQNFSNONCACHE)) {
                                brelse(bp);
-                               if (error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1))
+                               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                               if (error)
                                        return (error);
                                np->n_brev = np->n_lrev;
                                goto again;
                        }
                }
                                        return (error);
                                np->n_brev = np->n_lrev;
                                goto again;
                        }
                }
-               if (error = uiomove((char *)bp->b_data + on, n, uio)) {
+               error = uiomove((char *)bp->b_data + on, n, uio);
+               if (error) {
                        bp->b_flags |= B_ERROR;
                        brelse(bp);
                        return (error);
                        bp->b_flags |= B_ERROR;
                        brelse(bp);
                        return (error);
@@ -481,7 +526,6 @@ again:
                        bp->b_dirtyoff = on;
                        bp->b_dirtyend = on + n;
                }
                        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;
                if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
                    bp->b_validoff > bp->b_dirtyend) {
                        bp->b_validoff = bp->b_dirtyoff;
@@ -490,24 +534,24 @@ again:
                        bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
                        bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
                }
                        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 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))
+                       error = VOP_BWRITE(bp);
+                       if (error)
                                return (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;
                } else if ((n + on) == biosize &&
                        (nmp->nm_flag & NFSMNT_NQNFS) == 0) {
                        bp->b_proc = (struct proc *)0;
-                       bawrite(bp);
+                       bp->b_flags |= B_ASYNC;
+                       (void)nfs_writebp(bp, 0);
                } else
                        bdwrite(bp);
        } while (uio->uio_resid > 0 && n > 0);
                } else
                        bdwrite(bp);
        } while (uio->uio_resid > 0 && n > 0);
@@ -547,6 +591,7 @@ nfs_getcacheblk(vp, bn, size, p)
  * Flush and invalidate all dirty buffers. If another process is already
  * doing the flush, just wait for completion.
  */
  * Flush and invalidate all dirty buffers. If another process is already
  * doing the flush, just wait for completion.
  */
+int
 nfs_vinvalbuf(vp, flags, cred, p, intrflg)
        struct vnode *vp;
        int flags;
 nfs_vinvalbuf(vp, flags, cred, p, intrflg)
        struct vnode *vp;
        int flags;
@@ -607,6 +652,7 @@ nfs_vinvalbuf(vp, flags, cred, p, intrflg)
  * This is mainly to avoid queueing async I/O requests when the nfsiods
  * are all hung on a dead server.
  */
  * This is mainly to avoid queueing async I/O requests when the nfsiods
  * are all hung on a dead server.
  */
+int
 nfs_asyncio(bp, cred)
        register struct buf *bp;
        struct ucred *cred;
 nfs_asyncio(bp, cred)
        register struct buf *bp;
        struct ucred *cred;
@@ -623,6 +669,7 @@ nfs_asyncio(bp, cred)
                                bp->b_rcred = cred;
                        }
                } else {
                                bp->b_rcred = cred;
                        }
                } else {
+                       bp->b_flags |= B_WRITEINPROG;
                        if (bp->b_wcred == NOCRED && cred != NOCRED) {
                                crhold(cred);
                                bp->b_wcred = cred;
                        if (bp->b_wcred == NOCRED && cred != NOCRED) {
                                crhold(cred);
                                bp->b_wcred = cred;
@@ -634,7 +681,25 @@ nfs_asyncio(bp, cred)
                wakeup((caddr_t)&nfs_iodwant[i]);
                return (0);
            }
                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);
 }
 
 /*
 }
 
 /*
@@ -644,16 +709,17 @@ nfs_asyncio(bp, cred)
 int
 nfs_doio(bp, cr, p)
        register struct buf *bp;
 int
 nfs_doio(bp, cr, p)
        register struct buf *bp;
-       struct cred *cr;
+       struct ucred *cr;
        struct proc *p;
 {
        register struct uio *uiop;
        register struct vnode *vp;
        struct nfsnode *np;
        struct nfsmount *nmp;
        struct proc *p;
 {
        register struct uio *uiop;
        register struct vnode *vp;
        struct nfsnode *np;
        struct nfsmount *nmp;
-       int error, diff, len;
+       int error = 0, diff, len, iomode, must_commit = 0;
        struct uio uio;
        struct iovec io;
        struct uio uio;
        struct iovec io;
+       nfsquad_t tquad;
 
        vp = bp->b_vp;
        np = VTONFS(vp);
 
        vp = bp->b_vp;
        np = VTONFS(vp);
@@ -667,15 +733,31 @@ nfs_doio(bp, cr, p)
        /*
         * Historically, paging was done with physio, but no more.
         */
        /*
         * Historically, paging was done with physio, but no more.
         */
-       if (bp->b_flags & B_PHYS)
-           panic("doio phys");
-       if (bp->b_flags & B_READ) {
+       if (bp->b_flags & B_PHYS) {
+           /*
+            * ...though reading /dev/drum still gets us here.
+            */
+           io.iov_len = uiop->uio_resid = bp->b_bcount;
+           /* mapping was done by vmapbuf() */
+           io.iov_base = bp->b_data;
+           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
+               panic("physio write");
+           if (error) {
+               bp->b_flags |= B_ERROR;
+               bp->b_error = error;
+           }
+       } else if (bp->b_flags & B_READ) {
            io.iov_len = uiop->uio_resid = bp->b_bcount;
            io.iov_base = bp->b_data;
            uiop->uio_rw = UIO_READ;
            switch (vp->v_type) {
            case VREG:
            io.iov_len = uiop->uio_resid = bp->b_bcount;
            io.iov_base = bp->b_data;
            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) {
                nfsstats.read_bios++;
                error = nfs_readrpc(vp, uiop, cr);
                if (!error) {
@@ -688,7 +770,7 @@ nfs_doio(bp, cr, p)
                         * Just zero fill the rest of the valid area.
                         */
                        diff = bp->b_bcount - uiop->uio_resid;
                         * 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);
                                + diff);
                        if (len > 0) {
                            len = min(len, uiop->uio_resid);
@@ -701,7 +783,7 @@ nfs_doio(bp, cr, p)
                }
                if (p && (vp->v_flag & VTEXT) &&
                        (((nmp->nm_flag & NFSMNT_NQNFS) &&
                }
                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.ts_sec))) {
                          np->n_lrev != np->n_brev) ||
                         (!(nmp->nm_flag & NFSMNT_NQNFS) &&
                          np->n_mtime != np->n_vattr.va_mtime.ts_sec))) {
@@ -711,21 +793,23 @@ nfs_doio(bp, cr, p)
                }
                break;
            case VLNK:
                }
                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:
                nfsstats.readlink_bios++;
                error = nfs_readlinkrpc(vp, uiop, cr);
                break;
            case VDIR:
-               uiop->uio_offset = bp->b_lblkno;
                nfsstats.readdir_bios++;
                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;
+           default:
+               printf("nfs_doio:  type %x unexpected\n",vp->v_type);
                break;
            };
            if (error) {
                break;
            };
            if (error) {
@@ -735,16 +819,22 @@ nfs_doio(bp, cr, p)
        } else {
            io.iov_len = uiop->uio_resid = bp->b_dirtyend
                - bp->b_dirtyoff;
        } 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++;
                + 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
+               iomode = NFSV3WRITE_FILESYNC;
+           bp->b_flags |= B_WRITEINPROG;
+           error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
+           if (!error && iomode == NFSV3WRITE_UNSTABLE)
+               bp->b_flags |= B_NEEDCOMMIT;
            else
            else
-               error = nfs_writerpc(vp, uiop, cr, 0);
-           bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE);
+               bp->b_flags &= ~B_NEEDCOMMIT;
+           bp->b_flags &= ~B_WRITEINPROG;
 
            /*
             * For an interrupted write, the buffer is still valid and the
 
            /*
             * For an interrupted write, the buffer is still valid and the
@@ -752,9 +842,13 @@ nfs_doio(bp, cr, p)
             * 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.
             * 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;
 
                /*
                bp->b_flags |= B_DELWRI;
 
                /*
@@ -776,6 +870,8 @@ nfs_doio(bp, cr, p)
            }
        }
        bp->b_resid = uiop->uio_resid;
            }
        }
        bp->b_resid = uiop->uio_resid;
+       if (must_commit)
+               nfs_clearcommit(vp->v_mount);
        biodone(bp);
        return (error);
 }
        biodone(bp);
        return (error);
 }
index 1d3b946..4dd5f6d 100644 (file)
@@ -7,9 +7,10 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_node.c  8.3 (Berkeley) %G%
+ *     @(#)nfs_node.c  8.4 (Berkeley) %G%
  */
 
  */
 
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
 #include <sys/malloc.h>
 
 #include <nfs/rpcv2.h>
 #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.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nqnfs.h>
 
-#define        NFSNOHASH(fhsum) \
-       (&nfsnodehashtbl[(fhsum) & nfsnodehash])
 LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
 u_long nfsnodehash;
 
 LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
 u_long nfsnodehash;
 
@@ -38,6 +37,7 @@ u_long nfsnodehash;
  * Initialize hash links for nfsnodes
  * and build nfsnode free list.
  */
  * Initialize hash links for nfsnodes
  * and build nfsnode free list.
  */
+void
 nfs_nhinit()
 {
 
 nfs_nhinit()
 {
 
@@ -51,19 +51,20 @@ nfs_nhinit()
 /*
  * Compute an entry in the NFS hash table structure
  */
 /*
  * 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;
 {
        register u_char *fhpp;
        register u_long fhsum;
-       int i;
+       register int i;
 
        fhpp = &fhp->fh_bytes[0];
        fhsum = 0;
 
        fhpp = &fhp->fh_bytes[0];
        fhsum = 0;
-       for (i = 0; i < NFSX_FH; i++)
+       for (i = 0; i < fhsize; i++)
                fhsum += *fhpp++;
                fhsum += *fhpp++;
-       return (NFSNOHASH(fhsum));
+       return (fhsum);
 }
 
 /*
 }
 
 /*
@@ -72,9 +73,11 @@ nfs_hash(fhp)
  * In all cases, a pointer to a
  * nfsnode structure is returned.
  */
  * In all cases, a pointer to a
  * nfsnode structure is returned.
  */
-nfs_nget(mntp, fhp, npp)
+int
+nfs_nget(mntp, fhp, fhsize, npp)
        struct mount *mntp;
        struct mount *mntp;
-       register nfsv2fh_t *fhp;
+       register nfsfh_t *fhp;
+       int fhsize;
        struct nfsnode **npp;
 {
        register struct nfsnode *np;
        struct nfsnode **npp;
 {
        register struct nfsnode *np;
@@ -84,11 +87,11 @@ nfs_nget(mntp, fhp, npp)
        struct vnode *nvp;
        int error;
 
        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) {
 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);
                if (vget(vp, 1))
                        continue;
                vp = NFSTOV(np);
                if (vget(vp, 1))
@@ -96,35 +99,31 @@ loop:
                *npp = np;
                return(0);
        }
                *npp = np;
                return(0);
        }
-       if (error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp)) {
+       error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp);
+       if (error) {
                *npp = 0;
                return (error);
        }
        vp = nvp;
        MALLOC(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
                *npp = 0;
                return (error);
        }
        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
         */
        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);
        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;
-       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);
 }
 
        *npp = np;
        return (0);
 }
 
+int
 nfs_inactive(ap)
        struct vop_inactive_args /* {
                struct vnode *a_vp;
 nfs_inactive(ap)
        struct vop_inactive_args /* {
                struct vnode *a_vp;
@@ -138,7 +137,10 @@ nfs_inactive(ap)
        np = VTONFS(ap->a_vp);
        if (prtactive && ap->a_vp->v_usecount != 0)
                vprint("nfs_inactive: pushing active", ap->a_vp);
        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) {
                /*
        np->n_sillyrename = (struct sillyrename *)0;
        if (sp) {
                /*
@@ -148,9 +150,7 @@ nfs_inactive(ap)
                nfs_removeit(sp);
                crfree(sp->s_cred);
                vrele(sp->s_dvp);
                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);
        }
        np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
                NQNFSNONCACHE | NQNFSWRITE);
@@ -160,6 +160,7 @@ nfs_inactive(ap)
 /*
  * Reclaim an nfsnode so that it can be used for other purposes.
  */
 /*
  * Reclaim an nfsnode so that it can be used for other purposes.
  */
+int
 nfs_reclaim(ap)
        struct vop_reclaim_args /* {
                struct vnode *a_vp;
 nfs_reclaim(ap)
        struct vop_reclaim_args /* {
                struct vnode *a_vp;
@@ -168,17 +169,38 @@ nfs_reclaim(ap)
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
        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);
        extern int prtactive;
 
        if (prtactive && vp->v_usecount != 0)
                vprint("nfs_reclaim: pushing active", vp);
+
        LIST_REMOVE(np, n_hash);
 
        /*
         * For nqnfs, take it off the timer queue as required.
         */
        LIST_REMOVE(np, n_hash);
 
        /*
         * For nqnfs, take it off the timer queue as required.
         */
-       if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0)
+       if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) {
                CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
                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;
        cache_purge(vp);
        FREE(vp->v_data, M_NFSNODE);
        vp->v_data = (void *)0;
@@ -188,6 +210,7 @@ nfs_reclaim(ap)
 /*
  * Lock an nfsnode
  */
 /*
  * Lock an nfsnode
  */
+int
 nfs_lock(ap)
        struct vop_lock_args /* {
                struct vnode *a_vp;
 nfs_lock(ap)
        struct vop_lock_args /* {
                struct vnode *a_vp;
@@ -202,7 +225,7 @@ nfs_lock(ap)
         */
        while (vp->v_flag & VXLOCK) {
                vp->v_flag |= VXWANT;
         */
        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);
        }
        if (vp->v_tag == VT_NON)
                return (ENOENT);
@@ -212,6 +235,7 @@ nfs_lock(ap)
 /*
  * Unlock an nfsnode
  */
 /*
  * Unlock an nfsnode
  */
+int
 nfs_unlock(ap)
        struct vop_unlock_args /* {
                struct vnode *a_vp;
 nfs_unlock(ap)
        struct vop_unlock_args /* {
                struct vnode *a_vp;
@@ -224,6 +248,7 @@ nfs_unlock(ap)
 /*
  * Check for a locked nfsnode
  */
 /*
  * Check for a locked nfsnode
  */
+int
 nfs_islocked(ap)
        struct vop_islocked_args /* {
                struct vnode *a_vp;
 nfs_islocked(ap)
        struct vop_islocked_args /* {
                struct vnode *a_vp;
index 260eb55..3c9b1c0 100644 (file)
@@ -7,9 +7,10 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_nqlease.c       8.5 (Berkeley) %G%
+ *     @(#)nfs_nqlease.c       8.6 (Berkeley) %G%
  */
 
  */
 
+
 /*
  * References:
  *     Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
 /*
  * References:
  *     Cary G. Gray and David R. Cheriton, "Leases: An Efficient Fault-Tolerant
@@ -39,7 +40,7 @@
 
 #include <netinet/in.h>
 #include <nfs/rpcv2.h>
 
 #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>
 #include <nfs/nfs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/xdr_subs.h>
@@ -48,7 +49,6 @@
 #include <nfs/nfsmount.h>
 
 time_t nqnfsstarttime = (time_t)0;
 #include <nfs/nfsmount.h>
 
 time_t nqnfsstarttime = (time_t)0;
-u_long nqnfs_prog, nqnfs_vers;
 int nqsrv_clockskew = NQ_CLOCKSKEW;
 int nqsrv_writeslack = NQ_WRITESLACK;
 int nqsrv_maxlease = NQ_MAXLEASE;
 int nqsrv_clockskew = NQ_CLOCKSKEW;
 int nqsrv_writeslack = NQ_WRITESLACK;
 int nqsrv_maxlease = NQ_MAXLEASE;
@@ -64,39 +64,45 @@ struct mbuf *nfsm_rpchead();
  */
 int nqnfs_piggy[NFS_NPROCS] = {
        0,
  */
 int nqnfs_piggy[NFS_NPROCS] = {
        0,
-       NQL_READ,
-       NQL_WRITE,
        0,
        0,
-       NQL_READ,
-       NQL_READ,
-       NQL_READ,
+       ND_WRITE,
+       ND_READ,
+       0,
+       ND_READ,
+       ND_READ,
+       ND_WRITE,
+       0,
+       0,
        0,
        0,
-       NQL_WRITE,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
+       ND_READ,
+       ND_READ,
+       0,
        0,
        0,
        0,
        0,
-       NQL_READ,
        0,
        0,
-       NQL_READ,
        0,
        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 nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
 extern int nfsd_waiting;
+extern struct nfsstats nfsstats;
+extern int nfs_mount_type;
 
 #define TRUE   1
 #define        FALSE   0
 
 /*
 
 #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
  * The rules are as follows:
  * - if a current non-caching lease, reply non-caching
  * - if a current lease for same host only, extend lease
@@ -118,19 +124,21 @@ extern int nfsd_waiting;
  *     is when a new lease is being allocated, since it is not in the timer
  *     queue yet. (Ditto for the splsoftclock() and splx(s) calls)
  */
  *     is when a new lease is being allocated, since it is not in the timer
  *     queue yet. (Ditto for the splsoftclock() and splx(s) calls)
  */
-nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
+int
+nqsrv_getlease(vp, duration, flags, slp, procp, nam, cachablep, frev, cred)
        struct vnode *vp;
        u_long *duration;
        int flags;
        struct vnode *vp;
        u_long *duration;
        int flags;
-       struct nfsd *nd;
+       struct nfssvc_sock *slp;
+       struct proc *procp;
        struct mbuf *nam;
        int *cachablep;
        u_quad_t *frev;
        struct ucred *cred;
 {
        register struct nqlease *lp;
        struct mbuf *nam;
        int *cachablep;
        u_quad_t *frev;
        struct ucred *cred;
 {
        register struct nqlease *lp;
-       register struct nqfhhashhead *lpp;
-       register struct nqhost *lph;
+       register struct nqfhhashhead *lpp = 0;
+       register struct nqhost *lph = 0;
        struct nqlease *tlp;
        struct nqm **lphp;
        struct vattr vattr;
        struct nqlease *tlp;
        struct nqm **lphp;
        struct vattr vattr;
@@ -141,19 +149,22 @@ nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
                return (0);
        if (*duration > nqsrv_maxlease)
                *duration = nqsrv_maxlease;
                return (0);
        if (*duration > nqsrv_maxlease)
                *duration = nqsrv_maxlease;
-       if (error = VOP_GETATTR(vp, &vattr, cred, nd->nd_procp))
+       error = VOP_GETATTR(vp, &vattr, cred, procp);
+       if (error)
                return (error);
        *frev = vattr.va_filerev;
        s = splsoftclock();
        tlp = vp->v_lease;
                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++;
                nfsstats.srvnqnfs_getleases++;
-       if (tlp == 0) {
+       if (tlp == (struct nqlease *)0) {
+
                /*
                 * Find the lease by searching the hash list.
                 */
                fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
                /*
                 * 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)) {
+               error = VFS_VPTOFH(vp, &fh.fh_fid);
+               if (error) {
                        splx(s);
                        return (error);
                }
                        splx(s);
                        return (error);
                }
@@ -171,15 +182,15 @@ nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
                        }
        } else
                lp = tlp;
                        }
        } else
                lp = tlp;
-       if (lp != 0) {
+       if (lp) {
                if ((lp->lc_flag & LC_NONCACHABLE) ||
                    (lp->lc_morehosts == (struct nqm *)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;
                        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;
                                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) {
                                goto doreply;
                        i = 0;
                        if (lp->lc_morehosts) {
@@ -191,7 +202,7 @@ nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
                                ok = 0;
                        }
                        while (ok && (lph->lph_flag & LC_VALID)) {
                                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;
                                        goto doreply;
                                if (++i == LC_MOREHOSTSIZ) {
                                        i = 0;
@@ -211,12 +222,12 @@ nqsrv_getlease(vp, duration, flags, nd, nam, cachablep, frev, cred)
                                bzero((caddr_t)*lphp, sizeof (struct nqm));
                                lph = (*lphp)->lpm_hosts;
                        }
                                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_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);
                }
                        nqsrv_waitfor_expiry(lp);
                        nqsrv_unlocklease(lp);
                }
@@ -224,20 +235,20 @@ doreply:
                /*
                 * Update the lease and return
                 */
                /*
                 * 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;
                        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);
                                lp->lc_flag |= LC_WRITTEN;
                }
                splx(s);
                return (0);
        }
        splx(s);
-       if (flags & NQL_CHECK)
+       if (flags & ND_CHECK)
                return (0);
 
        /*
                return (0);
 
        /*
@@ -254,12 +265,15 @@ doreply:
        }
        MALLOC(lp, struct nqlease *, sizeof (struct nqlease), M_NQLEASE, M_WAITOK);
        bzero((caddr_t)lp, sizeof (struct nqlease));
        }
        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);
                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;
        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 (long));
+       bcopy(fh.fh_fid.fid_data, lp->lc_fiddata,
+               fh.fh_fid.fid_len - sizeof (long));
+       if(!lpp)
+               panic("nfs_nqlease.c: Phoney lpp");
        LIST_INSERT_HEAD(lpp, lp, lc_hash);
        vp->v_lease = lp;
        s = splsoftclock();
        LIST_INSERT_HEAD(lpp, lp, lc_hash);
        vp->v_lease = lp;
        s = splsoftclock();
@@ -275,7 +289,8 @@ doreply:
  * Local lease check for server syscalls.
  * Just set up args and let nqsrv_getlease() do the rest.
  */
  * Local lease check for server syscalls.
  * Just set up args and let nqsrv_getlease() do the rest.
  */
-lease_check(ap)
+int
+nqnfs_vop_lease_check(ap)
        struct vop_lease_args /* {
                struct vnode *a_vp;
                struct proc *a_p;
        struct vop_lease_args /* {
                struct vnode *a_vp;
                struct proc *a_p;
@@ -283,14 +298,12 @@ lease_check(ap)
                int a_flag;
        } */ *ap;
 {
                int a_flag;
        } */ *ap;
 {
-       int duration = 0, cache;
-       struct nfsd nfsd;
+       u_long duration = 0;
+       int cache;
        u_quad_t frev;
 
        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);
+       (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);
 }
 
        return (0);
 }
 
@@ -336,8 +349,9 @@ nqsrv_instimeq(lp, duration)
        newexpiry = time.tv_sec + duration + nqsrv_clockskew;
        if (lp->lc_expiry == newexpiry)
                return;
        newexpiry = time.tv_sec + duration + nqsrv_clockskew;
        if (lp->lc_expiry == newexpiry)
                return;
-       if (lp->lc_timer.cqe_next != 0)
+       if (lp->lc_timer.cqe_next != 0) {
                CIRCLEQ_REMOVE(&nqtimerhead, lp, lc_timer);
                CIRCLEQ_REMOVE(&nqtimerhead, lp, lc_timer);
+       }
        lp->lc_expiry = newexpiry;
 
        /*
        lp->lc_expiry = newexpiry;
 
        /*
@@ -346,8 +360,10 @@ nqsrv_instimeq(lp, duration)
        tlp = nqtimerhead.cqh_last;
        while (tlp != (void *)&nqtimerhead && tlp->lc_expiry > newexpiry)
                tlp = tlp->lc_timer.cqe_prev;
        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);
        if (tlp == nqtimerhead.cqh_last)
                NQSTORENOVRAM(newexpiry);
+#endif /* HASNVRAM */
        if (tlp == (void *)&nqtimerhead) {
                CIRCLEQ_INSERT_HEAD(&nqtimerhead, lp, lc_timer);
        } else {
        if (tlp == (void *)&nqtimerhead) {
                CIRCLEQ_INSERT_HEAD(&nqtimerhead, lp, lc_timer);
        } else {
@@ -361,6 +377,7 @@ nqsrv_instimeq(lp, duration)
  * This is somewhat messy due to the union in the nqhost structure.
  * The local host is indicated by the special value of NQLOCALSLP for slp.
  */
  * This is somewhat messy due to the union in the nqhost structure.
  * The local host is indicated by the special value of NQLOCALSLP for slp.
  */
+int
 nqsrv_cmpnam(slp, nam, lph)
        register struct nfssvc_sock *slp;
        struct mbuf *nam;
 nqsrv_cmpnam(slp, nam, lph)
        register struct nfssvc_sock *slp;
        struct mbuf *nam;
@@ -449,9 +466,9 @@ nqsrv_send_eviction(vp, lp, slp, nam, cred)
                        else
                                solockp = (int *)0;
                        nfsm_reqhead((struct vnode *)0, NQNFSPROC_EVICTED,
                        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);
                        fhp = (fhandle_t *)cp;
                        fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
                        VFS_VPTOFH(vp, &fhp->fh_fid);
@@ -465,8 +482,9 @@ nqsrv_send_eviction(vp, lp, slp, nam, cred)
                                printf("mbuf siz=%d\n",siz);
                                panic("Bad nfs svc reply");
                        }
                                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,
+                       m = nfsm_rpchead(cred, (NFSMNT_NFSV3 | NFSMNT_NQNFS),
+                               NQNFSPROC_EVICTED,
+                               RPCAUTH_UNIX, 5 * NFSX_UNSIGNED, (char *)0,
                                mreq, siz, &mheadend, &xid);
                        /*
                         * For stream protocols, prepend a Sun RPC
                                mreq, siz, &mheadend, &xid);
                        /*
                         * For stream protocols, prepend a Sun RPC
@@ -557,7 +575,7 @@ tryagain:
 void
 nqnfs_serverd()
 {
 void
 nqnfs_serverd()
 {
-       register struct nqlease *lp, *lq;
+       register struct nqlease *lp;
        register struct nqhost *lph;
        struct nqlease *nextlp;
        struct nqm *lphnext, *olphnext;
        register struct nqhost *lph;
        struct nqlease *nextlp;
        struct nqm *lphnext, *olphnext;
@@ -639,18 +657,22 @@ nqnfs_serverd()
  * Do the from/to xdr translation and call nqsrv_getlease() to
  * do the real work.
  */
  * Do the from/to xdr translation and call nqsrv_getlease() to
  * do the real work.
  */
-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;
+int
+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;
        register struct vattr *vap = &va;
        struct vnode *vp;
        struct vattr va;
        register struct vattr *vap = &va;
        struct vnode *vp;
-       nfsv2fh_t nfh;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
@@ -663,27 +685,29 @@ nqnfsrv_getlease(nfsd, mrep, md, dpos, cred, nam, mrq)
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
        flags = fxdr_unsigned(int, *tl++);
        nfsd->nd_duration = fxdr_unsigned(int, *tl);
        flags = fxdr_unsigned(int, *tl++);
        nfsd->nd_duration = fxdr_unsigned(int, *tl);
-       if (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);
                nfsm_reply(0);
-       if (rdonly && flags == NQL_WRITE) {
+       if (rdonly && flags == ND_WRITE) {
+               vput(vp);
                error = EROFS;
                nfsm_reply(0);
        }
                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);
                nam, &cache, &frev, cred);
-       error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+       error = VOP_GETATTR(vp, vap, cred, procp);
        vput(vp);
        vput(vp);
-       nfsm_reply(NFSX_NQFATTR + 4*NFSX_UNSIGNED);
-       nfsm_build(tl, u_long *, 4*NFSX_UNSIGNED);
+       nfsm_reply(NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
+       nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
        *tl++ = txdr_unsigned(cache);
        *tl++ = txdr_unsigned(nfsd->nd_duration);
        txdr_hyper(&frev, tl);
        *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(vap, fp);
        nfsm_srvdone;
 }
 
        nfsm_srvdone;
 }
 
@@ -691,23 +715,29 @@ nqnfsrv_getlease(nfsd, mrep, md, dpos, cred, nam, mrq)
  * Called from nfssvc_nfsd() when a "vacated" message is received from a
  * client. Find the entry and expire it.
  */
  * Called from nfssvc_nfsd() when a "vacated" message is received from a
  * client. Find the entry and expire it.
  */
-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;
+int
+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;
+       struct ucred *cred = &nfsd->nd_cr;
        register struct nqlease *lp;
        register struct nqhost *lph;
        struct nqlease *tlp = (struct nqlease *)0;
        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_long *tl;
        register long t1;
        struct nqm *lphnext;
        fhandle_t *fhp;
        register u_long *tl;
        register long 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);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
@@ -725,7 +755,7 @@ nqnfsrv_vacated(nfsd, mrep, md, dpos, cred, nam, mrq)
                        tlp = lp;
                        break;
                }
                        tlp = lp;
                        break;
                }
-       if (tlp != 0) {
+       if (tlp) {
                lp = tlp;
                len = 1;
                i = 0;
                lp = tlp;
                len = 1;
                i = 0;
@@ -733,7 +763,7 @@ nqnfsrv_vacated(nfsd, mrep, md, dpos, cred, nam, mrq)
                lphnext = lp->lc_morehosts;
                ok = 1;
                while (ok && (lph->lph_flag & LC_VALID)) {
                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;
                                lph->lph_flag |= LC_VACATED;
                                gotit++;
                                break;
@@ -762,6 +792,7 @@ nfsmout:
 /*
  * Client get lease rpc function.
  */
 /*
  * Client get lease rpc function.
  */
+int
 nqnfs_getlease(vp, rwflag, cred, p)
        register struct vnode *vp;
        int rwflag;
 nqnfs_getlease(vp, rwflag, cred, p)
        register struct vnode *vp;
        int rwflag;
@@ -770,7 +801,7 @@ nqnfs_getlease(vp, rwflag, cred, p)
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
+       register long t1, t2;
        register struct nfsnode *np;
        struct nfsmount *nmp = VFSTONFS(vp->v_mount);
        caddr_t bpos, dpos, cp2;
        register struct nfsnode *np;
        struct nfsmount *nmp = VFSTONFS(vp->v_mount);
        caddr_t bpos, dpos, cp2;
@@ -781,16 +812,16 @@ nqnfs_getlease(vp, rwflag, cred, p)
        u_quad_t frev;
        
        nfsstats.rpccnt[NQNFSPROC_GETLEASE]++;
        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);
                 &bpos);
-       nfsm_fhtom(vp);
-       nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+       nfsm_fhtom(vp, 1);
+       nfsm_build(tl, u_long *, 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);
        *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_long *, 4*NFSX_UNSIGNED);
+       nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
        cachable = fxdr_unsigned(int, *tl++);
        reqtime += fxdr_unsigned(int, *tl++);
        if (reqtime > time.tv_sec) {
        cachable = fxdr_unsigned(int, *tl++);
        reqtime += fxdr_unsigned(int, *tl++);
        if (reqtime > time.tv_sec) {
@@ -806,6 +837,7 @@ nqnfs_getlease(vp, rwflag, cred, p)
 /*
  * Client vacated message function.
  */
 /*
  * Client vacated message function.
  */
+int
 nqnfs_vacated(vp, cred)
        register struct vnode *vp;
        struct ucred *cred;
 nqnfs_vacated(vp, cred)
        register struct vnode *vp;
        struct ucred *cred;
@@ -813,6 +845,8 @@ nqnfs_vacated(vp, cred)
        register caddr_t cp;
        register struct mbuf *m;
        register int i;
        register caddr_t cp;
        register struct mbuf *m;
        register int i;
+       register u_long *tl;
+       register long t1, t2;
        caddr_t bpos;
        u_long xid;
        int error = 0;
        caddr_t bpos;
        u_long xid;
        int error = 0;
@@ -822,16 +856,16 @@ nqnfs_vacated(vp, cred)
        
        nmp = VFSTONFS(vp->v_mount);
        nfsstats.rpccnt[NQNFSPROC_VACATED]++;
        
        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 = 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,
+       m = nfsm_rpchead(cred, nmp->nm_flag, NQNFSPROC_VACATED,
+               RPCAUTH_UNIX, 5 * NFSX_UNSIGNED, (char *)0,
                mreq, i, &mheadend, &xid);
        if (nmp->nm_sotype == SOCK_STREAM) {
                M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
                mreq, i, &mheadend, &xid);
        if (nmp->nm_sotype == SOCK_STREAM) {
                M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
@@ -845,12 +879,14 @@ nqnfs_vacated(vp, cred)
        (void) nfs_send(nmp->nm_so, nmp->nm_nam, m, &myrep);
        if (nmp->nm_soflags & PR_CONNREQUIRED)
                nfs_sndunlock(&nmp->nm_flag);
        (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);
 }
 
 /*
  * Called for client side callbacks
  */
        return (error);
 }
 
 /*
  * Called for client side callbacks
  */
+int
 nqnfs_callback(nmp, mrep, md, dpos)
        struct nfsmount *nmp;
        struct mbuf *mrep, *md;
 nqnfs_callback(nmp, mrep, md, dpos)
        struct nfsmount *nmp;
        struct mbuf *mrep, *md;
@@ -859,28 +895,38 @@ nqnfs_callback(nmp, mrep, md, dpos)
        register struct vnode *vp;
        register u_long *tl;
        register long t1;
        register struct vnode *vp;
        register u_long *tl;
        register long t1;
-       nfsv2fh_t nfh;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        struct nfsnode *np;
        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))
+#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);
                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);
                m_freem(mrep);
                return (EPERM);
        }
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        m_freem(mrep);
-       if (error = nfs_nget(nmp->nm_mountp, fhp, &np))
+       error = nfs_nget(nmp->nm_mountp, (nfsfh_t *)fhp, NFSX_V3FH, &np);
+       if (error)
                return (error);
        vp = NFSTOV(np);
        if (np->n_timer.cqe_next != 0) {
                return (error);
        vp = NFSTOV(np);
        if (np->n_timer.cqe_next != 0) {
@@ -902,6 +948,7 @@ nqnfs_callback(nmp, mrep, md, dpos)
  * "sleep" since nfs_reclaim() called from vclean() can pull a node off
  * the list asynchronously.
  */
  * "sleep" since nfs_reclaim() called from vclean() can pull a node off
  * the list asynchronously.
  */
+int
 nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
        register struct nfsmount *nmp;
        struct ucred *cred;
 nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
        register struct nfsmount *nmp;
        struct ucred *cred;
@@ -913,35 +960,39 @@ nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
        register struct nfsnode *np;
        struct vnode *vp;
        struct nfsreq myrep;
        register struct nfsnode *np;
        struct vnode *vp;
        struct nfsreq myrep;
-       int error, vpid;
+       struct nfsuid *nuidp, *nnuidp;
+       int error = 0, vpid;
 
        /*
         * First initialize some variables
         */
 
        /*
         * 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) &&
 
        /*
         * 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
                } 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
        } else
-               nmp->nm_flag |= NFSMNT_WAITAUTH;
+           nmp->nm_flag |= NFSMNT_WAITAUTH;
 
        /*
         * Loop every second updating queue until there is a termination sig.
 
        /*
         * Loop every second updating queue until there is a termination sig.
@@ -969,13 +1020,11 @@ nqnfs_clientd(nmp, cred, ncd, flag, argp, p)
                while (np != (void *)&nmp->nm_timerhead &&
                       (nmp->nm_flag & NFSMNT_DISMINPROG) == 0) {
                        vp = NFSTOV(np);
                while (np != (void *)&nmp->nm_timerhead &&
                       (nmp->nm_flag & NFSMNT_DISMINPROG) == 0) {
                        vp = NFSTOV(np);
-if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash2");
                        vpid = vp->v_id;
                        if (np->n_expiry < time.tv_sec) {
                           if (vget(vp, 1) == 0) {
                             nmp->nm_inprog = vp;
                             if (vpid == vp->v_id) {
                        vpid = vp->v_id;
                        if (np->n_expiry < time.tv_sec) {
                           if (vget(vp, 1) == 0) {
                             nmp->nm_inprog = vp;
                             if (vpid == vp->v_id) {
-if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash3");
                                CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
                                np->n_timer.cqe_next = 0;
                                if ((np->n_flag & (NMODIFIED | NQNFSEVICTED))
                                CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
                                np->n_timer.cqe_next = 0;
                                if ((np->n_flag & (NMODIFIED | NQNFSEVICTED))
@@ -1000,9 +1049,8 @@ if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash3");
                                 == NQNFSWRITE && vp->v_dirtyblkhd.lh_first &&
                                 vget(vp, 1) == 0) {
                                 nmp->nm_inprog = vp;
                                 == NQNFSWRITE && vp->v_dirtyblkhd.lh_first &&
                                 vget(vp, 1) == 0) {
                                 nmp->nm_inprog = vp;
-if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash4");
                                 if (vpid == vp->v_id &&
                                 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;
                                        np->n_brev = np->n_lrev;
                                 vrele(vp);
                                 nmp->nm_inprog = NULLVP;
@@ -1037,6 +1085,16 @@ if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash4");
                        (void) dounmount(nmp->nm_mountp, 0, p);
            }
        }
                        (void) dounmount(nmp->nm_mountp, 0, 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;
        free((caddr_t)nmp, M_NFSMNT);
        if (error == EWOULDBLOCK)
                error = 0;
@@ -1048,7 +1106,7 @@ if (vp->v_mount->mnt_stat.f_fsid.val[1] != MOUNT_NFS) panic("trash4");
  * Called from the settimeofday() syscall.
  */
 void
  * Called from the settimeofday() syscall.
  */
 void
-lease_updatetime(deltat)
+nqnfs_lease_updatetime(deltat)
        register int deltat;
 {
        register struct nqlease *lp;
        register int deltat;
 {
        register struct nqlease *lp;
@@ -1070,14 +1128,14 @@ lease_updatetime(deltat)
         * queues.
         */
        for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
         * queues.
         */
        for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
-               if (mp->mnt_stat.f_fsid.val[1] == MOUNT_NFS) {
-                       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;
-                               }
+               if (mp->mnt_stat.f_type != nfs_mount_type)
+                       continue;
+               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;
                        }
                }
        }
                        }
                }
        }
@@ -1127,9 +1185,9 @@ nqnfs_clientlease(nmp, np, rwflag, cachable, expiry, frev)
 
        if (np->n_timer.cqe_next != 0) {
                CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
 
        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;
                        np->n_flag |= NQNFSWRITE;
-       } else if (rwflag == NQL_READ)
+       } else if (rwflag == ND_READ)
                np->n_flag &= ~NQNFSWRITE;
        else
                np->n_flag |= NQNFSWRITE;
                np->n_flag &= ~NQNFSWRITE;
        else
                np->n_flag |= NQNFSWRITE;
index 69e8006..e8fbde3 100644 (file)
@@ -7,11 +7,11 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_serv.c  8.5 (Berkeley) %G%
+ *     @(#)nfs_serv.c  8.6 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
- * 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
  * - these routines generally have 3 phases
  *   1 - break down and validate rpc request in mbuf list
  *   2 - do the vnode ops for the request
@@ -26,6 +26,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.
  *     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>
  */
 
 #include <sys/param.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/mount.h>
 #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/mbuf.h>
 #include <sys/dirent.h>
 #include <sys/stat.h>
+#include <sys/kernel.h>
+#include <ufs/ufs/dir.h>
 
 #include <vm/vm.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>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nqnfs.h>
 
 #include <nfs/rpcv2.h>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nqnfs.h>
 
-/* Defs */
-#define        TRUE    1
-#define        FALSE   0
-
 /* Global vars */
 /* Global vars */
-extern u_long nfs_procids[NFS_NPROCS];
 extern u_long nfs_xdrneg1;
 extern u_long nfs_false, nfs_true;
 extern u_long nfs_xdrneg1;
 extern u_long nfs_false, nfs_true;
-nfstype nfs_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+extern enum vtype nv3tov_type[8];
+extern struct nfsstats nfsstats;
+nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
                      NFCHR, NFNON };
                      NFCHR, NFNON };
+nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
+                     NFFIFO, NFNON };
+int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000;
 
 /*
 
 /*
- * nqnfs access service
+ * nfs v3 access service
  */
  */
-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;
+int
+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;
        struct vnode *vp;
-       nfsv2fh_t nfh;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
-       int error = 0, rdonly, cache, mode = 0;
+       int error = 0, rdonly, cache, getret;
        char *cp2;
        char *cp2;
-       struct mbuf *mb, *mreq;
+       struct mbuf *mb, *mreq, *mb2;
+       struct vattr vattr, *vap = &vattr;
+       u_long testmode, nfsmode;
        u_quad_t frev;
 
        u_quad_t frev;
 
+#ifndef nolint
+       cache = 0;
+#endif
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               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_long *, NFSX_UNSIGNED);
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(NFSX_UNSIGNED);
+               nfsm_srvpostop_attr(1, (struct vattr *)0);
+               return (0);
+       }
+       nfsmode = fxdr_unsigned(u_long, *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, vap, cred, procp);
        vput(vp);
        vput(vp);
-       nfsm_reply(0);
+       nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED);
+       nfsm_srvpostop_attr(getret, vap);
+       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+       *tl = txdr_unsigned(nfsmode);
        nfsm_srvdone;
 }
 
 /*
  * nfs getattr service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs getattr service
  */
-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;
+int
+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;
        register struct vattr *vap = &va;
        struct vnode *vp;
        struct vattr va;
        register struct vattr *vap = &va;
        struct vnode *vp;
-       nfsv2fh_t nfh;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
@@ -123,81 +162,117 @@ nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq)
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
                nfsm_reply(0);
                nfsm_reply(0);
-       nqsrv_getl(vp, NQL_READ);
-       error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+               return (0);
+       }
+       nqsrv_getl(vp, ND_READ);
+       error = VOP_GETATTR(vp, vap, cred, procp);
        vput(vp);
        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(vap, fp);
        nfsm_srvdone;
 }
 
 /*
  * nfs setattr service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs setattr service
  */
-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;
+int
+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 vattr *vap = &va;
        register struct nfsv2_sattr *sp;
        register struct vattr *vap = &va;
        register struct nfsv2_sattr *sp;
-       register struct nfsv2_fattr *fp;
+       register struct nfs_fattr *fp;
        struct vnode *vp;
        struct vnode *vp;
-       nfsv2fh_t nfh;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        fhandle_t *fhp;
        register u_long *tl;
        register long 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;
        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);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
-       nqsrv_getl(vp, NQL_WRITE);
        VATTR_NULL(vap);
        VATTR_NULL(vap);
-       /*
-        * 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)
-               vap->va_mode = nfstov_mode(sp->sa_mode);
-       if (sp->sa_uid != nfs_xdrneg1)
-               vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
-       if (sp->sa_gid != nfs_xdrneg1)
-               vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
-       if (nfsd->nd_nqlflag == NQL_NOVAL) {
-               if (sp->sa_nfssize != nfs_xdrneg1)
-                       vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_nfssize);
-               if (sp->sa_nfsatime.nfs_sec != nfs_xdrneg1) {
+       if (v3) {
+               nfsm_srvsattr(vap);
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+               gcheck = fxdr_unsigned(int, *tl);
+               if (gcheck) {
+                       nfsm_dissect(tl, u_long *, 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)
+                       vap->va_mode = nfstov_mode(sp->sa_mode);
+               if (sp->sa_uid != nfs_xdrneg1)
+                       vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
+               if (sp->sa_gid != nfs_xdrneg1)
+                       vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
+               if (sp->sa_size != nfs_xdrneg1)
+                       vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_size);
+               if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) {
 #ifdef notyet
 #ifdef notyet
-                       fxdr_nfstime(&sp->sa_nfsatime, &vap->va_atime);
+                       fxdr_nfsv2time(&sp->sa_atime, &vap->va_atime);
 #else
                        vap->va_atime.ts_sec =
 #else
                        vap->va_atime.ts_sec =
-                               fxdr_unsigned(long, sp->sa_nfsatime.nfs_sec);
+                               fxdr_unsigned(long, sp->sa_atime.nfsv2_sec);
                        vap->va_atime.ts_nsec = 0;
 #endif
                }
                        vap->va_atime.ts_nsec = 0;
 #endif
                }
-               if (sp->sa_nfsmtime.nfs_sec != nfs_xdrneg1)
-                       fxdr_nfstime(&sp->sa_nfsmtime, &vap->va_mtime);
-       } else {
-               fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
-               fxdr_nqtime(&sp->sa_nqatime, &vap->va_atime);
-               fxdr_nqtime(&sp->sa_nqmtime, &vap->va_mtime);
-               vap->va_flags = fxdr_unsigned(u_long, sp->sa_nqflags);
+               if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1)
+                       fxdr_nfsv2time(&sp->sa_mtime, &vap->va_mtime);
+
+       }
+
+       /*
+        * Now that we have all the fields, lets do it.
+        */
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(2 * NFSX_UNSIGNED);
+               nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap);
+               return (0);
+       }
+       nqsrv_getl(vp, ND_WRITE);
+       if (v3) {
+               error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp);
+               if (!error && gcheck &&
+                       (preat.va_ctime.ts_sec != guard.ts_sec ||
+                        preat.va_ctime.ts_nsec != guard.ts_nsec))
+                       error = NFSERR_NOT_SYNC;
+               if (error) {
+                       vput(vp);
+                       nfsm_reply(NFSX_WCCDATA(v3));
+                       nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap);
+                       return (0);
+               }
        }
 
        /*
        }
 
        /*
@@ -214,22 +289,22 @@ nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq)
                        error = EISDIR;
                        goto out;
                } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly,
                        error = EISDIR;
                        goto out;
                } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly,
-                       nfsd->nd_procp))
+                       procp))
                        goto out;
        }
                        goto out;
        }
-       if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
-               vput(vp);
-               nfsm_reply(0);
-       }
-       error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+       error = VOP_SETATTR(vp, vap, cred, procp);
+       postat_ret = VOP_GETATTR(vp, vap, cred, procp);
+       if (!error)
+               error = postat_ret;
 out:
        vput(vp);
 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_long *, 2*NFSX_UNSIGNED);
-               txdr_hyper(&frev2, tl);
+       nfsm_reply(NFSX_WCCORFATTR(v3));
+       if (v3) {
+               nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap);
+               return (0);
+       } else {
+               nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+               nfsm_srvfillattr(vap, fp);
        }
        nfsm_srvdone;
 }
        }
        nfsm_srvdone;
 }
@@ -237,101 +312,112 @@ out:
 /*
  * nfs lookup rpc
  */
 /*
  * nfs lookup rpc
  */
-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;
+int
+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 nameidata nd;
-       struct vnode *vp;
-       nfsv2fh_t nfh;
+       struct vnode *vp, *dirp;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        register caddr_t cp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        fhandle_t *fhp;
        register caddr_t cp;
        register u_long *tl;
        register long 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;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
-       struct vattr va, *vap = &va;
-       u_quad_t frev, frev2;
+       struct vattr va, dirattr, *vap = &va;
+       u_quad_t frev;
 
        fhp = &nfh.fh_generic;
 
        fhp = &nfh.fh_generic;
-       duration2 = 0;
-       if (nfsd->nd_nqlflag != NQL_NOVAL) {
-               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
-               duration2 = fxdr_unsigned(int, *tl);
-       }
        nfsm_srvmtofh(fhp);
        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;
        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))
-               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;
        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)) {
-               vput(vp);
-               nfsm_reply(0);
-       }
-       if (duration2)
-               (void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd,
-                       nam, &cache2, &frev2, cred);
-       error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+       error = VFS_VPTOFH(vp, &fhp->fh_fid);
+       if (!error)
+               error = VOP_GETATTR(vp, vap, cred, procp);
        vput(vp);
        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_long *, 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_long *, 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, v3);
+       if (v3) {
+               nfsm_srvpostop_attr(0, vap);
+               nfsm_srvpostop_attr(dirattr_ret, &dirattr);
+       } else {
+               nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+               nfsm_srvfillattr(vap, fp);
        }
        }
-       nfsm_srvfhtom(fhp);
-       nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
-       nfsm_srvfillattr;
        nfsm_srvdone;
 }
 
 /*
  * nfs readlink service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs readlink service
  */
-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;
+int
+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_long *tl;
        register long t1;
        caddr_t bpos;
        struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
        register struct iovec *ivp = iv;
        register struct mbuf *mp;
        register u_long *tl;
        register long 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, *mp3, *mreq;
        struct vnode *vp;
        char *cp2;
        struct mbuf *mb, *mb2, *mp2, *mp3, *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;
 
        fhandle_t *fhp;
        struct uio io, *uiop = &io;
        u_quad_t frev;
 
+#ifndef nolint
+       mp2 = mp3 = (struct mbuf *)0;
+#endif
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        len = 0;
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        len = 0;
@@ -363,21 +449,33 @@ nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq)
        uiop->uio_rw = UIO_READ;
        uiop->uio_segflg = UIO_SYSSPACE;
        uiop->uio_procp = (struct proc *)0;
        uiop->uio_rw = UIO_READ;
        uiop->uio_segflg = UIO_SYSSPACE;
        uiop->uio_procp = (struct proc *)0;
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly)) {
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
                m_freem(mp3);
                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) {
        }
        if (vp->v_type != VLNK) {
-               error = EINVAL;
+               if (v3)
+                       error = EINVAL;
+               else
+                       error = ENXIO;
                goto out;
        }
                goto out;
        }
-       nqsrv_getl(vp, NQL_READ);
+       nqsrv_getl(vp, ND_READ);
        error = VOP_READLINK(vp, uiop, cred);
 out:
        error = VOP_READLINK(vp, uiop, cred);
 out:
+       getret = VOP_GETATTR(vp, &attr, cred, procp);
        vput(vp);
        if (error)
                m_freem(mp3);
        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);
        if (uiop->uio_resid > 0) {
                len -= uiop->uio_resid;
                tlen = nfsm_rndup(len);
@@ -392,26 +490,32 @@ out:
 /*
  * nfs read service
  */
 /*
  * nfs read service
  */
-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;
+int
+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 iovec *iv;
        struct iovec *iv2;
        register struct mbuf *m;
-       register struct nfsv2_fattr *fp;
+       register struct nfs_fattr *fp;
        register u_long *tl;
        register long t1;
        register u_long *tl;
        register long t1;
+       register int i;
        caddr_t bpos;
        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;
        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, *vap = &va;
        fhandle_t *fhp;
        struct uio io, *uiop = &io;
        struct vattr va, *vap = &va;
@@ -420,38 +524,57 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       if (nfsd->nd_nqlflag == NQL_NOVAL) {
-               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
-               off = (off_t)fxdr_unsigned(u_long, *tl);
-       } else {
+       if (v3) {
                nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
                fxdr_hyper(tl, &off);
                nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
                fxdr_hyper(tl, &off);
+       } else {
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+               off = (off_t)fxdr_unsigned(u_long, *tl);
+       }
+       nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd));
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(2 * NFSX_UNSIGNED);
+               nfsm_srvpostop_attr(1, (struct vattr *)0);
+               return (0);
        }
        }
-       nfsm_srvstrsiz(cnt, NFS_MAXDATA);
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
        if (vp->v_type != VREG) {
        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_READ);
-       if ((error = nfsrv_access(vp, VREAD, cred, rdonly, nfsd->nd_procp)) &&
-           (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp))) {
-               vput(vp);
-               nfsm_reply(0);
+       if (!error) {
+           nqsrv_getl(vp, ND_READ);
+           if (error = nfsrv_access(vp, VREAD, cred, rdonly, procp))
+               error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
        }
        }
-       if (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp)) {
+       getret = VOP_GETATTR(vp, vap, cred, procp);
+       if (!error)
+               error = getret;
+       if (error) {
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
+               nfsm_reply(NFSX_POSTOPATTR(v3));
+               nfsm_srvpostop_attr(getret, vap);
+               return (0);
        }
        if (off >= vap->va_size)
                cnt = 0;
        }
        if (off >= vap->va_size)
                cnt = 0;
-       else if ((off + cnt) > vap->va_size)
+       else if ((off + reqlen) > vap->va_size)
                cnt = nfsm_rndup(vap->va_size - off);
                cnt = nfsm_rndup(vap->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_long *, NFSX_UNSIGNED);
+       else
+               cnt = reqlen;
+       nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt));
+       if (v3) {
+               nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
+               *tl++ = nfs_true;
+               fp = (struct nfs_fattr *)tl;
+               tl += (NFSX_V3FATTR / sizeof (u_long));
+       } else {
+               nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED);
+               fp = (struct nfs_fattr *)tl;
+               tl += (NFSX_V2FATTR / sizeof (u_long));
+       }
        len = left = cnt;
        if (cnt > 0) {
                /*
        len = left = cnt;
        if (cnt > 0) {
                /*
@@ -459,19 +582,11 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
                 */
                i = 0;
                m = m2 = mb;
                 */
                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) {
                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++;
-                               i++;
                                left -= siz;
                                left -= siz;
+                               i++;
                        }
                        if (left > 0) {
                                MGET(m, M_WAIT, MT_DATA);
                        }
                        if (left > 0) {
                                MGET(m, M_WAIT, MT_DATA);
@@ -479,10 +594,28 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
                                m->m_len = 0;
                                m2->m_next = m;
                                m2 = m;
                                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;
                uiop->uio_iovcnt = i;
                uiop->uio_offset = off;
                uiop->uio_resid = cnt;
@@ -491,19 +624,30 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
                error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
                off = uiop->uio_offset;
                FREE((caddr_t)iv2, M_TEMP);
                error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
                off = uiop->uio_offset;
                FREE((caddr_t)iv2, M_TEMP);
-               if (error || (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp))) {
+               if (error || (getret = VOP_GETATTR(vp, vap, cred, procp))) {
+                       if (!error)
+                               error = getret;
                        m_freem(mreq);
                        vput(vp);
                        m_freem(mreq);
                        vput(vp);
-                       nfsm_reply(0);
+                       nfsm_reply(NFSX_POSTOPATTR(v3));
+                       nfsm_srvpostop_attr(getret, vap);
+                       return (0);
                }
        } else
                uiop->uio_resid = 0;
        vput(vp);
                }
        } else
                uiop->uio_resid = 0;
        vput(vp);
-       nfsm_srvfillattr;
+       nfsm_srvfillattr(vap, fp);
        len -= uiop->uio_resid;
        tlen = nfsm_rndup(len);
        if (cnt != tlen || tlen != len)
        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;
 }
        *tl = txdr_unsigned(len);
        nfsm_srvdone;
 }
@@ -511,144 +655,600 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
 /*
  * nfs write service
  */
 /*
  * nfs write service
  */
-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;
+int
+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 struct iovec *ivp;
+       register int i, cnt;
        register struct mbuf *mp;
        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 struct vattr *vap = &va;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        register struct vattr *vap = &va;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
-       int error = 0, rdonly, cache, siz, len, xfer;
-       int ioflags = IO_SYNC | IO_NODELOCKED;
+       int error = 0, rdonly, cache, siz, len, xfer, 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;
        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;
 
        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);
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
-       if (nfsd->nd_nqlflag == NQL_NOVAL) {
-               off = (off_t)fxdr_unsigned(u_long, *++tl);
-               tl += 2;
-       } else {
+       if (v3) {
+               nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
                fxdr_hyper(tl, &off);
                fxdr_hyper(tl, &off);
+               tl += 3;
+               stable = fxdr_unsigned(int, *tl++);
+       } else {
+               nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+               off = (off_t)fxdr_unsigned(u_long, *++tl);
                tl += 2;
                tl += 2;
-               if (fxdr_unsigned(u_long, *tl++))
-                       ioflags |= IO_APPEND;
-       }
-       len = fxdr_unsigned(long, *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(long, *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 (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
+       if (len > NFS_MAXDATA || len < 0 || i < len) {
+               error = EIO;
+               nfsm_reply(2 * NFSX_UNSIGNED);
+               nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
+               return (0);
+       }
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(2 * NFSX_UNSIGNED);
+               nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
+               return (0);
+       }
+       if (v3)
+               forat_ret = VOP_GETATTR(vp, &forat, cred, procp);
        if (vp->v_type != VREG) {
        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;
+       }
+       if (!error) {
+               nqsrv_getl(vp, ND_WRITE);
+               error = nfsrv_access(vp, VWRITE, cred, rdonly, procp);
        }
        }
-       nqsrv_getl(vp, NQL_WRITE);
-       if (error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp)) {
+       if (error) {
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
+               nfsm_reply(NFSX_WCCDATA(v3));
+               nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
+               return (0);
        }
        }
-       uiop->uio_resid = 0;
-       uiop->uio_rw = UIO_WRITE;
-       uiop->uio_segflg = UIO_SYSSPACE;
-       uiop->uio_procp = (struct proc *)0;
+
+       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);
+                       ivp->iov_len = mp->m_len;
+                       ivp++;
+               }
+               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);
+       }
+       aftat_ret = VOP_GETATTR(vp, vap, cred, procp);
+       vput(vp);
+       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, vap);
+               if (error)
+                       return (0);
+               nfsm_build(tl, u_long *, 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(vap, 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;
+       struct iovec *iov;
+       struct nfsrvw_delayhash *wpp;
+       struct ucred *cred;
+       struct vattr va, forat;
+       register u_long *tl;
+       register long t1;
+       caddr_t bpos, dpos;
+       int error = 0, rdonly, cache, len, 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;
+       off_t off;
+       u_quad_t frev, cur_usec;
+
+#ifndef nolint
+       i = 0;
+       len = 0;
+#endif
+       *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_long *, 5 * NFSX_UNSIGNED);
+               fxdr_hyper(tl, &nfsd->nd_off);
+               tl += 3;
+               nfsd->nd_stable = fxdr_unsigned(int, *tl++);
+           } else {
+               nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+               nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl);
+               tl += 2;
+           }
+           len = fxdr_unsigned(long, *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);
+       }
+    
        /*
        /*
-        * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
-        * loop until done.
+        * Now, do VOP_WRITE()s for any one(s) that need to be done now
+        * and generate the associated reply mbuf list(s).
         */
         */
-       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) {
-                       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 ?? */
+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
                        else
-                               ivp->iov_op = NULL;
-#endif
-                       uiop->uio_iovcnt++;
-                       ivp++;
-                       len -= xfer;
-                       siz += xfer;
-                       mp = mp->m_next;
+                           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 (len > 0 && mp == NULL) {
-                       error = EBADRPC;
-                       vput(vp);
-                       nfsm_reply(0);
+    
+               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);
                }
                }
-               uiop->uio_resid = siz;
-               if (error = VOP_WRITE(vp, uiop, ioflags, cred)) {
-                       vput(vp);
-                       nfsm_reply(0);
+               m_freem(mrep);
+               if (vp) {
+                   aftat_ret = VOP_GETATTR(vp, &va, cred, procp);
+                   vput(vp);
                }
                }
-               off = uiop->uio_offset;
+
+               /*
+                * 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_long *, 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;
        }
        }
-       error = VOP_GETATTR(vp, vap, cred, nfsd->nd_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_long *, 2*NFSX_UNSIGNED);
-               txdr_hyper(&vap->va_filerev, tl);
+       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);
+}
+
+/*
+ * 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;
        }
        }
-       nfsm_srvdone;
+}
+
+/*
+ * 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_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);
 }
 
 /*
  * nfs create service
  * now does a truncate to 0 length via. setattr if it already exists
  */
 }
 
 /*
  * nfs create service
  * now does a truncate to 0 length via. setattr if it already exists
  */
-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;
+int
+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 vattr *vap = &va;
        register struct nfsv2_sattr *sp;
        register u_long *tl;
        register struct vattr *vap = &va;
        register struct nfsv2_sattr *sp;
        register u_long *tl;
@@ -656,65 +1256,122 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
        register caddr_t cp;
        register long t1;
        caddr_t bpos;
        register caddr_t cp;
        register long t1;
        caddr_t bpos;
-       int error = 0, rdev, cache, len, tsize;
+       int error = 0, rdev, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1;
+       int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
        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;
        fhandle_t *fhp;
-       u_quad_t frev;
+       u_quad_t frev, tempsize;
+       u_char cverf[NFSX_V3CREATEVERF];
 
 
+#ifndef nolint
+       rdev = 0;
+#endif
        nd.ni_cnd.cn_nameiop = 0;
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        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;
        nd.ni_cnd.cn_cred = cred;
        nd.ni_cnd.cn_nameiop = CREATE;
        nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
-       if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-           nfsd->nd_procp))
-               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(vap);
        VATTR_NULL(vap);
-       nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+       if (v3) {
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+               how = fxdr_unsigned(int, *tl);
+               switch (how) {
+               case NFSV3CREATE_GUARDED:
+                       if (nd.ni_vp) {
+                               error = EEXIST;
+                               break;
+                       }
+               case NFSV3CREATE_UNCHECKED:
+                       nfsm_srvsattr(vap);
+                       break;
+               case NFSV3CREATE_EXCLUSIVE:
+                       nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF);
+                       bcopy(cp, cverf, NFSX_V3CREATEVERF);
+                       exclusive_flag = 1;
+                       if (nd.ni_vp == NULL)
+                               vap->va_mode = 0;
+                       break;
+               };
+               vap->va_type = VREG;
+       } else {
+               nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
+               if (vap->va_type == VNON)
+                       vap->va_type = VREG;
+               vap->va_mode = nfstov_mode(sp->sa_mode);
+               switch (vap->va_type) {
+               case VREG:
+                       tsize = fxdr_unsigned(long, sp->sa_size);
+                       if (tsize != -1)
+                               vap->va_size = (u_quad_t)tsize;
+                       break;
+               case VCHR:
+               case VBLK:
+               case VFIFO:
+                       rdev = fxdr_unsigned(long, sp->sa_size);
+                       break;
+               };
+       }
+
        /*
         * Iff doesn't exist, create it
         * otherwise just truncate to 0 length
         *   should I set the mode too ??
         */
        if (nd.ni_vp == NULL) {
        /*
         * Iff doesn't exist, create it
         * otherwise just truncate to 0 length
         *   should I set the mode too ??
         */
        if (nd.ni_vp == NULL) {
-               vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
-               if (vap->va_type == VNON)
-                       vap->va_type = VREG;
-               vap->va_mode = nfstov_mode(sp->sa_mode);
-               if (nfsd->nd_nqlflag == NQL_NOVAL)
-                       rdev = fxdr_unsigned(long, sp->sa_nfssize);
-               else
-                       rdev = fxdr_unsigned(long, sp->sa_nqrdev);
                if (vap->va_type == VREG || vap->va_type == VSOCK) {
                        p->p_spare[1]--;
                        vrele(nd.ni_startdir);
                if (vap->va_type == VREG || vap->va_type == VSOCK) {
                        p->p_spare[1]--;
                        vrele(nd.ni_startdir);
-                       nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-                       if (error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
-                               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, vap);
+                       if (!error) {
+                               FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+                               if (exclusive_flag) {
+                                       exclusive_flag = 0;
+                                       VATTR_NULL(vap);
+                                       bcopy(cverf, (caddr_t)&vap->va_atime,
+                                               NFSX_V3CREATEVERF);
+                                       error = VOP_SETATTR(nd.ni_vp, vap, cred,
+                                               procp);
+                               }
+                       }
                } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
                        vap->va_type == VFIFO) {
                        if (vap->va_type == VCHR && rdev == 0xffffffff)
                                vap->va_type = VFIFO;
                } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
                        vap->va_type == VFIFO) {
                        if (vap->va_type == VCHR && rdev == 0xffffffff)
                                vap->va_type = VFIFO;
-                       if (vap->va_type == VFIFO) {
-#ifndef FIFO
-                               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)) {
+                       if (error = suser(cred, (u_short *)0)) {
+                               vrele(nd.ni_startdir);
+                               free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
                                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                                vput(nd.ni_dvp);
                                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                                vput(nd.ni_dvp);
-                               goto out;
+                               nfsm_reply(0);
+                               return (error);
                        } else
                                vap->va_rdev = (dev_t)rdev;
                        } else
                                vap->va_rdev = (dev_t)rdev;
-                       nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+                       nqsrv_getl(nd.ni_dvp, ND_WRITE);
                        if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
                                p->p_spare[1]--;
                                vrele(nd.ni_startdir);
                        if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
                                p->p_spare[1]--;
                                vrele(nd.ni_startdir);
@@ -722,8 +1379,8 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
                        }
                        nd.ni_cnd.cn_nameiop = LOOKUP;
                        nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
                        }
                        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)) {
                                free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
                                nfsm_reply(0);
                        if (error = lookup(&nd)) {
                                free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
                                nfsm_reply(0);
@@ -737,10 +1394,11 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
                                nfsm_reply(0);
                        }
                } else {
                                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;
                        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                        vput(nd.ni_dvp);
                        error = ENXIO;
-                       goto out;
                }
                vp = nd.ni_vp;
        } else {
                }
                vp = nd.ni_vp;
        } else {
@@ -753,42 +1411,53 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
                else
                        vput(nd.ni_dvp);
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                else
                        vput(nd.ni_dvp);
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
-               if (nfsd->nd_nqlflag == NQL_NOVAL) {
-                       tsize = fxdr_unsigned(long, sp->sa_nfssize);
-                       if (tsize != -1)
-                               vap->va_size = (u_quad_t)tsize;
-                       else
-                               vap->va_size = -1;
-               } else
-                       fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
                if (vap->va_size != -1) {
                if (vap->va_size != -1) {
-                       if (error = nfsrv_access(vp, VWRITE, cred,
-                           (nd.ni_cnd.cn_flags & RDONLY), nfsd->nd_procp)) {
-                               vput(vp);
-                               nfsm_reply(0);
+                       error = nfsrv_access(vp, VWRITE, cred,
+                           (nd.ni_cnd.cn_flags & RDONLY), procp);
+                       if (!error) {
+                               nqsrv_getl(vp, ND_WRITE);
+                               tempsize = vap->va_size;
+                               VATTR_NULL(vap);
+                               vap->va_size = tempsize;
+                               error = VOP_SETATTR(vp, vap, cred,
+                                        procp);
                        }
                        }
-                       nqsrv_getl(vp, NQL_WRITE);
-                       if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+                       if (error)
                                vput(vp);
                                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)) {
+       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, vap, cred, procp);
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
        }
        }
-       error = VOP_GETATTR(vp, vap, 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);
+       if (v3) {
+               if (exclusive_flag && !error &&
+                       bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF))
+                       error = EEXIST;
+               diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+               vrele(dirp);
+       }
+       nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3));
+       if (v3) {
+               if (!error) {
+                       nfsm_srvpostop_fh(fhp);
+                       nfsm_srvpostop_attr(0, vap);
+               }
+               nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+       } else {
+               nfsm_srvfhtom(fhp, v3);
+               nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+               nfsm_srvfillattr(vap, fp);
+       }
+       return (0);
 nfsmout:
 nfsmout:
-       if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags)
+       if (dirp)
+               vrele(dirp);
+       if (nd.ni_cnd.cn_nameiop) {
                p->p_spare[1]--, vrele(nd.ni_startdir);
        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        if (nd.ni_dvp == nd.ni_vp)
                p->p_spare[1]--, vrele(nd.ni_startdir);
        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        if (nd.ni_dvp == nd.ni_vp)
@@ -798,104 +1467,285 @@ nfsmout:
        if (nd.ni_vp)
                vput(nd.ni_vp);
        return (error);
        if (nd.ni_vp)
                vput(nd.ni_vp);
        return (error);
-
-out:
-       p->p_spare[1]--;
-       vrele(nd.ni_startdir);
-       free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
-       nfsm_reply(0);
 }
 
 /*
 }
 
 /*
- * nfs remove service
+ * nfs v3 mknod service
  */
  */
-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;
+int
+nfsrv_mknod(nfsd, slp, procp, mrq)
+       struct nfsrv_descript *nfsd;
+       struct nfssvc_sock *slp;
+       struct proc *procp;
+       struct mbuf **mrq;
 {
 {
-       struct nameidata nd;
+       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 vattr *vap = &va;
        register u_long *tl;
        register u_long *tl;
+       struct nameidata nd;
+       register caddr_t cp;
        register long t1;
        caddr_t bpos;
        register long t1;
        caddr_t bpos;
-       int error = 0, cache, len;
+       int error = 0, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1;
+       u_long major, minor;
+       enum vtype vtyp;
        char *cp2;
        char *cp2;
-       struct mbuf *mb, *mreq;
-       struct vnode *vp;
-       nfsv2fh_t nfh;
+       struct mbuf *mb, *mb2, *mreq;
+       struct vnode *vp, *dirp = (struct vnode *)0;
+       nfsfh_t nfh;
        fhandle_t *fhp;
        u_quad_t frev;
 
        fhandle_t *fhp;
        u_quad_t frev;
 
+       nd.ni_cnd.cn_nameiop = 0;
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        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_cred = cred;
-       nd.ni_cnd.cn_nameiop = DELETE;
-       nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
-       if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-           nfsd->nd_procp))
-               nfsm_reply(0);
-       vp = nd.ni_vp;
-       if (vp->v_type == VDIR &&
-               (error = suser(cred, (u_short *)0)))
+       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_long *, 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;
                goto out;
+       }
+       VATTR_NULL(vap);
+       nfsm_srvsattr(vap);
+       if (vtyp == VCHR || vtyp == VBLK) {
+               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+               major = fxdr_unsigned(u_long, *tl++);
+               minor = fxdr_unsigned(u_long, *tl);
+               vap->va_rdev = makedev(major, minor);
+       }
+
        /*
        /*
-        * The root of a mounted filesystem cannot be deleted.
+        * Iff doesn't exist, create it.
         */
         */
-       if (vp->v_flag & VROOT) {
-               error = EBUSY;
+       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;
        }
                goto out;
        }
-       if (vp->v_flag & VTEXT)
-               (void) vnode_pager_uncache(vp);
-out:
-       if (!error) {
-               nqsrv_getl(nd.ni_dvp, NQL_WRITE);
-               nqsrv_getl(vp, NQL_WRITE);
-               error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+       vap->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, vap);
+               if (!error)
+                       FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
        } else {
        } else {
-               VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
-               if (nd.ni_dvp == vp)
-                       vrele(nd.ni_dvp);
-               else
+               if (error = suser(cred, (u_short *)0)) {
+                       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);
                        vput(nd.ni_dvp);
+                       goto out;
+               }
+               nqsrv_getl(nd.ni_dvp, ND_WRITE);
+               if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
+                       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:
+       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, vap, cred, procp);
                vput(vp);
        }
                vput(vp);
        }
-       nfsm_reply(0);
+       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, vap);
+       }
+       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, 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_long *tl;
+       register long t1;
+       caddr_t bpos;
+       int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1;
+       int v3 = (nfsd->nd_flag & ND_NFSV3);
+       char *cp2;
+       struct mbuf *mb, *mreq, *mb2;
+       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_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, 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);
+       }
+       if (!error) {
+               vp = nd.ni_vp;
+               if (vp->v_type == VDIR &&
+                       (error = suser(cred, (u_short *)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:
+               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;
 }
 
 /*
  * nfs rename service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs rename service
  */
-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;
+int
+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_long *tl;
        register long t1;
        caddr_t bpos;
        register u_long *tl;
        register long 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;
        char *cp2;
-       struct mbuf *mb, *mreq;
+       struct mbuf *mb, *mreq, *mb2;
        struct nameidata fromnd, tond;
        struct nameidata fromnd, tond;
-       struct vnode *fvp, *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;
 
        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);
        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.
        /*
         * Remember our original uid so that we can reset cr_uid before
         * the second nfs_namei() call, in case it is remapped.
@@ -904,9 +1754,25 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
        fromnd.ni_cnd.cn_cred = cred;
        fromnd.ni_cnd.cn_nameiop = DELETE;
        fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
        fromnd.ni_cnd.cn_cred = cred;
        fromnd.ni_cnd.cn_nameiop = DELETE;
        fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
-       if (error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md,
-           &dpos, nfsd->nd_procp))
-               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);
        fvp = fromnd.ni_vp;
        nfsm_srvmtofh(tfhp);
        nfsm_strsiz(len2, NFS_MAXNAMLEN);
@@ -914,8 +1780,18 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
        tond.ni_cnd.cn_cred = cred;
        tond.ni_cnd.cn_nameiop = RENAME;
        tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
        tond.ni_cnd.cn_cred = cred;
        tond.ni_cnd.cn_nameiop = RENAME;
        tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
-       if (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);
                vrele(fvp);
                VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
                vrele(fromnd.ni_dvp);
                vrele(fvp);
@@ -925,27 +1801,45 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
        tvp = tond.ni_vp;
        if (tvp != NULL) {
                if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
        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) {
                        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) {
                        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) {
                        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) {
                goto out;
        }
        if (fvp->v_mount != tdvp->v_mount) {
-               error = EXDEV;
+               if (v3)
+                       error = EXDEV;
+               else
+                       error = ENOTEMPTY;
                goto out;
        }
        if (fvp == tdvp)
                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),
        /*
         * If source is the same as the destination (that is the
         * same vnode with the same name in the same directory),
@@ -958,10 +1852,10 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
                error = -1;
 out:
        if (!error) {
                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)
                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 {
                error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
                                   tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
        } else {
@@ -975,24 +1869,42 @@ out:
                VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
                vrele(fromnd.ni_dvp);
                vrele(fvp);
                VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
                vrele(fromnd.ni_dvp);
                vrele(fvp);
+               if (error == -1)
+                       error = 0;
        }
        p->p_spare[1]--;
        vrele(tond.ni_startdir);
        FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
 out1:
        }
        p->p_spare[1]--;
        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);
+       }
        p->p_spare[1]--;
        vrele(fromnd.ni_startdir);
        FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
        p->p_spare[1]--;
        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:
 
 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) {
                p->p_spare[1]--;
                vrele(tond.ni_startdir);
                FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
        }
                p->p_spare[1]--;
                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) {
                p->p_spare[1]--;
                vrele(fromnd.ni_startdir);
                FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
                p->p_spare[1]--;
                vrele(fromnd.ni_startdir);
                FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
@@ -1006,22 +1918,28 @@ nfsmout:
 /*
  * nfs link service
  */
 /*
  * nfs link service
  */
-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;
+int
+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_long *tl;
        register long t1;
        caddr_t bpos;
        struct nameidata nd;
        register u_long *tl;
        register long 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;
        char *cp2;
-       struct mbuf *mb, *mreq;
-       struct vnode *vp, *xp;
-       nfsv2fh_t nfh, dnfh;
+       struct mbuf *mb, *mreq, *mb2;
+       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;
 
        fhandle_t *fhp, *dfhp;
        u_quad_t frev;
 
@@ -1029,16 +1947,31 @@ nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
        dfhp = &dnfh.fh_generic;
        nfsm_srvmtofh(fhp);
        nfsm_srvmtofh(dfhp);
        dfhp = &dnfh.fh_generic;
        nfsm_srvmtofh(fhp);
        nfsm_srvmtofh(dfhp);
-       nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
-       if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
+       nfsm_srvnamesiz(len);
+       if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               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)))
                goto out1;
        nd.ni_cnd.cn_cred = cred;
        nd.ni_cnd.cn_nameiop = CREATE;
        nd.ni_cnd.cn_flags = LOCKPARENT;
        if (vp->v_type == VDIR && (error = suser(cred, (u_short *)0)))
                goto out1;
        nd.ni_cnd.cn_cred = cred;
        nd.ni_cnd.cn_nameiop = CREATE;
        nd.ni_cnd.cn_flags = LOCKPARENT;
-       if (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) {
                goto out1;
        xp = nd.ni_vp;
        if (xp != NULL) {
@@ -1050,8 +1983,8 @@ nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
                error = EXDEV;
 out:
        if (!error) {
                error = EXDEV;
 out:
        if (!error) {
-               nqsrv_getl(vp, NQL_WRITE);
-               nqsrv_getl(xp, NQL_WRITE);
+               nqsrv_getl(vp, ND_WRITE);
+               nqsrv_getl(xp, ND_WRITE);
                error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1063,47 +1996,76 @@ out:
                        vrele(nd.ni_vp);
        }
 out1:
                        vrele(nd.ni_vp);
        }
 out1:
+       if (v3)
+               getret = VOP_GETATTR(vp, &at, cred, procp);
+       if (dirp) {
+               diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp);
+               vrele(dirp);
+       }
        vrele(vp);
        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;
 }
 
 /*
  * nfs symbolic link service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs symbolic link service
  */
-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;
+int
+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 struct vattr *vap = &va;
        register u_long *tl;
        register long t1;
        struct nfsv2_sattr *sp;
        struct nameidata nd;
        register struct vattr *vap = &va;
        register u_long *tl;
        register long t1;
        struct nfsv2_sattr *sp;
-       caddr_t bpos;
+       char *bpos, *cp, *pathcp = (char *)0, *cp2;
        struct uio io;
        struct iovec iv;
        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;
 
        fhandle_t *fhp;
        u_quad_t frev;
 
-       pathcp = (char *)0;
+       nd.ni_cnd.cn_nameiop = 0;
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        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_cred = cred;
        nd.ni_cnd.cn_nameiop = CREATE;
-       nd.ni_cnd.cn_flags = LOCKPARENT;
-       if (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;
                goto out;
+       VATTR_NULL(vap);
+       if (v3)
+               nfsm_srvsattr(vap);
        nfsm_strsiz(len2, NFS_MAXPATHLEN);
        MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
        iv.iov_base = pathcp;
        nfsm_strsiz(len2, NFS_MAXPATHLEN);
        MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
        iv.iov_base = pathcp;
@@ -1116,9 +2078,14 @@ nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
        io.uio_rw = UIO_READ;
        io.uio_procp = (struct proc *)0;
        nfsm_mtouio(&io, len2);
        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);
+               vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode);
+       }
        *(pathcp + len2) = '\0';
        if (nd.ni_vp) {
        *(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);
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
                if (nd.ni_dvp == nd.ni_vp)
                        vrele(nd.ni_dvp);
@@ -1128,16 +2095,54 @@ nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
                error = EEXIST;
                goto out;
        }
                error = EEXIST;
                goto out;
        }
-       VATTR_NULL(vap);
-       vap->va_mode = fxdr_unsigned(u_short, 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, vap, pathcp);
        error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, 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, vap, 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);
 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, vap);
+               }
+               nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+       }
+       return (0);
 nfsmout:
 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);
        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        if (nd.ni_dvp == nd.ni_vp)
                vrele(nd.ni_dvp);
@@ -1153,42 +2158,66 @@ nfsmout:
 /*
  * nfs mkdir service
  */
 /*
  * nfs mkdir service
  */
-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;
+int
+nfsrv_mkdir(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;
        register struct vattr *vap = &va;
        register struct vattr *vap = &va;
-       register struct nfsv2_fattr *fp;
+       register struct nfs_fattr *fp;
        struct nameidata nd;
        register caddr_t cp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        struct nameidata nd;
        register caddr_t cp;
        register u_long *tl;
        register long 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;
        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);
        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;
        nd.ni_cnd.cn_cred = cred;
        nd.ni_cnd.cn_nameiop = CREATE;
        nd.ni_cnd.cn_flags = LOCKPARENT;
-       if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-           nfsd->nd_procp))
-               nfsm_reply(0);
-       nfsm_dissect(tl, u_long *, 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(vap);
        VATTR_NULL(vap);
+       if (v3) {
+               nfsm_srvsattr(vap);
+       } else {
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+               vap->va_mode = nfstov_mode(*tl++);
+       }
        vap->va_type = VDIR;
        vap->va_type = VDIR;
-       vap->va_mode = nfstov_mode(*tl++);
        vp = nd.ni_vp;
        if (vp != NULL) {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        vp = nd.ni_vp;
        if (vp != NULL) {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1198,26 +2227,40 @@ nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq)
                        vput(nd.ni_dvp);
                vrele(vp);
                error = EEXIST;
                        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, vap))
-               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)) {
+       nqsrv_getl(nd.ni_dvp, ND_WRITE);
+       error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap);
+       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, vap, cred, procp);
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
        }
        }
-       error = VOP_GETATTR(vp, vap, 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);
+out:
+       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, vap);
+               }
+               nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
+       } else {
+               nfsm_srvfhtom(fhp, v3);
+               nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
+               nfsm_srvfillattr(vap, fp);
+       }
+       return (0);
 nfsmout:
 nfsmout:
+       if (dirp)
+               vrele(dirp);
        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        if (nd.ni_dvp == nd.ni_vp)
                vrele(nd.ni_dvp);
        VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
        if (nd.ni_dvp == nd.ni_vp)
                vrele(nd.ni_dvp);
@@ -1231,34 +2274,55 @@ nfsmout:
 /*
  * nfs rmdir service
  */
 /*
  * nfs rmdir service
  */
-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;
+int
+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_long *tl;
        register long t1;
        caddr_t bpos;
        register u_long *tl;
        register long 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;
        char *cp2;
-       struct mbuf *mb, *mreq;
-       struct vnode *vp;
-       nfsv2fh_t nfh;
+       struct mbuf *mb, *mreq, *mb2;
+       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);
        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;
        nd.ni_cnd.cn_cred = cred;
        nd.ni_cnd.cn_nameiop = DELETE;
        nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
-       if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
-           nfsd->nd_procp))
-               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;
        vp = nd.ni_vp;
        if (vp->v_type != VDIR) {
                error = ENOTDIR;
@@ -1278,8 +2342,8 @@ nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq)
                error = EBUSY;
 out:
        if (!error) {
                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);
                error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
        } else {
                VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
@@ -1289,7 +2353,15 @@ out:
                        vput(nd.ni_dvp);
                vput(vp);
        }
                        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;
 }
 
        nfsm_srvdone;
 }
 
@@ -1313,7 +2385,7 @@ out:
  *     than requested, but this may not apply to all filesystems. For
  *     example, client NFS does not { although it is never remote mounted
  *     anyhow }
  *     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 ...
  * 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 ...
@@ -1323,20 +2395,25 @@ out:
  *     "entry" structures, but are in the rpc.
  */
 struct flrep {
  *     "entry" structures, but are in the rpc.
  */
 struct flrep {
-       u_long fl_cachable;
-       u_long fl_duration;
-       u_long fl_frev[2];
-       nfsv2fh_t fl_nfh;
-       u_long fl_fattr[NFSX_NQFATTR / sizeof (u_long)];
+       nfsuint64       fl_off;
+       u_long          fl_postopok;
+       u_long          fl_fattr[NFSX_V3FATTR / sizeof (u_long)];
+       u_long          fl_fhok;
+       u_long          fl_fhsize;
+       u_long          fl_nfh[NFSX_V3FH / sizeof (u_long)];
 };
 
 };
 
-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;
+int
+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;
        register char *bp, *be;
        register struct mbuf *mp;
        register struct dirent *dp;
@@ -1347,37 +2424,58 @@ nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq)
        struct mbuf *mb, *mb2, *mreq, *mp2;
        char *cpos, *cend, *cp2, *rbuf;
        struct vnode *vp;
        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;
        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);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
-       off = fxdr_unsigned(u_long, *tl++);
+       if (v3) {
+               nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
+               fxdr_hyper(tl, &toff);
+               tl += 2;
+               fxdr_hyper(tl, &verf);
+               tl += 2;
+       } else {
+               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+               toff = fxdr_unsigned(u_quad_t, *tl++);
+       }
+       off = toff;
        cnt = fxdr_unsigned(int, *tl);
        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;
        fullsiz = siz;
-       ncookies = siz / 16;    /* Guess at the number of cookies needed. */
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
-       nqsrv_getl(vp, NQL_READ);
-       if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               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);
+               if (!error && toff && verf != at.va_filerev)
+                       error = NFSERR_BAD_COOKIE;
+       }
+       if (!error)
+               error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
+       if (error) {
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
+               nfsm_reply(NFSX_POSTOPATTR(v3));
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
        }
        VOP_UNLOCK(vp);
        MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
        }
        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;
 again:
        iv.iov_base = rbuf;
        iv.iov_len = fullsiz;
@@ -1388,14 +2486,28 @@ again:
        io.uio_segflg = UIO_SYSSPACE;
        io.uio_rw = UIO_READ;
        io.uio_procp = (struct proc *)0;
        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;
+       if (cookies) {
+               free((caddr_t)cookies, M_TEMP);
+               cookies = NULL;
+       }
+       error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies);
        off = (off_t)io.uio_offset;
        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;
+       }
        if (error) {
                vrele(vp);
        if (error) {
                vrele(vp);
-               free((caddr_t)cookiebuf, M_TEMP);
                free((caddr_t)rbuf, 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;
        }
        if (io.uio_resid) {
                siz -= io.uio_resid;
@@ -1406,12 +2518,19 @@ again:
                 */
                if (siz == 0) {
                        vrele(vp);
                 */
                if (siz == 0) {
                        vrele(vp);
-                       nfsm_reply(2*NFSX_UNSIGNED);
-                       nfsm_build(tl, u_long *, 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_long *, 4 * NFSX_UNSIGNED);
+                               txdr_hyper(&at.va_filerev, tl);
+                               tl += 2;
+                       } else
+                               nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
                        *tl++ = nfs_false;
                        *tl = nfs_true;
                        *tl++ = nfs_false;
                        *tl = nfs_true;
-                       FREE((caddr_t)cookiebuf, M_TEMP);
                        FREE((caddr_t)rbuf, M_TEMP);
                        FREE((caddr_t)rbuf, M_TEMP);
+                       FREE((caddr_t)cookies, M_TEMP);
                        return (0);
                }
        }
                        return (0);
                }
        }
@@ -1422,31 +2541,39 @@ again:
         */
        cpos = rbuf;
        cend = rbuf + siz;
         */
        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;
                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;
        }
 
                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_long *, 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 */
        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;
                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;
                        if (len > cnt) {
                                eofflag = 0;
                                break;
@@ -1458,6 +2585,11 @@ again:
                        nfsm_clget;
                        *tl = nfs_true;
                        bp += NFSX_UNSIGNED;
                        nfsm_clget;
                        *tl = nfs_true;
                        bp += NFSX_UNSIGNED;
+                       if (v3) {
+                               nfsm_clget;
+                               *tl = 0;
+                               bp += NFSX_UNSIGNED;
+                       }
                        nfsm_clget;
                        *tl = txdr_unsigned(dp->d_fileno);
                        bp += NFSX_UNSIGNED;
                        nfsm_clget;
                        *tl = txdr_unsigned(dp->d_fileno);
                        bp += NFSX_UNSIGNED;
@@ -1486,12 +2618,18 @@ again:
                        nfsm_clget;
        
                        /* Finish off the record */
                        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;
                }
                cpos += dp->d_reclen;
                dp = (struct dirent *)cpos;
                        bp += NFSX_UNSIGNED;
                }
                cpos += dp->d_reclen;
                dp = (struct dirent *)cpos;
-               cookie++;
+               cookiep++;
+               ncookies--;
        }
        vrele(vp);
        nfsm_clget;
        }
        vrele(vp);
        nfsm_clget;
@@ -1508,18 +2646,22 @@ again:
                        mp->m_len = bp - mtod(mp, caddr_t);
        } else
                mp->m_len += bp - bpos;
                        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;
 }
 
        nfsm_srvdone;
 }
 
-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;
+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 char *bp, *be;
        register struct mbuf *mp;
        register struct dirent *dp;
@@ -1531,40 +2673,53 @@ nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq)
        char *cpos, *cend, *cp2, *rbuf;
        struct vnode *vp, *nvp;
        struct flrep fl;
        char *cpos, *cend, *cp2, *rbuf;
        struct vnode *vp, *nvp;
        struct flrep fl;
-       nfsv2fh_t nfh;
-       fhandle_t *fhp;
+       nfsfh_t nfh;
+       fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh;
        struct uio io;
        struct iovec iv;
        struct uio io;
        struct iovec iv;
-       struct vattr va, *vap = &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;
+       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);
 
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
-       nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
-       off = fxdr_unsigned(u_long, *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;
+       nfsm_dissect(tl, u_long *, 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;
        fullsiz = siz;
-       ncookies = siz / 16;    /* Guess at the number of cookies needed. */
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
-       nqsrv_getl(vp, NQL_READ);
-       if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(NFSX_UNSIGNED);
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
+       }
+       error = getret = VOP_GETATTR(vp, &at, cred, procp);
+       if (!error && toff && verf != at.va_filerev)
+               error = NFSERR_BAD_COOKIE;
+       if (!error) {
+               nqsrv_getl(vp, ND_READ);
+               error = nfsrv_access(vp, VEXEC, cred, rdonly, procp);
+       }
+       if (error) {
                vput(vp);
                vput(vp);
-               nfsm_reply(0);
+               nfsm_reply(NFSX_V3POSTOPATTR);
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
        }
        VOP_UNLOCK(vp);
        MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
        }
        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;
 again:
        iv.iov_base = rbuf;
        iv.iov_len = fullsiz;
@@ -1575,14 +2730,26 @@ again:
        io.uio_segflg = UIO_SYSSPACE;
        io.uio_rw = UIO_READ;
        io.uio_procp = (struct proc *)0;
        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;
+       eofflag = 0;
+       if (cookies) {
+               free((caddr_t)cookies, M_TEMP);
+               cookies = NULL;
+       }
+       error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies);
+       off = (u_quad_t)io.uio_offset;
+       getret = VOP_GETATTR(vp, &at, cred, procp);
+       if (!cookies && !error)
+               error = NFSERR_PERM;
+       if (!error)
+               error = getret;
        if (error) {
                vrele(vp);
        if (error) {
                vrele(vp);
-               free((caddr_t)cookiebuf, M_TEMP);
+               if (cookies)
+                       free((caddr_t)cookies, M_TEMP);
                free((caddr_t)rbuf, M_TEMP);
                free((caddr_t)rbuf, M_TEMP);
-               nfsm_reply(0);
+               nfsm_reply(NFSX_V3POSTOPATTR);
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
        }
        if (io.uio_resid) {
                siz -= io.uio_resid;
        }
        if (io.uio_resid) {
                siz -= io.uio_resid;
@@ -1593,11 +2760,15 @@ again:
                 */
                if (siz == 0) {
                        vrele(vp);
                 */
                if (siz == 0) {
                        vrele(vp);
-                       nfsm_reply(2 * NFSX_UNSIGNED);
-                       nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF +
+                               2 * NFSX_UNSIGNED);
+                       nfsm_srvpostop_attr(getret, &at);
+                       nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
+                       txdr_hyper(&at.va_filerev, tl);
+                       tl += 2;
                        *tl++ = nfs_false;
                        *tl = nfs_true;
                        *tl++ = nfs_false;
                        *tl = nfs_true;
-                       FREE((caddr_t)cookiebuf, M_TEMP);
+                       FREE((caddr_t)cookies, M_TEMP);
                        FREE((caddr_t)rbuf, M_TEMP);
                        return (0);
                }
                        FREE((caddr_t)rbuf, M_TEMP);
                        return (0);
                }
@@ -1609,27 +2780,31 @@ again:
         */
        cpos = rbuf;
        cend = rbuf + siz;
         */
        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;
                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;
        }
 
                siz = fullsiz;
                goto again;
        }
 
-       len = 3 * NFSX_UNSIGNED;        /* paranoia, probably can be 0 */
-       nfsm_reply(siz);
+       dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
+       nfsm_reply(cnt);
+       nfsm_srvpostop_attr(getret, &at);
+       nfsm_build(tl, u_long *, 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 */
        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;
                if (dp->d_fileno != 0) {
                        nlen = dp->d_namlen;
                        rem = nfsm_rndup(nlen)-nlen;
@@ -1640,59 +2815,51 @@ again:
                         */
                        if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
                                goto invalid;
                         */
                        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 =
+                       bzero((caddr_t)nfhp, NFSX_V3FH);
+                       nfhp->fh_fsid =
                                nvp->v_mount->mnt_stat.f_fsid;
                                nvp->v_mount->mnt_stat.f_fsid;
-                       if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) {
+                       if (VFS_VPTOFH(nvp, &nfhp->fh_fid)) {
                                vput(nvp);
                                goto invalid;
                        }
                                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, vap, cred, nfsd->nd_procp)) {
+                       if (VOP_GETATTR(nvp, vap, cred, procp)) {
                                vput(nvp);
                                goto invalid;
                        }
                        vput(nvp);
                                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) {
+
+                       /*
+                        * 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;
                        }
                                eofflag = 0;
                                break;
                        }
+
                        /*
                         * Build the directory record xdr from
                         * the dirent entry.
                         */
                        /*
                         * 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 = 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;
-                       }
+                       nfsm_clget;
+                       *tl = 0;
+                       bp += NFSX_UNSIGNED;
                        nfsm_clget;
                        *tl = txdr_unsigned(dp->d_fileno);
                        bp += NFSX_UNSIGNED;
                        nfsm_clget;
                        *tl = txdr_unsigned(dp->d_fileno);
                        bp += NFSX_UNSIGNED;
@@ -1705,8 +2872,8 @@ again:
                        cp = dp->d_name;
                        while (xfer > 0) {
                                nfsm_clget;
                        cp = dp->d_name;
                        while (xfer > 0) {
                                nfsm_clget;
-                               if ((bp+xfer) > be)
-                                       tsiz = be-bp;
+                               if ((bp + xfer) > be)
+                                       tsiz = be - bp;
                                else
                                        tsiz = xfer;
                                bcopy(cp, bp, tsiz);
                                else
                                        tsiz = xfer;
                                bcopy(cp, bp, tsiz);
@@ -1718,16 +2885,30 @@ again:
                        /* And null pad to a long boundary */
                        for (i = 0; i < rem; i++)
                                *bp++ = '\0';
                        /* And null pad to a long boundary */
                        for (i = 0; i < rem; i++)
                                *bp++ = '\0';
-                       nfsm_clget;
        
        
-                       /* Finish off the record */
-                       *tl = txdr_unsigned(*cookie);
-                       bp += NFSX_UNSIGNED;
+                       /*
+                        * 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;
                }
 invalid:
                cpos += dp->d_reclen;
                dp = (struct dirent *)cpos;
-               cookie++;
+               cookiep++;
+               ncookies--;
        }
        vrele(vp);
        nfsm_clget;
        }
        vrele(vp);
        nfsm_clget;
@@ -1744,54 +2925,286 @@ invalid:
                        mp->m_len = bp - mtod(mp, caddr_t);
        } else
                mp->m_len += bp - bpos;
                        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)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_long *tl;
+       register long 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_long *, 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);
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               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_long *, NFSX_V3WRITEVERF);
+               *tl++ = txdr_unsigned(boottime.tv_sec);
+               *tl = txdr_unsigned(boottime.tv_usec);
+       } else
+               return (0);
        nfsm_srvdone;
 }
 
 /*
  * nfs statfs service
  */
        nfsm_srvdone;
 }
 
 /*
  * nfs statfs service
  */
-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;
+int
+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 statfs *sf;
-       register struct nfsv2_statfs *sfp;
+       register struct nfs_statfs *sfp;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
        register u_long *tl;
        register long t1;
        caddr_t bpos;
-       int error = 0, rdonly, cache, 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;
        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;
        fhandle_t *fhp;
        struct statfs statfs;
-       u_quad_t frev;
+       u_quad_t frev, tval;
 
 
+#ifndef nolint
+       cache = 0;
+#endif
        fhp = &nfh.fh_generic;
        fhp = &nfh.fh_generic;
-       isnq = (nfsd->nd_nqlflag != NQL_NOVAL);
        nfsm_srvmtofh(fhp);
        nfsm_srvmtofh(fhp);
-       if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
-               nfsm_reply(0);
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(NFSX_UNSIGNED);
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
+       }
        sf = &statfs;
        sf = &statfs;
-       error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp);
+       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_long *tl;
+       register struct nfsv3_fsinfo *sip;
+       register long t1;
+       caddr_t bpos;
+       int error = 0, rdonly, cache, getret = 1, pref;
+       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);
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               nfsm_reply(NFSX_UNSIGNED);
+               nfsm_srvpostop_attr(getret, &at);
+               return (0);
+       }
+       getret = VOP_GETATTR(vp, &at, cred, procp);
        vput(vp);
        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_long *tl;
+       register struct nfsv3_pathconf *pc;
+       register long 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);
+       if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam,
+                &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) {
+               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;
 }
 
        nfsm_srvdone;
 }
 
@@ -1799,44 +3212,58 @@ nfsrv_statfs(nfsd, mrep, md, dpos, cred, nam, mrq)
  * Null operation, used by clients to ping server
  */
 /* ARGSUSED */
  * Null operation, used by clients to ping server
  */
 /* ARGSUSED */
-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;
+int
+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, *md = nfsd->nd_md;
+       struct mbuf *nam = nfsd->nd_nam;
+       caddr_t dpos = nfsd->nd_dpos;
+       struct ucred *cred = &nfsd->nd_cr;
        caddr_t bpos;
        caddr_t bpos;
-       int error = VNOVAL, cache;
+       int error = NFSERR_RETVOID, cache;
        struct mbuf *mb, *mreq;
        u_quad_t frev;
 
        struct mbuf *mb, *mreq;
        u_quad_t frev;
 
+#ifndef nolint
+       cache = 0;
+#endif
        nfsm_reply(0);
        nfsm_reply(0);
-       return (error);
+       return (0);
 }
 
 /*
  * No operation, used for obsolete procedures
  */
 /* ARGSUSED */
 }
 
 /*
  * No operation, used for obsolete procedures
  */
 /* ARGSUSED */
-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;
+int
+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, *md = nfsd->nd_md;
+       struct mbuf *nam = nfsd->nd_nam;
+       caddr_t dpos = nfsd->nd_dpos;
+       struct ucred *cred = &nfsd->nd_cr;
        caddr_t bpos;
        int error, cache;
        struct mbuf *mb, *mreq;
        u_quad_t frev;
 
        caddr_t bpos;
        int error, cache;
        struct mbuf *mb, *mreq;
        u_quad_t frev;
 
+#ifndef nolint
+       cache = 0;
+#endif
        if (nfsd->nd_repstat)
                error = nfsd->nd_repstat;
        else
                error = EPROCUNAVAIL;
        nfsm_reply(0);
        if (nfsd->nd_repstat)
                error = nfsd->nd_repstat;
        else
                error = EPROCUNAVAIL;
        nfsm_reply(0);
-       return (error);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -1849,6 +3276,7 @@ nfsrv_noop(nfsd, mrep, md, dpos, cred, nam, mrq)
  *     this because it opens a security hole, but since the nfs server opens
  *     a security hole the size of a barn door anyhow, what the heck.
  */
  *     this because it opens a security hole, but since the nfs server opens
  *     a security hole the size of a barn door anyhow, what the heck.
  */
+int
 nfsrv_access(vp, flags, cred, rdonly, p)
        register struct vnode *vp;
        int flags;
 nfsrv_access(vp, flags, cred, rdonly, p)
        register struct vnode *vp;
        int flags;
index 33f1149..530b506 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * 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
  *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -7,7 +7,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_socket.c        8.4 (Berkeley) %G%
+ *     @(#)nfs_socket.c        8.5 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
@@ -30,8 +30,9 @@
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+
 #include <nfs/rpcv2.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>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
  * External data, mostly RPC constants in XDR form
  */
 extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
  * External data, mostly RPC constants in XDR form
  */
 extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
-       rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+       rpc_msgaccepted, rpc_call, rpc_autherr,
        rpc_auth_kerb;
        rpc_auth_kerb;
-extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern u_long nfs_prog, nqnfs_prog;
 extern time_t nqnfsstarttime;
 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.
 
 /*
  * Defines which timer to use for the procnum.
@@ -106,7 +84,8 @@ static int nfsrv_errmap[ELAST] = {
  * 4 - write
  */
 static int proct[NFS_NPROCS] = {
  * 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,
 };
 
 /*
 };
 
 /*
@@ -136,10 +115,12 @@ int       nfsrv_null(),
        nfsrv_getattr(),
        nfsrv_setattr(),
        nfsrv_lookup(),
        nfsrv_getattr(),
        nfsrv_setattr(),
        nfsrv_lookup(),
+       nfsrv3_access(),
        nfsrv_readlink(),
        nfsrv_read(),
        nfsrv_write(),
        nfsrv_create(),
        nfsrv_readlink(),
        nfsrv_read(),
        nfsrv_write(),
        nfsrv_create(),
+       nfsrv_mknod(),
        nfsrv_remove(),
        nfsrv_rename(),
        nfsrv_link(),
        nfsrv_remove(),
        nfsrv_rename(),
        nfsrv_link(),
@@ -147,43 +128,49 @@ int       nfsrv_null(),
        nfsrv_mkdir(),
        nfsrv_rmdir(),
        nfsrv_readdir(),
        nfsrv_mkdir(),
        nfsrv_rmdir(),
        nfsrv_readdir(),
+       nfsrv_readdirplus(),
        nfsrv_statfs(),
        nfsrv_statfs(),
+       nfsrv_fsinfo(),
+       nfsrv_pathconf(),
+       nfsrv_commit(),
        nfsrv_noop(),
        nfsrv_noop(),
-       nqnfsrv_readdirlook(),
        nqnfsrv_getlease(),
        nqnfsrv_getlease(),
-       nqnfsrv_vacated(),
-       nqnfsrv_access();
+       nqnfsrv_vacated();
 
 
-int (*nfsrv_procs[NFS_NPROCS])() = {
+int (*nfsrv3_procs[NFS_NPROCS])() = {
        nfsrv_null,
        nfsrv_getattr,
        nfsrv_setattr,
        nfsrv_null,
        nfsrv_getattr,
        nfsrv_setattr,
-       nfsrv_noop,
        nfsrv_lookup,
        nfsrv_lookup,
+       nfsrv3_access,
        nfsrv_readlink,
        nfsrv_read,
        nfsrv_readlink,
        nfsrv_read,
-       nfsrv_noop,
        nfsrv_write,
        nfsrv_create,
        nfsrv_write,
        nfsrv_create,
+       nfsrv_mkdir,
+       nfsrv_symlink,
+       nfsrv_mknod,
        nfsrv_remove,
        nfsrv_remove,
+       nfsrv_rmdir,
        nfsrv_rename,
        nfsrv_link,
        nfsrv_rename,
        nfsrv_link,
-       nfsrv_symlink,
-       nfsrv_mkdir,
-       nfsrv_rmdir,
        nfsrv_readdir,
        nfsrv_readdir,
+       nfsrv_readdirplus,
        nfsrv_statfs,
        nfsrv_statfs,
-       nqnfsrv_readdirlook,
+       nfsrv_fsinfo,
+       nfsrv_pathconf,
+       nfsrv_commit,
        nqnfsrv_getlease,
        nqnfsrv_vacated,
        nfsrv_noop,
        nqnfsrv_getlease,
        nqnfsrv_vacated,
        nfsrv_noop,
-       nqnfsrv_access,
+       nfsrv_noop
 };
 
 /*
  * Initialize sockets and congestion for a new NFS connection.
  * We do not free the sockaddr if error.
  */
 };
 
 /*
  * Initialize sockets and congestion for a new NFS connection.
  * We do not free the sockaddr if error.
  */
+int
 nfs_connect(nmp, rep)
        register struct nfsmount *nmp;
        struct nfsreq *rep;
 nfs_connect(nmp, rep)
        register struct nfsmount *nmp;
        struct nfsreq *rep;
@@ -197,8 +184,9 @@ nfs_connect(nmp, rep)
 
        nmp->nm_so = (struct socket *)0;
        saddr = mtod(nmp->nm_nam, struct sockaddr *);
 
        nmp->nm_so = (struct socket *)0;
        saddr = mtod(nmp->nm_nam, struct sockaddr *);
-       if (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;
        nmp->nm_soflags = so->so_proto->pr_flags;
                goto bad;
        so = nmp->nm_so;
        nmp->nm_soflags = so->so_proto->pr_flags;
@@ -232,7 +220,8 @@ nfs_connect(nmp, rep)
                        goto bad;
                }
        } else {
                        goto bad;
                }
        } else {
-               if (error = soconnect(so, nmp->nm_nam))
+               error = soconnect(so, nmp->nm_nam);
+               if (error)
                        goto bad;
 
                /*
                        goto bad;
 
                /*
@@ -293,7 +282,8 @@ nfs_connect(nmp, rep)
                rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
                                * 2;
        }
                rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
                                * 2;
        }
-       if (error = soreserve(so, sndreserve, rcvreserve))
+       error = soreserve(so, sndreserve, rcvreserve);
+       if (error)
                goto bad;
        so->so_rcv.sb_flags |= SB_NOINTR;
        so->so_snd.sb_flags |= SB_NOINTR;
                goto bad;
        so->so_rcv.sb_flags |= SB_NOINTR;
        so->so_snd.sb_flags |= SB_NOINTR;
@@ -322,6 +312,7 @@ bad:
  * If this fails the mount point is DEAD!
  * nb: Must be called with the nfs_sndlock() set on the mount point.
  */
  * If this fails the mount point is DEAD!
  * nb: Must be called with the nfs_sndlock() set on the mount point.
  */
+int
 nfs_reconnect(rep)
        register struct nfsreq *rep;
 {
 nfs_reconnect(rep)
        register struct nfsreq *rep;
 {
@@ -330,7 +321,7 @@ nfs_reconnect(rep)
        int error;
 
        nfs_disconnect(nmp);
        int error;
 
        nfs_disconnect(nmp);
-       while (error = nfs_connect(nmp, rep)) {
+       while ((error = nfs_connect(nmp, rep))) {
                if (error == EINTR || error == ERESTART)
                        return (EINTR);
                (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
                if (error == EINTR || error == ERESTART)
                        return (EINTR);
                (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
@@ -377,6 +368,7 @@ nfs_disconnect(nmp)
  * - return EPIPE if a connection is lost for connection based sockets (TCP...)
  * - do any cleanup required by recoverable socket errors (???)
  */
  * - return EPIPE if a connection is lost for connection based sockets (TCP...)
  * - do any cleanup required by recoverable socket errors (???)
  */
+int
 nfs_send(so, nam, top, rep)
        register struct socket *so;
        struct mbuf *nam;
 nfs_send(so, nam, top, rep)
        register struct socket *so;
        struct mbuf *nam;
@@ -444,6 +436,7 @@ nfs_send(so, nam, top, rep)
  * For SOCK_STREAM we must be very careful to read an entire record once
  * we have read any of it, even if the system call has been interrupted.
  */
  * For SOCK_STREAM we must be very careful to read an entire record once
  * we have read any of it, even if the system call has been interrupted.
  */
+int
 nfs_receive(rep, aname, mp)
        register struct nfsreq *rep;
        struct mbuf **aname;
 nfs_receive(rep, aname, mp)
        register struct nfsreq *rep;
        struct mbuf **aname;
@@ -475,7 +468,8 @@ nfs_receive(rep, aname, mp)
         * until we have an entire rpc request/reply.
         */
        if (sotype != SOCK_DGRAM) {
         * until we have an entire rpc request/reply.
         */
        if (sotype != SOCK_DGRAM) {
-               if (error = nfs_sndlock(&rep->r_nmp->nm_flag, rep))
+               error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+               if (error)
                        return (error);
 tryagain:
                /*
                        return (error);
 tryagain:
                /*
@@ -491,8 +485,10 @@ tryagain:
                        nfs_sndunlock(&rep->r_nmp->nm_flag);
                        return (EINTR);
                }
                        nfs_sndunlock(&rep->r_nmp->nm_flag);
                        return (EINTR);
                }
-               if ((so = rep->r_nmp->nm_so) == NULL) {
-                       if (error = nfs_reconnect(rep)) {
+               so = rep->r_nmp->nm_so;
+               if (!so) {
+                       error = nfs_reconnect(rep); 
+                       if (error) {
                                nfs_sndunlock(&rep->r_nmp->nm_flag);
                                return (error);
                        }
                                nfs_sndunlock(&rep->r_nmp->nm_flag);
                                return (error);
                        }
@@ -501,7 +497,8 @@ tryagain:
                while (rep->r_flags & R_MUSTRESEND) {
                        m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
                        nfsstats.rpcretries++;
                while (rep->r_flags & R_MUSTRESEND) {
                        m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
                        nfsstats.rpcretries++;
-                       if (error = nfs_send(so, rep->r_nmp->nm_nam, m, rep)) {
+                       error = nfs_send(so, rep->r_nmp->nm_nam, m, rep);
+                       if (error) {
                                if (error == EINTR || error == ERESTART ||
                                    (error = nfs_reconnect(rep))) {
                                        nfs_sndunlock(&rep->r_nmp->nm_flag);
                                if (error == EINTR || error == ERESTART ||
                                    (error = nfs_reconnect(rep))) {
                                        nfs_sndunlock(&rep->r_nmp->nm_flag);
@@ -650,6 +647,7 @@ errout:
  * with outstanding requests using the xid, until ours is found.
  */
 /* ARGSUSED */
  * with outstanding requests using the xid, until ours is found.
  */
 /* ARGSUSED */
+int
 nfs_reply(myrep)
        struct nfsreq *myrep;
 {
 nfs_reply(myrep)
        struct nfsreq *myrep;
 {
@@ -671,7 +669,8 @@ nfs_reply(myrep)
                 * Also necessary for connection based protocols to avoid
                 * race conditions during a reconnect.
                 */
                 * Also necessary for connection based protocols to avoid
                 * race conditions during a reconnect.
                 */
-               if (error = nfs_rcvlock(myrep))
+               error = nfs_rcvlock(myrep);
+               if (error)
                        return (error);
                /* Already received, bye bye */
                if (myrep->r_mrep != NULL) {
                        return (error);
                /* Already received, bye bye */
                if (myrep->r_mrep != NULL) {
@@ -815,6 +814,7 @@ nfsmout:
  *       by mrep or error
  * nb: always frees up mreq mbuf list
  */
  *       by mrep or error
  * nb: always frees up mreq mbuf list
  */
+int
 nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
        struct vnode *vp;
        struct mbuf *mrest;
 nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
        struct vnode *vp;
        struct mbuf *mrest;
@@ -831,15 +831,17 @@ nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
        register int i;
        struct nfsmount *nmp;
        struct mbuf *md, *mheadend;
        register int i;
        struct nfsmount *nmp;
        struct mbuf *md, *mheadend;
-       struct nfsreq *reph;
        struct nfsnode *np;
        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;
        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_long xid;
        u_quad_t frev;
        u_long 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);
 
        nmp = VFSTONFS(vp->v_mount);
        MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
@@ -859,19 +861,21 @@ nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
         * Get the RPC header with authorization.
         */
 kerbauth:
         * Get the RPC header with authorization.
         */
 kerbauth:
-       auth_str = (char *)0;
+       verf_str = auth_str = (char *)0;
        if (nmp->nm_flag & NFSMNT_KERB) {
        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);
                        }
                        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;
                }
        } else {
                auth_type = RPCAUTH_UNIX;
@@ -881,8 +885,8 @@ kerbauth:
                        nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
                        5 * NFSX_UNSIGNED;
        }
                        nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 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);
 
        if (auth_str)
                free(auth_str, M_TEMP);
 
@@ -988,12 +992,12 @@ tryagain:
        /*
         * break down the rpc header and check if ok
         */
        /*
         * break down the rpc header and check if ok
         */
-       nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+       nfsm_dissect(tl, u_long *, 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_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);
                                failed_auth++;
                                mheadend->m_next = (struct mbuf *)0;
                                m_freem(mrep);
@@ -1010,22 +1014,25 @@ tryagain:
        }
 
        /*
        }
 
        /*
-        * 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(long, *tl));
-               nfsm_adv(i);
-       }
+       verf_type = fxdr_unsigned(int, *tl++);
+       i = fxdr_unsigned(int, *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_long *, NFSX_UNSIGNED);
        /* 0 == ok */
        if (*tl == 0) {
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                if (*tl != 0) {
                        error = fxdr_unsigned(int, *tl);
        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
        /* 0 == ok */
        if (*tl == 0) {
                nfsm_dissect(tl, u_long *, 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)
                                error = 0;
                                waituntil = time.tv_sec + trylater_delay;
                                while (time.tv_sec < waituntil)
@@ -1043,6 +1050,13 @@ tryagain:
                         */
                        if (error == ESTALE)
                                cache_purge(vp);
                         */
                        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);
                        m_freem(rep->r_mreq);
                        free((caddr_t)rep, M_NFSREQ);
                        return (error);
@@ -1074,10 +1088,10 @@ tryagain:
                return (0);
        }
        m_freem(mrep);
                return (0);
        }
        m_freem(mrep);
-       m_freem(rep->r_mreq);
-       free((caddr_t)rep, M_NFSREQ);
        error = EPROTONOSUPPORT;
 nfsmout:
        error = EPROTONOSUPPORT;
 nfsmout:
+       m_freem(rep->r_mreq);
+       free((caddr_t)rep, M_NFSREQ);
        return (error);
 }
 
        return (error);
 }
 
@@ -1085,9 +1099,11 @@ nfsmout:
  * Generate the rpc reply header
  * siz arg. is used to decide if adding a cluster is worthwhile
  */
  * Generate the rpc reply header
  * siz arg. is used to decide if adding a cluster is worthwhile
  */
-nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+int
+nfs_rephead(siz, nd, slp, err, cache, frev, mrq, mbp, bposp)
        int siz;
        int siz;
-       struct nfsd *nd;
+       struct nfsrv_descript *nd;
+       struct nfssvc_sock *slp;
        int err;
        int cache;
        u_quad_t *frev;
        int err;
        int cache;
        u_quad_t *frev;
@@ -1112,47 +1128,98 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
        } else
                mreq->m_data += max_hdr;
        tl = mtod(mreq, u_long *);
        } else
                mreq->m_data += max_hdr;
        tl = mtod(mreq, u_long *);
-       mreq->m_len = 6*NFSX_UNSIGNED;
-       bpos = ((caddr_t)tl)+mreq->m_len;
-       *tl++ = nd->nd_retxid;
+       mreq->m_len = 6 * NFSX_UNSIGNED;
+       bpos = ((caddr_t)tl) + mreq->m_len;
+       *tl++ = txdr_unsigned(nd->nd_retxid);
        *tl++ = rpc_reply;
        *tl++ = rpc_reply;
-       if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+       if (err == ERPCMISMATCH || (err & NFSERR_AUTHERR)) {
                *tl++ = rpc_msgdenied;
                *tl++ = rpc_msgdenied;
-               if (err == NQNFS_AUTHERR) {
+               if (err & NFSERR_AUTHERR) {
                        *tl++ = rpc_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;
                        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;
                }
        } 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;
+                   NFSKERBKEYSCHED_T keys;     /* stores key schedule */
+
+                   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_long *, 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);
                switch (err) {
                case EPROGUNAVAIL:
                        *tl = txdr_unsigned(RPC_PROGUNAVAIL);
                        break;
                case EPROGMISMATCH:
                        *tl = txdr_unsigned(RPC_PROGMISMATCH);
-                       nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
-                       *tl++ = txdr_unsigned(2);
-                       *tl = txdr_unsigned(2); /* someday 3 */
+                       nfsm_build(tl, u_long *, 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;
                        break;
                case EPROCUNAVAIL:
                        *tl = txdr_unsigned(RPC_PROCUNAVAIL);
                        break;
+               case EBADRPC:
+                       *tl = txdr_unsigned(RPC_GARBAGE);
+                       break;
                default:
                        *tl = 0;
                default:
                        *tl = 0;
-                       if (err != VNOVAL) {
+                       if (err != NFSERR_RETVOID) {
                                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                                if (err)
                                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                                if (err)
-                                       *tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+                                   *tl = txdr_unsigned(nfsrv_errmap(nd, err));
                                else
                                else
-                                       *tl = 0;
+                                   *tl = 0;
                        }
                        break;
                };
                        }
                        break;
                };
@@ -1161,16 +1228,14 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
        /*
         * For nqnfs, piggyback lease as requested.
         */
        /*
         * For nqnfs, piggyback lease as requested.
         */
-       if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
-               if (nd->nd_nqlflag) {
-                       nfsm_build(tl, u_long *, 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_long *, 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 {
                        *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_long *, NFSX_UNSIGNED);
                        *tl = 0;
                }
                        nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                        *tl = 0;
                }
@@ -1178,7 +1243,7 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
        *mrq = mreq;
        *mbp = mb;
        *bposp = bpos;
        *mrq = mreq;
        *mbp = mb;
        *bposp = bpos;
-       if (err != 0 && err != VNOVAL)
+       if (err != 0 && err != NFSERR_RETVOID)
                nfsstats.srvrpc_errs++;
        return (0);
 }
                nfsstats.srvrpc_errs++;
        return (0);
 }
@@ -1191,15 +1256,17 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
  */
 void
 nfs_timer(arg)
  */
 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 nfsreq *rep;
        register struct mbuf *m;
        register struct socket *so;
        register struct nfsmount *nmp;
        register int timeo;
+       register struct nfssvc_sock *slp;
        static long lasttime = 0;
        int s, error;
        static long lasttime = 0;
        int s, error;
+       u_quad_t cur_usec;
 
        s = splnet();
        for (rep = nfs_reqq.tqh_first; rep != 0; rep = rep->r_chain.tqe_next) {
 
        s = splnet();
        for (rep = nfs_reqq.tqh_first; rep != 0; rep = rep->r_chain.tqe_next) {
@@ -1296,14 +1363,26 @@ nfs_timer(arg)
                lasttime = time.tv_sec;
                nqnfs_serverd();
        }
                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);
+       }
        splx(s);
        splx(s);
-       timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+       timeout(nfs_timer, (void *)0, nfs_ticks);
 }
 
 /*
  * Test for a termination condition pending on the process.
  * This is used for NFSMNT_INT mounts.
  */
 }
 
 /*
  * Test for a termination condition pending on the process.
  * This is used for NFSMNT_INT mounts.
  */
+int
 nfs_sigintr(nmp, rep, p)
        struct nfsmount *nmp;
        struct nfsreq *rep;
 nfs_sigintr(nmp, rep, p)
        struct nfsmount *nmp;
        struct nfsreq *rep;
@@ -1327,6 +1406,7 @@ nfs_sigintr(nmp, rep, p)
  * and also to avoid race conditions between the processes with nfs requests
  * in progress when a reconnect is necessary.
  */
  * and also to avoid race conditions between the processes with nfs requests
  * in progress when a reconnect is necessary.
  */
+int
 nfs_sndlock(flagp, rep)
        register int *flagp;
        struct nfsreq *rep;
 nfs_sndlock(flagp, rep)
        register int *flagp;
        struct nfsreq *rep;
@@ -1372,6 +1452,7 @@ nfs_sndunlock(flagp)
        }
 }
 
        }
 }
 
+int
 nfs_rcvlock(rep)
        register struct nfsreq *rep;
 {
 nfs_rcvlock(rep)
        register struct nfsreq *rep;
 {
@@ -1572,7 +1653,8 @@ nfsrv_rcv(so, arg, waitflag)
                /*
                 * Now try and parse record(s) out of the raw stream data.
                 */
                /*
                 * Now try and parse record(s) out of the raw stream data.
                 */
-               if (error = nfsrv_getstream(slp, waitflag)) {
+               error = nfsrv_getstream(slp, waitflag);
+               if (error) {
                        if (error == EPERM)
                                slp->ns_flag |= SLP_DISCONN;
                        else
                        if (error == EPERM)
                                slp->ns_flag |= SLP_DISCONN;
                        else
@@ -1622,14 +1704,15 @@ dorecs:
  * stream socket. The "waitflag" argument indicates whether or not it
  * can sleep.
  */
  * stream socket. The "waitflag" argument indicates whether or not it
  * can sleep.
  */
+int
 nfsrv_getstream(slp, waitflag)
        register struct nfssvc_sock *slp;
        int waitflag;
 {
 nfsrv_getstream(slp, waitflag)
        register struct nfssvc_sock *slp;
        int waitflag;
 {
-       register struct mbuf *m;
+       register struct mbuf *m, **mpp;
        register char *cp1, *cp2;
        register int len;
        register char *cp1, *cp2;
        register int len;
-       struct mbuf *om, *m2, *recm;
+       struct mbuf *om, *m2, *recm = 0;
        u_long recmark;
 
        if (slp->ns_flag & SLP_GETSTREAM)
        u_long recmark;
 
        if (slp->ns_flag & SLP_GETSTREAM)
@@ -1660,7 +1743,12 @@ nfsrv_getstream(slp, waitflag)
                        }
                }
                slp->ns_cc -= NFSX_UNSIGNED;
                        }
                }
                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);
                if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
                        slp->ns_flag &= ~SLP_GETSTREAM;
                        return (EPERM);
@@ -1714,45 +1802,67 @@ nfsrv_getstream(slp, waitflag)
                slp->ns_flag &= ~SLP_GETSTREAM;
                return (0);
            }
                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;
+           }
        }
 }
 
 /*
  * Parse an RPC header.
  */
        }
 }
 
 /*
  * Parse an RPC header.
  */
-nfsrv_dorec(slp, nd)
+int
+nfsrv_dorec(slp, nfsd, ndp)
        register struct nfssvc_sock *slp;
        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;
 
        int error;
 
+       *ndp = NULL;
        if ((slp->ns_flag & SLP_VALID) == 0 ||
            (m = slp->ns_rec) == (struct mbuf *)0)
                return (ENOBUFS);
        if ((slp->ns_flag & SLP_VALID) == 0 ||
            (m = slp->ns_rec) == (struct mbuf *)0)
                return (ENOBUFS);
-       if (slp->ns_rec = m->m_nextpkt)
+       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) {
                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)) {
-               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);
        }
                return (error);
        }
+       *ndp = nd;
+       nfsd->nfsd_nd = nd;
        return (0);
 }
 
        return (0);
 }
 
@@ -1761,8 +1871,10 @@ nfsrv_dorec(slp, nd)
  * - verify it
  * - fill in the cred struct.
  */
  * - verify it
  * - fill in the cred struct.
  */
-nfs_getreq(nd, has_header)
-       register struct nfsd *nd;
+int
+nfs_getreq(nd, nfsd, has_header)
+       register struct nfsrv_descript *nd;
+       struct nfsd *nfsd;
        int has_header;
 {
        register int len, i;
        int has_header;
 {
        register int len, i;
@@ -1770,57 +1882,67 @@ nfs_getreq(nd, has_header)
        register long t1;
        struct uio uio;
        struct iovec iov;
        register long t1;
        struct uio uio;
        struct iovec iov;
-       caddr_t dpos, cp2;
+       caddr_t dpos, cp2, cp;
        u_long nfsvers, auth_type;
        u_long nfsvers, auth_type;
-       int error = 0, nqnfs = 0;
+       uid_t nickuid;
+       int error = 0, nqnfs = 0, ticklen;
        struct mbuf *mrep, *md;
        struct mbuf *mrep, *md;
+       register struct nfsuid *nuidp;
+       struct timeval tvin, tvout;
+       NFSKERBKEYSCHED_T keys; /* stores key schedule */
 
        mrep = nd->nd_mrep;
        md = nd->nd_md;
        dpos = nd->nd_dpos;
        if (has_header) {
 
        mrep = nd->nd_mrep;
        md = nd->nd_md;
        dpos = nd->nd_dpos;
        if (has_header) {
-               nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
-               nd->nd_retxid = *tl++;
+               nfsm_dissect(tl, u_long *, 10 * NFSX_UNSIGNED);
+               nd->nd_retxid = fxdr_unsigned(u_long, *tl++);
                if (*tl++ != rpc_call) {
                        m_freem(mrep);
                        return (EBADRPC);
                }
                if (*tl++ != rpc_call) {
                        m_freem(mrep);
                        return (EBADRPC);
                }
-       } else {
-               nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
-       }
+       } else
+               nfsm_dissect(tl, u_long *, 8 * NFSX_UNSIGNED);
        nd->nd_repstat = 0;
        nd->nd_repstat = 0;
+       nd->nd_flag = 0;
        if (*tl++ != rpc_vers) {
                nd->nd_repstat = ERPCMISMATCH;
                nd->nd_procnum = NFSPROC_NOOP;
                return (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 != nfs_prog) {
-               if (*tl == nqnfs_prog) {
+               if (*tl == nqnfs_prog)
                        nqnfs++;
                        nqnfs++;
-                       nfsvers = nqnfs_vers;
-               } else {
+               else {
                        nd->nd_repstat = EPROGUNAVAIL;
                        nd->nd_procnum = NFSPROC_NOOP;
                        return (0);
                }
        }
        tl++;
                        nd->nd_repstat = EPROGUNAVAIL;
                        nd->nd_procnum = NFSPROC_NOOP;
                        return (0);
                }
        }
        tl++;
-       if (*tl++ != nfsvers) {
+       nfsvers = fxdr_unsigned(u_long, *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_repstat = EPROGMISMATCH;
                nd->nd_procnum = NFSPROC_NOOP;
                return (0);
        }
+       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_long, *tl++);
        if (nd->nd_procnum == NFSPROC_NULL)
                return (0);
        if (nd->nd_procnum >= NFS_NPROCS ||
        nd->nd_procnum = fxdr_unsigned(u_long, *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);
        }
                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) {
        auth_type = *tl++;
        len = fxdr_unsigned(int, *tl++);
        if (len < 0 || len > RPCAUTH_MAXSIZ) {
@@ -1828,6 +1950,7 @@ nfs_getreq(nd, has_header)
                return (EBADRPC);
        }
 
                return (EBADRPC);
        }
 
+       nd->nd_flag &= ~ND_KERBAUTH;
        /*
         * Handle auth_unix or auth_kerb.
         */
        /*
         * Handle auth_unix or auth_kerb.
         */
@@ -1838,7 +1961,9 @@ nfs_getreq(nd, has_header)
                        return (EBADRPC);
                }
                nfsm_adv(nfsm_rndup(len));
                        return (EBADRPC);
                }
                nfsm_adv(nfsm_rndup(len));
-               nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+               nfsm_dissect(tl, u_long *, 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);
                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);
@@ -1846,45 +1971,124 @@ nfs_getreq(nd, has_header)
                        m_freem(mrep);
                        return (EBADRPC);
                }
                        m_freem(mrep);
                        return (EBADRPC);
                }
-               nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+               nfsm_dissect(tl, u_long *, (len + 2) * NFSX_UNSIGNED);
                for (i = 1; i <= len; i++)
                for (i = 1; 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 + 1);
                nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
-       } 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);
                }
                        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_long *, 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_long *)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_long *, 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_long *)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_long *, 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_long *, 3 * NFSX_UNSIGNED);
+                       tvin.tv_sec = *tl++;
+                       tvin.tv_usec = *tl;
+
+                       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);
+                       }
 
 
-       /*
-        * 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));
+                       /*
+                        * 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);
        }
 
        /*
        }
 
        /*
@@ -1892,16 +2096,14 @@ nfs_getreq(nd, has_header)
         */
        if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
         */
        if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
                nfsm_dissect(tl, u_long *, 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_long *, NFSX_UNSIGNED);
                        nd->nd_duration = fxdr_unsigned(int, *tl);
                } else
                        nd->nd_duration = NQ_MINLEASE;
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        nd->nd_duration = fxdr_unsigned(int, *tl);
                } else
                        nd->nd_duration = NQ_MINLEASE;
-       } else {
-               nd->nd_nqlflag = NQL_NOVAL;
+       } else
                nd->nd_duration = NQ_MINLEASE;
                nd->nd_duration = NQ_MINLEASE;
-       }
        nd->nd_md = md;
        nd->nd_dpos = dpos;
        return (0);
        nd->nd_md = md;
        nd->nd_dpos = dpos;
        return (0);
@@ -1922,13 +2124,13 @@ nfsrv_wakenfsd(slp)
 
        if ((slp->ns_flag & SLP_VALID) == 0)
                return;
 
        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++;
                                panic("nfsd wakeup");
                        slp->ns_sref++;
-                       nd->nd_slp = slp;
+                       nd->nfsd_slp = slp;
                        wakeup((caddr_t)nd);
                        return;
                }
                        wakeup((caddr_t)nd);
                        return;
                }
@@ -1937,6 +2139,7 @@ nfsrv_wakenfsd(slp)
        nfsd_head_flag |= NFSD_CHECKSLP;
 }
 
        nfsd_head_flag |= NFSD_CHECKSLP;
 }
 
+int
 nfs_msg(p, server, msg)
        struct proc *p;
        char *server, *msg;
 nfs_msg(p, server, msg)
        struct proc *p;
        char *server, *msg;
@@ -1949,4 +2152,5 @@ nfs_msg(p, server, msg)
                tpr = NULL;
        tprintf(tpr, "nfs server %s: %s\n", server, msg);
        tprintf_close(tpr);
                tpr = NULL;
        tprintf(tpr, "nfs server %s: %s\n", server, msg);
        tprintf_close(tpr);
+       return (0);
 }
 }
index 62e96e4..9dd4ed8 100644 (file)
@@ -7,7 +7,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_srvcache.c      8.2 (Berkeley) %G%
+ *     @(#)nfs_srvcache.c      8.3 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
 #endif
 #include <nfs/nfsm_subs.h>
 #include <nfs/rpcv2.h>
 #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.h>
 #include <nfs/nfsrvcache.h>
 #include <nfs/nqnfs.h>
 
+extern struct nfsstats nfsstats;
+extern int nfsv2_procid[NFS_NPROCS];
 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
 
 #define        NFSRCHASH(xid) \
 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
 
 #define        NFSRCHASH(xid) \
@@ -62,7 +64,6 @@ int nonidempotent[NFS_NPROCS] = {
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
-       FALSE,
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE,
@@ -71,6 +72,10 @@ int nonidempotent[NFS_NPROCS] = {
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        TRUE,
+       TRUE,
+       FALSE,
+       FALSE,
+       FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
@@ -81,7 +86,7 @@ int nonidempotent[NFS_NPROCS] = {
 };
 
 /* True iff the rpc reply is an nfs status ONLY! */
 };
 
 /* True iff the rpc reply is an nfs status ONLY! */
-static int repliesstatus[NFS_NPROCS] = {
+static int nfsv2_repstat[NFS_NPROCS] = {
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
        FALSE,
@@ -100,16 +105,12 @@ static int repliesstatus[NFS_NPROCS] = {
        TRUE,
        FALSE,
        FALSE,
        TRUE,
        FALSE,
        FALSE,
-       FALSE,
-       FALSE,
-       FALSE,
-       FALSE,
-       TRUE,
 };
 
 /*
  * Initialize the server request cache list
  */
 };
 
 /*
  * Initialize the server request cache list
  */
+void
 nfsrv_initcache()
 {
 
 nfsrv_initcache()
 {
 
@@ -131,9 +132,10 @@ nfsrv_initcache()
  *   return DOIT
  * Update/add new request at end of lru list
  */
  *   return DOIT
  * Update/add new request at end of lru list
  */
-nfsrv_getcache(nam, nd, repp)
-       struct mbuf *nam;
-       register struct nfsd *nd;
+int
+nfsrv_getcache(nd, slp, repp)
+       register struct nfsrv_descript *nd;
+       struct nfssvc_sock *slp;
        struct mbuf **repp;
 {
        register struct nfsrvcache *rp;
        struct mbuf **repp;
 {
        register struct nfsrvcache *rp;
@@ -142,13 +144,17 @@ nfsrv_getcache(nam, nd, repp)
        caddr_t bpos;
        int ret;
 
        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 &&
                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);
                        if ((rp->rc_flag & RC_LOCKED) != 0) {
                                rp->rc_flag |= RC_WANTED;
                                (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
@@ -167,7 +173,7 @@ loop:
                                ret = RC_DROPIT;
                        } else if (rp->rc_flag & RC_REPSTATUS) {
                                nfsstats.srvcache_nonidemdonehits++;
                                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) {
                                   0, (u_quad_t *)0, repp, &mb, &bpos);
                                ret = RC_REPLY;
                        } else if (rp->rc_flag & RC_REPMBUF) {
@@ -214,7 +220,7 @@ loop:
        TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
        rp->rc_state = RC_INPROG;
        rp->rc_xid = nd->nd_retxid;
        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;
        switch (saddr->sin_family) {
        case AF_INET:
                rp->rc_flag |= RC_INETADDR;
@@ -223,7 +229,7 @@ loop:
        case AF_ISO:
        default:
                rp->rc_flag |= RC_NAM;
        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;
                break;
        };
        rp->rc_proc = nd->nd_procnum;
@@ -240,21 +246,20 @@ loop:
  * Update a request cache entry after the rpc has been done
  */
 void
  * 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;
 
        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 &&
                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);
                        if ((rp->rc_flag & RC_LOCKED) != 0) {
                                rp->rc_flag |= RC_WANTED;
                                (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
@@ -267,7 +272,8 @@ loop:
                         * the reply for non-idempotent rpc's.
                         */
                        if (repvalid && nonidempotent[nd->nd_procnum]) {
                         * 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 {
                                        rp->rc_status = nd->nd_repstat;
                                        rp->rc_flag |= RC_REPSTATUS;
                                } else {
index 1950b3f..bcfec66 100644 (file)
@@ -10,6 +10,7 @@
  *     @(#)nfs_subs.c  8.6 (Berkeley) %G%
  */
 
  *     @(#)nfs_subs.c  8.6 (Berkeley) %G%
  */
 
+
 /*
  * These functions support the macros and help fiddle mbuf chains for
  * the nfs op functions. They do things like create the rpc header and
 /*
  * These functions support the macros and help fiddle mbuf chains for
  * the nfs op functions. They do things like create the rpc header and
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/malloc.h>
+#ifdef VFS_LKM
+#include <sys/sysent.h>
+#include <sys/syscall.h>
+#endif
+
+#include <vm/vm.h>
 
 #include <nfs/rpcv2.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>
 #include <nfs/nfsnode.h>
 #include <nfs/nfs.h>
 #include <nfs/xdr_subs.h>
 #include <netiso/iso.h>
 #endif
 
 #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
  */
 /*
  * 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_long nfs_xdrneg1;
 u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
 u_long nfs_xdrneg1;
 u_long 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;
        rpc_auth_kerb;
-u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+u_long nfs_prog, nqnfs_prog, nfs_true, nfs_false;
 
 /* And other global data */
 static u_long nfs_xid = 0;
 
 /* And other global data */
 static u_long nfs_xid = 0;
-enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+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_mount_type;
+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 struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
-extern int nqnfs_piggy[NFS_NPROCS];
 extern struct nfsrtt nfsrtt;
 extern time_t nqnfsstarttime;
 extern struct nfsrtt nfsrtt;
 extern time_t nqnfsstarttime;
-extern u_long nqnfs_prog, nqnfs_vers;
 extern int nqsrv_clockskew;
 extern int nqsrv_writeslack;
 extern int nqsrv_maxlease;
 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;
+
+#ifdef VFS_LKM
+struct getfh_args;
+extern int getfh(struct proc *, struct getfh_args *, int *);
+struct nfssvc_args;
+extern int nfssvc(struct proc *, struct nfssvc_args *, int *);
+#endif
 
 LIST_HEAD(nfsnodehashhead, nfsnode);
 
 LIST_HEAD(nfsnodehashhead, nfsnode);
-extern struct nfsnodehashhead *nfs_hash __P((nfsv2fh_t *));
 
 /*
  * Create the header for an rpc request packet
 
 /*
  * Create the header for an rpc request packet
@@ -126,14 +565,16 @@ nfsm_reqh(vp, procid, hsiz, bposp)
  * Returns the head of the mbuf list.
  */
 struct mbuf *
  * 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;
        register struct ucred *cr;
-       int nqnfs;
+       int nmflag;
        int procid;
        int auth_type;
        int auth_len;
        char *auth_str;
        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;
        struct mbuf *mrest;
        int mrest_len;
        struct mbuf **mbp;
@@ -147,15 +588,13 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
        int siz, grpsiz, authsiz;
 
        authsiz = nfsm_rndup(auth_len);
        int siz, grpsiz, authsiz;
 
        authsiz = nfsm_rndup(auth_len);
-       if (auth_type == RPCAUTH_NQNFS)
-               authsiz += 2 * NFSX_UNSIGNED;
        MGETHDR(mb, M_WAIT, MT_DATA);
        MGETHDR(mb, M_WAIT, MT_DATA);
-       if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+       if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) {
                MCLGET(mb, M_WAIT);
                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 {
        } else {
-               MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+               MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
        }
        mb->m_len = 0;
        mreq = mb;
        }
        mb->m_len = 0;
        mreq = mb;
@@ -164,20 +603,26 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
        /*
         * First the RPC header.
         */
        /*
         * First the RPC header.
         */
-       nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+       nfsm_build(tl, u_long *, 8 * NFSX_UNSIGNED);
        if (++nfs_xid == 0)
                nfs_xid++;
        *tl++ = *xidp = txdr_unsigned(nfs_xid);
        *tl++ = rpc_call;
        *tl++ = rpc_vers;
        if (++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_PROG);
-               *tl++ = txdr_unsigned(NQNFS_VER1);
+               *tl++ = txdr_unsigned(NQNFS_VER3);
        } else {
                *tl++ = txdr_unsigned(NFS_PROG);
        } 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.
 
        /*
         * And then the authorization cred.
@@ -196,10 +641,7 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
                for (i = 1; i <= grpsiz; i++)
                        *tl++ = txdr_unsigned(cr->cr_groups[i]);
                break;
                for (i = 1; i <= grpsiz; i++)
                        *tl++ = txdr_unsigned(cr->cr_groups[i]);
                break;
-       case RPCAUTH_NQNFS:
-               nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
-               *tl++ = txdr_unsigned(cr->cr_uid);
-               *tl = txdr_unsigned(auth_len);
+       case RPCAUTH_KERB4:
                siz = auth_len;
                while (siz > 0) {
                        if (M_TRAILINGSPACE(mb) == 0) {
                siz = auth_len;
                while (siz > 0) {
                        if (M_TRAILINGSPACE(mb) == 0) {
@@ -225,11 +667,43 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
                }
                break;
        };
                }
                break;
        };
-       nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
-       *tl++ = txdr_unsigned(RPCAUTH_NULL);
-       *tl = 0;
+
+       /*
+        * And the verifier...
+        */
+       nfsm_build(tl, u_long *, 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;
        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);
        mreq->m_pkthdr.rcvif = (struct ifnet *)0;
        *mbp = mb;
        return (mreq);
@@ -238,6 +712,7 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
 /*
  * copies mbuf chain to the uio scatter/gather list
  */
 /*
  * copies mbuf chain to the uio scatter/gather list
  */
+int
 nfsm_mbuftouio(mrep, uiop, siz, dpos)
        struct mbuf **mrep;
        register struct uio *uiop;
 nfsm_mbuftouio(mrep, uiop, siz, dpos)
        struct mbuf **mrep;
        register struct uio *uiop;
@@ -312,6 +787,7 @@ nfsm_mbuftouio(mrep, uiop, siz, dpos)
 /*
  * copies a uio scatter/gather list to an mbuf chain...
  */
 /*
  * copies a uio scatter/gather list to an mbuf chain...
  */
+int
 nfsm_uiotombuf(uiop, mq, siz, bpos)
        register struct uio *uiop;
        struct mbuf **mq;
 nfsm_uiotombuf(uiop, mq, siz, bpos)
        register struct uio *uiop;
        struct mbuf **mq;
@@ -399,6 +875,7 @@ nfsm_uiotombuf(uiop, mq, siz, bpos)
  * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
  * cases. (The macros use the vars. dpos and dpos2)
  */
  * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
  * cases. (The macros use the vars. dpos and dpos2)
  */
+int
 nfsm_disct(mdp, dposp, siz, left, cp2)
        struct mbuf **mdp;
        caddr_t *dposp;
 nfsm_disct(mdp, dposp, siz, left, cp2)
        struct mbuf **mdp;
        caddr_t *dposp;
@@ -461,6 +938,7 @@ nfsm_disct(mdp, dposp, siz, left, cp2)
 /*
  * Advance the position in the mbuf chain.
  */
 /*
  * Advance the position in the mbuf chain.
  */
+int
 nfs_adv(mdp, dposp, offs, left)
        struct mbuf **mdp;
        caddr_t *dposp;
 nfs_adv(mdp, dposp, offs, left)
        struct mbuf **mdp;
        caddr_t *dposp;
@@ -487,13 +965,14 @@ nfs_adv(mdp, dposp, offs, left)
 /*
  * Copy a string into mbufs for the hard cases...
  */
 /*
  * Copy a string into mbufs for the hard cases...
  */
+int
 nfsm_strtmbuf(mb, bpos, cp, siz)
        struct mbuf **mb;
        char **bpos;
        char *cp;
        long siz;
 {
 nfsm_strtmbuf(mb, bpos, cp, siz)
        struct mbuf **mb;
        char **bpos;
        char *cp;
        long siz;
 {
-       register struct mbuf *m1, *m2;
+       register struct mbuf *m1 = 0, *m2;
        long left, xfer, len, tlen;
        u_long *tl;
        int putsize;
        long left, xfer, len, tlen;
        u_long *tl;
        int putsize;
@@ -552,10 +1031,32 @@ nfsm_strtmbuf(mb, bpos, cp, siz)
 /*
  * Called once to initialize data structures...
  */
 /*
  * Called once to initialize data structures...
  */
-nfs_init()
+int
+nfs_init(vfsp)
+       struct vfsconf *vfsp;
 {
        register int i;
 
 {
        register int i;
 
+       /*
+        * 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");
+       }
+       nfs_mount_type = vfsp->vfc_typenum;
        nfsrtt.pos = 0;
        rpc_vers = txdr_unsigned(RPC_VER2);
        rpc_call = txdr_unsigned(RPC_CALL);
        nfsrtt.pos = 0;
        rpc_vers = txdr_unsigned(RPC_VER2);
        rpc_call = txdr_unsigned(RPC_CALL);
@@ -564,21 +1065,20 @@ nfs_init()
        rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
        rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
        rpc_autherr = txdr_unsigned(RPC_AUTHERR);
        rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
        rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
        rpc_autherr = txdr_unsigned(RPC_AUTHERR);
-       rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
        rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
        rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
-       rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
-       nfs_vers = txdr_unsigned(NFS_VER2);
+       rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
        nfs_prog = txdr_unsigned(NFS_PROG);
        nfs_prog = txdr_unsigned(NFS_PROG);
+       nqnfs_prog = txdr_unsigned(NQNFS_PROG);
        nfs_true = txdr_unsigned(TRUE);
        nfs_false = txdr_unsigned(FALSE);
        nfs_true = txdr_unsigned(TRUE);
        nfs_false = txdr_unsigned(FALSE);
-       /* Loop thru nfs procids */
-       for (i = 0; i < NFS_NPROCS; i++)
-               nfs_procids[i] = txdr_unsigned(i);
+       nfs_xdrneg1 = txdr_unsigned(-1);
+       nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
+       if (nfs_ticks < 1)
+               nfs_ticks = 1;
        /* Ensure async daemons disabled */
        for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
                nfs_iodwant[i] = (struct proc *)0;
        TAILQ_INIT(&nfs_bufq);
        /* Ensure async daemons disabled */
        for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
                nfs_iodwant[i] = (struct proc *)0;
        TAILQ_INIT(&nfs_bufq);
-       nfs_xdrneg1 = txdr_unsigned(-1);
        nfs_nhinit();                   /* Init the nfsnode table */
        nfsrv_init(0);                  /* Init server data structures */
        nfsrv_initcache();              /* Init the server request cache */
        nfs_nhinit();                   /* Init the nfsnode table */
        nfsrv_init(0);                  /* Init server data structures */
        nfsrv_initcache();              /* Init the server request cache */
@@ -590,8 +1090,6 @@ nfs_init()
                nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
                        + nqsrv_clockskew + nqsrv_writeslack;
                NQLOADNOVRAM(nqnfsstarttime);
                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);
        }
                CIRCLEQ_INIT(&nqtimerhead);
                nqfhhashtbl = hashinit(NQLCHSZ, M_NQLEASE, &nqfhhash);
        }
@@ -600,7 +1098,8 @@ nfs_init()
         * Initialize reply list and start timer
         */
        TAILQ_INIT(&nfs_reqq);
         * Initialize reply list and start timer
         */
        TAILQ_INIT(&nfs_reqq);
-       nfs_timer();
+       nfs_timer(0);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -617,6 +1116,7 @@ nfs_init()
  * Iff vap not NULL
  *    copy the attributes to *vaper
  */
  * Iff vap not NULL
  *    copy the attributes to *vaper
  */
+int
 nfs_loadattrcache(vpp, mdp, dposp, vaper)
        struct vnode **vpp;
        struct mbuf **mdp;
 nfs_loadattrcache(vpp, mdp, dposp, vaper)
        struct vnode **vpp;
        struct mbuf **mdp;
@@ -625,38 +1125,47 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
 {
        register struct vnode *vp = *vpp;
        register struct vattr *vap;
 {
        register struct vnode *vp = *vpp;
        register struct vattr *vap;
-       register struct nfsv2_fattr *fp;
+       register struct nfs_fattr *fp;
        extern int (**spec_nfsv2nodeop_p)();
        register struct nfsnode *np;
        register struct nfsnodehashhead *nhpp;
        register long t1;
        extern int (**spec_nfsv2nodeop_p)();
        register struct nfsnode *np;
        register struct nfsnodehashhead *nhpp;
        register long t1;
-       caddr_t dpos, cp2;
-       int error = 0, isnq;
+       caddr_t cp2;
+       int error = 0, rdev;
        struct mbuf *md;
        enum vtype vtyp;
        u_short vmode;
        struct mbuf *md;
        enum vtype vtyp;
        u_short vmode;
-       long rdev;
        struct timespec mtime;
        struct vnode *nvp;
        struct timespec mtime;
        struct vnode *nvp;
+       quad_t tval;
+       int v3 = NFS_ISV3(vp);
 
        md = *mdp;
 
        md = *mdp;
-       dpos = *dposp;
-       t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
-       isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
-       if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+       t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
+       if (error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2))
                return (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(long, 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 {
        } else {
-               rdev = fxdr_unsigned(long, 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(long, 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 
        /*
         * 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 
@@ -666,21 +1175,15 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
         */
        np = VTONFS(vp);
        if (vp->v_type == VNON) {
         */
        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) {
                if (vp->v_type == VFIFO) {
-#ifdef FIFO
                        extern int (**fifo_nfsv2nodeop_p)();
                        vp->v_op = fifo_nfsv2nodeop_p;
                        extern int (**fifo_nfsv2nodeop_p)();
                        vp->v_op = fifo_nfsv2nodeop_p;
-#else
-                       return (EOPNOTSUPP);
-#endif /* FIFO */
                }
                if (vp->v_type == VCHR || vp->v_type == VBLK) {
                        vp->v_op = spec_nfsv2nodeop_p;
                }
                if (vp->v_type == VCHR || vp->v_type == VBLK) {
                        vp->v_op = spec_nfsv2nodeop_p;
-                       if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+                       nvp = checkalias(vp, (dev_t)rdev, vp->v_mount);
+                       if (nvp) {
                                /*
                                 * Discard unneeded vnode, but save its nfsnode.
                                 */
                                /*
                                 * Discard unneeded vnode, but save its nfsnode.
                                 */
@@ -694,7 +1197,7 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
                                 * Reinitialize aliased node.
                                 */
                                np->n_vnode = nvp;
                                 * Reinitialize aliased node.
                                 */
                                np->n_vnode = nvp;
-                               nhpp = nfs_hash(&np->n_fh);
+                               nhpp = NFSNOHASH(nfs_hash(np->n_fhp, np->n_fhsize));
                                LIST_INSERT_HEAD(nhpp, np, n_hash);
                                *vpp = vp = nvp;
                        }
                                LIST_INSERT_HEAD(nhpp, np, n_hash);
                                *vpp = vp = nvp;
                        }
@@ -704,32 +1207,34 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
        vap = &np->n_vattr;
        vap->va_type = vtyp;
        vap->va_mode = (vmode & 07777);
        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];
        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(long, fp->fa_nqblocksize);
-               fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
-               vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
-               fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
-               vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
-               fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
-               vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
-               fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+       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(int, 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 {
        } else {
-               vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
-               vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
-               vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
-               vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
-               fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+               vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+               vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+               vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+               vap->va_size = fxdr_unsigned(u_long, fp->fa2_size);
+               vap->va_blocksize = fxdr_unsigned(long, fp->fa2_blocksize);
+               vap->va_bytes = fxdr_unsigned(long, fp->fa2_blocks) * NFS_FABLKSIZE;
+               vap->va_fileid = fxdr_unsigned(long, fp->fa2_fileid);
+               fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
                vap->va_flags = 0;
                vap->va_flags = 0;
-               vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+               vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa2_ctime.nfsv2_sec);
                vap->va_ctime.ts_nsec = 0;
                vap->va_ctime.ts_nsec = 0;
-               vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+               vap->va_gen = fxdr_unsigned(u_long, fp->fa2_ctime.nfsv2_usec);
                vap->va_filerev = 0;
        }
        if (vap->va_size != np->n_size) {
                vap->va_filerev = 0;
        }
        if (vap->va_size != np->n_size) {
@@ -746,26 +1251,13 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
                        np->n_size = vap->va_size;
        }
        np->n_attrstamp = time.tv_sec;
                        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));
        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 & NCHG) {
-                       if (np->n_flag & NACC) {
-                               vaper->va_atime.ts_sec = np->n_atim.tv_sec;
-                               vaper->va_atime.ts_nsec =
-                                   np->n_atim.tv_usec * 1000;
-                       }
-                       if (np->n_flag & NUPD) {
-                               vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
-                               vaper->va_mtime.ts_nsec =
-                                   np->n_mtim.tv_usec * 1000;
-                       }
+                       if (np->n_flag & NACC)
+                               vaper->va_atime = np->n_atim;
+                       if (np->n_flag & NUPD)
+                               vaper->va_mtime = np->n_mtim;
                }
        }
        return (0);
                }
        }
        return (0);
@@ -776,6 +1268,7 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper)
  * If the cache is valid, copy contents to *vap and return 0
  * otherwise return an error
  */
  * If the cache is valid, copy contents to *vap and return 0
  * otherwise return an error
  */
+int
 nfs_getattrcache(vp, vaper)
        register struct vnode *vp;
        struct vattr *vaper;
 nfs_getattrcache(vp, vaper)
        register struct vnode *vp;
        struct vattr *vaper;
@@ -783,12 +1276,7 @@ nfs_getattrcache(vp, vaper)
        register struct nfsnode *np = VTONFS(vp);
        register struct vattr *vap;
 
        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);
        }
                nfsstats.attrcache_misses++;
                return (ENOENT);
        }
@@ -808,23 +1296,11 @@ nfs_getattrcache(vp, vaper)
                        np->n_size = vap->va_size;
        }
        bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
                        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 & NCHG) {
-               if (np->n_flag & NACC) {
-                       vaper->va_atime.ts_sec = np->n_atim.tv_sec;
-                       vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
-               }
-               if (np->n_flag & NUPD) {
-                       vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
-                       vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
-               }
+               if (np->n_flag & NACC)
+                       vaper->va_atime = np->n_atim;
+               if (np->n_flag & NUPD)
+                       vaper->va_mtime = np->n_mtim;
        }
        return (0);
 }
        }
        return (0);
 }
@@ -832,7 +1308,8 @@ nfs_getattrcache(vp, vaper)
 /*
  * Set up nameidata for a lookup() call and do it
  */
 /*
  * Set up nameidata for a lookup() call and do it
  */
-nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+int
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
        register struct nameidata *ndp;
        fhandle_t *fhp;
        int len;
        register struct nameidata *ndp;
        fhandle_t *fhp;
        int len;
@@ -840,7 +1317,9 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
        struct mbuf *nam;
        struct mbuf **mdp;
        caddr_t *dposp;
        struct mbuf *nam;
        struct mbuf **mdp;
        caddr_t *dposp;
+       struct vnode **retdirp;
        struct proc *p;
        struct proc *p;
+       int kerbflag;
 {
        register int i, rem;
        register struct mbuf *md;
 {
        register int i, rem;
        register struct mbuf *md;
@@ -849,6 +1328,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
        int error, rdonly;
        struct componentname *cnp = &ndp->ni_cnd;
 
        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
        MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
        /*
         * Copy the name from the mbuf list to ndp->ni_pnbuf
@@ -870,7 +1350,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
                        rem = md->m_len;
                }
                if (*fromcp == '\0' || *fromcp == '/') {
                        rem = md->m_len;
                }
                if (*fromcp == '\0' || *fromcp == '/') {
-                       error = EINVAL;
+                       error = EACCES;
                        goto out;
                }
                cnp->cn_hash += (unsigned char)*fromcp;
                        goto out;
                }
                cnp->cn_hash += (unsigned char)*fromcp;
@@ -893,13 +1373,15 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
         * Extract and set starting directory.
         */
        if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
         * Extract and set starting directory.
         */
        if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
-           nam, &rdonly))
+           nam, &rdonly, kerbflag))
                goto out;
        if (dp->v_type != VDIR) {
                vrele(dp);
                error = ENOTDIR;
                goto out;
        }
                goto out;
        if (dp->v_type != VDIR) {
                vrele(dp);
                error = ENOTDIR;
                goto out;
        }
+       VREF(dp);
+       *retdirp = dp;
        ndp->ni_startdir = dp;
        if (rdonly)
                cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
        ndp->ni_startdir = dp;
        if (rdonly)
                cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
@@ -994,10 +1476,113 @@ nfsm_adj(mp, len, nul)
                }
                count -= m->m_len;
        }
                }
                count -= m->m_len;
        }
-       while (m = m->m_next)
+       for (m = m->m_next;m;m = m->m_next)
                m->m_len = 0;
 }
 
                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_long *tl;
+
+       if (before_ret) {
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+               *tl = nfs_false;
+       } else {
+               nfsm_build(tl, u_long *, 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_long *tl;
+       register struct nfs_fattr *fp;
+
+       if (after_ret) {
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+               *tl = nfs_false;
+       } else {
+               nfsm_build(tl, u_long *, 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)
 /*
  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
  *     - look up fsid in mount list (if not found ret error)
@@ -1005,7 +1590,8 @@ nfsm_adj(mp, len, nul)
  *     - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
  *     - if not lockflag unlock it with VOP_UNLOCK()
  */
  *     - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
  *     - if not lockflag unlock it with VOP_UNLOCK()
  */
-nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+int
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
        fhandle_t *fhp;
        int lockflag;
        struct vnode **vpp;
        fhandle_t *fhp;
        int lockflag;
        struct vnode **vpp;
@@ -1013,6 +1599,7 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
        struct nfssvc_sock *slp;
        struct mbuf *nam;
        int *rdonlyp;
        struct nfssvc_sock *slp;
        struct mbuf *nam;
        int *rdonlyp;
+       int kerbflag;
 {
        register struct mount *mp;
        register struct nfsuid *uidp;
 {
        register struct mount *mp;
        register struct nfsuid *uidp;
@@ -1021,27 +1608,23 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
        int error, exflags;
 
        *vpp = (struct vnode *)0;
        int error, exflags;
 
        *vpp = (struct vnode *)0;
-       if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+       mp = vfs_getvfs(&fhp->fh_fsid);
+       if (!mp)
                return (ESTALE);
                return (ESTALE);
-       if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+       error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon);
+       if (error)
                return (error);
        /*
         * Check/setup credentials.
         */
        if (exflags & MNT_EXKERB) {
                return (error);
        /*
         * 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);
                        vput(*vpp);
-                       return (NQNFS_AUTHERR);
+                       return (NFSERR_AUTHERR | AUTH_TOOWEAK);
                }
                }
-               cred->cr_uid = uidp->nu_cr.cr_uid;
-               for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
-                       cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
-               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;
                for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
        } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
                cred->cr_uid = credanon->cr_uid;
                for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
@@ -1064,6 +1647,7 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
  * The AF_INET family is handled as a special case so that address mbufs
  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
  */
  * The AF_INET family is handled as a special case so that address mbufs
  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
  */
+int
 netaddr_match(family, haddr, nam)
        int family;
        union nethostaddr *haddr;
 netaddr_match(family, haddr, nam)
        int family;
        union nethostaddr *haddr;
@@ -1098,3 +1682,142 @@ netaddr_match(family, haddr, nam)
        };
        return (0);
 }
        };
        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);
+}
index 3bb915e..d62a972 100644 (file)
@@ -7,7 +7,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_syscalls.c      8.4 (Berkeley) %G%
+ *     @(#)nfs_syscalls.c      8.5 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
 #ifdef ISO
 #include <netiso/iso.h>
 #endif
 #ifdef ISO
 #include <netiso/iso.h>
 #endif
+#include <nfs/xdr_subs.h>
 #include <nfs/rpcv2.h>
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfs.h>
+#include <nfs/nfsm_subs.h>
 #include <nfs/nfsrvcache.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nqnfs.h>
 #include <nfs/nfsrtt.h>
 
 #include <nfs/nfsrvcache.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nqnfs.h>
 #include <nfs/nfsrtt.h>
 
+void   nfsrv_zapsock   __P((struct nfssvc_sock *));
+
 /* Global defs. */
 /* Global defs. */
-extern u_long nfs_prog, nfs_vers;
-extern int (*nfsrv_procs[NFS_NPROCS])();
+extern int (*nfsrv3_procs[NFS_NPROCS])();
 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 extern int nfs_numasync;
 extern time_t nqnfsstarttime;
 extern int nqsrv_writeslack;
 extern int nfsrtton;
 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;
 static int nfs_numnfsd = 0;
 struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
 int nuidhash_max = NFS_MAXUIDHASH;
 static int nfs_numnfsd = 0;
@@ -60,7 +65,7 @@ static int modify_flag = 0;
 static struct nfsdrt nfsdrt;
 void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
 static void nfsd_rt();
 static struct nfsdrt nfsdrt;
 void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
 static void nfsd_rt();
-void nfsrv_slpderef(), nfsrv_init();
+void nfsrv_slpderef();
 
 #define        TRUE    1
 #define        FALSE   0
 
 #define        TRUE    1
 #define        FALSE   0
@@ -78,6 +83,7 @@ struct getfh_args {
        char    *fname;
        fhandle_t *fhp;
 };
        char    *fname;
        fhandle_t *fhp;
 };
+int
 getfh(p, uap, retval)
        struct proc *p;
        register struct getfh_args *uap;
 getfh(p, uap, retval)
        struct proc *p;
        register struct getfh_args *uap;
@@ -91,10 +97,12 @@ getfh(p, uap, retval)
        /*
         * Must be super user
         */
        /*
         * Must be super user
         */
-       if (error = suser(p->p_ucred, &p->p_acflag))
+       error = suser(p->p_ucred, &p->p_acflag);
+       if(error)
                return (error);
        NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
                return (error);
        NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
-       if (error = namei(&nd))
+       error = namei(&nd);
+       if (error)
                return (error);
        vp = nd.ni_vp;
        bzero((caddr_t)&fh, sizeof(fh));
                return (error);
        vp = nd.ni_vp;
        bzero((caddr_t)&fh, sizeof(fh));
@@ -121,6 +129,7 @@ struct nfssvc_args {
        int flag;
        caddr_t argp;
 };
        int flag;
        caddr_t argp;
 };
+int
 nfssvc(p, uap, retval)
        struct proc *p;
        register struct nfssvc_args *uap;
 nfssvc(p, uap, retval)
        struct proc *p;
        register struct nfssvc_args *uap;
@@ -134,27 +143,30 @@ nfssvc(p, uap, retval)
        struct nfsd_cargs ncd;
        struct nfsd *nfsd;
        struct nfssvc_sock *slp;
        struct nfsd_cargs ncd;
        struct nfsd *nfsd;
        struct nfssvc_sock *slp;
-       struct nfsuid *nuidp, **nuh;
+       struct nfsuid *nuidp;
        struct nfsmount *nmp;
        int error;
 
        /*
         * Must be super user
         */
        struct nfsmount *nmp;
        int error;
 
        /*
         * Must be super user
         */
-       if (error = suser(p->p_ucred, &p->p_acflag))
+       error = suser(p->p_ucred, &p->p_acflag);
+       if(error)
                return (error);
        while (nfssvc_sockhead_flag & SLP_INIT) {
                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 (uap->flag & NFSSVC_BIOD)
                error = nfssvc_iod(p);
        else if (uap->flag & NFSSVC_MNTD) {
                (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
        }
        if (uap->flag & NFSSVC_BIOD)
                error = nfssvc_iod(p);
        else if (uap->flag & NFSSVC_MNTD) {
-               if (error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)))
+               error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd));
+               if (error)
                        return (error);
                NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
                        ncd.ncd_dirp, p);
                        return (error);
                NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
                        ncd.ncd_dirp, p);
-               if (error = namei(&nd))
+               error = namei(&nd);
+               if (error)
                        return (error);
                if ((nd.ni_vp->v_flag & VROOT) == 0)
                        error = EINVAL;
                        return (error);
                if ((nd.ni_vp->v_flag & VROOT) == 0)
                        error = EINVAL;
@@ -169,37 +181,48 @@ nfssvc(p, uap, retval)
                error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
                        uap->argp, p);
        } else if (uap->flag & NFSSVC_ADDSOCK) {
                error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
                        uap->argp, p);
        } else if (uap->flag & NFSSVC_ADDSOCK) {
-               if (error = copyin(uap->argp, (caddr_t)&nfsdarg,
-                   sizeof(nfsdarg)))
+               error = copyin(uap->argp, (caddr_t)&nfsdarg, sizeof(nfsdarg));
+               if (error)
                        return (error);
                        return (error);
-               if (error = getsock(p->p_fd, nfsdarg.sock, &fp))
+               error = getsock(p->p_fd, nfsdarg.sock, &fp);
+               if (error)
                        return (error);
                /*
                 * Get the client address for connected sockets.
                 */
                if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
                        nam = (struct mbuf *)0;
                        return (error);
                /*
                 * Get the client address for connected sockets.
                 */
                if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
                        nam = (struct mbuf *)0;
-               else if (error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
-                       MT_SONAME))
-                       return (error);
+               else {
+                       error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
+                               MT_SONAME);
+                       if (error)
+                               return (error);
+               }
                error = nfssvc_addsock(fp, nam);
        } else {
                error = nfssvc_addsock(fp, nam);
        } else {
-               if (error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)))
+               error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd));
+               if (error)
                        return (error);
                        return (error);
-               if ((uap->flag & NFSSVC_AUTHIN) && (nfsd = nsd->nsd_nfsd) &&
-                       (nfsd->nd_slp->ns_flag & SLP_VALID)) {
-                       slp = nfsd->nd_slp;
+               if ((uap->flag & NFSSVC_AUTHIN) && ((nfsd = nsd->nsd_nfsd)) &&
+                       (nfsd->nfsd_slp->ns_flag & SLP_VALID)) {
+                       slp = nfsd->nfsd_slp;
 
                        /*
                         * First check to see if another nfsd has already
                         * added this credential.
                         */
 
                        /*
                         * 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) {
                            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;
                        }
                                        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.
                             */
                            /*
                             * Nope, so we will.
                             */
@@ -219,21 +242,53 @@ nfssvc(p, uap, retval)
                                    LIST_REMOVE(nuidp, nu_hash);
                                    TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp,
                                        nu_lru);
                                    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 = 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_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,
                                TAILQ_INSERT_TAIL(&slp->ns_uidlruhead, nuidp,
-                                   nu_lru);
+                                       nu_lru);
                                LIST_INSERT_HEAD(NUIDHASH(slp, nsd->nsd_uid),
                                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 ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
                            }
                        }
                }
                if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
-                       nfsd->nd_flag |= NFSD_AUTHFAIL;
+                       nfsd->nfsd_flag |= NFSD_AUTHFAIL;
                error = nfssvc_nfsd(nsd, uap->argp, p);
        }
        if (error == EINTR || error == ERESTART)
                error = nfssvc_nfsd(nsd, uap->argp, p);
        }
        if (error == EINTR || error == ERESTART)
@@ -244,6 +299,7 @@ nfssvc(p, uap, retval)
 /*
  * Adds a socket to the list for servicing by nfsds.
  */
 /*
  * Adds a socket to the list for servicing by nfsds.
  */
+int
 nfssvc_addsock(fp, mynam)
        struct file *fp;
        struct mbuf *mynam;
 nfssvc_addsock(fp, mynam)
        struct file *fp;
        struct mbuf *mynam;
@@ -279,7 +335,8 @@ nfssvc_addsock(fp, mynam)
                siz = NFS_MAXPACKET + sizeof (u_long);
        else
                siz = NFS_MAXPACKET;
                siz = NFS_MAXPACKET + sizeof (u_long);
        else
                siz = NFS_MAXPACKET;
-       if (error = soreserve(so, siz, siz)) {
+       error = soreserve(so, siz, siz); 
+       if (error) {
                m_freem(mynam);
                return (error);
        }
                m_freem(mynam);
                return (error);
        }
@@ -313,8 +370,6 @@ nfssvc_addsock(fp, mynam)
                        malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
                printf("Alloc nfssvc_sock 0x%x\n", slp);
                bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
                        malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
                printf("Alloc nfssvc_sock 0x%x\n", slp);
                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);
        }
                TAILQ_INIT(&slp->ns_uidlruhead);
                TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain);
        }
@@ -335,49 +390,53 @@ nfssvc_addsock(fp, mynam)
  * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
  * until it is killed by a signal.
  */
  * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
  * until it is killed by a signal.
  */
+int
 nfssvc_nfsd(nsd, argp, p)
        struct nfsd_srvargs *nsd;
        caddr_t argp;
        struct proc *p;
 {
 nfssvc_nfsd(nsd, argp, p)
        struct nfsd_srvargs *nsd;
        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;
        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 nfsd *nfsd = nsd->nsd_nfsd;
+       struct nfsrv_descript *nd = NULL;
+       struct mbuf *mreq;
        struct nfsuid *uidp;
        struct nfsuid *uidp;
-       int error, cacherep, s;
-       int sotype;
+       int error = 0, cacherep, s, sotype, writes_todo;
+       u_quad_t cur_usec;
 
 
+#ifndef nolint
+       cacherep = RC_DOIT;
+       writes_todo = 0;
+#endif
        s = splnet();
        s = splnet();
-       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);
                        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 (;;) {
                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) {
                            (nfsd_head_flag & NFSD_CHECKSLP) == 0) {
-                               nd->nd_flag |= NFSD_WAITING;
+                               nfsd->nfsd_flag |= NFSD_WAITING;
                                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;
                        }
                                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) {
                            (nfsd_head_flag & NFSD_CHECKSLP) != 0) {
                                for (slp = nfssvc_sockhead.tqh_first; slp != 0;
                                    slp = slp->ns_chain.tqe_next) {
@@ -385,14 +444,14 @@ nfssvc_nfsd(nsd, argp, p)
                                        == (SLP_VALID | SLP_DOREC)) {
                                            slp->ns_flag &= ~SLP_DOREC;
                                            slp->ns_sref++;
                                        == (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;
                        }
                                            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)
                                continue;
                        if (slp->ns_flag & SLP_VALID) {
                                if (slp->ns_flag & SLP_DISCONN)
@@ -405,85 +464,78 @@ nfssvc_nfsd(nsd, argp, p)
                                                M_WAIT);
                                        nfs_sndunlock(&slp->ns_solock);
                                }
                                                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;
                        }
                } else {
                        error = 0;
-                       slp = nd->nd_slp;
+                       slp = nfsd->nfsd_slp;
                }
                if (error || (slp->ns_flag & SLP_VALID) == 0) {
                }
                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;
                        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;
                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) {
-                       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 (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;
                                cacherep = RC_DROPIT;
                        else if (nd->nd_procnum != NFSPROC_WRITE) {
                                nd->nd_procnum = NFSPROC_NOOP;
@@ -491,36 +543,42 @@ nfssvc_nfsd(nsd, argp, p)
                                cacherep = RC_DOIT;
                        } else
                                modify_flag = 1;
                                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_procnum = NFSPROC_NOOP;
-                       nd->nd_repstat = NQNFS_AUTHERR;
+                       nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
                        cacherep = RC_DOIT;
                        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 (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]++;
                                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;
                        nd->nd_mrep = (struct mbuf *)0;
-               case RC_REPLY:
+                   case RC_REPLY:
                        m = mreq;
                        siz = 0;
                        while (m) {
                        m = mreq;
                        siz = 0;
                        while (m) {
@@ -545,15 +603,15 @@ nfssvc_nfsd(nsd, argp, p)
                        if (solockp)
                                (void) nfs_sndlock(solockp, (struct nfsreq *)0);
                        if (slp->ns_flag & SLP_VALID)
                        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)
                        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)
                        if (nd->nd_mrep)
                                m_freem(nd->nd_mrep);
                        if (error == EPIPE)
@@ -561,22 +619,43 @@ nfssvc_nfsd(nsd, argp, p)
                        if (solockp)
                                nfs_sndunlock(solockp);
                        if (error == EINTR || error == ERESTART) {
                        if (solockp)
                                nfs_sndunlock(solockp);
                        if (error == EINTR || error == ERESTART) {
+                               free((caddr_t)nd, M_NFSRVDESC);
                                nfsrv_slpderef(slp);
                                s = splnet();
                                goto done;
                        }
                        break;
                                nfsrv_slpderef(slp);
                                s = splnet();
                                goto done;
                        }
                        break;
-               case RC_DROPIT:
+                   case RC_DROPIT:
                        if (nfsrtton)
                        if (nfsrtton)
-                               nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+                               nfsd_rt(sotype, nd, cacherep);
                        m_freem(nd->nd_mrep);
                        m_freem(nd->nd_mrep);
-                       m_freem(nam2);
+                       m_freem(nd->nd_nam2);
                        break;
                        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 = splnet();
                s = splnet();
-               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);
                }
 #ifdef DIAGNOSTIC
                        nfsrv_slpderef(slp);
                }
 #ifdef DIAGNOSTIC
@@ -587,9 +666,9 @@ nfssvc_nfsd(nsd, argp, p)
 #endif
        }
 done:
 #endif
        }
 done:
-       TAILQ_REMOVE(&nfsd_head, nd, nd_chain);
+       TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain);
        splx(s);
        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 */
        nsd->nsd_nfsd = (struct nfsd *)0;
        if (--nfs_numnfsd == 0)
                nfsrv_init(TRUE);       /* Reinitialize everything */
@@ -601,12 +680,14 @@ done:
  * They do read-ahead and write-behind operations on the block I/O cache.
  * Never returns unless it fails or gets killed.
  */
  * They do read-ahead and write-behind operations on the block I/O cache.
  * Never returns unless it fails or gets killed.
  */
+int
 nfssvc_iod(p)
        struct proc *p;
 {
 nfssvc_iod(p)
        struct proc *p;
 {
-       register struct buf *bp;
+       register struct buf *bp, *nbp;
        register int i, myiod;
        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
 
        /*
         * Assign my position or return error if too many already running
@@ -625,24 +706,52 @@ nfssvc_iod(p)
         * Just loop around doin our stuff until SIGKILL
         */
        for (;;) {
         * 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);
+           }
+           if (error) {
+               nfs_asyncdaemon[myiod] = 0;
+               nfs_numasync--;
+               return (error);
+           }
        }
 }
 
        }
 }
 
@@ -653,17 +762,20 @@ nfssvc_iod(p)
  * will stop using it and clear ns_flag at the end so that it will not be
  * reassigned during cleanup.
  */
  * will stop using it and clear ns_flag at the end so that it will not be
  * reassigned during cleanup.
  */
+void
 nfsrv_zapsock(slp)
        register struct nfssvc_sock *slp;
 {
        register struct nfsuid *nuidp, *nnuidp;
 nfsrv_zapsock(slp)
        register struct nfssvc_sock *slp;
 {
        register struct nfsuid *nuidp, *nnuidp;
-       register int i;
+       register struct nfsrv_descript *nwp, *nnwp;
        struct socket *so;
        struct file *fp;
        struct mbuf *m;
        struct socket *so;
        struct file *fp;
        struct mbuf *m;
+       int s;
 
        slp->ns_flag &= ~SLP_ALLFLAGS;
 
        slp->ns_flag &= ~SLP_ALLFLAGS;
-       if (fp = slp->ns_fp) {
+       fp = slp->ns_fp;
+       if (fp) {
                slp->ns_fp = (struct file *)0;
                so = slp->ns_so;
                so->so_upcall = NULL;
                slp->ns_fp = (struct file *)0;
                so = slp->ns_so;
                so->so_upcall = NULL;
@@ -678,8 +790,18 @@ nfsrv_zapsock(slp)
                        nnuidp = nuidp->nu_lru.tqe_next;
                        LIST_REMOVE(nuidp, nu_hash);
                        TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru);
                        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);
                }
                        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);
        }
 }
 
        }
 }
 
@@ -687,13 +809,16 @@ nfsrv_zapsock(slp)
  * 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.
  */
  * 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.
  */
-nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+int
+nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key)
        register struct nfsmount *nmp;
        struct nfsreq *rep;
        struct ucred *cred;
        register struct nfsmount *nmp;
        struct nfsreq *rep;
        struct ucred *cred;
-       int *auth_type;
        char **auth_str;
        int *auth_len;
        char **auth_str;
        int *auth_len;
+       char *verf_str;
+       int *verf_len;
+       NFSKERBKEY_T key;               /* return session key */
 {
        int error = 0;
 
 {
        int error = 0;
 
@@ -701,13 +826,17 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
                nmp->nm_flag |= NFSMNT_WANTAUTH;
                (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
                        "nfsauth1", 2 * hz);
                nmp->nm_flag |= NFSMNT_WANTAUTH;
                (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
                        "nfsauth1", 2 * hz);
-               if (error = nfs_sigintr(nmp, rep, rep->r_procp)) {
+               error = nfs_sigintr(nmp, rep, rep->r_procp);
+               if (error) {
                        nmp->nm_flag &= ~NFSMNT_WANTAUTH;
                        return (error);
                }
        }
        nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
        nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
                        nmp->nm_flag &= ~NFSMNT_WANTAUTH;
                        return (error);
                }
        }
        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);
 
        nmp->nm_authuid = cred->cr_uid;
        wakeup((caddr_t)&nmp->nm_authstr);
 
@@ -726,8 +855,9 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
        if (error)
                free((caddr_t)*auth_str, M_TEMP);
        else {
        if (error)
                free((caddr_t)*auth_str, M_TEMP);
        else {
-               *auth_type = nmp->nm_authtype;
                *auth_len = nmp->nm_authlen;
                *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;
        }
        nmp->nm_flag &= ~NFSMNT_HASAUTH;
        nmp->nm_flag |= NFSMNT_WAITAUTH;
@@ -738,6 +868,149 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
        return (error);
 }
 
        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_long *nickp, *verfp;
+       struct timeval ktvin, ktvout;
+       NFSKERBKEYSCHED_T keys; /* stores key schedule */
+
+#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_long *)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_long *)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_long *tl;
+       register long t1;
+       struct mbuf *md = *mdp;
+       struct timeval ktvin, ktvout;
+       u_long nick;
+       NFSKERBKEYSCHED_T keys;
+       char *dpos = *dposp, *cp2;
+       int deltasec, error = 0;
+
+       if (len == (3 * NFSX_UNSIGNED)) {
+               nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+               ktvin.tv_sec = *tl++;
+               ktvin.tv_usec = *tl++;
+               nick = fxdr_unsigned(u_long, *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);
+}
+
 /*
  * Derefence a server socket structure. If it has no more references and
  * is no longer valid, you can throw it away.
 /*
  * Derefence a server socket structure. If it has no more references and
  * is no longer valid, you can throw it away.
@@ -814,8 +1087,6 @@ nfsrv_init(terminating)
            malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        printf("Alloc nfs_udpsock 0x%x\n", nfs_udpsock);
        bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
            malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        printf("Alloc nfs_udpsock 0x%x\n", nfs_udpsock);
        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);
 
        TAILQ_INIT(&nfs_udpsock->ns_uidlruhead);
        TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain);
 
@@ -823,8 +1094,6 @@ nfsrv_init(terminating)
            malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        printf("Alloc nfs_cltpsock 0x%x\n", nfs_cltpsock);
        bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
            malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        printf("Alloc nfs_cltpsock 0x%x\n", nfs_cltpsock);
        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);
 }
        TAILQ_INIT(&nfs_cltpsock->ns_uidlruhead);
        TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain);
 }
@@ -833,11 +1102,9 @@ nfsrv_init(terminating)
  * Add entries to the server monitor log.
  */
 static void
  * 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;
        int sotype;
-       register struct nfsd *nd;
-       struct mbuf *nam;
+       register struct nfsrv_descript *nd;
        int cacherep;
 {
        register struct drt *rt;
        int cacherep;
 {
        register struct drt *rt;
@@ -851,15 +1118,17 @@ nfsd_rt(startp, sotype, nd, nam, cacherep)
                rt->flag = DRT_CACHEDROP;
        if (sotype == SOCK_STREAM)
                rt->flag |= DRT_TCP;
                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;
                rt->flag |= DRT_NQNFS;
+       else if (nd->nd_flag & ND_NFSV3)
+               rt->flag |= DRT_NFSV3;
        rt->proc = nd->nd_procnum;
        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
        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;
 }
        rt->tstamp = time;
        nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
 }
index cdfa99c..fb0db63 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * 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
  *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -22,6 +22,7 @@
 #include <sys/buf.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/buf.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
+#include <sys/socketvar.h>
 #include <sys/systm.h>
 
 #include <net/if.h>
 #include <sys/systm.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
 
 #include <nfs/rpcv2.h>
 #include <netinet/in.h>
 
 #include <nfs/rpcv2.h>
-#include <nfs/nfsv2.h>
+#include <nfs/nfsproto.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsnode.h>
-#include <nfs/nfsmount.h>
 #include <nfs/nfs.h>
 #include <nfs/nfs.h>
+#include <nfs/nfsmount.h>
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nfsdiskless.h>
 #include <nfs/nqnfs.h>
 
 #include <nfs/xdr_subs.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nfsdiskless.h>
 #include <nfs/nqnfs.h>
 
+struct nfsstats nfsstats;
+static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
+                     struct proc *);
+extern int nfs_ticks;
+
 /*
  * nfs vfs operations.
  */
 /*
  * nfs vfs operations.
  */
@@ -53,6 +59,7 @@ struct vfsops nfs_vfsops = {
        nfs_fhtovp,
        nfs_vptofh,
        nfs_init,
        nfs_fhtovp,
        nfs_vptofh,
        nfs_init,
+       nfs_sysctl
 };
 
 /*
 };
 
 /*
@@ -61,17 +68,15 @@ struct vfsops nfs_vfsops = {
  * to ensure that it is allocated to initialized data (.data not .bss).
  */
 struct nfs_diskless nfs_diskless = { 0 };
  * to ensure that it is allocated to initialized data (.data not .bss).
  */
 struct nfs_diskless nfs_diskless = { 0 };
+int nfs_diskless_valid = 0;
 
 
-extern u_long nfs_procids[NFS_NPROCS];
-extern u_long nfs_prog, nfs_vers;
 void nfs_disconnect __P((struct nfsmount *));
 void nfsargs_ntoh __P((struct nfs_args *));
 void nfs_disconnect __P((struct nfsmount *));
 void nfsargs_ntoh __P((struct nfs_args *));
+int nfs_fsinfo __P((struct nfsmount *, struct vnode *, struct ucred *, 
+       struct proc *));
 static struct mount *nfs_mountdiskless __P((char *, char *, int,
     struct sockaddr_in *, struct nfs_args *, register struct vnode **));
 
 static struct mount *nfs_mountdiskless __P((char *, char *, int,
     struct sockaddr_in *, struct nfs_args *, register struct vnode **));
 
-#define TRUE   1
-#define        FALSE   0
-
 /*
  * nfs statfs call
  */
 /*
  * nfs statfs call
  */
@@ -82,39 +87,55 @@ nfs_statfs(mp, sbp, p)
        struct proc *p;
 {
        register struct vnode *vp;
        struct proc *p;
 {
        register struct vnode *vp;
-       register struct nfsv2_statfs *sfp;
+       register struct nfs_statfs *sfp;
        register caddr_t cp;
        register caddr_t cp;
-       register long t1;
+       register u_long *tl;
+       register long t1, t2;
        caddr_t bpos, dpos, cp2;
        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 mbuf *mreq, *mrep, *md, *mb, *mb2;
-       struct nfsmount *nmp;
        struct ucred *cred;
        struct nfsnode *np;
        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))
+#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);
                return (error);
        vp = NFSTOV(np);
-       nfsstats.rpccnt[NFSPROC_STATFS]++;
        cred = crget();
        cred->cr_ngroups = 1;
        cred = crget();
        cred->cr_ngroups = 1;
-       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));
-       sbp->f_type = MOUNT_NFS;
-       sbp->f_flags = nmp->nm_flag;
-       sbp->f_iosize = NFS_MAXDGRAMDATA;
-       sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
-       sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
-       sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
-       sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
-       if (isnq) {
-               sbp->f_files = fxdr_unsigned(long, sfp->sf_files);
-               sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree);
+       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));
+       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(long, sfp->sf_tfiles.nfsuquad[1])
+                       & 0x7fffffff);
+               sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1])
+                       & 0x7fffffff);
        } else {
        } else {
+               sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
+               sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
+               sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
+               sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
                sbp->f_files = 0;
                sbp->f_ffree = 0;
        }
                sbp->f_files = 0;
                sbp->f_ffree = 0;
        }
@@ -128,6 +149,66 @@ nfs_statfs(mp, sbp, p)
        return (error);
 }
 
        return (error);
 }
 
+/*
+ * 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 long t1, t2;
+       register u_long *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_long, fsp->fs_wtpref);
+               if (pref < nmp->nm_wsize)
+                       nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
+                               ~(NFS_FABLKSIZE - 1);
+               max = fxdr_unsigned(u_long, 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_long, fsp->fs_rtpref);
+               if (pref < nmp->nm_rsize)
+                       nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
+                               ~(NFS_FABLKSIZE - 1);
+               max = fxdr_unsigned(u_long, 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_long, 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. This depends on the info in the
  * nfs_diskless structure that has been filled in properly by some primary
 /*
  * Mount a remote root fs via. nfs. This depends on the info in the
  * nfs_diskless structure that has been filled in properly by some primary
@@ -150,6 +231,8 @@ nfs_mountroot()
        struct vnode *vp;
        struct proc *p = curproc;               /* XXX */
        int error, i;
        struct vnode *vp;
        struct proc *p = curproc;               /* XXX */
        int error, i;
+       u_long l;
+       char buf[128];
 
        /*
         * XXX time must be non-zero when we init the interface or else
 
        /*
         * XXX time must be non-zero when we init the interface or else
@@ -158,6 +241,11 @@ nfs_mountroot()
        if (time.tv_sec == 0)
                time.tv_sec = 1;
 
        if (time.tv_sec == 0)
                time.tv_sec = 1;
 
+       /* 
+        * XXX splnet, so networks will receive...
+        */
+       splnet();
+
 #ifdef notyet
        /* Set up swap credentials. */
        proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
 #ifdef notyet
        /* Set up swap credentials. */
        proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
@@ -173,9 +261,26 @@ nfs_mountroot()
         * Do enough of ifconfig(8) so that the critical net interface can
         * talk to the server.
         */
         * Do enough of ifconfig(8) so that the critical net interface can
         * talk to the server.
         */
-       if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
-               panic("nfs_mountroot: socreate: %d", error);
-       if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p))
+       error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0);
+       if (error)
+               panic("nfs_mountroot: socreate(%04x): %d",
+                       nd->myif.ifra_addr.sa_family, error);
+
+       /*
+        * We might not have been told the right interface, so we pass
+        * over the first ten interfaces of the same kind, until we get
+        * one of them configured.
+        */
+
+       for (i = strlen(nd->myif.ifra_name) - 1;
+               nd->myif.ifra_name[i] >= '0' && 
+               nd->myif.ifra_name[i] <= '9';
+               nd->myif.ifra_name[i] ++) {
+               error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p);
+               if(!error) 
+                       break;
+       }
+       if (error)
                panic("nfs_mountroot: SIOCAIFADDR: %d", error);
        soclose(so);
 
                panic("nfs_mountroot: SIOCAIFADDR: %d", error);
        soclose(so);
 
@@ -189,23 +294,35 @@ nfs_mountroot()
                sin = mask;
                sin.sin_family = AF_INET;
                sin.sin_len = sizeof(sin);
                sin = mask;
                sin.sin_family = AF_INET;
                sin.sin_len = sizeof(sin);
-               if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
+               error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
                    (struct sockaddr *)&nd->mygateway,
                    (struct sockaddr *)&mask,
                    (struct sockaddr *)&nd->mygateway,
                    (struct sockaddr *)&mask,
-                   RTF_UP | RTF_GATEWAY, (struct rtentry **)0))
+                   RTF_UP | RTF_GATEWAY, (struct rtentry **)0);
+               if (error)
                        panic("nfs_mountroot: RTM_ADD: %d", error);
        }
 
                        panic("nfs_mountroot: RTM_ADD: %d", error);
        }
 
-       /*
-        * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
-        * Create a fake mount point just for the swap vnode so that the
-        * swap file can be on a different server from the rootfs.
-        */
-       if (swdevt[0].sw_dev == NODEV) {
-               nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh;
-               (void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0,
+       if (nd->swap_nblks) {
+               /*
+                * Create a fake mount point just for the swap vnode so that the
+                * swap file can be on a different server from the rootfs.
+                */
+               nd->swap_args.fh = nd->swap_fh;
+               /*
+                * If using nfsv3_diskless, replace NFSX_V2FH with
+                * nd->swap_fhsize.
+                */
+               nd->swap_args.fhsize = NFSX_V2FH;
+               l = ntohl(nd->swap_saddr.sin_addr.s_addr);
+               sprintf(buf,"%ld.%ld.%ld.%ld:%s",
+                       (l >> 24) & 0xff, (l >> 16) & 0xff,
+                       (l >>  8) & 0xff, (l >>  0) & 0xff,nd->swap_hostnam);
+               printf("NFS SWAP: %s\n",buf);
+               (void) nfs_mountdiskless(buf, "/swap", 0,
                    &nd->swap_saddr, &nd->swap_args, &vp);
                    &nd->swap_saddr, &nd->swap_args, &vp);
-       
+
+               for (i=0;swdevt[i].sw_dev != NODEV;i++) ;
+
                /*
                 * Since the swap file is not the root dir of a file system,
                 * hack it to a regular file.
                /*
                 * Since the swap file is not the root dir of a file system,
                 * hack it to a regular file.
@@ -214,16 +331,31 @@ nfs_mountroot()
                vp->v_flag = 0;
                swapdev_vp = vp;
                VREF(vp);
                vp->v_flag = 0;
                swapdev_vp = vp;
                VREF(vp);
-               swdevt[0].sw_vp = vp;
-               swdevt[0].sw_nblks = ntohl(nd->swap_nblks);
-       } else if (bdevvp(swapdev, &swapdev_vp))
-               panic("nfs_mountroot: can't setup swapdev_vp");
+               swdevt[i].sw_vp = vp;
+               swdevt[i].sw_nblks = nd->swap_nblks*2;
+
+               if (!swdevt[i].sw_nblks) {
+                       swdevt[i].sw_nblks = 2048;
+                       printf("defaulting to %d kbyte.\n",
+                               swdevt[i].sw_nblks/2);
+               } else
+                       printf("using %d kbyte.\n",swdevt[i].sw_nblks/2);
+       }
 
        /*
         * Create the rootfs mount point.
         */
 
        /*
         * Create the rootfs mount point.
         */
-       nd->root_args.fh = (nfsv2fh_t *)nd->root_fh;
-       mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY,
+       nd->root_args.fh = nd->root_fh;
+       /*
+        * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize.
+        */
+       nd->root_args.fhsize = NFSX_V2FH;
+       l = ntohl(nd->swap_saddr.sin_addr.s_addr);
+       sprintf(buf,"%ld.%ld.%ld.%ld:%s",
+               (l >> 24) & 0xff, (l >> 16) & 0xff,
+               (l >>  8) & 0xff, (l >>  0) & 0xff,nd->root_hostnam);
+       printf("NFS ROOT: %s\n",buf);
+       mp = nfs_mountdiskless(buf, "/", MNT_RDONLY,
            &nd->root_saddr, &nd->root_args, &vp);
 
        if (vfs_lock(mp))
            &nd->root_saddr, &nd->root_args, &vp);
 
        if (vfs_lock(mp))
@@ -278,34 +410,13 @@ nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
                panic("nfs_mountroot: %s mount mbuf", which);
        bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
        m->m_len = sin->sin_len;
                panic("nfs_mountroot: %s mount mbuf", which);
        bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
        m->m_len = sin->sin_len;
-       nfsargs_ntoh(args);
-       if (error = mountnfs(args, mp, m, which, path, vpp))
+       error = mountnfs(args, mp, m, which, path, vpp);
+       if (error)
                panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
 
        return (mp);
 }
 
                panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
 
        return (mp);
 }
 
-/*
- * Convert the integer fields of the nfs_args structure from net byte order
- * to host byte order. Called by nfs_mountroot() above.
- */
-void
-nfsargs_ntoh(nfsp)
-       register struct nfs_args *nfsp;
-{
-
-       NTOHL(nfsp->sotype);
-       NTOHL(nfsp->proto);
-       NTOHL(nfsp->flags);
-       NTOHL(nfsp->wsize);
-       NTOHL(nfsp->rsize);
-       NTOHL(nfsp->timeo);
-       NTOHL(nfsp->retrans);
-       NTOHL(nfsp->maxgrouplist);
-       NTOHL(nfsp->readahead);
-       NTOHL(nfsp->leaseterm);
-       NTOHL(nfsp->deadthresh);
-}
 
 /*
  * VFS Operations.
 
 /*
  * VFS Operations.
@@ -331,23 +442,27 @@ nfs_mount(mp, path, data, ndp, p)
        struct vnode *vp;
        char pth[MNAMELEN], hst[MNAMELEN];
        u_int len;
        struct vnode *vp;
        char pth[MNAMELEN], hst[MNAMELEN];
        u_int len;
-       nfsv2fh_t nfh;
+       u_char nfh[NFSX_V3FHMAX];
 
 
-       if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)))
+       error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
+       if (error)
                return (error);
                return (error);
-       if (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);
                return (error);
-       if (error = copyinstr(path, pth, MNAMELEN-1, &len))
+       error = copyinstr(path, pth, MNAMELEN-1, &len);
+       if (error)
                return (error);
        bzero(&pth[len], MNAMELEN - len);
                return (error);
        bzero(&pth[len], MNAMELEN - len);
-       if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len))
+       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 */
                return (error);
        bzero(&hst[len], MNAMELEN - len);
        /* sockargs() call must be after above copyin() calls */
-       if (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);
                return (error);
-       args.fh = &nfh;
+       args.fh = nfh;
        error = mountnfs(&args, mp, nam, pth, hst, &vp);
        return (error);
 }
        error = mountnfs(&args, mp, nam, pth, hst, &vp);
        return (error);
 }
@@ -365,7 +480,7 @@ mountnfs(argp, mp, nam, pth, hst, vpp)
 {
        register struct nfsmount *nmp;
        struct nfsnode *np;
 {
        register struct nfsmount *nmp;
        struct nfsnode *np;
-       int error;
+       int error, maxio;
 
        if (mp->mnt_flag & MNT_UPDATE) {
                nmp = VFSTONFS(mp);
 
        if (mp->mnt_flag & MNT_UPDATE) {
                nmp = VFSTONFS(mp);
@@ -376,16 +491,12 @@ mountnfs(argp, mp, nam, pth, hst, vpp)
                MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
                    M_NFSMNT, M_WAITOK);
                bzero((caddr_t)nmp, sizeof (struct nfsmount));
                MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
                    M_NFSMNT, M_WAITOK);
                bzero((caddr_t)nmp, sizeof (struct nfsmount));
+               TAILQ_INIT(&nmp->nm_uidlruhead);
                mp->mnt_data = (qaddr_t)nmp;
        }
                mp->mnt_data = (qaddr_t)nmp;
        }
-       getnewfsid(mp, MOUNT_NFS);
+       vfs_getnewfsid(mp);
        nmp->nm_mountp = mp;
        nmp->nm_flag = argp->flags;
        nmp->nm_mountp = mp;
        nmp->nm_flag = argp->flags;
-       if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) ==
-               (NFSMNT_NQNFS | NFSMNT_MYWRITE)) {
-               error = EPERM;
-               goto bad;
-       }
        if (nmp->nm_flag & NFSMNT_NQNFS)
                /*
                 * We have to set mnt_maxsymlink to a non-zero value so
        if (nmp->nm_flag & NFSMNT_NQNFS)
                /*
                 * We have to set mnt_maxsymlink to a non-zero value so
@@ -398,14 +509,15 @@ mountnfs(argp, mp, nam, pth, hst, vpp)
        nmp->nm_retry = NFS_RETRANS;
        nmp->nm_wsize = NFS_WSIZE;
        nmp->nm_rsize = NFS_RSIZE;
        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;
        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));
-       mp->mnt_stat.f_type = MOUNT_NFS;
+       nmp->nm_fhsize = argp->fhsize;
+       bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
        bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
        bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
        nmp->nm_nam = nam;
        bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
        bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
        nmp->nm_nam = nam;
@@ -424,29 +536,48 @@ mountnfs(argp, mp, nam, pth, hst, vpp)
                        nmp->nm_retry = NFS_MAXREXMIT;
        }
 
                        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) {
                nmp->nm_wsize = argp->wsize;
                /* Round down to multiple of blocksize */
        if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
                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)
                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;
        }
        }
+       if (nmp->nm_wsize > maxio)
+               nmp->nm_wsize = maxio;
        if (nmp->nm_wsize > MAXBSIZE)
                nmp->nm_wsize = MAXBSIZE;
 
        if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
                nmp->nm_rsize = argp->rsize;
                /* Round down to multiple of blocksize */
        if (nmp->nm_wsize > MAXBSIZE)
                nmp->nm_wsize = MAXBSIZE;
 
        if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
                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)
                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;
        }
        }
+       if (nmp->nm_rsize > maxio)
+               nmp->nm_rsize = maxio;
        if (nmp->nm_rsize > MAXBSIZE)
                nmp->nm_rsize = MAXBSIZE;
        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;
        if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
                argp->maxgrouplist <= NFS_MAXGRPS)
                nmp->nm_numgrps = argp->maxgrouplist;
@@ -486,7 +617,8 @@ mountnfs(argp, mp, nam, pth, hst, vpp)
         * this problem, because one can identify root inodes by their
         * number == ROOTINO (2).
         */
         * this problem, because one can identify root inodes by their
         * number == ROOTINO (2).
         */
-       if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+       error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+       if (error)
                goto bad;
        *vpp = NFSTOV(np);
 
                goto bad;
        *vpp = NFSTOV(np);
 
@@ -514,7 +646,7 @@ nfs_unmount(mp, mntflags, p)
        extern int doforce;
 
        if (mntflags & MNT_FORCE) {
        extern int doforce;
 
        if (mntflags & MNT_FORCE) {
-               if (!doforce || (mp->mnt_flag & MNT_ROOTFS))
+               if (!doforce)
                        return (EINVAL);
                flags |= FORCECLOSE;
        }
                        return (EINVAL);
                flags |= FORCECLOSE;
        }
@@ -533,7 +665,8 @@ nfs_unmount(mp, mntflags, p)
         * the remote root.  See comment in mountnfs().  The VFS unmount()
         * has done vput on this vnode, otherwise we would get deadlock!
         */
         * 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))
+       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) {
                return(error);
        vp = NFSTOV(np);
        if (vp->v_usecount > 2) {
@@ -547,7 +680,8 @@ nfs_unmount(mp, mntflags, p)
        nmp->nm_flag |= NFSMNT_DISMINPROG;
        while (nmp->nm_inprog != NULLVP)
                (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
        nmp->nm_flag |= NFSMNT_DISMINPROG;
        while (nmp->nm_inprog != NULLVP)
                (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
-       if (error = vflush(mp, vp, flags)) {
+       error = vflush(mp, vp, flags);
+       if (error) {
                vput(vp);
                nmp->nm_flag &= ~NFSMNT_DISMINPROG;
                return (error);
                vput(vp);
                nmp->nm_flag &= ~NFSMNT_DISMINPROG;
                return (error);
@@ -588,7 +722,8 @@ nfs_root(mp, vpp)
        int error;
 
        nmp = VFSTONFS(mp);
        int error;
 
        nmp = VFSTONFS(mp);
-       if (error = nfs_nget(mp, &nmp->nm_fh, &np))
+       error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
+       if (error)
                return (error);
        vp = NFSTOV(np);
        vp->v_type = VDIR;
                return (error);
        vp = NFSTOV(np);
        vp->v_type = VDIR;
@@ -630,7 +765,8 @@ loop:
                        continue;
                if (vget(vp, 1))
                        goto loop;
                        continue;
                if (vget(vp, 1))
                        goto loop;
-               if (error = VOP_FSYNC(vp, cred, waitfor, p))
+               error = VOP_FSYNC(vp, cred, waitfor, p);
+               if (error)
                        allerror = error;
                vput(vp);
        }
                        allerror = error;
                vput(vp);
        }
@@ -711,3 +847,47 @@ nfs_quotactl(mp, cmd, uid, arg, p)
 
        return (EOPNOTSUPP);
 }
 
        return (EOPNOTSUPP);
 }
+
+/*
+ * 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;
+       }
+}
+
index 8f9dad2..ee58d0f 100644 (file)
@@ -7,17 +7,19 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_vnops.c 8.13 (Berkeley) %G%
+ *     @(#)nfs_vnops.c 8.14 (Berkeley) %G%
  */
 
  */
 
+
 /*
 /*
- * vnode op calls for sun nfs version 2
+ * vnode op calls for Sun NFS version 2 and 3
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
-#include <sys/proc.h>
 #include <sys/kernel.h>
 #include <sys/systm.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>
 #include <sys/mount.h>
 #include <sys/buf.h>
 #include <sys/malloc.h>
@@ -25,8 +27,9 @@
 #include <sys/conf.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/conf.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
-#include <sys/map.h>
 #include <sys/dirent.h>
 #include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <ufs/ufs/dir.h>
 
 #include <vm/vm.h>
 
 
 #include <vm/vm.h>
 
@@ -34,7 +37,7 @@
 #include <miscfs/fifofs/fifo.h>
 
 #include <nfs/rpcv2.h>
 #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>
 #include <nfs/nfs.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsmount.h>
 #include <nfs/nfsm_subs.h>
 #include <nfs/nqnfs.h>
 
 #include <nfs/nfsm_subs.h>
 #include <nfs/nqnfs.h>
 
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
 /* Defs */
 #define        TRUE    1
 #define        FALSE   0
 /* Defs */
 #define        TRUE    1
 #define        FALSE   0
@@ -94,7 +101,7 @@ struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
        { &vop_vfree_desc, nfs_vfree },         /* vfree */
        { &vop_truncate_desc, nfs_truncate },   /* truncate */
        { &vop_update_desc, nfs_update },       /* update */
        { &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(*)())NULL }
 };
 struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
 };
 struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
@@ -154,7 +161,6 @@ struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
        { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
 
 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
        { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
 
-#ifdef FIFO
 int (**fifo_nfsv2nodeop_p)();
 struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
        { &vop_default_desc, vn_default_error },
 int (**fifo_nfsv2nodeop_p)();
 struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
        { &vop_default_desc, vn_default_error },
@@ -205,16 +211,16 @@ struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
 };
 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
        { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
 };
 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
        { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
-#endif /* FIFO */
 
 void nqnfs_clientlease();
 
 void nqnfs_clientlease();
+int nfs_commit();
 
 /*
  * Global variables
  */
 
 /*
  * Global variables
  */
-extern u_long nfs_procids[NFS_NPROCS];
-extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false;
-extern char nfsiobuf[MAXPHYS+NBPG];
+extern u_long 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))
 struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 int nfs_numasync = 0;
 #define        DIRHDSIZ        (sizeof (struct dirent) - (MAXNAMLEN + 1))
@@ -240,9 +246,9 @@ nfs_null(vp, cred, procp)
 
 /*
  * nfs access vnode op.
 
 /*
  * 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(ap)
  */
 int
 nfs_access(ap)
@@ -256,36 +262,56 @@ nfs_access(ap)
        register struct vnode *vp = ap->a_vp;
        register u_long *tl;
        register caddr_t cp;
        register struct vnode *vp = ap->a_vp;
        register u_long *tl;
        register caddr_t cp;
-       caddr_t bpos, dpos;
-       int error = 0;
+       register int t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int error = 0, attrflag;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       u_long mode, rmode;
+       int v3 = NFS_ISV3(vp);
 
        /*
 
        /*
-        * For nqnfs, do an access rpc, otherwise you are stuck emulating
+        * 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.
         */
         * 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_long *, 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_long *, NFSX_UNSIGNED);
                if (ap->a_mode & VREAD)
                if (ap->a_mode & VREAD)
-                       *tl++ = nfs_true;
-               else
-                       *tl++ = nfs_false;
-               if (ap->a_mode & VWRITE)
-                       *tl++ = nfs_true;
+                       mode = NFSV3ACCESS_READ;
                else
                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_long *, NFSX_UNSIGNED);
+                       rmode = fxdr_unsigned(u_long, *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
                nfsm_reqdone;
                return (error);
        } else
@@ -316,15 +342,17 @@ nfs_open(ap)
        int error;
 
        if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
        int error;
 
        if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
+{ printf("open eacces vtyp=%d\n",vp->v_type);
                return (EACCES);
                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 {
                    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);
                    } while (error == NQNFS_EXPIRED);
                    if (error)
                        return (error);
@@ -337,22 +365,26 @@ nfs_open(ap)
                        np->n_brev = np->n_lrev;
                    }
                }
                        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;
                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 (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+                       if (vp->v_type == VDIR)
+                               np->n_direofoffset = 0;
+                       error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+                       if (error)
                                return (error);
                        np->n_mtime = vattr.va_mtime.ts_sec;
                } else {
                                return (error);
                        np->n_mtime = vattr.va_mtime.ts_sec;
                } else {
-                       if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+                       error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
+                       if (error)
                                return (error);
                        if (np->n_mtime != vattr.va_mtime.ts_sec) {
                                return (error);
                        if (np->n_mtime != vattr.va_mtime.ts_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);
                                if ((error = nfs_vinvalbuf(vp, V_SAVE,
                                        ap->a_cred, ap->a_p, 1)) == EINTR)
                                        return (error);
@@ -360,15 +392,41 @@ nfs_open(ap)
                                np->n_mtime = vattr.va_mtime.ts_sec;
                        }
                }
                                np->n_mtime = vattr.va_mtime.ts_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
                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
  */
 /* ARGSUSED */
 int
@@ -388,7 +446,10 @@ nfs_close(ap)
        if (vp->v_type == VREG) {
            if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
                (np->n_flag & NMODIFIED)) {
        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);
+               else
+                   error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
                np->n_attrstamp = 0;
            }
            if (np->n_flag & NWRITEERR) {
                np->n_attrstamp = 0;
            }
            if (np->n_flag & NWRITEERR) {
@@ -414,9 +475,12 @@ nfs_getattr(ap)
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        register caddr_t cp;
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        register caddr_t cp;
+       register u_long *tl;
+       register int t1, t2;
        caddr_t bpos, dpos;
        int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        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.
        
        /*
         * Update local times for special files.
@@ -429,10 +493,11 @@ nfs_getattr(ap)
        if (nfs_getattrcache(vp, ap->a_vap) == 0)
                return (0);
        nfsstats.rpccnt[NFSPROC_GETATTR]++;
        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_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);
 }
        nfsm_reqdone;
        return (error);
 }
@@ -449,91 +514,167 @@ nfs_setattr(ap)
                struct ucred *a_cred;
                struct proc *a_p;
        } */ *ap;
                struct ucred *a_cred;
                struct proc *a_p;
        } */ *ap;
+{
+       register struct vnode *vp = ap->a_vp;
+       register struct nfsnode *np = VTONFS(vp);
+       register struct vattr *vap = ap->a_vap;
+       int error = 0;
+       u_quad_t tsize;
+
+#ifndef nolint
+       tsize = (u_quad_t)0;
+#endif
+       if (vap->va_size != VNOVAL) {
+               switch (vp->v_type) {
+               case VDIR:
+                       return (EISDIR);
+               case VCHR:
+               case VBLK:
+                       if (vap->va_mtime.ts_sec == VNOVAL &&
+                           vap->va_atime.ts_sec == VNOVAL &&
+                           vap->va_mode == (u_short)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.ts_sec != VNOVAL ||
+               vap->va_atime.ts_sec != VNOVAL) && (np->n_flag & NMODIFIED) &&
+               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) {
+               np->n_size = np->n_vattr.va_size = tsize;
+               vnode_pager_setsize(vp, (u_long)np->n_size);
+       }
+       return (error);
+}
+
+/*
+ * 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 struct nfsv2_sattr *sp;
        register caddr_t cp;
-       register long t1;
+       register long t1, t2;
        caddr_t bpos, dpos, cp2;
        u_long *tl;
        caddr_t bpos, dpos, cp2;
        u_long *tl;
-       int error = 0, isnq;
+       int error = 0, wccflag = NFSV3_WCCRATTR;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        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;
+       u_quad_t frev;
+       int v3 = NFS_ISV3(vp);
 
 
-       if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL ||
-               vap->va_atime.ts_sec != VNOVAL) {
+       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_long *, 2 * NFSX_UNSIGNED);
+                       *tl++ = nfs_true;
+                       *tl = txdr_unsigned(vap->va_mode);
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = nfs_false;
+               }
+               if (vap->va_uid != (uid_t)VNOVAL) {
+                       nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       *tl++ = nfs_true;
+                       *tl = txdr_unsigned(vap->va_uid);
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = nfs_false;
+               }
+               if (vap->va_gid != (gid_t)VNOVAL) {
+                       nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       *tl++ = nfs_true;
+                       *tl = txdr_unsigned(vap->va_gid);
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = nfs_false;
+               }
                if (vap->va_size != VNOVAL) {
                if (vap->va_size != VNOVAL) {
-                       switch (vp->v_type) {
-                       case VDIR:
-                               return (EISDIR);
-                       case VCHR:
-                       case VBLK:
-                               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);
+                       nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+                       *tl++ = nfs_true;
+                       txdr_hyper(&vap->va_size, tl);
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = nfs_false;
+               }
+               if (vap->va_atime.ts_sec != VNOVAL) {
+                       if (vap->va_atime.ts_sec != time.tv_sec) {
+                               nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+                               *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
+                               txdr_nfsv3time(&vap->va_atime, tl);
+                       } else {
+                               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                               *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
                        }
                        }
-               } else if ((np->n_flag & NMODIFIED) &&
-                       (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
-                        ap->a_p, 1)) == 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)-1)
-               sp->sa_mode = VNOVAL;
-       else
-               sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode);
-       if (vap->va_uid == (uid_t)-1)
-               sp->sa_uid = VNOVAL;
-       else
-               sp->sa_uid = txdr_unsigned(vap->va_uid);
-       if (vap->va_gid == (gid_t)-1)
-               sp->sa_gid = VNOVAL;
-       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 = VNOVAL;
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
+               }
+               if (vap->va_mtime.ts_sec != VNOVAL) {
+                       if (vap->va_mtime.ts_sec != time.tv_sec) {
+                               nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+                               *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
+                               txdr_nfsv3time(&vap->va_atime, tl);
+                       } else {
+                               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                               *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
+                       }
+               } else {
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+                       *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
+               }
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+               *tl = nfs_false;
        } else {
        } 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_long *, 2*NFSX_UNSIGNED);
-               fxdr_hyper(tl, &frev);
-               if (frev > np->n_brev)
-                       np->n_brev = frev;
+               nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               if (vap->va_mode == (u_short)VNOVAL)
+                       sp->sa_mode = VNOVAL;
+               else
+                       sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode);
+               if (vap->va_uid == (uid_t)VNOVAL)
+                       sp->sa_uid = VNOVAL;
+               else
+                       sp->sa_uid = txdr_unsigned(vap->va_uid);
+               if (vap->va_gid == (gid_t)VNOVAL)
+                       sp->sa_gid = VNOVAL;
+               else
+                       sp->sa_gid = txdr_unsigned(vap->va_gid);
+               sp->sa_size = txdr_unsigned(vap->va_size);
+               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
+               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
        }
        }
+       nfsm_request(vp, NFSPROC_SETATTR, procp, cred);
+       if (v3) {
+               nfsm_wcc_data(vp, wccflag);
+       } else
+               nfsm_loadattr(vp, (struct vattr *)0);
        nfsm_reqdone;
        nfsm_reqdone;
-       if (error) {
-               np->n_size = np->n_vattr.va_size = tsize;
-               vnode_pager_setsize(vp, (u_long)np->n_size);
-       }
        return (error);
 }
 
        return (error);
 }
 
@@ -555,23 +696,20 @@ nfs_lookup(ap)
        register struct vnode *dvp = ap->a_dvp;
        register struct vnode **vpp = ap->a_vpp;
        register int flags = cnp->cn_flags;
        register struct vnode *dvp = ap->a_dvp;
        register struct vnode **vpp = ap->a_vpp;
        register int flags = cnp->cn_flags;
-       register struct vnode *vdp;
+       register struct vnode *newvp;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        struct nfsmount *nmp;
        caddr_t bpos, dpos, cp2;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        struct nfsmount *nmp;
        caddr_t bpos, dpos, cp2;
-       time_t reqtime;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       struct vnode *newvp;
        long len;
        long len;
-       nfsv2fh_t *fhp;
+       nfsfh_t *fhp;
        struct nfsnode *np;
        struct nfsnode *np;
-       int lockparent, wantparent, error = 0;
-       int nqlflag, cachable;
-       u_quad_t frev;
+       int lockparent, wantparent, error = 0, attrflag, fhsize;
+       int v3 = NFS_ISV3(dvp);
 
 
-       *vpp = NULL;
+       *vpp = NULLVP;
        if (dvp->v_type != VDIR)
                return (ENOTDIR);
        lockparent = flags & LOCKPARENT;
        if (dvp->v_type != VDIR)
                return (ENOTDIR);
        lockparent = flags & LOCKPARENT;
@@ -582,158 +720,108 @@ nfs_lookup(ap)
                struct vattr vattr;
                int vpid;
 
                struct vattr vattr;
                int vpid;
 
-               vdp = *vpp;
-               vpid = vdp->v_id;
+               newvp = *vpp;
+               vpid = newvp->v_id;
                /*
                 * See the comment starting `Step through' in ufs/ufs_lookup.c
                 * for an explanation of the locking protocol
                 */
                /*
                 * See the comment starting `Step through' in ufs/ufs_lookup.c
                 * for an explanation of the locking protocol
                 */
-               if (dvp == vdp) {
-                       VREF(vdp);
+               if (dvp == newvp) {
+                       VREF(newvp);
                        error = 0;
                } else
                        error = 0;
                } else
-                       error = vget(vdp, 1);
+                       error = vget(newvp, 1);
                if (!error) {
                if (!error) {
-                       if (vpid == vdp->v_id) {
-                          if (nmp->nm_flag & NFSMNT_NQNFS) {
-                               if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) {
-                                       nfsstats.lookupcache_hits++;
-                                       if (cnp->cn_nameiop != LOOKUP &&
-                                           (flags & ISLASTCN))
-                                           cnp->cn_flags |= SAVENAME;
-                                       return (0);
-                               } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) {
-                                       if (np->n_lrev != np->n_brev ||
-                                           (np->n_flag & NMODIFIED)) {
-                                               np->n_direofoffset = 0;
-                                               cache_purge(dvp);
-                                               error = nfs_vinvalbuf(dvp, 0,
-                                                   cnp->cn_cred, cnp->cn_proc,
-                                                   1);
-                                               if (error == EINTR)
-                                                       return (error);
-                                               np->n_brev = np->n_lrev;
-                                       } else {
-                                               nfsstats.lookupcache_hits++;
-                                               if (cnp->cn_nameiop != LOOKUP &&
-                                                   (flags & ISLASTCN))
-                                                   cnp->cn_flags |= SAVENAME;
-                                               return (0);
-                                       }
-                               }
-                          } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) &&
-                              vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) {
+                       if (vpid == newvp->v_id) {
+                          if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
+                           && vattr.va_ctime.ts_sec == VTONFS(newvp)->n_ctime) {
                                nfsstats.lookupcache_hits++;
                                if (cnp->cn_nameiop != LOOKUP &&
                                    (flags & ISLASTCN))
                                        cnp->cn_flags |= SAVENAME;
                                return (0);
                           }
                                nfsstats.lookupcache_hits++;
                                if (cnp->cn_nameiop != LOOKUP &&
                                    (flags & ISLASTCN))
                                        cnp->cn_flags |= SAVENAME;
                                return (0);
                           }
-                          cache_purge(vdp);
+                          cache_purge(newvp);
                        }
                        }
-                       vrele(vdp);
+                       vrele(newvp);
                }
                *vpp = NULLVP;
        }
        error = 0;
                }
                *vpp = NULLVP;
        }
        error = 0;
+       newvp = NULLVP;
        nfsstats.lookupcache_misses++;
        nfsstats.rpccnt[NFSPROC_LOOKUP]++;
        len = cnp->cn_namelen;
        nfsstats.lookupcache_misses++;
        nfsstats.rpccnt[NFSPROC_LOOKUP]++;
        len = cnp->cn_namelen;
-       nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
-
-       /*
-        * For nqnfs optionally piggyback a getlease request for the name
-        * being looked up.
-        */
-       if (nmp->nm_flag & NFSMNT_NQNFS) {
-               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
-               if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
-                   ((cnp->cn_flags & MAKEENTRY) &&
-                   (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))))
-                       *tl = txdr_unsigned(nmp->nm_leaseterm);
-               else
-                       *tl = 0;
-       }
-       nfsm_fhtom(dvp);
+       nfsm_reqhead(dvp, NFSPROC_LOOKUP,
+               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
-       reqtime = time.tv_sec;
        nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
        nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
-nfsmout:
        if (error) {
        if (error) {
-               if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
-                   (flags & ISLASTCN) && error == ENOENT)
-                       error = EJUSTRETURN;
-               if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
-                       cnp->cn_flags |= SAVENAME;
-               return (error);
-       }
-       if (nmp->nm_flag & NFSMNT_NQNFS) {
-               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
-               if (*tl) {
-                       nqlflag = fxdr_unsigned(int, *tl);
-                       nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
-                       cachable = fxdr_unsigned(int, *tl++);
-                       reqtime += fxdr_unsigned(int, *tl++);
-                       fxdr_hyper(tl, &frev);
-               } else
-                       nqlflag = 0;
+               nfsm_postop_attr(dvp, attrflag);
+               m_freem(mrep);
+               goto nfsmout;
        }
        }
-       nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
+       nfsm_getfh(fhp, fhsize, v3);
 
        /*
         * Handle RENAME case...
         */
        if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
 
        /*
         * Handle RENAME case...
         */
        if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
-               if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+               if (NFS_CMPFH(np, fhp, fhsize)) {
                        m_freem(mrep);
                        return (EISDIR);
                }
                        m_freem(mrep);
                        return (EISDIR);
                }
-               if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+               if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) {
                        m_freem(mrep);
                        return (error);
                }
                newvp = NFSTOV(np);
                        m_freem(mrep);
                        return (error);
                }
                newvp = NFSTOV(np);
-               if (error =
-                   nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
-                       vrele(newvp);
-                       m_freem(mrep);
-                       return (error);
-               }
+               if (v3) {
+                       nfsm_postop_attr(newvp, attrflag);
+                       nfsm_postop_attr(dvp, attrflag);
+               } else
+                       nfsm_loadattr(newvp, (struct vattr *)0);
                *vpp = newvp;
                m_freem(mrep);
                cnp->cn_flags |= SAVENAME;
                return (0);
        }
 
                *vpp = newvp;
                m_freem(mrep);
                cnp->cn_flags |= SAVENAME;
                return (0);
        }
 
-       if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
+       if (NFS_CMPFH(np, fhp, fhsize)) {
                VREF(dvp);
                newvp = dvp;
        } else {
                VREF(dvp);
                newvp = dvp;
        } else {
-               if (error = nfs_nget(dvp->v_mount, fhp, &np)) {
+               if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) {
                        m_freem(mrep);
                        return (error);
                }
                newvp = NFSTOV(np);
        }
                        m_freem(mrep);
                        return (error);
                }
                newvp = NFSTOV(np);
        }
-       if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) {
-               vrele(newvp);
-               m_freem(mrep);
-               return (error);
-       }
-       m_freem(mrep);
-       *vpp = newvp;
+       if (v3) {
+               nfsm_postop_attr(newvp, attrflag);
+               nfsm_postop_attr(dvp, attrflag);
+       } else
+               nfsm_loadattr(newvp, (struct vattr *)0);
        if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
                cnp->cn_flags |= SAVENAME;
        if ((cnp->cn_flags & MAKEENTRY) &&
            (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
        if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
                cnp->cn_flags |= SAVENAME;
        if ((cnp->cn_flags & MAKEENTRY) &&
            (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
-               if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
-                       np->n_ctime = np->n_vattr.va_ctime.ts_sec;
-               else if (nqlflag && reqtime > time.tv_sec)
-                       nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime,
-                               frev);
-               cache_enter(dvp, *vpp, cnp);
+               np->n_ctime = np->n_vattr.va_ctime.ts_sec;
+               cache_enter(dvp, newvp, cnp);
        }
        }
-       return (0);
+       *vpp = newvp;
+       nfsm_reqdone;
+       if (error) {
+               if (newvp != NULLVP)
+                       vrele(newvp);
+               if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
+                   (flags & ISLASTCN) && error == ENOENT)
+                       error = EJUSTRETURN;
+               if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+                       cnp->cn_flags |= SAVENAME;
+       }
+       return (error);
 }
 
 /*
 }
 
 /*
@@ -786,18 +874,22 @@ nfs_readlinkrpc(vp, uiop, cred)
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
+       register long t1, t2;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0;
+       int error = 0, len, attrflag;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       long len;
+       int v3 = NFS_ISV3(vp);
 
        nfsstats.rpccnt[NFSPROC_READLINK]++;
 
        nfsstats.rpccnt[NFSPROC_READLINK]++;
-       nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH);
-       nfsm_fhtom(vp);
+       nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3));
+       nfsm_fhtom(vp, v3);
        nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
        nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
-       nfsm_strsiz(len, NFS_MAXPATHLEN);
-       nfsm_mtouio(uiop, len);
+       if (v3)
+               nfsm_postop_attr(vp, attrflag);
+       if (!error) {
+               nfsm_strsiz(len, NFS_MAXPATHLEN);
+               nfsm_mtouio(uiop, len);
+       }
        nfsm_reqdone;
        return (error);
 }
        nfsm_reqdone;
        return (error);
 }
@@ -814,25 +906,27 @@ nfs_readrpc(vp, uiop, cred)
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
+       register long t1, t2;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct nfsmount *nmp;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct nfsmount *nmp;
-       long len, retlen, tsiz;
+       int error = 0, len, retlen, tsiz, eof, attrflag;
+       int v3 = NFS_ISV3(vp);
 
 
+#ifndef nolint
+       eof = 0;
+#endif
        nmp = VFSTONFS(vp->v_mount);
        tsiz = uiop->uio_resid;
        nmp = VFSTONFS(vp->v_mount);
        tsiz = uiop->uio_resid;
-       if (uiop->uio_offset + tsiz > 0xffffffff &&
-           (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+       if (uiop->uio_offset + tsiz > 0xffffffff && !v3)
                return (EFBIG);
        while (tsiz > 0) {
                nfsstats.rpccnt[NFSPROC_READ]++;
                len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
                return (EFBIG);
        while (tsiz > 0) {
                nfsstats.rpccnt[NFSPROC_READ]++;
                len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
-               nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3);
-               nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, NFSX_UNSIGNED*3);
-               if (nmp->nm_flag & NFSMNT_NQNFS) {
+               nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3);
+               nfsm_fhtom(vp, v3);
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
+               if (v3) {
                        txdr_hyper(&uiop->uio_offset, tl);
                        *(tl + 2) = txdr_unsigned(len);
                } else {
                        txdr_hyper(&uiop->uio_offset, tl);
                        *(tl + 2) = txdr_unsigned(len);
                } else {
@@ -841,14 +935,25 @@ nfs_readrpc(vp, uiop, cred)
                        *tl = 0;
                }
                nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
                        *tl = 0;
                }
                nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
-               nfsm_loadattr(vp, (struct vattr *)0);
+               if (v3) {
+                       nfsm_postop_attr(vp, attrflag);
+                       if (error) {
+                               m_freem(mrep);
+                               goto nfsmout;
+                       }
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       eof = fxdr_unsigned(int, *(tl + 1));
+               } else
+                       nfsm_loadattr(vp, (struct vattr *)0);
                nfsm_strsiz(retlen, nmp->nm_rsize);
                nfsm_mtouio(uiop, retlen);
                m_freem(mrep);
                nfsm_strsiz(retlen, nmp->nm_rsize);
                nfsm_mtouio(uiop, retlen);
                m_freem(mrep);
-               if (retlen < len)
+               tsiz -= retlen;
+               if (v3) {
+                       if (eof || retlen == 0)
+                               tsiz = 0;
+               } else if (retlen < len)
                        tsiz = 0;
                        tsiz = 0;
-               else
-                       tsiz -= len;
        }
 nfsmout:
        return (error);
        }
 nfsmout:
        return (error);
@@ -858,102 +963,136 @@ nfsmout:
  * nfs write call
  */
 int
  * nfs write call
  */
 int
-nfs_writerpc(vp, uiop, cred, ioflags)
+nfs_writerpc(vp, uiop, cred, iomode, must_commit)
        register struct vnode *vp;
        register struct vnode *vp;
-       struct uio *uiop;
+       register struct uio *uiop;
        struct ucred *cred;
        struct ucred *cred;
-       int ioflags;
+       int *iomode, *must_commit;
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
+       register int t1, t2, backup;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       struct nfsmount *nmp;
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
        struct nfsnode *np = VTONFS(vp);
        u_quad_t frev;
        struct nfsnode *np = VTONFS(vp);
        u_quad_t frev;
-       long len, tsiz;
+       int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit;
+       int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC;
 
 
-       nmp = VFSTONFS(vp->v_mount);
+#ifndef DIAGNOSTIC
+       if (uiop->uio_iovcnt != 1)
+               panic("nfs: writerpc iovcnt > 1");
+#endif
+       *must_commit = 0;
        tsiz = uiop->uio_resid;
        tsiz = uiop->uio_resid;
-       if (uiop->uio_offset + tsiz > 0xffffffff &&
-           (nmp->nm_flag & NFSMNT_NQNFS) == 0)
+       if (uiop->uio_offset + tsiz > 0xffffffff && !v3)
                return (EFBIG);
        while (tsiz > 0) {
                nfsstats.rpccnt[NFSPROC_WRITE]++;
                len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
                nfsm_reqhead(vp, NFSPROC_WRITE,
                return (EFBIG);
        while (tsiz > 0) {
                nfsstats.rpccnt[NFSPROC_WRITE]++;
                len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
                nfsm_reqhead(vp, NFSPROC_WRITE,
-                       NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len));
-               nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4);
-               if (nmp->nm_flag & NFSMNT_NQNFS) {
+                       NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
+               nfsm_fhtom(vp, v3);
+               if (v3) {
+                       nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
                        txdr_hyper(&uiop->uio_offset, tl);
                        tl += 2;
                        txdr_hyper(&uiop->uio_offset, tl);
                        tl += 2;
-                       *tl++ = 0;
+                       *tl++ = txdr_unsigned(len);
+                       *tl++ = txdr_unsigned(*iomode);
                } else {
                } else {
+                       nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
                        *++tl = txdr_unsigned(uiop->uio_offset);
                        tl += 2;
                }
                *tl = txdr_unsigned(len);
                nfsm_uiotom(uiop, len);
                nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
                        *++tl = txdr_unsigned(uiop->uio_offset);
                        tl += 2;
                }
                *tl = txdr_unsigned(len);
                nfsm_uiotom(uiop, len);
                nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
-               nfsm_loadattr(vp, (struct vattr *)0);
-               if (nmp->nm_flag & NFSMNT_MYWRITE)
-                       VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec;
-               else if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-                        NQNFS_CKCACHABLE(vp, NQL_WRITE)) {
-                       nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
-                       fxdr_hyper(tl, &frev);
-                       if (frev > np->n_brev)
-                               np->n_brev = frev;
-               }
+               if (v3) {
+                       wccflag = NFSV3_WCCCHK;
+                       nfsm_wcc_data(vp, wccflag);
+                       if (!error) {
+                               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
+                                       NFSX_V3WRITEVERF);
+                               rlen = fxdr_unsigned(int, *tl++);
+                               if (rlen == 0) {
+                                       error = NFSERR_IO;
+                                       break;
+                               } else if (rlen < len) {
+                                       backup = len - rlen;
+                                       uiop->uio_iov->iov_base -= backup;
+                                       uiop->uio_iov->iov_len += backup;
+                                       uiop->uio_offset -= backup;
+                                       uiop->uio_resid += backup;
+                                       len = rlen;
+                               }
+                               commit = fxdr_unsigned(int, *tl++);
+
+                               /*
+                                * Return the lowest committment level
+                                * obtained by any of the RPCs.
+                                */
+                               if (committed == NFSV3WRITE_FILESYNC)
+                                       committed = commit;
+                               else if (committed == NFSV3WRITE_DATASYNC &&
+                                       commit == NFSV3WRITE_UNSTABLE)
+                                       committed = commit;
+                               if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) {
+                                   bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
+                                       NFSX_V3WRITEVERF);
+                                   nmp->nm_flag |= NFSMNT_HASWRITEVERF;
+                               } else if (bcmp((caddr_t)tl,
+                                   (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
+                                   *must_commit = 1;
+                                   bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
+                                       NFSX_V3WRITEVERF);
+                               }
+                       }
+               } else
+                   nfsm_loadattr(vp, (struct vattr *)0);
+               if (wccflag)
+                   VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec;
                m_freem(mrep);
                tsiz -= len;
        }
 nfsmout:
                m_freem(mrep);
                tsiz -= len;
        }
 nfsmout:
+       *iomode = committed;
        if (error)
                uiop->uio_resid = tsiz;
        return (error);
 }
 
 /*
        if (error)
                uiop->uio_resid = tsiz;
        return (error);
 }
 
 /*
- * nfs mknod call
- * This is a kludge. Use a create rpc but with the IFMT bits of the mode
- * set to specify the file type and the size field for rdev.
+ * nfs mknod rpc
+ * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
+ * mode set to specify the file type and the size field for rdev.
  */
  */
-/* ARGSUSED */
 int
 int
-nfs_mknod(ap)
-       struct vop_mknod_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
-               struct componentname *a_cnp;
-               struct vattr *a_vap;
-       } */ *ap;
+nfs_mknodrpc(dvp, vpp, cnp, vap)
+       register struct vnode *dvp;
+       register struct vnode **vpp;
+       register struct componentname *cnp;
+       register struct vattr *vap;
 {
 {
-       register struct vnode *dvp = ap->a_dvp;
-       register struct vattr *vap = ap->a_vap;
-       register struct componentname *cnp = ap->a_cnp;
        register struct nfsv2_sattr *sp;
        register struct nfsv2_sattr *sp;
+       register struct nfsv3_sattr *sp3;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
-       struct vnode *newvp;
+       struct vnode *newvp = (struct vnode *)0;
+       struct nfsnode *np;
        struct vattr vattr;
        char *cp2;
        caddr_t bpos, dpos;
        struct vattr vattr;
        char *cp2;
        caddr_t bpos, dpos;
-       int error = 0, isnq;
+       int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        u_long rdev;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        u_long rdev;
+       int v3 = NFS_ISV3(dvp);
 
 
-       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
        if (vap->va_type == VCHR || vap->va_type == VBLK)
                rdev = txdr_unsigned(vap->va_rdev);
        if (vap->va_type == VCHR || vap->va_type == VBLK)
                rdev = txdr_unsigned(vap->va_rdev);
-#ifdef FIFO
-       else if (vap->va_type == VFIFO)
+       else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
                rdev = 0xffffffff;
                rdev = 0xffffffff;
-#endif /* FIFO */
        else {
                VOP_ABORTOP(dvp, cnp);
                vput(dvp);
        else {
                VOP_ABORTOP(dvp, cnp);
                vput(dvp);
@@ -964,37 +1103,87 @@ nfs_mknod(ap)
                vput(dvp);
                return (error);
        }
                vput(dvp);
                return (error);
        }
-       nfsstats.rpccnt[NFSPROC_CREATE]++;
-       nfsm_reqhead(dvp, NFSPROC_CREATE,
-         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
-       nfsm_fhtom(dvp);
+       nfsstats.rpccnt[NFSPROC_MKNOD]++;
+       nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
+               + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
-       sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
-       sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
-       sp->sa_gid = txdr_unsigned(vattr.va_gid);
-       if (isnq) {
-               sp->sa_nqrdev = rdev;
-               sp->sa_nqflags = 0;
-               txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
-               txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+       if (v3) {
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR);
+               *tl++ = vtonfsv3_type(vap->va_type);
+               sp3 = (struct nfsv3_sattr *)tl;
+               nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
+               if (vap->va_type == VCHR || vap->va_type == VBLK) {
+                       nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       *tl++ = txdr_unsigned(major(vap->va_rdev));
+                       *tl = txdr_unsigned(minor(vap->va_rdev));
+               }
        } else {
        } else {
-               sp->sa_nfssize = rdev;
-               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
-               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+               nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
+               sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+               sp->sa_gid = txdr_unsigned(vattr.va_gid);
+               sp->sa_size = rdev;
+               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
+               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
        }
        }
-       nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
-       nfsm_mtofh(dvp, newvp);
+       nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred);
+       if (!error) {
+               nfsm_mtofh(dvp, newvp, v3, gotvp);
+               if (!gotvp) {
+                       if (newvp) {
+                               vrele(newvp);
+                               newvp = (struct vnode *)0;
+                       }
+                       error = nfs_lookitup(dvp, cnp->cn_nameptr,
+                           cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
+                       if (!error)
+                               newvp = NFSTOV(np);
+               }
+       }
+       if (v3)
+               nfsm_wcc_data(dvp, wccflag);
        nfsm_reqdone;
        nfsm_reqdone;
-       if (!error && (cnp->cn_flags & MAKEENTRY))
-               cache_enter(dvp, newvp, cnp);
+       if (error) {
+               if (newvp)
+                       vrele(newvp);
+       } else {
+               if (cnp->cn_flags & MAKEENTRY)
+                       cache_enter(dvp, newvp, cnp);
+               *vpp = newvp;
+       }
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
-       VTONFS(dvp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        vrele(dvp);
        return (error);
 }
 
        vrele(dvp);
        return (error);
 }
 
+/*
+ * nfs mknod vop
+ * just call nfs_mknodrpc() to do the work.
+ */
+/* ARGSUSED */
+int
+nfs_mknod(ap)
+       struct vop_mknod_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+               struct vattr *a_vap;
+       } */ *ap;
+{
+       struct vnode *newvp;
+       int error;
+
+       error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap);
+       if (!error)
+               vrele(newvp);
+       return (error);
+}
+
+static u_long create_verf;
 /*
  * nfs file create call
  */
 /*
  * nfs file create call
  */
@@ -1011,50 +1200,97 @@ nfs_create(ap)
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
        register struct nfsv2_sattr *sp;
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
        register struct nfsv2_sattr *sp;
+       register struct nfsv3_sattr *sp3;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
+       struct nfsnode *np = (struct nfsnode *)0;
+       struct vnode *newvp = (struct vnode *)0;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0, isnq;
+       int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct vattr vattr;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct vattr vattr;
+       int v3 = NFS_ISV3(dvp);
+
+       /*
+        * Oops, not for me..
+        */
+       if (vap->va_type == VSOCK)
+               return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
 
        if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
                VOP_ABORTOP(dvp, cnp);
                vput(dvp);
                return (error);
        }
 
        if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
                VOP_ABORTOP(dvp, cnp);
                vput(dvp);
                return (error);
        }
+       if (vap->va_vaflags & VA_EXCLUSIVE)
+               fmode |= O_EXCL;
+again:
        nfsstats.rpccnt[NFSPROC_CREATE]++;
        nfsstats.rpccnt[NFSPROC_CREATE]++;
-       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
-       nfsm_reqhead(dvp, NFSPROC_CREATE,
-         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
-       nfsm_fhtom(dvp);
+       nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
+               nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
-       sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode);
-       sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
-       sp->sa_gid = txdr_unsigned(vattr.va_gid);
-       if (isnq) {
-               u_quad_t qval = 0;
-
-               txdr_hyper(&qval, &sp->sa_nqsize);
-               sp->sa_nqflags = 0;
-               sp->sa_nqrdev = -1;
-               txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
-               txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+       if (v3) {
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+               if (fmode & O_EXCL) {
+                   *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
+                   nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
+                   if (in_ifaddr)
+                       *tl++ = IA_SIN(in_ifaddr)->sin_addr.s_addr;
+                   else
+                       *tl++ = create_verf;
+                   *tl = ++create_verf;
+               } else {
+                   *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
+                   nfsm_build(tl, u_long *, NFSX_V3SRVSATTR);
+                   sp3 = (struct nfsv3_sattr *)tl;
+                   nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
+               }
        } else {
        } else {
-               sp->sa_nfssize = 0;
-               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
-               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+               nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
+               sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+               sp->sa_gid = txdr_unsigned(vattr.va_gid);
+               sp->sa_size = 0;
+               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
+               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
        }
        nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
        }
        nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
-       nfsm_mtofh(dvp, *ap->a_vpp);
+       if (!error) {
+               nfsm_mtofh(dvp, newvp, v3, gotvp);
+               if (!gotvp) {
+                       if (newvp) {
+                               vrele(newvp);
+                               newvp = (struct vnode *)0;
+                       }
+                       error = nfs_lookitup(dvp, cnp->cn_nameptr,
+                           cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
+                       if (!error)
+                               newvp = NFSTOV(np);
+               }
+       }
+       if (v3)
+               nfsm_wcc_data(dvp, wccflag);
        nfsm_reqdone;
        nfsm_reqdone;
-       if (!error && (cnp->cn_flags & MAKEENTRY))
-               cache_enter(dvp, *ap->a_vpp, cnp);
+       if (error) {
+               if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
+                       fmode &= ~O_EXCL;
+                       goto again;
+               }
+               if (newvp)
+                       vrele(newvp);
+       } else if (v3 && (fmode & O_EXCL))
+               error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc);
+       if (!error) {
+               if (cnp->cn_flags & MAKEENTRY)
+                       cache_enter(dvp, newvp, cnp);
+               *ap->a_vpp = newvp;
+       }
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
-       VTONFS(dvp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        vrele(dvp);
        return (error);
 }
        vrele(dvp);
        return (error);
 }
@@ -1089,11 +1325,18 @@ nfs_remove(ap)
        caddr_t bpos, dpos;
        int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        caddr_t bpos, dpos;
        int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       struct vattr vattr;
+       int v3 = NFS_ISV3(dvp);
 
 
-       if (vp->v_usecount > 1) {
-               if (!np->n_sillyrename)
-                       error = nfs_sillyrename(dvp, vp, cnp);
-       } else {
+#ifndef DIAGNOSTIC
+       if ((cnp->cn_flags & HASBUF) == 0)
+               panic("nfs_remove: no name");
+       if (vp->v_usecount < 1)
+               panic("nfs_remove: bad v_usecount");
+#endif
+       if (vp->v_usecount == 1 || (np->n_sillyrename &&
+           VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 &&
+           vattr.va_nlink > 1)) {
                /*
                 * Purge the name cache so that the chance of a lookup for
                 * the name succeeding while the remove is in progress is
                /*
                 * Purge the name cache so that the chance of a lookup for
                 * the name succeeding while the remove is in progress is
@@ -1103,23 +1346,14 @@ nfs_remove(ap)
                 */
                cache_purge(vp);
                /*
                 */
                cache_purge(vp);
                /*
-                * Throw away biocache buffers. Mainly to avoid
-                * unnecessary delayed writes.
+                * throw away biocache buffers, mainly to avoid
+                * unnecessary delayed writes later.
                 */
                error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
                 */
                error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
-               if (error == EINTR)
-                       return (error);
                /* Do the rpc */
                /* Do the rpc */
-               nfsstats.rpccnt[NFSPROC_REMOVE]++;
-               nfsm_reqhead(dvp, NFSPROC_REMOVE,
-                       NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
-               nfsm_fhtom(dvp);
-               nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
-               nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred);
-               nfsm_reqdone;
-               FREE(cnp->cn_pnbuf, M_NAMEI);
-               VTONFS(dvp)->n_flag |= NMODIFIED;
-               VTONFS(dvp)->n_attrstamp = 0;
+               if (error != EINTR)
+                       error = nfs_removerpc(dvp, cnp->cn_nameptr,
+                               cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc);
                /*
                 * Kludge City: If the first reply to the remove rpc is lost..
                 *   the reply to the retransmitted request will be ENOENT
                /*
                 * Kludge City: If the first reply to the remove rpc is lost..
                 *   the reply to the retransmitted request will be ENOENT
@@ -1128,7 +1362,9 @@ nfs_remove(ap)
                 */
                if (error == ENOENT)
                        error = 0;
                 */
                if (error == ENOENT)
                        error = 0;
-       }
+       } else if (!np->n_sillyrename)
+               error = nfs_sillyrename(dvp, vp, cnp);
+       FREE(cnp->cn_pnbuf, M_NAMEI);
        np->n_attrstamp = 0;
        vrele(dvp);
        vrele(vp);
        np->n_attrstamp = 0;
        vrele(dvp);
        vrele(vp);
@@ -1142,22 +1378,42 @@ int
 nfs_removeit(sp)
        register struct sillyrename *sp;
 {
 nfs_removeit(sp)
        register struct sillyrename *sp;
 {
+
+       return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred,
+               (struct proc *)0));
+}
+
+/*
+ * Nfs remove rpc, called from nfs_remove() and nfs_removeit().
+ */
+int
+nfs_removerpc(dvp, name, namelen, cred, proc)
+       register struct vnode *dvp;
+       char *name;
+       int namelen;
+       struct ucred *cred;
+       struct proc *proc;
+{
        register u_long *tl;
        register caddr_t cp;
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int error = 0;
+       register long t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int error = 0, wccflag = NFSV3_WCCRATTR;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int v3 = NFS_ISV3(dvp);
 
        nfsstats.rpccnt[NFSPROC_REMOVE]++;
 
        nfsstats.rpccnt[NFSPROC_REMOVE]++;
-       nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE,
-               NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen));
-       nfsm_fhtom(sp->s_dvp);
-       nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
-       nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred);
+       nfsm_reqhead(dvp, NFSPROC_REMOVE,
+               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
+       nfsm_fhtom(dvp, v3);
+       nfsm_strtom(name, namelen, NFS_MAXNAMLEN);
+       nfsm_request(dvp, NFSPROC_REMOVE, proc, cred);
+       if (v3)
+               nfsm_wcc_data(dvp, wccflag);
        nfsm_reqdone;
        nfsm_reqdone;
-       VTONFS(sp->s_dvp)->n_flag |= NMODIFIED;
-       VTONFS(sp->s_dvp)->n_attrstamp = 0;
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        return (error);
 }
 
        return (error);
 }
 
@@ -1181,13 +1437,13 @@ nfs_rename(ap)
        register struct vnode *tdvp = ap->a_tdvp;
        register struct componentname *tcnp = ap->a_tcnp;
        register struct componentname *fcnp = ap->a_fcnp;
        register struct vnode *tdvp = ap->a_tdvp;
        register struct componentname *tcnp = ap->a_tcnp;
        register struct componentname *fcnp = ap->a_fcnp;
-       register u_long *tl;
-       register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int error = 0;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int error;
 
 
+#ifndef DIAGNOSTIC
+       if ((tcnp->cn_flags & HASBUF) == 0 ||
+           (fcnp->cn_flags & HASBUF) == 0)
+               panic("nfs_rename: no name");
+#endif
        /* Check for cross-device rename */
        if ((fvp->v_mount != tdvp->v_mount) ||
            (tvp && (fvp->v_mount != tvp->v_mount))) {
        /* Check for cross-device rename */
        if ((fvp->v_mount != tdvp->v_mount) ||
            (tvp && (fvp->v_mount != tvp->v_mount))) {
@@ -1195,21 +1451,20 @@ nfs_rename(ap)
                goto out;
        }
 
                goto out;
        }
 
+       /*
+        * If the tvp exists and is in use, sillyrename it before doing the
+        * rename of the new file over it.
+        */
+       if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename &&
+               !nfs_sillyrename(tdvp, tvp, tcnp)) {
+               vrele(tvp);
+               tvp = NULL;
+       }
+
+       error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
+               tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
+               tcnp->cn_proc);
 
 
-       nfsstats.rpccnt[NFSPROC_RENAME]++;
-       nfsm_reqhead(fdvp, NFSPROC_RENAME,
-               (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+
-               nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/
-       nfsm_fhtom(fdvp);
-       nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_fhtom(tdvp);
-       nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred);
-       nfsm_reqdone;
-       VTONFS(fdvp)->n_flag |= NMODIFIED;
-       VTONFS(fdvp)->n_attrstamp = 0;
-       VTONFS(tdvp)->n_flag |= NMODIFIED;
-       VTONFS(tdvp)->n_attrstamp = 0;
        if (fvp->v_type == VDIR) {
                if (tvp != NULL && tvp->v_type == VDIR)
                        cache_purge(tdvp);
        if (fvp->v_type == VDIR) {
                if (tvp != NULL && tvp->v_type == VDIR)
                        cache_purge(tdvp);
@@ -1240,27 +1495,53 @@ nfs_renameit(sdvp, scnp, sp)
        struct vnode *sdvp;
        struct componentname *scnp;
        register struct sillyrename *sp;
        struct vnode *sdvp;
        struct componentname *scnp;
        register struct sillyrename *sp;
+{
+       return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen,
+               sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_proc));
+}
+
+/*
+ * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit().
+ */
+int
+nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
+       register struct vnode *fdvp;
+       char *fnameptr;
+       int fnamelen;
+       register struct vnode *tdvp;
+       char *tnameptr;
+       int tnamelen;
+       struct ucred *cred;
+       struct proc *proc;
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int error = 0;
+       register long t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int v3 = NFS_ISV3(fdvp);
 
        nfsstats.rpccnt[NFSPROC_RENAME]++;
 
        nfsstats.rpccnt[NFSPROC_RENAME]++;
-       nfsm_reqhead(sdvp, NFSPROC_RENAME,
-               (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+
-               nfsm_rndup(sp->s_namlen));
-       nfsm_fhtom(sdvp);
-       nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_fhtom(sdvp);
-       nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN);
-       nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred);
+       nfsm_reqhead(fdvp, NFSPROC_RENAME,
+               (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
+               nfsm_rndup(tnamelen));
+       nfsm_fhtom(fdvp, v3);
+       nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN);
+       nfsm_fhtom(tdvp, v3);
+       nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN);
+       nfsm_request(fdvp, NFSPROC_RENAME, proc, cred);
+       if (v3) {
+               nfsm_wcc_data(fdvp, fwccflag);
+               nfsm_wcc_data(tdvp, twccflag);
+       }
        nfsm_reqdone;
        nfsm_reqdone;
-       FREE(scnp->cn_pnbuf, M_NAMEI);
-       VTONFS(sdvp)->n_flag |= NMODIFIED;
-       VTONFS(sdvp)->n_attrstamp = 0;
+       VTONFS(fdvp)->n_flag |= NMODIFIED;
+       VTONFS(tdvp)->n_flag |= NMODIFIED;
+       if (!fwccflag)
+               VTONFS(fdvp)->n_attrstamp = 0;
+       if (!twccflag)
+               VTONFS(tdvp)->n_attrstamp = 0;
        return (error);
 }
 
        return (error);
 }
 
@@ -1280,14 +1561,18 @@ nfs_link(ap)
        register struct componentname *cnp = ap->a_cnp;
        register u_long *tl;
        register caddr_t cp;
        register struct componentname *cnp = ap->a_cnp;
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int error = 0;
+       register long t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int v3 = NFS_ISV3(vp);
 
 
-       if (tdvp->v_mount != vp->v_mount) {
-               VOP_ABORTOP(tdvp, cnp);
-               vput(tdvp);
+       if (vp->v_mount != tdvp->v_mount) {
+               /*VOP_ABORTOP(vp, cnp);*/
+               if (tdvp == vp)
+                       vrele(tdvp);
+               else
+                       vput(tdvp);
                return (EXDEV);
        }
 
                return (EXDEV);
        }
 
@@ -1300,17 +1585,23 @@ nfs_link(ap)
 
        nfsstats.rpccnt[NFSPROC_LINK]++;
        nfsm_reqhead(vp, NFSPROC_LINK,
 
        nfsstats.rpccnt[NFSPROC_LINK]++;
        nfsm_reqhead(vp, NFSPROC_LINK,
-               NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
-       nfsm_fhtom(vp);
-       nfsm_fhtom(tdvp);
+               NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       nfsm_fhtom(vp, v3);
+       nfsm_fhtom(tdvp, v3);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred);
+       if (v3) {
+               nfsm_postop_attr(vp, attrflag);
+               nfsm_wcc_data(tdvp, wccflag);
+       }
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
-       VTONFS(vp)->n_attrstamp = 0;
-       VTONFS(vp)->n_flag |= NMODIFIED;
-       VTONFS(tdvp)->n_attrstamp = 0;
-       vput(tdvp);
+       VTONFS(tdvp)->n_flag |= NMODIFIED;
+       if (!attrflag)
+               VTONFS(vp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(tdvp)->n_attrstamp = 0;
+       vrele(tdvp);
        /*
         * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
         */
        /*
         * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
         */
@@ -1322,7 +1613,6 @@ nfs_link(ap)
 /*
  * nfs symbolic link create call
  */
 /*
  * nfs symbolic link create call
  */
-/* start here */
 int
 nfs_symlink(ap)
        struct vop_symlink_args /* {
 int
 nfs_symlink(ap)
        struct vop_symlink_args /* {
@@ -1337,42 +1627,50 @@ nfs_symlink(ap)
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
        register struct nfsv2_sattr *sp;
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
        register struct nfsv2_sattr *sp;
+       register struct nfsv3_sattr *sp3;
        register u_long *tl;
        register caddr_t cp;
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int slen, error = 0, isnq;
+       register long t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       struct vnode *newvp = (struct vnode *)0;
+       int v3 = NFS_ISV3(dvp);
 
        nfsstats.rpccnt[NFSPROC_SYMLINK]++;
        slen = strlen(ap->a_target);
 
        nfsstats.rpccnt[NFSPROC_SYMLINK]++;
        slen = strlen(ap->a_target);
-       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
-       nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+
-           nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq));
-       nfsm_fhtom(dvp);
+       nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED +
+           nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+       if (v3) {
+               nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
+               nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid,
+                       cnp->cn_cred->cr_gid);
+       }
        nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
        nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
-       sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode);
-       sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
-       sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
-       if (isnq) {
-               quad_t qval = -1;
-
-               txdr_hyper(&qval, &sp->sa_nqsize);
-               sp->sa_nqflags = 0;
-               txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
-               txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
-       } else {
-               sp->sa_nfssize = -1;
-               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
-               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+       if (!v3) {
+               nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode);
+               sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+               sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
+               sp->sa_size = -1;
+               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
+               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
        }
        nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
        }
        nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
+       if (v3) {
+               if (!error)
+                       nfsm_mtofh(dvp, newvp, v3, gotvp);
+               nfsm_wcc_data(dvp, wccflag);
+       }
        nfsm_reqdone;
        nfsm_reqdone;
+       if (newvp)
+               vrele(newvp);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
-       VTONFS(dvp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        vrele(dvp);
        /*
         * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
        vrele(dvp);
        /*
         * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
@@ -1397,16 +1695,21 @@ nfs_mkdir(ap)
        register struct vnode *dvp = ap->a_dvp;
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
        register struct vnode *dvp = ap->a_dvp;
        register struct vattr *vap = ap->a_vap;
        register struct componentname *cnp = ap->a_cnp;
-       register struct vnode **vpp = ap->a_vpp;
        register struct nfsv2_sattr *sp;
        register struct nfsv2_sattr *sp;
+       register struct nfsv3_sattr *sp3;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        register int len;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        register int len;
+       struct nfsnode *np = (struct nfsnode *)0;
+       struct vnode *newvp = (struct vnode *)0;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0, firsttry = 1, isnq;
+       nfsfh_t *fhp;
+       int error = 0, wccflag = NFSV3_WCCRATTR, attrflag;
+       int fhsize, gotvp = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct vattr vattr;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct vattr vattr;
+       int v3 = NFS_ISV3(dvp);
 
        if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
                VOP_ABORTOP(dvp, cnp);
 
        if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
                VOP_ABORTOP(dvp, cnp);
@@ -1414,56 +1717,54 @@ nfs_mkdir(ap)
                return (error);
        }
        len = cnp->cn_namelen;
                return (error);
        }
        len = cnp->cn_namelen;
-       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
        nfsstats.rpccnt[NFSPROC_MKDIR]++;
        nfsm_reqhead(dvp, NFSPROC_MKDIR,
        nfsstats.rpccnt[NFSPROC_MKDIR]++;
        nfsm_reqhead(dvp, NFSPROC_MKDIR,
-         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq));
-       nfsm_fhtom(dvp);
+         NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq));
-       sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode);
-       sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
-       sp->sa_gid = txdr_unsigned(vattr.va_gid);
-       if (isnq) {
-               quad_t qval = -1;
-
-               txdr_hyper(&qval, &sp->sa_nqsize);
-               sp->sa_nqflags = 0;
-               txdr_nqtime(&vap->va_atime, &sp->sa_nqatime);
-               txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime);
+       if (v3) {
+               nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
+               nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
        } else {
        } else {
-               sp->sa_nfssize = -1;
-               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
-               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+               nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+               sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode);
+               sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
+               sp->sa_gid = txdr_unsigned(vattr.va_gid);
+               sp->sa_size = -1;
+               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
+               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
        }
        nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
        }
        nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
-       nfsm_mtofh(dvp, *vpp);
+       if (!error)
+               nfsm_mtofh(dvp, newvp, v3, gotvp);
+       if (v3)
+               nfsm_wcc_data(dvp, wccflag);
        nfsm_reqdone;
        VTONFS(dvp)->n_flag |= NMODIFIED;
        nfsm_reqdone;
        VTONFS(dvp)->n_flag |= NMODIFIED;
-       VTONFS(dvp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        /*
         * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
         * if we can succeed in looking up the directory.
        /*
         * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
         * if we can succeed in looking up the directory.
-        * "firsttry" is necessary since the macros may "goto nfsmout" which
-        * is above the if on errors. (Ugh)
         */
         */
-       if (error == EEXIST && firsttry) {
-               firsttry = 0;
-               error = 0;
-               nfsstats.rpccnt[NFSPROC_LOOKUP]++;
-               *vpp = NULL;
-               nfsm_reqhead(dvp, NFSPROC_LOOKUP,
-                   NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
-               nfsm_fhtom(dvp);
-               nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
-               nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
-               nfsm_mtofh(dvp, *vpp);
-               if ((*vpp)->v_type != VDIR) {
-                       vput(*vpp);
-                       error = EEXIST;
+       if (error == EEXIST || (!error && !gotvp)) {
+               if (newvp) {
+                       vrele(newvp);
+                       newvp = (struct vnode *)0;
+               }
+               error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred,
+                       cnp->cn_proc, &np);
+               if (!error) {
+                       newvp = NFSTOV(np);
+                       if (newvp->v_type != VDIR)
+                               error = EEXIST;
                }
                }
-               m_freem(mrep);
        }
        }
+       if (error) {
+               if (newvp)
+                       vrele(newvp);
+       } else
+               *ap->a_vpp = newvp;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        vrele(dvp);
        return (error);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        vrele(dvp);
        return (error);
@@ -1485,10 +1786,11 @@ nfs_rmdir(ap)
        register struct componentname *cnp = ap->a_cnp;
        register u_long *tl;
        register caddr_t cp;
        register struct componentname *cnp = ap->a_cnp;
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
-       caddr_t bpos, dpos;
-       int error = 0;
+       register long t1, t2;
+       caddr_t bpos, dpos, cp2;
+       int error = 0, wccflag = NFSV3_WCCRATTR;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int v3 = NFS_ISV3(dvp);
 
        if (dvp == vp) {
                vrele(dvp);
 
        if (dvp == vp) {
                vrele(dvp);
@@ -1498,14 +1800,17 @@ nfs_rmdir(ap)
        }
        nfsstats.rpccnt[NFSPROC_RMDIR]++;
        nfsm_reqhead(dvp, NFSPROC_RMDIR,
        }
        nfsstats.rpccnt[NFSPROC_RMDIR]++;
        nfsm_reqhead(dvp, NFSPROC_RMDIR,
-               NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen));
-       nfsm_fhtom(dvp);
+               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       nfsm_fhtom(dvp, v3);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred);
+       if (v3)
+               nfsm_wcc_data(dvp, wccflag);
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
-       VTONFS(dvp)->n_attrstamp = 0;
+       if (!wccflag)
+               VTONFS(dvp)->n_attrstamp = 0;
        cache_purge(dvp);
        cache_purge(vp);
        vrele(vp);
        cache_purge(dvp);
        cache_purge(vp);
        vrele(vp);
@@ -1520,9 +1825,6 @@ nfs_rmdir(ap)
 
 /*
  * nfs readdir call
 
 /*
  * nfs readdir call
- * Although cookie is defined as opaque, I translate it to/from net byte
- * order so that it looks more sensible. This appears consistent with the
- * Ultrix implementation of NFS.
  */
 int
 nfs_readdir(ap)
  */
 int
 nfs_readdir(ap)
@@ -1530,9 +1832,6 @@ nfs_readdir(ap)
                struct vnode *a_vp;
                struct uio *a_uio;
                struct ucred *a_cred;
                struct vnode *a_vp;
                struct uio *a_uio;
                struct ucred *a_cred;
-               int *a_eofflag;
-               u_long *a_cookies;
-               int a_ncookies;
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
@@ -1541,22 +1840,15 @@ nfs_readdir(ap)
        int tresid, error;
        struct vattr vattr;
 
        int tresid, error;
        struct vattr vattr;
 
-       /*
-        * We don't allow exporting NFS mounts, and currently local requests
-        * do not need cookies.
-        */
-       if (ap->a_ncookies)
-               panic("nfs_readdir: not hungry");
-
        if (vp->v_type != VDIR)
                return (EPERM);
        /*
         * First, check for hit on the EOF offset cache
         */
        if (vp->v_type != VDIR)
                return (EPERM);
        /*
         * First, check for hit on the EOF offset cache
         */
-       if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset &&
+       if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
            (np->n_flag & NMODIFIED) == 0) {
                if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
            (np->n_flag & NMODIFIED) == 0) {
                if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
-                       if (NQNFS_CKCACHABLE(vp, NQL_READ)) {
+                       if (NQNFS_CKCACHABLE(vp, ND_READ)) {
                                nfsstats.direofcache_hits++;
                                return (0);
                        }
                                nfsstats.direofcache_hits++;
                                return (0);
                        }
@@ -1584,90 +1876,147 @@ nfs_readdir(ap)
  */
 int
 nfs_readdirrpc(vp, uiop, cred)
  */
 int
 nfs_readdirrpc(vp, uiop, cred)
-       register struct vnode *vp;
-       struct uio *uiop;
+       struct vnode *vp;
+       register struct uio *uiop;
        struct ucred *cred;
 {
        struct ucred *cred;
 {
-       register long len;
+       register int len, left;
        register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
        register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
-       long tlen, lastlen;
+       register long t1, t2;
+       register nfsuint64 *cookiep;
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       struct mbuf *md2;
-       caddr_t dpos2;
-       int siz;
-       int more_dirs = 1;
-       u_long off, savoff;
-       struct dirent *savdp;
-       struct nfsmount *nmp;
-       struct nfsnode *np = VTONFS(vp);
-       long tresid;
+       nfsuint64 cookie;
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       struct nfsnode *dnp = VTONFS(vp);
+       nfsfh_t *fhp;
+       u_quad_t frev, fileno;
+       int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1, i;
+       int cachable, attrflag, fhsize;
+       int v3 = NFS_ISV3(vp);
+
+#ifndef nolint
+       dp = (struct dirent *)0;
+#endif
+#ifndef DIAGNOSTIC
+       if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
+               (uiop->uio_resid & (NFS_DIRBLKSIZ - 1)))
+               panic("nfs readdirrpc bad uio");
+#endif
 
 
-       nmp = VFSTONFS(vp->v_mount);
-       tresid = uiop->uio_resid;
        /*
        /*
-        * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
-        * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+        * If there is no cookie, assume end of directory.
+        */
+       cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
+       if (cookiep)
+               cookie = *cookiep;
+       else
+               return (0);
+       /*
+        * Loop around doing readdir rpc's of size nm_readdirsize
+        * truncated to a multiple of DIRBLKSIZ.
         * The stopping criteria is EOF or buffer full.
         */
         * The stopping criteria is EOF or buffer full.
         */
-       while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
+       while (more_dirs && bigenough) {
                nfsstats.rpccnt[NFSPROC_READDIR]++;
                nfsstats.rpccnt[NFSPROC_READDIR]++;
-               nfsm_reqhead(vp, NFSPROC_READDIR,
-                       NFSX_FH + 2 * NFSX_UNSIGNED);
-               nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
-               off = (u_long)uiop->uio_offset;
-               *tl++ = txdr_unsigned(off);
-               *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
-                       nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
+               nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) +
+                       NFSX_READDIR(v3));
+               nfsm_fhtom(vp, v3);
+               if (v3) {
+                       nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
+                       *tl++ = cookie.nfsuquad[0];
+                       *tl++ = cookie.nfsuquad[1];
+                       *tl++ = dnp->n_cookieverf.nfsuquad[0];
+                       *tl++ = dnp->n_cookieverf.nfsuquad[1];
+               } else {
+                       nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       *tl++ = cookie.nfsuquad[0];
+               }
+               *tl = txdr_unsigned(nmp->nm_readdirsize);
                nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
                nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
-               siz = 0;
+               if (v3) {
+                       nfsm_postop_attr(vp, attrflag);
+                       if (!error) {
+                               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+                               dnp->n_cookieverf.nfsuquad[0] = *tl++;
+                               dnp->n_cookieverf.nfsuquad[1] = *tl;
+                       } else {
+                               m_freem(mrep);
+                               goto nfsmout;
+                       }
+               }
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                more_dirs = fxdr_unsigned(int, *tl);
        
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                more_dirs = fxdr_unsigned(int, *tl);
        
-               /* Save the position so that we can do nfsm_mtouio() later */
-               dpos2 = dpos;
-               md2 = md;
-       
                /* loop thru the dir entries, doctoring them to 4bsd form */
                /* loop thru the dir entries, doctoring them to 4bsd form */
-#ifdef lint
-               dp = (struct dirent *)0;
-#endif /* lint */
-               while (more_dirs && siz < uiop->uio_resid) {
-                       savoff = off;           /* Hold onto offset and dp */
-                       savdp = dp;
-                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
-                       dp = (struct dirent *)tl;
-                       dp->d_fileno = fxdr_unsigned(u_long, *tl++);
-                       len = fxdr_unsigned(int, *tl);
+               while (more_dirs && bigenough) {
+                       if (v3) {
+                               nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+                               fxdr_hyper(tl, &fileno);
+                               len = fxdr_unsigned(int, *(tl + 2));
+                       } else {
+                               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+                               fileno = fxdr_unsigned(u_quad_t, *tl++);
+                               len = fxdr_unsigned(int, *tl);
+                       }
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
-                       dp->d_namlen = (u_char)len;
-                       dp->d_type = DT_UNKNOWN;
-                       nfsm_adv(len);          /* Point past name */
                        tlen = nfsm_rndup(len);
                        tlen = nfsm_rndup(len);
-                       /*
-                        * This should not be necessary, but some servers have
-                        * broken XDR such that these bytes are not null filled.
-                        */
-                       if (tlen != len) {
-                               *dpos = '\0';   /* Null-terminate */
-                               nfsm_adv(tlen - len);
-                               len = tlen;
+                       if (tlen == len)
+                               tlen += 4;      /* To ensure null termination */
+                       left = DIRBLKSIZ - blksiz;
+                       if ((tlen + DIRHDSIZ) > left) {
+                               dp->d_reclen += left;
+                               uiop->uio_iov->iov_base += left;
+                               uiop->uio_iov->iov_len -= left;
+                               uiop->uio_offset += left;
+                               uiop->uio_resid -= left;
+                               blksiz = 0;
                        }
                        }
-                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
-                       off = fxdr_unsigned(u_long, *tl);
-                       *tl++ = 0;      /* Ensures null termination of name */
+                       if ((tlen + DIRHDSIZ) > uiop->uio_resid)
+                               bigenough = 0;
+                       if (bigenough) {
+                               dp = (struct dirent *)uiop->uio_iov->iov_base;
+                               dp->d_fileno = (int)fileno;
+                               dp->d_namlen = len;
+                               dp->d_reclen = tlen + DIRHDSIZ;
+                               dp->d_type = DT_UNKNOWN;
+                               blksiz += dp->d_reclen;
+                               if (blksiz == DIRBLKSIZ)
+                                       blksiz = 0;
+                               uiop->uio_offset += DIRHDSIZ;
+                               uiop->uio_resid -= DIRHDSIZ;
+                               uiop->uio_iov->iov_base += DIRHDSIZ;
+                               uiop->uio_iov->iov_len -= DIRHDSIZ;
+                               nfsm_mtouio(uiop, len);
+                               cp = uiop->uio_iov->iov_base;
+                               tlen -= len;
+                               *cp = '\0';     /* null terminate */
+                               uiop->uio_iov->iov_base += tlen;
+                               uiop->uio_iov->iov_len -= tlen;
+                               uiop->uio_offset += tlen;
+                               uiop->uio_resid -= tlen;
+                       } else
+                               nfsm_adv(nfsm_rndup(len));
+                       if (v3) {
+                               nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+                       } else {
+                               nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       }
+                       if (bigenough) {
+                               cookie.nfsuquad[0] = *tl++;
+                               if (v3)
+                                       cookie.nfsuquad[1] = *tl++;
+                       } else if (v3)
+                               tl += 2;
+                       else
+                               tl++;
                        more_dirs = fxdr_unsigned(int, *tl);
                        more_dirs = fxdr_unsigned(int, *tl);
-                       dp->d_reclen = len + 4 * NFSX_UNSIGNED;
-                       siz += dp->d_reclen;
                }
                /*
                 * If at end of rpc data, get the eof boolean
                }
                /*
                 * If at end of rpc data, get the eof boolean
@@ -1675,187 +2024,224 @@ nfs_readdirrpc(vp, uiop, cred)
                if (!more_dirs) {
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
                if (!more_dirs) {
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
-
-                       /*
-                        * If at EOF, cache directory offset
-                        */
-                       if (!more_dirs)
-                               np->n_direofoffset = off;
-               }
-               /*
-                * If there is too much to fit in the data buffer, use savoff and
-                * savdp to trim off the last record.
-                * --> we are not at eof
-                */
-               if (siz > uiop->uio_resid) {
-                       off = savoff;
-                       siz -= dp->d_reclen;
-                       dp = savdp;
-                       more_dirs = 0;  /* Paranoia */
                }
                }
-               if (siz > 0) {
-                       lastlen = dp->d_reclen;
-                       md = md2;
-                       dpos = dpos2;
-                       nfsm_mtouio(uiop, siz);
-                       uiop->uio_offset = (off_t)off;
-               } else
-                       more_dirs = 0;  /* Ugh, never happens, but in case.. */
                m_freem(mrep);
        }
        /*
                m_freem(mrep);
        }
        /*
-        * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
+        * Fill last record, iff any, out to a multiple of DIRBLKSIZ
         * by increasing d_reclen for the last record.
         */
         * by increasing d_reclen for the last record.
         */
-       if (uiop->uio_resid < tresid) {
-               len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
-               if (len > 0) {
-                       dp = (struct dirent *)
-                               (uiop->uio_iov->iov_base - lastlen);
-                       dp->d_reclen += len;
-                       uiop->uio_iov->iov_base += len;
-                       uiop->uio_iov->iov_len -= len;
-                       uiop->uio_resid -= len;
-               }
+       if (blksiz > 0) {
+               left = DIRBLKSIZ - blksiz;
+               dp->d_reclen += left;
+               uiop->uio_iov->iov_base += left;
+               uiop->uio_iov->iov_len -= left;
+               uiop->uio_offset += left;
+               uiop->uio_resid -= left;
+       }
+
+       /*
+        * We are now either at the end of the directory or have filled the
+        * block.
+        */
+       if (bigenough)
+               dnp->n_direofoffset = uiop->uio_offset;
+       else {
+               if (uiop->uio_resid > 0)
+                       printf("EEK! readdirrpc resid > 0\n");
+               cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
+               *cookiep = cookie;
        }
 nfsmout:
        return (error);
 }
 
 /*
        }
 nfsmout:
        return (error);
 }
 
 /*
- * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc().
+ * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
  */
 int
  */
 int
-nfs_readdirlookrpc(vp, uiop, cred)
+nfs_readdirplusrpc(vp, uiop, cred)
        struct vnode *vp;
        register struct uio *uiop;
        struct ucred *cred;
 {
        struct vnode *vp;
        register struct uio *uiop;
        struct ucred *cred;
 {
-       register int len;
+       register int len, left;
        register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
        register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
-       register long t1;
-       caddr_t bpos, dpos, cp2;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       register long t1, t2;
+       register struct vnode *newvp;
+       register nfsuint64 *cookiep;
+       caddr_t bpos, dpos, cp2, dpossav1, dpossav2;
+       struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2;
        struct nameidata nami, *ndp = &nami;
        struct componentname *cnp = &ndp->ni_cnd;
        struct nameidata nami, *ndp = &nami;
        struct componentname *cnp = &ndp->ni_cnd;
-       u_long off, endoff, fileno;
-       time_t reqtime, ltime;
-       struct nfsmount *nmp;
-       struct nfsnode *np;
-       struct vnode *newvp;
-       nfsv2fh_t *fhp;
-       u_quad_t frev;
-       int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i;
-       int cachable;
-
-       if (uiop->uio_iovcnt != 1)
-               panic("nfs rdirlook");
-       nmp = VFSTONFS(vp->v_mount);
-       tresid = uiop->uio_resid;
+       nfsuint64 cookie;
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       struct nfsnode *dnp = VTONFS(vp), *np;
+       nfsfh_t *fhp;
+       u_quad_t frev, fileno;
+       int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
+       int cachable, attrflag, fhsize;
+
+#ifndef nolint
+       dp = (struct dirent *)0;
+#endif
+#ifndef DIAGNOSTIC
+       if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
+               (uiop->uio_resid & (DIRBLKSIZ - 1)))
+               panic("nfs readdirplusrpc bad uio");
+#endif
        ndp->ni_dvp = vp;
        newvp = NULLVP;
        ndp->ni_dvp = vp;
        newvp = NULLVP;
+
+       /*
+        * If there is no cookie, assume end of directory.
+        */
+       cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
+       if (cookiep)
+               cookie = *cookiep;
+       else
+               return (0);
        /*
        /*
-        * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
-        * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ.
+        * Loop around doing readdir rpc's of size nm_readdirsize
+        * truncated to a multiple of DIRBLKSIZ.
         * The stopping criteria is EOF or buffer full.
         */
         * The stopping criteria is EOF or buffer full.
         */
-       while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
-               nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++;
-               nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK,
-                       NFSX_FH + 3 * NFSX_UNSIGNED);
-               nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
-               off = (u_long)uiop->uio_offset;
-               *tl++ = txdr_unsigned(off);
-               *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
-                       nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
-               if (nmp->nm_flag & NFSMNT_NQLOOKLEASE)
-                       *tl = txdr_unsigned(nmp->nm_leaseterm);
-               else
-                       *tl = 0;
-               reqtime = time.tv_sec;
-               nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred);
-               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+       while (more_dirs && bigenough) {
+               nfsstats.rpccnt[NFSPROC_READDIRPLUS]++;
+               nfsm_reqhead(vp, NFSPROC_READDIRPLUS,
+                       NFSX_FH(1) + 6 * NFSX_UNSIGNED);
+               nfsm_fhtom(vp, 1);
+               nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED);
+               *tl++ = cookie.nfsuquad[0];
+               *tl++ = cookie.nfsuquad[1];
+               *tl++ = dnp->n_cookieverf.nfsuquad[0];
+               *tl++ = dnp->n_cookieverf.nfsuquad[1];
+               *tl++ = txdr_unsigned(nmp->nm_readdirsize);
+               *tl = txdr_unsigned(nmp->nm_rsize);
+               nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred);
+               nfsm_postop_attr(vp, attrflag);
+               if (error) {
+                       m_freem(mrep);
+                       goto nfsmout;
+               }
+               nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+               dnp->n_cookieverf.nfsuquad[0] = *tl++;
+               dnp->n_cookieverf.nfsuquad[1] = *tl++;
                more_dirs = fxdr_unsigned(int, *tl);
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
                more_dirs = fxdr_unsigned(int, *tl);
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
-               bigenough = 1;
                while (more_dirs && bigenough) {
                while (more_dirs && bigenough) {
-                       doit = 1;
-                       nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
-                       if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) {
-                               cachable = fxdr_unsigned(int, *tl++);
-                               ltime = reqtime + fxdr_unsigned(int, *tl++);
-                               fxdr_hyper(tl, &frev);
-                       }
-                       nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
-                       if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
-                               VREF(vp);
-                               newvp = vp;
-                               np = VTONFS(vp);
-                       } else {
-                               if (error = nfs_nget(vp->v_mount, fhp, &np))
-                                       doit = 0;
-                               newvp = NFSTOV(np);
-                       }
-                       if (error = nfs_loadattrcache(&newvp, &md, &dpos,
-                               (struct vattr *)0))
-                               doit = 0;
-                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
-                       fileno = fxdr_unsigned(u_long, *tl++);
-                       len = fxdr_unsigned(int, *tl);
+                       nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+                       fxdr_hyper(tl, &fileno);
+                       len = fxdr_unsigned(int, *(tl + 2));
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
-                       tlen = (len + 4) & ~0x3;
+                       tlen = nfsm_rndup(len);
+                       if (tlen == len)
+                               tlen += 4;      /* To ensure null termination*/
+                       left = DIRBLKSIZ - blksiz;
+                       if ((tlen + DIRHDSIZ) > left) {
+                               dp->d_reclen += left;
+                               uiop->uio_iov->iov_base += left;
+                               uiop->uio_iov->iov_len -= left;
+                               uiop->uio_offset += left;
+                               uiop->uio_resid -= left;
+                               blksiz = 0;
+                       }
                        if ((tlen + DIRHDSIZ) > uiop->uio_resid)
                                bigenough = 0;
                        if ((tlen + DIRHDSIZ) > uiop->uio_resid)
                                bigenough = 0;
-                       if (bigenough && doit) {
+                       if (bigenough) {
                                dp = (struct dirent *)uiop->uio_iov->iov_base;
                                dp = (struct dirent *)uiop->uio_iov->iov_base;
-                               dp->d_fileno = fileno;
+                               dp->d_fileno = (int)fileno;
                                dp->d_namlen = len;
                                dp->d_reclen = tlen + DIRHDSIZ;
                                dp->d_namlen = len;
                                dp->d_reclen = tlen + DIRHDSIZ;
-                               dp->d_type =
-                                   IFTODT(VTTOIF(np->n_vattr.va_type));
+                               dp->d_type = DT_UNKNOWN;
+                               blksiz += dp->d_reclen;
+                               if (blksiz == DIRBLKSIZ)
+                                       blksiz = 0;
+                               uiop->uio_offset += DIRHDSIZ;
                                uiop->uio_resid -= DIRHDSIZ;
                                uiop->uio_iov->iov_base += DIRHDSIZ;
                                uiop->uio_iov->iov_len -= DIRHDSIZ;
                                cnp->cn_nameptr = uiop->uio_iov->iov_base;
                                cnp->cn_namelen = len;
                                uiop->uio_resid -= DIRHDSIZ;
                                uiop->uio_iov->iov_base += DIRHDSIZ;
                                uiop->uio_iov->iov_len -= DIRHDSIZ;
                                cnp->cn_nameptr = uiop->uio_iov->iov_base;
                                cnp->cn_namelen = len;
-                               ndp->ni_vp = newvp;
                                nfsm_mtouio(uiop, len);
                                cp = uiop->uio_iov->iov_base;
                                tlen -= len;
                                nfsm_mtouio(uiop, len);
                                cp = uiop->uio_iov->iov_base;
                                tlen -= len;
-                               for (i = 0; i < tlen; i++)
-                                       *cp++ = '\0';
+                               *cp = '\0';
                                uiop->uio_iov->iov_base += tlen;
                                uiop->uio_iov->iov_len -= tlen;
                                uiop->uio_iov->iov_base += tlen;
                                uiop->uio_iov->iov_len -= tlen;
+                               uiop->uio_offset += tlen;
                                uiop->uio_resid -= tlen;
                                uiop->uio_resid -= tlen;
+                       } else
+                               nfsm_adv(nfsm_rndup(len));
+                       nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+                       if (bigenough) {
+                               cookie.nfsuquad[0] = *tl++;
+                               cookie.nfsuquad[1] = *tl++;
+                       } else
+                               tl += 2;
+
+                       /*
+                        * Since the attributes are before the file handle
+                        * (sigh), we must skip over the attributes and then
+                        * come back and get them.
+                        */
+                       attrflag = fxdr_unsigned(int, *tl);
+                       if (attrflag) {
+                           dpossav1 = dpos;
+                           mdsav1 = md;
+                           nfsm_adv(NFSX_V3FATTR);
+                           nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+                           doit = fxdr_unsigned(int, *tl);
+                           if (doit) {
+                               nfsm_getfh(fhp, fhsize, 1);
+                               if (NFS_CMPFH(dnp, fhp, fhsize)) {
+                                   VREF(vp);
+                                   newvp = vp;
+                                   np = dnp;
+                               } else {
+                                   if (error = nfs_nget(vp->v_mount, fhp,
+                                       fhsize, &np))
+                                       doit = 0;
+                                   else
+                                       newvp = NFSTOV(np);
+                               }
+                           }
+                           if (doit) {
+                               dpossav2 = dpos;
+                               dpos = dpossav1;
+                               mdsav2 = md;
+                               md = mdsav1;
+                               nfsm_loadattr(newvp, (struct vattr *)0);
+                               dpos = dpossav2;
+                               md = mdsav2;
+                               dp->d_type =
+                                   IFTODT(VTTOIF(np->n_vattr.va_type));
+                               ndp->ni_vp = newvp;
                                cnp->cn_hash = 0;
                                cnp->cn_hash = 0;
-                               for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++)
-                                       cnp->cn_hash += (unsigned char)*cp * i;
-                               if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
-                                       ltime > time.tv_sec)
-                                       nqnfs_clientlease(nmp, np, NQL_READ,
-                                               cachable, ltime, frev);
+                               for (cp = cnp->cn_nameptr, i = 1; i <= len;
+                                   i++, cp++)
+                                   cnp->cn_hash += (unsigned char)*cp * i;
                                if (cnp->cn_namelen <= NCHNAMLEN)
                                    cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
                                if (cnp->cn_namelen <= NCHNAMLEN)
                                    cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
+                           }
                        } else {
                        } else {
-                               nfsm_adv(nfsm_rndup(len));
+                           /* Just skip over the file handle */
+                           nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+                           i = fxdr_unsigned(int, *tl);
+                           nfsm_adv(nfsm_rndup(i));
                        }
                        if (newvp != NULLVP) {
                        }
                        if (newvp != NULLVP) {
-                               vrele(newvp);
-                               newvp = NULLVP;
+                           vrele(newvp);
+                           newvp = NULLVP;
                        }
                        }
-                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
-                       if (bigenough)
-                               endoff = off = fxdr_unsigned(u_long, *tl++);
-                       else
-                               endoff = fxdr_unsigned(u_long, *tl++);
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = fxdr_unsigned(int, *tl);
                }
                /*
                        more_dirs = fxdr_unsigned(int, *tl);
                }
                /*
@@ -1864,31 +2250,33 @@ nfs_readdirlookrpc(vp, uiop, cred)
                if (!more_dirs) {
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
                if (!more_dirs) {
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
-
-                       /*
-                        * If at EOF, cache directory offset
-                        */
-                       if (!more_dirs)
-                               VTONFS(vp)->n_direofoffset = endoff;
                }
                }
-               if (uiop->uio_resid < tresid)
-                       uiop->uio_offset = (off_t)off;
-               else
-                       more_dirs = 0;
                m_freem(mrep);
        }
        /*
         * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
         * by increasing d_reclen for the last record.
         */
                m_freem(mrep);
        }
        /*
         * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
         * by increasing d_reclen for the last record.
         */
-       if (uiop->uio_resid < tresid) {
-               len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
-               if (len > 0) {
-                       dp->d_reclen += len;
-                       uiop->uio_iov->iov_base += len;
-                       uiop->uio_iov->iov_len -= len;
-                       uiop->uio_resid -= len;
-               }
+       if (blksiz > 0) {
+               left = DIRBLKSIZ - blksiz;
+               dp->d_reclen += left;
+               uiop->uio_iov->iov_base += left;
+               uiop->uio_iov->iov_len -= left;
+               uiop->uio_offset += left;
+               uiop->uio_resid -= left;
+       }
+
+       /*
+        * We are now either at the end of the directory or have filled the
+        * block.
+        */
+       if (bigenough)
+               dnp->n_direofoffset = uiop->uio_offset;
+       else {
+               if (uiop->uio_resid > 0)
+                       printf("EEK! readdirplusrpc resid > 0\n");
+               cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
+               *cookiep = cookie;
        }
 nfsmout:
        if (newvp != NULLVP)
        }
 nfsmout:
        if (newvp != NULLVP)
@@ -1910,19 +2298,19 @@ nfs_sillyrename(dvp, vp, cnp)
        struct vnode *dvp, *vp;
        struct componentname *cnp;
 {
        struct vnode *dvp, *vp;
        struct componentname *cnp;
 {
-       register struct nfsnode *np;
        register struct sillyrename *sp;
        register struct sillyrename *sp;
+       struct nfsnode *np;
        int error;
        short pid;
 
        cache_purge(dvp);
        np = VTONFS(vp);
        int error;
        short pid;
 
        cache_purge(dvp);
        np = VTONFS(vp);
-#ifdef SILLYSEPARATE
+#ifndef DIAGNOSTIC
+       if (vp->v_type == VDIR)
+               panic("nfs: sillyrename dir");
+#endif
        MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
                M_NFSREQ, M_WAITOK);
        MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
                M_NFSREQ, M_WAITOK);
-#else
-       sp = &np->n_silly;
-#endif
        sp->s_cred = crdup(cnp->cn_cred);
        sp->s_dvp = dvp;
        VREF(dvp);
        sp->s_cred = crdup(cnp->cn_cred);
        sp->s_dvp = dvp;
        VREF(dvp);
@@ -1937,7 +2325,8 @@ nfs_sillyrename(dvp, vp, cnp)
        sp->s_name[5] = hextoasc[(pid >> 12) & 0xf];
 
        /* Try lookitups until we get one that isn't there */
        sp->s_name[5] = hextoasc[(pid >> 12) & 0xf];
 
        /* Try lookitups until we get one that isn't there */
-       while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) {
+       while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
+               cnp->cn_proc, (struct nfsnode **)0) == 0) {
                sp->s_name[4]++;
                if (sp->s_name[4] > 'z') {
                        error = EINVAL;
                sp->s_name[4]++;
                if (sp->s_name[4] > 'z') {
                        error = EINVAL;
@@ -1946,55 +2335,133 @@ nfs_sillyrename(dvp, vp, cnp)
        }
        if (error = nfs_renameit(dvp, cnp, sp))
                goto bad;
        }
        if (error = nfs_renameit(dvp, cnp, sp))
                goto bad;
-       nfs_lookitup(sp, &np->n_fh, cnp->cn_proc);
+       error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
+               cnp->cn_proc, &np);
        np->n_sillyrename = sp;
        return (0);
 bad:
        vrele(sp->s_dvp);
        crfree(sp->s_cred);
        np->n_sillyrename = sp;
        return (0);
 bad:
        vrele(sp->s_dvp);
        crfree(sp->s_cred);
-#ifdef SILLYSEPARATE
        free((caddr_t)sp, M_NFSREQ);
        free((caddr_t)sp, M_NFSREQ);
-#endif
        return (error);
 }
 
 /*
        return (error);
 }
 
 /*
- * Look up a file name for silly rename stuff.
- * Just like nfs_lookup() except that it doesn't load returned values
- * into the nfsnode table.
- * If fhp != NULL it copies the returned file handle out
+ * Look up a file name and optionally either update the file handle or
+ * allocate an nfsnode, depending on the value of npp.
+ * npp == NULL --> just do the lookup
+ * *npp == NULL --> allocate a new nfsnode and make sure attributes are
+ *                     handled too
+ * *npp != NULL --> update the file handle in the vnode
  */
 int
  */
 int
-nfs_lookitup(sp, fhp, procp)
-       register struct sillyrename *sp;
-       nfsv2fh_t *fhp;
+nfs_lookitup(dvp, name, len, cred, procp, npp)
+       register struct vnode *dvp;
+       char *name;
+       int len;
+       struct ucred *cred;
        struct proc *procp;
        struct proc *procp;
+       struct nfsnode **npp;
 {
 {
-       register struct vnode *vp = sp->s_dvp;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
+       struct vnode *newvp = (struct vnode *)0;
+       struct nfsnode *np, *dnp = VTONFS(dvp);
        caddr_t bpos, dpos, cp2;
        caddr_t bpos, dpos, cp2;
-       int error = 0, isnq;
+       int error = 0, fhlen, attrflag;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       long len;
+       nfsfh_t *nfhp;
+       int v3 = NFS_ISV3(dvp);
 
 
-       isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
        nfsstats.rpccnt[NFSPROC_LOOKUP]++;
        nfsstats.rpccnt[NFSPROC_LOOKUP]++;
-       len = sp->s_namlen;
-       nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
-       if (isnq) {
-               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
-               *tl = 0;
+       nfsm_reqhead(dvp, NFSPROC_LOOKUP,
+               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       nfsm_fhtom(dvp, v3);
+       nfsm_strtom(name, len, NFS_MAXNAMLEN);
+       nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred);
+       if (npp && !error) {
+               nfsm_getfh(nfhp, fhlen, v3);
+               if (*npp) {
+                   np = *npp;
+                   if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) {
+                       free((caddr_t)np->n_fhp, M_NFSBIGFH);
+                       np->n_fhp = &np->n_fh;
+                   } else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH)
+                       np->n_fhp =(nfsfh_t *)malloc(fhlen,M_NFSBIGFH,M_WAITOK);
+                   bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen);
+                   np->n_fhsize = fhlen;
+                   newvp = NFSTOV(np);
+               } else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
+                   VREF(dvp);
+                   newvp = dvp;
+               } else {
+                   error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np);
+                   if (error) {
+                       m_freem(mrep);
+                       return (error);
+                   }
+                   newvp = NFSTOV(np);
+               }
+               if (v3) {
+                       nfsm_postop_attr(newvp, attrflag);
+                       if (!attrflag && *npp == NULL) {
+                               m_freem(mrep);
+                               vrele(newvp);
+                               return (ENOENT);
+                       }
+               } else
+                       nfsm_loadattr(newvp, (struct vattr *)0);
        }
        }
-       nfsm_fhtom(vp);
-       nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN);
-       nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred);
-       if (fhp != NULL) {
-               if (isnq)
-                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
-               nfsm_dissect(cp, caddr_t, NFSX_FH);
-               bcopy(cp, (caddr_t)fhp, NFSX_FH);
+       nfsm_reqdone;
+       if (npp && *npp == NULL) {
+               if (error) {
+                       if (newvp)
+                               vrele(newvp);
+               } else
+                       *npp = np;
+       }
+       return (error);
+}
+
+/*
+ * Nfs Version 3 commit rpc
+ */
+int
+nfs_commit(vp, offset, cnt, cred, procp)
+       register struct vnode *vp;
+       u_quad_t offset;
+       int cnt;
+       struct ucred *cred;
+       struct proc *procp;
+{
+       register caddr_t cp;
+       register u_long *tl;
+       register int t1, t2;
+       register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       caddr_t bpos, dpos, cp2;
+       int error = 0, wccflag = NFSV3_WCCRATTR;
+       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       
+       if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0)
+               return (0);
+       nfsstats.rpccnt[NFSPROC_COMMIT]++;
+       nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1));
+       nfsm_fhtom(vp, 1);
+       nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
+       txdr_hyper(&offset, tl);
+       tl += 2;
+       *tl = txdr_unsigned(cnt);
+       nfsm_request(vp, NFSPROC_COMMIT, procp, cred);
+       nfsm_wcc_data(vp, wccflag);
+       if (!error) {
+               nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF);
+               if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
+                       NFSX_V3WRITEVERF)) {
+                       bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
+                               NFSX_V3WRITEVERF);
+                       error = NFSERR_STALEWRITEVERF;
+               }
        }
        nfsm_reqdone;
        return (error);
        }
        nfsm_reqdone;
        return (error);
@@ -2003,9 +2470,7 @@ nfs_lookitup(sp, fhp, procp)
 /*
  * Kludge City..
  * - make nfs_bmap() essentially a no-op that does no translation
 /*
  * Kludge City..
  * - make nfs_bmap() essentially a no-op that does no translation
- * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc
- *   after mapping the physical addresses into Kernel Virtual space in the
- *   nfsiobuf area.
+ * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc
  *   (Maybe I could use the process's page mapping, but I was concerned that
  *    Kernel Write might not be enabled and also figured copyout() would do
  *    a lot more work than bcopy() and also it currently happens in the
  *   (Maybe I could use the process's page mapping, but I was concerned that
  *    Kernel Write might not be enabled and also figured copyout() would do
  *    a lot more work than bcopy() and also it currently happens in the
@@ -2027,8 +2492,6 @@ nfs_bmap(ap)
                *ap->a_vpp = vp;
        if (ap->a_bnp != NULL)
                *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
                *ap->a_vpp = vp;
        if (ap->a_bnp != NULL)
                *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
-       if (ap->a_runp != NULL)
-               *ap->a_runp = 0;
        return (0);
 }
 
        return (0);
 }
 
@@ -2088,9 +2551,7 @@ nfs_mmap(ap)
 }
 
 /*
 }
 
 /*
- * Flush all the blocks associated with a vnode.
- *     Walk through the buffer pool and push any dirty pages
- *     associated with the vnode.
+ * fsync vnode op. Just call nfs_flush() with commit == 1.
  */
 /* ARGSUSED */
 int
  */
 /* ARGSUSED */
 int
@@ -2103,29 +2564,122 @@ nfs_fsync(ap)
                struct proc * a_p;
        } */ *ap;
 {
                struct proc * a_p;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
+
+       return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));
+}
+
+/*
+ * Flush all the blocks associated with a vnode.
+ *     Walk through the buffer pool and push any dirty pages
+ *     associated with the vnode.
+ */
+int
+nfs_flush(vp, cred, waitfor, p, commit)
+       register struct vnode *vp;
+       struct ucred *cred;
+       int waitfor;
+       struct proc *p;
+       int commit;
+{
        register struct nfsnode *np = VTONFS(vp);
        register struct buf *bp;
        register struct nfsnode *np = VTONFS(vp);
        register struct buf *bp;
+       register int i;
        struct buf *nbp;
        struct buf *nbp;
-       struct nfsmount *nmp;
-       int s, error = 0, slptimeo = 0, slpflag = 0;
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos;
+       int passone = 1;
+       u_quad_t off = (u_quad_t)-1, endoff = 0, toff;
+#ifndef NFS_COMMITBVECSIZ
+#define NFS_COMMITBVECSIZ      20
+#endif
+       struct buf *bvec[NFS_COMMITBVECSIZ];
 
 
-       nmp = VFSTONFS(vp->v_mount);
        if (nmp->nm_flag & NFSMNT_INT)
                slpflag = PCATCH;
        if (nmp->nm_flag & NFSMNT_INT)
                slpflag = PCATCH;
+       if (!commit)
+               passone = 0;
+       /*
+        * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the
+        * server, but nas not been committed to stable storage on the server
+        * yet. On the first pass, the byte range is worked out and the commit
+        * rpc is done. On the second pass, nfs_writebp() is called to do the
+        * job.
+        */
+again:
+       bvecpos = 0;
+       if (NFS_ISV3(vp) && commit) {
+               s = splbio();
+               for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+                       nbp = bp->b_vnbufs.le_next;
+                       if (bvecpos >= NFS_COMMITBVECSIZ)
+                               break;
+                       if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
+                               != (B_DELWRI | B_NEEDCOMMIT))
+                               continue;
+                       bremfree(bp);
+                       bp->b_flags |= (B_BUSY | B_WRITEINPROG);
+                       /*
+                        * A list of these buffers is kept so that the
+                        * second loop knows which buffers have actually
+                        * been committed. This is necessary, since there
+                        * may be a race between the commit rpc and new
+                        * uncommitted writes on the file.
+                        */
+                       bvec[bvecpos++] = bp;
+                       toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
+                               bp->b_dirtyoff;
+                       if (toff < off)
+                               off = toff;
+                       toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
+                       if (toff > endoff)
+                               endoff = toff;
+               }
+               splx(s);
+       }
+       if (bvecpos > 0) {
+               /*
+                * Commit data on the server, as required.
+                */
+               retv = nfs_commit(vp, off, (int)(endoff - off), cred, p);
+               if (retv == NFSERR_STALEWRITEVERF)
+                       nfs_clearcommit(vp->v_mount);
+               /*
+                * Now, either mark the blocks I/O done or mark the
+                * blocks dirty, depending on whether the commit
+                * succeeded.
+                */
+               for (i = 0; i < bvecpos; i++) {
+                       bp = bvec[i];
+                       bp->b_flags &= ~(B_NEEDCOMMIT | B_WRITEINPROG);
+                       if (retv) {
+                           brelse(bp);
+                       } else {
+                           vp->v_numoutput++;
+                           bp->b_flags |= B_ASYNC;
+                           bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
+                           bp->b_dirtyoff = bp->b_dirtyend = 0;
+                           reassignbuf(bp, vp);
+                           biodone(bp);
+                       }
+               }
+       }
+
+       /*
+        * Start/do any write(s) that are required.
+        */
 loop:
        s = splbio();
        for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
                nbp = bp->b_vnbufs.le_next;
                if (bp->b_flags & B_BUSY) {
 loop:
        s = splbio();
        for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
                nbp = bp->b_vnbufs.le_next;
                if (bp->b_flags & B_BUSY) {
-                       if (ap->a_waitfor != MNT_WAIT)
+                       if (waitfor != MNT_WAIT || passone)
                                continue;
                        bp->b_flags |= B_WANTED;
                        error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
                                "nfsfsync", slptimeo);
                        splx(s);
                        if (error) {
                                continue;
                        bp->b_flags |= B_WANTED;
                        error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
                                "nfsfsync", slptimeo);
                        splx(s);
                        if (error) {
-                           if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+                           if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
                                return (EINTR);
                            if (slpflag == PCATCH) {
                                slpflag = 0;
                                return (EINTR);
                            if (slpflag == PCATCH) {
                                slpflag = 0;
@@ -2136,21 +2690,29 @@ loop:
                }
                if ((bp->b_flags & B_DELWRI) == 0)
                        panic("nfs_fsync: not dirty");
                }
                if ((bp->b_flags & B_DELWRI) == 0)
                        panic("nfs_fsync: not dirty");
+               if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT))
+                       continue;
                bremfree(bp);
                bremfree(bp);
-               bp->b_flags |= B_BUSY;
+               if (passone || !commit)
+                   bp->b_flags |= (B_BUSY|B_ASYNC);
+               else
+                   bp->b_flags |= (B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT);
                splx(s);
                splx(s);
-               bp->b_flags |= B_ASYNC;
                VOP_BWRITE(bp);
                goto loop;
        }
        splx(s);
                VOP_BWRITE(bp);
                goto loop;
        }
        splx(s);
-       if (ap->a_waitfor == MNT_WAIT) {
+       if (passone) {
+               passone = 0;
+               goto again;
+       }
+       if (waitfor == MNT_WAIT) {
                while (vp->v_numoutput) {
                        vp->v_flag |= VBWAIT;
                        error = tsleep((caddr_t)&vp->v_numoutput,
                                slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
                        if (error) {
                while (vp->v_numoutput) {
                        vp->v_flag |= VBWAIT;
                        error = tsleep((caddr_t)&vp->v_numoutput,
                                slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
                        if (error) {
-                           if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+                           if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
                                return (EINTR);
                            if (slpflag == PCATCH) {
                                slpflag = 0;
                                return (EINTR);
                            if (slpflag == PCATCH) {
                                slpflag = 0;
@@ -2158,8 +2720,8 @@ loop:
                            }
                        }
                }
                            }
                        }
                }
-               if (vp->v_dirtyblkhd.lh_first) {
-#ifdef DIAGNOSTIC
+               if (vp->v_dirtyblkhd.lh_first && commit) {
+#ifndef DIAGNOSTIC
                        vprint("nfs_fsync: dirty", vp);
 #endif
                        goto loop;
                        vprint("nfs_fsync: dirty", vp);
 #endif
                        goto loop;
@@ -2175,10 +2737,11 @@ loop:
 /*
  * Return POSIX pathconf information applicable to nfs.
  *
 /*
  * Return POSIX pathconf information applicable to nfs.
  *
- * Currently the NFS protocol does not support getting such
- * information from the remote server.
+ * The NFS V2 protocol doesn't support this, so just return EINVAL
+ * for V2.
  */
 /* ARGSUSED */
  */
 /* ARGSUSED */
+int
 nfs_pathconf(ap)
        struct vop_pathconf_args /* {
                struct vnode *a_vp;
 nfs_pathconf(ap)
        struct vop_pathconf_args /* {
                struct vnode *a_vp;
@@ -2220,13 +2783,12 @@ nfs_print(ap)
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
 
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
 
-       printf("tag VT_NFS, fileid %d fsid 0x%x",
+       printf("tag VT_NFS, fileid %ld fsid 0x%lx",
                np->n_vattr.va_fileid, np->n_vattr.va_fsid);
                np->n_vattr.va_fileid, np->n_vattr.va_fsid);
-#ifdef FIFO
        if (vp->v_type == VFIFO)
                fifo_printinfo(vp);
        if (vp->v_type == VFIFO)
                fifo_printinfo(vp);
-#endif /* FIFO */
        printf("\n");
        printf("\n");
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -2316,6 +2878,84 @@ nfs_update(ap)
        return (EOPNOTSUPP);
 }
 
        return (EOPNOTSUPP);
 }
 
+/*
+ * Just call nfs_writebp() with the force argument set to 1.
+ */
+int
+nfs_bwrite(ap)
+       struct vop_bwrite_args /* {
+               struct vnode *a_bp;
+       } */ *ap;
+{
+
+       return (nfs_writebp(ap->a_bp, 1));
+}
+
+/*
+ * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless
+ * the force flag is one and it also handles the B_NEEDCOMMIT flag.
+ */
+int
+nfs_writebp(bp, force)
+       register struct buf *bp;
+       int force;
+{
+       register int oldflags = bp->b_flags, retv = 1;
+       register struct proc *p = curproc;      /* XXX */
+       off_t off;
+
+       if(!(bp->b_flags & B_BUSY))
+               panic("bwrite: buffer is not busy???");
+
+       bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
+
+       if (oldflags & B_ASYNC) {
+               if (oldflags & B_DELWRI) {
+                       reassignbuf(bp, bp->b_vp);
+               } else if (p) {
+                       ++p->p_stats->p_ru.ru_oublock;
+               }
+       }
+       bp->b_vp->v_numoutput++;
+
+       /*
+        * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not
+        * an actual write will have to be scheduled via. VOP_STRATEGY().
+        * If B_WRITEINPROG is already set, then push it with a write anyhow.
+        */
+       if (oldflags & (B_NEEDCOMMIT | B_WRITEINPROG) == B_NEEDCOMMIT) {
+               off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
+               bp->b_flags |= B_WRITEINPROG;
+               retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff,
+                       bp->b_wcred, bp->b_proc);
+               bp->b_flags &= ~B_WRITEINPROG;
+               if (!retv) {
+                       bp->b_dirtyoff = bp->b_dirtyend = 0;
+                       bp->b_flags &= ~B_NEEDCOMMIT;
+                       biodone(bp);
+               } else if (retv == NFSERR_STALEWRITEVERF)
+                       nfs_clearcommit(bp->b_vp->v_mount);
+       }
+       if (retv) {
+               if (force)
+                       bp->b_flags |= B_WRITEINPROG;
+               VOP_STRATEGY(bp);
+       }
+
+       if( (oldflags & B_ASYNC) == 0) {
+               int rtval = biowait(bp);
+               if (oldflags & B_DELWRI) {
+                       reassignbuf(bp, bp->b_vp);
+               } else if (p) {
+                       ++p->p_stats->p_ru.ru_oublock;
+               }
+               brelse(bp);
+               return (rtval);
+       } 
+
+       return (0);
+}
+
 /*
  * nfs special file access vnode op.
  * Essentially just get vattr and then imitate iaccess() since the device is
 /*
  * nfs special file access vnode op.
  * Essentially just get vattr and then imitate iaccess() since the device is
@@ -2345,7 +2985,8 @@ nfsspec_access(ap)
        if (cred->cr_uid == 0)
                return (0);
        vap = &vattr;
        if (cred->cr_uid == 0)
                return (0);
        vap = &vattr;
-       if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p))
+       error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p);
+       if (error)
                return (error);
        /*
         * Access check is based on only one of owner, group, public.
                return (error);
        /*
         * Access check is based on only one of owner, group, public.
@@ -2362,7 +3003,8 @@ nfsspec_access(ap)
 found:
                ;
        }
 found:
                ;
        }
-       return ((vap->va_mode & mode) == mode ? 0 : EACCES);
+       error = (vap->va_mode & mode) == mode ? 0 : EACCES;
+       return (error);
 }
 
 /*
 }
 
 /*
@@ -2383,7 +3025,8 @@ nfsspec_read(ap)
         * Set access flag.
         */
        np->n_flag |= NACC;
         * Set access flag.
         */
        np->n_flag |= NACC;
-       np->n_atim = time;
+       np->n_atim.ts_sec = time.tv_sec;
+       np->n_atim.ts_nsec = time.tv_usec * 1000;
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
@@ -2405,7 +3048,8 @@ nfsspec_write(ap)
         * Set update flag.
         */
        np->n_flag |= NUPD;
         * Set update flag.
         */
        np->n_flag |= NUPD;
-       np->n_mtim = time;
+       np->n_mtim.ts_sec = time.tv_sec;
+       np->n_mtim.ts_nsec = time.tv_usec * 1000;
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
@@ -2432,23 +3076,16 @@ nfsspec_close(ap)
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
                        VATTR_NULL(&vattr);
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
                        VATTR_NULL(&vattr);
-                       if (np->n_flag & NACC) {
-                               vattr.va_atime.ts_sec = np->n_atim.tv_sec;
-                               vattr.va_atime.ts_nsec =
-                                   np->n_atim.tv_usec * 1000;
-                       }
-                       if (np->n_flag & NUPD) {
-                               vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
-                               vattr.va_mtime.ts_nsec =
-                                   np->n_mtim.tv_usec * 1000;
-                       }
+                       if (np->n_flag & NACC)
+                               vattr.va_atime = np->n_atim;
+                       if (np->n_flag & NUPD)
+                               vattr.va_mtime = np->n_mtim;
                        (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
                }
        }
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
 }
 
                        (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
                }
        }
        return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
 }
 
-#ifdef FIFO
 /*
  * Read wrapper for fifos.
  */
 /*
  * Read wrapper for fifos.
  */
@@ -2468,7 +3105,8 @@ nfsfifo_read(ap)
         * Set access flag.
         */
        np->n_flag |= NACC;
         * Set access flag.
         */
        np->n_flag |= NACC;
-       np->n_atim = time;
+       np->n_atim.ts_sec = time.tv_sec;
+       np->n_atim.ts_nsec = time.tv_usec * 1000;
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
@@ -2491,7 +3129,8 @@ nfsfifo_write(ap)
         * Set update flag.
         */
        np->n_flag |= NUPD;
         * Set update flag.
         */
        np->n_flag |= NUPD;
-       np->n_mtim = time;
+       np->n_mtim.ts_sec = time.tv_sec;
+       np->n_mtim.ts_nsec = time.tv_usec * 1000;
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
@@ -2515,27 +3154,24 @@ nfsfifo_close(ap)
        extern int (**fifo_vnodeop_p)();
 
        if (np->n_flag & (NACC | NUPD)) {
        extern int (**fifo_vnodeop_p)();
 
        if (np->n_flag & (NACC | NUPD)) {
-               if (np->n_flag & NACC)
-                       np->n_atim = time;
-               if (np->n_flag & NUPD)
-                       np->n_mtim = time;
+               if (np->n_flag & NACC) {
+                       np->n_atim.ts_sec = time.tv_sec;
+                       np->n_atim.ts_nsec = time.tv_usec * 1000;
+               }
+               if (np->n_flag & NUPD) {
+                       np->n_mtim.ts_sec = time.tv_sec;
+                       np->n_mtim.ts_nsec = time.tv_usec * 1000;
+               }
                np->n_flag |= NCHG;
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
                        VATTR_NULL(&vattr);
                np->n_flag |= NCHG;
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
                        VATTR_NULL(&vattr);
-                       if (np->n_flag & NACC) {
-                               vattr.va_atime.ts_sec = np->n_atim.tv_sec;
-                               vattr.va_atime.ts_nsec =
-                                   np->n_atim.tv_usec * 1000;
-                       }
-                       if (np->n_flag & NUPD) {
-                               vattr.va_mtime.ts_sec = np->n_mtim.tv_sec;
-                               vattr.va_mtime.ts_nsec =
-                                   np->n_mtim.tv_usec * 1000;
-                       }
+                       if (np->n_flag & NACC)
+                               vattr.va_atime = np->n_atim;
+                       if (np->n_flag & NUPD)
+                               vattr.va_mtime = np->n_mtim;
                        (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
                }
        }
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
 }
                        (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
                }
        }
        return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
 }
-#endif /* FIFO */
index 0de9337..44f586f 100644 (file)
@@ -7,34 +7,68 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsdiskless.h       8.1 (Berkeley) %G%
+ *     @(#)nfsdiskless.h       8.2 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSDISKLESS_H_
+#define _NFS_NFSDISKLESS_H_
+
 /*
  * Structure that must be initialized for a diskless nfs client.
  * This structure is used by nfs_mountroot() to set up the root and swap
  * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
  * interface can communicate with the server.
  * The primary bootstrap is expected to fill in the appropriate fields before
 /*
  * Structure that must be initialized for a diskless nfs client.
  * This structure is used by nfs_mountroot() to set up the root and swap
  * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
  * interface can communicate with the server.
  * The primary bootstrap is expected to fill in the appropriate fields before
- * starting vmunix. Whether or not the swap area is nfs mounted is determined
- * by the value in swdevt[0]. (equal to NODEV --> swap over nfs)
+ * starting the kernel. Whether or not the swap area is nfs mounted is
+ * determined by the value in swdevt[0]. (equal to NODEV --> swap over nfs)
  * Currently only works for AF_INET protocols.
  * NB: All fields are stored in net byte order to avoid hassles with
  * client/server byte ordering differences.
  */
  * Currently only works for AF_INET protocols.
  * NB: All fields are stored in net byte order to avoid hassles with
  * client/server byte ordering differences.
  */
+
+/*
+ * I have defined a new structure that can handle an NFS Version 3 file handle
+ * but the kernel still expects the old Version 2 one to be provided. The
+ * changes required in nfs_vfsops.c for using the new are documented there in
+ * comments. (I felt that breaking network booting code by changing this
+ * structure would not be prudent at this time, since almost all servers are
+ * still Version 2 anyhow.)
+ */
+struct nfsv3_diskless {
+       struct ifaliasreq myif;                 /* Default interface */
+       struct sockaddr_in mygateway;           /* Default gateway */
+       struct nfs_args swap_args;              /* Mount args for swap file */
+       int             swap_fhsize;            /* Size of file handle */
+       u_char          swap_fh[NFSX_V3FHMAX];  /* Swap file's file handle */
+       struct sockaddr_in swap_saddr;          /* Address of swap server */
+       char            swap_hostnam[MNAMELEN]; /* Host name for mount pt */
+       int             swap_nblks;             /* Size of server swap file */
+       struct ucred    swap_ucred;             /* Swap credentials */
+       struct nfs_args root_args;              /* Mount args for root fs */
+       int             root_fhsize;            /* Size of root file handle */
+       u_char          root_fh[NFSX_V3FHMAX];  /* File handle of root dir */
+       struct sockaddr_in root_saddr;          /* Address of root server */
+       char            root_hostnam[MNAMELEN]; /* Host name for mount pt */
+       long            root_time;              /* Timestamp of root fs */
+       char            my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
+};
+
 struct nfs_diskless {
        struct ifaliasreq myif;                 /* Default interface */
        struct sockaddr_in mygateway;           /* Default gateway */
        struct nfs_args swap_args;              /* Mount args for swap file */
 struct nfs_diskless {
        struct ifaliasreq myif;                 /* Default interface */
        struct sockaddr_in mygateway;           /* Default gateway */
        struct nfs_args swap_args;              /* Mount args for swap file */
-       u_char          swap_fh[NFS_FHSIZE];    /* Swap file's file handle */
+       u_char          swap_fh[NFSX_V2FH];     /* Swap file's file handle */
        struct sockaddr_in swap_saddr;          /* Address of swap server */
        char            swap_hostnam[MNAMELEN]; /* Host name for mount pt */
        int             swap_nblks;             /* Size of server swap file */
        struct ucred    swap_ucred;             /* Swap credentials */
        struct nfs_args root_args;              /* Mount args for root fs */
        struct sockaddr_in swap_saddr;          /* Address of swap server */
        char            swap_hostnam[MNAMELEN]; /* Host name for mount pt */
        int             swap_nblks;             /* Size of server swap file */
        struct ucred    swap_ucred;             /* Swap credentials */
        struct nfs_args root_args;              /* Mount args for root fs */
-       u_char          root_fh[NFS_FHSIZE];    /* File handle of root dir */
+       u_char          root_fh[NFSX_V2FH];     /* File handle of root dir */
        struct sockaddr_in root_saddr;          /* Address of root server */
        char            root_hostnam[MNAMELEN]; /* Host name for mount pt */
        long            root_time;              /* Timestamp of root fs */
        char            my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
 };
        struct sockaddr_in root_saddr;          /* Address of root server */
        char            root_hostnam[MNAMELEN]; /* Host name for mount pt */
        long            root_time;              /* Timestamp of root fs */
        char            my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
 };
+
+#endif
index a38a7a8..5482e91 100644 (file)
@@ -7,9 +7,14 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsm_subs.h 8.1 (Berkeley) %G%
+ *     @(#)nfsm_subs.h 8.2 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSM_SUBS_H_
+#define _NFS_NFSM_SUBS_H_
+
+
 /*
  * These macros do strange and peculiar things to mbuf chains for
  * the assistance of the nfs code. To attempt to use them for any
 /*
  * These macros do strange and peculiar things to mbuf chains for
  * the assistance of the nfs code. To attempt to use them for any
@@ -60,44 +65,153 @@ extern struct mbuf *nfsm_reqh();
                mb->m_len += (s); \
                bpos += (s); }
 
                mb->m_len += (s); \
                bpos += (s); }
 
-#define        nfsm_dissect(a,c,s) \
+#define        nfsm_dissect(a, c, s) \
                { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
                if (t1 >= (s)) { \
                        (a) = (c)(dpos); \
                        dpos += (s); \
                { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
                if (t1 >= (s)) { \
                        (a) = (c)(dpos); \
                        dpos += (s); \
-               } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+               } else if (t1 = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+                       error = t1; \
                        m_freem(mrep); \
                        goto nfsmout; \
                } else { \
                        (a) = (c)cp2; \
                } }
 
                        m_freem(mrep); \
                        goto nfsmout; \
                } else { \
                        (a) = (c)cp2; \
                } }
 
-#define nfsm_fhtom(v) \
-               nfsm_build(cp,caddr_t,NFSX_FH); \
-               bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+#define nfsm_fhtom(v, v3) \
+             { if (v3) { \
+                       t2 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED; \
+                       if (t2 <= M_TRAILINGSPACE(mb)) { \
+                               nfsm_build(tl, u_long *, t2); \
+                               *tl++ = txdr_unsigned(VTONFS(v)->n_fhsize); \
+                               *(tl + ((t2>>2) - 2)) = 0; \
+                               bcopy((caddr_t)VTONFS(v)->n_fhp,(caddr_t)tl, \
+                                       VTONFS(v)->n_fhsize); \
+                       } else if (t2 = nfsm_strtmbuf(&mb, &bpos, \
+                               (caddr_t)VTONFS(v)->n_fhp, VTONFS(v)->n_fhsize)) { \
+                               error = t2; \
+                               m_freem(mreq); \
+                               goto nfsmout; \
+                       } \
+               } else { \
+                       nfsm_build(cp, caddr_t, NFSX_V2FH); \
+                       bcopy((caddr_t)VTONFS(v)->n_fhp, cp, NFSX_V2FH); \
+               } }
 
 
-#define nfsm_srvfhtom(f) \
-               nfsm_build(cp,caddr_t,NFSX_FH); \
-               bcopy((caddr_t)(f), cp, NFSX_FH)
+#define nfsm_srvfhtom(f, v3) \
+               { if (v3) { \
+                       nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3FH); \
+                       *tl++ = txdr_unsigned(NFSX_V3FH); \
+                       bcopy((caddr_t)(f), (caddr_t)tl, NFSX_V3FH); \
+               } else { \
+                       nfsm_build(cp, caddr_t, NFSX_V2FH); \
+                       bcopy((caddr_t)(f), cp, NFSX_V2FH); \
+               } }
 
 
-#define nfsm_mtofh(d,v) \
-               { struct nfsnode *np; nfsv2fh_t *fhp; \
-               nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
-               if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
-                       m_freem(mrep); \
-                       goto nfsmout; \
+#define nfsm_srvpostop_fh(f) \
+               { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED + NFSX_V3FH); \
+               *tl++ = nfs_true; \
+               *tl++ = txdr_unsigned(NFSX_V3FH); \
+               bcopy((caddr_t)(f), (caddr_t)tl, NFSX_V3FH); \
+               }
+
+#define nfsm_mtofh(d, v, v3, f) \
+               { struct nfsnode *ttnp; nfsfh_t *ttfhp; int ttfhsize; \
+               if (v3) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       (f) = fxdr_unsigned(int, *tl); \
+               } else \
+                       (f) = 1; \
+               if (f) { \
+                       nfsm_getfh(ttfhp, ttfhsize, (v3)); \
+                       if (t1 = nfs_nget((d)->v_mount, ttfhp, ttfhsize, \
+                               &ttnp)) { \
+                               error = t1; \
+                               m_freem(mrep); \
+                               goto nfsmout; \
+                       } \
+                       (v) = NFSTOV(ttnp); \
+               } \
+               if (v3) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       if (f) \
+                               (f) = fxdr_unsigned(int, *tl); \
+                       else if (fxdr_unsigned(int, *tl)) \
+                               nfsm_adv(NFSX_V3FATTR); \
                } \
                } \
-               (v) = NFSTOV(np); \
-               nfsm_loadattr(v, (struct vattr *)0); \
+               if (f) \
+                       nfsm_loadattr((v), (struct vattr *)0); \
                }
 
                }
 
-#define        nfsm_loadattr(v,a) \
-               { struct vnode *tvp = (v); \
-               if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+#define nfsm_getfh(f, s, v3) \
+               { if (v3) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       if (((s) = fxdr_unsigned(int, *tl)) <= 0 || \
+                               (s) > NFSX_V3FHMAX) { \
+                               m_freem(mrep); \
+                               error = EBADRPC; \
+                               goto nfsmout; \
+                       } \
+               } else \
+                       (s) = NFSX_V2FH; \
+               nfsm_dissect((f), nfsfh_t *, nfsm_rndup(s)); }
+
+#define        nfsm_loadattr(v, a) \
+               { struct vnode *ttvp = (v); \
+               if (t1 = nfs_loadattrcache(&ttvp, &md, &dpos, (a))) { \
+                       error = t1; \
                        m_freem(mrep); \
                        goto nfsmout; \
                } \
                        m_freem(mrep); \
                        goto nfsmout; \
                } \
-               (v) = tvp; }
+               (v) = ttvp; }
+
+#define        nfsm_postop_attr(v, f) \
+               { struct vnode *ttvp = (v); \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if ((f) = fxdr_unsigned(int, *tl)) { \
+                       if (t1 = nfs_loadattrcache(&ttvp, &md, &dpos, \
+                               (struct vattr *)0)) { \
+                               error = t1; \
+                               (f) = 0; \
+                               m_freem(mrep); \
+                               goto nfsmout; \
+                       } \
+                       (v) = ttvp; \
+               } }
+
+/* Used as (f) for nfsm_wcc_data() */
+#define NFSV3_WCCRATTR 0
+#define NFSV3_WCCCHK   1
+
+#define        nfsm_wcc_data(v, f) \
+               { int ttattrf, ttretf = 0; \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if (*tl == nfs_true) { \
+                       nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED); \
+                       if (f) \
+                               ttretf = (VTONFS(v)->n_mtime == \
+                                       fxdr_unsigned(u_long, *(tl + 2))); \
+               } \
+               nfsm_postop_attr((v), ttattrf); \
+               if (f) { \
+                       (f) = ttretf; \
+               } else { \
+                       (f) = ttattrf; \
+               } }
+
+#define nfsm_v3sattr(s, a, u, g) \
+               { (s)->sa_modetrue = nfs_true; \
+               (s)->sa_mode = vtonfsv3_mode((a)->va_mode); \
+               (s)->sa_uidtrue = nfs_true; \
+               (s)->sa_uid = txdr_unsigned(u); \
+               (s)->sa_gidtrue = nfs_true; \
+               (s)->sa_gid = txdr_unsigned(g); \
+               (s)->sa_sizefalse = nfs_false; \
+               (s)->sa_atimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); \
+               txdr_nfsv3time(&(a)->va_atime, &(s)->sa_atime); \
+               (s)->sa_mtimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); \
+               txdr_nfsv3time(&(a)->va_mtime, &(s)->sa_mtime); \
+               }
 
 #define        nfsm_strsiz(s,m) \
                { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
 
 #define        nfsm_strsiz(s,m) \
                { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
@@ -114,15 +228,27 @@ extern struct mbuf *nfsm_reqh();
                        nfsm_reply(0); \
                } }
 
                        nfsm_reply(0); \
                } }
 
+#define        nfsm_srvnamesiz(s) \
+               { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+               if (((s) = fxdr_unsigned(long,*tl)) > NFS_MAXNAMLEN) \
+                       error = NFSERR_NAMETOL; \
+               if ((s) <= 0) \
+                       error = EBADRPC; \
+               if (error) \
+                       nfsm_reply(0); \
+               }
+
 #define nfsm_mtouio(p,s) \
                if ((s) > 0 && \
 #define nfsm_mtouio(p,s) \
                if ((s) > 0 && \
-                  (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+                  (t1 = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+                       error = t1; \
                        m_freem(mrep); \
                        goto nfsmout; \
                }
 
 #define nfsm_uiotom(p,s) \
                        m_freem(mrep); \
                        goto nfsmout; \
                }
 
 #define nfsm_uiotom(p,s) \
-               if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+               if (t1 = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+                       error = t1; \
                        m_freem(mreq); \
                        goto nfsmout; \
                }
                        m_freem(mreq); \
                        goto nfsmout; \
                }
@@ -137,8 +263,12 @@ extern struct mbuf *nfsm_reqh();
 
 #define        nfsm_request(v, t, p, c)        \
                if (error = nfs_request((v), mreq, (t), (p), \
 
 #define        nfsm_request(v, t, p, c)        \
                if (error = nfs_request((v), mreq, (t), (p), \
-                  (c), &mrep, &md, &dpos)) \
-                       goto nfsmout
+                  (c), &mrep, &md, &dpos)) { \
+                       if (error & NFSERR_RETERR) \
+                               error &= ~NFSERR_RETERR; \
+                       else \
+                               goto nfsmout; \
+               }
 
 #define        nfsm_strtom(a,s,m) \
                if ((s) > (m)) { \
 
 #define        nfsm_strtom(a,s,m) \
                if ((s) > (m)) { \
@@ -152,7 +282,8 @@ extern struct mbuf *nfsm_reqh();
                        *tl++ = txdr_unsigned(s); \
                        *(tl+((t2>>2)-2)) = 0; \
                        bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
                        *tl++ = txdr_unsigned(s); \
                        *(tl+((t2>>2)-2)) = 0; \
                        bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
-               } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+               } else if (t2 = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+                       error = t2; \
                        m_freem(mreq); \
                        goto nfsmout; \
                }
                        m_freem(mreq); \
                        goto nfsmout; \
                }
@@ -164,30 +295,53 @@ extern struct mbuf *nfsm_reqh();
 #define        nfsm_reply(s) \
                { \
                nfsd->nd_repstat = error; \
 #define        nfsm_reply(s) \
                { \
                nfsd->nd_repstat = error; \
-               if (error) \
-                  (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+               if (error && !(nfsd->nd_flag & ND_NFSV3)) \
+                  (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \
                        mrq, &mb, &bpos); \
                else \
                        mrq, &mb, &bpos); \
                else \
-                  (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+                  (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \
                        mrq, &mb, &bpos); \
                m_freem(mrep); \
                mreq = *mrq; \
                        mrq, &mb, &bpos); \
                m_freem(mrep); \
                mreq = *mrq; \
-               if (error) \
+               if (error && (!(nfsd->nd_flag & ND_NFSV3) || \
+                       error == EBADRPC)) \
                        return(0); \
                }
 
                        return(0); \
                }
 
+#define        nfsm_writereply(s, v3) \
+               { \
+               nfsd->nd_repstat = error; \
+               if (error && !(v3)) \
+                  (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \
+                       &mreq, &mb, &bpos); \
+               else \
+                  (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \
+                       &mreq, &mb, &bpos); \
+               }
+
 #define        nfsm_adv(s) \
 #define        nfsm_adv(s) \
-               t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+               t1 = mtod(md, caddr_t)+md->m_len-dpos; \
                if (t1 >= (s)) { \
                        dpos += (s); \
                if (t1 >= (s)) { \
                        dpos += (s); \
-               } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+               } else if (t1 = nfs_adv(&md, &dpos, (s), t1)) { \
+                       error = t1; \
                        m_freem(mrep); \
                        goto nfsmout; \
                        m_freem(mrep); \
                        goto nfsmout; \
-               }
+               } }
 
 #define nfsm_srvmtofh(f) \
 
 #define nfsm_srvmtofh(f) \
-               nfsm_dissect(tl, u_long *, NFSX_FH); \
-               bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+               { if (nfsd->nd_flag & ND_NFSV3) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       if (fxdr_unsigned(int, *tl) != NFSX_V3FH) { \
+                               error = EBADRPC; \
+                               nfsm_reply(0); \
+                       } \
+               } \
+               nfsm_dissect(tl, u_long *, NFSX_V3FH); \
+               bcopy((caddr_t)tl, (caddr_t)(f), NFSX_V3FH); \
+               if ((nfsd->nd_flag & ND_NFSV3) == 0) \
+                       nfsm_adv(NFSX_V2FH - NFSX_V3FH); \
+               }
 
 #define        nfsm_clget \
                if (bp >= be) { \
 
 #define        nfsm_clget \
                if (bp >= be) { \
@@ -203,41 +357,57 @@ extern struct mbuf *nfsm_reqh();
                } \
                tl = (u_long *)bp
 
                } \
                tl = (u_long *)bp
 
-#define        nfsm_srvfillattr \
-       fp->fa_type = vtonfs_type(vap->va_type); \
-       fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
-       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_nqlflag == NQL_NOVAL) { \
-               fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
-               if (vap->va_type == VFIFO) \
-                       fp->fa_nfsrdev = 0xffffffff; \
-               else \
-                       fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
-               fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
-               fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
-               fp->fa_nfssize = txdr_unsigned(vap->va_size); \
-               fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
-               txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
-               txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
-               fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
-               fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
-       } else { \
-               fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
-               if (vap->va_type == VFIFO) \
-                       fp->fa_nqrdev = 0xffffffff; \
-               else \
-                       fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
-               fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
-               fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
-               txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
-               txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
-               txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
-               txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
-               txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
-               fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
-               fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
-               txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
-       }
+#define        nfsm_srvfillattr(a, f) \
+               nfsm_srvfattr(nfsd, (a), (f))
+
+#define nfsm_srvwcc_data(br, b, ar, a) \
+               nfsm_srvwcc(nfsd, (br), (b), (ar), (a), &mb, &bpos)
+
+#define nfsm_srvpostop_attr(r, a) \
+               nfsm_srvpostopattr(nfsd, (r), (a), &mb, &bpos)
+
+#define nfsm_srvsattr(a) \
+               { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if (*tl == nfs_true) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       (a)->va_mode = nfstov_mode(*tl); \
+               } \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if (*tl == nfs_true) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       (a)->va_uid = fxdr_unsigned(uid_t, *tl); \
+               } \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if (*tl == nfs_true) { \
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+                       (a)->va_gid = fxdr_unsigned(gid_t, *tl); \
+               } \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               if (*tl == nfs_true) { \
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \
+                       fxdr_hyper(tl, &(a)->va_size); \
+               } \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               switch (fxdr_unsigned(int, *tl)) { \
+               case NFSV3SATTRTIME_TOCLIENT: \
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \
+                       fxdr_nfsv3time(tl, &(a)->va_atime); \
+                       break; \
+               case NFSV3SATTRTIME_TOSERVER: \
+                       (a)->va_atime.ts_sec = time.tv_sec; \
+                       (a)->va_atime.ts_nsec = time.tv_usec * 1000; \
+                       break; \
+               }; \
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \
+               switch (fxdr_unsigned(int, *tl)) { \
+               case NFSV3SATTRTIME_TOCLIENT: \
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \
+                       fxdr_nfsv3time(tl, &(a)->va_mtime); \
+                       break; \
+               case NFSV3SATTRTIME_TOSERVER: \
+                       (a)->va_mtime.ts_sec = time.tv_sec; \
+                       (a)->va_mtime.ts_nsec = time.tv_usec * 1000; \
+                       break; \
+               }; }
 
 
+#endif
index 4450565..d245a3b 100644 (file)
@@ -7,9 +7,13 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsmount.h  8.2 (Berkeley) %G%
+ *     @(#)nfsmount.h  8.3 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSMOUNT_H_
+#define _NFS_NFSMOUNT_H_
+
 /*
  * Mount structure.
  * One allocated on every NFS mount.
 /*
  * Mount structure.
  * One allocated on every NFS mount.
@@ -19,7 +23,8 @@ struct        nfsmount {
        int     nm_flag;                /* Flags for soft/hard... */
        struct  mount *nm_mountp;       /* Vfs structure for this filesystem */
        int     nm_numgrps;             /* Max. size of groupslist */
        int     nm_flag;                /* Flags for soft/hard... */
        struct  mount *nm_mountp;       /* Vfs structure for this filesystem */
        int     nm_numgrps;             /* Max. size of groupslist */
-       nfsv2fh_t nm_fh;                /* File handle of root dir */
+       u_char  nm_fh[NFSX_V3FHMAX];    /* File handle of root dir */
+       int     nm_fhsize;              /* Size of root file handle */
        struct  socket *nm_so;          /* Rpc socket */
        int     nm_sotype;              /* Type of socket */
        int     nm_soproto;             /* and protocol */
        struct  socket *nm_so;          /* Rpc socket */
        int     nm_sotype;              /* Type of socket */
        int     nm_soproto;             /* and protocol */
@@ -35,14 +40,22 @@ struct      nfsmount {
        int     nm_deadthresh;          /* Threshold of timeouts-->dead server*/
        int     nm_rsize;               /* Max size of read rpc */
        int     nm_wsize;               /* Max size of write rpc */
        int     nm_deadthresh;          /* Threshold of timeouts-->dead server*/
        int     nm_rsize;               /* Max size of read rpc */
        int     nm_wsize;               /* Max size of write rpc */
+       int     nm_readdirsize;         /* Size of a readdir rpc */
        int     nm_readahead;           /* Num. of blocks to readahead */
        int     nm_leaseterm;           /* Term (sec) for NQNFS lease */
        int     nm_readahead;           /* Num. of blocks to readahead */
        int     nm_leaseterm;           /* Term (sec) for NQNFS lease */
-       CIRCLEQ_HEAD(nfsnodes, nfsnode) nm_timerhead; /* Lease timer queue */
+       CIRCLEQ_HEAD(, nfsnode) nm_timerhead; /* Head of lease timer queue */
        struct vnode *nm_inprog;        /* Vnode in prog by nqnfs_clientd() */
        uid_t   nm_authuid;             /* Uid for authenticator */
        int     nm_authtype;            /* Authenticator type */
        int     nm_authlen;             /* and length */
        char    *nm_authstr;            /* Authenticator string */
        struct vnode *nm_inprog;        /* Vnode in prog by nqnfs_clientd() */
        uid_t   nm_authuid;             /* Uid for authenticator */
        int     nm_authtype;            /* Authenticator type */
        int     nm_authlen;             /* and length */
        char    *nm_authstr;            /* Authenticator string */
+       char    *nm_verfstr;            /* and the verifier */
+       int     nm_verflen;
+       u_char  nm_verf[NFSX_V3WRITEVERF]; /* V3 write verifier */
+       NFSKERBKEY_T nm_key;            /* and the session key */
+       int     nm_numuids;             /* Number of nfsuid mappings */
+       TAILQ_HEAD(, nfsuid) nm_uidlruhead; /* Lists of nfsuid mappings */
+       LIST_HEAD(, nfsuid) nm_uidhashtbl[NFS_MUIDHASHSIZ];
 };
 
 #ifdef KERNEL
 };
 
 #ifdef KERNEL
@@ -98,3 +111,5 @@ int  nfs_vptofh __P((
                struct vnode *vp,
                struct fid *fhp));
 int    nfs_init __P(());
                struct vnode *vp,
                struct fid *fhp));
 int    nfs_init __P(());
+
+#endif
index d11b812..34f19c9 100644 (file)
@@ -7,9 +7,17 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsnode.h   8.7 (Berkeley) %G%
+ *     @(#)nfsnode.h   8.8 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSNODE_H_
+#define _NFS_NFSNODE_H_
+
+#ifndef _NFS_NFS_H_
+#include <nfs/nfs.h>
+#endif
+
 /*
  * Silly rename structure that hangs off the nfsnode until the name
  * can be removed by nfs_inactive()
 /*
  * Silly rename structure that hangs off the nfsnode until the name
  * can be removed by nfs_inactive()
@@ -21,37 +29,74 @@ struct sillyrename {
        char    s_name[20];
 };
 
        char    s_name[20];
 };
 
+/*
+ * This structure is used to save the logical directory offset to
+ * NFS cookie mappings.
+ * The mappings are stored in a list headed
+ * by n_cookies, as required.
+ * There is one mapping for each NFS_DIRBLKSIZ bytes of directory information
+ * stored in increasing logical offset byte order.
+ */
+#define NFSNUMCOOKIES          31
+
+struct nfsdmap {
+       LIST_ENTRY(nfsdmap)     ndm_list;
+       int                     ndm_eocookie;
+       nfsuint64               ndm_cookies[NFSNUMCOOKIES];
+};
+
 /*
  * The nfsnode is the nfs equivalent to ufs's inode. Any similarity
  * is purely coincidental.
  * There is a unique nfsnode allocated for each active file,
  * each current directory, each mounted-on file, text file, and the root.
  * An nfsnode is 'named' by its file handle. (nget/nfs_node.c)
 /*
  * The nfsnode is the nfs equivalent to ufs's inode. Any similarity
  * is purely coincidental.
  * There is a unique nfsnode allocated for each active file,
  * each current directory, each mounted-on file, text file, and the root.
  * An nfsnode is 'named' by its file handle. (nget/nfs_node.c)
+ * If this structure exceeds 256 bytes (it is currently 256 using 4.4BSD-Lite
+ * type definitions), file handles of > 32 bytes should probably be split out
+ * into a separate MALLOC()'d data structure. (Reduce the size of nfsfh_t by
+ * changing the definition in sys/mount.h of NFS_SMALLFH.)
+ * NB: Hopefully the current order of the fields is such that everything will
+ *     be well aligned and, therefore, tightly packed.
  */
  */
-
 struct nfsnode {
 struct nfsnode {
-       LIST_ENTRY(nfsnode) n_hash;     /* Hash chain */
-       CIRCLEQ_ENTRY(nfsnode) n_timer; /* Nqnfs timer chain */
-       nfsv2fh_t n_fh;                 /* NFS File Handle */
-       long    n_flag;                 /* Flag for locking.. */
-       struct  vnode *n_vnode;         /* vnode associated with this node */
-       struct  vattr n_vattr;          /* Vnode attribute cache */
-       time_t  n_attrstamp;            /* Time stamp for cached attributes */
-       struct  sillyrename *n_sillyrename; /* Ptr to silly rename struct */
-       u_quad_t n_size;                /* Current size of file */
-       int     n_error;                /* Save write error value */
-       u_long  n_direofoffset;         /* Dir. EOF offset cache */
-       time_t  n_mtime;                 /* Prev modify time. */
-       time_t  n_ctime;                 /* Prev create time. */
-       u_quad_t n_brev;                 /* Modify rev when cached */
-       u_quad_t n_lrev;                 /* Modify rev for lease */
-       time_t  n_expiry;                /* Lease expiry time */
-       long    spare1;                 /* To 8 byte boundary */
-       struct  sillyrename n_silly;    /* Silly rename struct */
-       struct  timeval n_atim;         /* Special file times */
-       struct  timeval n_mtim;
+       LIST_ENTRY(nfsnode)     n_hash;         /* Hash chain */
+       CIRCLEQ_ENTRY(nfsnode)  n_timer;        /* Nqnfs timer chain */
+       u_quad_t                n_size;         /* Current size of file */
+       u_quad_t                n_brev;         /* Modify rev when cached */
+       u_quad_t                n_lrev;         /* Modify rev for lease */
+       struct vattr            n_vattr;        /* Vnode attribute cache */
+       time_t                  n_attrstamp;    /* Attr. cache timestamp */
+       time_t                  n_mtime;        /* Prev modify time. */
+       time_t                  n_ctime;        /* Prev create time. */
+       time_t                  n_expiry;       /* Lease expiry time */
+       nfsfh_t                 *n_fhp;         /* NFS File Handle */
+       struct vnode            *n_vnode;       /* associated vnode */
+       struct lockf            *n_lockf;       /* Locking record of file */
+       int                     n_error;        /* Save write error value */
+       union {
+               struct timespec nf_atim;        /* Special file times */
+               nfsuint64       nd_cookieverf;  /* Cookie verifier (dir only) */
+       } n_un1;
+       union {
+               struct timespec nf_mtim;
+               off_t           nd_direof;      /* Dir. EOF offset cache */
+       } n_un2;
+       union {
+               struct sillyrename *nf_silly;   /* Ptr to silly rename struct */
+               LIST_HEAD(, nfsdmap) nd_cook;   /* cookies */
+       } n_un3;
+       short                   n_fhsize;       /* size in bytes, of fh */
+       short                   n_flag;         /* Flag for locking.. */
+       nfsfh_t                 n_fh;           /* Small File Handle */
 };
 
 };
 
+#define n_atim         n_un1.nf_atim
+#define n_mtim         n_un2.nf_mtim
+#define n_sillyrename  n_un3.nf_silly
+#define n_cookieverf   n_un1.nd_cookieverf
+#define n_direofoffset n_un2.nd_direof
+#define n_cookies      n_un3.nd_cook
+
 /*
  * Flags for n_flag
  */
 /*
  * Flags for n_flag
  */
@@ -75,7 +120,7 @@ struct nfsnode {
 /*
  * Queue head for nfsiod's
  */
 /*
  * Queue head for nfsiod's
  */
-TAILQ_HEAD(nfsbufs, buf) nfs_bufq;
+TAILQ_HEAD(, buf) nfs_bufq;
 
 #ifdef KERNEL
 /*
 
 #ifdef KERNEL
 /*
@@ -87,25 +132,22 @@ int        nfs_mknod __P((struct vop_mknod_args *));
 int    nfs_open __P((struct vop_open_args *));
 int    nfs_close __P((struct vop_close_args *));
 int    nfsspec_close __P((struct vop_close_args *));
 int    nfs_open __P((struct vop_open_args *));
 int    nfs_close __P((struct vop_close_args *));
 int    nfsspec_close __P((struct vop_close_args *));
-#ifdef FIFO
 int    nfsfifo_close __P((struct vop_close_args *));
 int    nfsfifo_close __P((struct vop_close_args *));
-#endif
 int    nfs_access __P((struct vop_access_args *));
 int    nfsspec_access __P((struct vop_access_args *));
 int    nfs_getattr __P((struct vop_getattr_args *));
 int    nfs_setattr __P((struct vop_setattr_args *));
 int    nfs_read __P((struct vop_read_args *));
 int    nfs_write __P((struct vop_write_args *));
 int    nfs_access __P((struct vop_access_args *));
 int    nfsspec_access __P((struct vop_access_args *));
 int    nfs_getattr __P((struct vop_getattr_args *));
 int    nfs_setattr __P((struct vop_setattr_args *));
 int    nfs_read __P((struct vop_read_args *));
 int    nfs_write __P((struct vop_write_args *));
-#define nfs_lease_check ((int (*) __P((struct  vop_lease_args *)))nullop)
+#define        nfs_lease_check ((int (*) __P((struct  vop_lease_args *)))nullop)
+#define nqnfs_vop_lease_check  lease_check
 int    nfsspec_read __P((struct vop_read_args *));
 int    nfsspec_write __P((struct vop_write_args *));
 int    nfsspec_read __P((struct vop_read_args *));
 int    nfsspec_write __P((struct vop_write_args *));
-#ifdef FIFO
 int    nfsfifo_read __P((struct vop_read_args *));
 int    nfsfifo_write __P((struct vop_write_args *));
 int    nfsfifo_read __P((struct vop_read_args *));
 int    nfsfifo_write __P((struct vop_write_args *));
-#endif
 #define nfs_ioctl ((int (*) __P((struct  vop_ioctl_args *)))enoioctl)
 #define nfs_select ((int (*) __P((struct  vop_select_args *)))seltrue)
 #define nfs_ioctl ((int (*) __P((struct  vop_ioctl_args *)))enoioctl)
 #define nfs_select ((int (*) __P((struct  vop_select_args *)))seltrue)
-#define        nfs_revoke vop_revoke
+#define nfs_revoke vop_revoke
 int    nfs_mmap __P((struct vop_mmap_args *));
 int    nfs_fsync __P((struct vop_fsync_args *));
 #define nfs_seek ((int (*) __P((struct  vop_seek_args *)))nullop)
 int    nfs_mmap __P((struct vop_mmap_args *));
 int    nfs_fsync __P((struct vop_fsync_args *));
 #define nfs_seek ((int (*) __P((struct  vop_seek_args *)))nullop)
@@ -129,6 +171,7 @@ int nfs_islocked __P((struct vop_islocked_args *));
 int    nfs_pathconf __P((struct vop_pathconf_args *));
 int    nfs_advlock __P((struct vop_advlock_args *));
 int    nfs_blkatoff __P((struct vop_blkatoff_args *));
 int    nfs_pathconf __P((struct vop_pathconf_args *));
 int    nfs_advlock __P((struct vop_advlock_args *));
 int    nfs_blkatoff __P((struct vop_blkatoff_args *));
+int    nfs_bwrite __P((struct vop_bwrite_args *));
 int    nfs_vget __P((struct mount *, ino_t, struct vnode **));
 int    nfs_valloc __P((struct vop_valloc_args *));
 #define nfs_reallocblks \
 int    nfs_vget __P((struct mount *, ino_t, struct vnode **));
 int    nfs_valloc __P((struct vop_valloc_args *));
 #define nfs_reallocblks \
@@ -136,5 +179,16 @@ int        nfs_valloc __P((struct vop_valloc_args *));
 int    nfs_vfree __P((struct vop_vfree_args *));
 int    nfs_truncate __P((struct vop_truncate_args *));
 int    nfs_update __P((struct vop_update_args *));
 int    nfs_vfree __P((struct vop_vfree_args *));
 int    nfs_truncate __P((struct vop_truncate_args *));
 int    nfs_update __P((struct vop_update_args *));
-int    nfs_bwrite __P((struct vop_bwrite_args *));
+
+/* other stuff */
+int    nfs_removeit __P((struct sillyrename *));
+int    nfs_nget __P((struct mount *,nfsfh_t *,int,struct nfsnode **));
+int    nfs_lookitup __P((struct vnode *,char *,int,struct ucred *,struct proc *,struct nfsnode **));
+int    nfs_sillyrename __P((struct vnode *,struct vnode *,struct componentname *));
+nfsuint64 *nfs_getcookie __P((struct nfsnode *, off_t, int));
+void nfs_invaldir __P((struct vnode *));
+#define nqnfs_lease_updatetime lease_updatetime
+
 #endif /* KERNEL */
 #endif /* KERNEL */
+
+#endif
index 71b778a..a8c826d 100644 (file)
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsproto.h  8.1 (Berkeley) %G%
+ *     @(#)nfsproto.h  8.2 (Berkeley) %G%
  */
 
  */
 
+#ifndef _NFS_NFSPROTO_H_
+#define _NFS_NFSPROTO_H_
+
+
 /*
 /*
- * nfs definitions as per the version 2 specs
+ * nfs definitions as per the Version 2 and 3 specs
  */
 
 /*
  */
 
 /*
- * Constants as defined in the Sun NFS Version 2 spec.
+ * Constants as defined in the Sun NFS Version 2 and 3 specs.
  * "NFS: Network File System Protocol Specification" RFC1094
  * "NFS: Network File System Protocol Specification" RFC1094
+ * and in the "NFS: Network File System Version 3 Protocol
+ * Specification"
  */
 
 #define NFS_PORT       2049
 #define        NFS_PROG        100003
 #define NFS_VER2       2
  */
 
 #define NFS_PORT       2049
 #define        NFS_PROG        100003
 #define NFS_VER2       2
-#define        NFS_MAXDGRAMDATA 8192
+#define        NFS_VER3        3
+#define NFS_V2MAXDATA  8192
+#define        NFS_MAXDGRAMDATA 16384
 #define        NFS_MAXDATA     32768
 #define        NFS_MAXPATHLEN  1024
 #define        NFS_MAXNAMLEN   255
 #define        NFS_MAXDATA     32768
 #define        NFS_MAXPATHLEN  1024
 #define        NFS_MAXNAMLEN   255
-#define        NFS_FHSIZE      32
 #define        NFS_MAXPKTHDR   404
 #define        NFS_MAXPKTHDR   404
-#define NFS_MAXPACKET  (NFS_MAXPKTHDR+NFS_MAXDATA)
+#define NFS_MAXPACKET  (NFS_MAXPKTHDR + NFS_MAXDATA)
 #define        NFS_MINPACKET   20
 #define        NFS_FABLKSIZE   512     /* Size in bytes of a block wrt fa_blocks */
 
 #define        NFS_MINPACKET   20
 #define        NFS_FABLKSIZE   512     /* Size in bytes of a block wrt fa_blocks */
 
-/* Stat numbers for rpc returns */
-#define        NFS_OK          0
-#define        NFSERR_PERM     1
-#define        NFSERR_NOENT    2
-#define        NFSERR_IO       5
-#define        NFSERR_NXIO     6
-#define        NFSERR_ACCES    13
-#define        NFSERR_EXIST    17
-#define        NFSERR_NODEV    19
-#define        NFSERR_NOTDIR   20
-#define        NFSERR_ISDIR    21
-#define        NFSERR_FBIG     27
-#define        NFSERR_NOSPC    28
-#define        NFSERR_ROFS     30
-#define        NFSERR_NAMETOL  63
-#define        NFSERR_NOTEMPTY 66
-#define        NFSERR_DQUOT    69
-#define        NFSERR_STALE    70
-#define        NFSERR_WFLUSH   99
+/* Stat numbers for rpc returns (version 2 and 3) */
+#define        NFS_OK                  0
+#define        NFSERR_PERM             1
+#define        NFSERR_NOENT            2
+#define        NFSERR_IO               5
+#define        NFSERR_NXIO             6
+#define        NFSERR_ACCES            13
+#define        NFSERR_EXIST            17
+#define        NFSERR_XDEV             18      /* Version 3 only */
+#define        NFSERR_NODEV            19
+#define        NFSERR_NOTDIR           20
+#define        NFSERR_ISDIR            21
+#define        NFSERR_INVAL            22      /* Version 3 only */
+#define        NFSERR_FBIG             27
+#define        NFSERR_NOSPC            28
+#define        NFSERR_ROFS             30
+#define        NFSERR_MLINK            31      /* Version 3 only */
+#define        NFSERR_NAMETOL          63
+#define        NFSERR_NOTEMPTY         66
+#define        NFSERR_DQUOT            69
+#define        NFSERR_STALE            70
+#define        NFSERR_REMOTE           71      /* Version 3 only */
+#define        NFSERR_WFLUSH           99      /* Version 2 only */
+#define        NFSERR_BADHANDLE        10001   /* The rest Version 3 only */
+#define        NFSERR_NOT_SYNC         10002
+#define        NFSERR_BAD_COOKIE       10003
+#define        NFSERR_NOTSUPP          10004
+#define        NFSERR_TOOSMALL         10005
+#define        NFSERR_SERVERFAULT      10006
+#define        NFSERR_BADTYPE          10007
+#define        NFSERR_JUKEBOX          10008
+#define NFSERR_TRYLATER                NFSERR_JUKEBOX
+#define        NFSERR_STALEWRITEVERF   30001   /* Fake return for nfs_commit() */
+
+#define NFSERR_RETVOID         0x20000000 /* Return void, not error */
+#define NFSERR_AUTHERR         0x40000000 /* Mark an authentication error */
+#define NFSERR_RETERR          0x80000000 /* Mark an error return for V3 */
 
 /* Sizes in bytes of various nfs rpc components */
 
 /* Sizes in bytes of various nfs rpc components */
-#define        NFSX_FH         32
 #define        NFSX_UNSIGNED   4
 #define        NFSX_UNSIGNED   4
-#define        NFSX_NFSFATTR   68
-#define        NFSX_NQFATTR    92
-#define        NFSX_NFSSATTR   32
-#define        NFSX_NQSATTR    44
-#define        NFSX_COOKIE     4
-#define NFSX_NFSSTATFS 20
-#define        NFSX_NQSTATFS   28
-#define        NFSX_FATTR(isnq)        ((isnq) ? NFSX_NQFATTR : NFSX_NFSFATTR)
-#define        NFSX_SATTR(isnq)        ((isnq) ? NFSX_NQSATTR : NFSX_NFSSATTR)
-#define        NFSX_STATFS(isnq)       ((isnq) ? NFSX_NQSTATFS : NFSX_NFSSTATFS)
-
-/* nfs rpc procedure numbers */
+
+/* specific to NFS Version 2 */
+#define        NFSX_V2FH       32
+#define        NFSX_V2FATTR    68
+#define        NFSX_V2SATTR    32
+#define        NFSX_V2COOKIE   4
+#define NFSX_V2STATFS  20
+
+/* specific to NFS Version 3 */
+#define NFSX_V3FH              (sizeof (fhandle_t)) /* size this server uses */
+#define        NFSX_V3FHMAX            64      /* max. allowed by protocol */
+#define NFSX_V3FATTR           84
+#define NFSX_V3SATTR           60      /* max. all fields filled in */
+#define NFSX_V3SRVSATTR                (sizeof (struct nfsv3_sattr))
+#define NFSX_V3POSTOPATTR      (NFSX_V3FATTR + NFSX_UNSIGNED)
+#define NFSX_V3WCCDATA         (NFSX_V3POSTOPATTR + 8 * NFSX_UNSIGNED)
+#define NFSX_V3COOKIEVERF      8
+#define NFSX_V3WRITEVERF       8
+#define NFSX_V3CREATEVERF      8
+#define NFSX_V3STATFS          52
+#define NFSX_V3FSINFO          48
+#define NFSX_V3PATHCONF                24
+
+/* variants for both versions */
+#define NFSX_FH(v3)            ((v3) ? (NFSX_V3FHMAX + NFSX_UNSIGNED) : \
+                                       NFSX_V2FH)
+#define NFSX_SRVFH(v3)         ((v3) ? NFSX_V3FH : NFSX_V2FH)
+#define        NFSX_FATTR(v3)          ((v3) ? NFSX_V3FATTR : NFSX_V2FATTR)
+#define NFSX_PREOPATTR(v3)     ((v3) ? (7 * NFSX_UNSIGNED) : 0)
+#define NFSX_POSTOPATTR(v3)    ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : 0)
+#define NFSX_POSTOPORFATTR(v3) ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : \
+                                       NFSX_V2FATTR)
+#define NFSX_WCCDATA(v3)       ((v3) ? NFSX_V3WCCDATA : 0)
+#define NFSX_WCCORFATTR(v3)    ((v3) ? NFSX_V3WCCDATA : NFSX_V2FATTR)
+#define        NFSX_SATTR(v3)          ((v3) ? NFSX_V3SATTR : NFSX_V2SATTR)
+#define        NFSX_COOKIEVERF(v3)     ((v3) ? NFSX_V3COOKIEVERF : 0)
+#define        NFSX_WRITEVERF(v3)      ((v3) ? NFSX_V3WRITEVERF : 0)
+#define NFSX_READDIR(v3)       ((v3) ? (5 * NFSX_UNSIGNED) : \
+                                       (2 * NFSX_UNSIGNED))
+#define        NFSX_STATFS(v3)         ((v3) ? NFSX_V3STATFS : NFSX_V2STATFS)
+
+/* nfs rpc procedure numbers (before version mapping) */
 #define        NFSPROC_NULL            0
 #define        NFSPROC_GETATTR         1
 #define        NFSPROC_SETATTR         2
 #define        NFSPROC_NULL            0
 #define        NFSPROC_GETATTR         1
 #define        NFSPROC_SETATTR         2
-#define        NFSPROC_NOOP            3
-#define        NFSPROC_ROOT            NFSPROC_NOOP    /* Obsolete */
-#define        NFSPROC_LOOKUP          4
+#define        NFSPROC_LOOKUP          3
+#define        NFSPROC_ACCESS          4
 #define        NFSPROC_READLINK        5
 #define        NFSPROC_READ            6
 #define        NFSPROC_READLINK        5
 #define        NFSPROC_READ            6
-#define        NFSPROC_WRITECACHE      NFSPROC_NOOP    /* Obsolete */
-#define        NFSPROC_WRITE           8
-#define        NFSPROC_CREATE          9
-#define        NFSPROC_REMOVE          10
-#define        NFSPROC_RENAME          11
-#define        NFSPROC_LINK            12
-#define        NFSPROC_SYMLINK         13
-#define        NFSPROC_MKDIR           14
-#define        NFSPROC_RMDIR           15
+#define        NFSPROC_WRITE           7
+#define        NFSPROC_CREATE          8
+#define        NFSPROC_MKDIR           9
+#define        NFSPROC_SYMLINK         10
+#define        NFSPROC_MKNOD           11
+#define        NFSPROC_REMOVE          12
+#define        NFSPROC_RMDIR           13
+#define        NFSPROC_RENAME          14
+#define        NFSPROC_LINK            15
 #define        NFSPROC_READDIR         16
 #define        NFSPROC_READDIR         16
-#define        NFSPROC_STATFS          17
+#define        NFSPROC_READDIRPLUS     17
+#define        NFSPROC_FSSTAT          18
+#define        NFSPROC_FSINFO          19
+#define        NFSPROC_PATHCONF        20
+#define        NFSPROC_COMMIT          21
+
+/* And leasing (nqnfs) procedure numbers (must be last) */
+#define        NQNFSPROC_GETLEASE      22
+#define        NQNFSPROC_VACATED       23
+#define        NQNFSPROC_EVICTED       24
+
+#define NFSPROC_NOOP           25
+#define        NFS_NPROCS              26
 
 
-/* NQ nfs numbers */
-#define        NQNFSPROC_READDIRLOOK   18
-#define        NQNFSPROC_GETLEASE      19
-#define        NQNFSPROC_VACATED       20
-#define        NQNFSPROC_EVICTED       21
-#define        NQNFSPROC_ACCESS        22
+/* Actual Version 2 procedure numbers */
+#define        NFSV2PROC_NULL          0
+#define        NFSV2PROC_GETATTR       1
+#define        NFSV2PROC_SETATTR       2
+#define        NFSV2PROC_NOOP          3
+#define        NFSV2PROC_ROOT          NFSV2PROC_NOOP  /* Obsolete */
+#define        NFSV2PROC_LOOKUP        4
+#define        NFSV2PROC_READLINK      5
+#define        NFSV2PROC_READ          6
+#define        NFSV2PROC_WRITECACHE    NFSV2PROC_NOOP  /* Obsolete */
+#define        NFSV2PROC_WRITE         8
+#define        NFSV2PROC_CREATE        9
+#define        NFSV2PROC_REMOVE        10
+#define        NFSV2PROC_RENAME        11
+#define        NFSV2PROC_LINK          12
+#define        NFSV2PROC_SYMLINK       13
+#define        NFSV2PROC_MKDIR         14
+#define        NFSV2PROC_RMDIR         15
+#define        NFSV2PROC_READDIR       16
+#define        NFSV2PROC_STATFS        17
+
+/*
+ * Constants used by the Version 3 protocol for various RPCs
+ */
+#define NFSV3SATTRTIME_DONTCHANGE      0
+#define NFSV3SATTRTIME_TOSERVER                1
+#define NFSV3SATTRTIME_TOCLIENT                2
+
+#define NFSV3ACCESS_READ               0x01
+#define NFSV3ACCESS_LOOKUP             0x02
+#define NFSV3ACCESS_MODIFY             0x04
+#define NFSV3ACCESS_EXTEND             0x08
+#define NFSV3ACCESS_DELETE             0x10
+#define NFSV3ACCESS_EXECUTE            0x20
+
+#define NFSV3WRITE_UNSTABLE            0
+#define NFSV3WRITE_DATASYNC            1
+#define NFSV3WRITE_FILESYNC            2
+
+#define NFSV3CREATE_UNCHECKED          0
+#define NFSV3CREATE_GUARDED            1
+#define NFSV3CREATE_EXCLUSIVE          2
+
+#define NFSV3FSINFO_LINK               0x01
+#define NFSV3FSINFO_SYMLINK            0x02
+#define NFSV3FSINFO_HOMOGENEOUS                0x08
+#define NFSV3FSINFO_CANSETTIME         0x10
 
 
-#define        NFS_NPROCS              23
 /* Conversion macros */
 /* Conversion macros */
-extern int             vttoif_tab[];
-#define        vtonfs_mode(t,m) \
+#define        vtonfsv2_mode(t,m) \
                txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : \
                                MAKEIMODE((t), (m)))
                txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : \
                                MAKEIMODE((t), (m)))
-#define        nfstov_mode(a)  (fxdr_unsigned(u_short, (a))&07777)
-#define        vtonfs_type(a)  txdr_unsigned(nfs_type[((long)(a))])
-#define        nfstov_type(a)  ntov_type[fxdr_unsigned(u_long,(a))&0x7]
+#define vtonfsv3_mode(m)       txdr_unsigned((m) & 07777)
+#define        nfstov_mode(a)          (fxdr_unsigned(u_short, (a))&07777)
+#define        vtonfsv2_type(a)        txdr_unsigned(nfsv2_type[((long)(a))])
+#define        vtonfsv3_type(a)        txdr_unsigned(nfsv3_type[((long)(a))])
+#define        nfsv2tov_type(a)        nv2tov_type[fxdr_unsigned(u_long,(a))&0x7]
+#define        nfsv3tov_type(a)        nv3tov_type[fxdr_unsigned(u_long,(a))&0x7]
 
 /* File types */
 
 /* File types */
-typedef enum { NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 } nfstype;
+typedef enum { NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5,
+       NFSOCK=6, NFFIFO=7 } nfstype;
 
 /* Structs for common parts of the rpc's */
 
 /* Structs for common parts of the rpc's */
+/*
+ * File Handle (32 bytes for version 2), variable up to 64 for version 3.
+ * File Handles of up to NFS_SMALLFH in size are stored directly in the
+ * nfs node, whereas larger ones are malloc'd. (This never happens when
+ * NFS_SMALLFH is set to 64.)
+ * NFS_SMALLFH should be in the range of 32 to 64 and be divisible by 4.
+ */
+#ifndef NFS_SMALLFH
+#define NFS_SMALLFH    64
+#endif
+union nfsfh {
+       fhandle_t       fh_generic;
+       u_char          fh_bytes[NFS_SMALLFH];
+};
+typedef union nfsfh nfsfh_t;
+
 struct nfsv2_time {
 struct nfsv2_time {
-       u_long  nfs_sec;
-       u_long  nfs_usec;
+       u_long  nfsv2_sec;
+       u_long  nfsv2_usec;
+};
+typedef struct nfsv2_time      nfstime2;
+
+struct nfsv3_time {
+       u_long  nfsv3_sec;
+       u_long  nfsv3_nsec;
+};
+typedef struct nfsv3_time      nfstime3;
+
+/*
+ * Quads are defined as arrays of 2 longs to ensure dense packing for the
+ * protocol and to facilitate xdr conversion.
+ */
+struct nfs_uquad {
+       u_long  nfsuquad[2];
 };
 };
+typedef        struct nfs_uquad        nfsuint64;
 
 
-struct nqnfs_time {
-       u_long  nq_sec;
-       u_long  nq_nsec;
+/*
+ * Used to convert between two u_longs and a u_quad_t.
+ */
+union nfs_quadconvert {
+       u_long          lval[2];
+       u_quad_t        qval;
+};
+typedef union nfs_quadconvert  nfsquad_t;
+
+/*
+ * NFS Version 3 special file number.
+ */
+struct nfsv3_spec {
+       u_long  specdata1;
+       u_long  specdata2;
 };
 };
+typedef        struct nfsv3_spec       nfsv3spec;
 
 /*
  * File attributes and setable attributes. These structures cover both
 
 /*
  * File attributes and setable attributes. These structures cover both
- * NFS version 2 and the NQNFS protocol. Note that the union is only
- * used to that one pointer can refer to both variants. These structures
+ * NFS version 2 and the version 3 protocol. Note that the union is only
+ * used so that one pointer can refer to both variants. These structures
  * go out on the wire and must be densely packed, so no quad data types
  * are used. (all fields are longs or u_longs or structures of same)
  * go out on the wire and must be densely packed, so no quad data types
  * are used. (all fields are longs or u_longs or structures of same)
- * NB: You can't do sizeof(struct nfsv2_fattr), you must use the
- *     NFSX_FATTR(isnq) macro.
+ * NB: You can't do sizeof(struct nfs_fattr), you must use the
+ *     NFSX_FATTR(v3) macro.
  */
  */
-struct nfsv2_fattr {
+struct nfs_fattr {
        u_long  fa_type;
        u_long  fa_mode;
        u_long  fa_nlink;
        u_long  fa_type;
        u_long  fa_mode;
        u_long  fa_nlink;
@@ -135,100 +288,128 @@ struct nfsv2_fattr {
        u_long  fa_gid;
        union {
                struct {
        u_long  fa_gid;
        union {
                struct {
-                       u_long  nfsfa_size;
-                       u_long  nfsfa_blocksize;
-                       u_long  nfsfa_rdev;
-                       u_long  nfsfa_blocks;
-                       u_long  nfsfa_fsid;
-                       u_long  nfsfa_fileid;
-                       struct nfsv2_time nfsfa_atime;
-                       struct nfsv2_time nfsfa_mtime;
-                       struct nfsv2_time nfsfa_ctime;
+                       u_long          nfsv2fa_size;
+                       u_long          nfsv2fa_blocksize;
+                       u_long          nfsv2fa_rdev;
+                       u_long          nfsv2fa_blocks;
+                       u_long          nfsv2fa_fsid;
+                       u_long          nfsv2fa_fileid;
+                       nfstime2        nfsv2fa_atime;
+                       nfstime2        nfsv2fa_mtime;
+                       nfstime2        nfsv2fa_ctime;
                } fa_nfsv2;
                struct {
                } fa_nfsv2;
                struct {
-                       struct {
-                               u_long  nqfa_qsize[2];
-                       } nqfa_size;
-                       u_long  nqfa_blocksize;
-                       u_long  nqfa_rdev;
-                       struct {
-                               u_long  nqfa_qbytes[2];
-                       } nqfa_bytes;
-                       u_long  nqfa_fsid;
-                       u_long  nqfa_fileid;
-                       struct nqnfs_time nqfa_atime;
-                       struct nqnfs_time nqfa_mtime;
-                       struct nqnfs_time nqfa_ctime;
-                       u_long  nqfa_flags;
-                       u_long  nqfa_gen;
-                       struct {
-                               u_long  nqfa_qfilerev[2];
-                       } nqfa_filerev;
-               } fa_nqnfs;
+                       nfsuint64       nfsv3fa_size;
+                       nfsuint64       nfsv3fa_used;
+                       nfsv3spec       nfsv3fa_rdev;
+                       nfsuint64       nfsv3fa_fsid;
+                       nfsuint64       nfsv3fa_fileid;
+                       nfstime3        nfsv3fa_atime;
+                       nfstime3        nfsv3fa_mtime;
+                       nfstime3        nfsv3fa_ctime;
+               } fa_nfsv3;
        } fa_un;
 };
 
 /* and some ugly defines for accessing union components */
        } fa_un;
 };
 
 /* and some ugly defines for accessing union components */
-#define        fa_nfssize              fa_un.fa_nfsv2.nfsfa_size
-#define        fa_nfsblocksize         fa_un.fa_nfsv2.nfsfa_blocksize
-#define        fa_nfsrdev              fa_un.fa_nfsv2.nfsfa_rdev
-#define        fa_nfsblocks            fa_un.fa_nfsv2.nfsfa_blocks
-#define        fa_nfsfsid              fa_un.fa_nfsv2.nfsfa_fsid
-#define        fa_nfsfileid            fa_un.fa_nfsv2.nfsfa_fileid
-#define        fa_nfsatime             fa_un.fa_nfsv2.nfsfa_atime
-#define        fa_nfsmtime             fa_un.fa_nfsv2.nfsfa_mtime
-#define        fa_nfsctime             fa_un.fa_nfsv2.nfsfa_ctime
-#define        fa_nqsize               fa_un.fa_nqnfs.nqfa_size
-#define        fa_nqblocksize          fa_un.fa_nqnfs.nqfa_blocksize
-#define        fa_nqrdev               fa_un.fa_nqnfs.nqfa_rdev
-#define        fa_nqbytes              fa_un.fa_nqnfs.nqfa_bytes
-#define        fa_nqfsid               fa_un.fa_nqnfs.nqfa_fsid
-#define        fa_nqfileid             fa_un.fa_nqnfs.nqfa_fileid
-#define        fa_nqatime              fa_un.fa_nqnfs.nqfa_atime
-#define        fa_nqmtime              fa_un.fa_nqnfs.nqfa_mtime
-#define        fa_nqctime              fa_un.fa_nqnfs.nqfa_ctime
-#define        fa_nqflags              fa_un.fa_nqnfs.nqfa_flags
-#define        fa_nqgen                fa_un.fa_nqnfs.nqfa_gen
-#define        fa_nqfilerev            fa_un.fa_nqnfs.nqfa_filerev
+#define        fa2_size                fa_un.fa_nfsv2.nfsv2fa_size
+#define        fa2_blocksize           fa_un.fa_nfsv2.nfsv2fa_blocksize
+#define        fa2_rdev                fa_un.fa_nfsv2.nfsv2fa_rdev
+#define        fa2_blocks              fa_un.fa_nfsv2.nfsv2fa_blocks
+#define        fa2_fsid                fa_un.fa_nfsv2.nfsv2fa_fsid
+#define        fa2_fileid              fa_un.fa_nfsv2.nfsv2fa_fileid
+#define        fa2_atime               fa_un.fa_nfsv2.nfsv2fa_atime
+#define        fa2_mtime               fa_un.fa_nfsv2.nfsv2fa_mtime
+#define        fa2_ctime               fa_un.fa_nfsv2.nfsv2fa_ctime
+#define        fa3_size                fa_un.fa_nfsv3.nfsv3fa_size
+#define        fa3_used                fa_un.fa_nfsv3.nfsv3fa_used
+#define        fa3_rdev                fa_un.fa_nfsv3.nfsv3fa_rdev
+#define        fa3_fsid                fa_un.fa_nfsv3.nfsv3fa_fsid
+#define        fa3_fileid              fa_un.fa_nfsv3.nfsv3fa_fileid
+#define        fa3_atime               fa_un.fa_nfsv3.nfsv3fa_atime
+#define        fa3_mtime               fa_un.fa_nfsv3.nfsv3fa_mtime
+#define        fa3_ctime               fa_un.fa_nfsv3.nfsv3fa_ctime
 
 struct nfsv2_sattr {
 
 struct nfsv2_sattr {
-       u_long  sa_mode;
-       u_long  sa_uid;
-       u_long  sa_gid;
+       u_long          sa_mode;
+       u_long          sa_uid;
+       u_long          sa_gid;
+       u_long          sa_size;
+       nfstime2        sa_atime;
+       nfstime2        sa_mtime;
+};
+
+/*
+ * NFS Version 3 sattr structure for the new node creation case.
+ */
+struct nfsv3_sattr {
+       u_long          sa_modetrue;
+       u_long          sa_mode;
+       u_long          sa_uidtrue;
+       u_long          sa_uid;
+       u_long          sa_gidtrue;
+       u_long          sa_gid;
+       u_long          sa_sizefalse;
+       u_long          sa_atimetype;
+       nfstime3        sa_atime;
+       u_long          sa_mtimetype;
+       nfstime3        sa_mtime;
+};
+
+struct nfs_statfs {
        union {
                struct {
        union {
                struct {
-                       u_long  nfssa_size;
-                       struct nfsv2_time nfssa_atime;
-                       struct nfsv2_time nfssa_mtime;
-               } sa_nfsv2;
+                       u_long          nfsv2sf_tsize;
+                       u_long          nfsv2sf_bsize;
+                       u_long          nfsv2sf_blocks;
+                       u_long          nfsv2sf_bfree;
+                       u_long          nfsv2sf_bavail;
+               } sf_nfsv2;
                struct {
                struct {
-                       struct {
-                               u_long  nqsa_qsize[2];
-                       } nqsa_size;
-                       struct nqnfs_time nqsa_atime;
-                       struct nqnfs_time nqsa_mtime;
-                       u_long  nqsa_flags;
-                       u_long  nqsa_rdev;
-               } sa_nqnfs;
-       } sa_un;
+                       nfsuint64       nfsv3sf_tbytes;
+                       nfsuint64       nfsv3sf_fbytes;
+                       nfsuint64       nfsv3sf_abytes;
+                       nfsuint64       nfsv3sf_tfiles;
+                       nfsuint64       nfsv3sf_ffiles;
+                       nfsuint64       nfsv3sf_afiles;
+                       u_long          nfsv3sf_invarsec;
+               } sf_nfsv3;
+       } sf_un;
 };
 
 };
 
-/* and some ugly defines for accessing the unions */
-#define        sa_nfssize              sa_un.sa_nfsv2.nfssa_size
-#define        sa_nfsatime             sa_un.sa_nfsv2.nfssa_atime
-#define        sa_nfsmtime             sa_un.sa_nfsv2.nfssa_mtime
-#define        sa_nqsize               sa_un.sa_nqnfs.nqsa_size
-#define        sa_nqatime              sa_un.sa_nqnfs.nqsa_atime
-#define        sa_nqmtime              sa_un.sa_nqnfs.nqsa_mtime
-#define        sa_nqflags              sa_un.sa_nqnfs.nqsa_flags
-#define        sa_nqrdev               sa_un.sa_nqnfs.nqsa_rdev
-
-struct nfsv2_statfs {
-       u_long  sf_tsize;
-       u_long  sf_bsize;
-       u_long  sf_blocks;
-       u_long  sf_bfree;
-       u_long  sf_bavail;
-       u_long  sf_files;       /* Nqnfs only */
-       u_long  sf_ffree;       /* ditto      */
+#define sf_tsize       sf_un.sf_nfsv2.nfsv2sf_tsize
+#define sf_bsize       sf_un.sf_nfsv2.nfsv2sf_bsize
+#define sf_blocks      sf_un.sf_nfsv2.nfsv2sf_blocks
+#define sf_bfree       sf_un.sf_nfsv2.nfsv2sf_bfree
+#define sf_bavail      sf_un.sf_nfsv2.nfsv2sf_bavail
+#define sf_tbytes      sf_un.sf_nfsv3.nfsv3sf_tbytes
+#define sf_fbytes      sf_un.sf_nfsv3.nfsv3sf_fbytes
+#define sf_abytes      sf_un.sf_nfsv3.nfsv3sf_abytes
+#define sf_tfiles      sf_un.sf_nfsv3.nfsv3sf_tfiles
+#define sf_ffiles      sf_un.sf_nfsv3.nfsv3sf_ffiles
+#define sf_afiles      sf_un.sf_nfsv3.nfsv3sf_afiles
+#define sf_invarsec    sf_un.sf_nfsv3.nfsv3sf_invarsec
+
+struct nfsv3_fsinfo {
+       u_long          fs_rtmax;
+       u_long          fs_rtpref;
+       u_long          fs_rtmult;
+       u_long          fs_wtmax;
+       u_long          fs_wtpref;
+       u_long          fs_wtmult;
+       u_long          fs_dtpref;
+       nfsuint64       fs_maxfilesize;
+       nfstime3        fs_timedelta;
+       u_long          fs_properties;
 };
 };
+
+struct nfsv3_pathconf {
+       u_long          pc_linkmax;
+       u_long          pc_namemax;
+       u_long          pc_notrunc;
+       u_long          pc_chownrestricted;
+       u_long          pc_caseinsensitive;
+       u_long          pc_casepreserving;
+};
+
+#endif
index afc1a93..20f512c 100644 (file)
@@ -7,9 +7,13 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsrtt.h    8.1 (Berkeley) %G%
+ *     @(#)nfsrtt.h    8.2 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSRTT_H_
+#define _NFS_NFSRTT_H_
+
 /*
  * Definitions for performance monitor.
  * The client and server logging are turned on by setting the global
 /*
  * Definitions for performance monitor.
  * The client and server logging are turned on by setting the global
@@ -52,6 +56,7 @@ struct nfsrtt {
 #define        DRT_TCP         0x02    /* Client used TCP transport */
 #define        DRT_CACHEREPLY  0x04    /* Reply was from recent request cache */
 #define        DRT_CACHEDROP   0x08    /* Rpc request dropped, due to recent reply */
 #define        DRT_TCP         0x02    /* Client used TCP transport */
 #define        DRT_CACHEREPLY  0x04    /* Reply was from recent request cache */
 #define        DRT_CACHEDROP   0x08    /* Rpc request dropped, due to recent reply */
+#define DRT_NFSV3      0x10    /* Rpc used NFS Version 3 */
 
 /*
  * Server log structure
 
 /*
  * Server log structure
@@ -68,3 +73,5 @@ struct nfsdrt {
                struct timeval tstamp;  /* Timestamp of log entry */
        } drt[NFSRTTLOGSIZ];
 };
                struct timeval tstamp;  /* Timestamp of log entry */
        } drt[NFSRTTLOGSIZ];
 };
+
+#endif
index 86439cd..90fd5d3 100644 (file)
@@ -7,14 +7,18 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfsrvcache.h        8.2 (Berkeley) %G%
+ *     @(#)nfsrvcache.h        8.3 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NFSRVCACHE_H_
+#define _NFS_NFSRVCACHE_H_
+
 /*
  * Definitions for the server recent request cache
  */
 
 /*
  * Definitions for the server recent request cache
  */
 
-#define        NFSRVCACHESIZ   256
+#define        NFSRVCACHESIZ   64
 
 struct nfsrvcache {
        TAILQ_ENTRY(nfsrvcache) rc_lru;         /* LRU chain */
 
 struct nfsrvcache {
        TAILQ_ENTRY(nfsrvcache) rc_lru;         /* LRU chain */
@@ -54,3 +58,5 @@ struct nfsrvcache {
 #define        RC_NQNFS        0x10
 #define        RC_INETADDR     0x20
 #define        RC_NAM          0x40
 #define        RC_NQNFS        0x10
 #define        RC_INETADDR     0x20
 #define        RC_NAM          0x40
+
+#endif
index 5ca8627..1e6a18f 100644 (file)
@@ -7,9 +7,13 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nqnfs.h     8.2 (Berkeley) %G%
+ *     @(#)nqnfs.h     8.3 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_NQNFS_H_
+#define _NFS_NQNFS_H_
+
 /*
  * Definitions for NQNFS (Not Quite NFS) cache consistency protocol.
  */
 /*
  * Definitions for NQNFS (Not Quite NFS) cache consistency protocol.
  */
@@ -28,7 +32,7 @@
 #define        NQLCHSZ         256     /* Server hash table size */
 
 #define        NQNFS_PROG      300105  /* As assigned by Sun */
 #define        NQLCHSZ         256     /* Server hash table size */
 
 #define        NQNFS_PROG      300105  /* As assigned by Sun */
-#define        NQNFS_VER1      1
+#define        NQNFS_VER3      3
 #define        NQNFS_EVICTSIZ  156     /* Size of eviction request in bytes */
 
 /*
 #define        NQNFS_EVICTSIZ  156     /* Size of eviction request in bytes */
 
 /*
@@ -36,6 +40,9 @@
  * RAM on the server. The default definitions below assume that NOVRAM is not
  * available.
  */
  * RAM on the server. The default definitions below assume that NOVRAM is not
  * available.
  */
+#ifdef HASNVRAM
+#  undef HASNVRAM
+#endif
 #define        NQSTORENOVRAM(t)
 #define        NQLOADNOVRAM(t)
 
 #define        NQSTORENOVRAM(t)
 #define        NQLOADNOVRAM(t)
 
@@ -109,14 +116,6 @@ struct nqm {
        struct nqhost   lpm_hosts[LC_MOREHOSTSIZ];
 };
 
        struct nqhost   lpm_hosts[LC_MOREHOSTSIZ];
 };
 
-/*
- * Flag bits for flags argument to nqsrv_getlease.
- */
-#define        NQL_READ        LEASE_READ      /* Read Request */
-#define        NQL_WRITE       LEASE_WRITE     /* Write Request */
-#define        NQL_CHECK       0x4             /* Check for lease */
-#define        NQL_NOVAL       0xffffffff      /* Invalid */
-
 /*
  * Special value for slp for local server calls.
  */
 /*
  * Special value for slp for local server calls.
  */
@@ -127,9 +126,9 @@ struct nqm {
  */
 #define        nqsrv_getl(v, l) \
                (void) nqsrv_getlease((v), &nfsd->nd_duration, \
  */
 #define        nqsrv_getl(v, l) \
                (void) nqsrv_getlease((v), &nfsd->nd_duration, \
-                ((nfsd->nd_nqlflag != 0 && nfsd->nd_nqlflag != NQL_NOVAL) ? nfsd->nd_nqlflag : \
-                ((l) | NQL_CHECK)), \
-                nfsd, nam, &cache, &frev, cred)
+                ((nfsd->nd_flag & ND_LEASE) ? (nfsd->nd_flag & ND_LEASE) : \
+                ((l) | ND_CHECK)), \
+                slp, procp, nfsd->nd_nam, &cache, &frev, cred)
 
 /*
  * Client side macros that check for a valid lease.
 
 /*
  * Client side macros that check for a valid lease.
@@ -137,13 +136,13 @@ struct nqm {
 #define        NQNFS_CKINVALID(v, n, f) \
  ((time.tv_sec > (n)->n_expiry && \
  VFSTONFS((v)->v_mount)->nm_timeouts < VFSTONFS((v)->v_mount)->nm_deadthresh) \
 #define        NQNFS_CKINVALID(v, n, f) \
  ((time.tv_sec > (n)->n_expiry && \
  VFSTONFS((v)->v_mount)->nm_timeouts < VFSTONFS((v)->v_mount)->nm_deadthresh) \
-  || ((f) == NQL_WRITE && ((n)->n_flag & NQNFSWRITE) == 0))
+  || ((f) == ND_WRITE && ((n)->n_flag & NQNFSWRITE) == 0))
 
 #define        NQNFS_CKCACHABLE(v, f) \
  ((time.tv_sec <= VTONFS(v)->n_expiry || \
   VFSTONFS((v)->v_mount)->nm_timeouts >= VFSTONFS((v)->v_mount)->nm_deadthresh) \
    && (VTONFS(v)->n_flag & NQNFSNONCACHE) == 0 && \
 
 #define        NQNFS_CKCACHABLE(v, f) \
  ((time.tv_sec <= VTONFS(v)->n_expiry || \
   VFSTONFS((v)->v_mount)->nm_timeouts >= VFSTONFS((v)->v_mount)->nm_deadthresh) \
    && (VTONFS(v)->n_flag & NQNFSNONCACHE) == 0 && \
-   ((f) == NQL_READ || (VTONFS(v)->n_flag & NQNFSWRITE)))
+   ((f) == ND_READ || (VTONFS(v)->n_flag & NQNFSWRITE)))
 
 #define        NQNFS_NEEDLEASE(v, p) \
                (time.tv_sec > VTONFS(v)->n_expiry ? \
 
 #define        NQNFS_NEEDLEASE(v, p) \
                (time.tv_sec > VTONFS(v)->n_expiry ? \
@@ -151,12 +150,12 @@ struct nqm {
                 (((time.tv_sec + NQ_RENEWAL) > VTONFS(v)->n_expiry && \
                   nqnfs_piggy[p]) ? \
                   ((VTONFS(v)->n_flag & NQNFSWRITE) ? \
                 (((time.tv_sec + NQ_RENEWAL) > VTONFS(v)->n_expiry && \
                   nqnfs_piggy[p]) ? \
                   ((VTONFS(v)->n_flag & NQNFSWRITE) ? \
-                   NQL_WRITE : nqnfs_piggy[p]) : 0))
+                   ND_WRITE : nqnfs_piggy[p]) : 0))
 
 /*
  * List head for timer queue.
  */
 
 /*
  * List head for timer queue.
  */
-CIRCLEQ_HEAD(nqleases, nqlease) nqtimerhead;
+CIRCLEQ_HEAD(, nqlease) nqtimerhead;
 
 /*
  * List head for the file handle hash table.
 
 /*
  * List head for the file handle hash table.
@@ -171,4 +170,18 @@ u_long nqfhhash;
  */
 #define        NQNFS_EXPIRED   500
 #define        NQNFS_TRYLATER  501
  */
 #define        NQNFS_EXPIRED   500
 #define        NQNFS_TRYLATER  501
-#define NQNFS_AUTHERR  502
+
+#ifdef KERNEL
+void   nqnfs_lease_updatetime __P((int));
+int    nqsrv_cmpnam __P((struct nfssvc_sock *,struct mbuf *,struct nqhost *));
+int    nqsrv_getlease __P((struct vnode *, u_long *, int,
+               struct nfssvc_sock *, struct proc *, struct mbuf *, int *,
+               u_quad_t *, struct ucred *));
+int    nqnfs_getlease __P((struct vnode *, int, struct ucred *,struct proc *));
+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 *));
+#endif
+
+#endif
index d4df320..264c655 100644 (file)
@@ -7,9 +7,13 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)rpcv2.h     8.1 (Berkeley) %G%
+ *     @(#)rpcv2.h     8.2 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_RPCV2_H_
+#define _NFS_RPCV2_H_
+
 /*
  * Definitions for Sun RPC Version 2, from
  * "RPC: Remote Procedure Call Protocol Specification" RFC1057
 /*
  * Definitions for Sun RPC Version 2, from
  * "RPC: Remote Procedure Call Protocol Specification" RFC1057
 #define        RPCAUTH_NULL    0
 #define        RPCAUTH_UNIX    1
 #define        RPCAUTH_SHORT   2
 #define        RPCAUTH_NULL    0
 #define        RPCAUTH_UNIX    1
 #define        RPCAUTH_SHORT   2
+#define RPCAUTH_KERB4  4
 #define        RPCAUTH_NQNFS   300000
 #define        RPCAUTH_MAXSIZ  400
 #define        RPCAUTH_NQNFS   300000
 #define        RPCAUTH_MAXSIZ  400
+#define        RPCVERF_MAXSIZ  12      /* For Kerb, can actually be 400 */
 #define        RPCAUTH_UNIXGIDS 16
 
 #define        RPCAUTH_UNIXGIDS 16
 
+/*
+ * Constants associated with authentication flavours.
+ */
+#define RPCAKN_FULLNAME        0
+#define RPCAKN_NICKNAME        1
+
 /* Rpc Constants */
 #define        RPC_CALL        0
 #define        RPC_REPLY       1
 /* Rpc Constants */
 #define        RPC_CALL        0
 #define        RPC_REPLY       1
 #define        RPCMNT_NAMELEN  255
 #define        RPCMNT_PATHLEN  1024
 #define        RPCPROG_NFS     100003
 #define        RPCMNT_NAMELEN  255
 #define        RPCMNT_PATHLEN  1024
 #define        RPCPROG_NFS     100003
+
+/*
+ * Structures used for RPCAUTH_KERB4.
+ */
+struct nfsrpc_fullverf {
+       u_long          t1;
+       u_long          t2;
+       u_long          w2;
+};
+
+struct nfsrpc_fullblock {
+       u_long          t1;
+       u_long          t2;
+       u_long          w1;
+       u_long          w2;
+};
+
+struct nfsrpc_nickverf {
+       u_long                  kind;
+       struct nfsrpc_fullverf  verf;
+};
+
+/*
+ * and their sizes in bytes.. If sizeof (struct nfsrpc_xx) != these
+ * constants, well then things will break in mount_nfs and nfsd.
+ */
+#define RPCX_FULLVERF  12
+#define RPCX_FULLBLOCK 16
+#define RPCX_NICKVERF  16
+
+#ifdef NFSKERB
+XXX
+#else
+typedef u_char                 NFSKERBKEY_T[2];
+typedef u_char                 NFSKERBKEYSCHED_T[2];
+#endif
+#define NFS_KERBSRV    "rcmd"          /* Kerberos Service for NFS */
+#define NFS_KERBTTL    (30 * 60)       /* Credential ttl (sec) */
+#define NFS_KERBCLOCKSKEW (5 * 60)     /* Clock skew (sec) */
+#define NFS_KERBW1(t)  (*((u_long *)(&((t).dat[((t).length + 3) & ~0x3]))))
+#endif
index 62d8f00..bd3411a 100644 (file)
@@ -7,9 +7,13 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)xdr_subs.h  8.2 (Berkeley) %G%
+ *     @(#)xdr_subs.h  8.3 (Berkeley) %G%
  */
 
  */
 
+
+#ifndef _NFS_XDR_SUBS_H_
+#define _NFS_XDR_SUBS_H_
+
 /*
  * Macros used for conversion to/from xdr representation by nfs...
  * These use the MACHINE DEPENDENT routines ntohl, htonl
 /*
  * Macros used for conversion to/from xdr representation by nfs...
  * These use the MACHINE DEPENDENT routines ntohl, htonl
 #define        fxdr_unsigned(t, v)     ((t)ntohl((long)(v)))
 #define        txdr_unsigned(v)        (htonl((long)(v)))
 
 #define        fxdr_unsigned(t, v)     ((t)ntohl((long)(v)))
 #define        txdr_unsigned(v)        (htonl((long)(v)))
 
-#define        fxdr_nfstime(f, t) { \
-       register u_long _nsec; \
-       (t)->ts_sec = ntohl(((struct nfsv2_time *)(f))->nfs_sec); \
-       _nsec = ntohl(((struct nfsv2_time *)(f))->nfs_usec); \
-       if (_nsec != VNOVAL) \
-               _nsec *= 1000; \
-       (t)->ts_nsec = _nsec; \
+#define        fxdr_nfsv2time(f, t) { \
+       (t)->ts_sec = ntohl(((struct nfsv2_time *)(f))->nfsv2_sec); \
+       if (((struct nfsv2_time *)(f))->nfsv2_usec != 0xffffffff) \
+               (t)->ts_nsec = 1000 * ntohl(((struct nfsv2_time *)(f))->nfsv2_usec); \
+       else \
+               (t)->ts_nsec = 0; \
 }
 }
-#define        txdr_nfstime(f, t) { \
-       register u_long _nsec = (f)->ts_nsec; \
-       ((struct nfsv2_time *)(t))->nfs_sec = htonl((f)->ts_sec); \
-       if (_nsec != VNOVAL) \
-               _nsec /= 1000; \
-       ((struct nfsv2_time *)(t))->nfs_usec = htonl(_nsec); \
+#define        txdr_nfsv2time(f, t) { \
+       ((struct nfsv2_time *)(t))->nfsv2_sec = htonl((f)->ts_sec); \
+       if ((f)->ts_nsec != -1) \
+               ((struct nfsv2_time *)(t))->nfsv2_usec = htonl((f)->ts_nsec / 1000); \
+       else \
+               ((struct nfsv2_time *)(t))->nfsv2_usec = 0xffffffff; \
 }
 
 }
 
-#define        fxdr_nqtime(f, t) { \
-       (t)->ts_sec = ntohl(((struct nqnfs_time *)(f))->nq_sec); \
-       (t)->ts_nsec = ntohl(((struct nqnfs_time *)(f))->nq_nsec); \
+#define        fxdr_nfsv3time(f, t) { \
+       (t)->ts_sec = ntohl(((struct nfsv3_time *)(f))->nfsv3_sec); \
+       (t)->ts_nsec = ntohl(((struct nfsv3_time *)(f))->nfsv3_nsec); \
 }
 }
-#define        txdr_nqtime(f, t) { \
-       ((struct nqnfs_time *)(t))->nq_sec = htonl((f)->ts_sec); \
-       ((struct nqnfs_time *)(t))->nq_nsec = htonl((f)->ts_nsec); \
+#define        txdr_nfsv3time(f, t) { \
+       ((struct nfsv3_time *)(t))->nfsv3_sec = htonl((f)->ts_sec); \
+       ((struct nfsv3_time *)(t))->nfsv3_nsec = htonl((f)->ts_nsec); \
 }
 
 #define        fxdr_hyper(f, t) { \
 }
 
 #define        fxdr_hyper(f, t) { \
@@ -57,3 +60,5 @@
        ((long *)(t))[0] = htonl(((long *)(f))[_QUAD_HIGHWORD]); \
        ((long *)(t))[1] = htonl(((long *)(f))[_QUAD_LOWWORD]); \
 }
        ((long *)(t))[0] = htonl(((long *)(f))[_QUAD_HIGHWORD]); \
        ((long *)(t))[1] = htonl(((long *)(f))[_QUAD_LOWWORD]); \
 }
+
+#endif