]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_vnops.c
xnu-3248.20.55.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_vnops.c
index a5917a2977773be7528b68f556a1511689e03a95..ae1906aed738f44f0c0a02c3afe047e95ffb7ab2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
 #include <kern/task.h>
 #include <kern/sched_prim.h>
 
+#define NFS_VNOP_DBG(...) NFS_DBG(NFS_FAC_VNOP, 7, ## __VA_ARGS__)
+#define DEFAULT_READLINK_NOCACHE 0
+
 /*
  * NFS vnode ops
  */
@@ -443,8 +446,9 @@ struct vnodeopv_desc fifo_nfsv4nodeop_opv_desc =
        { &fifo_nfsv4nodeop_p, fifo_nfsv4nodeop_entries };
 #endif /* FIFO */
 
-
 int    nfs_sillyrename(nfsnode_t,nfsnode_t,struct componentname *,vfs_context_t);
+int    nfs_getattr_internal(nfsnode_t, struct nfs_vattr *, vfs_context_t, int);
+int    nfs_refresh_fh(nfsnode_t, vfs_context_t);
 
 /*
  * Find the slot in the access cache for this UID.
@@ -469,7 +473,7 @@ nfs_node_access_slot(nfsnode_t np, uid_t uid, int add)
 }
 
 int
-nfs3_access_rpc(nfsnode_t np, u_int32_t *access, vfs_context_t ctx)
+nfs3_access_rpc(nfsnode_t np, u_int32_t *access, int rpcflags, vfs_context_t ctx)
 {
        int error = 0, lockerror = ENOENT, status, slot;
        uint32_t access_result = 0;
@@ -486,7 +490,9 @@ nfs3_access_rpc(nfsnode_t np, u_int32_t *access, vfs_context_t ctx)
        nfsm_chain_add_32(error, &nmreq, *access);
        nfsm_chain_build_done(error, &nmreq);
        nfsmout_if(error);
-       error = nfs_request(np, NULL, &nmreq, NFSPROC_ACCESS, ctx, NULL, &nmrep, &xid, &status);
+       error = nfs_request2(np, NULL, &nmreq, NFSPROC_ACCESS,
+               vfs_context_thread(ctx), vfs_context_ucred(ctx),
+               NULL, rpcflags, &nmrep, &xid, &status);
        if ((lockerror = nfs_node_lock(np)))
                error = lockerror;
        nfsm_chain_postop_attr_update(error, &nmrep, np, &xid);
@@ -542,7 +548,7 @@ nfs_vnop_access(
 {
        vfs_context_t ctx = ap->a_context;
        vnode_t vp = ap->a_vp;
-       int error = 0, slot, dorpc;
+       int error = 0, slot, dorpc, rpcflags = 0;
        u_int32_t access, waccess;
        nfsnode_t np = VTONFS(vp);
        struct nfsmount *nmp;
@@ -551,7 +557,7 @@ nfs_vnop_access(
        uid_t uid;
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -630,7 +636,10 @@ nfs_vnop_access(
         * Does our cached result allow us to give a definite yes to
         * this request?
         */
