int nfs4_op_renew(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RENEW4args * const arg_RENEW4 = &op->nfs_argop4_u.oprenew; RENEW4res * const res_RENEW4 = &resp->nfs_resop4_u.oprenew; nfs_client_id_t *clientid; int rc; /* Lock are not supported */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_RENEW; if (data->minorversion > 0) { res_RENEW4->status = NFS4ERR_NOTSUPP; return res_RENEW4->status; } /* Tell the admin what I am doing... */ LogFullDebug(COMPONENT_CLIENTID, "RENEW Client id = %" PRIx64, arg_RENEW4->clientid); /* Is this an existing client id ? */ rc = nfs_client_id_get_confirmed(arg_RENEW4->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { /* Unknown client id */ res_RENEW4->status = clientid_error_to_nfsstat(rc); return res_RENEW4->status; } PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (!reserve_lease(clientid)) { res_RENEW4->status = NFS4ERR_EXPIRED; } else { update_lease(clientid); /* update the lease, check the state of callback * path and return correct error */ if (nfs_param.nfsv4_param.allow_delegations && get_cb_chan_down(clientid) && clientid->curr_deleg_grants) { res_RENEW4->status = NFS4ERR_CB_PATH_DOWN; /* Set the time for first PATH_DOWN response */ if (clientid->first_path_down_resp_time == 0) clientid->first_path_down_resp_time = time(NULL); } else { res_RENEW4->status = NFS4_OK; /* Reset */ clientid->first_path_down_resp_time = 0; } } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); dec_client_id_ref(clientid); return res_RENEW4->status; } /* nfs4_op_renew */
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 nfs4_op_release_lockowner(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RELEASE_LOCKOWNER4args * const arg_RELEASE_LOCKOWNER4 = &op->nfs_argop4_u.oprelease_lockowner; RELEASE_LOCKOWNER4res * const res_RELEASE_LOCKOWNER4 = &resp->nfs_resop4_u.oprelease_lockowner; nfs_client_id_t *nfs_client_id; state_owner_t *lock_owner; state_nfs4_owner_name_t owner_name; int rc; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 RELEASE_LOCKOWNER handler ----------------------"); resp->resop = NFS4_OP_RELEASE_LOCKOWNER; res_RELEASE_LOCKOWNER4->status = NFS4_OK; if (data->minorversion > 0) { res_RELEASE_LOCKOWNER4->status = NFS4ERR_NOTSUPP; return res_RELEASE_LOCKOWNER4->status; } /* Check clientid */ rc = nfs_client_id_get_confirmed(arg_RELEASE_LOCKOWNER4->lock_owner. clientid, &nfs_client_id); if (rc != CLIENT_ID_SUCCESS) { res_RELEASE_LOCKOWNER4->status = clientid_error_to_nfsstat(rc); goto out2; } PTHREAD_MUTEX_lock(&nfs_client_id->cid_mutex); if (!reserve_lease(nfs_client_id)) { PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); dec_client_id_ref(nfs_client_id); res_RELEASE_LOCKOWNER4->status = NFS4ERR_EXPIRED; goto out2; } PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); /* look up the lock owner and see if we can find it */ convert_nfs4_lock_owner(&arg_RELEASE_LOCKOWNER4->lock_owner, &owner_name); /* If this lock owner is not known yet, allocated * and set up a new one */ lock_owner = create_nfs4_owner(&owner_name, nfs_client_id, STATE_LOCK_OWNER_NFSV4, NULL, 0, NULL, CARE_NOT); if (lock_owner == NULL) { /* the owner doesn't exist, we are done */ LogDebug(COMPONENT_NFS_V4_LOCK, "lock owner does not exist"); res_RELEASE_LOCKOWNER4->status = NFS4_OK; goto out1; } res_RELEASE_LOCKOWNER4->status = release_lock_owner(lock_owner); /* Release the reference to the lock owner acquired * via create_nfs4_owner */ dec_state_owner_ref(lock_owner); out1: /* Update the lease before exit */ PTHREAD_MUTEX_lock(&nfs_client_id->cid_mutex); update_lease(nfs_client_id); PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); dec_client_id_ref(nfs_client_id); out2: LogDebug(COMPONENT_NFS_V4_LOCK, "Leaving NFS v4 RELEASE_LOCKOWNER handler -----------------------"); return res_RELEASE_LOCKOWNER4->status; } /* nfs4_op_release_lock_owner */
int nfs4_op_lockt(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Alias for arguments */ LOCKT4args * const arg_LOCKT4 = &op->nfs_argop4_u.oplockt; /* Alias for response */ LOCKT4res * const res_LOCKT4 = &resp->nfs_resop4_u.oplockt; /* Return code from state calls */ state_status_t state_status = STATE_SUCCESS; /* Client id record */ nfs_client_id_t *clientid = NULL; /* Lock owner name */ state_nfs4_owner_name_t owner_name; /* Lock owner record */ state_owner_t *lock_owner = NULL; /* Owner of conflicting lock */ state_owner_t *conflict_owner = NULL; /* Description of lock to test */ fsal_lock_param_t lock_desc = { FSAL_NO_LOCK, 0, 0 }; /* Description of conflicting lock */ fsal_lock_param_t conflict_desc; /* return code from id confirm calls */ int rc; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCKT handler ----------------------------"); /* Initialize to sane default */ resp->resop = NFS4_OP_LOCKT; res_LOCKT4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_LOCKT4->status != NFS4_OK) return res_LOCKT4->status; /* Lock length should not be 0 */ if (arg_LOCKT4->length == 0LL) { res_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } if (nfs_in_grace()) { res_LOCKT4->status = NFS4ERR_GRACE; return res_LOCKT4->status; } /* Convert lock parameters to internal types */ switch (arg_LOCKT4->locktype) { case READ_LT: case READW_LT: lock_desc.lock_type = FSAL_LOCK_R; break; case WRITE_LT: case WRITEW_LT: lock_desc.lock_type = FSAL_LOCK_W; break; default: LogDebug(COMPONENT_NFS_V4_LOCK, "Invalid lock type"); res_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } lock_desc.lock_start = arg_LOCKT4->offset; if (arg_LOCKT4->length != STATE_LOCK_OFFSET_EOF) lock_desc.lock_length = arg_LOCKT4->length; else lock_desc.lock_length = 0; /* Check for range overflow. Comparing beyond 2^64 is not * possible in 64 bit 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_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } /* Check clientid */ rc = nfs_client_id_get_confirmed(data->minorversion == 0 ? arg_LOCKT4->owner.clientid : data->session->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { res_LOCKT4->status = clientid_error_to_nfsstat(rc); return res_LOCKT4->status; } PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (data->minorversion == 0 && !reserve_lease(clientid)) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); dec_client_id_ref(clientid); res_LOCKT4->status = NFS4ERR_EXPIRED; return res_LOCKT4->status; } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* Is this lock_owner known ? */ convert_nfs4_lock_owner(&arg_LOCKT4->owner, &owner_name); /* 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, NULL, 0, NULL, CARE_ALWAYS); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { LogEvent(COMPONENT_NFS_V4_LOCK, "LOCKT unable to create lock owner"); res_LOCKT4->status = NFS4ERR_SERVERFAULT; goto out; } LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCKT", data->current_entry, lock_owner, &lock_desc); 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 test * the lock in SAL (and FSAL). */ state_status = state_test(data->current_entry, lock_owner, &lock_desc, &conflict_owner, &conflict_desc); if (state_status == STATE_LOCK_CONFLICT) { /* A conflicting lock from a different lock_owner, * returns NFS4ERR_DENIED */ LogStateOwner("Conflict: ", conflict_owner); Process_nfs4_conflict(&res_LOCKT4->LOCKT4res_u.denied, conflict_owner, &conflict_desc); } if (data->minorversion == 0) op_ctx->clientid = NULL; /* Release NFS4 Open Owner reference */ dec_state_owner_ref(lock_owner); /* Return result */ res_LOCKT4->status = nfs4_Errno_state(state_status); out: /* Update the lease before exit */ if (data->minorversion == 0) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); update_lease(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } dec_client_id_ref(clientid); return res_LOCKT4->status; } /* nfs4_op_lockt */
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_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_release_lockowner(struct nfs_argop4 * op, compound_data_t * data, struct nfs_resop4 * resp) { nfs_client_id_t * pnfs_client_id; state_owner_t * plock_owner; state_nfs4_owner_name_t owner_name; int rc; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 RELEASE_LOCKOWNER handler -----------------------------------------------------"); resp->resop = NFS4_OP_RELEASE_LOCKOWNER; res_RELEASE_LOCKOWNER4.status = NFS4_OK; /* Check clientid */ rc = nfs_client_id_get_confirmed(arg_RELEASE_LOCKOWNER4.lock_owner.clientid, &pnfs_client_id); if(rc != CLIENT_ID_SUCCESS) { res_RELEASE_LOCKOWNER4.status = clientid_error_to_nfsstat(rc); goto out2; } P(pnfs_client_id->cid_mutex); if(!reserve_lease(pnfs_client_id)) { V(pnfs_client_id->cid_mutex); dec_client_id_ref(pnfs_client_id); res_RELEASE_LOCKOWNER4.status = NFS4ERR_EXPIRED; goto out2; } V(pnfs_client_id->cid_mutex); /* look up the lock owner and see if we can find it */ convert_nfs4_lock_owner(&arg_RELEASE_LOCKOWNER4.lock_owner, &owner_name); /* If this open owner is not known yet, allocated and set up a new one */ plock_owner = create_nfs4_owner(&owner_name, pnfs_client_id, STATE_OPEN_OWNER_NFSV4, NULL, 0, NULL, CARE_NOT); if(plock_owner == NULL) { /* the owner doesn't exist, we are done */ LogDebug(COMPONENT_NFS_V4_LOCK, "lock owner does not exist"); res_RELEASE_LOCKOWNER4.status = NFS4_OK; goto out1; } P(plock_owner->so_mutex); /* got the owner, does it still have any locks being held */ if(!glist_empty(&plock_owner->so_lock_list)) { V(plock_owner->so_mutex); res_RELEASE_LOCKOWNER4.status = NFS4ERR_LOCKS_HELD; } else { V(plock_owner->so_mutex); /* found the lock owner and it doesn't have any locks, release it */ release_lockstate(plock_owner); res_RELEASE_LOCKOWNER4.status = NFS4_OK; } /* Release the reference to the lock owner acquired via create_nfs4_owner */ dec_state_owner_ref(plock_owner); out1: /* Update the lease before exit */ P(pnfs_client_id->cid_mutex); update_lease(pnfs_client_id); V(pnfs_client_id->cid_mutex); dec_client_id_ref(pnfs_client_id); out2: LogDebug(COMPONENT_NFS_V4_LOCK, "Leaving NFS v4 RELEASE_LOCKOWNER handler -----------------------------------------------------"); return res_RELEASE_LOCKOWNER4.status; } /* nfs4_op_release_lock_owner */
int nfs4_op_open(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Shorter alias for OPEN4 arguments */ OPEN4args * const arg_OPEN4 = &(op->nfs_argop4_u.opopen); /* Shorter alias for OPEN4 response */ OPEN4res * const res_OPEN4 = &(resp->nfs_resop4_u.opopen); /* The handle from which the change_info4 is to be * generated. Every mention of change_info4 in RFC5661 * speaks of the parent directory of the file being opened. * However, with CLAIM_FH, CLAIM_DELEG_CUR_FH, and * CLAIM_DELEG_PREV_FH, there is no way to derive the parent * directory from the file handle. It is Unclear what the * correct behavior is. In our implementation, we take the * change_info4 of whatever filehandle is current when the * OPEN operation is invoked. */ struct fsal_obj_handle *obj_change = NULL; /* The found client record */ nfs_client_id_t *clientid = NULL; /* The found or created state owner for this open */ state_owner_t *owner = NULL; /* The supplied calim type */ open_claim_type4 claim = arg_OPEN4->claim.claim; /* The open state for the file */ state_t *file_state = NULL; /* True if the state was newly created */ bool new_state = false; int retval; LogDebug(COMPONENT_STATE, "Entering NFS v4 OPEN handler -----------------------------"); /* What kind of open is it ? */ LogFullDebug(COMPONENT_STATE, "OPEN: Claim type = %d, Open Type = %d, Share Deny = %d, Share Access = %d ", arg_OPEN4->claim.claim, arg_OPEN4->openhow.opentype, arg_OPEN4->share_deny, arg_OPEN4->share_access); resp->resop = NFS4_OP_OPEN; res_OPEN4->status = NFS4_OK; res_OPEN4->OPEN4res_u.resok4.rflags = 0; /* Check export permissions if OPEN4_CREATE */ if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE) && ((op_ctx->export_perms->options & EXPORT_OPTION_MD_WRITE_ACCESS) == 0)) { res_OPEN4->status = NFS4ERR_ROFS; LogDebug(COMPONENT_NFS_V4, "Status of OP_OPEN due to export permissions = %s", nfsstat4_to_str(res_OPEN4->status)); return res_OPEN4->status; } /* Check export permissions if OPEN4_SHARE_ACCESS_WRITE */ if (((arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) && ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0)) { res_OPEN4->status = NFS4ERR_ROFS; LogDebug(COMPONENT_NFS_V4, "Status of OP_OPEN due to export permissions = %s", nfsstat4_to_str(res_OPEN4->status)); return res_OPEN4->status; } /* Do basic checks on a filehandle */ res_OPEN4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_OPEN4->status != NFS4_OK) return res_OPEN4->status; if (data->current_obj == NULL) { /* This should be impossible, as PUTFH fills in the * current entry and previous checks weed out handles * in the PseudoFS and DS handles. */ res_OPEN4->status = NFS4ERR_SERVERFAULT; LogCrit(COMPONENT_NFS_V4, "Impossible condition in compound data at %s:%u.", __FILE__, __LINE__); goto out3; } /* It this a known client id? */ LogDebug(COMPONENT_STATE, "OPEN Client id = %" PRIx64, arg_OPEN4->owner.clientid); retval = nfs_client_id_get_confirmed( data->minorversion == 0 ? arg_OPEN4->owner.clientid : data->session->clientid, &clientid); if (retval != CLIENT_ID_SUCCESS) { res_OPEN4->status = clientid_error_to_nfsstat(retval); LogDebug(COMPONENT_NFS_V4, "nfs_client_id_get_confirmed failed"); return res_OPEN4->status; } /* Check if lease is expired and reserve it */ PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (data->minorversion == 0 && !reserve_lease(clientid)) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); res_OPEN4->status = NFS4ERR_EXPIRED; LogDebug(COMPONENT_NFS_V4, "Lease expired"); goto out3; } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* Get the open owner */ if (!open4_open_owner(op, data, resp, clientid, &owner)) { LogDebug(COMPONENT_NFS_V4, "open4_open_owner failed"); goto out2; } /* Do the claim check here, so we can save the result in the * owner for NFSv4.0. */ res_OPEN4->status = open4_validate_claim(data, claim, clientid); if (res_OPEN4->status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "open4_validate_claim failed"); goto out; } /* After this point we know we have only CLAIM_NULL, * CLAIM_FH, or CLAIM_PREVIOUS, and that our grace period and * minor version are appropriate for the claim specified. */ if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE) && (claim != CLAIM_NULL)) { res_OPEN4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4, "OPEN4_CREATE but not CLAIM_NULL"); goto out2; } /* So we still have a reference even after we repalce the * current FH. */ obj_change = data->current_obj; obj_change->obj_ops->get_ref(obj_change); /* Update the change info for entry_change. */ res_OPEN4->OPEN4res_u.resok4.cinfo.before = fsal_get_changeid4(obj_change); /* Check if share_access does not have any access set, or has * invalid bits that are set. check that share_deny doesn't * have any invalid bits set. */ if (!(arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_BOTH) || (data->minorversion == 0 && arg_OPEN4->share_access & ~OPEN4_SHARE_ACCESS_BOTH) || (arg_OPEN4->share_access & (~OPEN4_SHARE_ACCESS_WANT_DELEG_MASK & ~OPEN4_SHARE_ACCESS_BOTH)) || (arg_OPEN4->share_deny & ~OPEN4_SHARE_DENY_BOTH)) { res_OPEN4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4, "Invalid SHARE_ACCESS or SHARE_DENY"); goto out; } /* Utilize the extended FSAL APU functionality to perform the open. */ open4_ex(arg_OPEN4, data, res_OPEN4, clientid, owner, &file_state, &new_state); if (res_OPEN4->status != NFS4_OK) goto out; memset(&res_OPEN4->OPEN4res_u.resok4.attrset, 0, sizeof(struct bitmap4)); if (arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4 || arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4_1) { struct bitmap4 *bits = &res_OPEN4->OPEN4res_u.resok4.attrset; set_attribute_in_bitmap(bits, FATTR4_TIME_ACCESS); set_attribute_in_bitmap(bits, FATTR4_TIME_MODIFY); } /* If server use OPEN_CONFIRM4, set the correct flag, * but not for 4.1 */ if (owner->so_owner.so_nfs4_owner.so_confirmed == false) res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_CONFIRM; res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_LOCKTYPE_POSIX; LogFullDebug(COMPONENT_STATE, "NFS4 OPEN returning NFS4_OK"); /* regular exit */ res_OPEN4->status = NFS4_OK; /* Update change_info4 */ res_OPEN4->OPEN4res_u.resok4.cinfo.after = fsal_get_changeid4(obj_change); res_OPEN4->OPEN4res_u.resok4.cinfo.atomic = FALSE; /* Handle open stateid/seqid for success */ update_stateid(file_state, &res_OPEN4->OPEN4res_u.resok4.stateid, data, open_tag); out: if (res_OPEN4->status != NFS4_OK) { LogDebug(COMPONENT_STATE, "failed with status %s", nfsstat4_to_str(res_OPEN4->status)); } /* Save the response in the open owner. * obj_change is either the parent directory or for a CLAIM_PREV is * the entry itself. In either case, it's the right entry to use in * saving the request results. */ if (data->minorversion == 0) { Copy_nfs4_state_req(owner, arg_OPEN4->seqid, op, obj_change, resp, open_tag); } out2: /* Update the lease before exit */ if (data->minorversion == 0) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); update_lease(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } if (file_state != NULL) dec_state_t_ref(file_state); /* Clean up if we have an error exit */ if ((file_state != NULL) && new_state && (res_OPEN4->status != NFS4_OK)) { /* Need to destroy open owner and state */ state_del(file_state); } if (obj_change) obj_change->obj_ops->put_ref(obj_change); if (owner != NULL) { /* Need to release the open owner for this call */ dec_state_owner_ref(owner); } out3: dec_client_id_ref(clientid); return res_OPEN4->status; } /* nfs4_op_open */
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; }
/** * @brief Check and look up the supplied stateid * * This function yields the state for the stateid if it is valid. * * @param[in] stateid Stateid to look up * @param[in] entry Associated file (if any) * @param[out] state Found state * @param[in] data Compound data * @param[in] flags Flags governing special stateids * @param[in] owner_seqid seqid on v4.0 owner * @param[in] check_seqid Whether to validate owner_seqid * @param[in] tag Arbitrary string for logging/debugging * * @return NFSv4 status codes */ nfsstat4 nfs4_Check_Stateid(stateid4 *stateid, cache_entry_t *entry, state_t **state, compound_data_t *data, int flags, seqid4 owner_seqid, bool check_seqid, const char *tag) { uint32_t epoch = 0; uint64_t epoch_low = ServerEpoch & 0xFFFFFFFF; state_t *state2 = NULL; /* string str has to accomodate stateid->other(OTHERSIZE * 2 ), * stateid->seqid(max 10 bytes), * a colon (:) and a terminating null character. */ char str[OTHERSIZE * 2 + 10 + 2]; int32_t diff; clientid4 clientid; nfs_client_id_t *pclientid; int rc; nfsstat4 status; if (isDebug(COMPONENT_STATE)) { sprint_mem(str, (char *)stateid->other, OTHERSIZE); sprintf(str + OTHERSIZE * 2, ":%u", (unsigned int)stateid->seqid); } LogFullDebug(COMPONENT_STATE, "Check %s stateid flags%s%s%s%s%s%s%s", tag, flags & STATEID_SPECIAL_ALL_0 ? " ALL_0" : "", flags & STATEID_SPECIAL_ALL_1 ? " ALL_1" : "", flags & STATEID_SPECIAL_CURRENT ? " CURRENT" : "", flags & STATEID_SPECIAL_CLOSE_40 ? " CLOSE_40" : "", flags & STATEID_SPECIAL_CLOSE_41 ? " CLOSE_41" : "", flags & STATEID_SPECIAL_FREE ? " FREE" : "", flags == 0 ? " NONE" : ""); /* Test for OTHER is all zeros */ if (memcmp(stateid->other, all_zero, OTHERSIZE) == 0) { if (stateid->seqid == 0 && (flags & STATEID_SPECIAL_ALL_0) != 0) { /* All 0 stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special all 0 stateid", tag); /** @todo FSF: eventually this may want to return an * actual state for use in temporary locks for I/O. */ data->current_stateid_valid = false; goto success; } if (stateid->seqid == 1 && (flags & STATEID_SPECIAL_CURRENT) != 0) { /* Special current stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special 'current' stateid", tag); if (!data->current_stateid_valid) { LogDebug(COMPONENT_STATE, "Check %s stateid STATEID_SPECIAL_CURRENT - current stateid is bad", tag); status = NFS4ERR_BAD_STATEID; goto failure; } /* Copy current stateid in and proceed to checks */ *stateid = data->current_stateid; goto check_it; } LogDebug(COMPONENT_STATE, "Check %s stateid with OTHER all zeros, seqid %u unexpected", tag, (unsigned int)stateid->seqid); status = NFS4ERR_BAD_STATEID; goto failure; } /* Test for OTHER is all ones */ if (memcmp(stateid->other, all_ones, OTHERSIZE) == 0) { /* Test for special all ones stateid */ if (stateid->seqid == seqid_all_one && (flags & STATEID_SPECIAL_ALL_1) != 0) { /* All 1 stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special all 1 stateid", tag); /** @todo FSF: eventually this may want to return an * actual state for use in temporary locks for I/O. */ data->current_stateid_valid = false; goto success; } LogDebug(COMPONENT_STATE, "Check %s stateid with OTHER all ones, seqid %u unexpected", tag, (unsigned int)stateid->seqid); status = NFS4ERR_BAD_STATEID; goto failure; } check_it: /* Extract the clientid from the stateid other field */ memcpy(&clientid, stateid->other, sizeof(clientid)); /* Extract the epoch from the clientid */ epoch = clientid >> (clientid4) 32; /* Check if stateid was made from this server instance */ if (epoch != epoch_low) { LogDebug(COMPONENT_STATE, "Check %s stateid found stale stateid %s", tag, str); status = NFS4ERR_STALE_STATEID; goto failure; } /* Try to get the related state */ if (!nfs4_State_Get_Pointer(stateid->other, &state2)) { /* We matched this server's epoch, but could not find the * stateid. Chances are, the client was expired and the state * has all been freed. * * We could use another check here for a BAD stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid could not find state %s", tag, str); /* Try and find the clientid */ rc = nfs_client_id_get_confirmed(clientid, &pclientid); if (rc != CLIENT_ID_SUCCESS) { /* Unknown client id (or other problem), * return that result. */ status = clientid_error_to_nfsstat(rc); goto failure; } if ((flags & (STATEID_SPECIAL_CLOSE_40 | STATEID_SPECIAL_CLOSE_41)) != 0) { /* This is a close with a valid clientid, but invalid * stateid counter. We will assume this is a replayed * close. */ if (data->preserved_clientid != NULL) { /* We don't expect this, but, just in case... * Update and release already reserved lease. */ pthread_mutex_lock(&data->preserved_clientid ->cid_mutex); update_lease(data->preserved_clientid); pthread_mutex_unlock(&data->preserved_clientid ->cid_mutex); data->preserved_clientid = NULL; } /* Check if lease is expired and reserve it */ pthread_mutex_lock(&pclientid->cid_mutex); if (!reserve_lease(pclientid)) { LogDebug(COMPONENT_STATE, "Returning NFS4ERR_EXPIRED"); pthread_mutex_unlock(&pclientid->cid_mutex); status = NFS4ERR_EXPIRED; goto failure; } if ((flags & STATEID_SPECIAL_CLOSE_40) != 0) { /* Just update the lease and leave the reserved * clientid NULL. */ update_lease(pclientid); } else { /* Remember the reserved clientid for the rest * of the compound. */ data->preserved_clientid = pclientid; } pthread_mutex_unlock(&pclientid->cid_mutex); /* Replayed close, it's ok, but stateid doesn't exist */ LogDebug(COMPONENT_STATE, "Check %s stateid is a replayed close", tag); data->current_stateid_valid = false; goto success; } /* Release the clientid reference we just acquired. */ dec_client_id_ref(pclientid); status = NFS4ERR_BAD_STATEID; goto failure; } /* Now, if this lease is not already reserved, reserve it */ if (data->preserved_clientid != state2->state_owner->so_owner.so_nfs4_owner.so_clientrec) { if (data->preserved_clientid != NULL) { /* We don't expect this to happen, but, just in case... * Update and release already reserved lease. */ pthread_mutex_lock(&data->preserved_clientid ->cid_mutex); update_lease(data->preserved_clientid); pthread_mutex_unlock(&data->preserved_clientid ->cid_mutex); data->preserved_clientid = NULL; } /* Check if lease is expired and reserve it */ pthread_mutex_lock(&state2->state_owner->so_owner .so_nfs4_owner.so_clientrec->cid_mutex); if (!reserve_lease (state2->state_owner->so_owner.so_nfs4_owner. so_clientrec)) { LogDebug(COMPONENT_STATE, "Returning NFS4ERR_EXPIRED"); pthread_mutex_unlock(&state2->state_owner->so_owner .so_nfs4_owner.so_clientrec ->cid_mutex); status = NFS4ERR_EXPIRED; goto failure; } data->preserved_clientid = state2->state_owner->so_owner.so_nfs4_owner.so_clientrec; pthread_mutex_unlock(&state2->state_owner->so_owner .so_nfs4_owner.so_clientrec->cid_mutex); } /* Sanity check : Is this the right file ? */ if ((entry != NULL) && (state2->state_entry != entry)) { LogDebug(COMPONENT_STATE, "Check %s stateid found stateid %s has wrong file", tag, str); status = NFS4ERR_BAD_STATEID; goto failure; } /* Whether stateid.seqid may be zero depends on the state type exclusively, See RFC 5661 pp. 161,287-288. */ if ((state2->state_type == STATE_TYPE_LAYOUT) || (stateid->seqid != 0)) { /* Check seqid in stateid */ /** * @todo fsf: maybe change to simple comparison: * stateid->seqid < state2->state_seqid * as good enough and maybe makes pynfs happy. */ diff = stateid->seqid - state2->state_seqid; if (diff < 0) { /* if this is NFSv4.0 and stateid's seqid is one less * than current AND if owner_seqid is current * pass state back to allow replay check */ if ((check_seqid) && ((diff == -1) || ((state2->state_seqid == 1) && (stateid->seqid == seqid_all_one))) && (owner_seqid == state2->state_owner->so_owner.so_nfs4_owner. so_seqid)) { LogDebug(COMPONENT_STATE, "possible replay?"); *state = state2; status = NFS4ERR_REPLAY; goto replay; } /* OLD_STATEID */ LogDebug(COMPONENT_STATE, "Check %s stateid found OLD stateid %s, expected seqid %u", tag, str, (unsigned int)state2->state_seqid); status = NFS4ERR_OLD_STATEID; goto failure; } /* stateid seqid is current and owner seqid is previous, * replay (should be an error condition that did not change * the stateid, no real need to check since the operation * must be the same) */ else if ((diff == 0) && (check_seqid) && (owner_seqid == state2->state_owner->so_owner.so_nfs4_owner. so_seqid)) { LogDebug(COMPONENT_STATE, "possible replay?"); *state = state2; status = NFS4ERR_REPLAY; goto replay; } else if (diff > 0) { /* BAD_STATEID */ LogDebug(COMPONENT_STATE, "Check %s stateid found BAD stateid %s, expected seqid %u", tag, str, (unsigned int)state2->state_seqid); status = NFS4ERR_BAD_STATEID; goto failure; } } if ((flags & STATEID_SPECIAL_FREE) != 0) { switch (state2->state_type) { case STATE_TYPE_LOCK: PTHREAD_RWLOCK_rdlock(&state2->state_entry->state_lock); if (glist_empty (&state2->state_data.lock.state_locklist)) { LogFullDebug(COMPONENT_STATE, "Check %s stateid %s has no locks, ok to free", tag, str); PTHREAD_RWLOCK_unlock(&state2->state_entry-> state_lock); break; } PTHREAD_RWLOCK_unlock(&state2->state_entry->state_lock); /* Fall through for failure */ case STATE_TYPE_NONE: case STATE_TYPE_SHARE: case STATE_TYPE_DELEG: case STATE_TYPE_LAYOUT: LogDebug(COMPONENT_STATE, "Check %s stateid found stateid %s with locks held", tag, str); status = NFS4ERR_LOCKS_HELD; goto failure; } } data->current_stateid_valid = true; LogFullDebug(COMPONENT_STATE, "Check %s stateid found valid stateid %s - %p", tag, str, state2); /* Copy stateid into current for later use */ data->current_stateid = *stateid; data->current_stateid.seqid = state2->state_seqid; success: *state = state2; return NFS4_OK; failure: *state = NULL; replay: data->current_stateid_valid = false; return status; }