diff options
-rw-r--r-- | sys/fs/nfs/nfsrvstate.h | 1 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdstate.c | 63 |
2 files changed, 43 insertions, 21 deletions
diff --git a/sys/fs/nfs/nfsrvstate.h b/sys/fs/nfs/nfsrvstate.h index 9eebeece9727..4c171e8b3f50 100644 --- a/sys/fs/nfs/nfsrvstate.h +++ b/sys/fs/nfs/nfsrvstate.h @@ -99,6 +99,7 @@ struct nfsclient { struct nfsstatehead lc_deleg; /* Delegations */ struct nfsstatehead lc_olddeleg; /* and old delegations */ struct nfssessionhead lc_session; /* List of NFSv4.1 sessions */ + uint64_t lc_prevsess; /* CreateSession cache */ time_t lc_expiry; /* Expiry time (sec) */ time_t lc_delegtime; /* Old deleg expiry (sec) */ nfsquad_t lc_clientid; /* 64 bit clientid */ diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c index 842d3c75f678..6b2a6e56795d 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -336,10 +336,10 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, * Add it after assigning a client id to it. */ new_clp->lc_flags |= LCL_NEEDSCONFIRM; - if ((nd->nd_flag & ND_NFSV41) != 0) - new_clp->lc_confirm.lval[0] = confirmp->lval[0] = - ++confirm_index; - else + if ((nd->nd_flag & ND_NFSV41) != 0) { + confirmp->lval[0] = ++confirm_index; + new_clp->lc_confirm.lval[0] = confirmp->lval[0] - 1; + } else confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = @@ -348,6 +348,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, nfsrv_nextclientindex(); new_clp->lc_stateindex = 0; new_clp->lc_statemaxindex = 0; + new_clp->lc_prevsess = 0; new_clp->lc_cbref = 0; new_clp->lc_expiry = nfsrv_leaseexpiry(); LIST_INIT(&new_clp->lc_open); @@ -449,10 +450,10 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, } new_clp->lc_flags |= LCL_NEEDSCONFIRM; - if ((nd->nd_flag & ND_NFSV41) != 0) - new_clp->lc_confirm.lval[0] = confirmp->lval[0] = - ++confirm_index; - else + if ((nd->nd_flag & ND_NFSV41) != 0) { + confirmp->lval[0] = ++confirm_index; + new_clp->lc_confirm.lval[0] = confirmp->lval[0] - 1; + } else confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = @@ -461,6 +462,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, nfsrv_nextclientindex(); new_clp->lc_stateindex = 0; new_clp->lc_statemaxindex = 0; + new_clp->lc_prevsess = 0; new_clp->lc_cbref = 0; new_clp->lc_expiry = nfsrv_leaseexpiry(); @@ -596,6 +598,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, struct nfssessionhash *shp; struct nfsdsession *sep; uint64_t sessid[2]; + bool sess_replay; static uint64_t next_sess = 0; if (clpp) @@ -676,13 +679,29 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, * Perform any operations specified by the opflags. */ if (opflags & CLOPS_CONFIRM) { - if ((nd->nd_flag & ND_NFSV41) != 0 && - clp->lc_confirm.lval[0] != confirm.lval[0]) + sess_replay = false; + if ((nd->nd_flag & ND_NFSV41) != 0) { + /* + * For the case where lc_confirm.lval[0] == confirm.lval[0], + * use the new session, but with the previous sessionid. + * This is not exactly what the RFC describes, but should + * result in the same reply as the previous CreateSession. + */ + if (clp->lc_confirm.lval[0] + 1 == confirm.lval[0]) { + clp->lc_confirm.lval[0] = confirm.lval[0]; + clp->lc_prevsess = sessid[0]; + } else if (clp->lc_confirm.lval[0] == confirm.lval[0]) { + if (clp->lc_prevsess == 0) + error = NFSERR_SEQMISORDERED; + else + sessid[0] = clp->lc_prevsess; + sess_replay = true; + } else error = NFSERR_SEQMISORDERED; - else if ((nd->nd_flag & ND_NFSV41) == 0 && + } else if ((nd->nd_flag & ND_NFSV41) == 0 && clp->lc_confirm.qval != confirm.qval) error = NFSERR_STALECLIENTID; - else if (nfsrv_notsamecredname(nd, clp)) + if (error == 0 && nfsrv_notsamecredname(nd, clp)) error = NFSERR_CLIDINUSE; if (!error) { @@ -716,7 +735,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, if (nsep != NULL) { /* Hold a reference on the xprt for a backchannel. */ if ((nsep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) - != 0) { + != 0 && !sess_replay) { if (clp->lc_req.nr_client == NULL) clp->lc_req.nr_client = (struct __rpc_client *) clnt_bck_create(nd->nd_xprt->xp_socket, @@ -735,14 +754,16 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, NFSX_V4SESSIONID); NFSBCOPY(sessid, nsep->sess_cbsess.nfsess_sessionid, NFSX_V4SESSIONID); - shp = NFSSESSIONHASH(nsep->sess_sessionid); - NFSLOCKSTATE(); - NFSLOCKSESSION(shp); - LIST_INSERT_HEAD(&shp->list, nsep, sess_hash); - LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list); - nsep->sess_clp = clp; - NFSUNLOCKSESSION(shp); - NFSUNLOCKSTATE(); + if (!sess_replay) { + shp = NFSSESSIONHASH(nsep->sess_sessionid); + NFSLOCKSTATE(); + NFSLOCKSESSION(shp); + LIST_INSERT_HEAD(&shp->list, nsep, sess_hash); + LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list); + nsep->sess_clp = clp; + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + } } } } else if (clp->lc_flags & LCL_NEEDSCONFIRM) { |