int nfs4_op_destroy_session(struct nfs_argop4 *op, compound_data_t *data,
			    struct nfs_resop4 *resp)
{

	DESTROY_SESSION4args * const arg_DESTROY_SESSION4 =
	    &op->nfs_argop4_u.opdestroy_session;
	DESTROY_SESSION4res * const res_DESTROY_SESSION4 =
	    &resp->nfs_resop4_u.opdestroy_session;

	sockaddr_t nw_addr;
	sockaddr_t client_addr;
	nfs41_session_t *session;

	resp->resop = NFS4_OP_DESTROY_SESSION;
	res_DESTROY_SESSION4->dsr_status = NFS4_OK;

	if (data->minorversion == 0) {
		res_DESTROY_SESSION4->dsr_status = NFS4ERR_INVAL;
		return res_DESTROY_SESSION4->dsr_status = NFS4ERR_INVAL;
	}

	if (!nfs41_Session_Get_Pointer(arg_DESTROY_SESSION4->dsa_sessionid,
				       &session)) {
		res_DESTROY_SESSION4->dsr_status = NFS4ERR_BADSESSION;
		return res_DESTROY_SESSION4->dsr_status;
	}

	/* DESTROY_SESSION MUST be invoked on a connection that is associated
	 * with the session being destroyed
	 */

	/* Copy the address coming over the wire. */
	copy_xprt_addr(&nw_addr, data->req->rq_xprt);

	/* Copy the address recorded in the session. */
	copy_xprt_addr(&client_addr, session->xprt);

	/* Compare these fields. */
	if (!cmp_sockaddr(&nw_addr, &client_addr, false)) {
		res_DESTROY_SESSION4->dsr_status =
		    NFS4ERR_CONN_NOT_BOUND_TO_SESSION;
		return res_DESTROY_SESSION4->dsr_status;
	}

	if (!nfs41_Session_Del(arg_DESTROY_SESSION4->dsa_sessionid))
		res_DESTROY_SESSION4->dsr_status = NFS4ERR_BADSESSION;
	else
		res_DESTROY_SESSION4->dsr_status = NFS4_OK;

	/* Release ref taken in get_pointer */

	dec_session_ref(session);

	return res_DESTROY_SESSION4->dsr_status;
}				/* nfs41_op_destroy_session */
int nfs41_Session_Del(char sessionid[NFS4_SESSIONID_SIZE])
{
    struct gsh_buffdesc key, old_key, old_value;

    key.addr = sessionid;
    key.len = NFS4_SESSIONID_SIZE;

    if (HashTable_Del(ht_session_id, &key, &old_key, &old_value) ==
            HASHTABLE_SUCCESS) {
        nfs41_session_t *session = old_value.addr;

        /* unref session */
        dec_session_ref(session);

        return true;
    } else {
        return false;
    }
}
int nfs4_op_create_session(struct nfs_argop4 *op, compound_data_t *data,
                           struct nfs_resop4 *resp)
{
    /* Result of looking up the clientid in the confirmed ID
       table */
    nfs_client_id_t *conf = NULL;	/* XXX these are not good names */
    /* Result of looking up the clientid in the unconfirmed ID
       table */
    nfs_client_id_t *unconf = NULL;
    /* The found clientid (either one of the preceding) */
    nfs_client_id_t *found = NULL;
    /* The found client record */
    nfs_client_record_t *client_record;
    /* The created session */
    nfs41_session_t *nfs41_session = NULL;
    /* Client supplied clientid */
    clientid4 clientid = 0;
    /* The client address as a string, for gratuitous logging */
    const char *str_client_addr = "(unknown)";
    /* The client name, for gratuitous logging */
    char str_client[CLIENTNAME_BUFSIZE];
    /* Display buffer for client name */
    struct display_buffer dspbuf_client = {
        sizeof(str_client), str_client, str_client
    };
    /* The clientid4 broken down into fields */
    char str_clientid4[DISPLAY_CLIENTID_SIZE];
    /* Display buffer for clientid4 */
    struct display_buffer dspbuf_clientid4 = {
        sizeof(str_clientid4), str_clientid4, str_clientid4
    };
    /* Return code from clientid calls */
    int i, rc = 0;
    /* Component for logging */
    log_components_t component = COMPONENT_CLIENTID;
    /* Abbreviated alias for arguments */
    CREATE_SESSION4args * const arg_CREATE_SESSION4 =
        &op->nfs_argop4_u.opcreate_session;
    /* Abbreviated alias for response */
    CREATE_SESSION4res * const res_CREATE_SESSION4 =
        &resp->nfs_resop4_u.opcreate_session;
    /* Abbreviated alias for successful response */
    CREATE_SESSION4resok * const res_CREATE_SESSION4ok =
        &res_CREATE_SESSION4->CREATE_SESSION4res_u.csr_resok4;

