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 */
void free_nfs4_owner(state_owner_t * owner)
{
  if(owner->so_owner.so_nfs4_owner.so_related_owner != NULL)
    dec_state_owner_ref(owner->so_owner.so_nfs4_owner.so_related_owner);

  /* Release the saved response. */
  nfs4_Compound_FreeOne(&owner->so_owner.so_nfs4_owner.so_resp);

  /* Remove the owner from the owners per clientid list. */
  P(owner->so_owner.so_nfs4_owner.so_clientrec->cid_mutex);

  glist_del(&owner->so_owner.so_nfs4_owner.so_perclient);

  V(owner->so_owner.so_nfs4_owner.so_clientrec->cid_mutex);

  dec_client_id_ref(owner->so_owner.so_nfs4_owner.so_clientrec);
}
/**
 * @brief Get the per-clientid state owner
 *
 * @todo FSF: I'm not really sure about the refcounting here...
 *
 * @param[in]  clientid       The clientid to look up
 * @param[out] clientid_owner Matching state owner
 *
 * @return State status.
 */
state_status_t get_clientid_owner(clientid4 clientid,
                                  state_owner_t **clientid_owner)
{
  /* Pointer to client record, retrieved by ID and in which the
     client state owner is stored */
  nfs_client_id_t *pclientid = NULL;

  if ((nfs_client_id_get_confirmed(clientid, &pclientid))
      != CLIENT_ID_SUCCESS)
    {
      return STATE_NOT_FOUND;
    }
  else
    {
      *clientid_owner = &pclientid->cid_owner;
      dec_client_id_ref(pclientid);
      return STATE_SUCCESS;
    }
}
Exemple #4
0
void free_nfs4_owner(state_owner_t *owner)
{
	state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner;

	if (nfs4_owner->so_related_owner != NULL)
		dec_state_owner_ref(nfs4_owner->so_related_owner);

	/* Release the saved response. */
	nfs4_Compound_FreeOne(&nfs4_owner->so_resp);

	/* Remove the owner from the owners per clientid list. */
	PTHREAD_MUTEX_lock(&nfs4_owner->so_clientrec->cid_mutex);

	glist_del(&nfs4_owner->so_perclient);

	PTHREAD_MUTEX_unlock(&nfs4_owner->so_clientrec->cid_mutex);

	dec_client_id_ref(nfs4_owner->so_clientrec);
}
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 */
void remove_nfs4_owner(state_owner_t        * powner,
                       const char           * str)
{
  hash_buffer_t           buffkey, old_key, old_value;
  state_nfs4_owner_name_t oname;
  int                     rc;

  memset(&oname, 0, sizeof(oname));

  oname.son_clientid  = powner->so_owner.so_nfs4_owner.so_clientid;
  oname.son_owner_len = powner->so_owner_len;
  oname.son_islock    = powner->so_type == STATE_LOCK_OWNER_NFSV4;
  memcpy(oname.son_owner_val, powner->so_owner_val, powner->so_owner_len);

  buffkey.pdata = (caddr_t) &oname;
  buffkey.len   = sizeof(*powner);

  rc = HashTable_DelRef(ht_nfs4_owner,
                        &buffkey,
                        &old_key,
                        &old_value,
                        Hash_dec_state_owner_ref);

  switch(rc)
    {
      case HASHTABLE_SUCCESS:
        if(powner->so_type == STATE_LOCK_OWNER_NFSV4 &&
           powner->so_owner.so_nfs4_owner.so_related_owner != NULL)
          dec_state_owner_ref(powner->so_owner.so_nfs4_owner.so_related_owner);

        /* Release the owner_name (key) and owner (data) back to appropriate pools */
        LogFullDebug(COMPONENT_STATE, "Free %s", str);

        nfs4_Compound_FreeOne(&powner->so_owner.so_nfs4_owner.so_resp);

        P(powner->so_owner.so_nfs4_owner.so_pclientid->cid_mutex);

        glist_del(&powner->so_owner.so_nfs4_owner.so_perclient);

        V(powner->so_owner.so_nfs4_owner.so_pclientid->cid_mutex);

        dec_client_id_ref(powner->so_owner.so_nfs4_owner.so_pclientid);

        pool_free(state_owner_pool, old_value.pdata);
        pool_free(state_nfs4_owner_name_pool, old_key.pdata);
        break;

      case HASHTABLE_NOT_DELETED:
        /* ref count didn't end up at 0, don't free. */
        LogDebug(COMPONENT_STATE,
                 "HashTable_DelRef didn't reduce refcount to 0 for %s",
                  str);
        break;

      default:
        /* some problem occurred */
        LogDebug(COMPONENT_STATE,
                 "HashTable_DelRef failed (%s) for %s",
                  hash_table_err_to_str(rc), str);
        break;
    }
}
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_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_setclientid_confirm(struct nfs_argop4 *op, compound_data_t *data,
				struct nfs_resop4 *resp)
{
	SETCLIENTID_CONFIRM4args * const arg_SETCLIENTID_CONFIRM4 =
	    &op->nfs_argop4_u.opsetclientid_confirm;
	SETCLIENTID_CONFIRM4res * const res_SETCLIENTID_CONFIRM4 =
	    &resp->nfs_resop4_u.opsetclientid_confirm;
	nfs_client_id_t *conf = NULL;
	nfs_client_id_t *unconf = NULL;
	nfs_client_record_t *client_record;
	clientid4 clientid = 0;
	char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1];
	const char *str_client_addr = "(unknown)";
	/* The client name, for gratuitous logging */
	char str_client[CLIENTNAME_BUFSIZE];
	/* Display buffer for client name */
	struct display_buffer dspbuf_client = {
		sizeof(str_client), str_client, str_client};
	/* The clientid4 broken down into fields */
	char str_clientid4[DISPLAY_CLIENTID_SIZE];
	/* Display buffer for clientid4 */
	struct display_buffer dspbuf_clientid4 = {
		sizeof(str_clientid4), str_clientid4, str_clientid4};
	int rc;

	/* Make sure str_client is always printable even
	 * if log level changes midstream.
	 */
	display_printf(&dspbuf_client, "(unknown)");
	display_reset_buffer(&dspbuf_client);

	resp->resop = NFS4_OP_SETCLIENTID_CONFIRM;
	res_SETCLIENTID_CONFIRM4->status = NFS4_OK;
	clientid = arg_SETCLIENTID_CONFIRM4->clientid;

	display_clientid(&dspbuf_clientid4, clientid);

	if (data->minorversion > 0) {
		res_SETCLIENTID_CONFIRM4->status = NFS4ERR_NOTSUPP;
		return res_SETCLIENTID_CONFIRM4->status;
	}

	if (op_ctx->client != NULL)
		str_client_addr = op_ctx->client->hostaddr_str;

	if (isDebug(COMPONENT_CLIENTID)) {
		sprint_mem(str_verifier,
			   arg_SETCLIENTID_CONFIRM4->setclientid_confirm,
			   NFS4_VERIFIER_SIZE);
	} else {
		str_verifier[0] = '\0';
	}

	LogDebug(COMPONENT_CLIENTID,
		 "SETCLIENTID_CONFIRM client addr=%s clientid=%s setclientid_confirm=%s",
		 str_client_addr, str_clientid4, str_verifier);

	/* First try to look up unconfirmed record */
	rc = nfs_client_id_get_unconfirmed(clientid, &unconf);

	if (rc == CLIENT_ID_SUCCESS) {
		client_record = unconf->cid_client_record;

		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);
			LogFullDebug(COMPONENT_CLIENTID, "Found %s", str);
		}
	} else {
		rc = nfs_client_id_get_confirmed(clientid, &conf);

		if (rc != CLIENT_ID_SUCCESS) {
			/* No record whatsoever of this clientid */
			LogDebug(COMPONENT_CLIENTID,
				 "%s clientid = %s",
				 clientid_error_to_str(rc), str_clientid4);
			res_SETCLIENTID_CONFIRM4->status =
			    clientid_error_to_nfsstat_no_expire(rc);

			return res_SETCLIENTID_CONFIRM4->status;
		}

		client_record = conf->cid_client_record;

		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, conf);
			LogFullDebug(COMPONENT_CLIENTID, "Found %s", str);
		}
	}

	PTHREAD_MUTEX_lock(&client_record->cr_mutex);

	inc_client_record_ref(client_record);

	if (isFullDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN] = "\0";
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_client_record(&dspbuf, client_record);

		LogFullDebug(COMPONENT_CLIENTID,
			     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
			     str,
			     client_record->cr_confirmed_rec,
			     client_record->cr_unconfirmed_rec);
	}

	/* At this point one and only one of pconf and punconf is non-NULL */

	if (unconf != NULL) {
		/* First must match principal */
		if (!nfs_compare_clientcred(&unconf->cid_credential,
					    &data->credential)
		    || op_ctx->client == NULL
		    || unconf->gsh_client == NULL
		    || op_ctx->client != unconf->gsh_client) {
			if (isDebug(COMPONENT_CLIENTID)) {
				char *unconfirmed_addr = "(unknown)";

				if (unconf->gsh_client != NULL)
					unconfirmed_addr =
					    unconf->gsh_client->hostaddr_str;

				LogDebug(COMPONENT_CLIENTID,
					 "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE",
					 str_clientid4,
					 str_client_addr,
					 unconfirmed_addr);
			}

			res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE;
			dec_client_id_ref(unconf);
			goto out;
		} else if (unconf->cid_confirmed == CONFIRMED_CLIENT_ID &&
			   memcmp(unconf->cid_verifier,
				  arg_SETCLIENTID_CONFIRM4->setclientid_confirm,
				  NFS4_VERIFIER_SIZE) == 0) {
			/* We must have raced with another
			   SETCLIENTID_CONFIRM */
			if (isDebug(COMPONENT_CLIENTID)) {
				char str[LOG_BUFF_LEN] = "\0";
				struct display_buffer dspbuf = {
					sizeof(str), str, str};

				display_client_id_rec(&dspbuf, unconf);
				LogDebug(COMPONENT_CLIENTID,
					 "Race against confirm for %s", str);
			}

			res_SETCLIENTID_CONFIRM4->status = NFS4_OK;
			dec_client_id_ref(unconf);

			goto out;
		} else if (unconf->cid_confirmed != UNCONFIRMED_CLIENT_ID) {
			/* We raced with another thread that dealt
			 * with this unconfirmed record.  Release our
			 * reference, and pretend we didn't find a
			 * record.
			 */
			if (isDebug(COMPONENT_CLIENTID)) {
				char str[LOG_BUFF_LEN] = "\0";
				struct display_buffer dspbuf = {
					sizeof(str), str, str};

				display_client_id_rec(&dspbuf, unconf);

				LogDebug(COMPONENT_CLIENTID,
					 "Race against expire for %s", str);
			}

			res_SETCLIENTID_CONFIRM4->status =
			    NFS4ERR_STALE_CLIENTID;

			dec_client_id_ref(unconf);

			goto out;
		}
	}

	if (conf != NULL) {
		if (isDebug(COMPONENT_CLIENTID) && conf != NULL)
			display_clientid_name(&dspbuf_client, conf);

		/* First must match principal */
		if (!nfs_compare_clientcred(&conf->cid_credential,
					    &data->credential)
		    || op_ctx->client == NULL
		    || conf->gsh_client == NULL
		    || op_ctx->client != conf->gsh_client) {
			if (isDebug(COMPONENT_CLIENTID)) {
				char *confirmed_addr = "(unknown)";

				if (conf->gsh_client != NULL)
					confirmed_addr =
					    conf->gsh_client->hostaddr_str;

				LogDebug(COMPONENT_CLIENTID,
					 "Confirmed ClientId %s->%s addr=%s: Principals do not match...  confirmed addr=%s Return NFS4ERR_CLID_INUSE",
					 str_clientid4,
					 str_client,
					 str_client_addr,
					 confirmed_addr);
			}

			res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE;
		} else if (memcmp(
				conf->cid_verifier,
				arg_SETCLIENTID_CONFIRM4->setclientid_confirm,
				NFS4_VERIFIER_SIZE) == 0) {
			/* In this case, the record was confirmed and
			 * we have received a retry
			 */
			if (isDebug(COMPONENT_CLIENTID)) {
				char str[LOG_BUFF_LEN] = "\0";
				struct display_buffer dspbuf = {
					sizeof(str), str, str};

				display_client_id_rec(&dspbuf, conf);
				LogDebug(COMPONENT_CLIENTID,
					 "Retry confirm for %s", str);
			}

			res_SETCLIENTID_CONFIRM4->status = NFS4_OK;
		} else {
			/* This is a case not covered... Return
			 * NFS4ERR_CLID_INUSE
			 */
			if (isDebug(COMPONENT_CLIENTID)) {
				char str[LOG_BUFF_LEN] = "\0";
				struct display_buffer dspbuf = {
					sizeof(str), str, str};
				char str_conf_verifier[NFS4_VERIFIER_SIZE * 2 +
						       1];

				sprint_mem(str_conf_verifier,
					   conf->cid_verifier,
					   NFS4_VERIFIER_SIZE);

				display_client_id_rec(&dspbuf, conf);

				LogDebug(COMPONENT_CLIENTID,
					 "Confirm verifier=%s doesn't match verifier=%s for %s",
					 str_conf_verifier, str_verifier, str);
			}

			res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE;
		}

		/* Release our reference to the confirmed clientid. */
		dec_client_id_ref(conf);
		goto out;
	}

	/* We don't need to do any further principal checks, we can't
	 * have a confirmed clientid record with a different principal
	 * than the unconfirmed record.  Also, at this point, we have
	 * a matching unconfirmed clientid (punconf != NULL and pconf
	 * == NULL).
	 */

	/* Make sure we have a reference to the confirmed clientid
	 * record if any
	 */
	if (conf == NULL) {
		conf = client_record->cr_confirmed_rec;

		if (isDebug(COMPONENT_CLIENTID) && conf != NULL)
			display_clientid_name(&dspbuf_client, conf);

		/* Need a reference to the confirmed record for below */
		if (conf != NULL)
			inc_client_id_ref(conf);
	}

	if (conf != NULL && conf->cid_clientid != clientid) {
		/* Old confirmed record - need to expire it */
		if (isDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, conf);
			LogDebug(COMPONENT_CLIENTID, "Expiring %s", str);
		}

		/* Expire clientid and release our reference. */
		nfs_client_id_expire(conf, false);

		dec_client_id_ref(conf);

		conf = NULL;
	}

	if (conf != NULL) {
		/* At this point we are updating the confirmed
		 * clientid.  Update the confirmed record from the
		 * unconfirmed record.
		 */
		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);
			LogFullDebug(COMPONENT_CLIENTID, "Updating from %s",
				     str);
		}

		/* Copy callback information into confirmed clientid record */
		memcpy(conf->cid_cb.v40.cb_client_r_addr,
		       unconf->cid_cb.v40.cb_client_r_addr,
		       sizeof(conf->cid_cb.v40.cb_client_r_addr));

		conf->cid_cb.v40.cb_addr = unconf->cid_cb.v40.cb_addr;
		conf->cid_cb.v40.cb_program = unconf->cid_cb.v40.cb_program;

		conf->cid_cb.v40.cb_callback_ident =
		    unconf->cid_cb.v40.cb_callback_ident;

		nfs_rpc_destroy_chan(&conf->cid_cb.v40.cb_chan);

		memcpy(conf->cid_verifier, unconf->cid_verifier,
		       NFS4_VERIFIER_SIZE);

		/* unhash the unconfirmed clientid record */
		remove_unconfirmed_client_id(unconf);

		/* Release our reference to the unconfirmed entry */
		dec_client_id_ref(unconf);

		if (isDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, conf);
			LogDebug(COMPONENT_CLIENTID, "Updated %s", str);
		}
		/* Check and update call back channel state */
		if (nfs_param.nfsv4_param.allow_delegations &&
		    nfs_test_cb_chan(conf) != RPC_SUCCESS) {
			set_cb_chan_down(conf, true);
			LogCrit(COMPONENT_CLIENTID,
				"setclid confirm: Callback channel is down");
		} else {
			set_cb_chan_down(conf, false);
			LogDebug(COMPONENT_CLIENTID,
				"setclid confirm: Callback channel is UP");
		}

		/* Release our reference to the confirmed clientid. */
		dec_client_id_ref(conf);
	} else {
		/* This is a new clientid */
		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);
			LogFullDebug(COMPONENT_CLIENTID,
				     "Confirming new %s",
				     str);
		}

		rc = nfs_client_id_confirm(unconf, COMPONENT_CLIENTID);

		if (rc != CLIENT_ID_SUCCESS) {
			res_SETCLIENTID_CONFIRM4->status =
			    clientid_error_to_nfsstat_no_expire(rc);

			LogEvent(COMPONENT_CLIENTID,
				 "FAILED to confirm client");

			/* Release our reference to the unconfirmed record */
			dec_client_id_ref(unconf);

			goto out;
		}

		/* check if the client can perform reclaims */
		nfs4_chk_clid(unconf);

		if (isDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);

			LogDebug(COMPONENT_CLIENTID, "Confirmed %s", str);
		}

		/* Check and update call back channel state */
		if (nfs_param.nfsv4_param.allow_delegations &&
		    nfs_test_cb_chan(unconf) != RPC_SUCCESS) {
			set_cb_chan_down(unconf, true);
			LogCrit(COMPONENT_CLIENTID,
				"setclid confirm: Callback channel is down");
		} else {
			set_cb_chan_down(unconf, false);
			LogDebug(COMPONENT_CLIENTID,
				"setclid confirm: Callback channel is UP");
		}

		/* Release our reference to the now confirmed record */
		dec_client_id_ref(unconf);
	}

	if (isFullDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN] = "\0";
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_client_record(&dspbuf, client_record);
		LogFullDebug(COMPONENT_CLIENTID,
			     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
			     str,
			     client_record->cr_confirmed_rec,
			     client_record->cr_unconfirmed_rec);
	}

	/* Successful exit */
	res_SETCLIENTID_CONFIRM4->status = NFS4_OK;

 out:

	PTHREAD_MUTEX_unlock(&client_record->cr_mutex);
	/* Release our reference to the client record and return */
	dec_client_record_ref(client_record);
	return res_SETCLIENTID_CONFIRM4->status;
}
Exemple #10
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_create_session(struct nfs_argop4 *op, compound_data_t *data,
                           struct nfs_resop4 *resp)
{
    /* Result of looking up the clientid in the confirmed ID
       table */
    nfs_client_id_t *conf = NULL;	/* XXX these are not good names */
    /* Result of looking up the clientid in the unconfirmed ID
       table */
    nfs_client_id_t *unconf = NULL;
    /* The found clientid (either one of the preceding) */
    nfs_client_id_t *found = NULL;
    /* The found client record */
    nfs_client_record_t *client_record;
    /* The created session */
    nfs41_session_t *nfs41_session = NULL;
    /* Client supplied clientid */
    clientid4 clientid = 0;
    /* The client address as a string, for gratuitous logging */
    const char *str_client_addr = "(unknown)";
    /* The client name, for gratuitous logging */
    char str_client[CLIENTNAME_BUFSIZE];
    /* Display buffer for client name */
    struct display_buffer dspbuf_client = {
        sizeof(str_client), str_client, str_client
    };
    /* The clientid4 broken down into fields */
    char str_clientid4[DISPLAY_CLIENTID_SIZE];
    /* Display buffer for clientid4 */
    struct display_buffer dspbuf_clientid4 = {
        sizeof(str_clientid4), str_clientid4, str_clientid4
    };
    /* Return code from clientid calls */
    int i, rc = 0;
    /* Component for logging */
    log_components_t component = COMPONENT_CLIENTID;
    /* Abbreviated alias for arguments */
    CREATE_SESSION4args * const arg_CREATE_SESSION4 =
        &op->nfs_argop4_u.opcreate_session;
    /* Abbreviated alias for response */
    CREATE_SESSION4res * const res_CREATE_SESSION4 =
        &resp->nfs_resop4_u.opcreate_session;
    /* Abbreviated alias for successful response */
    CREATE_SESSION4resok * const res_CREATE_SESSION4ok =
        &res_CREATE_SESSION4->CREATE_SESSION4res_u.csr_resok4;

