Ejemplo n.º 1
0
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 */
Ejemplo n.º 2
0
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 */
Ejemplo n.º 4
0
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 */
Ejemplo n.º 6
0
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 */
Ejemplo n.º 7
0
/**
 * @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;
}
Ejemplo n.º 8
0
/**
 * @brief the NFS4_OP_SEQUENCE operation
 *
 * @param[in]     op   nfs4_op arguments
 * @param[in,out] data Compound request's data
 * @param[out]    resp nfs4_op results
 *
 * @return per RFC5661, p. 374
 *
 * @see nfs4_Compound
 *
 */
int nfs4_op_sequence(struct nfs_argop4 *op, compound_data_t *data,
		     struct nfs_resop4 *resp)
{
	SEQUENCE4args * const arg_SEQUENCE4 = &op->nfs_argop4_u.opsequence;
	SEQUENCE4res * const res_SEQUENCE4 = &resp->nfs_resop4_u.opsequence;

	nfs41_session_t *session;

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

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

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

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

		return res_SEQUENCE4->sr_status;
	}

	/* session->refcount +1 */

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

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

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

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

	data->preserved_clientid = session->clientid_record;

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

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

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

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

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

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

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

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

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

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

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

	res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags = 0;

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

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

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

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

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

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

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