    /* Make sure str_client is always printable even
     * if log level changes midstream.
     */
    display_printf(&dspbuf_client, "(unknown)");
    display_reset_buffer(&dspbuf_client);

    if (op_ctx->client != NULL)
        str_client_addr = op_ctx->client->hostaddr_str;

    if (isDebug(COMPONENT_SESSIONS))
        component = COMPONENT_SESSIONS;

    resp->resop = NFS4_OP_CREATE_SESSION;
    res_CREATE_SESSION4->csr_status = NFS4_OK;
    clientid = arg_CREATE_SESSION4->csa_clientid;

    display_clientid(&dspbuf_clientid4, clientid);

    if (data->minorversion == 0)
        return res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL;

    LogDebug(component,
             "CREATE_SESSION client addr=%s clientid=%s -------------------",
             str_client_addr, str_clientid4);

    /* First try to look up unconfirmed record */
    rc = nfs_client_id_get_unconfirmed(clientid, &unconf);

    if (rc == CLIENT_ID_SUCCESS) {
        client_record = unconf->cid_client_record;
        found = unconf;
    } else {
        rc = nfs_client_id_get_confirmed(clientid, &conf);
        if (rc != CLIENT_ID_SUCCESS) {
            /* No record whatsoever of this clientid */
            LogDebug(component,
                     "%s clientid=%s",
                     clientid_error_to_str(rc), str_clientid4);

            if (rc == CLIENT_ID_EXPIRED)
                rc = CLIENT_ID_STALE;

            res_CREATE_SESSION4->csr_status =
                clientid_error_to_nfsstat_no_expire(rc);

            return res_CREATE_SESSION4->csr_status;
        }

        client_record = conf->cid_client_record;
        found = conf;
    }

    PTHREAD_MUTEX_lock(&client_record->cr_mutex);

    inc_client_record_ref(client_record);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_record(&dspbuf, client_record);

