int nfs41_op_exchange_id(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; char str_client[NFS4_OPAQUE_LIMIT * 2 + 1]; nfs_client_record_t * pclient_record; nfs_client_id_t * pconf; nfs_client_id_t * punconf; int rc; int len; char * temp; bool_t update; const char * update_str; log_components_t component = COMPONENT_CLIENTID; #if 0 /** @todo: plante le client sous windows. Ai-je réellement besoin de cela ???? */ /* Check flags value (test EID4) */ if(arg_EXCHANGE_ID4.eia_flags & all_eia_flags != arg_EXCHANGE_ID4.eia_flags) { res_EXCHANGE_ID4.eir_status = NFS4ERR_INVAL; return res_EXCHANGE_ID4.eir_status; } #endif if(isDebug(COMPONENT_SESSIONS)) component = COMPONENT_SESSIONS; #define arg_EXCHANGE_ID4 op->nfs_argop4_u.opexchange_id #define res_EXCHANGE_ID4 resp->nfs_resop4_u.opexchange_id #define res_EXCHANGE_ID4_ok resp->nfs_resop4_u.opexchange_id.EXCHANGE_ID4res_u.eir_resok4 resp->resop = NFS4_OP_EXCHANGE_ID; update = (arg_EXCHANGE_ID4.eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) != 0; if(isDebug(component)) { DisplayOpaqueValue(arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_val, arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_len, str_client); sprint_mem(str_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); update_str = update ? "UPDATE" : "NO UPDATE"; } LogDebug(component, "EXCHANGE_ID Client addr=%s id=%s verf=%s %s --------------------", data->pworker->hostaddr_str, str_client, str_verifier, update_str); /* Do we already have one or more records for client id (x)? */ pclient_record = get_client_record(arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_val, arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_len); if(pclient_record == NULL) { /* Some major failure */ LogCrit(component, "EXCHANGE_ID failed"); res_EXCHANGE_ID4.eir_status = NFS4ERR_SERVERFAULT; return res_EXCHANGE_ID4.eir_status; } /* * The following checks are based on RFC5661 * * This attempts to implement the logic described in 18.35.4. IMPLEMENTATION */ P(pclient_record->cr_mutex); if(isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_record(pclient_record, str); LogFullDebug(COMPONENT_CLIENTID, "Client Record %s cr_pconfirmed_id=%p cr_punconfirmed_id=%p", str, pclient_record->cr_pconfirmed_id, pclient_record->cr_punconfirmed_id); } pconf = pclient_record->cr_pconfirmed_id; if(pconf != NULL) { /* Need a reference to the confirmed record for below */ inc_client_id_ref(pconf); } if(pconf != NULL && !update) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A not set */ /** @todo FSF: old code ifdefed out nfs_compare_clientcred with _NFSV4_COMPARE_CRED_IN_EXCHANGE_ID */ if(!nfs_compare_clientcred(&pconf->cid_credential, &data->credential) || !cmp_sockaddr(&pconf->cid_client_addr, &data->pworker->hostaddr, IGNORE_PORT)) { /** @todo FSF: should also check if there is no state */ P(pconf->cid_mutex); if(valid_lease(pconf)) { V(pconf->cid_mutex); /* CASE 3, client collisions, old clientid is expired */ if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(pconf, str); LogDebug(COMPONENT_CLIENTID, "Expiring %s", str); } /* Expire clientid and release our reference. */ nfs_client_id_expire(pconf); dec_client_id_ref(pconf); pconf = NULL; } else { V(pconf->cid_mutex); /* CASE 3, client collisions, old clientid is not expired */ if(isDebug(component)) { char confirmed_addr[SOCK_NAME_MAX]; sprint_sockip(&pconf->cid_client_addr, confirmed_addr, sizeof(confirmed_addr)); LogDebug(component, "Confirmed ClientId %"PRIx64"->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", pconf->cid_clientid, str_client, confirmed_addr); } res_EXCHANGE_ID4.eir_status = NFS4ERR_CLID_INUSE; /* Release our reference to the confirmed clientid. */ dec_client_id_ref(pconf); goto out; } } else if(memcmp(arg_EXCHANGE_ID4.eia_clientowner.co_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2, Non-Update on Existing Client ID */ /* Return what was last returned without changing any refcounts */ LogDebug(COMPONENT_CLIENTID, "Non-update of confirmed ClientId %"PRIx64"->%s", pconf->cid_clientid, str_client); punconf = pconf; goto return_ok; } else { /* CASE 5, client restart */ /** @todo FSF: expire old clientid? */ LogDebug(component, "Restarted ClientId %"PRIx64"->%s", pconf->cid_clientid, str_client); } } else if(pconf != NULL) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A set */ if(memcmp(arg_EXCHANGE_ID4.eia_clientowner.co_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /** @todo FSF: old code ifdefed out nfs_compare_clientcred with _NFSV4_COMPARE_CRED_IN_EXCHANGE_ID */ if(!nfs_compare_clientcred(&pconf->cid_credential, &data->credential) || !cmp_sockaddr(&pconf->cid_client_addr, &data->pworker->hostaddr, IGNORE_PORT)) { /* CASE 9, Update but wrong principal */ if(isDebug(component)) { char confirmed_addr[SOCK_NAME_MAX]; sprint_sockip(&pconf->cid_client_addr, confirmed_addr, sizeof(confirmed_addr)); LogDebug(component, "Confirmed ClientId %"PRIx64"->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_PERM", pconf->cid_clientid, str_client, confirmed_addr); } res_EXCHANGE_ID4.eir_status = NFS4ERR_PERM; } else { /* CASE 6, Update */ /** @todo: this is not implemented, the things it updates aren't even tracked */ LogMajor(component, "EXCHANGE_ID Update not supported"); res_EXCHANGE_ID4.eir_status = NFS4ERR_NOTSUPP; } } else { /* CASE 8, Update but wrong verifier */ if(isDebug(component)) { char str_old_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; sprint_mem(str_old_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE); LogDebug(component, "Confirmed clientid %"PRIx64"->'%s': Verifiers do not match... confirmed verifier=%s", pconf->cid_clientid, str_client, str_old_verifier); } res_EXCHANGE_ID4.eir_status = NFS4ERR_NOT_SAME; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(pconf); goto out; } else if(pconf == NULL && update) { LogDebug(component, "No confirmed clientid to update for %s", str_client); res_EXCHANGE_ID4.eir_status = NFS4ERR_NOENT; goto out; } /* At this point, no matter what the case was above, we should remove any * pre-existing unconfirmed record. */ punconf = pclient_record->cr_punconfirmed_id; if(punconf != NULL) { /* CASE 4, replacement of unconfirmed record */ /* Delete the unconfirmed clientid record */ if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(punconf, str); LogDebug(COMPONENT_CLIENTID, "Replacing %s", str); } /* unhash the clientid record */ remove_unconfirmed_client_id(punconf); } /* Now we can proceed to build the new unconfirmed record. We have determined * the clientid and setclientid_confirm values above. */ punconf = create_client_id(0, pclient_record, &data->pworker->hostaddr, &data->credential); if(punconf == NULL) { /* Error already logged, return */ res_EXCHANGE_ID4.eir_status = NFS4ERR_RESOURCE; goto out; } memcpy(punconf->cid_incoming_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); if(gethostname(punconf->cid_server_owner, sizeof(punconf->cid_server_owner)) == -1) { /* Free the clientid record and return */ free_client_id(punconf); res_EXCHANGE_ID4.eir_status = NFS4ERR_SERVERFAULT; goto out; } snprintf(punconf->cid_server_scope, sizeof(punconf->cid_server_scope), "%s_NFS-Ganesha", punconf->cid_server_owner); rc = nfs_client_id_insert(punconf); if(rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_EXCHANGE_ID4.eir_status = clientid_error_to_nfsstat(rc); goto out; } return_ok: /* Build the reply */ res_EXCHANGE_ID4_ok.eir_clientid = punconf->cid_clientid; res_EXCHANGE_ID4_ok.eir_sequenceid = punconf->cid_create_session_sequence; #if defined(_USE_FSALMDS) && defined(_USE_FSALDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_FSALMDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_FSALDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_DS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #else res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #endif res_EXCHANGE_ID4_ok.eir_state_protect.spr_how = SP4_NONE; len = strlen(punconf->cid_server_owner); temp = gsh_malloc(len); if(temp == NULL) { LogDebug(component, "Could not allocate memory for so_major_id in response"); /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, punconf->cid_server_owner, len); res_EXCHANGE_ID4_ok.eir_server_owner.so_major_id.so_major_id_len = len; res_EXCHANGE_ID4_ok.eir_server_owner.so_major_id.so_major_id_val = temp; res_EXCHANGE_ID4_ok.eir_server_owner.so_minor_id = 0; len = strlen(punconf->cid_server_scope); temp = gsh_malloc(len); if(temp == NULL) { LogDebug(component, "Could not allocate memory for eir_server_scope in response"); /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, punconf->cid_server_scope, len); res_EXCHANGE_ID4_ok.eir_server_scope.eir_server_scope_len = len; res_EXCHANGE_ID4_ok.eir_server_scope.eir_server_scope_val = temp; res_EXCHANGE_ID4_ok.eir_server_impl_id.eir_server_impl_id_len = 0; res_EXCHANGE_ID4_ok.eir_server_impl_id.eir_server_impl_id_val = NULL; if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; sprint_mem(str_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(punconf, str); LogDebug(COMPONENT_CLIENTID, "EXCHANGE_ID reply Verifier=%s %s", str_verifier, str); } res_EXCHANGE_ID4.eir_status = NFS4_OK; out: V(pclient_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(pclient_record); return res_EXCHANGE_ID4.eir_status; } /* nfs41_op_exchange_id */
int nfs4_op_setclientid(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_setclientid"; char str_verifier[MAXNAMLEN]; char str_client[MAXNAMLEN]; #define arg_SETCLIENTID4 op->nfs_argop4_u.opsetclientid #define res_SETCLIENTID4 resp->nfs_resop4_u.opsetclientid clientid4 clientid; nfs_client_id_t * nfs_clientid; nfs_client_id_t new_nfs_clientid; nfs_worker_data_t *pworker = NULL; pworker = (nfs_worker_data_t *) data->pclient->pworker; strncpy(str_verifier, arg_SETCLIENTID4.client.verifier, MAXNAMLEN); strncpy(str_client, arg_SETCLIENTID4.client.id.id_val, arg_SETCLIENTID4.client.id.id_len); str_client[arg_SETCLIENTID4.client.id.id_len] = '\0'; LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Client id len = %u", arg_SETCLIENTID4.client.id.id_len); LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Client name = #%s#", str_client); /*LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Verifier = #%s#", str_verifier ) ; */ LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Callback: cb_program = %u|0x%x, cb_location = { r_addr = %s r_netid = %s }", arg_SETCLIENTID4.callback.cb_program, arg_SETCLIENTID4.callback.cb_program, #ifdef _USE_NFS4_1 arg_SETCLIENTID4.callback.cb_location.na_r_addr, arg_SETCLIENTID4.callback.cb_location.na_r_netid); #else arg_SETCLIENTID4.callback.cb_location.r_addr, arg_SETCLIENTID4.callback.cb_location.r_netid); #endif LogDebug(COMPONENT_NFS_V4, "SETCLIENTID callback_ident : %u", arg_SETCLIENTID4.callback_ident); /* First build the clientid4 nickname */ /* There was no pb, returns the clientid */ resp->resop = NFS4_OP_SETCLIENTID; res_SETCLIENTID4.status = NFS4_OK; /* Compute the client id */ if(nfs_client_id_basic_compute(str_client, &clientid) != CLIENT_ID_SUCCESS) { res_SETCLIENTID4.status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4.status; } LogDebug(COMPONENT_NFS_V4, "SETCLIENTID computed clientid4=%"PRIx64" for name='%s'", clientid, str_client); retry: /* Does this id already exists ? */ if(nfs_client_id_Get_Pointer(clientid, &nfs_clientid) == CLIENT_ID_SUCCESS) { /* Client id already in use */ LogDebug(COMPONENT_NFS_V4, "SETCLIENTID ClientId %"PRIx64" already in use for client '%s', check if same", clientid, nfs_clientid->client_name); P(nfs_clientid->clientid_mutex); /* Is existing client id confirmed? */ if(nfs_clientid->confirmed == CONFIRMED_CLIENT_ID) { /* Check if client id has same credentials */ if(nfs_compare_clientcred(&(nfs_clientid->credential), &(data->credential)) == FALSE) { LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Confirmed ClientId %"PRIx64" -> '%s': Credential do not match... Return NFS4ERR_CLID_INUSE", clientid, nfs_clientid->client_name); res_SETCLIENTID4.status = NFS4ERR_CLID_INUSE; #ifdef _USE_NFS4_1 res_SETCLIENTID4.SETCLIENTID4res_u.client_using.na_r_netid = nfs_clientid->client_r_netid; res_SETCLIENTID4.SETCLIENTID4res_u.client_using.na_r_addr = nfs_clientid->client_r_addr; #else res_SETCLIENTID4.SETCLIENTID4res_u.client_using.r_netid = nfs_clientid->client_r_netid; res_SETCLIENTID4.SETCLIENTID4res_u.client_using.r_addr = nfs_clientid->client_r_addr; #endif V(nfs_clientid->clientid_mutex); return res_SETCLIENTID4.status; } else LogDebug(COMPONENT_NFS_V4, "SETCLIENTID ClientId %"PRIx64" is set again by same principal", clientid); /* We have a confirmed client record, is it expired? */ if (nfs4_is_lease_expired(nfs_clientid)) { LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Existing ClientId %"PRIx64" is expired", clientid); V(nfs_clientid->clientid_mutex); nfs_client_id_expire(nfs_clientid); goto retry; } /* Ask for a different client with the same client id... returns an error if different client */ LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Confirmed ClientId %"PRIx64" already in use for client '%s'", clientid, nfs_clientid->client_name); if(strncmp (nfs_clientid->incoming_verifier, arg_SETCLIENTID4.client.verifier, NFS4_VERIFIER_SIZE)) { LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Confirmed ClientId %"PRIx64" already in use for client '%s', verifier do not match...", clientid, nfs_clientid->client_name); /* A client has rebooted and rebuilds its state */ LogDebug(COMPONENT_NFS_V4, "SETCLIENTID: a client has rebooted. Remove the record for this client and create a new one."); V(nfs_clientid->clientid_mutex); nfs_client_id_expire(nfs_clientid); goto retry; } else { LogDebug(COMPONENT_NFS_V4, "SETCLIENTID Confirmed ClientId %"PRIx64" already in use for client '%s', verifier matches. Now check callback", clientid, nfs_clientid->client_name); LogDebug(COMPONENT_NFS_V4, "SETCLIENTID '%s' will set the client UNCONFIRMED and returns NFS4_OK", nfs_clientid->client_name); /* TODO: update callback program here. */ /* Set the client UNCONFIRMED */ nfs_clientid->confirmed = UNCONFIRMED_CLIENT_ID; res_SETCLIENTID4.status = NFS4_OK; } } else { LogDebug(COMPONENT_NFS_V4, "SETCLIENTID ClientId %"PRIx64" already in use for client '%s', but unconfirmed", clientid, nfs_clientid->client_name); V(nfs_clientid->clientid_mutex); nfs_client_id_expire(nfs_clientid); goto retry; } V(nfs_clientid->clientid_mutex); } else { /* Client record did not exist, build the client record */ nfs_clientid = &new_nfs_clientid; strncpy(nfs_clientid->client_name, arg_SETCLIENTID4.client.id.id_val, arg_SETCLIENTID4.client.id.id_len); nfs_clientid->client_name[arg_SETCLIENTID4.client.id.id_len] = '\0'; #ifdef _USE_NFS4_1 strncpy(nfs_clientid->client_r_addr, arg_SETCLIENTID4.callback.cb_location.na_r_addr, SOCK_NAME_MAX); strncpy(nfs_clientid->client_r_netid, arg_SETCLIENTID4.callback.cb_location.na_r_netid, MAXNAMLEN); #else strncpy(nfs_clientid->client_r_addr, arg_SETCLIENTID4.callback.cb_location.r_addr, SOCK_NAME_MAX); strncpy(nfs_clientid->client_r_netid, arg_SETCLIENTID4.callback.cb_location.r_netid, MAXNAMLEN); #endif strncpy(nfs_clientid->incoming_verifier, arg_SETCLIENTID4.client.verifier, NFS4_VERIFIER_SIZE); snprintf(nfs_clientid->verifier, NFS4_VERIFIER_SIZE, "%u", (unsigned int)ServerBootTime); nfs_clientid->confirmed = UNCONFIRMED_CLIENT_ID; nfs_clientid->cb_program = arg_SETCLIENTID4.callback.cb_program; nfs_clientid->clientid = clientid; nfs_clientid->last_renew = time(NULL); nfs_clientid->credential = data->credential; nfs_clientid->create_session_sequence = 0; if(pthread_mutex_init(&nfs_clientid->clientid_mutex, NULL) == -1) { res_SETCLIENTID4.status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4.status; } if(nfs_client_id_add(clientid, *nfs_clientid, data->pclient) != CLIENT_ID_SUCCESS) { res_SETCLIENTID4.status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4.status; } } res_SETCLIENTID4.SETCLIENTID4res_u.resok4.clientid = clientid; memset(res_SETCLIENTID4.SETCLIENTID4res_u.resok4.setclientid_confirm, 0, NFS4_VERIFIER_SIZE); snprintf(res_SETCLIENTID4.SETCLIENTID4res_u.resok4.setclientid_confirm, NFS4_VERIFIER_SIZE, "%u", (unsigned int)ServerBootTime); /* LogDebug(COMPONENT_NFS_V4, "SETCLIENTID reply :ClientId=%llx Verifier=%s", res_SETCLIENTID4.SETCLIENTID4res_u.resok4.clientid , res_SETCLIENTID4.SETCLIENTID4res_u.resok4.setclientid_confirm ) ; */ res_SETCLIENTID4.status = NFS4_OK; return res_SETCLIENTID4.status; } /* nfs4_op_setclientid */
int nfs4_op_setclientid_confirm(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETCLIENTID_CONFIRM4args * const arg_SETCLIENTID_CONFIRM4 = &op->nfs_argop4_u.opsetclientid_confirm; SETCLIENTID_CONFIRM4res * const res_SETCLIENTID_CONFIRM4 = &resp->nfs_resop4_u.opsetclientid_confirm; nfs_client_id_t *conf = NULL; nfs_client_id_t *unconf = NULL; nfs_client_record_t *client_record; clientid4 clientid = 0; char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; 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}; int rc; /* Make sure str_client is always printable even * if log level changes midstream. */ display_printf(&dspbuf_client, "(unknown)"); display_reset_buffer(&dspbuf_client); resp->resop = NFS4_OP_SETCLIENTID_CONFIRM; res_SETCLIENTID_CONFIRM4->status = NFS4_OK; clientid = arg_SETCLIENTID_CONFIRM4->clientid; display_clientid(&dspbuf_clientid4, clientid); if (data->minorversion > 0) { res_SETCLIENTID_CONFIRM4->status = NFS4ERR_NOTSUPP; return res_SETCLIENTID_CONFIRM4->status; } if (op_ctx->client != NULL) str_client_addr = op_ctx->client->hostaddr_str; if (isDebug(COMPONENT_CLIENTID)) { sprint_mem(str_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE); } else { str_verifier[0] = '\0'; } LogDebug(COMPONENT_CLIENTID, "SETCLIENTID_CONFIRM client addr=%s clientid=%s setclientid_confirm=%s", str_client_addr, str_clientid4, str_verifier); /* 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; if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Found %s", str); } } else { rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc != CLIENT_ID_SUCCESS) { /* No record whatsoever of this clientid */ LogDebug(COMPONENT_CLIENTID, "%s clientid = %s", clientid_error_to_str(rc), str_clientid4); res_SETCLIENTID_CONFIRM4->status = clientid_error_to_nfsstat_no_expire(rc); return res_SETCLIENTID_CONFIRM4->status; } client_record = conf->cid_client_record; if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogFullDebug(COMPONENT_CLIENTID, "Found %s", str); } } PTHREAD_MUTEX_lock(&client_record->cr_mutex); inc_client_record_ref(client_record); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(COMPONENT_CLIENTID, "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 pconf and punconf is non-NULL */ if (unconf != NULL) { /* First must match principal */ if (!nfs_compare_clientcred(&unconf->cid_credential, &data->credential) || op_ctx->client == NULL || unconf->gsh_client == NULL || op_ctx->client != unconf->gsh_client) { if (isDebug(COMPONENT_CLIENTID)) { char *unconfirmed_addr = "(unknown)"; if (unconf->gsh_client != NULL) unconfirmed_addr = unconf->gsh_client->hostaddr_str; LogDebug(COMPONENT_CLIENTID, "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client_addr, unconfirmed_addr); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; dec_client_id_ref(unconf); goto out; } else if (unconf->cid_confirmed == CONFIRMED_CLIENT_ID && memcmp(unconf->cid_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE) == 0) { /* We must have raced with another SETCLIENTID_CONFIRM */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Race against confirm for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4_OK; dec_client_id_ref(unconf); goto out; } else if (unconf->cid_confirmed != UNCONFIRMED_CLIENT_ID) { /* We raced with another thread that dealt * with this unconfirmed record. Release our * reference, and pretend we didn't find a * record. */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Race against expire for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_STALE_CLIENTID; dec_client_id_ref(unconf); goto out; } } if (conf != NULL) { if (isDebug(COMPONENT_CLIENTID) && conf != NULL) display_clientid_name(&dspbuf_client, conf); /* First must match principal */ if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential) || op_ctx->client == NULL || conf->gsh_client == NULL || op_ctx->client != conf->gsh_client) { if (isDebug(COMPONENT_CLIENTID)) { char *confirmed_addr = "(unknown)"; if (conf->gsh_client != NULL) confirmed_addr = conf->gsh_client->hostaddr_str; LogDebug(COMPONENT_CLIENTID, "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); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; } else if (memcmp( conf->cid_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE) == 0) { /* In this case, the record was confirmed and * we have received a retry */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Retry confirm for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4_OK; } else { /* This is a case not covered... Return * NFS4ERR_CLID_INUSE */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; char str_conf_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; sprint_mem(str_conf_verifier, conf->cid_verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Confirm verifier=%s doesn't match verifier=%s for %s", str_conf_verifier, str_verifier, str); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } /* 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. Also, at this point, we have * a matching unconfirmed clientid (punconf != NULL and pconf * == NULL). */ /* 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_CLIENTID) && 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_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "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. */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Updating from %s", str); } /* Copy callback information into confirmed clientid record */ memcpy(conf->cid_cb.v40.cb_client_r_addr, unconf->cid_cb.v40.cb_client_r_addr, sizeof(conf->cid_cb.v40.cb_client_r_addr)); conf->cid_cb.v40.cb_addr = unconf->cid_cb.v40.cb_addr; conf->cid_cb.v40.cb_program = unconf->cid_cb.v40.cb_program; conf->cid_cb.v40.cb_callback_ident = unconf->cid_cb.v40.cb_callback_ident; nfs_rpc_destroy_chan(&conf->cid_cb.v40.cb_chan); memcpy(conf->cid_verifier, unconf->cid_verifier, NFS4_VERIFIER_SIZE); /* 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_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Updated %s", str); } /* Check and update call back channel state */ if (nfs_param.nfsv4_param.allow_delegations && nfs_test_cb_chan(conf) != RPC_SUCCESS) { set_cb_chan_down(conf, true); LogCrit(COMPONENT_CLIENTID, "setclid confirm: Callback channel is down"); } else { set_cb_chan_down(conf, false); LogDebug(COMPONENT_CLIENTID, "setclid confirm: Callback channel is UP"); } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); } else { /* This is a new clientid */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Confirming new %s", str); } rc = nfs_client_id_confirm(unconf, COMPONENT_CLIENTID); if (rc != CLIENT_ID_SUCCESS) { res_SETCLIENTID_CONFIRM4->status = clientid_error_to_nfsstat_no_expire(rc); LogEvent(COMPONENT_CLIENTID, "FAILED to confirm client"); /* Release our reference to the unconfirmed record */ dec_client_id_ref(unconf); goto out; } /* check if the client can perform reclaims */ nfs4_chk_clid(unconf); if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Confirmed %s", str); } /* Check and update call back channel state */ if (nfs_param.nfsv4_param.allow_delegations && nfs_test_cb_chan(unconf) != RPC_SUCCESS) { set_cb_chan_down(unconf, true); LogCrit(COMPONENT_CLIENTID, "setclid confirm: Callback channel is down"); } else { set_cb_chan_down(unconf, false); LogDebug(COMPONENT_CLIENTID, "setclid confirm: Callback channel is UP"); } /* Release our reference to the now confirmed record */ dec_client_id_ref(unconf); } if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(COMPONENT_CLIENTID, "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p", str, client_record->cr_confirmed_rec, client_record->cr_unconfirmed_rec); } /* Successful exit */ res_SETCLIENTID_CONFIRM4->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_SETCLIENTID_CONFIRM4->status; }
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; }
static void reap_hash_table(hash_table_t * ht_reap) { struct rbt_head * head_rbt; hash_data_t * pdata = NULL; uint32_t i; int v4, rc; struct rbt_node * pn; nfs_client_id_t * pclientid; nfs_client_record_t * precord; /* For each bucket of the requested hashtable */ for(i = 0; i < ht_reap->parameter.index_size; i++) { head_rbt = &ht_reap->partitions[i].rbt; restart: /* acquire mutex */ pthread_rwlock_wrlock(&ht_reap->partitions[i].lock); /* go through all entries in the red-black-tree*/ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); pclientid = (nfs_client_id_t *)pdata->buffval.pdata; /* * little hack: only want to reap v4 clients * 4.1 initializess this field to '1' */ v4 = (pclientid->cid_create_session_sequence == 0); P(pclientid->cid_mutex); if(!valid_lease(pclientid) && v4) { inc_client_id_ref(pclientid); /* Take a reference to the client record */ precord = pclientid->cid_client_record; inc_client_record_ref(precord); V(pclientid->cid_mutex); pthread_rwlock_unlock(&ht_reap->partitions[i].lock); if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(pclientid, str); LogFullDebug(COMPONENT_CLIENTID, "Expire index %d %s", i, str); } /* Take cr_mutex and expire clientid */ P(precord->cr_mutex); rc = nfs_client_id_expire(pclientid); V(precord->cr_mutex); dec_client_id_ref(pclientid); dec_client_record_ref(precord); if(rc) goto restart; } else { V(pclientid->cid_mutex); } RBT_INCREMENT(pn); } pthread_rwlock_unlock(&ht_reap->partitions[i].lock); }
void *reaper_thread(void *unused) { hash_table_t *ht = ht_client_id; struct rbt_head *head_rbt; hash_data_t *pdata = NULL; int i, v4; struct rbt_node *pn; nfs_client_id_t *clientp; #ifndef _NO_BUDDY_SYSTEM if((i = BuddyInit(&nfs_param.buddy_param_admin)) != BUDDY_SUCCESS) { /* Failed init */ LogFatal(COMPONENT_MAIN, "Memory manager could not be initialized"); } LogInfo(COMPONENT_MAIN, "Memory manager successfully initialized"); #endif SetNameFunction("reaper_thr"); while(1) { /* Initial wait */ /* TODO: should this be configurable? */ /* sleep(nfs_param.core_param.reaper_delay); */ sleep(reaper_delay); LogFullDebug(COMPONENT_MAIN, "NFS reaper : now checking clients"); /* For each bucket of the hashtable */ for(i = 0; i < ht->parameter.index_size; i++) { head_rbt = &(ht->array_rbt[i]); restart: /* acquire mutex */ P_w(&(ht->array_lock[i])); /* go through all entries in the red-black-tree*/ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); clientp = (nfs_client_id_t *)pdata->buffval.pdata; /* * little hack: only want to reap v4 clients * 4.1 initializess this field to '1' */ v4 = (clientp->create_session_sequence == 0); if (clientp->confirmed != EXPIRED_CLIENT_ID && nfs4_is_lease_expired(clientp) && v4) { V_w(&(ht->array_lock[i])); LogDebug(COMPONENT_MAIN, "NFS reaper: expire client %s", clientp->client_name); nfs_client_id_expire(clientp); goto restart; } if (clientp->confirmed == EXPIRED_CLIENT_ID) { LogDebug(COMPONENT_MAIN, "reaper: client %s already expired", clientp->client_name); } RBT_INCREMENT(pn); } V_w(&(ht->array_lock[i])); } } /* while ( 1 ) */
int nfs4_op_exchange_id(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { nfs_client_record_t *client_record; nfs_client_id_t *conf; nfs_client_id_t *unconf; int rc; int len; char *temp; bool update; EXCHANGE_ID4args * const arg_EXCHANGE_ID4 = &op->nfs_argop4_u.opexchange_id; EXCHANGE_ID4res * const res_EXCHANGE_ID4 = &resp->nfs_resop4_u.opexchange_id; EXCHANGE_ID4resok * const res_EXCHANGE_ID4_ok = (&resp->nfs_resop4_u.opexchange_id.EXCHANGE_ID4res_u.eir_resok4); uint32_t pnfs_flags; in_addr_t server_addr = 0; resp->resop = NFS4_OP_EXCHANGE_ID; if (data->minorversion == 0) return res_EXCHANGE_ID4->eir_status = NFS4ERR_INVAL; if ((arg_EXCHANGE_ID4-> eia_flags & ~(EXCHGID4_FLAG_SUPP_MOVED_REFER | EXCHGID4_FLAG_SUPP_MOVED_MIGR | EXCHGID4_FLAG_BIND_PRINC_STATEID | EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_UPD_CONFIRMED_REC_A | EXCHGID4_FLAG_CONFIRMED_R)) != 0) return res_EXCHANGE_ID4->eir_status = NFS4ERR_INVAL; /* If client did not ask for pNFS related server roles than just set server roles */ pnfs_flags = arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_MASK_PNFS; if (pnfs_flags == 0) { if (nfs_param.nfsv4_param.pnfs_mds) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_MDS; if (nfs_param.nfsv4_param.pnfs_ds) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_DS; if (pnfs_flags == 0) pnfs_flags |= EXCHGID4_FLAG_USE_NON_PNFS; } /* If client did ask for pNFS related server roles than try to match the server roles to the client request. */ else { if ((arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_USE_PNFS_MDS) && (nfs_param.nfsv4_param.pnfs_mds)) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_MDS; if ((arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_USE_PNFS_DS) && (nfs_param.nfsv4_param.pnfs_ds)) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_DS; if (pnfs_flags == 0) pnfs_flags |= EXCHGID4_FLAG_USE_NON_PNFS; } LogDebug(COMPONENT_CLIENTID, "EXCHANGE_ID pnfs_flags 0x%08x eia_flags 0x%08x", pnfs_flags, arg_EXCHANGE_ID4->eia_flags); update = (arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) != 0; server_addr = get_raddr(data->req->rq_xprt); /* Do we already have one or more records for client id (x)? */ client_record = get_client_record(arg_EXCHANGE_ID4->eia_clientowner.co_ownerid. co_ownerid_val, arg_EXCHANGE_ID4->eia_clientowner.co_ownerid. co_ownerid_len, pnfs_flags, server_addr); if (client_record == NULL) { /* Some major failure */ LogCrit(COMPONENT_CLIENTID, "EXCHANGE_ID failed"); res_EXCHANGE_ID4->eir_status = NFS4ERR_SERVERFAULT; return res_EXCHANGE_ID4->eir_status; } /* * The following checks are based on RFC5661 * * This attempts to implement the logic described in * 18.35.4. IMPLEMENTATION */ PTHREAD_MUTEX_lock(&client_record->cr_mutex); conf = client_record->cr_confirmed_rec; if (conf != NULL) { /* Need a reference to the confirmed record for below */ inc_client_id_ref(conf); } if (conf != NULL && !update) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A not set * * Compare the client credentials, but don't compare * the client address. Doing so interferes with * trunking and the ability of a client to reconnect * after being assigned a new address. */ if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential)) { PTHREAD_MUTEX_lock(&conf->cid_mutex); if (!valid_lease(conf) || !client_id_has_state(conf)) { PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* CASE 3, client collisions, old * clientid is expired * * Expire clientid and release our reference. */ nfs_client_id_expire(conf, false); dec_client_id_ref(conf); conf = NULL; } else { PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* CASE 3, client collisions, old * clientid is not expired */ res_EXCHANGE_ID4->eir_status = NFS4ERR_CLID_INUSE; /* Release our reference to the * confirmed clientid. */ dec_client_id_ref(conf); goto out; } } else if (memcmp(arg_EXCHANGE_ID4->eia_clientowner.co_verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2, Non-Update on Existing Client ID * * Return what was last returned without * changing any refcounts */ unconf = conf; res_EXCHANGE_ID4_ok->eir_flags |= EXCHGID4_FLAG_CONFIRMED_R; goto return_ok; } else { /* CASE 5, client restart */ /** @todo FSF: expire old clientid? */ } } else if (conf != NULL) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A set */ if (memcmp(arg_EXCHANGE_ID4->eia_clientowner.co_verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential) || op_ctx->client == NULL || conf->gsh_client == NULL || op_ctx->client != conf->gsh_client) { /* CASE 9, Update but wrong principal */ res_EXCHANGE_ID4->eir_status = NFS4ERR_PERM; } else { /* CASE 6, Update */ /** @todo: this is not implemented, the things it updates aren't even tracked */ LogMajor(COMPONENT_CLIENTID, "EXCHANGE_ID Update not supported"); res_EXCHANGE_ID4->eir_status = NFS4ERR_NOTSUPP; } } else { /* CASE 8, Update but wrong verifier */ res_EXCHANGE_ID4->eir_status = NFS4ERR_NOT_SAME; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } else if (conf == NULL && update) { res_EXCHANGE_ID4->eir_status = NFS4ERR_NOENT; goto out; } /* At this point, no matter what the case was above, we should * remove any pre-existing unconfirmed record. */ unconf = client_record->cr_unconfirmed_rec; if (unconf != NULL) { /* CASE 4, replacement of unconfirmed record * * Delete the unconfirmed clientid record * unhash the clientid record */ remove_unconfirmed_client_id(unconf); } /* Now we can proceed to build the new unconfirmed record. We * have determined the clientid and setclientid_confirm values * above. */ unconf = create_client_id(0, client_record, &data->credential, data->minorversion); if (unconf == NULL) { /* Error already logged, return */ res_EXCHANGE_ID4->eir_status = NFS4ERR_RESOURCE; goto out; } unconf->cid_create_session_sequence = 1; glist_init(&unconf->cid_cb.v41.cb_session_list); memcpy(unconf->cid_incoming_verifier, arg_EXCHANGE_ID4->eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); if (gethostname(unconf->cid_server_owner, sizeof(unconf->cid_server_owner)) == -1) { /* Free the clientid record and return */ free_client_id(unconf); res_EXCHANGE_ID4->eir_status = NFS4ERR_SERVERFAULT; goto out; } snprintf(unconf->cid_server_scope, sizeof(unconf->cid_server_scope), "%s_NFS-Ganesha", unconf->cid_server_owner); LogDebug(COMPONENT_CLIENTID, "Serving IP %s", unconf->cid_server_scope); rc = nfs_client_id_insert(unconf); if (rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_EXCHANGE_ID4->eir_status = clientid_error_to_nfsstat(rc); goto out; } return_ok: /* Build the reply */ res_EXCHANGE_ID4_ok->eir_clientid = unconf->cid_clientid; res_EXCHANGE_ID4_ok->eir_sequenceid = unconf->cid_create_session_sequence; res_EXCHANGE_ID4_ok->eir_flags |= client_record->cr_pnfs_flags; res_EXCHANGE_ID4_ok->eir_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; res_EXCHANGE_ID4_ok->eir_state_protect.spr_how = SP4_NONE; len = strlen(unconf->cid_server_owner); temp = gsh_malloc(len); if (temp == NULL) { /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, unconf->cid_server_owner, len); res_EXCHANGE_ID4_ok->eir_server_owner.so_major_id.so_major_id_len = len; res_EXCHANGE_ID4_ok->eir_server_owner.so_major_id.so_major_id_val = temp; res_EXCHANGE_ID4_ok->eir_server_owner.so_minor_id = 0; len = strlen(unconf->cid_server_scope); temp = gsh_malloc(len); if (temp == NULL) { /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, unconf->cid_server_scope, len); res_EXCHANGE_ID4_ok->eir_server_scope.eir_server_scope_len = len; res_EXCHANGE_ID4_ok->eir_server_scope.eir_server_scope_val = temp; res_EXCHANGE_ID4_ok->eir_server_impl_id.eir_server_impl_id_len = 0; res_EXCHANGE_ID4_ok->eir_server_impl_id.eir_server_impl_id_val = NULL; res_EXCHANGE_ID4->eir_status = NFS4_OK; out: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(client_record); return res_EXCHANGE_ID4->eir_status; }
static int reap_hash_table(hash_table_t *ht_reap) { struct rbt_head *head_rbt; struct hash_data *addr = NULL; uint32_t i; int rc; struct rbt_node *pn; nfs_client_id_t *pclientid; nfs_client_record_t *precord; int count = 0; struct req_op_context req_ctx; struct user_cred creds; /* We need a real context. Make all reaping done * by root,root */ memset(&creds, 0, sizeof(creds)); req_ctx.creds = &creds; /* For each bucket of the requested hashtable */ for (i = 0; i < ht_reap->parameter.index_size; i++) { head_rbt = &ht_reap->partitions[i].rbt; restart: /* acquire mutex */ PTHREAD_RWLOCK_wrlock(&ht_reap->partitions[i].lock); /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { addr = RBT_OPAQ(pn); pclientid = addr->val.addr; count++; pthread_mutex_lock(&pclientid->cid_mutex); if (!valid_lease(pclientid)) { inc_client_id_ref(pclientid); /* Take a reference to the client record */ precord = pclientid->cid_client_record; inc_client_record_ref(precord); pthread_mutex_unlock(&pclientid->cid_mutex); PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i]. lock); if (isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(pclientid, str); LogFullDebug(COMPONENT_CLIENTID, "Expire index %d %s", i, str); } /* Take cr_mutex and expire clientid */ pthread_mutex_lock(&precord->cr_mutex); /** * @TODO This is incomplete! the context has to be filled in from * somewhere */ memset(&req_ctx, 0, sizeof(req_ctx)); rc = nfs_client_id_expire(pclientid, &req_ctx); pthread_mutex_unlock(&precord->cr_mutex); dec_client_id_ref(pclientid); dec_client_record_ref(precord); if (rc) goto restart; } else { pthread_mutex_unlock(&pclientid->cid_mutex); } RBT_INCREMENT(pn); } PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock); }