4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / sys / nfs / nfs_vnops.c
index c233c59..03b383c 100644 (file)
@@ -1,13 +1,13 @@
 /*
 /*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
  * %sccs.include.redist.c%
  *
  *
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
  * %sccs.include.redist.c%
  *
- *     @(#)nfs_vnops.c 7.85 (Berkeley) %G%
+ *     @(#)nfs_vnops.c 8.1 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
 #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/specdev.h>
-#include <sys/fifo.h>
 #include <sys/map.h>
 #include <sys/map.h>
+#include <sys/dirent.h>
 
 #include <vm/vm.h>
 
 
 #include <vm/vm.h>
 
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
 #include <nfs/rpcv2.h>
 #include <nfs/nfsv2.h>
 #include <nfs/nfs.h>
 #include <nfs/rpcv2.h>
 #include <nfs/nfsv2.h>
 #include <nfs/nfs.h>
@@ -82,6 +84,7 @@ struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
        { &vop_strategy_desc, nfs_strategy },   /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
        { &vop_strategy_desc, nfs_strategy },   /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
+       { &vop_pathconf_desc, nfs_pathconf },   /* pathconf */
        { &vop_advlock_desc, nfs_advlock },     /* advlock */
        { &vop_blkatoff_desc, nfs_blkatoff },   /* blkatoff */
        { &vop_valloc_desc, nfs_valloc },       /* valloc */
        { &vop_advlock_desc, nfs_advlock },     /* advlock */
        { &vop_blkatoff_desc, nfs_blkatoff },   /* blkatoff */
        { &vop_valloc_desc, nfs_valloc },       /* valloc */
@@ -105,7 +108,7 @@ struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
        { &vop_mknod_desc, spec_mknod },        /* mknod */
        { &vop_open_desc, spec_open },          /* open */
        { &vop_close_desc, nfsspec_close },     /* close */
        { &vop_mknod_desc, spec_mknod },        /* mknod */
        { &vop_open_desc, spec_open },          /* open */
        { &vop_close_desc, nfsspec_close },     /* close */
-       { &vop_access_desc, nfs_access },       /* access */
+       { &vop_access_desc, nfsspec_access },   /* access */
        { &vop_getattr_desc, nfs_getattr },     /* getattr */
        { &vop_setattr_desc, nfs_setattr },     /* setattr */
        { &vop_read_desc, nfsspec_read },       /* read */
        { &vop_getattr_desc, nfs_getattr },     /* getattr */
        { &vop_setattr_desc, nfs_setattr },     /* setattr */
        { &vop_read_desc, nfsspec_read },       /* read */
@@ -132,6 +135,7 @@ struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
        { &vop_strategy_desc, spec_strategy },  /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
        { &vop_strategy_desc, spec_strategy },  /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
+       { &vop_pathconf_desc, spec_pathconf },  /* pathconf */
        { &vop_advlock_desc, spec_advlock },    /* advlock */
        { &vop_blkatoff_desc, spec_blkatoff },  /* blkatoff */
        { &vop_valloc_desc, spec_valloc },      /* valloc */
        { &vop_advlock_desc, spec_advlock },    /* advlock */
        { &vop_blkatoff_desc, spec_blkatoff },  /* blkatoff */
        { &vop_valloc_desc, spec_valloc },      /* valloc */
@@ -153,7 +157,7 @@ struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
        { &vop_mknod_desc, fifo_mknod },        /* mknod */
        { &vop_open_desc, fifo_open },          /* open */
        { &vop_close_desc, nfsfifo_close },     /* close */
        { &vop_mknod_desc, fifo_mknod },        /* mknod */
        { &vop_open_desc, fifo_open },          /* open */
        { &vop_close_desc, nfsfifo_close },     /* close */
-       { &vop_access_desc, nfs_access },       /* access */
+       { &vop_access_desc, nfsspec_access },   /* access */
        { &vop_getattr_desc, nfs_getattr },     /* getattr */
        { &vop_setattr_desc, nfs_setattr },     /* setattr */
        { &vop_read_desc, nfsfifo_read },       /* read */
        { &vop_getattr_desc, nfs_getattr },     /* getattr */
        { &vop_setattr_desc, nfs_setattr },     /* setattr */
        { &vop_read_desc, nfsfifo_read },       /* read */
@@ -180,6 +184,7 @@ struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
        { &vop_strategy_desc, fifo_badop },     /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
        { &vop_strategy_desc, fifo_badop },     /* strategy */
        { &vop_print_desc, nfs_print },         /* print */
        { &vop_islocked_desc, nfs_islocked },   /* islocked */
+       { &vop_pathconf_desc, fifo_pathconf },  /* pathconf */
        { &vop_advlock_desc, fifo_advlock },    /* advlock */
        { &vop_blkatoff_desc, fifo_blkatoff },  /* blkatoff */
        { &vop_valloc_desc, fifo_valloc },      /* valloc */
        { &vop_advlock_desc, fifo_advlock },    /* advlock */
        { &vop_blkatoff_desc, fifo_blkatoff },  /* blkatoff */
        { &vop_valloc_desc, fifo_valloc },      /* valloc */
@@ -193,16 +198,19 @@ struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
        { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
 #endif /* FIFO */
 
        { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
 #endif /* FIFO */
 
+void nqnfs_clientlease();
+
 /*
  * Global variables
  */
 extern u_long nfs_procids[NFS_NPROCS];
 /*
  * Global variables
  */
 extern u_long nfs_procids[NFS_NPROCS];
-extern u_long nfs_prog, nfs_vers;
+extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false;
 extern char nfsiobuf[MAXPHYS+NBPG];
 extern char nfsiobuf[MAXPHYS+NBPG];
-struct buf nfs_bqueue;         /* Queue head for nfsiod's */
 struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 int nfs_numasync = 0;
 struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
 int nfs_numasync = 0;
-#define        DIRHDSIZ        (sizeof (struct readdir) - (MAXNAMLEN + 1))
+/* Queue head for nfsiod's */
+struct queue_entry nfs_bufq;
+#define        DIRHDSIZ        (sizeof (struct dirent) - (MAXNAMLEN + 1))
 
 /*
  * nfs null call from vfs.
 
 /*
  * nfs null call from vfs.
@@ -225,7 +233,9 @@ nfs_null(vp, cred, procp)
 
 /*
  * nfs access vnode op.
 
 /*
  * nfs access vnode op.
- * Essentially just get vattr and then imitate iaccess()
+ * 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.
  */
 int
 nfs_access(ap)
  */
 int
 nfs_access(ap)
@@ -236,47 +246,51 @@ nfs_access(ap)
                struct proc *a_p;
        } */ *ap;
 {
                struct proc *a_p;
        } */ *ap;
 {
-       register struct vattr *vap;
-       register gid_t *gp;
-       register struct ucred *cred = ap->a_cred;
-       mode_t mode = ap->a_mode;
-       struct vattr vattr;
-       register int i;
-       int error;
+       register struct vnode *vp = ap->a_vp;
+       register u_long *tl;
+       register caddr_t cp;
+       caddr_t bpos, dpos;
+       int error = 0;
+       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
 
        /*
 
        /*
-        * If you're the super-user,
-        * you always get access.
+        * For nqnfs, do an access rpc, otherwise you are stuck emulating
+        * ufs_access() locally using the vattr. This may not be correct,
+        * since the server may apply other access criteria such as
+        * client uid-->server uid mapping that we do not know about, but
+        * this is better than just returning anything that is lying about
+        * in the cache.
         */
         */
-       if (cred->cr_uid == 0)
-               return (0);
-       vap = &vattr;
-       if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p))
+       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 (ap->a_mode & VREAD)
+                       *tl++ = nfs_true;
+               else
+                       *tl++ = nfs_false;
+               if (ap->a_mode & VWRITE)
+                       *tl++ = nfs_true;
+               else
+                       *tl++ = nfs_false;
+               if (ap->a_mode & VEXEC)
+                       *tl = nfs_true;
+               else
+                       *tl = nfs_false;
+               nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred);
+               nfsm_reqdone;
                return (error);
                return (error);