    /* Make sure str_client is always printable even
     * if log level changes midstream.
     */
    display_printf(&dspbuf_client, "(unknown)");
    display_reset_buffer(&dspbuf_client);

    if (op_ctx->client != NULL)
        str_client_addr = op_ctx->client->hostaddr_str;

    if (isDebug(COMPONENT_SESSIONS))
        component = COMPONENT_SESSIONS;

    resp->resop = NFS4_OP_CREATE_SESSION;
    res_CREATE_SESSION4->csr_status = NFS4_OK;
    clientid = arg_CREATE_SESSION4->csa_clientid;

    display_clientid(&dspbuf_clientid4, clientid);

    if (data->minorversion == 0)
        return res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL;

    LogDebug(component,
             "CREATE_SESSION client addr=%s clientid=%s -------------------",
             str_client_addr, str_clientid4);

    /* First try to look up unconfirmed record */
    rc = nfs_client_id_get_unconfirmed(clientid, &unconf);

    if (rc == CLIENT_ID_SUCCESS) {
        client_record = unconf->cid_client_record;
        found = unconf;
    } else {
        rc = nfs_client_id_get_confirmed(clientid, &conf);
        if (rc != CLIENT_ID_SUCCESS) {
            /* No record whatsoever of this clientid */
            LogDebug(component,
                     "%s clientid=%s",
                     clientid_error_to_str(rc), str_clientid4);

            if (rc == CLIENT_ID_EXPIRED)
                rc = CLIENT_ID_STALE;

            res_CREATE_SESSION4->csr_status =
                clientid_error_to_nfsstat_no_expire(rc);

            return res_CREATE_SESSION4->csr_status;
        }

        client_record = conf->cid_client_record;
        found = conf;
    }