-       uid = kauth_cred_getuid(vfs_context_ucred(ctx));
+       if (auth_is_kerberized(np->n_auth) || auth_is_kerberized(nmp->nm_auth))
+               uid = nfs_cred_getasid2uid(vfs_context_ucred(ctx));
+       else
+               uid = kauth_cred_getuid(vfs_context_ucred(ctx));
        slot = nfs_node_access_slot(np, uid, 0);
        dorpc = 1;
        if (access == 0) {
@@ -640,8 +649,8 @@ nfs_vnop_access(
                waccess = 0;
        } else if (NACCESSVALID(np, slot)) {
                microuptime(&now);
-               if ((now.tv_sec < (np->n_accessstamp[slot] + nfs_access_cache_timeout)) &&
-                   ((np->n_access[slot] & access) == access)) {
+               if (((now.tv_sec < (np->n_accessstamp[slot] + nfs_access_cache_timeout)) &&
+                   ((np->n_access[slot] & access) == access)) || nfs_use_cache(nmp)) {
                        /* OSAddAtomic(1, &nfsstats.accesscache_hits); */
                        dorpc = 0;
                        waccess = np->n_access[slot];
@@ -651,7 +660,23 @@ nfs_vnop_access(
        if (dorpc) {
                /* Either a no, or a don't know.  Go to the wire. */
                /* OSAddAtomic(1, &nfsstats.accesscache_misses); */
-               error = nmp->nm_funcs->nf_access_rpc(np, &waccess, ctx);
+
+               /*
+                * Allow an access call to timeout if we have it cached
+                * so we won't hang if the server isn't responding.
+                */
+               if (NACCESSVALID(np, slot))
+                       rpcflags |= R_SOFT;
+
+               error = nmp->nm_funcs->nf_access_rpc(np, &waccess, rpcflags, ctx);
+
+               /*
+                * If the server didn't respond return the cached access.
+                */
+               if ((error == ETIMEDOUT) && (rpcflags & R_SOFT)) {
+                       error = 0;
+                       waccess = np->n_access[slot];
+               }
        }
        if (!error && ((waccess & access) != access))
                error = EACCES;
@@ -689,7 +714,7 @@ nfs_vnop_open(
                return (EINVAL);
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        if (np->n_flag & NREVOKE)
                return (EIO);
@@ -855,9 +880,34 @@ out:
                NP(np, "nfs_vnop_open: error %d, %d", error, kauth_cred_getuid(noop->noo_cred));
        if (noop)
                nfs_open_owner_rele(noop);
+       if (!error && vtype == VREG && (ap->a_mode & FWRITE)) {
+               lck_mtx_lock(&nmp->nm_lock);
+               nmp->nm_state &= ~NFSSTA_SQUISHY;
+               nmp->nm_curdeadtimeout = nmp->nm_deadtimeout;
+               if (nmp->nm_curdeadtimeout <= 0)
+                       nmp->nm_deadto_start = 0;
+               nmp->nm_writers++;
+               lck_mtx_unlock(&nmp->nm_lock);
+       }
+               
        return (error);
 }
 
+static uint32_t
+nfs_no_of_open_file_writers(nfsnode_t np)
+{
+       uint32_t writers = 0;
+       struct nfs_open_file *nofp;
+
+       TAILQ_FOREACH(nofp,  &np->n_opens, nof_link) {
+               writers += nofp->nof_w + nofp->nof_rw + nofp->nof_w_dw + nofp->nof_rw_dw +
+                       nofp->nof_w_drw + nofp->nof_rw_drw + nofp->nof_d_w_dw +
+                       nofp->nof_d_rw_dw + nofp->nof_d_w_drw + nofp->nof_d_rw_drw +
+                       nofp->nof_d_w + nofp->nof_d_rw;
+       }
+       
+       return (writers);
+}
 
 /*
  * NFS close vnode op
@@ -990,11 +1040,36 @@ nfs_vnop_close(
                 * Guess this is the final close.
                 * We should unlock all locks and close all opens.
                 */
+               uint32_t writers;
                mount_t mp = vnode_mount(vp);
-               int force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
+               int force = (!mp || vfs_isforce(mp));
+
+               writers = nfs_no_of_open_file_writers(np);
                nfs_release_open_state_for_node(np, force);
+               if (writers) {
+                       lck_mtx_lock(&nmp->nm_lock);
+                       if (writers > nmp->nm_writers) {
+                               NP(np, "nfs_vnop_close: number of write opens for mount underrun. Node has %d"
+                                  " opens for write. Mount has total of %d opens for write\n", 
+                                  writers, nmp->nm_writers);
+                               nmp->nm_writers = 0;
+                       } else {
+                               nmp->nm_writers -= writers;
+                       }
+                       lck_mtx_unlock(&nmp->nm_lock);
+               }
+               
                return (error);
+       } else if (fflag & FWRITE) {
+               lck_mtx_lock(&nmp->nm_lock);
+               if (nmp->nm_writers == 0) {
+                       NP(np, "nfs_vnop_close: removing open writer from mount, but mount has no files open for writing");
+               } else {
+                       nmp->nm_writers--;
+               }
+               lck_mtx_unlock(&nmp->nm_lock);
        }
+       
 
        noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 0);
        if (!noop) {
@@ -1065,7 +1140,7 @@ nfs_close(
        struct nfs_lock_owner *nlop;
        int error = 0, changed = 0, delegated = 0, closed = 0, downgrade = 0;
        uint32_t newAccessMode, newDenyMode;
-
+       
        /* warn if modes don't match current state */
        if (((accessMode & nofp->nof_access) != accessMode) || ((denyMode & nofp->nof_deny) != denyMode))
                NP(np, "nfs_close: mode mismatch %d %d, current %d %d, %d",
@@ -1191,12 +1266,11 @@ v3close:
                NP(np, "nfs_close: LOST%s, %d", !nofp->nof_opencnt ? " (last)" : "",
                        kauth_cred_getuid(nofp->nof_owner->noo_cred));
        }
+               
        return (error);
 }
 
 
-
-
 int
 nfs3_getattr_rpc(
        nfsnode_t np,
@@ -1212,13 +1286,16 @@ nfs3_getattr_rpc(
        int error = 0, status, nfsvers, rpcflags = 0;
        struct nfsm_chain nmreq, nmrep;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
        if (flags & NGA_MONITOR) /* vnode monitor requests should be soft */
                rpcflags = R_RECOVER;
 
+       if (flags & NGA_SOFT) /* Return ETIMEDOUT if server not responding */
+               rpcflags |= R_SOFT;
+
        nfsm_chain_null(&nmreq);
        nfsm_chain_null(&nmrep);
 
@@ -1241,9 +1318,159 @@ nfsmout:
        return (error);
 }
 
+/*
+ * nfs_refresh_fh will attempt to update the file handle for the node.
+ *
+ * It only does this for symbolic links and regular files that are not currently opened.
+ *
+ * On Success returns 0 and the nodes file handle is updated, or ESTALE on failure.
+ */ 
+int
+nfs_refresh_fh(nfsnode_t np, vfs_context_t ctx)
+{
+       vnode_t dvp, vp = NFSTOV(np);
+       nfsnode_t dnp;
+       const char *v_name = vnode_getname(vp);
+       char *name;
+       int namelen, fhsize, refreshed;
+       int error, wanted = 0;
+       uint8_t *fhp;
+       struct timespec ts = {2, 0};
+
+       NFS_VNOP_DBG("vnode is %d\n", vnode_vtype(vp));
+
+       dvp = vnode_parent(vp);
+       if ((vnode_vtype(vp) != VREG && vnode_vtype(vp) != VLNK) ||
+           v_name == NULL || *v_name == '\0' || dvp == NULL) {
+               if (v_name != NULL)
+                       vnode_putname(v_name);
+               return (ESTALE);
+       }
+       dnp = VTONFS(dvp);
+       
+       namelen = strlen(v_name);
+       MALLOC(name, char *, namelen + 1, M_TEMP, M_WAITOK);
+       if (name == NULL) {
+               vnode_putname(v_name);
+               return (ESTALE);
+       }
+       bcopy(v_name, name, namelen+1);
+       NFS_VNOP_DBG("Trying to refresh %s : %s\n", v_name, name);
+       vnode_putname(v_name);
+
+       /* Allocate the maximum size file handle */
+       MALLOC(fhp, uint8_t *, NFS4_FHSIZE, M_TEMP, M_WAITOK);
+       if (fhp == NULL) {
+               FREE(name, M_TEMP);
+               return (ESTALE);
+       }
+       
+       if ((error = nfs_node_lock(np))) {
+               FREE(name, M_TEMP);
+               FREE(fhp, M_TEMP);
+               return (ESTALE);
+       }
+       
+       fhsize = np->n_fhsize;
+       bcopy(np->n_fhp, fhp, fhsize);
+       while (ISSET(np->n_flag, NREFRESH)) {
+               SET(np->n_flag, NREFRESHWANT);
+               NFS_VNOP_DBG("Waiting for refresh of %s\n", name);
+               msleep(np, &np->n_lock, PZERO-1, "nfsrefreshwant", &ts);
+               if ((error = nfs_sigintr(NFSTONMP(np), NULL,  vfs_context_thread(ctx), 0)))
+                       break;
+       }
+       refreshed = error ? 0 : !NFS_CMPFH(np, fhp, fhsize);
+       SET(np->n_flag, NREFRESH);
+       nfs_node_unlock(np);
+
+       NFS_VNOP_DBG("error = %d, refreshed = %d\n", error, refreshed);
+       if (error || refreshed)
+               goto nfsmout;
+       
+       /* Check that there are no open references for this file */
+       lck_mtx_lock(&np->n_openlock);
+       if (np->n_openrefcnt || !TAILQ_EMPTY(&np->n_opens) || !TAILQ_EMPTY(&np->n_lock_owners)) {
+               int cnt = 0;
+               struct nfs_open_file *ofp;
+               
+               TAILQ_FOREACH(ofp, &np->n_opens, nof_link) {
+                       cnt += ofp->nof_opencnt;
+               }
+               if (cnt) {
+                       lck_mtx_unlock(&np->n_openlock);
+                       NFS_VNOP_DBG("Can not refresh file handle for %s with open state\n", name);
+                       NFS_VNOP_DBG("\topenrefcnt = %d, opens = %d lock_owners = %d\n", 
+                                         np->n_openrefcnt, cnt, !TAILQ_EMPTY(&np->n_lock_owners));
+                       error = ESTALE;
+                       goto nfsmout;
+               }
+       }
+       lck_mtx_unlock(&np->n_openlock);
+       /* 
+        * Since the FH is currently stale we should not be able to
+        * establish any open state until the FH is refreshed.
+        */
+
+       error = nfs_node_lock(np);
+       nfsmout_if(error);
+       /*
+        * Symlinks should never need invalidations and are holding
+        * the one and only nfsbuf in an uncached acquired state
+        * trying to do a readlink. So we will hang if we invalidate
+        * in that case. Only in in the VREG case do we need to
+        * invalidate.
+        */
+       if (vnode_vtype(vp) == VREG) {
+               np->n_flag &= ~NNEEDINVALIDATE;
+               nfs_node_unlock(np);
+               error = nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ctx, 1);
+               if (error)
+                       NFS_VNOP_DBG("nfs_vinvalbuf returned %d\n", error);
+               nfsmout_if(error);
+       } else {
+               nfs_node_unlock(np);
+       }
+
+       NFS_VNOP_DBG("Looking up %s\n", name);
+       error = nfs_lookitup(dnp, name, namelen, ctx, &np);
+       if (error)
+               NFS_VNOP_DBG("nfs_lookitup returned %d\n", error);
+
+nfsmout:
+       nfs_node_lock_force(np);
+       wanted = ISSET(np->n_flag, NREFRESHWANT);
+       CLR(np->n_flag, NREFRESH|NREFRESHWANT);
+       nfs_node_unlock(np);
+       if (wanted)
+               wakeup(np);
+       
+       if (error == 0)
+               NFS_VNOP_DBG("%s refreshed file handle\n", name);
+
+       FREE(name, M_TEMP);
+       FREE(fhp, M_TEMP);
+       
+       return (error ? ESTALE : 0);
+}
 
 int
 nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
+{
+       int error;
+       
+retry:
+       error = nfs_getattr_internal(np, nvap, ctx, flags);
+       if (error == ESTALE) {
+               error = nfs_refresh_fh(np, ctx);
+               if (!error)
+                       goto retry;
+       }
+       return (error);
+}
+
+int
+nfs_getattr_internal(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
 {
        struct nfsmount *nmp;
        int error = 0, nfsvers, inprogset = 0, wanted = 0, avoidfloods;
@@ -1253,7 +1480,9 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
 
        FSDBG_TOP(513, np->n_size, np, np->n_vattr.nva_size, np->n_flag);
 
-       if (!(nmp = NFSTONMP(np)))
+       nmp = NFSTONMP(np);
+       
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -1277,7 +1506,8 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
                /*
                 * Use the cache or wait for any getattr in progress if:
                 * - it's a cached request, or
-                * - we have a delegation
+                * - we have a delegation, or
+                * - the server isn't responding
                 */
                while (1) {
                        error = nfs_getattrcache(np, nvap, flags);
@@ -1313,11 +1543,19 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
        nfs_node_unlock(np);
 
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                error = ENXIO;
        if (error)
                goto nfsmout;
 
+       /*
+        * Return cached attributes if they are valid,
+        * if the server doesn't respond, and this is
+        * some softened up style of mount.
+        */
+       if (NATTRVALID(np) && nfs_use_cache(nmp))
+               flags |= NGA_SOFT;
+
        /*
         * We might want to try to get both the attributes and access info by
         * making an ACCESS call and seeing if it returns updated attributes.
@@ -1328,7 +1566,17 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
                if (nfs_attrcachetimeout(np) > 0) {
                        /*  OSAddAtomic(1, &nfsstats.accesscache_misses); */
                        u_int32_t access = NFS_ACCESS_ALL;
-                       error = nmp->nm_funcs->nf_access_rpc(np, &access, ctx);
+                       int rpcflags = 0;
+
+                       /* Return cached attrs if server doesn't respond */
+                       if (flags & NGA_SOFT)
+                               rpcflags |= R_SOFT;
+
+                       error = nmp->nm_funcs->nf_access_rpc(np, &access, rpcflags, ctx);
+
+                       if (error == ETIMEDOUT)
+                               goto returncached;
+
                        if (error)
                                goto nfsmout;
                        nfs_node_lock_force(np);
@@ -1342,6 +1590,7 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags)
        }
 
        avoidfloods = 0;
+
 tryagain:
        error = nmp->nm_funcs->nf_getattr_rpc(np, NULL, np->n_fhp, np->n_fhsize, flags, ctx, nvap, &xid);
        if (!error) {
@@ -1349,7 +1598,22 @@ tryagain:
                error = nfs_loadattrcache(np, nvap, &xid, 0);
                nfs_node_unlock(np);
        }
+
+       /*
+        * If the server didn't respond, return cached attributes.
+        */
+returncached:
+       if ((flags & NGA_SOFT) && (error == ETIMEDOUT)) {
+               nfs_node_lock_force(np);
+               error = nfs_getattrcache(np, nvap, flags);
+               if (!error || (error != ENOENT)) {
+                       nfs_node_unlock(np);
+                       goto nfsmout;
+               }
+               nfs_node_unlock(np);
+       }
        nfsmout_if(error);
+
        if (!xid) { /* out-of-order rpc - attributes were dropped */
                FSDBG(513, -1, np, np->n_xid >> 32, np->n_xid);
                if (avoidfloods++ < 20)
@@ -1373,11 +1637,16 @@ nfsmout:
                        cache_purge(vp);
                        np->n_ncgen++;
                        NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap);
+                       NFS_VNOP_DBG("Purge directory 0x%llx\n", 
+                             (uint64_t)VM_KERNEL_ADDRPERM(vp));
                }
                if (NFS_CHANGED(nfsvers, np, nvap)) {
                        FSDBG(513, -1, np, -1, np);
-                       if (vtype == VDIR)
+                       if (vtype == VDIR) {
+                               NFS_VNOP_DBG("Invalidate directory 0x%llx\n", 
+                                      (uint64_t)VM_KERNEL_ADDRPERM(vp));
                                nfs_invaldir(np);
+                       }
                        nfs_node_unlock(np);
                        if (wanted)
                                wakeup(np);
@@ -1416,6 +1685,28 @@ nfsmout:
 /*
  * NFS getattr call from vfs.
  */
+
+/*
+ * The attributes we support over the wire.
+ * We also get fsid but the vfs layer gets it out of the mount 
+ * structure after this calling us so there's no need to return it,
+ * and Finder expects to call getattrlist just looking for the FSID
+ * with out hanging on a non responsive server.
+ */
+#define NFS3_SUPPORTED_VATTRS \
+       (VNODE_ATTR_va_rdev |           \
+        VNODE_ATTR_va_nlink |          \
+        VNODE_ATTR_va_data_size |      \
+        VNODE_ATTR_va_data_alloc |     \
+        VNODE_ATTR_va_uid |            \
+        VNODE_ATTR_va_gid |            \
+        VNODE_ATTR_va_mode |           \
+        VNODE_ATTR_va_modify_time |    \
+        VNODE_ATTR_va_change_time |    \
+        VNODE_ATTR_va_access_time |    \
+        VNODE_ATTR_va_fileid |         \
+        VNODE_ATTR_va_type)
+
 int
 nfs3_vnop_getattr(
        struct vnop_getattr_args /* {
@@ -1430,6 +1721,19 @@ nfs3_vnop_getattr(
        struct vnode_attr *vap = ap->a_vap;
        dev_t rdev;
 
+       /*
+        * Lets don't go over the wire if we don't support any of the attributes.
+        * Just fall through at the VFS layer and let it cons up what it needs.
+        */
+       /* Return the io size no matter what, since we don't go over the wire for this */
+       VATTR_RETURN(vap, va_iosize, nfs_iosize);
+       if ((vap->va_active & NFS3_SUPPORTED_VATTRS) == 0)
+               return (0);
+
+       if (VATTR_IS_ACTIVE(ap->a_vap, va_name))
+           NFS_VNOP_DBG("Getting attrs for 0x%llx, vname is %s\n", 
+                 (uint64_t)VM_KERNEL_ADDRPERM(ap->a_vp),
+                 ap->a_vp->v_name ? ap->a_vp->v_name : "empty");
        error = nfs_getattr(VTONFS(ap->a_vp), &nva, ap->a_context, NGA_CACHED);
        if (error)
                return (error);
@@ -1445,7 +1749,6 @@ nfs3_vnop_getattr(
        VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
        VATTR_RETURN(vap, va_data_size, nva.nva_size);
        VATTR_RETURN(vap, va_data_alloc, nva.nva_bytes);
-       VATTR_RETURN(vap, va_iosize, nfs_iosize);
        vap->va_access_time.tv_sec = nva.nva_timesec[NFSTIME_ACCESS];
        vap->va_access_time.tv_nsec = nva.nva_timensec[NFSTIME_ACCESS];
        VATTR_SET_SUPPORTED(vap, va_access_time);
@@ -1488,7 +1791,7 @@ nfs_vnop_setattr(
        struct nfs_open_file *nofp = NULL;
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
@@ -1791,7 +2094,7 @@ nfs3_setattr_rpc(
        u_int64_t xid, nextxid;
        struct nfsm_chain nmreq, nmrep;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -1977,7 +2280,7 @@ nfs_vnop_lookup(
 
        mp = vnode_mount(dvp);
        nmp = VFSTONFS(mp);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                error = ENXIO;
                goto error_return;
        }
@@ -2011,7 +2314,7 @@ nfs_vnop_lookup(
                /* FALLTHROUGH */
        case -1:
                /* cache hit, not really an error */
-               OSAddAtomic(1, &nfsstats.lookupcache_hits);
+               OSAddAtomic64(1, &nfsstats.lookupcache_hits);
 
                nfs_node_clear_busy(dnp);
                busyerror = ENOENT;
@@ -2050,7 +2353,7 @@ nfs_vnop_lookup(
 
        /* do we know this name is too long? */
        nmp = VTONMP(dvp);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                error = ENXIO;
                goto error_return;
        }
@@ -2063,7 +2366,7 @@ nfs_vnop_lookup(
        error = 0;
        newvp = NULLVP;
 
-       OSAddAtomic(1, &nfsstats.lookupcache_misses);
+       OSAddAtomic64(1, &nfsstats.lookupcache_misses);
 
        error = nmp->nm_funcs->nf_lookup_rpc_async(dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx, &req);
        nfsmout_if(error);
@@ -2143,6 +2446,8 @@ error_return:
        return (error);
 }
 
+int nfs_readlink_nocache = DEFAULT_READLINK_NOCACHE;
+
 /*
  * NFS readlink call
  */
@@ -2162,6 +2467,8 @@ nfs_vnop_readlink(
        uint32_t buflen;
        uio_t uio = ap->a_uio;
        struct nfsbuf *bp = NULL;
+       struct timespec ts;
+       int timeo;
 
        if (vnode_vtype(ap->a_vp) != VLNK)
                return (EPERM);
@@ -2172,34 +2479,66 @@ nfs_vnop_readlink(
                return (EINVAL);
 
        nmp = VTONMP(ap->a_vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
+       
        /* nfs_getattr() will check changed and purge caches */
-       if ((error = nfs_getattr(np, NULL, ctx, NGA_CACHED))) {
+       if ((error = nfs_getattr(np, NULL, ctx, nfs_readlink_nocache ? NGA_UNCACHED : NGA_CACHED))) {
                FSDBG(531, np, 0xd1e0001, 0, error);
                return (error);
        }
 
-       OSAddAtomic(1, &nfsstats.biocache_readlinks);
-       error = nfs_buf_get(np, 0, NFS_MAXPATHLEN, vfs_context_thread(ctx), NBLK_READ, &bp);
+       if (nfs_readlink_nocache) {
+               timeo = nfs_attrcachetimeout(np);
+               nanouptime(&ts);
+       }
+       
+retry:
+       OSAddAtomic64(1, &nfsstats.biocache_readlinks);
+       error = nfs_buf_get(np, 0, NFS_MAXPATHLEN, vfs_context_thread(ctx), NBLK_META, &bp);
        if (error) {
                FSDBG(531, np, 0xd1e0002, 0, error);
                return (error);
        }
+
+       if (nfs_readlink_nocache) {
+               NFS_VNOP_DBG("timeo = %d ts.tv_sec = %ld need refresh = %d cached = %d\n", timeo, ts.tv_sec,
+                            (np->n_rltim.tv_sec + timeo) < ts.tv_sec || nfs_readlink_nocache > 1, 
+                            ISSET(bp->nb_flags, NB_CACHE) == NB_CACHE);
+               /* n_rltim is synchronized by the associated nfs buf */
+               if (ISSET(bp->nb_flags, NB_CACHE) && ((nfs_readlink_nocache > 1)  || ((np->n_rltim.tv_sec + timeo) < ts.tv_sec))) {
+                       SET(bp->nb_flags, NB_INVAL);
+                       nfs_buf_release(bp, 0);
+                       goto retry;
+               }
+       }
        if (!ISSET(bp->nb_flags, NB_CACHE)) {
-               OSAddAtomic(1, &nfsstats.readlink_bios);
+readagain:
+               OSAddAtomic64(1, &nfsstats.readlink_bios);
                buflen = bp->nb_bufsize;
                error = nmp->nm_funcs->nf_readlink_rpc(np, bp->nb_data, &buflen, ctx);
                if (error) {
+                       if (error == ESTALE) {
+                               NFS_VNOP_DBG("Stale FH from readlink rpc\n");
+                               error = nfs_refresh_fh(np, ctx);
+                               if (error == 0)
+                                       goto readagain;
+                       }
                        SET(bp->nb_flags, NB_ERROR);
                        bp->nb_error = error;
+                       NFS_VNOP_DBG("readlink failed %d\n", error);
                } else {
                        bp->nb_validoff = 0;
                        bp->nb_validend = buflen;
+                       np->n_rltim = ts;
+                       NFS_VNOP_DBG("readlink of %.*s\n", bp->nb_validend, (char *)bp->nb_data);
                }
+       } else {
+               NFS_VNOP_DBG("got cached link of %.*s\n", bp->nb_validend, (char *)bp->nb_data);
        }
+       
        if (!error && (bp->nb_validend > 0))
                error = uiomove(bp->nb_data, bp->nb_validend, uio);
        FSDBG(531, np, bp->nb_validend, 0, error);
@@ -2220,7 +2559,7 @@ nfs3_readlink_rpc(nfsnode_t np, char *buf, uint32_t *buflenp, vfs_context_t ctx)
        struct nfsm_chain nmreq, nmrep;
 
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        nfsm_chain_null(&nmreq);
@@ -2277,7 +2616,7 @@ nfs_read_rpc(nfsnode_t np, uio_t uio, vfs_context_t ctx)
 
        FSDBG_TOP(536, np, uio_offset(uio), uio_resid(uio), 0);
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        nmrsize = nmp->nm_rsize;
@@ -2349,7 +2688,7 @@ nfs3_read_rpc_async(
        struct nfsm_chain nmreq;
 
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -2387,7 +2726,7 @@ nfs3_read_rpc_async_finish(
        struct nfsm_chain nmrep;
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                nfs_request_async_cancel(req);
                return (ENXIO);
        }
@@ -2536,13 +2875,63 @@ nfs_vnop_write(
                goto out;
 
        if (((uio_offset(uio) + uio_resid(uio)) > (off_t)np->n_size) && !(ioflag & IO_APPEND)) {
-               /* it looks like we'll be extending the file, so take the data lock exclusive */
+               /*
+                * It looks like we'll be extending the file, so take the data lock exclusive.
+                */
                nfs_data_unlock(np);
                nfs_data_lock(np, NFS_DATA_LOCK_EXCLUSIVE);
+
+               /*
+                * Also, if the write begins after the previous EOF buffer, make sure to zero
+                * and validate the new bytes in that buffer.
+                */
+               struct nfsbuf *eofbp = NULL;
+               daddr64_t eofbn = np->n_size / biosize;
+               int eofoff = np->n_size % biosize;
+               lbn = uio_offset(uio) / biosize;
+
+               if (eofoff && (eofbn < lbn)) {
+                       if ((error = nfs_buf_get(np, eofbn, biosize, thd, NBLK_WRITE|NBLK_ONLYVALID, &eofbp)))
+                               goto out;
+                       np->n_size += (biosize - eofoff);
+                       nfs_node_lock_force(np);
+                       CLR(np->n_flag, NUPDATESIZE);
+                       np->n_flag |= NMODIFIED;
+                       nfs_node_unlock(np);
+                       FSDBG(516, np, np->n_size, np->n_vattr.nva_size, 0xf00d0001);
+                       ubc_setsize(vp, (off_t)np->n_size); /* XXX errors */
+                       if (eofbp) {
+                               /*
+                                * For the old last page, don't zero bytes if there
+                                * are invalid bytes in that page (i.e. the page isn't
+                                * currently valid).
+                                * For pages after the old last page, zero them and
+                                * mark them as valid.
+                                */
+                               char *d;
+                               int i;
+                               if (ioflag & IO_NOCACHE)
+                                       SET(eofbp->nb_flags, NB_NOCACHE);
+                               NFS_BUF_MAP(eofbp);
+                               FSDBG(516, eofbp, eofoff, biosize - eofoff, 0xe0fff01e);
+                               d = eofbp->nb_data;
+                               i = eofoff/PAGE_SIZE;
+                               while (eofoff < biosize) {
+                                       int poff = eofoff & PAGE_MASK;
+                                       if (!poff || NBPGVALID(eofbp,i)) {
+                                               bzero(d + eofoff, PAGE_SIZE - poff);
+                                               NBPGVALID_SET(eofbp, i);
+                                       }
+                                       eofoff += PAGE_SIZE - poff;
+                                       i++;
+                               }
+                               nfs_buf_release(eofbp, 1);
+                       }
+               }
        }
 
        do {
-               OSAddAtomic(1, &nfsstats.biocache_writes);
+               OSAddAtomic64(1, &nfsstats.biocache_writes);
                lbn = uio_offset(uio) / biosize;
                on = uio_offset(uio) % biosize;
                n = biosize - on;
@@ -2638,17 +3027,11 @@ again:
                 * and zero the new bytes.
                 */
                if ((uio_offset(uio) + n) > (off_t)np->n_size) {
-                       struct nfsbuf *eofbp = NULL;
                        daddr64_t eofbn = np->n_size / biosize;
-                       int eofoff = np->n_size % biosize;
                        int neweofoff = (uio_offset(uio) + n) % biosize;
 
                        FSDBG(515, 0xb1ffa000, uio_offset(uio) + n, eofoff, neweofoff);
 
-                       if (eofoff && (eofbn < lbn) &&
-                           ((error = nfs_buf_get(np, eofbn, biosize, thd, NBLK_WRITE|NBLK_ONLYVALID, &eofbp))))
-                               goto out;
-
                        /* if we're extending within the same last block */
                        /* and the block is flagged as being cached... */
                        if ((lbn == eofbn) && ISSET(bp->nb_flags, NB_CACHE)) {
@@ -2685,38 +3068,6 @@ again:
                        nfs_node_unlock(np);
                        FSDBG(516, np, np->n_size, np->n_vattr.nva_size, 0xf00d0001);
                        ubc_setsize(vp, (off_t)np->n_size); /* XXX errors */
-                       if (eofbp) {
-                               /*
-                                * We may need to zero any previously invalid data
-                                * after the old EOF in the previous EOF buffer.
-                                *
-                                * For the old last page, don't zero bytes if there
-                                * are invalid bytes in that page (i.e. the page isn't
-                                * currently valid).
-                                * For pages after the old last page, zero them and
-                                * mark them as valid.
-                                */
-                               char *d;
-                               int i;
-                               if (ioflag & IO_NOCACHE)
-                                       SET(eofbp->nb_flags, NB_NOCACHE);
-                               NFS_BUF_MAP(eofbp);
-                               FSDBG(516, eofbp, eofoff, biosize - eofoff, 0xe0fff01e);
-                               d = eofbp->nb_data;
-                               i = eofoff/PAGE_SIZE;
-                               while (eofoff < biosize) {
-                                       int poff = eofoff & PAGE_MASK;
-                                       if (!poff || NBPGVALID(eofbp,i)) {
-                                               bzero(d + eofoff, PAGE_SIZE - poff);
-                                               NBPGVALID_SET(eofbp, i);
-                                       }
-                                       if (bp->nb_validend == eofoff)
-                                               bp->nb_validend += PAGE_SIZE - poff;
-                                       eofoff += PAGE_SIZE - poff;
-                                       i++;
-                               }
-                               nfs_buf_release(eofbp, 1);
-                       }
                }
                /*
                 * If dirtyend exceeds file size, chop it down.  This should
@@ -3020,11 +3371,12 @@ nfs_write_rpc2(
 {
        struct nfsmount *nmp;
        int error = 0, nfsvers;
-       int backup, wverfset, commit, committed;
+       int wverfset, commit, committed;
        uint64_t wverf = 0, wverf2;
        size_t nmwsize, totalsize, tsiz, len, rlen;
        struct nfsreq rq, *req = &rq;
        uint32_t stategenid = 0, vrestart = 0, restart = 0;
+       uio_t uio_save = NULL;
 
 #if DIAGNOSTIC
        /* XXX limitation based on need to back up uio on short write */
@@ -3033,7 +3385,7 @@ nfs_write_rpc2(
 #endif
        FSDBG_TOP(537, np, uio_offset(uio), uio_resid(uio), *iomodep);
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        nmwsize = nmp->nm_wsize;
@@ -3047,6 +3399,11 @@ nfs_write_rpc2(
                return (EFBIG);
        }
 
+       uio_save = uio_duplicate(uio);
+       if (uio_save == NULL) {
+               return (EIO);
+       }
+
        while (tsiz > 0) {
                len = (tsiz > nmwsize) ? nmwsize : tsiz;
                FSDBG(537, np, uio_offset(uio), len, 0);
@@ -3060,7 +3417,7 @@ nfs_write_rpc2(
                if (!error)
                        error = nmp->nm_funcs->nf_write_rpc_async_finish(np, req, &commit, &rlen, &wverf2);
                nmp = NFSTONMP(np);
-               if (!nmp)
+               if (nfs_mount_gone(nmp))
                        error = ENXIO;
                if ((nmp->nm_vers >= NFS_VER4) && nfs_mount_state_error_should_restart(error) &&
                    (++restart <= nfs_mount_state_max_restarts(nmp))) { /* guard against no progress */
@@ -3088,8 +3445,9 @@ nfs_write_rpc2(
 
                /* check for a short write */
                if (rlen < len) {
-                       backup = len - rlen;
-                       uio_pushback(uio, backup);
+                       /* Reset the uio to reflect the actual transfer */
+                       *uio = *uio_save;
+                       uio_update(uio, totalsize - (tsiz - rlen));
                        len = rlen;
                }
 
@@ -3110,13 +3468,14 @@ nfs_write_rpc2(
                                error = EIO;
                                break;
                        }
-                       backup = totalsize - tsiz;
-                       uio_pushback(uio, backup);
+                       *uio = *uio_save;       // Reset the uio back to the start
                        committed = NFS_WRITE_FILESYNC;
                        wverfset = 0;
                        tsiz = totalsize;
                }
        }
+       if (uio_save)
+               uio_free(uio_save);
        if (wverfset && wverfp)
                *wverfp = wverf;
        *iomodep = committed;
@@ -3143,7 +3502,7 @@ nfs3_write_rpc_async(
        struct nfsm_chain nmreq;
 
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -3192,7 +3551,7 @@ nfs3_write_rpc_async_finish(
        struct nfsm_chain nmrep;
 
        nmp = NFSTONMP(np);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                nfs_request_async_cancel(req);
                return (ENXIO);
        }
@@ -3204,7 +3563,7 @@ nfs3_write_rpc_async_finish(
        if (error == EINPROGRESS) /* async request restarted */
                return (error);
        nmp = NFSTONMP(np);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                error = ENXIO;
        if (!error && (lockerror = nfs_node_lock(np)))
                error = lockerror;
@@ -3283,13 +3642,13 @@ nfs3_vnop_mknod(
        int error = 0, lockerror = ENOENT, busyerror = ENOENT, status, wccpostattr = 0;
        struct timespec premtime = { 0, 0 };
        u_int32_t rdev;
-       u_int64_t xid, dxid;
+       u_int64_t xid = 0, dxid;
        int nfsvers, gotuid, gotgid;
        struct nfsm_chain nmreq, nmrep;
        struct nfsreq rq, *req = &rq;
 
        nmp = VTONMP(dvp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -3439,7 +3798,7 @@ nfs3_vnop_create(
        struct nfs_dulookup dul;
 
        nmp = VTONMP(dvp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -3618,7 +3977,7 @@ nfs_vnop_remove(
        /* XXX prevent removing a sillyrenamed file? */
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
@@ -3772,7 +4131,7 @@ int
 nfs_removeit(struct nfs_sillyrename *nsp)
 {
        struct nfsmount *nmp = NFSTONMP(nsp->nsr_dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        return nmp->nm_funcs->nf_remove_rpc(nsp->nsr_dnp, nsp->nsr_name, nsp->nsr_namlen, NULL, nsp->nsr_cred);
 }
@@ -3796,7 +4155,7 @@ nfs3_remove_rpc(
        struct nfsm_chain nmreq, nmrep;
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        if ((nfsvers == NFS_VER2) && (namelen > NFS_MAXNAMLEN))
@@ -3870,7 +4229,7 @@ nfs_vnop_rename(
        tnp = tvp ? VTONFS(tvp) : NULL;
 
        nmp = NFSTONMP(fdnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -4029,7 +4388,7 @@ nfs3_rename_rpc(
        struct nfsm_chain nmreq, nmrep;
 
        nmp = NFSTONMP(fdnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        if ((nfsvers == NFS_VER2) &&
@@ -4111,7 +4470,7 @@ nfs3_vnop_link(
                return (EXDEV);
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN))
@@ -4203,7 +4562,7 @@ nfs3_vnop_symlink(
        struct timespec premtime = { 0, 0 };
        vnode_t newvp = NULL;
        int nfsvers, gotuid, gotgid;
-       u_int64_t xid, dxid;
+       u_int64_t xid = 0, dxid;
        nfsnode_t np = NULL;
        nfsnode_t dnp = VTONFS(dvp);
        struct nfsmount *nmp;
@@ -4212,7 +4571,7 @@ nfs3_vnop_symlink(
        struct nfs_dulookup dul;
 
        nmp = VTONMP(dvp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -4361,14 +4720,14 @@ nfs3_vnop_mkdir(
        int error = 0, lockerror = ENOENT, busyerror = ENOENT, status, wccpostattr = 0;
        struct timespec premtime = { 0, 0 };
        int nfsvers, gotuid, gotgid;
-       u_int64_t xid, dxid;
+       u_int64_t xid= 0, dxid;
        fhandle_t fh;
        struct nfsm_chain nmreq, nmrep;
        struct nfsreq rq, *req = &rq;
        struct nfs_dulookup dul;
 
        nmp = VTONMP(dvp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN))
@@ -4513,7 +4872,7 @@ nfs3_vnop_rmdir(
        struct nfs_dulookup dul;
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN))
@@ -4636,7 +4995,7 @@ nfs_vnop_readdir(
        thread_t thd;
 
        nmp = VTONMP(dvp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        bigcookies = (nmp->nm_state & NFSSTA_BIGCOOKIES);
@@ -4705,7 +5064,7 @@ nfs_vnop_readdir(
        }
 
        while (!error && !done) {
-               OSAddAtomic(1, &nfsstats.biocache_readdirs);
+               OSAddAtomic64(1, &nfsstats.biocache_readdirs);
                cookie = nextcookie;
 getbuffer:
                error = nfs_buf_get(dnp, lbn, NFS_DIRBLKSIZ, thd, NBLK_READ, &bp);
@@ -4955,7 +5314,7 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp)
 
        if (cookie == dnp->n_eofcookie) { /* EOF cookie */
                nfs_node_unlock(dnp);
-               OSAddAtomic(1, &nfsstats.direofcache_hits);
+               OSAddAtomic64(1, &nfsstats.direofcache_hits);
                *ptc = 0;
                return (-1);
        }
@@ -4969,7 +5328,7 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp)
                        /* found a match for this cookie */
                        *lbnp = ndcc->cookies[i].lbn;
                        nfs_node_unlock(dnp);
-                       OSAddAtomic(1, &nfsstats.direofcache_hits);
+                       OSAddAtomic64(1, &nfsstats.direofcache_hits);
                        *ptc = 0;
                        return (0);
                }
@@ -4981,14 +5340,14 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp)
        if (eofptc) {
                /* but 32-bit match hit the EOF cookie */
                nfs_node_unlock(dnp);
-               OSAddAtomic(1, &nfsstats.direofcache_hits);
+               OSAddAtomic64(1, &nfsstats.direofcache_hits);
                return (-1);
        }
        if (iptc >= 0) {
                /* but 32-bit match got a hit */
                *lbnp = ndcc->cookies[iptc].lbn;
                nfs_node_unlock(dnp);
-               OSAddAtomic(1, &nfsstats.direofcache_hits);
+               OSAddAtomic64(1, &nfsstats.direofcache_hits);
                return (0);
        }
        nfs_node_unlock(dnp);
@@ -4998,7 +5357,7 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp)
         * Let's search the directory's buffers for the cookie.
         */
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        dpptc = NULL;
        found = 0;
@@ -5065,13 +5424,13 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp)
        }
        lck_mtx_unlock(nfs_buf_mutex);
        if (found) {
-               OSAddAtomic(1, &nfsstats.direofcache_hits);
+               OSAddAtomic64(1, &nfsstats.direofcache_hits);
                return (0);
        }
 
        /* still not found... oh well, just start a new block */
        *lbnp = cookie;
-       OSAddAtomic(1, &nfsstats.direofcache_misses);
+       OSAddAtomic64(1, &nfsstats.direofcache_misses);
        return (0);
 }
 
@@ -5169,7 +5528,8 @@ nfs_dir_buf_cache_lookup(nfsnode_t dnp, nfsnode_t *npp, struct componentname *cn
        daddr64_t lbn, nextlbn;
        int dotunder = (cnp->cn_namelen > 2) && (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == '_');
 
-       if (!(nmp = NFSTONMP(dnp)))
+       nmp = NFSTONMP(dnp);
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        if (!purge)
                *npp = NULL;
@@ -5310,7 +5670,7 @@ nfs3_readdir_rpc(nfsnode_t dnp, struct nfsbuf *bp, vfs_context_t ctx)
        struct timeval now;
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
        nmreaddirsize = nmp->nm_readdirsize;
@@ -5333,7 +5693,7 @@ noplus:
        } else {
                cookie = bp->nb_lblkno;
                /* increment with every buffer read */
-               OSAddAtomic(1, &nfsstats.readdir_bios);
+               OSAddAtomic64(1, &nfsstats.readdir_bios);
        }
        lastcookie = cookie;
 
@@ -5446,7 +5806,7 @@ nextbuffer:
                                space_free = nfs_dir_buf_freespace(bp, rdirplus);
                                dp = NFS_DIR_BUF_FIRST_DIRENTRY(bp);
                                /* increment with every buffer read */
-                               OSAddAtomic(1, &nfsstats.readdir_bios);
+                               OSAddAtomic64(1, &nfsstats.readdir_bios);
                        }
                        nmrepsave = nmrep;
                        dp->d_fileno = fileno;
@@ -5593,7 +5953,7 @@ nfs_sillyrename(
        struct nfsmount *nmp;
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        nfs_name_cache_purge(dnp, np, cnp, ctx);
@@ -5672,7 +6032,7 @@ nfs3_lookup_rpc_async(
        int error = 0, nfsvers;
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -5708,6 +6068,8 @@ nfs3_lookup_rpc_async_finish(
        struct nfsm_chain nmrep;
 
        nmp = NFSTONMP(dnp);
+       if (nmp == NULL)
+               return (ENXIO);
        nfsvers = nmp->nm_vers;
 
        nfsm_chain_null(&nmrep);
@@ -5771,7 +6133,7 @@ nfs_lookitup(
        struct nfsreq rq, *req = &rq;
 
        nmp = NFSTONMP(dnp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXNAME) &&
@@ -5972,7 +6334,7 @@ nfs3_commit_rpc(
 
        nmp = NFSTONMP(np);
        FSDBG(521, np, offset, count, nmp ? nmp->nm_state : 0);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        if (!(nmp->nm_state & NFSSTA_HASWRITEVERF))
                return (0);
@@ -6066,7 +6428,7 @@ nfs3_pathconf_rpc(
        struct nfsmount *nmp = NFSTONMP(np);
        uint32_t val = 0;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        nfsvers = nmp->nm_vers;
 
@@ -6088,6 +6450,7 @@ nfs3_pathconf_rpc(
                error = status;
        nfsm_chain_get_32(error, &nmrep, nfsap->nfsa_maxlink);
        nfsm_chain_get_32(error, &nmrep, nfsap->nfsa_maxname);
+       nfsap->nfsa_flags &= ~(NFS_FSFLAG_NO_TRUNC|NFS_FSFLAG_CHOWN_RESTRICTED|NFS_FSFLAG_CASE_INSENSITIVE|NFS_FSFLAG_CASE_PRESERVING);
        nfsm_chain_get_32(error, &nmrep, val);
        if (val)
                nfsap->nfsa_flags |= NFS_FSFLAG_NO_TRUNC;
@@ -6118,6 +6481,7 @@ nfs3_pathconf_cache(struct nfsmount *nmp, struct nfs_fsattr *nfsap)
 {
        nmp->nm_fsattr.nfsa_maxlink = nfsap->nfsa_maxlink;
        nmp->nm_fsattr.nfsa_maxname = nfsap->nfsa_maxname;
+       nmp->nm_fsattr.nfsa_flags &= ~(NFS_FSFLAG_NO_TRUNC|NFS_FSFLAG_CHOWN_RESTRICTED|NFS_FSFLAG_CASE_INSENSITIVE|NFS_FSFLAG_CASE_PRESERVING);
        nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_NO_TRUNC;
        nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_CHOWN_RESTRICTED;
        nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_CASE_INSENSITIVE;
@@ -6157,7 +6521,7 @@ nfs_vnop_pathconf(
        uint nbits;
 
        nmp = VTONMP(vp);
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        switch (ap->a_name) {
@@ -6198,7 +6562,7 @@ nfs_vnop_pathconf(
                        if (error)
                                return (error);
                        nmp = VTONMP(vp);
-                       if (!nmp)
+                       if (nfs_mount_gone(nmp))
                                return (ENXIO);
                        lck_mtx_lock(&nmp->nm_lock);
                        if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS) {
@@ -6218,7 +6582,7 @@ nfs_vnop_pathconf(
                if (error)
                        return (error);
                nmp = VTONMP(vp);
-               if (!nmp)
+               if (nfs_mount_gone(nmp))
                        return (ENXIO);
                lck_mtx_lock(&nmp->nm_lock);
                nfsap = &nfsa;
@@ -6546,18 +6910,94 @@ nfs_vnop_ioctl(
 {
        vfs_context_t ctx = ap->a_context;
        vnode_t vp = ap->a_vp;
+       struct nfsmount *mp = VTONMP(vp);
+       struct user_nfs_gss_principal gprinc;
+       uint32_t len;
        int error = ENOTTY;
 
+       if (mp == NULL)
+               return (ENXIO);
+       
        switch (ap->a_command) {
 
        case F_FULLFSYNC:
                if (vnode_vfsisrdonly(vp))
                        return (EROFS);
-               if (!VTONMP(vp))
-                       return (ENXIO);
                error = nfs_flush(VTONFS(vp), MNT_WAIT, vfs_context_thread(ctx), 0);
                break;
-
+       case NFS_FSCTL_DESTROY_CRED:
+               if (!auth_is_kerberized(mp->nm_auth))
+                       return (ENOTSUP);
+               error = nfs_gss_clnt_ctx_remove(mp, vfs_context_ucred(ctx));
+               break;
+       case NFS_FSCTL_SET_CRED:
+               if (!auth_is_kerberized(mp->nm_auth))
+                       return (ENOTSUP);
+               NFS_DBG(NFS_FAC_GSS, 7, "Enter NFS_FSCTL_SET_CRED (proc %d) data = %p\n", vfs_context_is64bit(ctx), (void *)ap->a_data);
+               if (vfs_context_is64bit(ctx)) {
+                       gprinc = *(struct user_nfs_gss_principal *)ap->a_data;
+               } else {
+                       struct nfs_gss_principal *tp;
+                       tp = (struct nfs_gss_principal *)ap->a_data;
+                       gprinc.princlen = tp->princlen;
+                       gprinc.nametype = tp->nametype;
+                       gprinc.principal = CAST_USER_ADDR_T(tp->principal);
+               }
+               if (gprinc.princlen > MAXPATHLEN)
+                       return (EINVAL);
+               NFS_DBG(NFS_FAC_GSS, 7, "Received principal length %d name type = %d\n", gprinc.princlen, gprinc.nametype);
+               uint8_t *p;
+               MALLOC(p, uint8_t *, gprinc.princlen+1, M_TEMP, M_WAITOK|M_ZERO);
+               if (p == NULL)
+                       return (ENOMEM);
+               error = copyin(gprinc.principal, p, gprinc.princlen);
+               if (error) {
+                       NFS_DBG(NFS_FAC_GSS, 7, "NFS_FSCTL_SET_CRED could not copy in princiapl data of len %d: %d\n",
+                               gprinc.princlen, error);
+                       FREE(p, M_TEMP);
+                       return (error);
+               }
+               NFS_DBG(NFS_FAC_GSS, 7, "Seting credential to principal %s\n", p);
+               error = nfs_gss_clnt_ctx_set_principal(mp, ctx, p, gprinc.princlen, gprinc.nametype);
+               NFS_DBG(NFS_FAC_GSS, 7, "Seting credential to principal %s returned %d\n", p, error);
+               FREE(p, M_TEMP);
+               break;
+       case NFS_FSCTL_GET_CRED:
+               if (!auth_is_kerberized(mp->nm_auth))
+                       return (ENOTSUP);
+               error = nfs_gss_clnt_ctx_get_principal(mp, ctx, &gprinc);
+               if (error)
+                       break;
+               if (vfs_context_is64bit(ctx)) {
+                       struct user_nfs_gss_principal *upp = (struct user_nfs_gss_principal *)ap->a_data;
+                       len = upp->princlen;
+                       if (gprinc.princlen < len)
+                               len = gprinc.princlen;
+                       upp->princlen = gprinc.princlen;
+                       upp->nametype = gprinc.nametype;
+                       upp->flags = gprinc.flags;
+                       if (gprinc.principal)
+                               error = copyout((void *)gprinc.principal, upp->principal, len);
+                       else
+                               upp->principal = USER_ADDR_NULL;
+               } else {
+                       struct nfs_gss_principal *u32pp = (struct nfs_gss_principal *)ap->a_data;
+                       len = u32pp->princlen;
+                       if (gprinc.princlen < len)
+                               len = gprinc.princlen;
+                       u32pp->princlen = gprinc.princlen;
+                       u32pp->nametype = gprinc.nametype;
+                       u32pp->flags = gprinc.flags;
+                       if (gprinc.principal)
+                               error = copyout((void *)gprinc.principal, u32pp->principal, len);
+                       else
+                               u32pp->principal = (user32_addr_t)0;
+               }
+               if (error) {
+                       NFS_DBG(NFS_FAC_GSS, 7, "NFS_FSCTL_GET_CRED could not copy out princiapl data of len %d: %d\n",
+                               gprinc.princlen, error);
+               }
+               FREE(gprinc.principal, M_TEMP);
        }
 
        return (error);
@@ -6631,7 +7071,7 @@ nfs_vnop_pagein(
        if (size <= 0) {
                printf("nfs_pagein: invalid size %ld", size);
                if (!nofreeupl)
-                       (void) ubc_upl_abort(pl, 0);
+                       (void) ubc_upl_abort_range(pl, pl_offset, size, 0);
                return (EINVAL);
        }
        if (f_offset < 0 || f_offset >= (off_t)np->n_size || (f_offset & PAGE_MASK_64)) {
@@ -6650,7 +7090,7 @@ nfs_vnop_pagein(
                &uio_buf, sizeof(uio_buf));
 
        nmp = VTONMP(vp);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                if (!nofreeupl)
                        ubc_upl_abort_range(pl, pl_offset, size,
                                UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
@@ -6698,7 +7138,7 @@ tryagain:
 #if UPL_DEBUG
                        upl_ubc_alias_set(pl, (uintptr_t) current_thread(), (uintptr_t) 2);
 #endif /* UPL_DEBUG */
-                       OSAddAtomic(1, &nfsstats.pageins);
+                       OSAddAtomic64(1, &nfsstats.pageins);
                        error = nmp->nm_funcs->nf_read_rpc_async_finish(np, req[nextwait], uio, &retsize, NULL);
                        req[nextwait] = NULL;
                        nextwait = (nextwait + 1) % MAXPAGINGREQS;
@@ -6778,7 +7218,7 @@ cancel:
  * erroneous.
  */
 char nfs_pageouterrorhandler(int);
-enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, RETRYWITHSLEEP, SEVER};
+enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, SEVER};
 #define NFS_ELAST 88
 static u_char errorcount[NFS_ELAST+1]; /* better be zeros when initialized */
 static const char errortooutcome[NFS_ELAST+1] = {
@@ -6944,7 +7384,7 @@ nfs_vnop_pageout(
        if (size <= 0) {
                printf("nfs_pageout: invalid size %ld", size);
                if (!nofreeupl)
-                       ubc_upl_abort(pl, 0);
+                       ubc_upl_abort_range(pl, pl_offset, size, 0);
                return (EINVAL);
        }
 
@@ -6977,7 +7417,7 @@ nfs_vnop_pageout(
                                nfs_data_unlock_noupdate(np);
                                /* no panic. just tell vm we are busy */
                                if (!nofreeupl)
-                                       ubc_upl_abort(pl, 0);
+                                       ubc_upl_abort_range(pl, pl_offset, size, 0);
                                return (EBUSY);
                        }
                        if (bp->nb_dirtyend > 0) {
@@ -7024,7 +7464,7 @@ nfs_vnop_pageout(
                                    lck_mtx_unlock(nfs_buf_mutex);
                                    nfs_data_unlock_noupdate(np);
                                    if (!nofreeupl)
-                                       ubc_upl_abort(pl, 0);
+                                           ubc_upl_abort_range(pl, pl_offset, size, 0);
                                    return (EBUSY);
                                }
                                if ((bp->nb_dirtyoff < start) ||
@@ -7135,7 +7575,7 @@ tryagain:
                        uio_reset(auio, txoffset, UIO_SYSSPACE, UIO_WRITE);
                        uio_addiov(auio, CAST_USER_ADDR_T(txaddr), iosize);
                        FSDBG(323, uio_offset(auio), iosize, txaddr, txsize);
-                       OSAddAtomic(1, &nfsstats.pageouts);
+                       OSAddAtomic64(1, &nfsstats.pageouts);
                        nfs_node_lock_force(np);
                        np->n_numoutput++;
                        nfs_node_unlock(np);
@@ -7321,11 +7761,6 @@ cancel:
                                case RETRY:
                                        abortflags = UPL_ABORT_FREE_ON_EMPTY;
                                        break;
-                               case RETRYWITHSLEEP:
-                                       abortflags = UPL_ABORT_FREE_ON_EMPTY;
-                                       /* pri unused. PSOCK for placeholder. */
-                                       tsleep(&lbolt, PSOCK, "nfspageout", 0);
-                                       break;
                                case SEVER: /* not implemented */
                                default:
                                        NP(np, "nfs_pageout: action %d not expected", action);
@@ -7358,7 +7793,7 @@ nfs_vnop_blktooff(
        vnode_t vp = ap->a_vp;
        struct nfsmount *nmp = VTONMP(vp);
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        biosize = nmp->nm_biosize;
 
@@ -7380,7 +7815,7 @@ nfs_vnop_offtoblk(
        vnode_t vp = ap->a_vp;
        struct nfsmount *nmp = VTONMP(vp);
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        biosize = nmp->nm_biosize;
 
@@ -7407,7 +7842,7 @@ nfs_vnop_monitor(
        struct nfsmount *nmp = VTONMP(ap->a_vp);
        int error = 0;
 
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        /* make sure that the vnode's monitoring status is up to date */