-       /*
-        * Access check is based on only one of owner, group, public.
-        * If not owner, then check group. If not a member of the
-        * group, then check public access.
-        */
-       if (cred->cr_uid != vap->va_uid) {
-               mode >>= 3;
-               gp = cred->cr_groups;
-               for (i = 0; i < cred->cr_ngroups; i++, gp++)
-                       if (vap->va_gid == *gp)
-                               goto found;
-               mode >>= 3;
-found:
-               ;
-       }
-       if ((vap->va_mode & mode) != 0)
-               return (0);
-       return (EACCES);
+       } else
+               return (nfsspec_access(ap));
 }
 
 /*
  * nfs open vnode op
 }
 
 /*
  * nfs open vnode op
- * Just check to see if the type is ok
+ * Check to see if the type is ok
  * and that deletion is not in progress.
  * and that deletion is not in progress.
+ * For paged in text files, you will need to flush the page cache
+ * if consistency is lost.
  */
 /* ARGSUSED */
 int
  */
 /* ARGSUSED */
 int
@@ -289,11 +303,59 @@ nfs_open(ap)
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       struct vattr vattr;
+       int error;
 
        if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
                return (EACCES);
 
        if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
                return (EACCES);
-       if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0)
-               VTONFS(vp)->n_attrstamp = 0; /* For Open/Close consistency */
+       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)) {
+                   do {
+                       error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p);
+                   } 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, ap->a_cred,
+                               ap->a_p, 1)) == EINTR)
+                               return (error);
+                       (void) vnode_pager_uncache(vp);
+                       np->n_brev = np->n_lrev;
+                   }
+               }
+           } else {
+               if (np->n_flag & NMODIFIED) {
+                       if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+                               ap->a_p, 1)) == EINTR)
+                               return (error);
+                       (void) vnode_pager_uncache(vp);
+                       np->n_attrstamp = 0;
+                       np->n_direofoffset = 0;
+                       if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+                               return (error);
+                       np->n_mtime = vattr.va_mtime.ts_sec;
+               } else {
+                       if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p))
+                               return (error);
+                       if (np->n_mtime != vattr.va_mtime.ts_sec) {
+                               np->n_direofoffset = 0;
+                               if ((error = nfs_vinvalbuf(vp, V_SAVE,
+                                       ap->a_cred, ap->a_p, 1)) == EINTR)
+                                       return (error);
+                               (void) vnode_pager_uncache(vp);
+                               np->n_mtime = vattr.va_mtime.ts_sec;
+                       }
+               }
+           }
+       } else if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
+               np->n_attrstamp = 0; /* For Open/Close consistency */
        return (0);
 }
 
        return (0);
 }
 
@@ -319,8 +381,7 @@ 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 = vinvalbuf(vp, TRUE, ap->a_cred, ap->a_p);
-               np->n_flag &= ~NMODIFIED;
+               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) {
@@ -353,13 +414,8 @@ nfs_getattr(ap)
        /*
         * Update local times for special files.
         */
        /*
         * Update local times for special files.
         */
-       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 | NUPD))
                np->n_flag |= NCHG;
                np->n_flag |= NCHG;
-       }
        /*
         * First look in the cache.
         */
        /*
         * First look in the cache.
         */
@@ -392,44 +448,61 @@ nfs_setattr(ap)
        register long t1;
        caddr_t bpos, dpos, cp2;
        u_long *tl;
        register long t1;
        caddr_t bpos, dpos, cp2;
        u_long *tl;
-       int error = 0;
+       int error = 0, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        register struct vattr *vap = ap->a_vap;
        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;
+       u_quad_t frev, tsize;
 
 
+       if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL ||
+               vap->va_atime.ts_sec != VNOVAL) {
+               if (vap->va_size != VNOVAL) {
+                       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 ((np->n_flag & NMODIFIED) &&
+                       (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
+                        ap->a_p, 1)) == EINTR)
+                       return (error);
+       }
        nfsstats.rpccnt[NFSPROC_SETATTR]++;
        nfsstats.rpccnt[NFSPROC_SETATTR]++;
-       nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR);
+       isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+       nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq));
        nfsm_fhtom(vp);
        nfsm_fhtom(vp);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR);
-       if (vap->va_mode == 0xffff)
+       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);
                sp->sa_mode = VNOVAL;
        else
                sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode);
-       if (vap->va_uid == 0xffff)
+       if (vap->va_uid == (uid_t)-1)
                sp->sa_uid = VNOVAL;
        else
                sp->sa_uid = txdr_unsigned(vap->va_uid);
                sp->sa_uid = VNOVAL;
        else
                sp->sa_uid = txdr_unsigned(vap->va_uid);
-       if (vap->va_gid == 0xffff)
+       if (vap->va_gid == (gid_t)-1)
                sp->sa_gid = VNOVAL;
        else
                sp->sa_gid = txdr_unsigned(vap->va_gid);
                sp->sa_gid = VNOVAL;
        else
                sp->sa_gid = txdr_unsigned(vap->va_gid);
-       sp->sa_size = txdr_unsigned(vap->va_size);
-       sp->sa_atime.tv_sec = txdr_unsigned(vap->va_atime.ts_sec);
-       sp->sa_atime.tv_usec = txdr_unsigned(vap->va_flags);
-       txdr_time(&vap->va_mtime, &sp->sa_mtime);
-       if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL ||
-           vap->va_atime.ts_sec != VNOVAL) {
-               if (np->n_flag & NMODIFIED) {
-                       if (vap->va_size == 0)
-                               error =
-                                   vinvalbuf(vp, FALSE, ap->a_cred, ap->a_p);
-                       else
-                               error =
-                                   vinvalbuf(vp, TRUE, ap->a_cred, ap->a_p);
-                       np->n_flag &= ~NMODIFIED;
-               }
+       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 {
+               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);
        }
        nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred);
        nfsm_loadattr(vp, (struct vattr *)0);
@@ -441,6 +514,10 @@ nfs_setattr(ap)
                        np->n_brev = frev;
        }
        nfsm_reqdone;
                        np->n_brev = frev;
        }
        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);
 }
 
@@ -461,6 +538,7 @@ nfs_lookup(ap)
        register struct componentname *cnp = ap->a_cnp;
        register struct vnode *dvp = ap->a_dvp;
        register struct vnode **vpp = ap->a_vpp;
        register struct componentname *cnp = ap->a_cnp;
        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 u_long *tl;
        register caddr_t cp;
        register struct vnode *vdp;
        register u_long *tl;
        register caddr_t cp;
@@ -481,8 +559,8 @@ nfs_lookup(ap)
        *vpp = NULL;
        if (dvp->v_type != VDIR)
                return (ENOTDIR);
        *vpp = NULL;
        if (dvp->v_type != VDIR)
                return (ENOTDIR);
-       lockparent = cnp->cn_flags & LOCKPARENT;
-       wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
+       lockparent = flags & LOCKPARENT;
+       wantparent = flags & (LOCKPARENT|WANTPARENT);
        nmp = VFSTONFS(dvp->v_mount);
        np = VTONFS(dvp);
        if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
        nmp = VFSTONFS(dvp->v_mount);
        np = VTONFS(dvp);
        if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
