diff --git a/sys/fs/nfsclient/nfs_clrpcops.c.close b/sys/fs/nfsclient/nfs_clrpcops.c.write --- a/sys/fs/nfsclient/nfs_clrpcops.c.close +++ b/sys/fs/nfsclient/nfs_clrpcops.c.write @@ -3627,7 +3627,8 @@ nfsattrbit_t attrbits, dattrbits; size_t tresid; u_int32_t *tl2 = NULL, rderr; - struct timespec dctime; + struct timespec dctime, ts; + bool attr_ok; KASSERT(uiop->uio_iovcnt == 1 && (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0, @@ -3812,6 +3813,7 @@ *tl = txdr_unsigned(NFSV4OP_GETATTR); (void) nfsrv_putattrbit(nd, &dattrbits); } + nanouptime(&ts); error = nfscl_request(nd, vp, p, cred, stuff); if (error) return (error); @@ -3969,6 +3971,7 @@ ncookie.lval[1]; if (nfhp != NULL) { + attr_ok = true; if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len, dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) { VREF(vp); @@ -3998,12 +4001,32 @@ if (!error) { newvp = NFSTOV(np); unlocknewvp = 1; + /* + * If n_localmodtime >= time before RPC, + * then a file modification operation, + * such as VOP_SETATTR() of size, has + * ocuurred while the Lookup RPC and + * acquisition of the vnode happened. As + * such, the attributes might be stale, + * with possibly an incorrect size. + */ + NFSLOCKNODE(np); + if (timespecisset( + &np->n_localmodtime) && + timespeccmp(&np->n_localmodtime, + &ts, >=)) { + NFSCL_DEBUG(4, "nfsrpc_readdirplus:" + " localmod stale attributes\n"); + attr_ok = false; + } + NFSUNLOCKNODE(np); } } nfhp = NULL; if (newvp != NULLVP) { - error = nfscl_loadattrcache(&newvp, - &nfsva, NULL, NULL, 0, 0); + if (attr_ok) + error = nfscl_loadattrcache(&newvp, + &nfsva, NULL, NULL, 0, 0); if (error) { if (unlocknewvp) vput(newvp); diff --git a/sys/fs/nfsclient/nfs_clvnops.c.stat b/sys/fs/nfsclient/nfs_clvnops.c.write --- a/sys/fs/nfsclient/nfs_clvnops.c.stat +++ b/sys/fs/nfsclient/nfs_clvnops.c.write @@ -1014,6 +1014,7 @@ struct vattr *vap = ap->a_vap; int error = 0; u_quad_t tsize; + struct timespec ts; #ifndef nolint tsize = (u_quad_t)0; @@ -1108,11 +1109,18 @@ NFSUNLOCKNODE(np); } error = nfs_setattrrpc(vp, vap, ap->a_cred, td); - if (error && vap->va_size != VNOVAL) { - NFSLOCKNODE(np); - np->n_size = np->n_vattr.na_size = tsize; - vnode_pager_setsize(vp, tsize); - NFSUNLOCKNODE(np); + if (vap->va_size != VNOVAL) { + if (error == 0) { + nanouptime(&ts); + NFSLOCKNODE(np); + np->n_localmodtime = ts; + NFSUNLOCKNODE(np); + } else { + NFSLOCKNODE(np); + np->n_size = np->n_vattr.na_size = tsize; + vnode_pager_setsize(vp, tsize); + NFSUNLOCKNODE(np); + } } return (error); } @@ -1169,7 +1177,7 @@ struct nfsfh *nfhp; struct nfsvattr dnfsva, nfsva; struct vattr vattr; - struct timespec nctime; + struct timespec nctime, ts; uint32_t openmode; *vpp = NULLVP; @@ -1293,6 +1301,7 @@ newvp = NULLVP; NFSINCRGLOBAL(nfsstatsv1.lookupcache_misses); + nanouptime(&ts); error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, NULL, openmode); @@ -1359,6 +1368,22 @@ if (error) return (error); newvp = NFSTOV(np); + /* + * If n_localmodtime >= time before RPC, then + * a file modification operation, such as + * VOP_SETATTR() of size, has occurred while + * the Lookup RPC and acquisition of the vnode + * happened. As such, the attributes might + * be stale, with possibly an incorrect size. + */ + NFSLOCKNODE(np); + if (timespecisset(&np->n_localmodtime) && + timespeccmp(&np->n_localmodtime, &ts, >=)) { + NFSCL_DEBUG(4, "nfs_lookup: rename localmod " + "stale attributes\n"); + attrflag = 0; + } + NFSUNLOCKNODE(np); if (attrflag) (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, 0, 1); @@ -1418,6 +1443,22 @@ if (error) return (error); newvp = NFSTOV(np); + /* + * If n_localmodtime >= time before RPC, then + * a file modification operation, such as + * VOP_SETATTR() of size, has occurred while + * the Lookup RPC and acquisition of the vnode + * happened. As such, the attributes might + * be stale, with possibly an incorrect size. + */ + NFSLOCKNODE(np); + if (timespecisset(&np->n_localmodtime) && + timespeccmp(&np->n_localmodtime, &ts, >=)) { + NFSCL_DEBUG(4, "nfs_lookup: localmod " + "stale attributes\n"); + attrflag = 0; + } + NFSUNLOCKNODE(np); if (attrflag) (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, 0, 1); @@ -2635,7 +2676,9 @@ struct componentname cn; int error = 0, attrflag, dattrflag; u_int hash; + struct timespec ts; + nanouptime(&ts); error = nfsrpc_lookup(dvp, name, len, cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, NULL, 0); if (dattrflag) @@ -2696,6 +2739,22 @@ if (error) return (error); newvp = NFSTOV(np); + /* + * If n_localmodtime >= time before RPC, then + * a file modification operation, such as + * VOP_SETATTR() of size, has occurred while + * the Lookup RPC and acquisition of the vnode + * happened. As such, the attributes might + * be stale, with possibly an incorrect size. + */ + NFSLOCKNODE(np); + if (timespecisset(&np->n_localmodtime) && + timespeccmp(&np->n_localmodtime, &ts, >=)) { + NFSCL_DEBUG(4, "nfs_lookitup: localmod " + "stale attributes\n"); + attrflag = 0; + } + NFSUNLOCKNODE(np); } if (!attrflag && *npp == NULL) { if (newvp == dvp) @@ -3643,11 +3702,14 @@ struct thread *td = curthread; struct nfsvattr nfsva; struct nfsmount *nmp; + struct nfsnode *np; off_t alen; int attrflag, error, ret; + struct timespec ts; attrflag = 0; nmp = VFSTONFS(vp->v_mount); + np = VTONFS(vp); mtx_lock(&nmp->nm_mtx); if (NFSHASNFSV4(nmp) && nmp->nm_minorvers >= NFSV42_MINORVERSION && (nmp->nm_privflag & NFSMNTP_NOALLOCATE) == 0) { @@ -3667,6 +3729,10 @@ if (error == 0) { *ap->a_offset += alen; *ap->a_len -= alen; + nanouptime(&ts); + NFSLOCKNODE(np); + np->n_localmodtime = ts; + NFSUNLOCKNODE(np); } else if (error == NFSERR_NOTSUPP) { mtx_lock(&nmp->nm_mtx); nmp->nm_privflag |= NFSMNTP_NOALLOCATE; @@ -3701,6 +3767,7 @@ off_t tlen, mlen; int attrflag, error, ret; bool clipped; + struct timespec ts; error = 0; attrflag = 0; @@ -3743,6 +3810,10 @@ if (error == 0) { NFSCL_DEBUG(4, "dealloc: attrflag=%d na_size=%ju\n", attrflag, (uintmax_t)nfsva.na_size); + nanouptime(&ts); + NFSLOCKNODE(np); + np->n_localmodtime = ts; + NFSUNLOCKNODE(np); if (attrflag != 0) { if ((uint64_t)*ap->a_offset < nfsva.na_size) *ap->a_offset += omin((off_t) diff --git a/sys/fs/nfsclient/nfsnode.h.stat b/sys/fs/nfsclient/nfsnode.h --- a/sys/fs/nfsclient/nfsnode.h.stat +++ b/sys/fs/nfsclient/nfsnode.h @@ -129,6 +129,7 @@ struct nfsv4node *n_v4; /* extra V4 stuff */ struct ucred *n_writecred; /* Cred. for putpages */ struct nfsclopen *n_openstateid; /* Cached open stateid */ + struct timespec n_localmodtime; /* Last local modify */ }; #define n_atim n_un1.nf_atim