/** * @brief Display NFSv4 owner * * @param[in] owner The state owner * @param[out] str Output string * * @return The length of the output string. */ int display_nfs4_owner(state_owner_t *owner, char *str) { char *strtmp = str; strtmp += sprintf(strtmp, "%s %p:", state_owner_type_to_str(owner->so_type), owner); strtmp += sprintf(strtmp, " clientid={"); strtmp += display_client_id_rec(owner->so_owner.so_nfs4_owner.so_clientrec, strtmp); strtmp += sprintf(strtmp, "} owner="); strtmp += DisplayOpaqueValue(owner->so_owner_val, owner->so_owner_len, strtmp); strtmp += sprintf(strtmp, " confirmed=%u seqid=%u", owner->so_owner.so_nfs4_owner.so_confirmed, owner->so_owner.so_nfs4_owner.so_seqid); if(owner->so_owner.so_nfs4_owner.so_related_owner != NULL) { strtmp += sprintf(strtmp, " related_owner={"); strtmp += display_nfs4_owner(owner->so_owner.so_nfs4_owner.so_related_owner, strtmp); strtmp += sprintf(strtmp, "}"); } strtmp += sprintf(strtmp, " refcount=%d", atomic_fetch_int32_t(&owner->so_refcount)); return strtmp - str; }
/** * @brief Release a lease reservation and update lease. * * Lease reservation prevents any other thread from expiring the lease. This * function releases the lease reservation. Before releasing the last * reservation, cid_last_renew will be updated. * * @param[in] clientid Clientid record to update * * @return 1 if lease is valid, 0 if not. * */ void update_lease(nfs_client_id_t *clientid) { clientid->cid_lease_reservations--; /* Renew lease when last reservation is released */ if (clientid->cid_lease_reservations == 0) clientid->cid_last_renew = time(NULL); if (isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(clientid, str); LogFullDebug(COMPONENT_CLIENTID, "Update Lease %s", str); } }
/** * @brief Check if lease is valid * * The caller must hold cid_mutex. * * @param[in] clientid Record to check lease for. * * @return 1 if lease is valid, 0 if not. * */ bool valid_lease(nfs_client_id_t *clientid) { unsigned int valid; valid = _valid_lease(clientid); if (isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(clientid, str); LogFullDebug(COMPONENT_CLIENTID, "Check Lease %s (Valid=%s %u seconds left)", str, valid ? "YES" : "NO", valid); } return valid != 0; }
/** * * reserve_lease_lock: Check if lease is valid and reserve it and retain cid_mutex. * * Check if lease is valid and reserve it and retain cid_mutex. * * Lease reservation prevents any other thread from expiring the lease. Caller * must call update lease to release the reservation. * * @param pclientid [IN] clientid record to check lease for. * * @return 1 if lease is valid, 0 if not. * */ int reserve_lease(nfs_client_id_t * pclientid) { unsigned int valid; valid = _valid_lease(pclientid); if(valid != 0) pclientid->cid_lease_reservations++; if(isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(pclientid, str); LogFullDebug(COMPONENT_CLIENTID, "Reserve Lease %s (Valid=%s %u seconds left)", str, valid ? "YES" : "NO", valid); } return valid != 0; }
int nfs4_op_setclientid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETCLIENTID4args * const arg_SETCLIENTID4 = &op->nfs_argop4_u.opsetclientid; SETCLIENTID4res * const res_SETCLIENTID4 = &resp->nfs_resop4_u.opsetclientid; clientaddr4 * const res_SETCLIENTID4_INUSE = &resp->nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.client_using; char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; char str_client[NFS4_OPAQUE_LIMIT * 2 + 1]; char str_client_addr[SOCK_NAME_MAX + 1]; nfs_client_record_t *client_record; nfs_client_id_t *conf; nfs_client_id_t *unconf; clientid4 clientid; verifier4 verifier; sockaddr_t client_addr; int rc; resp->resop = NFS4_OP_SETCLIENTID; if (data->minorversion > 0) { res_SETCLIENTID4->status = NFS4ERR_NOTSUPP; return res_SETCLIENTID4->status; } copy_xprt_addr(&client_addr, data->req->rq_xprt); if (isDebug(COMPONENT_CLIENTID)) { sprint_sockip(&client_addr, str_client_addr, sizeof(str_client_addr)); DisplayOpaqueValue(arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len, str_client); sprint_mem(str_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); } LogDebug(COMPONENT_CLIENTID, "SETCLIENTID Client addr=%s id=%s verf=%s callback={program=%u r_addr=%s r_netid=%s} ident=%u", str_client_addr, str_client, str_verifier, arg_SETCLIENTID4->callback.cb_program, arg_SETCLIENTID4->callback.cb_location.r_addr, arg_SETCLIENTID4->callback.cb_location.r_netid, arg_SETCLIENTID4->callback_ident); /* Do we already have one or more records for client id (x)? */ client_record = get_client_record(arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len, 0, 0); if (client_record == NULL) { /* Some major failure */ LogCrit(COMPONENT_CLIENTID, "SETCLIENTID failed"); res_SETCLIENTID4->status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4->status; } /* The following checks are based on RFC3530bis draft 16 * * This attempts to implement the logic described in * 15.35.5. IMPLEMENTATION Consider the major bullets as CASE * 1, CASE 2, CASE 3, CASE 4, and CASE 5. */ pthread_mutex_lock(&client_record->cr_mutex); if (isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_record(client_record, str); 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); } conf = client_record->cr_confirmed_rec; if (conf != NULL) { /* Need a reference to the confirmed record for below */ inc_client_id_ref(conf); if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential) || !cmp_sockaddr(&conf->cid_client_addr, &client_addr, true)) { /* CASE 1: * * Confirmed record exists and not the same principal */ if (isDebug(COMPONENT_CLIENTID)) { char confirmed_addr[SOCK_NAME_MAX + 1]; sprint_sockip(&conf->cid_client_addr, confirmed_addr, sizeof(confirmed_addr)); LogDebug(COMPONENT_CLIENTID, "Confirmed ClientId %" PRIx64 "->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", conf->cid_clientid, str_client, confirmed_addr); } res_SETCLIENTID4->status = NFS4ERR_CLID_INUSE; res_SETCLIENTID4_INUSE->r_netid = (char *)netid_nc_table[conf->cid_cb.v40.cb_addr.nc] .netid; res_SETCLIENTID4_INUSE->r_addr = gsh_strdup(conf->cid_cb.v40.cb_client_r_addr); /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } /* Check if confirmed record is for (v, x, c, l, s) */ if (memcmp (arg_SETCLIENTID4->client.verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2: * * A confirmed record exists for this long * form client id and verifier. * * Consider this to be a possible update of * the call-back information. * * Remove any pre-existing unconfirmed record * for (v, x, c). * * Return the same short form client id (c), * but a new setclientid_confirm verifier (t). */ LogFullDebug(COMPONENT_CLIENTID, "Update ClientId %" PRIx64 "->%s", conf->cid_clientid, str_client); clientid = conf->cid_clientid; new_clientid_verifier(verifier); } else { /* Must be CASE 3 or CASE 4 * * Confirmed record is for (u, x, c, l, s). * * These are actually the same, doesn't really * matter if an unconfirmed record exists or * not. Any existing unconfirmed record will * be removed and a new unconfirmed record * added. * * Return a new short form clientid (d) and a * new setclientid_confirm verifier (t). (Note * the spec calls the values e and r for CASE * 4). */ LogFullDebug(COMPONENT_CLIENTID, "Replace ClientId %" PRIx64 "->%s", conf->cid_clientid, str_client); clientid = new_clientid(); new_clientid_verifier(verifier); } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); } else { /* CASE 5: * * * Remove any existing unconfirmed record. * * Return a new short form clientid (d) and a new * setclientid_confirm verifier (t). */ LogFullDebug(COMPONENT_CLIENTID, "New client"); clientid = new_clientid(); new_clientid_verifier(verifier); } /* 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) { /* Delete the unconfirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid record (whether we raced with a * SETCLIENTID_CONFIRM or the reaper thread (if either * of those operations had won the race, * cr_punconfirmed_id would have been NULL). */ if (isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(unconf, str); LogDebug(COMPONENT_CLIENTID, "Replacing %s", str); } /* unhash the clientid record */ remove_unconfirmed_client_id(unconf); unconf = NULL; } /* Now we can proceed to build the new unconfirmed record. We * have determined the clientid and setclientid_confirm values * above. */ unconf = create_client_id(clientid, client_record, &client_addr, &data->credential, 0); if (unconf == NULL) { /* Error already logged, return */ res_SETCLIENTID4->status = NFS4ERR_RESOURCE; goto out; } if (strmaxcpy(unconf->cid_cb.v40.cb_client_r_addr, arg_SETCLIENTID4->callback.cb_location.r_addr, sizeof(unconf->cid_cb.v40.cb_client_r_addr)) == -1) { LogCrit(COMPONENT_CLIENTID, "Callback r_addr %s too long", arg_SETCLIENTID4->callback.cb_location.r_addr); res_SETCLIENTID4->status = NFS4ERR_INVAL; goto out; } nfs_set_client_location(unconf, &arg_SETCLIENTID4->callback.cb_location); memcpy(unconf->cid_incoming_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); memcpy(unconf->cid_verifier, verifier, sizeof(NFS4_write_verifier)); unconf->cid_cb.v40.cb_program = arg_SETCLIENTID4->callback.cb_program; unconf->cid_cb.v40.cb_callback_ident = arg_SETCLIENTID4->callback_ident; rc = nfs_client_id_insert(unconf); if (rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_SETCLIENTID4->status = clientid_error_to_nfsstat(rc); goto out; } if (isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; sprint_mem(str_verifier, verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(unconf, str); LogDebug(COMPONENT_CLIENTID, "SETCLIENTID reply Verifier=%s %s", str_verifier, str); } res_SETCLIENTID4->status = NFS4_OK; res_SETCLIENTID4->SETCLIENTID4res_u.resok4.clientid = clientid; memcpy(res_SETCLIENTID4->SETCLIENTID4res_u.resok4.setclientid_confirm, &verifier, NFS4_VERIFIER_SIZE); out: pthread_mutex_unlock(&client_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(client_record); return res_SETCLIENTID4->status; }
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_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; }
/** * @brief Display NFSv4 owner * * @param[in] owner The state owner * @param[out] str Output string * * @return the bytes remaining in the buffer. */ int display_nfs4_owner(struct display_buffer *dspbuf, state_owner_t *owner) { int b_left; if (owner == NULL) return display_cat(dspbuf, "<NULL>"); b_left = display_printf(dspbuf, "%s %p:", state_owner_type_to_str(owner->so_type), owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " clientid={"); if (b_left <= 0) return b_left; b_left = display_client_id_rec(dspbuf, owner->so_owner.so_nfs4_owner .so_clientrec); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "} owner="); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, owner->so_owner_val, owner->so_owner_len); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " confirmed=%u seqid=%u", owner->so_owner.so_nfs4_owner.so_confirmed, owner->so_owner.so_nfs4_owner.so_seqid); if (b_left <= 0) return b_left; if (owner->so_owner.so_nfs4_owner.so_related_owner != NULL) { b_left = display_printf(dspbuf, " related_owner={"); if (b_left <= 0) return b_left; b_left = display_nfs4_owner(dspbuf, owner->so_owner .so_nfs4_owner.so_related_owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "}"); if (b_left <= 0) return b_left; } return display_printf(dspbuf, " refcount=%d", atomic_fetch_int32_t(&owner->so_refcount)); }
int nfs4_op_lock(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Shorter alias for arguments */ LOCK4args * const arg_LOCK4 = &op->nfs_argop4_u.oplock; /* Shorter alias for response */ LOCK4res * const res_LOCK4 = &resp->nfs_resop4_u.oplock; /* Status code from state calls */ state_status_t state_status = STATE_SUCCESS; /* Data for lock state to be created */ union state_data candidate_data; /* Status code for protocol functions */ nfsstat4 nfs_status = 0; /* Created or found lock state */ state_t *lock_state = NULL; /* Associated open state */ state_t *state_open = NULL; /* The lock owner */ state_owner_t *lock_owner = NULL; /* The open owner */ state_owner_t *open_owner = NULL; /* The owner of a conflicting lock */ state_owner_t *conflict_owner = NULL; /* The owner in which to store the response for NFSv4.0 */ state_owner_t *resp_owner = NULL; /* Sequence ID, for NFSv4.0 */ seqid4 seqid = 0; /* The client performing these operations */ nfs_client_id_t *clientid = NULL; /* Name for the lock owner */ state_nfs4_owner_name_t owner_name; /* Description of requrested lock */ fsal_lock_param_t lock_desc; /* Description of conflicting lock */ fsal_lock_param_t conflict_desc; /* Whether to block */ state_blocking_t blocking = STATE_NON_BLOCKING; /* Tracking data for the lock state */ struct state_refer refer; /* Indicate if we let FSAL to handle requests during grace. */ bool_t fsal_grace = false; int rc; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCK handler ----------------------"); /* Initialize to sane starting values */ resp->resop = NFS4_OP_LOCK; res_LOCK4->status = NFS4_OK; /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; } res_LOCK4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_LOCK4->status != NFS4_OK) return res_LOCK4->status; /* Convert lock parameters to internal types */ switch (arg_LOCK4->locktype) { case READW_LT: blocking = STATE_NFSV4_BLOCKING; /* Fall through */ case READ_LT: lock_desc.lock_type = FSAL_LOCK_R; break; case WRITEW_LT: blocking = STATE_NFSV4_BLOCKING; /* Fall through */ case WRITE_LT: lock_desc.lock_type = FSAL_LOCK_W; break; default: LogDebug(COMPONENT_NFS_V4_LOCK, "Invalid lock type"); res_LOCK4->status = NFS4ERR_INVAL; return res_LOCK4->status; } lock_desc.lock_start = arg_LOCK4->offset; lock_desc.lock_sle_type = FSAL_POSIX_LOCK; lock_desc.lock_reclaim = arg_LOCK4->reclaim; if (arg_LOCK4->length != STATE_LOCK_OFFSET_EOF) lock_desc.lock_length = arg_LOCK4->length; else lock_desc.lock_length = 0; if (arg_LOCK4->locker.new_lock_owner) { /* Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid( &arg_LOCK4->locker.locker4_u.open_owner.open_stateid, data->current_obj, &state_open, data, STATEID_SPECIAL_FOR_LOCK, arg_LOCK4->locker.locker4_u.open_owner.open_seqid, data->minorversion == 0, lock_tag); if (nfs_status != NFS4_OK) { if (nfs_status == NFS4ERR_REPLAY) { open_owner = get_state_owner_ref(state_open); LogStateOwner("Open: ", open_owner); if (open_owner != NULL) { resp_owner = open_owner; seqid = arg_LOCK4->locker.locker4_u .open_owner.open_seqid; goto check_seqid; } } res_LOCK4->status = nfs_status; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid for open owner"); return res_LOCK4->status; } open_owner = get_state_owner_ref(state_open); LogStateOwner("Open: ", open_owner); if (open_owner == NULL) { /* State is going stale. */ res_LOCK4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid, stale open owner"); goto out2; } lock_state = NULL; lock_owner = NULL; resp_owner = open_owner; seqid = arg_LOCK4->locker.locker4_u.open_owner.open_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK New lock owner from open owner", data->current_obj, open_owner, &lock_desc); /* Check is the clientid is known or not */ rc = nfs_client_id_get_confirmed( data->minorversion == 0 ? arg_LOCK4->locker. locker4_u.open_owner.lock_owner.clientid : data->session->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { res_LOCK4->status = clientid_error_to_nfsstat(rc); LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs_client_id_get"); goto out2; } if (isDebug(COMPONENT_CLIENTID) && (clientid != open_owner->so_owner.so_nfs4_owner.so_clientrec)) { char str_open[LOG_BUFF_LEN / 2]; struct display_buffer dspbuf_open = { sizeof(str_open), str_open, str_open}; char str_lock[LOG_BUFF_LEN / 2]; struct display_buffer dspbuf_lock = { sizeof(str_lock), str_lock, str_lock}; display_client_id_rec(&dspbuf_open, open_owner->so_owner .so_nfs4_owner.so_clientrec); display_client_id_rec(&dspbuf_lock, clientid); LogDebug(COMPONENT_CLIENTID, "Unexpected, new lock owner clientid {%s} doesn't match open owner clientid {%s}", str_lock, str_open); } /* The related stateid is already stored in state_open */ /* An open state has been found. Check its type */ if (state_open->state_type != STATE_TYPE_SHARE) { res_LOCK4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed open stateid is not a SHARE"); goto out2; } /* Is this lock_owner known ? */ convert_nfs4_lock_owner(&arg_LOCK4->locker.locker4_u.open_owner. lock_owner, &owner_name); LogStateOwner("Lock: ", lock_owner); } else { /* Existing lock owner Find the lock stateid From * that, get the open_owner * * There was code here before to handle all-0 stateid, * but that really doesn't apply - when we handle * temporary locks for I/O operations (which is where * we will see all-0 or all-1 stateid, those will not * come in through nfs4_op_lock. * * Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid( &arg_LOCK4->locker.locker4_u.lock_owner.lock_stateid, data->current_obj, &lock_state, data, STATEID_SPECIAL_FOR_LOCK, arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid, data->minorversion == 0, lock_tag); if (nfs_status != NFS4_OK) { if (nfs_status == NFS4ERR_REPLAY) { lock_owner = get_state_owner_ref(lock_state); LogStateOwner("Lock: ", lock_owner); if (lock_owner != NULL) { open_owner = lock_owner->so_owner .so_nfs4_owner.so_related_owner; inc_state_owner_ref(open_owner); resp_owner = lock_owner; seqid = arg_LOCK4->locker.locker4_u .lock_owner.lock_seqid; goto check_seqid; } } res_LOCK4->status = nfs_status; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid for existing lock owner"); return res_LOCK4->status; } /* Check if lock state belongs to same export */ if (!state_same_export(lock_state, op_ctx->ctx_export)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %" PRIu16" request for export %"PRIu16, state_export_id(lock_state), op_ctx->ctx_export->export_id); res_LOCK4->status = NFS4ERR_INVAL; goto out2; } /* A lock state has been found. Check its type */ if (lock_state->state_type != STATE_TYPE_LOCK) { res_LOCK4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed existing lock owner, state type is not LOCK"); goto out2; } /* Get the old lockowner. We can do the following * 'cast', in NFSv4 lock_owner4 and open_owner4 are * different types but with the same definition */ lock_owner = get_state_owner_ref(lock_state); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { /* State is going stale. */ res_LOCK4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid, stale open owner"); goto out2; } open_owner = lock_owner->so_owner.so_nfs4_owner.so_related_owner; LogStateOwner("Open: ", open_owner); inc_state_owner_ref(open_owner); state_open = lock_state->state_data.lock.openstate; inc_state_t_ref(state_open); resp_owner = lock_owner; seqid = arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK Existing lock owner", data->current_obj, lock_owner, &lock_desc); /* Get the client for this open owner */ clientid = open_owner->so_owner.so_nfs4_owner.so_clientrec; inc_client_id_ref(clientid); } check_seqid: /* Check seqid (lock_seqid or open_seqid) */ if (data->minorversion == 0) { if (!Check_nfs4_seqid(resp_owner, seqid, op, data->current_obj, resp, lock_tag)) { /* Response is all setup for us and LogDebug * told what was wrong */ goto out2; } } /* Lock length should not be 0 */ if (arg_LOCK4->length == 0LL) { res_LOCK4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length == 0"); goto out; } /* Check for range overflow. Comparing beyond 2^64 is not * possible int 64 bits precision, but off+len > 2^64-1 is * equivalent to len > 2^64-1 - off */ if (lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) { res_LOCK4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length overflow"); goto out; } /* Check if open state has correct access for type of lock. * * Don't need to check for conflicting states since this open * state assures there are no conflicting states. */ if (((arg_LOCK4->locktype == WRITE_LT || arg_LOCK4->locktype == WRITEW_LT) && ((state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0)) || ((arg_LOCK4->locktype == READ_LT || arg_LOCK4->locktype == READW_LT) && ((state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0))) { /* The open state doesn't allow access based on the * type of lock */ LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, SHARE doesn't allow access", data->current_obj, lock_owner, &lock_desc); res_LOCK4->status = NFS4ERR_OPENMODE; goto out; } /* Do grace period checking (use resp_owner below since a new * lock request with a new lock owner doesn't have a lock owner * yet, but does have an open owner - resp_owner is always one or * the other and non-NULL at this point - so makes for a better log). */ if (nfs_in_grace()) { if (op_ctx->fsal_export->exp_ops. fs_supports(op_ctx->fsal_export, fso_grace_method)) fsal_grace = true; if (!fsal_grace && !arg_LOCK4->reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, non-reclaim while in grace", data->current_obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_GRACE; goto out; } if (!fsal_grace && arg_LOCK4->reclaim && !clientid->cid_allow_reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, invalid reclaim while in grace", data->current_obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_NO_GRACE; goto out; } } else { if (arg_LOCK4->reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, reclaim while not in grace", data->current_obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_NO_GRACE; goto out; } } /* Test if this request is attempting to create a new lock owner */ if (arg_LOCK4->locker.new_lock_owner) { bool_t isnew; /* A lock owner is always associated with a previously made open which has itself a previously made stateid */ /* This lock owner is not known yet, allocated and set up a new one */ lock_owner = create_nfs4_owner(&owner_name, clientid, STATE_LOCK_OWNER_NFSV4, open_owner, 0, &isnew, CARE_ALWAYS); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { res_LOCK4->status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_EVENT, "LOCK failed to create new lock owner", data->current_obj, open_owner, &lock_desc); goto out2; } if (!isnew) { PTHREAD_MUTEX_lock(&lock_owner->so_mutex); /* Check lock_seqid if it has attached locks. */ if (!glist_empty(&lock_owner->so_lock_list) && (data->minorversion == 0) && !Check_nfs4_seqid(lock_owner, arg_LOCK4->locker.locker4_u. open_owner.lock_seqid, op, data->current_obj, resp, lock_tag)) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to create new lock owner, re-use", data->current_obj, open_owner, &lock_desc); dump_all_locks( "All locks (re-use of lock owner)"); PTHREAD_MUTEX_unlock(&lock_owner->so_mutex); /* Response is all setup for us and * LogDebug told what was wrong */ goto out2; } PTHREAD_MUTEX_unlock(&lock_owner->so_mutex); /* Lock owner is known, see if we also already have * a stateid. Do this here since it's impossible for * there to be such a state if the lock owner was * previously unknown. */ lock_state = nfs4_State_Get_Obj(data->current_obj, lock_owner); } if (lock_state == NULL) { /* Prepare state management structure */ memset(&candidate_data, 0, sizeof(candidate_data)); candidate_data.lock.openstate = state_open; /* Add the lock state to the lock table */ state_status = state_add(data->current_obj, STATE_TYPE_LOCK, &candidate_data, lock_owner, &lock_state, data->minorversion > 0 ? &refer : NULL); if (state_status != STATE_SUCCESS) { res_LOCK4->status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to add new stateid", data->current_obj, lock_owner, &lock_desc); goto out2; } glist_init(&lock_state->state_data.lock.state_locklist); /* Add lock state to the list of lock states belonging to the open state */ glist_add_tail( &state_open->state_data.share.share_lockstates, &lock_state->state_data.lock.state_sharelist); } } if (data->minorversion == 0) { op_ctx->clientid = &lock_owner->so_owner.so_nfs4_owner.so_clientid; } /* Now we have a lock owner and a stateid. Go ahead and push * lock into SAL (and FSAL). */ state_status = state_lock(data->current_obj, lock_owner, lock_state, blocking, NULL, /* No block data for now */ &lock_desc, &conflict_owner, &conflict_desc); if (state_status != STATE_SUCCESS) { if (state_status == STATE_LOCK_CONFLICT) { /* A conflicting lock from a different lock_owner, returns NFS4ERR_DENIED */ Process_nfs4_conflict(&res_LOCK4->LOCK4res_u.denied, conflict_owner, &conflict_desc); } LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed with status %s", state_err_str(state_status)); res_LOCK4->status = nfs4_Errno_state(state_status); /* Save the response in the lock or open owner */ if (res_LOCK4->status != NFS4ERR_RESOURCE && res_LOCK4->status != NFS4ERR_BAD_STATEID && data->minorversion == 0) { Copy_nfs4_state_req(resp_owner, seqid, op, data->current_obj, resp, lock_tag); } if (arg_LOCK4->locker.new_lock_owner) { /* Need to destroy new state */ state_del(lock_state); } goto out2; } if (data->minorversion == 0) op_ctx->clientid = NULL; res_LOCK4->status = NFS4_OK; /* Handle stateid/seqid for success */ update_stateid(lock_state, &res_LOCK4->LOCK4res_u.resok4.lock_stateid, data, lock_tag); if (arg_LOCK4->locker.new_lock_owner) { /* Also save the response in the lock owner */ Copy_nfs4_state_req(lock_owner, arg_LOCK4->locker.locker4_u.open_owner. lock_seqid, op, data->current_obj, resp, lock_tag); } if (isFullDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN]; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, lock_state); LogFullDebug(COMPONENT_NFS_V4_LOCK, "LOCK stateid %s", str); } LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK applied", data->current_obj, lock_owner, &lock_desc); out: if (data->minorversion == 0) { /* Save the response in the lock or open owner */ Copy_nfs4_state_req(resp_owner, seqid, op, data->current_obj, resp, lock_tag); } out2: if (state_open != NULL) dec_state_t_ref(state_open); if (lock_state != NULL) dec_state_t_ref(lock_state); LogStateOwner("Open: ", open_owner); LogStateOwner("Lock: ", lock_owner); if (open_owner != NULL) dec_state_owner_ref(open_owner); if (lock_owner != NULL) dec_state_owner_ref(lock_owner); if (clientid != NULL) dec_client_id_ref(clientid); return res_LOCK4->status; } /* nfs4_op_lock */
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); }
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); }
int nfs4_op_destroy_clientid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { DESTROY_CLIENTID4args * const arg_DESTROY_CLIENTID4 = &op->nfs_argop4_u.opdestroy_clientid; DESTROY_CLIENTID4res * const res_DESTROY_CLIENTID4 = &resp->nfs_resop4_u.opdestroy_clientid; nfs_client_record_t *client_record = NULL; nfs_client_id_t *conf = NULL, *unconf = NULL, *found = NULL; clientid4 clientid; int rc; resp->resop = NFS4_OP_DESTROY_CLIENTID; clientid = arg_DESTROY_CLIENTID4->dca_clientid; if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN]; struct display_buffer dspbuf = {sizeof(str), str, str}; display_clientid(&dspbuf, clientid); LogDebug(COMPONENT_CLIENTID, "DESTROY_CLIENTID clientid=%s", str); } res_DESTROY_CLIENTID4->dcr_status = NFS4_OK; /* First try to look up confirmed record */ rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc == CLIENT_ID_SUCCESS) { client_record = conf->cid_client_record; found = conf; } else { /* fall back to unconfirmed */ rc = nfs_client_id_get_unconfirmed(clientid, &unconf); if (rc == CLIENT_ID_SUCCESS) { client_record = unconf->cid_client_record; found = unconf; } /* handle the perverse case of a clientid being confirmed * in the above interval */ rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc == CLIENT_ID_SUCCESS) { if (found != NULL) dec_client_id_ref(found); client_record = conf->cid_client_record; found = conf; } } /* ref +1 */ if (client_record == NULL) { /* Fine. We're done. */ res_DESTROY_CLIENTID4->dcr_status = NFS4ERR_STALE_CLIENTID; goto out; } (void) inc_client_record_ref(client_record); PTHREAD_MUTEX_lock(&client_record->cr_mutex); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN]; 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); } /* per Frank, we must check the confirmed and unconfirmed * state of client_record again now that we hold cr_mutex */ conf = client_record->cr_confirmed_rec; unconf = client_record->cr_unconfirmed_rec; if ((!conf) && (!unconf)) { /* We raced a thread destroying clientid, and lost. * We're done. */ goto cleanup; } if (conf) { /* We MUST NOT destroy a clientid that has nfsv41 sessions or * state. Since the minorversion is 4.1 or higher, this is * equivalent to a session check. */ PTHREAD_MUTEX_lock(&conf->cid_mutex); if (!glist_empty(&conf->cid_cb.v41.cb_session_list)) { res_DESTROY_CLIENTID4->dcr_status = NFS4ERR_CLIENTID_BUSY; PTHREAD_MUTEX_unlock(&conf->cid_mutex); goto cleanup; } PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* Delete the confirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid record. */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN]; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Removing confirmed clientid %s", str); } /* unhash the clientid record */ (void)remove_confirmed_client_id(conf); } if (unconf) { /* Delete the unconfirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid record. */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN]; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Removing unconfirmed clientid %s", str); } /* unhash the clientid record */ (void)remove_unconfirmed_client_id(unconf); } cleanup: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); dec_client_record_ref(client_record); /* ref +0 */ if (found != NULL) dec_client_id_ref(found); out: return res_DESTROY_CLIENTID4->dcr_status; }
int nfs4_op_setclientid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETCLIENTID4args * const arg_SETCLIENTID4 = &op->nfs_argop4_u.opsetclientid; SETCLIENTID4res * const res_SETCLIENTID4 = &resp->nfs_resop4_u.opsetclientid; clientaddr4 * const res_SETCLIENTID4_INUSE = &resp->nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.client_using; char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; struct display_buffer dspbuf_verifier = { sizeof(str_verifier), str_verifier, str_verifier}; char str_client[NFS4_OPAQUE_LIMIT * 2 + 1]; struct display_buffer dspbuf_client = { sizeof(str_client), str_client, str_client}; const char *str_client_addr = "(unknown)"; /* 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}; nfs_client_record_t *client_record; nfs_client_id_t *conf; nfs_client_id_t *unconf; clientid4 clientid; verifier4 verifier; int rc; resp->resop = NFS4_OP_SETCLIENTID; if (data->minorversion > 0) { res_SETCLIENTID4->status = NFS4ERR_NOTSUPP; return res_SETCLIENTID4->status; } if (op_ctx->client != NULL) str_client_addr = op_ctx->client->hostaddr_str; if (isDebug(COMPONENT_CLIENTID)) { display_opaque_value(&dspbuf_client, arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len); display_opaque_bytes(&dspbuf_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); } else { str_client[0] = '\0'; str_verifier[0] = '\0'; } LogDebug(COMPONENT_CLIENTID, "SETCLIENTID Client addr=%s id=%s verf=%s callback={program=%u r_addr=%s r_netid=%s} ident=%u", str_client_addr, str_client, str_verifier, arg_SETCLIENTID4->callback.cb_program, arg_SETCLIENTID4->callback.cb_location.r_addr, arg_SETCLIENTID4->callback.cb_location.r_netid, arg_SETCLIENTID4->callback_ident); /* Do we already have one or more records for client id (x)? */ client_record = get_client_record(arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len, 0, 0); if (client_record == NULL) { /* Some major failure */ LogCrit(COMPONENT_CLIENTID, "SETCLIENTID failed"); res_SETCLIENTID4->status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4->status; } /* The following checks are based on RFC3530bis draft 16 * * This attempts to implement the logic described in * 15.35.5. IMPLEMENTATION Consider the major bullets as CASE * 1, CASE 2, CASE 3, CASE 4, and CASE 5. */ PTHREAD_MUTEX_lock(&client_record->cr_mutex); 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); } conf = client_record->cr_confirmed_rec; if (conf != NULL) { bool credmatch; /* Need a reference to the confirmed record for below */ inc_client_id_ref(conf); clientid = conf->cid_clientid; display_clientid(&dspbuf_clientid4, clientid); credmatch = nfs_compare_clientcred(&conf->cid_credential, &data->credential) && op_ctx->client != NULL && conf->gsh_client != NULL && op_ctx->client == conf->gsh_client; /* Only reject if the principal doesn't match and the * clientid has live state associated. Per RFC 7530 * Section 9.1.2. Server Release of Client ID. */ if (!credmatch && clientid_has_state(conf)) { /* CASE 1: * * Confirmed record exists and not the same principal */ 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': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client, confirmed_addr); } res_SETCLIENTID4->status = NFS4ERR_CLID_INUSE; res_SETCLIENTID4_INUSE->r_netid = (char *)netid_nc_table[conf->cid_cb.v40.cb_addr.nc] .netid; res_SETCLIENTID4_INUSE->r_addr = gsh_strdup(conf->cid_cb.v40.cb_client_r_addr); /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } /* Check if confirmed record is for (v, x, c, l, s) */ if (credmatch && memcmp(arg_SETCLIENTID4->client.verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2: * * A confirmed record exists for this long * form client id and verifier. * * Consider this to be a possible update of * the call-back information. * * Remove any pre-existing unconfirmed record * for (v, x, c). * * Return the same short form client id (c), * but a new setclientid_confirm verifier (t). */ LogFullDebug(COMPONENT_CLIENTID, "Update ClientId %s->%s", str_clientid4, str_client); new_clientid_verifier(verifier); } else { /* Must be CASE 3 or CASE 4 * * Confirmed record is for (u, x, c, l, s). * * These are actually the same, doesn't really * matter if an unconfirmed record exists or * not. Any existing unconfirmed record will * be removed and a new unconfirmed record * added. * * Return a new short form clientid (d) and a * new setclientid_confirm verifier (t). (Note * the spec calls the values e and r for CASE * 4). */ LogFullDebug(COMPONENT_CLIENTID, "Replace ClientId %s->%s", str_clientid4, str_client); clientid = new_clientid(); new_clientid_verifier(verifier); } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); } else { /* CASE 5: * * * Remove any existing unconfirmed record. * * Return a new short form clientid (d) and a new * setclientid_confirm verifier (t). */ clientid = new_clientid(); display_clientid(&dspbuf_clientid4, clientid); LogFullDebug(COMPONENT_CLIENTID, "New client %s", str_clientid4); new_clientid_verifier(verifier); } /* 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) { /* Delete the unconfirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid record (whether we raced with a * SETCLIENTID_CONFIRM or the reaper thread (if either * of those operations had won the race, * cr_punconfirmed_id would have been NULL). */ 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, "Replacing %s", str); } /* unhash the clientid record */ remove_unconfirmed_client_id(unconf); unconf = NULL; } /* Now we can proceed to build the new unconfirmed record. We * have determined the clientid and setclientid_confirm values * above. */ unconf = create_client_id(clientid, client_record, &data->credential, 0); if (unconf == NULL) { /* Error already logged, return */ res_SETCLIENTID4->status = NFS4ERR_RESOURCE; goto out; } if (strmaxcpy(unconf->cid_cb.v40.cb_client_r_addr, arg_SETCLIENTID4->callback.cb_location.r_addr, sizeof(unconf->cid_cb.v40.cb_client_r_addr)) == -1) { LogCrit(COMPONENT_CLIENTID, "Callback r_addr %s too long", arg_SETCLIENTID4->callback.cb_location.r_addr); res_SETCLIENTID4->status = NFS4ERR_INVAL; goto out; } nfs_set_client_location(unconf, &arg_SETCLIENTID4->callback.cb_location); memcpy(unconf->cid_incoming_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); memcpy(unconf->cid_verifier, verifier, sizeof(NFS4_write_verifier)); unconf->cid_cb.v40.cb_program = arg_SETCLIENTID4->callback.cb_program; unconf->cid_cb.v40.cb_callback_ident = arg_SETCLIENTID4->callback_ident; rc = nfs_client_id_insert(unconf); if (rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_SETCLIENTID4->status = clientid_error_to_nfsstat_no_expire(rc); goto out; } if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; sprint_mem(str_verifier, verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "SETCLIENTID reply Verifier=%s %s", str_verifier, str); } res_SETCLIENTID4->status = NFS4_OK; res_SETCLIENTID4->SETCLIENTID4res_u.resok4.clientid = clientid; memcpy(res_SETCLIENTID4->SETCLIENTID4res_u.resok4.setclientid_confirm, &verifier, NFS4_VERIFIER_SIZE); out: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(client_record); return res_SETCLIENTID4->status; }
/** * @brief Display NFSv4 owner * * @param[in] owner The state owner * @param[out] str Output string * * @return the bytes remaining in the buffer. */ int display_nfs4_owner(struct display_buffer *dspbuf, state_owner_t *owner) { int b_left; time_t texpire; struct state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; if (owner == NULL) return display_cat(dspbuf, "<NULL>"); b_left = display_printf(dspbuf, "%s %p:", state_owner_type_to_str(owner->so_type), owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " clientid={"); if (b_left <= 0) return b_left; b_left = display_client_id_rec(dspbuf, nfs4_owner->so_clientrec); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "} owner="); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, owner->so_owner_val, owner->so_owner_len); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " confirmed=%u seqid=%u", nfs4_owner->so_confirmed, nfs4_owner->so_seqid); if (b_left <= 0) return b_left; if (nfs4_owner->so_related_owner != NULL) { b_left = display_printf(dspbuf, " related_owner={"); if (b_left <= 0) return b_left; b_left = display_nfs4_owner(dspbuf, nfs4_owner->so_related_owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "}"); if (b_left <= 0) return b_left; } texpire = atomic_fetch_time_t(&nfs4_owner->so_cache_expire); if (texpire != 0) { b_left = display_printf(dspbuf, " cached(expires in %d secs)", texpire - time(NULL)); if (b_left <= 0) return b_left; } return display_printf(dspbuf, " refcount=%d", atomic_fetch_int32_t(&owner->so_refcount)); }