@@ -503,19 +581,27 @@ nfs_lookup(ap)
                if (!error) {
                        if (vpid == vdp->v_id) {
                           if (nmp->nm_flag & NFSMNT_NQNFS) {
                if (!error) {
                        if (vpid == vdp->v_id) {
                           if (nmp->nm_flag & NFSMNT_NQNFS) {
-                               if (NQNFS_CKCACHABLE(dvp, NQL_READ)) {
+                               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);
                                        if (np->n_lrev != np->n_brev ||
                                            (np->n_flag & NMODIFIED)) {
                                                np->n_direofoffset = 0;
                                                cache_purge(dvp);
-                                               error = vinvalbuf(dvp, FALSE,
-                                                   cnp->cn_cred, cnp->cn_proc);
-                                               np->n_flag &= ~NMODIFIED;
+                                               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 &&
                                                np->n_brev = np->n_lrev;
                                        } else {
                                                nfsstats.lookupcache_hits++;
                                                if (cnp->cn_nameiop != LOOKUP &&
-                                                   (cnp->cn_flags&ISLASTCN))
+                                                   (flags & ISLASTCN))
                                                    cnp->cn_flags |= SAVENAME;
                                                return (0);
                                        }
                                                    cnp->cn_flags |= SAVENAME;
                                                return (0);
                                        }
@@ -524,7 +610,7 @@ nfs_lookup(ap)
                               vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) {
                                nfsstats.lookupcache_hits++;
                                if (cnp->cn_nameiop != LOOKUP &&
                               vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) {
                                nfsstats.lookupcache_hits++;
                                if (cnp->cn_nameiop != LOOKUP &&
-                                   (cnp->cn_flags&ISLASTCN))
+                                   (flags & ISLASTCN))
                                        cnp->cn_flags |= SAVENAME;
                                return (0);
                           }
                                        cnp->cn_flags |= SAVENAME;
                                return (0);
                           }
@@ -545,16 +631,13 @@ nfs_lookup(ap)
         * being looked up.
         */
        if (nmp->nm_flag & NFSMNT_NQNFS) {
         * being looked up.
         */
        if (nmp->nm_flag & NFSMNT_NQNFS) {
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
                if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
-                   ((cnp->cn_flags&MAKEENTRY) &&
-                   (cnp->cn_nameiop != DELETE || !(cnp->cn_flags&ISLASTCN)))) {
-                       nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
-                       *tl++ = txdr_unsigned(NQL_READ);
+                   ((cnp->cn_flags & MAKEENTRY) &&
+                   (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))))
                        *tl = txdr_unsigned(nmp->nm_leaseterm);
                        *tl = txdr_unsigned(nmp->nm_leaseterm);
-               } else {
-                       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+               else
                        *tl = 0;
                        *tl = 0;
-               }
        }
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
        }
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
@@ -563,9 +646,9 @@ nfs_lookup(ap)
 nfsmout:
        if (error) {
                if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
 nfsmout:
        if (error) {
                if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
-                   (cnp->cn_flags & ISLASTCN) && error == ENOENT)
+                   (flags & ISLASTCN) && error == ENOENT)
                        error = EJUSTRETURN;
                        error = EJUSTRETURN;
-               if (cnp->cn_nameiop != LOOKUP && (cnp->cn_flags&ISLASTCN))
+               if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
                        cnp->cn_flags |= SAVENAME;
                return (error);
        }
                        cnp->cn_flags |= SAVENAME;
                return (error);
        }
@@ -585,7 +668,7 @@ nfsmout:
        /*
         * Handle RENAME case...
         */
        /*
         * Handle RENAME case...
         */
-       if (cnp->cn_nameiop == RENAME && wantparent && (cnp->cn_flags&ISLASTCN)) {
+       if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
                if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
                        m_freem(mrep);
                        return (EISDIR);
                if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
                        m_freem(mrep);
                        return (EISDIR);
@@ -624,50 +707,15 @@ nfsmout:
        }
        m_freem(mrep);
        *vpp = newvp;
        }
        m_freem(mrep);
        *vpp = newvp;
-       if (cnp->cn_nameiop != LOOKUP && (cnp->cn_flags&ISLASTCN))
+       if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
                cnp->cn_flags |= SAVENAME;
                cnp->cn_flags |= SAVENAME;
-       if ((cnp->cn_flags&MAKEENTRY) &&
-           (cnp->cn_nameiop != DELETE || !(cnp->cn_flags&ISLASTCN))) {
+       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;
                if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
                        np->n_ctime = np->n_vattr.va_ctime.ts_sec;
-               else if (nqlflag && reqtime > time.tv_sec) {
-                       if (np->n_tnext) {
-                               if (np->n_tnext == (struct nfsnode *)nmp)
-                                       nmp->nm_tprev = np->n_tprev;
-                               else
-                                       np->n_tnext->n_tprev = np->n_tprev;
-                               if (np->n_tprev == (struct nfsnode *)nmp)
-                                       nmp->nm_tnext = np->n_tnext;
-                               else
-                                       np->n_tprev->n_tnext = np->n_tnext;
-                               if (nqlflag == NQL_WRITE)
-                                       np->n_flag |= NQNFSWRITE;
-                       } else if (nqlflag == NQL_READ)
-                               np->n_flag &= ~NQNFSWRITE;
-                       else
-                               np->n_flag |= NQNFSWRITE;
-                       if (cachable)
-                               np->n_flag &= ~NQNFSNONCACHE;
-                       else
-                               np->n_flag |= NQNFSNONCACHE;
-                       np->n_expiry = reqtime;
-                       np->n_lrev = frev;
-                       tp = nmp->nm_tprev;
-                       while (tp != (struct nfsnode *)nmp && tp->n_expiry > np->n_expiry)
-                               tp = tp->n_tprev;
-                       if (tp == (struct nfsnode *)nmp) {
-                               np->n_tnext = nmp->nm_tnext;
-                               nmp->nm_tnext = np;
-                       } else {
-                               np->n_tnext = tp->n_tnext;
-                               tp->n_tnext = np;
-                       }
-                       np->n_tprev = tp;
-                       if (np->n_tnext == (struct nfsnode *)nmp)
-                               nmp->nm_tprev = np;
-                       else
-                               np->n_tnext->n_tprev = np;
-               }
+               else if (nqlflag && reqtime > time.tv_sec)
+                       nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime,
+                               frev);
                cache_enter(dvp, *vpp, cnp);
        }
        return (0);
                cache_enter(dvp, *vpp, cnp);
        }
        return (0);
@@ -760,15 +808,23 @@ nfs_readrpc(vp, uiop, cred)
 
        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)
+               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);
        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);
-               *tl++ = txdr_unsigned(uiop->uio_offset);
-               *tl++ = txdr_unsigned(len);
-               *tl = 0;
+               if (nmp->nm_flag & NFSMNT_NQNFS) {
+                       txdr_hyper(&uiop->uio_offset, tl);
+                       *(tl + 2) = txdr_unsigned(len);
+               } else {
+                       *tl++ = txdr_unsigned(uiop->uio_offset);
+                       *tl++ = txdr_unsigned(len);
+                       *tl = 0;
+               }
                nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
                nfsm_loadattr(vp, (struct vattr *)0);
                nfsm_strsiz(retlen, nmp->nm_rsize);
                nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
                nfsm_loadattr(vp, (struct vattr *)0);
                nfsm_strsiz(retlen, nmp->nm_rsize);