    PTHREAD_MUTEX_lock(&client_record->cr_mutex);

    inc_client_record_ref(client_record);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_record(&dspbuf, client_record);

        LogFullDebug(component,
                     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
                     str,
                     client_record->cr_confirmed_rec,
                     client_record->cr_unconfirmed_rec);
    }

    /* At this point one and only one of conf and unconf is
     * non-NULL, and found also references the single clientid
     * record that was found.
     */

    LogDebug(component,
             "CREATE_SESSION clientid=%s csa_sequence=%" PRIu32
             " clientid_cs_seq=%" PRIu32 " data_oppos=%d data_use_drc=%d",
             str_clientid4, arg_CREATE_SESSION4->csa_sequence,
             found->cid_create_session_sequence, data->oppos,
             data->use_drc);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_id_rec(&dspbuf, found);
        LogFullDebug(component, "Found %s", str);
    }

    data->use_drc = false;

    if (data->oppos == 0) {
        /* Special case : the request is used without use of
         * OP_SEQUENCE
         */
        if ((arg_CREATE_SESSION4->csa_sequence + 1 ==
                found->cid_create_session_sequence)
                && (found->cid_create_session_slot.cache_used)) {
            data->use_drc = true;
            data->cached_res =
                &found->cid_create_session_slot.cached_result;

            res_CREATE_SESSION4->csr_status = NFS4_OK;

            dec_client_id_ref(found);

            LogDebug(component,
                     "CREATE_SESSION replay=%p special case",
                     data->cached_res);

            goto out;
        } else if (arg_CREATE_SESSION4->csa_sequence !=
                   found->cid_create_session_sequence) {
            res_CREATE_SESSION4->csr_status =
                NFS4ERR_SEQ_MISORDERED;

            dec_client_id_ref(found);

            LogDebug(component,
                     "CREATE_SESSION returning NFS4ERR_SEQ_MISORDERED");

            goto out;
        }

    }

    if (unconf != NULL) {
        /* First must match principal */
        if (!nfs_compare_clientcred(&unconf->cid_credential,
                                    &data->credential)) {
            if (isDebug(component)) {
                char *unconfirmed_addr = "(unknown)";

                if (unconf->gsh_client != NULL)
                    unconfirmed_addr =
                        unconf->gsh_client->hostaddr_str;

                LogDebug(component,
                         "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE",
                         str_clientid4,
                         str_client_addr,
                         unconfirmed_addr);
            }

            dec_client_id_ref(unconf);
            res_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE;
            goto out;
        }
    }

    if (conf != NULL) {
        if (isDebug(component) && conf != NULL)
            display_clientid_name(&dspbuf_client, conf);

        /* First must match principal */
        if (!nfs_compare_clientcred(&conf->cid_credential,
                                    &data->credential)) {
            if (isDebug(component)) {
                char *confirmed_addr = "(unknown)";

                if (conf->gsh_client != NULL)
                    confirmed_addr =
                        conf->gsh_client->hostaddr_str;

                LogDebug(component,
                         "Confirmed ClientId %s->%s addr=%s: Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE",
                         str_clientid4, str_client,
                         str_client_addr, confirmed_addr);
            }

            /* Release our reference to the confirmed clientid. */
            dec_client_id_ref(conf);
            res_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE;
            goto out;
        }

        /* In this case, the record was confirmed proceed with
           CREATE_SESSION */
    }

    /* We don't need to do any further principal checks, we can't
     * have a confirmed clientid record with a different principal
     * than the unconfirmed record.
     */

    /* At this point, we need to try and create the session before
     * we modify the confirmed and/or unconfirmed clientid
     * records.
     */

    /* Check flags value (test CSESS15) */
    if (arg_CREATE_SESSION4->csa_flags &
            ~(CREATE_SESSION4_FLAG_PERSIST |
              CREATE_SESSION4_FLAG_CONN_BACK_CHAN |
              CREATE_SESSION4_FLAG_CONN_RDMA)) {
        LogDebug(component,
                 "Invalid create session flags %" PRIu32,
                 arg_CREATE_SESSION4->csa_flags);
        dec_client_id_ref(found);
        res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL;
        goto out;
    }

    /* Record session related information at the right place */
    nfs41_session = pool_alloc(nfs41_session_pool);

    if (nfs41_session == NULL) {
        LogCrit(component, "Could not allocate memory for a session");
        dec_client_id_ref(found);
        res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT;
        goto out;
    }

    nfs41_session->clientid = clientid;
    nfs41_session->clientid_record = found;
    nfs41_session->refcount = 2;	/* sentinel ref + call path ref */
    nfs41_session->fore_channel_attrs =
        arg_CREATE_SESSION4->csa_fore_chan_attrs;
    nfs41_session->back_channel_attrs =
        arg_CREATE_SESSION4->csa_back_chan_attrs;
    nfs41_session->xprt = data->req->rq_xprt;
    nfs41_session->flags = false;
    nfs41_session->cb_program = 0;
    PTHREAD_MUTEX_init(&nfs41_session->cb_mutex, NULL);
    PTHREAD_COND_init(&nfs41_session->cb_cond, NULL);
    for (i = 0; i < NFS41_NB_SLOTS; i++)
        PTHREAD_MUTEX_init(&nfs41_session->slots[i].lock, NULL);

    /* Take reference to clientid record on behalf the session. */
    inc_client_id_ref(found);

    /* add to head of session list (encapsulate?) */
    PTHREAD_MUTEX_lock(&found->cid_mutex);
    glist_add(&found->cid_cb.v41.cb_session_list,
              &nfs41_session->session_link);
    PTHREAD_MUTEX_unlock(&found->cid_mutex);

    /* Set ca_maxrequests */
    nfs41_session->fore_channel_attrs.ca_maxrequests = NFS41_NB_SLOTS;
    nfs41_Build_sessionid(&clientid, nfs41_session->session_id);

    res_CREATE_SESSION4ok->csr_sequence = arg_CREATE_SESSION4->csa_sequence;

    /* return the input for wanting of something better (will
     * change in later versions)
     */
    res_CREATE_SESSION4ok->csr_fore_chan_attrs =
        nfs41_session->fore_channel_attrs;
    res_CREATE_SESSION4ok->csr_back_chan_attrs =
        nfs41_session->back_channel_attrs;
    res_CREATE_SESSION4ok->csr_flags = 0;

    memcpy(res_CREATE_SESSION4ok->csr_sessionid,
           nfs41_session->session_id,
           NFS4_SESSIONID_SIZE);

    /* Create Session replay cache */
    data->cached_res = &found->cid_create_session_slot.cached_result;
    found->cid_create_session_slot.cache_used = true;

    LogDebug(component, "CREATE_SESSION replay=%p", data->cached_res);

    if (!nfs41_Session_Set(nfs41_session)) {
        LogDebug(component, "Could not insert session into table");

        /* Release the session resources by dropping our reference
         * and the sentinel reference.
         */
        dec_session_ref(nfs41_session);
        dec_session_ref(nfs41_session);

        /* Decrement our reference to the clientid record */
        dec_client_id_ref(found);

        /* Maybe a more precise status would be better */
        res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT;
        goto out;
    }

    /* Make sure we have a reference to the confirmed clientid
       record if any */
    if (conf == NULL) {
        conf = client_record->cr_confirmed_rec;

        if (isDebug(component) && conf != NULL)
            display_clientid_name(&dspbuf_client, conf);

        /* Need a reference to the confirmed record for below */
        if (conf != NULL)
            inc_client_id_ref(conf);
    }

    if (conf != NULL && conf->cid_clientid != clientid) {
        /* Old confirmed record - need to expire it */
        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Expiring %s", str);
        }

        /* Expire clientid and release our reference. */
        nfs_client_id_expire(conf, false);
        dec_client_id_ref(conf);
        conf = NULL;
    }

    if (conf != NULL) {
        /* At this point we are updating the confirmed
         * clientid.  Update the confirmed record from the
         * unconfirmed record.
         */
        display_clientid(&dspbuf_clientid4, conf->cid_clientid);
        LogDebug(component,
                 "Updating clientid %s->%s cb_program=%u",
                 str_clientid4, str_client,
                 arg_CREATE_SESSION4->csa_cb_program);

        if (unconf != NULL) {
            /* unhash the unconfirmed clientid record */
            remove_unconfirmed_client_id(unconf);
            /* Release our reference to the unconfirmed entry */
            dec_client_id_ref(unconf);
        }

        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Updated %s", str);
        }
    } else {
        /* This is a new clientid */
        if (isFullDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, unconf);
            LogFullDebug(component, "Confirming new %s", str);
        }

        rc = nfs_client_id_confirm(unconf, component);

        if (rc != CLIENT_ID_SUCCESS) {
            res_CREATE_SESSION4->csr_status =
                clientid_error_to_nfsstat_no_expire(rc);

            /* Need to destroy the session */
            if (!nfs41_Session_Del(nfs41_session->session_id))
                LogDebug(component,
                         "Oops nfs41_Session_Del failed");

            /* Release our reference to the unconfirmed record */
            dec_client_id_ref(unconf);
            goto out;
        }
        nfs4_chk_clid(unconf);

        conf = unconf;
        unconf = NULL;

        if (isDebug(component)) {
            char str[LOG_BUFF_LEN];
            struct display_buffer dspbuf = {sizeof(str), str, str};

            display_client_id_rec(&dspbuf, conf);
            LogDebug(component, "Confirmed %s", str);
        }
    }
    conf->cid_create_session_sequence++;

    /* Bump the lease timer */
    conf->cid_last_renew = time(NULL);

    /* Release our reference to the confirmed record */
    dec_client_id_ref(conf);

    if (isFullDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_client_record(&dspbuf, client_record);
        LogFullDebug(component,
                     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
                     str,
                     client_record->cr_confirmed_rec,
                     client_record->cr_unconfirmed_rec);
    }

    /* Handle the creation of the back channel, if the client
       requested one. */
    if (arg_CREATE_SESSION4->csa_flags &
            CREATE_SESSION4_FLAG_CONN_BACK_CHAN) {
        nfs41_session->cb_program = arg_CREATE_SESSION4->csa_cb_program;
        if (nfs_rpc_create_chan_v41(
                    nfs41_session,
                    arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_len,
                    arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_val)
                == 0) {
            res_CREATE_SESSION4ok->csr_flags |=
                CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
        }
    }

    if (isDebug(component)) {
        char str[LOG_BUFF_LEN];
        struct display_buffer dspbuf = {sizeof(str), str, str};

        display_session(&dspbuf, nfs41_session);

        LogDebug(component,
                 "success %s csa_flags 0x%X csr_flags 0x%X",
                 str, arg_CREATE_SESSION4->csa_flags,
                 res_CREATE_SESSION4ok->csr_flags);
    }

    /* Release our reference to the session */
    dec_session_ref(nfs41_session);

    /* Successful exit */
    res_CREATE_SESSION4->csr_status = NFS4_OK;

