diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h index 9b09520b325..e6a125b388a 100644 --- a/sys/fs/nfs/nfs.h +++ b/sys/fs/nfs/nfs.h @@ -865,6 +865,8 @@ struct nfsslot { /* Enumerated type for nfsuserd state. */ typedef enum { NOTRUNNING=0, STARTSTOP=1, RUNNING=2 } nfsuserd_state; +typedef enum { UNKNOWN=0, DELETED=1, NLINK_ZERO=2, VALID=3 } nfsremove_status; + #endif /* _KERNEL */ #endif /* _NFS_NFS_H */ diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 68d2f7d993f..f46b0d28286 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -251,9 +251,9 @@ static struct { { NFSV4OP_CREATE, 5, "Create", 6, }, { NFSV4OP_CREATE, 1, "Create", 6, }, { NFSV4OP_CREATE, 3, "Create", 6, }, + { NFSV4OP_REMOVE, 3, "Remove", 6, }, { NFSV4OP_REMOVE, 1, "Remove", 6, }, - { NFSV4OP_REMOVE, 1, "Remove", 6, }, - { NFSV4OP_SAVEFH, 5, "Rename", 6, }, + { NFSV4OP_SAVEFH, 7, "Rename", 6, }, { NFSV4OP_SAVEFH, 6, "Link", 4, }, { NFSV4OP_READDIR, 2, "Readdir", 7, }, { NFSV4OP_READDIR, 2, "Readdir", 7, }, diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 7206d12bd6f..f59a898369e 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -483,11 +483,13 @@ int nfsrpc_mknod(vnode_t, char *, int, struct vattr *, u_int32_t, int nfsrpc_create(vnode_t, char *, int, struct vattr *, nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *); -int nfsrpc_remove(vnode_t, char *, int, vnode_t, struct ucred *, NFSPROC_T *, - struct nfsvattr *, int *); -int nfsrpc_rename(vnode_t, vnode_t, char *, int, vnode_t, vnode_t, char *, int, - struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, - int *, int *); +int nfsrpc_remove(struct vnode *, char *, int, struct vnode *, + struct nfsvattr *, int *, nfsremove_status *, struct nfsvattr *, int *, + struct ucred *, NFSPROC_T *); +int nfsrpc_rename(struct vnode *, struct vnode *, char *, int, struct vnode *, + struct vnode *, char *, int, nfsremove_status *, struct nfsvattr *, + struct nfsvattr *, int *, int *, struct nfsvattr *, int *, struct ucred *, + NFSPROC_T *); int nfsrpc_link(vnode_t, vnode_t, char *, int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, int *, int *); @@ -616,7 +618,7 @@ void nfscl_lockinit(struct nfsv4lock *); void nfscl_lockexcl(struct nfsv4lock *, void *); void nfscl_lockunlock(struct nfsv4lock *); void nfscl_lockderef(struct nfsv4lock *); -void nfscl_delegreturnvp(vnode_t, NFSPROC_T *); +void nfscl_delegreturnvp(struct vnode *, bool, NFSPROC_T *); void nfscl_docb(struct nfsrv_descript *, NFSPROC_T *); void nfscl_releasealllocks(struct nfsclclient *, vnode_t, NFSPROC_T *, void *, int); @@ -656,6 +658,7 @@ void nfscl_freelayout(struct nfscllayout *); void nfscl_freeflayout(struct nfsclflayout *); void nfscl_freedevinfo(struct nfscldevinfo *); int nfscl_layoutcommit(vnode_t, NFSPROC_T *); +void nfscl_startdelegrecall(struct nfsclclient *, struct nfsfh *); /* nfs_clport.c */ int nfscl_nget(mount_t, vnode_t, struct nfsfh *, diff --git a/sys/fs/nfsclient/nfs_clnode.c b/sys/fs/nfsclient/nfs_clnode.c index be2024730cf..f85f961d424 100644 --- a/sys/fs/nfsclient/nfs_clnode.c +++ b/sys/fs/nfsclient/nfs_clnode.c @@ -205,7 +205,7 @@ nfs_freesillyrename(void *arg, __unused int pending) } static void -ncl_releasesillyrename(struct vnode *vp, struct thread *td) +ncl_releasesillyrename(struct vnode *vp, bool flushed, struct thread *td) { struct nfsnode *np; struct sillyrename *sp; @@ -220,7 +220,8 @@ ncl_releasesillyrename(struct vnode *vp, struct thread *td) sp = NULL; if (sp != NULL) { NFSUNLOCKNODE(np); - (void) ncl_vinvalbuf(vp, 0, td, 1); + if (flushed) + (void)ncl_vinvalbuf(vp, 0, td, 1); /* * Remove the silly file that was rename'd earlier */ @@ -238,9 +239,13 @@ ncl_inactive(struct vop_inactive_args *ap) struct vnode *vp = ap->a_vp; struct nfsnode *np; struct thread *td; + struct nfsmount *nmp; + bool flushed; td = curthread; np = VTONFS(vp); + nmp = VFSTONFS(vp->v_mount); + flushed = true; if (NFS_ISV4(vp) && vp->v_type == VREG) { NFSLOCKNODE(np); np->n_openstateid = NULL; @@ -251,13 +256,18 @@ ncl_inactive(struct vop_inactive_args *ap) * buffers/pages must be flushed before the close, so that the * stateid is available for the writes. */ - vnode_pager_clean_sync(vp); - (void)ncl_flush(vp, MNT_WAIT, td, 1, 0); + if ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || !NFSHASNFSV4N(nmp) || + nfscl_mustflush(vp) != 0) { + vnode_pager_clean_sync(vp); + (void)ncl_flush(vp, MNT_WAIT, td, 1, 0); + } else { + flushed = false; + } (void)nfsrpc_close(vp, 1, td); } NFSLOCKNODE(np); - ncl_releasesillyrename(vp, td); + ncl_releasesillyrename(vp, flushed, td); /* * NMODIFIED means that there might be dirty/stale buffers @@ -294,7 +304,7 @@ ncl_reclaim(struct vop_reclaim_args *ap) nfs_reclaim_p(ap); NFSLOCKNODE(np); - ncl_releasesillyrename(vp, td); + ncl_releasesillyrename(vp, true, td); if (NFS_ISV4(vp) && vp->v_type == VREG) { np->n_openstateid = NULL; @@ -315,7 +325,7 @@ ncl_reclaim(struct vop_reclaim_args *ap) MNT_ILOCK(mp); if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0) { MNT_IUNLOCK(mp); - nfscl_delegreturnvp(vp, td); + nfscl_delegreturnvp(vp, true, td); } else MNT_IUNLOCK(mp); } else diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 4ff56f75123..c07da6f9275 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -2859,22 +2859,28 @@ nfsmout: * Nfs remove rpc */ int -nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp, - struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp) +nfsrpc_remove(struct vnode *dvp, char *name, int namelen, struct vnode *vp, + struct nfsvattr *nap, int *attrflagp, nfsremove_status *file_status, + struct nfsvattr *dnap, int *dattrflagp, struct ucred *cred, NFSPROC_T *p) { - u_int32_t *tl; + uint32_t *tl; struct nfsrv_descript nfsd, *nd = &nfsd; struct nfsnode *np; struct nfsmount *nmp; nfsv4stateid_t dstateid; - int error, ret = 0, i; + nfsattrbit_t attrbits; + int error, i, ret; *dattrflagp = 0; + *attrflagp = 0; + *file_status = UNKNOWN; + ret = 0; if (namelen > NFS_MAXNAMLEN) return (ENAMETOOLONG); nmp = VFSTONFS(dvp->v_mount); tryagain: - if (NFSHASNFSV4(nmp) && ret == 0) { + if (NFSHASNFSV4(nmp) && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || + !NFSHASNFSV4N(nmp)) && ret == 0) { ret = nfscl_removedeleg(vp, p, &dstateid); if (ret == 1) { NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp, cred); @@ -2899,9 +2905,19 @@ tryagain: } if (ret == 0) NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp, cred); - (void) nfsm_strtom(nd, name, namelen); + (void)nfsm_strtom(nd, name, namelen); + if (ret == 0 && (nd->nd_flag & ND_NFSV4) != 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_PUTFH); + np = VTONFS(vp); + (void)nfsm_fhtom(nmp, nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0); + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + NFSGETATTR_ATTRBIT(&attrbits); + *tl = txdr_unsigned(NFSV4OP_GETATTR); + (void)nfsrv_putattrbit(nd, &attrbits); + } error = nfscl_request(nd, dvp, p, cred); - if (error) + if (error != 0) return (error); if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) { /* For NFSv4, parse out any Delereturn replies. */ @@ -2924,7 +2940,41 @@ tryagain: } error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, NULL); } - if (nd->nd_repstat && !error) + if (ret == 0 && (nd->nd_flag & (ND_NFSV4 | + ND_NOMOREDATA)) == ND_NFSV4) { + /* Parse out the Remove reply for NFSPROC_REMOVE. */ + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED + 2 * NFSX_HYPER); + /* No use for change info for now. */ + /* The Remove succeeded. */ + nd->nd_repstat = 0; + } + if (ret == 0 && (nd->nd_flag & (ND_NFSV4 | + ND_NOMOREDATA)) == ND_NFSV4) { + /* Parse out the PutFH, Getattr for NFSPROC_REMOVE. */ + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) != 0) { + i = fxdr_unsigned(int, *(tl + 1)); + if (i == NFSERR_STALE) + *file_status = DELETED; + } else { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) != 0) { + i = fxdr_unsigned(int, *(tl + 1)); + if (i == NFSERR_STALE) + *file_status = DELETED; + } else { + error = nfsm_loadattr(nd, nap); + if (error == 0) { + *attrflagp = 1; + if (nap->na_nlink == 0) + *file_status = NLINK_ZERO; + else + *file_status = VALID; + } + } + } + } + if (nd->nd_repstat != 0 && error == 0) error = nd->nd_repstat; nfsmout: m_freem(nd->nd_mrep); @@ -2935,12 +2985,14 @@ nfsmout: * Do an nfs rename rpc. */ int -nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen, - vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred, - NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap, - int *fattrflagp, int *tattrflagp) +nfsrpc_rename(struct vnode *fdvp, struct vnode *fvp, char *fnameptr, + int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr, + int tnamelen, nfsremove_status *tvp_status, struct nfsvattr *fnap, + struct nfsvattr *tnap, int *fattrflagp, int *tattrflagp, + struct nfsvattr *tvpnap, int *tvpattrflagp, struct ucred *cred, + NFSPROC_T *p) { - u_int32_t *tl; + uint32_t *tl; struct nfsrv_descript nfsd, *nd = &nfsd; struct nfsmount *nmp; struct nfsnode *np; @@ -2950,11 +3002,14 @@ nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen, *fattrflagp = 0; *tattrflagp = 0; + *tvpattrflagp = 0; + *tvp_status = UNKNOWN; nmp = VFSTONFS(fdvp->v_mount); if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN) return (ENAMETOOLONG); tryagain: - if (NFSHASNFSV4(nmp) && ret == 0) { + if (NFSHASNFSV4(nmp) && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || + !NFSHASNFSV4N(nmp)) && ret == 0) { ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp, &tdstateid, &gottd, p); if (gotfd && gottd) { @@ -3007,29 +3062,44 @@ tryagain: } if (ret == 0) NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp, cred); - if (nd->nd_flag & ND_NFSV4) { + if ((nd->nd_flag & ND_NFSV4) != 0) { NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_GETATTR); NFSWCCATTR_ATTRBIT(&attrbits); - (void) nfsrv_putattrbit(nd, &attrbits); + (void)nfsrv_putattrbit(nd, &attrbits); NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_PUTFH); (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh, VTONFS(tdvp)->n_fhp->nfh_len, 0); NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_GETATTR); - (void) nfsrv_putattrbit(nd, &attrbits); + (void)nfsrv_putattrbit(nd, &attrbits); nd->nd_flag |= ND_V4WCCATTR; NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_RENAME); } - (void) nfsm_strtom(nd, fnameptr, fnamelen); - if (!(nd->nd_flag & ND_NFSV4)) + (void)nfsm_strtom(nd, fnameptr, fnamelen); + if ((nd->nd_flag & ND_NFSV4) == 0) (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh, VTONFS(tdvp)->n_fhp->nfh_len, 0); - (void) nfsm_strtom(nd, tnameptr, tnamelen); + (void)nfsm_strtom(nd, tnameptr, tnamelen); + if (ret == 0 && (nd->nd_flag & ND_NFSV4) != 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + /* When tvp == NULL, it doesn't matter which dvp is used. */ + *tl = txdr_unsigned(NFSV4OP_PUTFH); + if (tvp != NULL) + (void)nfsm_fhtom(nmp, nd, VTONFS(tvp)->n_fhp->nfh_fh, + VTONFS(tvp)->n_fhp->nfh_len, 0); + else + (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh, + VTONFS(tdvp)->n_fhp->nfh_len, 0); + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_GETATTR); + NFSGETATTR_ATTRBIT(&attrbits); + (void)nfsrv_putattrbit(nd, &attrbits); + } error = nfscl_request(nd, fdvp, p, cred); - if (error) + if (error != 0) return (error); if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) { /* For NFSv4, parse out any Delereturn replies. */ @@ -3045,7 +3115,7 @@ tryagain: for (i = 0; i < (ret * 2); i++) { if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) { - NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); if (*(tl + 1)) { if (i == 1 && ret > 1) { /* @@ -3065,23 +3135,57 @@ tryagain: } /* Now, the first wcc attribute reply. */ if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) { - NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); if (*(tl + 1)) nd->nd_flag |= ND_NOMOREDATA; } error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL, NULL); /* and the second wcc attribute reply. */ if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 && - !error) { - NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + error == 0) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); if (*(tl + 1)) nd->nd_flag |= ND_NOMOREDATA; } - if (!error) + if (error == 0) error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp, NULL, NULL); } - if (nd->nd_repstat && !error) + if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 && + ret == 0 && error == 0) { + /* Parse out the rename successful reply. */ + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + + 4 * NFSX_HYPER); + nd->nd_repstat = 0; /* Rename succeeded. */ + /* Parse PutFH reply for tvp. */ + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) != 0) { + if (tvp != NULL) { + i = fxdr_unsigned(int, *(tl + 1)); + if (i == NFSERR_STALE) + *tvp_status = DELETED; + } + } else { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) != 0) { + if (tvp != NULL) { + i = fxdr_unsigned(int, *(tl + 1)); + if (i == NFSERR_STALE) + *tvp_status = DELETED; + } + } else { + error = nfsm_loadattr(nd, tvpnap); + if (error == 0 && tvp != NULL) { + *tvpattrflagp = 1; + if (tvpnap->na_nlink == 0) + *tvp_status = NLINK_ZERO; + else + *tvp_status = VALID; + } + } + } + } + if (nd->nd_repstat != 0 && error == 0) error = nd->nd_repstat; nfsmout: m_freem(nd->nd_mrep); diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index adfb68f5d8f..fe3682ae437 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -3508,7 +3508,7 @@ nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p, * Return any delegation for this vp. */ void -nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p) +nfscl_delegreturnvp(struct vnode *vp, bool retdeleg, NFSPROC_T *p) { struct nfsclclient *clp; struct nfscldeleg *dp; @@ -3531,12 +3531,15 @@ nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p) if (clp != NULL) dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); - if (dp != NULL) { + if (dp != NULL && + (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0) { nfscl_cleandeleg(dp); nfscl_freedeleg(&clp->nfsc_deleg, dp, false); NFSUNLOCKCLSTATE(); - newnfs_copycred(&dp->nfsdl_cred, cred); - nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); + if (retdeleg) { + newnfs_copycred(&dp->nfsdl_cred, cred); + nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); + } free(dp, M_NFSCLDELEG); } else NFSUNLOCKCLSTATE(); @@ -3716,18 +3719,10 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) clp = nfscl_getclnt(cbident); else clp = nfscl_getclntsess(sessionid); - if (clp != NULL) { - dp = nfscl_finddeleg(clp, nfhp->nfh_fh, - nfhp->nfh_len); - if (dp != NULL && (dp->nfsdl_flags & - NFSCLDL_DELEGRET) == 0) { - dp->nfsdl_flags |= - NFSCLDL_RECALL; - wakeup((caddr_t)clp); - } - } else { + if (clp != NULL) + nfscl_startdelegrecall(clp, nfhp); + else error = NFSERR_SERVERFAULT; - } NFSUNLOCKCLSTATE(); } if (nfhp != NULL) @@ -5957,3 +5952,18 @@ tryagain: NFSUNLOCKCLSTATE(); return (0); } + +/* + * Start the recall of a delegation. Called for CB_RECALL and REMOVE + * when nlink == 0 after the REMOVE. + */ +void nfscl_startdelegrecall(struct nfsclclient *clp, struct nfsfh *nfhp) +{ + struct nfscldeleg *dp; + + dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len); + if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0) { + dp->nfsdl_flags |= NFSCLDL_RECALL; + wakeup((caddr_t)clp); + } +} diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index c2185992f99..84046cdb503 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -106,6 +106,7 @@ uint32_t nfscl_accesscache_load_done_id; extern struct nfsstatsv1 nfsstatsv1; extern int nfsrv_useacl; extern int nfscl_debuglevel; +NFSCLSTATEMUTEX; MALLOC_DECLARE(M_NEWNFSREQ); static vop_read_t nfsfifo_read; @@ -250,10 +251,13 @@ VFS_VOP_VECTOR_REGISTER(newnfs_fifoops); static int nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct vattr *vap); static int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name, - int namelen, struct ucred *cred, struct thread *td); + int namelen, struct ucred *cred, struct thread *td, bool silly); +static void nfs_removestatus(struct vnode *vp, nfsremove_status file_status, + bool silly, struct thread *td); static int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp, - char *tnameptr, int tnamelen, struct ucred *cred, struct thread *td); + char *tnameptr, int tnamelen, bool silly, struct ucred *cred, + struct thread *td); static int nfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp, struct sillyrename *sp); @@ -829,9 +833,11 @@ nfs_close(struct vop_close_args *ap) struct ucred *cred; int error = 0, ret, localcred = 0; int fmode = ap->a_fflag; + struct nfsmount *nmp; if (NFSCL_FORCEDISM(vp->v_mount)) return (0); + nmp = VFSTONFS(vp->v_mount); /* * During shutdown, a_cred isn't valid, so just use root. */ @@ -885,7 +891,9 @@ nfs_close(struct vop_close_args *ap) error = ncl_flush(vp, MNT_WAIT, ap->a_td, cm, 0); /* np->n_flag &= ~NMODIFIED; */ } else if (NFS_ISV4(vp)) { - if (nfscl_mustflush(vp) != 0) { + if (!NFSHASNFSV4N(nmp) || + (nmp->nm_flag & NFSMNT_NOCTO) == 0 || + nfscl_mustflush(vp) != 0) { int cm = newnfs_commit_on_close ? 1 : 0; if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) { NFSVOPLOCK(vp, LK_UPGRADE | LK_RETRY); @@ -927,7 +935,7 @@ nfs_close(struct vop_close_args *ap) * is the cause of some caching/coherency issue that might * crop up.) */ - if (VFSTONFS(vp->v_mount)->nm_negnametimeo == 0) { + if (nmp->nm_negnametimeo == 0) { np->n_attrstamp = 0; KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); } @@ -944,7 +952,7 @@ nfs_close(struct vop_close_args *ap) */ if (error == 0 && nfscl_nodeleg(vp, 0) != 0 && vp->v_type == VREG && - (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) == 0) { + (nmp->nm_flag & NFSMNT_NOCTO) == 0) { ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva); if (!ret) { np->n_change = nfsva.na_filerev; @@ -1025,8 +1033,9 @@ nfs_getattr(struct vop_getattr_args *ap) return (0); } } + error = nfsrpc_getattr(vp, ap->a_cred, td, &nfsva); - if (!error) + if (error == 0) error = nfscl_loadattrcache(&vp, &nfsva, vap, 0, 0); if (!error) { /* @@ -1999,6 +2008,7 @@ nfs_remove(struct vop_remove_args *ap) struct nfsnode *np = VTONFS(vp); int error = 0; struct vattr vattr; + struct nfsmount *nmp; KASSERT(vrefcnt(vp) > 0, ("nfs_remove: bad v_usecount")); if (vp->v_type == VDIR) @@ -2006,6 +2016,7 @@ nfs_remove(struct vop_remove_args *ap) else if (vrefcnt(vp) == 1 || (np->n_sillyrename && VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 && vattr.va_nlink > 1)) { + nmp = VFSTONFS(vp->v_mount); /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is @@ -2017,12 +2028,19 @@ nfs_remove(struct vop_remove_args *ap) /* * throw away biocache buffers, mainly to avoid * unnecessary delayed writes later. + * Flushing here would be more correct for the case + * where nfs_close() did not do a flush. However, it + * could be a large performance hit for some servers + * and only matters when the file name being removed is + * one of multiple hard links. */ - error = ncl_vinvalbuf(vp, 0, curthread, 1); + if (!NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) || + (nmp->nm_flag & NFSMNT_NOCTO) == 0) + error = ncl_vinvalbuf(vp, 0, curthread, 1); if (error != EINTR && error != EIO) /* Do the rpc */ error = nfs_removerpc(dvp, vp, cnp->cn_nameptr, - cnp->cn_namelen, cnp->cn_cred, curthread); + cnp->cn_namelen, cnp->cn_cred, curthread, false); /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT @@ -2053,7 +2071,32 @@ ncl_removeit(struct sillyrename *sp, struct vnode *vp) if (sp->s_dvp->v_type == VBAD) return (0); return (nfs_removerpc(sp->s_dvp, vp, sp->s_name, sp->s_namlen, - sp->s_cred, NULL)); + sp->s_cred, NULL, true)); +} + +/* + * Handle the nfsremove_status reply from the RPC function. + */ +static void +nfs_removestatus(struct vnode *vp, nfsremove_status file_status, + bool silly, struct thread *td) +{ + + switch (file_status) { + case NLINK_ZERO: + /* Get rid of any delegation. */ + nfscl_delegreturnvp(vp, false, td); + /* FALLTHROUGH */ + case DELETED: + /* Throw away buffer cache blocks. */ + (void)ncl_vinvalbuf(vp, 0, td, 1); + break; + case VALID: + /* Nothing to do, delegation is still ok. */ + break; + default: + break; + } } /* @@ -2061,17 +2104,20 @@ ncl_removeit(struct sillyrename *sp, struct vnode *vp) */ static int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name, - int namelen, struct ucred *cred, struct thread *td) + int namelen, struct ucred *cred, struct thread *td, bool silly) { - struct nfsvattr dnfsva; + struct nfsvattr dnfsva, nfsva; struct nfsnode *dnp = VTONFS(dvp); - int error = 0, dattrflag; + struct nfsmount *nmp; + int attrflag, error = 0, dattrflag; + nfsremove_status file_status; + nmp = VFSTONFS(dvp->v_mount); NFSLOCKNODE(dnp); dnp->n_flag |= NREMOVEINPROG; NFSUNLOCKNODE(dnp); - error = nfsrpc_remove(dvp, name, namelen, vp, cred, td, &dnfsva, - &dattrflag); + error = nfsrpc_remove(dvp, name, namelen, vp, &nfsva, &attrflag, + &file_status, &dnfsva, &dattrflag, cred, td); NFSLOCKNODE(dnp); if ((dnp->n_flag & NREMOVEWANT)) { dnp->n_flag &= ~(NREMOVEWANT | NREMOVEINPROG); @@ -2081,11 +2127,19 @@ nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name, dnp->n_flag &= ~NREMOVEINPROG; NFSUNLOCKNODE(dnp); } - if (dattrflag) + + if (NFSHASNFSV4(nmp) && NFSHASNFSV4N(nmp)) { + if (file_status != DELETED && attrflag != 0) + (void)nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1); + if ((nmp->nm_flag & NFSMNT_NOCTO) != 0) + nfs_removestatus(vp, file_status, silly, td); + } + + if (dattrflag != 0) (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, 0, 1); NFSLOCKNODE(dnp); dnp->n_flag |= NMODIFIED; - if (!dattrflag) { + if (dattrflag == 0) { dnp->n_attrstamp = 0; KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); } @@ -2110,6 +2164,7 @@ nfs_rename(struct vop_rename_args *ap) struct nfsnode *fnp = VTONFS(ap->a_fvp); struct nfsnode *tdnp = VTONFS(ap->a_tdvp); struct nfsv4node *newv4 = NULL; + struct nfsmount *nmp; int error; /* Check for cross-device rename */ @@ -2118,6 +2173,7 @@ nfs_rename(struct vop_rename_args *ap) error = EXDEV; goto out; } + nmp = VFSTONFS(fvp->v_mount); if (fvp == tvp) { printf("nfs_rename: fvp == tvp (can't happen)\n"); @@ -2140,11 +2196,15 @@ nfs_rename(struct vop_rename_args *ap) * that was written back to our cache earlier. Not checking for * this condition can result in potential (silent) data loss. */ - error = VOP_FSYNC(fvp, MNT_WAIT, curthread); + if ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || !NFSHASNFSV4(nmp) || + !NFSHASNFSV4N(nmp) || nfscl_mustflush(fvp) != 0) + error = VOP_FSYNC(fvp, MNT_WAIT, curthread); NFSVOPUNLOCK(fvp); - if (!error && tvp) + if (error == 0 && tvp != NULL && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || + !NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) || + nfscl_mustflush(tvp) != 0)) error = VOP_FSYNC(tvp, MNT_WAIT, curthread); - if (error) + if (error != 0) goto out; /* @@ -2159,7 +2219,7 @@ nfs_rename(struct vop_rename_args *ap) } error = nfs_renamerpc(fdvp, fvp, fcnp->cn_nameptr, fcnp->cn_namelen, - tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, + tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, false, tcnp->cn_cred, curthread); if (error == 0 && NFS_ISV4(tdvp)) { @@ -2228,7 +2288,7 @@ nfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp, { return (nfs_renamerpc(sdvp, svp, scnp->cn_nameptr, scnp->cn_namelen, - sdvp, NULL, sp->s_name, sp->s_namlen, scnp->cn_cred, + sdvp, NULL, sp->s_name, sp->s_namlen, true, scnp->cn_cred, curthread)); } @@ -2238,16 +2298,19 @@ nfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp, static int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr, - int tnamelen, struct ucred *cred, struct thread *td) + int tnamelen, bool silly, struct ucred *cred, struct thread *td) { - struct nfsvattr fnfsva, tnfsva; + struct nfsvattr fnfsva, tnfsva, tvpnfsva; struct nfsnode *fdnp = VTONFS(fdvp); struct nfsnode *tdnp = VTONFS(tdvp); - int error = 0, fattrflag, tattrflag; + struct nfsmount *nmp; + int error = 0, fattrflag, tattrflag, tvpattrflag; + nfsremove_status tvp_status; + nmp = VFSTONFS(fdvp->v_mount); error = nfsrpc_rename(fdvp, fvp, fnameptr, fnamelen, tdvp, tvp, - tnameptr, tnamelen, cred, td, &fnfsva, &tnfsva, &fattrflag, - &tattrflag); + tnameptr, tnamelen, &tvp_status, &fnfsva, &tnfsva, &fattrflag, + &tattrflag, &tvpnfsva, &tvpattrflag, cred, td); NFSLOCKNODE(fdnp); fdnp->n_flag |= NMODIFIED; if (fattrflag != 0) { @@ -2268,6 +2331,15 @@ nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr, NFSUNLOCKNODE(tdnp); KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp); } + + if (tvp != NULL) { + if (NFSHASNFSV4(nmp) && NFSHASNFSV4N(nmp) && + (nmp->nm_flag & NFSMNT_NOCTO) != 0) + nfs_removestatus(tvp, tvp_status, silly, td); + if (!silly && tvpattrflag != 0) + (void)nfscl_loadattrcache(&tvp, &tvpnfsva, NULL, 0, 1); + } + if (error && NFS_ISV4(fdvp)) error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); return (error); @@ -2291,7 +2363,9 @@ nfs_link(struct vop_link_args *ap) * doesn't get "out of sync" with the server. * XXX There should be a better way! */ +#ifdef notnow VOP_FSYNC(vp, MNT_WAIT, curthread); +#endif error = nfsrpc_link(tdvp, vp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, curthread, &dnfsva, &nfsva, &attrflag, &dattrflag);