@@ -787,10 +843,11 @@ nfsmout:
  * nfs write call
  */
 int
  * nfs write call
  */
 int
-nfs_writerpc(vp, uiop, cred)
+nfs_writerpc(vp, uiop, cred, ioflags)
        register struct vnode *vp;
        struct uio *uiop;
        struct ucred *cred;
        register struct vnode *vp;
        struct uio *uiop;
        struct ucred *cred;
+       int ioflags;
 {
        register u_long *tl;
        register caddr_t cp;
 {
        register u_long *tl;
        register caddr_t cp;
@@ -805,15 +862,28 @@ nfs_writerpc(vp, uiop, cred)
 
        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)
+               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);
        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);
-               *(tl+1) = txdr_unsigned(uiop->uio_offset);
-               *(tl+3) = txdr_unsigned(len);
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4);
+               if (nmp->nm_flag & NFSMNT_NQNFS) {
+                       txdr_hyper(&uiop->uio_offset, tl);
+                       tl += 2;
+                       if (ioflags & IO_APPEND)
+                               *tl++ = txdr_unsigned(1);
+                       else
+                               *tl++ = 0;
+               } else {
+                       *++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);
                nfsm_uiotom(uiop, len);
                nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
                nfsm_loadattr(vp, (struct vattr *)0);
@@ -856,12 +926,16 @@ nfs_mknod(ap)
        register struct nfsv2_sattr *sp;
        register u_long *tl;
        register caddr_t cp;
        register struct nfsv2_sattr *sp;
        register u_long *tl;
        register caddr_t cp;
-       register long t2;
+       register long t1, t2;
+       struct vnode *newvp;
+       struct vattr vattr;
+       char *cp2;
        caddr_t bpos, dpos;
        caddr_t bpos, dpos;
-       int error = 0;
+       int error = 0, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        u_long rdev;
 
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        u_long rdev;
 
+       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
        if (vap->va_type == VCHR || vap->va_type == VBLK)
                rdev = txdr_unsigned(vap->va_rdev);
 #ifdef FIFO
        if (vap->va_type == VCHR || vap->va_type == VBLK)
                rdev = txdr_unsigned(vap->va_rdev);
 #ifdef FIFO
@@ -873,21 +947,35 @@ nfs_mknod(ap)
                vput(dvp);
                return (EOPNOTSUPP);
        }
                vput(dvp);
                return (EOPNOTSUPP);
        }
+       if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+               VOP_ABORTOP(dvp, cnp);
+               vput(dvp);
+               return (error);
+       }
        nfsstats.rpccnt[NFSPROC_CREATE]++;
        nfsm_reqhead(dvp, NFSPROC_CREATE,
        nfsstats.rpccnt[NFSPROC_CREATE]++;
        nfsm_reqhead(dvp, NFSPROC_CREATE,
-         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR);
+         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR);
+       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_mode = vtonfs_mode(vap->va_type, 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 = rdev;
-       /* or should these be VNOVAL ?? */
-       txdr_time(&vap->va_atime, &sp->sa_atime);
-       txdr_time(&vap->va_mtime, &sp->sa_mtime);
+       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);
+       } else {
+               sp->sa_nfssize = rdev;
+               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+       }
        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, newvp);
        nfsm_reqdone;
        nfsm_reqdone;
+       if (!error && (cnp->cn_flags & MAKEENTRY))
+               cache_enter(dvp, newvp, cnp);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        vrele(dvp);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        vrele(dvp);
@@ -914,25 +1002,43 @@ nfs_create(ap)
        register caddr_t cp;
        register long t1, t2;
        caddr_t bpos, dpos, cp2;
        register caddr_t cp;
        register long t1, t2;
        caddr_t bpos, dpos, cp2;
-       int error = 0;
+       int error = 0, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       struct vattr vattr;
 
 
+       if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+               VOP_ABORTOP(dvp, cnp);
+               vput(dvp);
+               return (error);
+       }
        nfsstats.rpccnt[NFSPROC_CREATE]++;
        nfsstats.rpccnt[NFSPROC_CREATE]++;
+       isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS);
        nfsm_reqhead(dvp, NFSPROC_CREATE,
        nfsm_reqhead(dvp, NFSPROC_CREATE,
-         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR);
+         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq));
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR);
+       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_mode = vtonfs_mode(vap->va_type, 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 = txdr_unsigned(0);
-       /* or should these be VNOVAL ?? */
-       txdr_time(&vap->va_atime, &sp->sa_atime);
-       txdr_time(&vap->va_mtime, &sp->sa_mtime);
+       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);
+       } else {
+               sp->sa_nfssize = 0;
+               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+       }
        nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
        nfsm_mtofh(dvp, *ap->a_vpp);
        nfsm_reqdone;
        nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
        nfsm_mtofh(dvp, *ap->a_vpp);
        nfsm_reqdone;
+       if (!error && (cnp->cn_flags & MAKEENTRY))
+               cache_enter(dvp, *ap->a_vpp, cnp);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        vrele(dvp);
        FREE(cnp->cn_pnbuf, M_NAMEI);
        VTONFS(dvp)->n_flag |= NMODIFIED;
        vrele(dvp);
@@ -986,7 +1092,9 @@ nfs_remove(ap)
                 * Throw away biocache buffers. Mainly to avoid
                 * unnecessary delayed writes.
                 */
                 * Throw away biocache buffers. Mainly to avoid
                 * unnecessary delayed writes.
                 */
-               error = vinvalbuf(vp, FALSE, cnp->cn_cred, cnp->cn_proc);
+               error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
+               if (error == EINTR)
+                       return (error);
                /* Do the rpc */
                nfsstats.rpccnt[NFSPROC_REMOVE]++;
                nfsm_reqhead(dvp, NFSPROC_REMOVE,
                /* Do the rpc */
                nfsstats.rpccnt[NFSPROC_REMOVE]++;
                nfsm_reqhead(dvp, NFSPROC_REMOVE,
@@ -1209,23 +1317,33 @@ nfs_symlink(ap)
        register caddr_t cp;
        register long t2;
        caddr_t bpos, dpos;
        register caddr_t cp;
        register long t2;
        caddr_t bpos, dpos;
-       int slen, error = 0;
+       int slen, error = 0, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
 
        nfsstats.rpccnt[NFSPROC_SYMLINK]++;
        slen = strlen(ap->a_target);
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
 
        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_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+
-           nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR);
+           nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq));
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
        nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR);
+       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);
        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);
-       sp->sa_size = txdr_unsigned(VNOVAL);
-       txdr_time(&vap->va_atime, &sp->sa_atime);       /* or VNOVAL ?? */
-       txdr_time(&vap->va_mtime, &sp->sa_mtime);       /* or VNOVAL ?? */
+       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);
+       }
        nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
        nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
        nfsm_reqdone;
        FREE(cnp->cn_pnbuf, M_NAMEI);
@@ -1261,22 +1379,38 @@ nfs_mkdir(ap)
        register long t1, t2;
        register int len;
        caddr_t bpos, dpos, cp2;
        register long t1, t2;
        register int len;
        caddr_t bpos, dpos, cp2;
-       int error = 0, firsttry = 1;
+       int error = 0, firsttry = 1, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       struct vattr vattr;
 
 
+       if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
+               VOP_ABORTOP(dvp, cnp);
+               vput(dvp);
+               return (error);
+       }
        len = cnp->cn_namelen;
        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);
+         NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq));
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
        nfsm_fhtom(dvp);
        nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