out:
    PTHREAD_MUTEX_unlock(&client_record->cr_mutex);
    /* Release our reference to the client record and return */
    dec_client_record_ref(client_record);
    return res_CREATE_SESSION4->csr_status;
}
Exemple #12
0
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_setclientid(struct nfs_argop4 *op, compound_data_t *data,
			struct nfs_resop4 *resp)
{
	SETCLIENTID4args * const arg_SETCLIENTID4 =
	    &op->nfs_argop4_u.opsetclientid;
	SETCLIENTID4res * const res_SETCLIENTID4 =
	    &resp->nfs_resop4_u.opsetclientid;
	clientaddr4 * const res_SETCLIENTID4_INUSE =
	    &resp->nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.client_using;
	char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1];
	struct display_buffer dspbuf_verifier = {
			sizeof(str_verifier), str_verifier, str_verifier};
	char str_client[NFS4_OPAQUE_LIMIT * 2 + 1];
	struct display_buffer dspbuf_client = {
			sizeof(str_client), str_client, str_client};
	const char *str_client_addr = "(unknown)";
	/* The clientid4 broken down into fields */
	char str_clientid4[DISPLAY_CLIENTID_SIZE];
	/* Display buffer for clientid4 */
	struct display_buffer dspbuf_clientid4 = {
		sizeof(str_clientid4), str_clientid4, str_clientid4};
	nfs_client_record_t *client_record;
	nfs_client_id_t *conf;
	nfs_client_id_t *unconf;
	clientid4 clientid;
	verifier4 verifier;
	int rc;

	resp->resop = NFS4_OP_SETCLIENTID;

	if (data->minorversion > 0) {
		res_SETCLIENTID4->status = NFS4ERR_NOTSUPP;
		return res_SETCLIENTID4->status;
	}

	if (op_ctx->client != NULL)
		str_client_addr = op_ctx->client->hostaddr_str;

	if (isDebug(COMPONENT_CLIENTID)) {
		display_opaque_value(&dspbuf_client,
				     arg_SETCLIENTID4->client.id.id_val,
				     arg_SETCLIENTID4->client.id.id_len);

		display_opaque_bytes(&dspbuf_verifier,
				     arg_SETCLIENTID4->client.verifier,
				     NFS4_VERIFIER_SIZE);
	} else {
		str_client[0] = '\0';
		str_verifier[0] = '\0';
	}

	LogDebug(COMPONENT_CLIENTID,
		 "SETCLIENTID Client addr=%s id=%s verf=%s callback={program=%u r_addr=%s r_netid=%s} ident=%u",
		 str_client_addr, str_client, str_verifier,
		 arg_SETCLIENTID4->callback.cb_program,
		 arg_SETCLIENTID4->callback.cb_location.r_addr,
		 arg_SETCLIENTID4->callback.cb_location.r_netid,
		 arg_SETCLIENTID4->callback_ident);

	/* Do we already have one or more records for client id (x)? */
	client_record = get_client_record(arg_SETCLIENTID4->client.id.id_val,
					  arg_SETCLIENTID4->client.id.id_len,
					  0,
					  0);

	if (client_record == NULL) {
		/* Some major failure */
		LogCrit(COMPONENT_CLIENTID, "SETCLIENTID failed");

		res_SETCLIENTID4->status = NFS4ERR_SERVERFAULT;
		return res_SETCLIENTID4->status;
	}

	/* The following checks are based on RFC3530bis draft 16
	 *
	 * This attempts to implement the logic described in
	 * 15.35.5. IMPLEMENTATION Consider the major bullets as CASE
	 * 1, CASE 2, CASE 3, CASE 4, and CASE 5.
	 */

	PTHREAD_MUTEX_lock(&client_record->cr_mutex);

	if (isFullDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN] = "\0";
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_client_record(&dspbuf, client_record);

		LogFullDebug(COMPONENT_CLIENTID,
			     "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p",
			     str,
			     client_record->cr_confirmed_rec,
			     client_record->cr_unconfirmed_rec);
	}

	conf = client_record->cr_confirmed_rec;

	if (conf != NULL) {
		bool credmatch;

		/* Need a reference to the confirmed record for below */
		inc_client_id_ref(conf);

		clientid = conf->cid_clientid;
		display_clientid(&dspbuf_clientid4, clientid);

		credmatch = nfs_compare_clientcred(&conf->cid_credential,
						   &data->credential)
			    && op_ctx->client != NULL
			    && conf->gsh_client != NULL
			    && op_ctx->client == conf->gsh_client;

		/* Only reject if the principal doesn't match and the
		 * clientid has live state associated. Per RFC 7530
		 * Section 9.1.2. Server Release of Client ID.
		 */
		if (!credmatch && clientid_has_state(conf)) {
			/* CASE 1:
			 *
			 * Confirmed record exists and not the same principal
			 */
			if (isDebug(COMPONENT_CLIENTID)) {
				char *confirmed_addr = "(unknown)";

				if (conf->gsh_client != NULL)
					confirmed_addr =
					    conf->gsh_client->hostaddr_str;

				LogDebug(COMPONENT_CLIENTID,
					 "Confirmed ClientId %s->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE",
					 str_clientid4, str_client,
					 confirmed_addr);
			}

			res_SETCLIENTID4->status = NFS4ERR_CLID_INUSE;
			res_SETCLIENTID4_INUSE->r_netid =
			    (char *)netid_nc_table[conf->cid_cb.v40.cb_addr.nc]
			    .netid;
			res_SETCLIENTID4_INUSE->r_addr =
			    gsh_strdup(conf->cid_cb.v40.cb_client_r_addr);

			/* Release our reference to the confirmed clientid. */
			dec_client_id_ref(conf);
			goto out;
		}

		/* Check if confirmed record is for (v, x, c, l, s) */
		if (credmatch && memcmp(arg_SETCLIENTID4->client.verifier,
					conf->cid_incoming_verifier,
					NFS4_VERIFIER_SIZE) == 0) {
			/* CASE 2:
			 *
			 * A confirmed record exists for this long
			 * form client id and verifier.
			 *
			 * Consider this to be a possible update of
			 * the call-back information.
			 *
			 * Remove any pre-existing unconfirmed record
			 * for (v, x, c).
			 *
			 * Return the same short form client id (c),
			 * but a new setclientid_confirm verifier (t).
			 */
			LogFullDebug(COMPONENT_CLIENTID,
				     "Update ClientId %s->%s",
				     str_clientid4, str_client);

			new_clientid_verifier(verifier);
		} else {
			/* Must be CASE 3 or CASE 4
			 *
			 * Confirmed record is for (u, x, c, l, s).
			 *
			 * These are actually the same, doesn't really
			 * matter if an unconfirmed record exists or
			 * not. Any existing unconfirmed record will
			 * be removed and a new unconfirmed record
			 * added.
			 *
			 * Return a new short form clientid (d) and a
			 * new setclientid_confirm verifier (t). (Note
			 * the spec calls the values e and r for CASE
			 * 4).
			 */
			LogFullDebug(COMPONENT_CLIENTID,
				     "Replace ClientId %s->%s",
				     str_clientid4, str_client);

			clientid = new_clientid();
			new_clientid_verifier(verifier);
		}

		/* Release our reference to the confirmed clientid. */
		dec_client_id_ref(conf);
	} else {
		/* CASE 5:
		 *
		 *
		 * Remove any existing unconfirmed record.
		 *
		 * Return a new short form clientid (d) and a new
		 * setclientid_confirm verifier (t).
		 */
		clientid = new_clientid();
		display_clientid(&dspbuf_clientid4, clientid);
		LogFullDebug(COMPONENT_CLIENTID,
			     "New client %s", str_clientid4);
		new_clientid_verifier(verifier);
	}

	/* At this point, no matter what the case was above, we should
	 * remove any pre-existing unconfirmed record.
	 */

	unconf = client_record->cr_unconfirmed_rec;

	if (unconf != NULL) {
		/* Delete the unconfirmed clientid record. Because we
		 * have the cr_mutex, we have won any race to deal
		 * with this clientid record (whether we raced with a
		 * SETCLIENTID_CONFIRM or the reaper thread (if either
		 * of those operations had won the race,
		 * cr_punconfirmed_id would have been NULL).
		 */
		if (isDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);

			LogDebug(COMPONENT_CLIENTID, "Replacing %s", str);
		}

		/* unhash the clientid record */
		remove_unconfirmed_client_id(unconf);
		unconf = NULL;
	}

	/* Now we can proceed to build the new unconfirmed record. We
	 * have determined the clientid and setclientid_confirm values
	 * above.
	 */

	unconf = create_client_id(clientid,
				  client_record,
				  &data->credential,
				  0);

	if (unconf == NULL) {
		/* Error already logged, return */
		res_SETCLIENTID4->status = NFS4ERR_RESOURCE;
		goto out;
	}

	if (strmaxcpy(unconf->cid_cb.v40.cb_client_r_addr,
		      arg_SETCLIENTID4->callback.cb_location.r_addr,
		      sizeof(unconf->cid_cb.v40.cb_client_r_addr)) == -1) {
		LogCrit(COMPONENT_CLIENTID, "Callback r_addr %s too long",
			arg_SETCLIENTID4->callback.cb_location.r_addr);
		res_SETCLIENTID4->status = NFS4ERR_INVAL;

		goto out;
	}

	nfs_set_client_location(unconf,
				&arg_SETCLIENTID4->callback.cb_location);

	memcpy(unconf->cid_incoming_verifier,
	       arg_SETCLIENTID4->client.verifier,
	       NFS4_VERIFIER_SIZE);

	memcpy(unconf->cid_verifier, verifier, sizeof(NFS4_write_verifier));

	unconf->cid_cb.v40.cb_program = arg_SETCLIENTID4->callback.cb_program;
	unconf->cid_cb.v40.cb_callback_ident = arg_SETCLIENTID4->callback_ident;

	rc = nfs_client_id_insert(unconf);

	if (rc != CLIENT_ID_SUCCESS) {
		/* Record is already freed, return. */
		res_SETCLIENTID4->status =
					clientid_error_to_nfsstat_no_expire(rc);
		goto out;
	}

	if (isDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN] = "\0";
		struct display_buffer dspbuf = {sizeof(str), str, str};

		sprint_mem(str_verifier, verifier, NFS4_VERIFIER_SIZE);

		display_client_id_rec(&dspbuf, unconf);
		LogDebug(COMPONENT_CLIENTID, "SETCLIENTID reply Verifier=%s %s",
			 str_verifier, str);
	}

	res_SETCLIENTID4->status = NFS4_OK;
	res_SETCLIENTID4->SETCLIENTID4res_u.resok4.clientid = clientid;
	memcpy(res_SETCLIENTID4->SETCLIENTID4res_u.resok4.setclientid_confirm,
	       &verifier, NFS4_VERIFIER_SIZE);

 out:

	PTHREAD_MUTEX_unlock(&client_record->cr_mutex);

	/* Release our reference to the client record */
	dec_client_record_ref(client_record);

	return res_SETCLIENTID4->status;
}
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 */
/**
 * @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;
}
static void reap_hash_table(hash_table_t * ht_reap)
{
  struct rbt_head     * head_rbt;
  hash_data_t         * pdata = NULL;
  uint32_t              i;
  int                   v4, rc;
  struct rbt_node     * pn;
  nfs_client_id_t     * pclientid;
  nfs_client_record_t * precord;

  /* For each bucket of the requested hashtable */
  for(i = 0; i < ht_reap->parameter.index_size; i++)
    {
      head_rbt = &ht_reap->partitions[i].rbt;

 restart:
      /* acquire mutex */
      pthread_rwlock_wrlock(&ht_reap->partitions[i].lock);

      /* go through all entries in the red-black-tree*/
      RBT_LOOP(head_rbt, pn)
        {
          pdata = RBT_OPAQ(pn);

          pclientid = (nfs_client_id_t *)pdata->buffval.pdata;
          /*
           * little hack: only want to reap v4 clients
           * 4.1 initializess this field to '1'
           */
          v4 = (pclientid->cid_create_session_sequence == 0);

          P(pclientid->cid_mutex);

          if(!valid_lease(pclientid) && v4)
            {
              inc_client_id_ref(pclientid);

              /* Take a reference to the client record */
              precord = pclientid->cid_client_record;
              inc_client_record_ref(precord);

              V(pclientid->cid_mutex);

              pthread_rwlock_unlock(&ht_reap->partitions[i].lock);

              if(isDebug(COMPONENT_CLIENTID))
                {
                  char str[HASHTABLE_DISPLAY_STRLEN];

                  display_client_id_rec(pclientid, str);

                  LogFullDebug(COMPONENT_CLIENTID,
                               "Expire index %d %s",
                               i, str);
                }

              /* Take cr_mutex and expire clientid */
              P(precord->cr_mutex);

              rc = nfs_client_id_expire(pclientid);

              V(precord->cr_mutex);

              dec_client_id_ref(pclientid);
              dec_client_record_ref(precord);
              if(rc)
                goto restart;
            }
          else
            {
              V(pclientid->cid_mutex);
            }

          RBT_INCREMENT(pn);
        }

      pthread_rwlock_unlock(&ht_reap->partitions[i].lock);
    }