        LogFullDebug(component,
                     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
                     str,
                     client_record->cr_confirmed_rec,
                     client_record->cr_unconfirmed_rec);
    }

    /* At this point one and only one of conf and unconf is
     * non-NULL, and found also references the single clientid
     * record that was found.
     */

    LogDebug(component,
             "CREATE_SESSION clientid=%s csa_sequence=%" PRIu32
             " clientid_cs_seq=%" PRIu32 " data_oppos=%d data_use_drc=%d",
             str_clientid4, arg_CREATE_SESSION4->csa_sequence,
             found->cid_create_session_sequence, data->oppos,
             data->use_drc);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_id_rec(&dspbuf, found);
        LogFullDebug(component, "Found %s", str);
    }

    data->use_drc = false;

    if (data->oppos == 0) {
        /* Special case : the request is used without use of
         * OP_SEQUENCE
         */
        if ((arg_CREATE_SESSION4->csa_sequence + 1 ==
                found->cid_create_session_sequence)
                && (found->cid_create_session_slot.cache_used)) {
            data->use_drc = true;
            data->cached_res =
                &found->cid_create_session_slot.cached_result;

            res_CREATE_SESSION4->csr_status = NFS4_OK;

            dec_client_id_ref(found);

            LogDebug(component,
                     "CREATE_SESSION replay=%p special case",
                     data->cached_res);

            goto out;
        } else if (arg_CREATE_SESSION4->csa_sequence !=
                   found->cid_create_session_sequence) {
            res_CREATE_SESSION4->csr_status =
                NFS4ERR_SEQ_MISORDERED;

            dec_client_id_ref(found);

            LogDebug(component,
                     "CREATE_SESSION returning NFS4ERR_SEQ_MISORDERED");

            goto out;
        }

    }

    if (unconf != NULL) {
        /* First must match principal */
        if (!nfs_compare_clientcred(&unconf->cid_credential,
                                    &data->credential)) {
            if (isDebug(component)) {
                char *unconfirmed_addr = "(unknown)";

                if (unconf->gsh_client != NULL)
                    unconfirmed_addr =
                        unconf->gsh_client->hostaddr_str;

                LogDebug(component,
                         "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE",
                         str_clientid4,
                         str_client_addr,
                         unconfirmed_addr);
            }

            dec_client_id_ref(unconf);
            res_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE;
            goto out;
        }
    }

    if (conf != NULL) {
        if (isDebug(component) && conf != NULL)
            display_clientid_name(&dspbuf_client, conf);

        /* First must match principal */
        if (!nfs_compare_clientcred(&conf->cid_credential,
                                    &data->credential)) {
            if (isDebug(component)) {
                char *confirmed_addr = "(unknown)";

                if (conf->gsh_client != NULL)
                    confirmed_addr =
                        conf->gsh_client->hostaddr_str;

                LogDebug(component,
                         "Confirmed ClientId %s->%s addr=%s: Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE",
                         str_clientid4, str_client,
                         str_client_addr, confirmed_addr);
            }

            /* Release our reference to the confirmed clientid. */
            dec_client_id_ref(conf);
            res_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE;
            goto out;
        }

        /* In this case, the record was confirmed proceed with
           CREATE_SESSION */
    }

    /* We don't need to do any further principal checks, we can't
     * have a confirmed clientid record with a different principal
     * than the unconfirmed record.
     */

    /* At this point, we need to try and create the session before
     * we modify the confirmed and/or unconfirmed clientid
     * records.
     */

    /* Check flags value (test CSESS15) */
    if (arg_CREATE_SESSION4->csa_flags &
            ~(CREATE_SESSION4_FLAG_PERSIST |
              CREATE_SESSION4_FLAG_CONN_BACK_CHAN |
              CREATE_SESSION4_FLAG_CONN_RDMA)) {
        LogDebug(component,
                 "Invalid create session flags %" PRIu32,
                 arg_CREATE_SESSION4->csa_flags);
        dec_client_id_ref(found);
        res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL;
        goto out;
    }

    /* Record session related information at the right place */
    nfs41_session = pool_alloc(nfs41_session_pool);

    if (nfs41_session == NULL) {
        LogCrit(component, "Could not allocate memory for a session");
        dec_client_id_ref(found);
        res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT;
        goto out;
    }

    nfs41_session->clientid = clientid;
    nfs41_session->clientid_record = found;
    nfs41_session->refcount = 2;	/* sentinel ref + call path ref */
    nfs41_session->fore_channel_attrs =
        arg_CREATE_SESSION4->csa_fore_chan_attrs;
    nfs41_session->back_channel_attrs =
        arg_CREATE_SESSION4->csa_back_chan_attrs;
    nfs41_session->xprt = data->req->rq_xprt;
    nfs41_session->flags = false;
    nfs41_session->cb_program = 0;
    PTHREAD_MUTEX_init(&nfs41_session->cb_mutex, NULL);
    PTHREAD_COND_init(&nfs41_session->cb_cond, NULL);
    for (i = 0; i < NFS41_NB_SLOTS; i++)
        PTHREAD_MUTEX_init(&nfs41_session->slots[i].lock, NULL);

    /* Take reference to clientid record on behalf the session. */
    inc_client_id_ref(found);

    /* add to head of session list (encapsulate?) */
    PTHREAD_MUTEX_lock(&found->cid_mutex);
    glist_add(&found->cid_cb.v41.cb_session_list,
              &nfs41_session->session_link);
    PTHREAD_MUTEX_unlock(&found->cid_mutex);

    /* Set ca_maxrequests */
    nfs41_session->fore_channel_attrs.ca_maxrequests = NFS41_NB_SLOTS;
    nfs41_Build_sessionid(&clientid, nfs41_session->session_id);

    res_CREATE_SESSION4ok->csr_sequence = arg_CREATE_SESSION4->csa_sequence;

    /* return the input for wanting of something better (will
     * change in later versions)
     */
    res_CREATE_SESSION4ok->csr_fore_chan_attrs =
        nfs41_session->fore_channel_attrs;
    res_CREATE_SESSION4ok->csr_back_chan_attrs =
        nfs41_session->back_channel_attrs;
    res_CREATE_SESSION4ok->csr_flags = 0;

    memcpy(res_CREATE_SESSION4ok->csr_sessionid,
           nfs41_session->session_id,
           NFS4_SESSIONID_SIZE);

    /* Create Session replay cache */
    data->cached_res = &found->cid_create_session_slot.cached_result;
    found->cid_create_session_slot.cache_used = true;

    LogDebug(component, "CREATE_SESSION replay=%p", data->cached_res);

    if (!nfs41_Session_Set(nfs41_session)) {
        LogDebug(component, "Could not insert session into table");

        /* Release the session resources by dropping our reference
         * and the sentinel reference.
         */
        dec_session_ref(nfs41_session);
        dec_session_ref(nfs41_session);

        /* Decrement our reference to the clientid record */
        dec_client_id_ref(found);

        /* Maybe a more precise status would be better */
        res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT;
        goto out;
    }

    /* Make sure we have a reference to the confirmed clientid
       record if any */
    if (conf == NULL) {
        conf = client_record->cr_confirmed_rec;

        if (isDebug(component) && conf != NULL)
            display_clientid_name(&dspbuf_client, conf);

        /* Need a reference to the confirmed record for below */
        if (conf != NULL)
            inc_client_id_ref(conf);
    }

    if (conf != NULL && conf->cid_clientid != clientid) {
        /* Old confirmed record - need to expire it */
        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Expiring %s", str);
        }

        /* Expire clientid and release our reference. */
        nfs_client_id_expire(conf, false);
        dec_client_id_ref(conf);
        conf = NULL;
    }

    if (conf != NULL) {
        /* At this point we are updating the confirmed
         * clientid.  Update the confirmed record from the
         * unconfirmed record.
         */
        display_clientid(&dspbuf_clientid4, conf->cid_clientid);
        LogDebug(component,
                 "Updating clientid %s->%s cb_program=%u",
                 str_clientid4, str_client,
                 arg_CREATE_SESSION4->csa_cb_program);

        if (unconf != NULL) {
            /* unhash the unconfirmed clientid record */
            remove_unconfirmed_client_id(unconf);
            /* Release our reference to the unconfirmed entry */
            dec_client_id_ref(unconf);
        }

        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Updated %s", str);
        }
    } else {
        /* This is a new clientid */
        if (isFullDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, unconf);
            LogFullDebug(component, "Confirming new %s", str);
        }

        rc = nfs_client_id_confirm(unconf, component);

        if (rc != CLIENT_ID_SUCCESS) {
            res_CREATE_SESSION4->csr_status =
                clientid_error_to_nfsstat_no_expire(rc);

            /* Need to destroy the session */
            if (!nfs41_Session_Del(nfs41_session->session_id))
                LogDebug(component,
                         "Oops nfs41_Session_Del failed");

            /* Release our reference to the unconfirmed record */
            dec_client_id_ref(unconf);
            goto out;
        }
        nfs4_chk_clid(unconf);

        conf = unconf;
        unconf = NULL;

        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Confirmed %s", str);
        }
    }
    conf->cid_create_session_sequence++;

    /* Bump the lease timer */
    conf->cid_last_renew = time(NULL);

    /* Release our reference to the confirmed record */
    dec_client_id_ref(conf);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_record(&dspbuf, client_record);
        LogFullDebug(component,
                     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
                     str,
                     client_record->cr_confirmed_rec,
                     client_record->cr_unconfirmed_rec);
    }

    /* Handle the creation of the back channel, if the client
       requested one. */
    if (arg_CREATE_SESSION4->csa_flags &
            CREATE_SESSION4_FLAG_CONN_BACK_CHAN) {
        nfs41_session->cb_program = arg_CREATE_SESSION4->csa_cb_program;
        if (nfs_rpc_create_chan_v41(
                    nfs41_session,
                    arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_len,
                    arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_val)
                == 0) {
            res_CREATE_SESSION4ok->csr_flags |=
                CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
        }
    }

    if (isDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_session(&dspbuf, nfs41_session);

        LogDebug(component,
                 "success %s csa_flags 0x%X csr_flags 0x%X",
                 str, arg_CREATE_SESSION4->csa_flags,
                 res_CREATE_SESSION4ok->csr_flags);
    }

    /* Release our reference to the session */
    dec_session_ref(nfs41_session);

    /* Successful exit */
    res_CREATE_SESSION4->csr_status = NFS4_OK;