-       nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR);
+       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_mode = vtonfs_mode(VDIR, 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 = txdr_unsigned(VNOVAL);
-       txdr_time(&vap->va_atime, &sp->sa_atime);       /* or VNOVAL ?? */
-       txdr_time(&vap->va_mtime, &sp->sa_mtime);       /* or VNOVAL ?? */
+       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);
+       } else {
+               sp->sa_nfssize = -1;
+               txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime);
+               txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime);
+       }
        nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
        nfsm_mtofh(dvp, *vpp);
        nfsm_reqdone;
        nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
        nfsm_mtofh(dvp, *vpp);
        nfsm_reqdone;
@@ -1418,7 +1552,7 @@ nfs_readdirrpc(vp, uiop, cred)
        struct ucred *cred;
 {
        register long len;
        struct ucred *cred;
 {
        register long len;
-       register struct readdir *dp;
+       register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
        register long t1;
        register u_long *tl;
        register caddr_t cp;
        register long t1;
@@ -1430,8 +1564,8 @@ nfs_readdirrpc(vp, uiop, cred)
        caddr_t dpos2;
        int siz;
        int more_dirs = 1;
        caddr_t dpos2;
        int siz;
        int more_dirs = 1;
-       off_t off, savoff;
-       struct readdir *savdp;
+       u_long off, savoff;
+       struct dirent *savdp;
        struct nfsmount *nmp;
        struct nfsnode *np = VTONFS(vp);
        long tresid;
        struct nfsmount *nmp;
        struct nfsnode *np = VTONFS(vp);
        long tresid;
@@ -1446,10 +1580,11 @@ nfs_readdirrpc(vp, uiop, cred)
        while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
                nfsstats.rpccnt[NFSPROC_READDIR]++;
                nfsm_reqhead(vp, NFSPROC_READDIR,
        while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
                nfsstats.rpccnt[NFSPROC_READDIR]++;
                nfsm_reqhead(vp, NFSPROC_READDIR,
-                       NFSX_FH+2*NFSX_UNSIGNED);
+                       NFSX_FH + 2 * NFSX_UNSIGNED);
                nfsm_fhtom(vp);
                nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
-               *tl++ = txdr_unsigned(uiop->uio_offset);
+               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_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
                *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
                        nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
                nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
@@ -1462,23 +1597,23 @@ nfs_readdirrpc(vp, uiop, cred)
                md2 = md;
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
                md2 = md;
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
-               off = uiop->uio_offset;
 #ifdef lint
 #ifdef lint
-               dp = (struct readdir *)0;
+               dp = (struct dirent *)0;
 #endif /* lint */
                while (more_dirs && siz < uiop->uio_resid) {
                        savoff = off;           /* Hold onto offset and dp */
                        savdp = dp;
 #endif /* lint */
                while (more_dirs && siz < uiop->uio_resid) {
                        savoff = off;           /* Hold onto offset and dp */
                        savdp = dp;
-                       nfsm_dissecton(tl, u_long *, 2*NFSX_UNSIGNED);
-                       dp = (struct readdir *)tl;
-                       dp->d_ino = fxdr_unsigned(u_long, *tl++);
+                       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);
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
                        len = fxdr_unsigned(int, *tl);
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                                error = EBADRPC;
                                m_freem(mrep);
                                goto nfsmout;
                        }
-                       dp->d_namlen = (u_short)len;
+                       dp->d_namlen = (u_char)len;
+                       dp->d_type = DT_UNKNOWN;
                        nfsm_adv(len);          /* Point past name */
                        tlen = nfsm_rndup(len);
                        /*
                        nfsm_adv(len);          /* Point past name */
                        tlen = nfsm_rndup(len);
                        /*
@@ -1490,18 +1625,18 @@ nfs_readdirrpc(vp, uiop, cred)
                                nfsm_adv(tlen - len);
                                len = tlen;
                        }
                                nfsm_adv(tlen - len);
                                len = tlen;
                        }
-                       nfsm_dissecton(tl, u_long *, 2*NFSX_UNSIGNED);
-                       off = fxdr_unsigned(off_t, *tl);
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+                       off = fxdr_unsigned(u_long, *tl);
                        *tl++ = 0;      /* Ensures null termination of name */
                        more_dirs = fxdr_unsigned(int, *tl);
                        *tl++ = 0;      /* Ensures null termination of name */
                        more_dirs = fxdr_unsigned(int, *tl);
-                       dp->d_reclen = len+4*NFSX_UNSIGNED;
+                       dp->d_reclen = len + 4 * NFSX_UNSIGNED;
                        siz += dp->d_reclen;
                }
                /*
                 * If at end of rpc data, get the eof boolean
                 */
                if (!more_dirs) {
                        siz += dp->d_reclen;
                }
                /*
                 * If at end of rpc data, get the eof boolean
                 */
                if (!more_dirs) {
-                       nfsm_dissecton(tl, u_long *, NFSX_UNSIGNED);
+                       nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
 
                        /*
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
 
                        /*
@@ -1526,7 +1661,7 @@ nfs_readdirrpc(vp, uiop, cred)
                        md = md2;
                        dpos = dpos2;
                        nfsm_mtouio(uiop, siz);
                        md = md2;
                        dpos = dpos2;
                        nfsm_mtouio(uiop, siz);
-                       uiop->uio_offset = off;
+                       uiop->uio_offset = (off_t)off;
                } else
                        more_dirs = 0;  /* Ugh, never happens, but in case.. */
                m_freem(mrep);
                } else
                        more_dirs = 0;  /* Ugh, never happens, but in case.. */
                m_freem(mrep);
@@ -1538,7 +1673,7 @@ nfs_readdirrpc(vp, uiop, cred)
        if (uiop->uio_resid < tresid) {
                len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
                if (len > 0) {
        if (uiop->uio_resid < tresid) {
                len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1);
                if (len > 0) {
-                       dp = (struct readdir *)
+                       dp = (struct dirent *)
                                (uiop->uio_iov->iov_base - lastlen);
                        dp->d_reclen += len;
                        uiop->uio_iov->iov_base += len;
                                (uiop->uio_iov->iov_base - lastlen);
                        dp->d_reclen += len;
                        uiop->uio_iov->iov_base += len;
@@ -1551,8 +1686,7 @@ nfsmout:
 }
 
 /*
 }
 
 /*
- * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc() when
- * the "rdirlook" mount option is specified.
+ * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc().
  */
 int
 nfs_readdirlookrpc(vp, uiop, cred)
  */
 int
 nfs_readdirlookrpc(vp, uiop, cred)
@@ -1561,7 +1695,7 @@ nfs_readdirlookrpc(vp, uiop, cred)
        struct ucred *cred;
 {
        register int len;
        struct ucred *cred;
 {
        register int len;
-       register struct readdir *dp;
+       register struct dirent *dp;
        register u_long *tl;
        register caddr_t cp;
        register long t1;
        register u_long *tl;
        register caddr_t cp;
        register long t1;
@@ -1569,13 +1703,12 @@ nfs_readdirlookrpc(vp, uiop, cred)
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct nameidata nami, *ndp = &nami;
        struct componentname *cnp = &ndp->ni_cnd;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        struct nameidata nami, *ndp = &nami;
        struct componentname *cnp = &ndp->ni_cnd;
-       off_t off, endoff;
+       u_long off, endoff, fileno;
        time_t reqtime, ltime;
        struct nfsmount *nmp;
        struct nfsnode *np, *tp;
        struct vnode *newvp;
        nfsv2fh_t *fhp;
        time_t reqtime, ltime;
        struct nfsmount *nmp;
        struct nfsnode *np, *tp;
        struct vnode *newvp;
        nfsv2fh_t *fhp;
-       u_long fileno;
        u_quad_t frev;
        int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i;
        int cachable;
        u_quad_t frev;
        int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i;
        int cachable;
@@ -1594,27 +1727,32 @@ nfs_readdirlookrpc(vp, uiop, cred)
        while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
                nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++;
                nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK,
        while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) {
                nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++;
                nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK,
-                       NFSX_FH+3*NFSX_UNSIGNED);
+                       NFSX_FH + 3 * NFSX_UNSIGNED);
                nfsm_fhtom(vp);
                nfsm_fhtom(vp);
-               nfsm_build(tl, u_long *, 3*NFSX_UNSIGNED);
-               *tl++ = txdr_unsigned(uiop->uio_offset);
+               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));
                *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
                        nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1));