int nfs4_op_destroy_clientid(struct nfs_argop4 *op, compound_data_t *data,
			     struct nfs_resop4 *resp)
{
	DESTROY_CLIENTID4args * const arg_DESTROY_CLIENTID4 =
	    &op->nfs_argop4_u.opdestroy_clientid;
	DESTROY_CLIENTID4res * const res_DESTROY_CLIENTID4 =
	    &resp->nfs_resop4_u.opdestroy_clientid;
	nfs_client_record_t *client_record = NULL;
	nfs_client_id_t *conf = NULL, *unconf = NULL, *found = NULL;
	clientid4 clientid;
	int rc;

	resp->resop = NFS4_OP_DESTROY_CLIENTID;

	clientid = arg_DESTROY_CLIENTID4->dca_clientid;

	if (isDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN];
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_clientid(&dspbuf, clientid);

		LogDebug(COMPONENT_CLIENTID,
			 "DESTROY_CLIENTID clientid=%s",
			 str);
	}

	res_DESTROY_CLIENTID4->dcr_status = NFS4_OK;

	/* First try to look up confirmed record */
	rc = nfs_client_id_get_confirmed(clientid, &conf);

	if (rc == CLIENT_ID_SUCCESS) {
		client_record = conf->cid_client_record;
		found = conf;
	} else {
		/* fall back to unconfirmed */
		rc = nfs_client_id_get_unconfirmed(clientid, &unconf);

		if (rc == CLIENT_ID_SUCCESS) {
			client_record = unconf->cid_client_record;
			found = unconf;
		}

		/* handle the perverse case of a clientid being confirmed
		 * in the above interval */
		rc = nfs_client_id_get_confirmed(clientid, &conf);

		if (rc == CLIENT_ID_SUCCESS) {
			if (found != NULL)
				dec_client_id_ref(found);
			client_record = conf->cid_client_record;
			found = conf;
		}
	}

	/* ref +1 */
	if (client_record == NULL) {
		/* Fine.  We're done. */
		res_DESTROY_CLIENTID4->dcr_status = NFS4ERR_STALE_CLIENTID;
		goto out;
	}

	(void) inc_client_record_ref(client_record);

	PTHREAD_MUTEX_lock(&client_record->cr_mutex);

	if (isFullDebug(COMPONENT_CLIENTID)) {
		char str[LOG_BUFF_LEN];
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_client_record(&dspbuf, client_record);

		LogFullDebug(COMPONENT_CLIENTID,
			     "Client Record %s cr_confirmed_rec=%p "
			     "cr_unconfirmed_rec=%p", str,
			     client_record->cr_confirmed_rec,
			     client_record->cr_unconfirmed_rec);
	}

	/* per Frank, we must check the confirmed and unconfirmed
	 * state of client_record again now that we hold cr_mutex
	 */
	conf = client_record->cr_confirmed_rec;
	unconf = client_record->cr_unconfirmed_rec;

	if ((!conf) && (!unconf)) {
		/* We raced a thread destroying clientid, and lost.
		 * We're done. */
		goto cleanup;
	}

	if (conf) {
		/* We MUST NOT destroy a clientid that has nfsv41 sessions or
		 * state. Since the minorversion is 4.1 or higher, this is
		 * equivalent to a session check.
		 */
		PTHREAD_MUTEX_lock(&conf->cid_mutex);
		if (!glist_empty(&conf->cid_cb.v41.cb_session_list)) {
			res_DESTROY_CLIENTID4->dcr_status =
							NFS4ERR_CLIENTID_BUSY;
			PTHREAD_MUTEX_unlock(&conf->cid_mutex);
			goto cleanup;
		}
		PTHREAD_MUTEX_unlock(&conf->cid_mutex);

		/* Delete the confirmed clientid record. Because we
		 * have the cr_mutex, we have won any race to deal
		 * with this clientid record.
		 */
		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN];
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, conf);

			LogDebug(COMPONENT_CLIENTID,
				 "Removing confirmed clientid %s", str);
		}

		/* unhash the clientid record */
		(void)remove_confirmed_client_id(conf);
	}

	if (unconf) {
		/* Delete the unconfirmed clientid record. Because we
		 * have the cr_mutex, we have won any race to deal
		 * with this clientid record.
		 */
		if (isFullDebug(COMPONENT_CLIENTID)) {
			char str[LOG_BUFF_LEN];
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_client_id_rec(&dspbuf, unconf);

			LogDebug(COMPONENT_CLIENTID,
				 "Removing unconfirmed clientid %s", str);
		}

		/* unhash the clientid record */
		(void)remove_unconfirmed_client_id(unconf);
	}

 cleanup:

	PTHREAD_MUTEX_unlock(&client_record->cr_mutex);
	dec_client_record_ref(client_record);	/* ref +0 */

	if (found != NULL)
		dec_client_id_ref(found);

 out:

	return res_DESTROY_CLIENTID4->dcr_status;
}
Exemple #18
0
static int reap_hash_table(hash_table_t *ht_reap)
{
	struct rbt_head *head_rbt;
	struct hash_data *addr = NULL;
	uint32_t i;
	int rc;
	struct rbt_node *pn;
	nfs_client_id_t *pclientid;
	nfs_client_record_t *precord;
	int count = 0;
	struct req_op_context req_ctx;
	struct user_cred creds;

	/* We need a real context.  Make all reaping done
	 * by root,root
	 */
	memset(&creds, 0, sizeof(creds));
	req_ctx.creds = &creds;

	/* For each bucket of the requested hashtable */
	for (i = 0; i < ht_reap->parameter.index_size; i++) {
		head_rbt = &ht_reap->partitions[i].rbt;

 restart:
		/* acquire mutex */
		PTHREAD_RWLOCK_wrlock(&ht_reap->partitions[i].lock);

		/* go through all entries in the red-black-tree */
		RBT_LOOP(head_rbt, pn) {
			addr = RBT_OPAQ(pn);

			pclientid = addr->val.addr;
			count++;

			pthread_mutex_lock(&pclientid->cid_mutex);

			if (!valid_lease(pclientid)) {
				inc_client_id_ref(pclientid);

				/* Take a reference to the client record */
				precord = pclientid->cid_client_record;
				inc_client_record_ref(precord);

				pthread_mutex_unlock(&pclientid->cid_mutex);

				PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].
						      lock);

				if (isDebug(COMPONENT_CLIENTID)) {
					char str[HASHTABLE_DISPLAY_STRLEN];

					display_client_id_rec(pclientid, str);

					LogFullDebug(COMPONENT_CLIENTID,
						     "Expire index %d %s", i,
						     str);
				}

				/* Take cr_mutex and expire clientid */
				pthread_mutex_lock(&precord->cr_mutex);
/**
 * @TODO This is incomplete! the context has to be filled in from
 * somewhere
 */
				memset(&req_ctx, 0, sizeof(req_ctx));
				rc = nfs_client_id_expire(pclientid, &req_ctx);

				pthread_mutex_unlock(&precord->cr_mutex);

				dec_client_id_ref(pclientid);
				dec_client_record_ref(precord);
				if (rc)
					goto restart;
			} else {
				pthread_mutex_unlock(&pclientid->cid_mutex);
			}

			RBT_INCREMENT(pn);
		}

		PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock);
	}
int nfs4_op_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;
}
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;
}
Exemple #21
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 */