out:
    PTHREAD_MUTEX_unlock(&client_record->cr_mutex);
    /* Release our reference to the client record and return */
    dec_client_record_ref(client_record);
    return res_CREATE_SESSION4->csr_status;
}
示例#4
0
/**
 * @brief the NFS4_OP_SEQUENCE operation
 *
 * @param[in]     op   nfs4_op arguments
 * @param[in,out] data Compound request's data
 * @param[out]    resp nfs4_op results
 *
 * @return per RFC5661, p. 374
 *
 * @see nfs4_Compound
 *
 */
int nfs4_op_sequence(struct nfs_argop4 *op, compound_data_t *data,
		     struct nfs_resop4 *resp)
{
	SEQUENCE4args * const arg_SEQUENCE4 = &op->nfs_argop4_u.opsequence;
	SEQUENCE4res * const res_SEQUENCE4 = &resp->nfs_resop4_u.opsequence;

	nfs41_session_t *session;

	resp->resop = NFS4_OP_SEQUENCE;
	res_SEQUENCE4->sr_status = NFS4_OK;

	if (data->minorversion == 0) {
		res_SEQUENCE4->sr_status = NFS4ERR_INVAL;
		return res_SEQUENCE4->sr_status;
	}

	/* OP_SEQUENCE is always the first operation of the request */
	if (data->oppos != 0) {
		res_SEQUENCE4->sr_status = NFS4ERR_SEQUENCE_POS;
		return res_SEQUENCE4->sr_status;
	}