-               *tl = txdr_unsigned(nmp->nm_leaseterm);
+               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);
                more_dirs = fxdr_unsigned(int, *tl);
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
                reqtime = time.tv_sec;
                nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred);
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                more_dirs = fxdr_unsigned(int, *tl);
        
                /* loop thru the dir entries, doctoring them to 4bsd form */
-               off = uiop->uio_offset;
                bigenough = 1;
                while (more_dirs && bigenough) {
                        doit = 1;
                bigenough = 1;
                while (more_dirs && bigenough) {
                        doit = 1;
-                       nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
-                       cachable = fxdr_unsigned(int, *tl++);
-                       ltime = reqtime + fxdr_unsigned(int, *tl++);
-                       fxdr_hyper(tl, &frev);
+                       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);
                        nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH);
                        if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) {
                                VREF(vp);
@@ -1628,7 +1766,7 @@ nfs_readdirlookrpc(vp, uiop, cred)
                        if (error = nfs_loadattrcache(&newvp, &md, &dpos,
                                (struct vattr *)0))
                                doit = 0;
                        if (error = nfs_loadattrcache(&newvp, &md, &dpos,
                                (struct vattr *)0))
                                doit = 0;
-                       nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
                        fileno = fxdr_unsigned(u_long, *tl++);
                        len = fxdr_unsigned(int, *tl);
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
                        fileno = fxdr_unsigned(u_long, *tl++);
                        len = fxdr_unsigned(int, *tl);
                        if (len <= 0 || len > NFS_MAXNAMLEN) {
@@ -1640,10 +1778,12 @@ nfs_readdirlookrpc(vp, uiop, cred)
                        if ((tlen + DIRHDSIZ) > uiop->uio_resid)
                                bigenough = 0;
                        if (bigenough && doit) {
                        if ((tlen + DIRHDSIZ) > uiop->uio_resid)
                                bigenough = 0;
                        if (bigenough && doit) {
-                               dp = (struct readdir *)uiop->uio_iov->iov_base;
-                               dp->d_ino = fileno;
+                               dp = (struct dirent *)uiop->uio_iov->iov_base;
+                               dp->d_fileno = 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));
                                uiop->uio_resid -= DIRHDSIZ;
                                uiop->uio_iov->iov_base += DIRHDSIZ;
                                uiop->uio_iov->iov_len -= DIRHDSIZ;
                                uiop->uio_resid -= DIRHDSIZ;
                                uiop->uio_iov->iov_base += DIRHDSIZ;
                                uiop->uio_iov->iov_len -= DIRHDSIZ;
@@ -1661,41 +1801,12 @@ nfs_readdirlookrpc(vp, uiop, cred)
                                cnp->cn_hash = 0;
                                for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++)
                                        cnp->cn_hash += (unsigned char)*cp * i;
                                cnp->cn_hash = 0;
                                for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++)
                                        cnp->cn_hash += (unsigned char)*cp * i;
-                               if (ltime > time.tv_sec) {
-                                       if (np->n_tnext) {
-                                               if (np->n_tnext == (struct nfsnode *)nmp)
-                                                       nmp->nm_tprev = np->n_tprev;
-                                               else
-                                                       np->n_tnext->n_tprev = np->n_tprev;
-                                               if (np->n_tprev == (struct nfsnode *)nmp)
-                                                       nmp->nm_tnext = np->n_tnext;
-                                               else
-                                                       np->n_tprev->n_tnext = np->n_tnext;
-                                       } else
-                                               np->n_flag &= ~NQNFSWRITE;
-                                       if (cachable)
-                                               np->n_flag &= ~NQNFSNONCACHE;
-                                       else
-                                               np->n_flag |= NQNFSNONCACHE;
-                                       np->n_expiry = ltime;
-                                       np->n_lrev = frev;
-                                       tp = nmp->nm_tprev;
-                                       while (tp != (struct nfsnode *)nmp && tp->n_expiry > np->n_expiry)
-                                               tp = tp->n_tprev;
-                                       if (tp == (struct nfsnode *)nmp) {
-                                               np->n_tnext = nmp->nm_tnext;
-                                               nmp->nm_tnext = np;
-                                       } else {
-                                               np->n_tnext = tp->n_tnext;
-                                               tp->n_tnext = np;
-                                       }
-                                       np->n_tprev = tp;
-                                       if (np->n_tnext == (struct nfsnode *)nmp)
-                                               nmp->nm_tprev = np;
-                                       else
-                                               np->n_tnext->n_tprev = np;
-                                       cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
-                               }
+                               if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) &&
+                                       ltime > time.tv_sec)
+                                       nqnfs_clientlease(nmp, np, NQL_READ,
+                                               cachable, ltime, frev);
+                               if (cnp->cn_namelen <= NCHNAMLEN)
+                                   cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
                        } else {
                                nfsm_adv(nfsm_rndup(len));
                        }
                        } else {
                                nfsm_adv(nfsm_rndup(len));
                        }
@@ -1703,11 +1814,11 @@ nfs_readdirlookrpc(vp, uiop, cred)
                                vrele(newvp);
                                newvp = NULLVP;
                        }
                                vrele(newvp);
                                newvp = NULLVP;
                        }
-                       nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+                       nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
                        if (bigenough)
                        if (bigenough)
-                               endoff = off = fxdr_unsigned(off_t, *tl++);
+                               endoff = off = fxdr_unsigned(u_long, *tl++);
                        else
                        else
-                               endoff = fxdr_unsigned(off_t, *tl++);
+                               endoff = fxdr_unsigned(u_long, *tl++);
                        more_dirs = fxdr_unsigned(int, *tl);
                }
                /*
                        more_dirs = fxdr_unsigned(int, *tl);
                }
                /*
@@ -1724,7 +1835,7 @@ nfs_readdirlookrpc(vp, uiop, cred)
                                VTONFS(vp)->n_direofoffset = endoff;
                }
                if (uiop->uio_resid < tresid)
                                VTONFS(vp)->n_direofoffset = endoff;
                }
                if (uiop->uio_resid < tresid)
-                       uiop->uio_offset = off;
+                       uiop->uio_offset = (off_t)off;
                else
                        more_dirs = 0;
                m_freem(mrep);
                else
                        more_dirs = 0;
                m_freem(mrep);
@@ -1828,17 +1939,24 @@ nfs_lookitup(sp, fhp, procp)
        register long t1, t2;
        caddr_t bpos, dpos, cp2;
        u_long xid;
        register long t1, t2;
        caddr_t bpos, dpos, cp2;
        u_long xid;
-       int error = 0;
+       int error = 0, isnq;
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        long len;
 
        struct mbuf *mreq, *mrep, *md, *mb, *mb2;
        long len;
 
+       isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
        nfsstats.rpccnt[NFSPROC_LOOKUP]++;
        len = sp->s_namlen;
        nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len));
        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_fhtom(vp);
        nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN);
        nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred);
        if (fhp != NULL) {
        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_dissect(cp, caddr_t, NFSX_FH);
                bcopy(cp, (caddr_t)fhp, NFSX_FH);
        }
@@ -1864,6 +1982,7 @@ nfs_bmap(ap)
                daddr_t  a_bn;
                struct vnode **a_vpp;
                daddr_t *a_bnp;
                daddr_t  a_bn;
                struct vnode **a_vpp;
                daddr_t *a_bnp;
+               int *a_runp;
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
        } */ *ap;
 {
        register struct vnode *vp = ap->a_vp;
@@ -1876,176 +1995,38 @@ nfs_bmap(ap)
 }
 
 /*
 }
 
 /*
- * Strategy routine for phys. i/o
- * If the biod's are running, queue a request
- * otherwise just call nfs_doio() to get it done
+ * Strategy routine.
+ * For async requests when nfsiod(s) are running, queue the request by
+ * calling nfs_asyncio(), otherwise just all nfs_doio() to do the
+ * request.
  */
 int
 nfs_strategy(ap)
  */
 int
 nfs_strategy(ap)
