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_renew(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { nfs_client_id_t *pclientid; /* Lock are not supported */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_RENEW; /* Tell the admin what I am doing... */ LogFullDebug(COMPONENT_CLIENTID, "RENEW Client id = %"PRIx64, arg_RENEW4.clientid); /* Is this an existing client id ? */ if(nfs_client_id_get_confirmed(arg_RENEW4.clientid, &pclientid) != CLIENT_ID_SUCCESS) { /* Unknown client id */ res_RENEW4.status = NFS4ERR_STALE_CLIENTID; return res_RENEW4.status; } P(pclientid->cid_mutex); if(!reserve_lease(pclientid)) { res_RENEW4.status = NFS4ERR_EXPIRED; } else { update_lease(pclientid); res_RENEW4.status = NFS4_OK; /* Regular exit */ } V(pclientid->cid_mutex); dec_client_id_ref(pclientid); return res_RENEW4.status; } /* nfs4_op_renew */
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 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 */
/** * @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; }
/** * @brief the NFS4_OP_SEQUENCE operation * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request's data * @param[out] resp nfs4_op results * * @return per RFC5661, p. 374 * * @see nfs4_Compound * */ int nfs4_op_sequence(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SEQUENCE4args * const arg_SEQUENCE4 = &op->nfs_argop4_u.opsequence; SEQUENCE4res * const res_SEQUENCE4 = &resp->nfs_resop4_u.opsequence; nfs41_session_t *session; resp->resop = NFS4_OP_SEQUENCE; res_SEQUENCE4->sr_status = NFS4_OK; if (data->minorversion == 0) { res_SEQUENCE4->sr_status = NFS4ERR_INVAL; return res_SEQUENCE4->sr_status; } /* OP_SEQUENCE is always the first operation of the request */ if (data->oppos != 0) { res_SEQUENCE4->sr_status = NFS4ERR_SEQUENCE_POS; return res_SEQUENCE4->sr_status; } if (!nfs41_Session_Get_Pointer(arg_SEQUENCE4->sa_sessionid, &session)) { if (nfs_in_grace()) { memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4. sr_sessionid, arg_SEQUENCE4->sa_sessionid, NFS4_SESSIONID_SIZE); res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid = arg_SEQUENCE4->sa_sequenceid; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid = arg_SEQUENCE4->sa_slotid; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4. sr_highest_slotid = NFS41_NB_SLOTS - 1; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4. sr_target_highest_slotid = arg_SEQUENCE4->sa_slotid; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4. sr_status_flags = SEQ4_STATUS_RESTART_RECLAIM_NEEDED; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s flags 0x%X", nfsstat4_to_str(res_SEQUENCE4->sr_status), res_SEQUENCE4->SEQUENCE4res_u.sr_resok4. sr_status_flags); } else { res_SEQUENCE4->sr_status = NFS4ERR_BADSESSION; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); } return res_SEQUENCE4->sr_status; } /* session->refcount +1 */ LogDebug(COMPONENT_SESSIONS, "SEQUENCE session=%p", session); /* Check if lease is expired and reserve it */ pthread_mutex_lock(&session->clientid_record->cid_mutex); if (!reserve_lease(session->clientid_record)) { pthread_mutex_unlock(&session->clientid_record->cid_mutex); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_EXPIRED; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } data->preserved_clientid = session->clientid_record; pthread_mutex_unlock(&session->clientid_record->cid_mutex); /* Check is slot is compliant with ca_maxrequests */ if (arg_SEQUENCE4->sa_slotid >= session->fore_channel_attrs.ca_maxrequests) { dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_BADSLOT; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } /* By default, no DRC replay */ data->use_drc = false; pthread_mutex_lock(&session->slots[arg_SEQUENCE4->sa_slotid].lock); if (session->slots[arg_SEQUENCE4->sa_slotid].sequence + 1 != arg_SEQUENCE4->sa_sequenceid) { if (session->slots[arg_SEQUENCE4->sa_slotid].sequence == arg_SEQUENCE4->sa_sequenceid) { #if IMPLEMENT_CACHETHIS /** @todo * * Ganesha always caches result anyway so ignore * cachethis */ if (session->slots[arg_SEQUENCE4->sa_slotid] .cache_used) { #endif /* Replay operation through the DRC */ data->use_drc = true; data->cached_res = &session->slots[arg_SEQUENCE4->sa_slotid]. cached_result; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Use sesson slot %" PRIu32 "=%p for DRC", arg_SEQUENCE4->sa_slotid, data->cached_res); pthread_mutex_unlock(&session-> slots[arg_SEQUENCE4->sa_slotid].lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4_OK; return res_SEQUENCE4->sr_status; #if IMPLEMENT_CACHETHIS } else { /* Illegal replay */ pthread_mutex_unlock(&session-> slots[arg_SEQUENCE4->sa_slotid].lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_RETRY_UNCACHED_REP; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4-> sr_status)); return res_SEQUENCE4->sr_status; } #endif } pthread_mutex_unlock(&session-> slots[arg_SEQUENCE4->sa_slotid].lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_SEQ_MISORDERED; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } /* Keep memory of the session in the COMPOUND's data */ data->session = session; /* Record the sequenceid and slotid in the COMPOUND's data */ data->sequence = arg_SEQUENCE4->sa_sequenceid; data->slot = arg_SEQUENCE4->sa_slotid; /* Update the sequence id within the slot */ session->slots[arg_SEQUENCE4->sa_slotid].sequence += 1; memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sessionid, arg_SEQUENCE4->sa_sessionid, NFS4_SESSIONID_SIZE); res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid = session->slots[arg_SEQUENCE4->sa_slotid].sequence; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid = arg_SEQUENCE4->sa_slotid; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_highest_slotid = NFS41_NB_SLOTS - 1; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_target_highest_slotid = arg_SEQUENCE4->sa_slotid; /* Maybe not the best choice */ res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags = 0; if (nfs_rpc_get_chan(session->clientid_record, 0) == NULL) { res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags |= SEQ4_STATUS_CB_PATH_DOWN; } #if IMPLEMENT_CACHETHIS /* Ganesha always caches result anyway so ignore cachethis */ if (arg_SEQUENCE4->sa_cachethis) { #endif data->cached_res = &session->slots[arg_SEQUENCE4->sa_slotid].cached_result; session->slots[arg_SEQUENCE4->sa_slotid].cache_used = true; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Use sesson slot %" PRIu32 "=%p for DRC", arg_SEQUENCE4->sa_slotid, data->cached_res); #if IMPLEMENT_CACHETHIS } else { data->cached_res = NULL; session->slots[arg_SEQUENCE4->sa_slotid].cache_used = false; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Don't use sesson slot %" PRIu32 "=NULL for DRC", arg_SEQUENCE4->sa_slotid); } #endif pthread_mutex_unlock(&session->slots[arg_SEQUENCE4->sa_slotid].lock); /* If we were successful, stash the clientid in the request * context. */ data->req_ctx->clientid = &data->session->clientid; res_SEQUENCE4->sr_status = NFS4_OK; return res_SEQUENCE4->sr_status; } /* nfs41_op_sequence */