	if (!nfs41_Session_Get_Pointer(arg_SEQUENCE4->sa_sessionid, &session)) {
		if (nfs_in_grace()) {
			memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.
			       sr_sessionid, arg_SEQUENCE4->sa_sessionid,
			       NFS4_SESSIONID_SIZE);
			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid =
			    arg_SEQUENCE4->sa_sequenceid;
			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid =
			    arg_SEQUENCE4->sa_slotid;
			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.
			    sr_highest_slotid = NFS41_NB_SLOTS - 1;
			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.
			    sr_target_highest_slotid = arg_SEQUENCE4->sa_slotid;
			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.
			    sr_status_flags =
			    SEQ4_STATUS_RESTART_RECLAIM_NEEDED;
			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
				    "SEQUENCE returning status %s flags 0x%X",
				    nfsstat4_to_str(res_SEQUENCE4->sr_status),
				    res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.
				    sr_status_flags);
		} else {
			res_SEQUENCE4->sr_status = NFS4ERR_BADSESSION;
			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
				    "SEQUENCE returning status %s",
				    nfsstat4_to_str(res_SEQUENCE4->sr_status));
		}

		return res_SEQUENCE4->sr_status;
	}

	/* session->refcount +1 */

	LogDebug(COMPONENT_SESSIONS, "SEQUENCE session=%p", session);

	/* Check if lease is expired and reserve it */
	pthread_mutex_lock(&session->clientid_record->cid_mutex);

	if (!reserve_lease(session->clientid_record)) {
		pthread_mutex_unlock(&session->clientid_record->cid_mutex);

		dec_session_ref(session);
		res_SEQUENCE4->sr_status = NFS4ERR_EXPIRED;
		LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
			    "SEQUENCE returning status %s",
			    nfsstat4_to_str(res_SEQUENCE4->sr_status));
		return res_SEQUENCE4->sr_status;
	}

	data->preserved_clientid = session->clientid_record;

	pthread_mutex_unlock(&session->clientid_record->cid_mutex);

	/* Check is slot is compliant with ca_maxrequests */
	if (arg_SEQUENCE4->sa_slotid >=
	    session->fore_channel_attrs.ca_maxrequests) {
		dec_session_ref(session);
		res_SEQUENCE4->sr_status = NFS4ERR_BADSLOT;
		LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
			    "SEQUENCE returning status %s",
			    nfsstat4_to_str(res_SEQUENCE4->sr_status));
		return res_SEQUENCE4->sr_status;
	}

	/* By default, no DRC replay */
	data->use_drc = false;

	pthread_mutex_lock(&session->slots[arg_SEQUENCE4->sa_slotid].lock);
	if (session->slots[arg_SEQUENCE4->sa_slotid].sequence + 1 !=
	    arg_SEQUENCE4->sa_sequenceid) {
		if (session->slots[arg_SEQUENCE4->sa_slotid].sequence ==
		    arg_SEQUENCE4->sa_sequenceid) {
#if IMPLEMENT_CACHETHIS
			/** @todo
			 *
			 * Ganesha always caches result anyway so ignore
			 * cachethis
			 */
			if (session->slots[arg_SEQUENCE4->sa_slotid]
			    .cache_used) {
#endif
				/* Replay operation through the DRC */
				data->use_drc = true;
				data->cached_res =
				    &session->slots[arg_SEQUENCE4->sa_slotid].
				    cached_result;

				LogFullDebugAlt(COMPONENT_SESSIONS,
						COMPONENT_CLIENTID,
						"Use sesson slot %" PRIu32
						"=%p for DRC",
						arg_SEQUENCE4->sa_slotid,
						data->cached_res);

				pthread_mutex_unlock(&session->
					slots[arg_SEQUENCE4->sa_slotid].lock);
				dec_session_ref(session);
				res_SEQUENCE4->sr_status = NFS4_OK;
				return res_SEQUENCE4->sr_status;
#if IMPLEMENT_CACHETHIS
			} else {
				/* Illegal replay */
				pthread_mutex_unlock(&session->
					slots[arg_SEQUENCE4->sa_slotid].lock);
				dec_session_ref(session);
				res_SEQUENCE4->sr_status =
				    NFS4ERR_RETRY_UNCACHED_REP;
				LogDebugAlt(COMPONENT_SESSIONS,
					    COMPONENT_CLIENTID,
					    "SEQUENCE returning status %s",
					    nfsstat4_to_str(res_SEQUENCE4->
							    sr_status));
				return res_SEQUENCE4->sr_status;
			}
#endif
		}

		pthread_mutex_unlock(&session->
			slots[arg_SEQUENCE4->sa_slotid].lock);
		dec_session_ref(session);
		res_SEQUENCE4->sr_status = NFS4ERR_SEQ_MISORDERED;
		LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
			    "SEQUENCE returning status %s",
			    nfsstat4_to_str(res_SEQUENCE4->sr_status));
		return res_SEQUENCE4->sr_status;
	}

	/* Keep memory of the session in the COMPOUND's data */
	data->session = session;

	/* Record the sequenceid and slotid in the COMPOUND's data */
	data->sequence = arg_SEQUENCE4->sa_sequenceid;
	data->slot = arg_SEQUENCE4->sa_slotid;

	/* Update the sequence id within the slot */
	session->slots[arg_SEQUENCE4->sa_slotid].sequence += 1;

	memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sessionid,
	       arg_SEQUENCE4->sa_sessionid, NFS4_SESSIONID_SIZE);
	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid =
	    session->slots[arg_SEQUENCE4->sa_slotid].sequence;
	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid =
	    arg_SEQUENCE4->sa_slotid;
	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_highest_slotid =
	    NFS41_NB_SLOTS - 1;
	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_target_highest_slotid =
	    arg_SEQUENCE4->sa_slotid;	/* Maybe not the best choice */

	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags = 0;

	if (nfs_rpc_get_chan(session->clientid_record, 0) == NULL) {
		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags |=
		    SEQ4_STATUS_CB_PATH_DOWN;
	}

#if IMPLEMENT_CACHETHIS
/* Ganesha always caches result anyway so ignore cachethis */
	if (arg_SEQUENCE4->sa_cachethis) {
#endif
		data->cached_res =
		    &session->slots[arg_SEQUENCE4->sa_slotid].cached_result;
		session->slots[arg_SEQUENCE4->sa_slotid].cache_used = true;

		LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
				"Use sesson slot %" PRIu32 "=%p for DRC",
				arg_SEQUENCE4->sa_slotid, data->cached_res);
#if IMPLEMENT_CACHETHIS
	} else {
		data->cached_res = NULL;
		session->slots[arg_SEQUENCE4->sa_slotid].cache_used = false;

		LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
				"Don't use sesson slot %" PRIu32
				"=NULL for DRC", arg_SEQUENCE4->sa_slotid);
	}
#endif

	pthread_mutex_unlock(&session->slots[arg_SEQUENCE4->sa_slotid].lock);

	/* If we were successful, stash the clientid in the request
	 * context.
	 */
	data->req_ctx->clientid = &data->session->clientid;

	res_SEQUENCE4->sr_status = NFS4_OK;
	return res_SEQUENCE4->sr_status;
}				/* nfs41_op_sequence */