-       struct vop_strategy_args /* {
-               struct buf *a_bp;
-       } */ *ap;
+       struct vop_strategy_args *ap;
 {
        register struct buf *bp = ap->a_bp;
 {
        register struct buf *bp = ap->a_bp;
-       register struct buf *dp;
-       register int i;
+       struct ucred *cr;
+       struct proc *p;
        int error = 0;
        int error = 0;
-       int fnd = 0;
 
 
-       /*
-        * Set b_proc. It seems a bit silly to do it here, but since bread()
-        * doesn't set it, I will.
-        * Set b_proc == NULL for asynchronous ops, since these may still
-        * be hanging about after the process terminates.
-        */
-       if ((bp->b_flags & B_PHYS) == 0) {
-               if (bp->b_flags & B_ASYNC)
-                       bp->b_proc = (struct proc *)0;
-               else
-                       bp->b_proc = curproc;
-       }
+       if (bp->b_flags & B_PHYS)
+               panic("nfs physio");
+       if (bp->b_flags & B_ASYNC)
+               p = (struct proc *)0;
+       else
+               p = curproc;    /* XXX */
+       if (bp->b_flags & B_READ)
+               cr = bp->b_rcred;
+       else
+               cr = bp->b_wcred;
        /*
         * If the op is asynchronous and an i/o daemon is waiting
         * queue the request, wake it up and wait for completion
         * otherwise just do it ourselves.
         */
        /*
         * If the op is asynchronous and an i/o daemon is waiting
         * queue the request, wake it up and wait for completion
         * otherwise just do it ourselves.
         */
-       if ((bp->b_flags & B_ASYNC) == 0 || nfs_numasync == 0)
-               return (nfs_doio(bp));
-       for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
-               if (nfs_iodwant[i]) {
-                       dp = &nfs_bqueue;
-                       if (dp->b_actf == NULL) {
-                               dp->b_actl = bp;
-                               bp->b_actf = dp;
-                       } else {
-                               dp->b_actf->b_actl = bp;
-                               bp->b_actf = dp->b_actf;
-                       }
-                       dp->b_actf = bp;
-                       bp->b_actl = dp;
-                       fnd++;
-                       wakeup((caddr_t)&nfs_iodwant[i]);
-                       break;
-               }
-       }
-       if (!fnd)
-               error = nfs_doio(bp);
-       return (error);
-}
-
-/*
- * Fun and games with i/o
- * Essentially play ubasetup() and disk interrupt service routine by
- * mapping the data buffer into kernel virtual space and doing the
- * nfs read or write rpc's from it.
- * If the nfsiod's are not running, this is just called from nfs_strategy(),
- * otherwise it is called by the nfsiods to do what would normally be
- * partially disk interrupt driven.
- */
-int
-nfs_doio(bp)
-       register struct buf *bp;
-{
-       register struct uio *uiop;
-       register struct vnode *vp;
-       struct nfsnode *np;
-       struct ucred *cr;
-       int error;
-       struct uio uio;
-       struct iovec io;
-
-       vp = bp->b_vp;
-       np = VTONFS(vp);
-       uiop = &uio;
-       uiop->uio_iov = &io;
-       uiop->uio_iovcnt = 1;
-       uiop->uio_segflg = UIO_SYSSPACE;
-       uiop->uio_procp = bp->b_proc;
-
-       /*
-        * For phys i/o, map the b_addr into kernel virtual space using
-        * the Nfsiomap pte's
-        * Also, add a temporary b_rcred for reading using the process's uid
-        * and a guess at a group
-        */
-       if (bp->b_flags & B_PHYS) {
-               if (bp->b_flags & B_DIRTY)
-                       uiop->uio_procp = pageproc;
-               cr = crcopy(uiop->uio_procp->p_ucred);
-               /* mapping was already done by vmapbuf */
-               io.iov_base = bp->b_un.b_addr;
-
-               /*
-                * And do the i/o rpc
-                */
-               io.iov_len = uiop->uio_resid = bp->b_bcount;
-               uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
-               if (bp->b_flags & B_READ) {
-                       uiop->uio_rw = UIO_READ;
-                       nfsstats.read_physios++;
-                       bp->b_error = error = nfs_readrpc(vp, uiop, cr);
-                       (void) vnode_pager_uncache(vp);
-               } else {
-                       uiop->uio_rw = UIO_WRITE;
-                       nfsstats.write_physios++;
-                       bp->b_error = error = nfs_writerpc(vp, uiop, cr);
-               }
-
-               /*
-                * Finally, release pte's used by physical i/o
-                */
-               crfree(cr);
-       } else {
-               if (bp->b_flags & B_READ) {
-                       io.iov_len = uiop->uio_resid = bp->b_bcount;
-                       io.iov_base = bp->b_un.b_addr;
-                       uiop->uio_rw = UIO_READ;
-                       switch (vp->v_type) {
-                       case VREG:
-                               uiop->uio_offset = bp->b_blkno * DEV_BSIZE;
-                               nfsstats.read_bios++;
-                               error = nfs_readrpc(vp, uiop, bp->b_rcred);
-                               break;
-                       case VLNK:
-                               uiop->uio_offset = 0;
-                               nfsstats.readlink_bios++;
-                               error = nfs_readlinkrpc(vp, uiop, bp->b_rcred);
-                               break;
-                       case VDIR:
-                               uiop->uio_offset = bp->b_lblkno;
-                               nfsstats.readdir_bios++;
-                               if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_RDIRALOOK)
-                                   error = nfs_readdirlookrpc(vp, uiop, bp->b_rcred);
-                               else
-                                   error = nfs_readdirrpc(vp, uiop, bp->b_rcred);
-                               /*
-                                * Save offset cookie in b_blkno.
-                                */
-                               bp->b_blkno = uiop->uio_offset;
-                               break;
-                       };
-                       bp->b_error = error;
-               } else {
-                       io.iov_len = uiop->uio_resid = bp->b_dirtyend
-                               - bp->b_dirtyoff;
-                       uiop->uio_offset = (bp->b_blkno * DEV_BSIZE)
-                               + bp->b_dirtyoff;
-                       io.iov_base = bp->b_un.b_addr + bp->b_dirtyoff;
-                       uiop->uio_rw = UIO_WRITE;
-                       nfsstats.write_bios++;
-                       bp->b_error = error = nfs_writerpc(vp, uiop,
-                               bp->b_wcred);
-                       if (error) {
-                               np->n_error = error;
-                               np->n_flag |= NWRITEERR;
-                       }
-                       bp->b_dirtyoff = bp->b_dirtyend = 0;
-               }
-       }
-       if (error)
-               bp->b_flags |= B_ERROR;
-       bp->b_resid = uiop->uio_resid;
-       biodone(bp);
+       if ((bp->b_flags & B_ASYNC) == 0 ||
+               nfs_asyncio(bp, NOCRED))
+               error = nfs_doio(bp, cr, p);
        return (error);
 }
 
        return (error);
 }
 
