diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -753,6 +753,8 @@ struct ucred *, struct thread *); int nfsvno_listxattr(struct vnode *, uint64_t, struct ucred *, struct thread *, u_char **, uint32_t *, bool *); +void nfsm_trimtrailing(struct nfsrv_descript *, struct mbuf *, char *, int, + int); /* nfs_commonkrpc.c */ int newnfs_nmcancelreqs(struct nfsmount *); diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -146,8 +146,6 @@ static int nfsrv_dssetacl(struct vnode *, struct acl *, struct ucred *, NFSPROC_T *); static int nfsrv_pnfsstatfs(struct statfs *, struct mount *); -static void nfsm_trimtrailing(struct nfsrv_descript *, struct mbuf *, - char *, int, int); int nfs_pnfsio(task_fn_t *, void *); @@ -6564,7 +6562,7 @@ /* * Trim trailing data off the mbuf list being built. */ -static void +void nfsm_trimtrailing(struct nfsrv_descript *nd, struct mbuf *mb, char *bpos, int bextpg, int bextpgsiz) { diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c --- a/sys/fs/nfsserver/nfs_nfsdsocket.c +++ b/sys/fs/nfsserver/nfs_nfsdsocket.c @@ -534,9 +534,21 @@ { int error = 0, lktype; vnode_t vp; - mount_t mp = NULL; + mount_t mp; struct nfsrvfh fh; struct nfsexstuff nes; + struct mbuf *md; + char *dpos; + + /* + * Save the current position in the request mbuf list so + * that a rollback to this location can be done upon an + * ERELOOKUP error return from an RPC function. + */ + md = nd->nd_md; + dpos = nd->nd_dpos; +tryagain: + mp = NULL; /* * Get a locked vnode for the first file handle @@ -634,6 +646,21 @@ if (mp != NULL && nfsrv_writerpc[nd->nd_procnum] != 0) vn_finished_write(mp); + if (error == 0 && nd->nd_repstat == ERELOOKUP) { + /* + * Roll back to the beginning of the RPC request + * arguments. + */ + nd->nd_md = md; + nd->nd_dpos = dpos; + + /* Free the junk RPC reply and redo the RPC. */ + m_freem(nd->nd_mreq); + nd->nd_mreq = nd->nd_mb = NULL; + nd->nd_repstat = 0; + goto tryagain; + } + nfsrvd_statend(nfsv3to4op[nd->nd_procnum], /*bytes*/ 0, /*now*/ NULL, /*then*/ &start_time); } @@ -691,6 +718,9 @@ static u_int64_t compref = 0; struct bintime start_time; struct thread *p; + struct mbuf *mb, *md; + char *bpos, *dpos; + int bextpg, bextpgsiz; p = curthread; @@ -1045,6 +1075,20 @@ break; } } + + /* + * Save the current positions in the mbuf lists so + * that a rollback to this location can be done upon a + * redo due to a ERELOOKUP return for a operation. + */ + mb = nd->nd_mb; + bpos = nd->nd_bpos; + bextpg = nd->nd_bextpg; + bextpgsiz = nd->nd_bextpgsiz; + md = nd->nd_md; + dpos = nd->nd_dpos; +tryagain: + if (nfsv4_opflag[op].retfh == 1) { if (!vp) { nd->nd_repstat = NFSERR_NOFILEHANDLE; @@ -1154,6 +1198,23 @@ error = 0; } + if (nd->nd_repstat == ERELOOKUP) { + /* + * Roll back to the beginning of the operation + * arguments. + */ + nd->nd_md = md; + nd->nd_dpos = dpos; + + /* + * Trim off the bogus reply for this operation + * and redo the operation. + */ + nfsm_trimtrailing(nd, mb, bpos, bextpg, bextpgsiz); + nd->nd_repstat = 0; + goto tryagain; + } + if (statsinprog != 0) { nfsrvd_statend(op, /*bytes*/ 0, /*now*/ NULL, /*then*/ &start_time);