|
@@ -1439,8 +1439,10 @@ free_session_slots(struct nfsd4_session *ses)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < ses->se_fchannel.maxreqs; i++)
|
|
|
+ for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
|
|
|
+ free_svc_cred(&ses->se_slots[i]->sl_cred);
|
|
|
kfree(ses->se_slots[i]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2295,6 +2297,8 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
|
|
|
slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
|
|
|
slot->sl_opcnt = resp->opcnt;
|
|
|
slot->sl_status = resp->cstate.status;
|
|
|
+ free_svc_cred(&slot->sl_cred);
|
|
|
+ copy_cred(&slot->sl_cred, &resp->rqstp->rq_cred);
|
|
|
|
|
|
if (!nfsd4_cache_this(resp)) {
|
|
|
slot->sl_flags &= ~NFSD4_SLOT_CACHED;
|
|
@@ -3001,6 +3005,34 @@ static bool nfsd4_request_too_big(struct svc_rqst *rqstp,
|
|
|
return xb->len > session->se_fchannel.maxreq_sz;
|
|
|
}
|
|
|
|
|
|
+static bool replay_matches_cache(struct svc_rqst *rqstp,
|
|
|
+ struct nfsd4_sequence *seq, struct nfsd4_slot *slot)
|
|
|
+{
|
|
|
+ struct nfsd4_compoundargs *argp = rqstp->rq_argp;
|
|
|
+
|
|
|
+ if ((bool)(slot->sl_flags & NFSD4_SLOT_CACHETHIS) !=
|
|
|
+ (bool)seq->cachethis)
|
|
|
+ return false;
|
|
|
+ /*
|
|
|
+ * If there's an error than the reply can have fewer ops than
|
|
|
+ * the call. But if we cached a reply with *more* ops than the
|
|
|
+ * call you're sending us now, then this new call is clearly not
|
|
|
+ * really a replay of the old one:
|
|
|
+ */
|
|
|
+ if (slot->sl_opcnt < argp->opcnt)
|
|
|
+ return false;
|
|
|
+ /* This is the only check explicitly called by spec: */
|
|
|
+ if (!same_creds(&rqstp->rq_cred, &slot->sl_cred))
|
|
|
+ return false;
|
|
|
+ /*
|
|
|
+ * There may be more comparisons we could actually do, but the
|
|
|
+ * spec doesn't require us to catch every case where the calls
|
|
|
+ * don't match (that would require caching the call as well as
|
|
|
+ * the reply), so we don't bother.
|
|
|
+ */
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
__be32
|
|
|
nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
union nfsd4_op_u *u)
|
|
@@ -3060,6 +3092,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|
|
status = nfserr_seq_misordered;
|
|
|
if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
|
|
|
goto out_put_session;
|
|
|
+ status = nfserr_seq_false_retry;
|
|
|
+ if (!replay_matches_cache(rqstp, seq, slot))
|
|
|
+ goto out_put_session;
|
|
|
cstate->slot = slot;
|
|
|
cstate->session = session;
|
|
|
cstate->clp = clp;
|