@@ -2088,36 +2069,64 @@ nfs_fsync(ap)
        register struct nfsnode *np = VTONFS(vp);
        register struct buf *bp;
        struct buf *nbp;
        register struct nfsnode *np = VTONFS(vp);
        register struct buf *bp;
        struct buf *nbp;
-       int s, error = 0;
+       struct nfsmount *nmp;
+       int s, error = 0, slptimeo = 0, slpflag = 0;
 
 
+       nmp = VFSTONFS(vp->v_mount);
+       if (nmp->nm_flag & NFSMNT_INT)
+               slpflag = PCATCH;
 loop:
        s = splbio();
 loop:
        s = splbio();
-       for (bp = vp->v_dirtyblkhd; bp; bp = nbp) {
-               nbp = bp->b_blockf;
-               if ((bp->b_flags & B_BUSY))
-                       continue;
+       for (bp = vp->v_dirtyblkhd.le_next; bp; bp = nbp) {
+               nbp = bp->b_vnbufs.qe_next;
+               if (bp->b_flags & B_BUSY) {
+                       if (ap->a_waitfor != MNT_WAIT)
+                               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))
+                               return (EINTR);
+                           if (slpflag == PCATCH) {
+                               slpflag = 0;
+                               slptimeo = 2 * hz;
+                           }
+                       }
+                       goto loop;
+               }
                if ((bp->b_flags & B_DELWRI) == 0)
                        panic("nfs_fsync: not dirty");
                bremfree(bp);
                bp->b_flags |= B_BUSY;
                splx(s);
                if ((bp->b_flags & B_DELWRI) == 0)
                        panic("nfs_fsync: not dirty");
                bremfree(bp);
                bp->b_flags |= B_BUSY;
                splx(s);
-               error = bawrite(bp);
+               bp->b_flags |= B_ASYNC;
+               VOP_BWRITE(bp);
                goto loop;
        }
                goto loop;
        }
+       splx(s);
        if (ap->a_waitfor == MNT_WAIT) {
                while (vp->v_numoutput) {
                        vp->v_flag |= VBWAIT;
        if (ap->a_waitfor == MNT_WAIT) {
                while (vp->v_numoutput) {
                        vp->v_flag |= VBWAIT;
-                       sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
+                       error = tsleep((caddr_t)&vp->v_numoutput,
+                               slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
+                       if (error) {
+                           if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p))
+                               return (EINTR);
+                           if (slpflag == PCATCH) {
+                               slpflag = 0;
+                               slptimeo = 2 * hz;
+                           }
+                       }
                }
                }
+               if (vp->v_dirtyblkhd.le_next) {
 #ifdef DIAGNOSTIC
 #ifdef DIAGNOSTIC
-               if (vp->v_dirtyblkhd) {
                        vprint("nfs_fsync: dirty", vp);
                        vprint("nfs_fsync: dirty", vp);
+#endif
                        goto loop;
                }
                        goto loop;
                }
-#endif
        }
        }
-       splx(s);
-       np->n_flag &= ~NMODIFIED;
        if (np->n_flag & NWRITEERR) {
                error = np->n_error;
                np->n_flag &= ~NWRITEERR;
        if (np->n_flag & NWRITEERR) {
                error = np->n_error;
                np->n_flag &= ~NWRITEERR;
@@ -2125,6 +2134,24 @@ loop:
        return (error);
 }
 
        return (error);
 }
 
+/*
+ * Return POSIX pathconf information applicable to nfs.
+ *
+ * Currently the NFS protocol does not support getting such
+ * information from the remote server.
+ */
+/* ARGSUSED */
+nfs_pathconf(ap)
+       struct vop_pathconf_args /* {
+               struct vnode *a_vp;
+               int a_name;
+               int *a_retval;
+       } */ *ap;
+{
+
+       return (EINVAL);
+}
+
 /*
  * NFS advisory byte-level locks.
  * Currently unsupported.
 /*
  * NFS advisory byte-level locks.
  * Currently unsupported.
@@ -2251,6 +2278,55 @@ nfs_update(ap)
        return (EOPNOTSUPP);
 }
 
        return (EOPNOTSUPP);
 }
 
+/*
+ * nfs special file access vnode op.
+ * Essentially just get vattr and then imitate iaccess() since the device is
+ * local to the client.
+ */
+int
+nfsspec_access(ap)
+       struct vop_access_args /* {
+               struct vnode *a_vp;
+               int  a_mode;
+               struct ucred *a_cred;
+               struct proc *a_p;
+       } */ *ap;
+{
+       register struct vattr *vap;
+       register gid_t *gp;
+       register struct ucred *cred = ap->a_cred;
+       mode_t mode = ap->a_mode;
+       struct vattr vattr;
+       register int i;
+       int error;
+
+       /*
+        * If you're the super-user,
+        * you always get access.
+        */
+       if (cred->cr_uid == 0)
+               return (0);
+       vap = &vattr;
+       if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p))
+               return (error);
+       /*
+        * Access check is based on only one of owner, group, public.
+        * If not owner, then check group. If not a member of the
+        * group, then check public access.
+        */
+       if (cred->cr_uid != vap->va_uid) {
+               mode >>= 3;
+               gp = cred->cr_groups;
+               for (i = 0; i < cred->cr_ngroups; i++, gp++)
+                       if (vap->va_gid == *gp)
+                               goto found;
+               mode >>= 3;
+found:
+               ;
+       }
+       return ((vap->va_mode & mode) == mode ? 0 : EACCES);
+}
+
 /*
  * Read wrapper for special devices.
  */
 /*
  * Read wrapper for special devices.
  */
@@ -2263,7 +2339,6 @@ nfsspec_read(ap)
                struct ucred *a_cred;
        } */ *ap;
 {
                struct ucred *a_cred;
        } */ *ap;
 {
-       extern int (**spec_vnodeop_p)();
        register struct nfsnode *np = VTONFS(ap->a_vp);
 
        /*
        register struct nfsnode *np = VTONFS(ap->a_vp);
 
        /*
@@ -2286,7 +2361,6 @@ nfsspec_write(ap)
                struct ucred *a_cred;
        } */ *ap;
 {
                struct ucred *a_cred;
        } */ *ap;
 {
-       extern int (**spec_vnodeop_p)();
        register struct nfsnode *np = VTONFS(ap->a_vp);
 
        /*
        register struct nfsnode *np = VTONFS(ap->a_vp);
 
        /*
@@ -2314,13 +2388,8 @@ nfsspec_close(ap)
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        struct vattr vattr;
        register struct vnode *vp = ap->a_vp;
        register struct nfsnode *np = VTONFS(vp);
        struct vattr vattr;
-       extern int (**spec_vnodeop_p)();
 
        if (np->n_flag & (NACC | NUPD)) {
 
        if (np->n_flag & (NACC | NUPD)) {
-               if (np->n_flag & NACC)
-                       np->n_atim = time;
-               if (np->n_flag & NUPD)
-                       np->n_mtim = time;
                np->n_flag |= NCHG;
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
                np->n_flag |= NCHG;
                if (vp->v_usecount == 1 &&
                    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {