/**
 *
 * @brief invalidates an entry in the cache
 *
 * This function invalidates the related cache entry correponding to a
 * FSAL handle. It is designed to be called when an FSAL upcall is
 * triggered.
 *
 * @param[in]  handle FSAL handle for the entry to be invalidated
 * @param[out] status Returned status
 *
 * @retval CACHE_INODE_SUCCESS if operation is a success
 * @retval CACHE_INODE_INVALID_ARGUMENT bad parameter(s) as input
 * @retval CACHE_INODE_NOT_FOUND if entry is not cached
 * @retval CACHE_INODE_STATE_CONFLICT if invalidating this entry would
 *                                    result is state conflict
 * @retval CACHE_INODE_INCONSISTENT_ENTRY if entry is not consistent
 * @retval Other errors shows a FSAL error.
 *
 */
cache_inode_status_t
cache_inode_invalidate(cache_inode_fsal_data_t *fsal_data,
                       cache_inode_status_t *status,
                       uint32_t flags)
{
     hash_buffer_t key, value;
     int rc = 0 ;
     cache_entry_t *entry;
     struct hash_latch latch;

     if (status == NULL || fsal_data == NULL) {
          *status = CACHE_INODE_INVALID_ARGUMENT;
          goto out;
     }

     /* Locate the entry in the cache */
     FSAL_ExpandHandle(NULL,  /* pcontext but not used... */
                       FSAL_DIGEST_SIZEOF,
                       &fsal_data->fh_desc);

     /* Turn the input to a hash key */
     key.pdata = fsal_data->fh_desc.start;
     key.len = fsal_data->fh_desc.len;

     if ((rc = HashTable_GetLatch(fh_to_cache_entry_ht,
                                  &key,
                                  &value,
                                  FALSE,
                                  &latch)) == HASHTABLE_ERROR_NO_SUCH_KEY) {
          /* Entry is not cached */
          HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);
          *status = CACHE_INODE_NOT_FOUND;
          return *status;
     } else if (rc != HASHTABLE_SUCCESS) {
          LogCrit(COMPONENT_CACHE_INODE,
                  "Unexpected error %u while calling HashTable_GetLatch", rc) ;
          *status = CACHE_INODE_INVALID_ARGUMENT;
          goto out;
     }

     entry = value.pdata;
     if (cache_inode_lru_ref(entry, 0) != CACHE_INODE_SUCCESS) {
          HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);
          *status = CACHE_INODE_NOT_FOUND;
          return *status;
     }
     HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);

     PTHREAD_RWLOCK_WRLOCK(&entry->attr_lock);
     PTHREAD_RWLOCK_WRLOCK(&entry->content_lock);

     /* We can invalidate entries with state just fine.  We force
        Cache_inode to contact the FSAL for any use of content or
        attributes, and if the FSAL indicates the entry is stale, it
        can be disposed of then. */

     /* We should have a way to invalidate content and attributes
        separately.  Or at least a way to invalidate attributes
        without invalidating content (since any change in content
        really ought to modify mtime, at least.) */

     if ((flags & CACHE_INODE_INVALIDATE_CLEARBITS) != 0)
       atomic_clear_uint32_t_bits(&entry->flags,
                                  CACHE_INODE_TRUST_ATTRS |
                                  CACHE_INODE_DIR_POPULATED |
                                  CACHE_INODE_TRUST_CONTENT);

     /* The main reason for holding the lock at this point is so we
        don't clear the trust bits while someone is populating the
        directory or refreshing attributes. */

     if (((flags & CACHE_INODE_INVALIDATE_CLOSE) != 0) &&
         (entry->type == REGULAR_FILE)) {
          cache_inode_close(entry,
                            NULL,
                            (CACHE_INODE_FLAG_REALLYCLOSE |
                             CACHE_INODE_FLAG_CONTENT_HAVE |
                             CACHE_INODE_FLAG_CONTENT_HOLD),
                            status);
     }

     PTHREAD_RWLOCK_UNLOCK(&entry->attr_lock);
     PTHREAD_RWLOCK_UNLOCK(&entry->content_lock);

     cache_inode_lru_unref(entry, 0);

out:

     /* Memory copying attributes with every call is expensive.
        Let's not do it.  */

     return (*status);
} /* cache_inode_invalidate */
Esempio n. 2
0
state_nlm_client_t *get_nlm_client(care_t               care,
                                   SVCXPRT            * xprt,
                                   state_nsm_client_t * pnsm_client,
                                   char               * caller_name)
{
  state_nlm_client_t   key;
  state_nlm_client_t * pclient;
  char                 str[HASHTABLE_DISPLAY_STRLEN];
  struct hash_latch    latch;
  hash_error_t         rc;
  hash_buffer_t        buffkey;
  hash_buffer_t        buffval;

  if(caller_name == NULL)
    return NULL;

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

  key.slc_nsm_client          = pnsm_client;
  key.slc_nlm_caller_name_len = strlen(caller_name);
  key.slc_client_type         = svc_get_xprt_type(xprt);

  if(key.slc_nlm_caller_name_len > LM_MAXSTRLEN)
    return NULL;

  key.slc_nlm_caller_name = caller_name;
  
  if(isFullDebug(COMPONENT_STATE))
    {
      display_nlm_client(&key, str);
      LogFullDebug(COMPONENT_STATE,
                   "Find {%s}", str);
    }

  buffkey.pdata = &key;
  buffkey.len   = sizeof(key);

  rc = HashTable_GetLatch(ht_nlm_client,
                          &buffkey,
                          &buffval,
                          TRUE,
                          &latch);

  /* If we found it, return it */
  if(rc == HASHTABLE_SUCCESS)
    {
      pclient = buffval.pdata;

      /* Return the found NLM Client */
      if(isFullDebug(COMPONENT_STATE))
        {
          display_nlm_client(pclient, str);
          LogFullDebug(COMPONENT_STATE,
                       "Found {%s}",
                       str);
        }

      /* Increment refcount under hash latch.
       * This prevents dec ref from removing this entry from hash if a race
       * occurs.
       */
      inc_nlm_client_ref(pclient);

      HashTable_ReleaseLatched(ht_nlm_client, &latch);

      if(care == CARE_MONITOR && !nsm_monitor(pnsm_client))
          {
            dec_nlm_client_ref(pclient);
            pclient = NULL;
          }

      return pclient;
    }

  /* An error occurred, return NULL */
  if(rc != HASHTABLE_ERROR_NO_SUCH_KEY)
    {
      display_nlm_client(&key, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, could not find {%s}",
              hash_table_err_to_str(rc), str);

      return NULL;
    }

  /* Not found, but we don't care, return NULL */
  if(care == CARE_NOT)
    {
      /* Return the found NLM Client */
      if(isFullDebug(COMPONENT_STATE))
        {
          display_nlm_client(&key, str);
          LogFullDebug(COMPONENT_STATE,
                       "Ignoring {%s}",
                       str);
        }

      HashTable_ReleaseLatched(ht_nlm_client, &latch);

      return NULL;
    }

  pclient = gsh_malloc(sizeof(*pclient));

  if(pclient == NULL)
    {
      display_nlm_client(&key, str);
      LogCrit(COMPONENT_STATE,
              "No memory for {%s}",
              str);

      return NULL;
    }

  /* Copy everything over */
  memcpy(pclient, &key, sizeof(key));

  pclient->slc_nlm_caller_name = gsh_strdup(key.slc_nlm_caller_name);

  /* Take a reference to the NSM Client */
  inc_nsm_client_ref(pnsm_client);

  if(pclient->slc_nlm_caller_name == NULL)
    {
      /* Discard the created client */
      free_nlm_client(pclient);
      return NULL;
    }

  pclient->slc_refcount = 1;

  if(isFullDebug(COMPONENT_STATE))
    {
      display_nlm_client(pclient, str);
      LogFullDebug(COMPONENT_STATE,
                   "New {%s}", str);
    }

  buffkey.pdata = pclient;
  buffkey.len   = sizeof(*pclient);
  buffval.pdata = pclient;
  buffval.len   = sizeof(*pclient);

  rc = HashTable_SetLatched(ht_nlm_client,
                            &buffval,
                            &buffval,
                            &latch,
                            FALSE,
                            NULL,
                            NULL);

  /* An error occurred, return NULL */
  if(rc != HASHTABLE_SUCCESS)
    {
      display_nlm_client(pclient, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, inserting {%s}",
              hash_table_err_to_str(rc), str);

      free_nlm_client(pclient);

      return NULL;
    }

  if(care != CARE_MONITOR || nsm_monitor(pnsm_client))
    return pclient;

  /* Failed to monitor, release client reference
   * and almost certainly remove it from the hash table.
   */
  dec_nlm_client_ref(pclient);

  return NULL;
}
Esempio n. 3
0
state_nsm_client_t *get_nsm_client(care_t    care,
                                   SVCXPRT * xprt,
                                   char    * caller_name)
{
  state_nsm_client_t   key;
  state_nsm_client_t * pclient;
  char                 sock_name[SOCK_NAME_MAX];
  char                 str[HASHTABLE_DISPLAY_STRLEN];
  struct hash_latch    latch;
  hash_error_t         rc;
  hash_buffer_t        buffkey;
  hash_buffer_t        buffval;

  if(caller_name == NULL)
    return NULL;

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

  if(nfs_param.core_param.nsm_use_caller_name)
    {
      key.ssc_nlm_caller_name_len = strlen(caller_name);

      if(key.ssc_nlm_caller_name_len > LM_MAXSTRLEN)
        {
          return NULL;
        }

      key.ssc_nlm_caller_name = caller_name;
    }
  else if(xprt == NULL)
    {
      int rc = ipstring_to_sockaddr(caller_name, &key.ssc_client_addr);
      if(rc != 0)
        {
          LogEvent(COMPONENT_STATE,
                   "Error %s, converting caller_name %s to an ipaddress",
                   gai_strerror(rc), caller_name);

          return NULL;
        }

      key.ssc_nlm_caller_name_len = strlen(caller_name);

      if(key.ssc_nlm_caller_name_len > LM_MAXSTRLEN)
        {
          return NULL;
        }

      key.ssc_nlm_caller_name = caller_name;
    }
  else
    {
      key.ssc_nlm_caller_name = sock_name;

      if(copy_xprt_addr(&key.ssc_client_addr, xprt) == 0)
        {
          LogCrit(COMPONENT_STATE,
                  "Error converting caller_name %s to an ipaddress",
                  caller_name);

          return NULL;
        }

      if(sprint_sockip(&key.ssc_client_addr,
                       key.ssc_nlm_caller_name,
                       sizeof(sock_name)) == 0)
        {
          LogCrit(COMPONENT_STATE,
                  "Error converting caller_name %s to an ipaddress",
                  caller_name);

          return NULL;
        }

      key.ssc_nlm_caller_name_len = strlen(key.ssc_nlm_caller_name);
    }

  
  if(isFullDebug(COMPONENT_STATE))
    {
      display_nsm_client(&key, str);
      LogFullDebug(COMPONENT_STATE,
                   "Find {%s}", str);
    }

  buffkey.pdata = &key;
  buffkey.len   = sizeof(key);

  rc = HashTable_GetLatch(ht_nsm_client,
                          &buffkey,
                          &buffval,
                          TRUE,
                          &latch);

  /* If we found it, return it */
  if(rc == HASHTABLE_SUCCESS)
    {
      pclient = buffval.pdata;

      /* Return the found NSM Client */
      if(isFullDebug(COMPONENT_STATE))
        {
          display_nsm_client(pclient, str);
          LogFullDebug(COMPONENT_STATE,
                       "Found {%s}",
                       str);
        }

      /* Increment refcount under hash latch.
       * This prevents dec ref from removing this entry from hash if a race
       * occurs.
       */
      inc_nsm_client_ref(pclient);

      HashTable_ReleaseLatched(ht_nsm_client, &latch);

      if(care == CARE_MONITOR && !nsm_monitor(pclient))
          {
            dec_nsm_client_ref(pclient);
            pclient = NULL;
          }

      return pclient;
    }

  /* An error occurred, return NULL */
  if(rc != HASHTABLE_ERROR_NO_SUCH_KEY)
    {
      display_nsm_client(&key, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, could not find {%s}",
              hash_table_err_to_str(rc), str);

      return NULL;
    }

  /* Not found, but we don't care, return NULL */
  if(care == CARE_NOT)
    {
      /* Return the found NSM Client */
      if(isFullDebug(COMPONENT_STATE))
        {
          display_nsm_client(&key, str);
          LogFullDebug(COMPONENT_STATE,
                       "Ignoring {%s}",
                       str);
        }

      HashTable_ReleaseLatched(ht_nsm_client, &latch);

      return NULL;
    }

  pclient = gsh_malloc(sizeof(*pclient));

  if(pclient == NULL)
    {
      display_nsm_client(&key, str);
      LogCrit(COMPONENT_STATE,
              "No memory for {%s}",
              str);

      return NULL;
    }

  /* Copy everything over */
  memcpy(pclient, &key, sizeof(key));

  if(pthread_mutex_init(&pclient->ssc_mutex, NULL) == -1)
    {
      /* Mutex initialization failed, free the created client */
      display_nsm_client(&key, str);
      LogCrit(COMPONENT_STATE,
              "Could not init mutex for {%s}",
              str);

      gsh_free(pclient);
      return NULL;
    }

  pclient->ssc_nlm_caller_name = gsh_strdup(key.ssc_nlm_caller_name);

  if(pclient->ssc_nlm_caller_name == NULL)
    {
      /* Discard the created client */
      free_nsm_client(pclient);
      return NULL;
    }

  init_glist(&pclient->ssc_lock_list);
  init_glist(&pclient->ssc_share_list);
  pclient->ssc_refcount = 1;

  if(isFullDebug(COMPONENT_STATE))
    {
      display_nsm_client(pclient, str);
      LogFullDebug(COMPONENT_STATE,
                   "New {%s}", str);
    }

  buffkey.pdata = pclient;
  buffkey.len   = sizeof(*pclient);
  buffval.pdata = pclient;
  buffval.len   = sizeof(*pclient);

  rc = HashTable_SetLatched(ht_nsm_client,
                            &buffval,
                            &buffval,
                            &latch,
                            FALSE,
                            NULL,
                            NULL);

  /* An error occurred, return NULL */
  if(rc != HASHTABLE_SUCCESS)
    {
      display_nsm_client(pclient, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, inserting {%s}",
              hash_table_err_to_str(rc), str);

      free_nsm_client(pclient);

      return NULL;
    }

  if(care != CARE_MONITOR || nsm_monitor(pclient))
    return pclient;

  /* Failed to monitor, release client reference
   * and almost certainly remove it from the hash table.
   */
  dec_nsm_client_ref(pclient);

  return NULL;
}
Esempio n. 4
0
void dec_nlm_client_ref(state_nlm_client_t *pclient)
{
  char              str[HASHTABLE_DISPLAY_STRLEN];
  struct hash_latch latch;
  hash_error_t      rc;
  hash_buffer_t     buffkey;
  hash_buffer_t     old_value;
  hash_buffer_t     old_key;
  int32_t           refcount;

  if(isDebug(COMPONENT_STATE))
    display_nlm_client(pclient, str);

  refcount = atomic_dec_int32_t(&pclient->slc_refcount);

  if(refcount > 0)
    {
      LogFullDebug(COMPONENT_STATE,
                   "Decrement refcount now=%"PRId32" {%s}",
                   refcount, str);

      return;
    }

  LogFullDebug(COMPONENT_STATE,
               "Try to remove {%s}",
               str);

  buffkey.pdata = pclient;
  buffkey.len   = sizeof(*pclient);

  /* Get the hash table entry and hold latch */
  rc = HashTable_GetLatch(ht_nlm_client,
                          &buffkey,
                          &old_value,
                          TRUE,
                          &latch);

  if(rc != HASHTABLE_SUCCESS)
    {
      if(rc == HASHTABLE_ERROR_NO_SUCH_KEY)
        HashTable_ReleaseLatched(ht_nlm_client, &latch);

      display_nlm_client(pclient, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, could not find {%s}",
              hash_table_err_to_str(rc), str);

      return;
    }

  refcount = atomic_fetch_int32_t(&pclient->slc_refcount);

  if(refcount > 0)
    {
      LogDebug(COMPONENT_STATE,
               "Did not release refcount now=%"PRId32" {%s}",
               refcount, str);

      HashTable_ReleaseLatched(ht_nlm_client, &latch);

      return;
    }

  /* use the key to delete the entry */
  rc = HashTable_DeleteLatched(ht_nlm_client,
                               &buffkey,
                               &latch,
                               &old_key,
                               &old_value);

  if(rc != HASHTABLE_SUCCESS)
    {
      if(rc == HASHTABLE_ERROR_NO_SUCH_KEY)
        HashTable_ReleaseLatched(ht_nlm_client, &latch);

      display_nlm_client(pclient, str);

      LogCrit(COMPONENT_STATE,
              "Error %s, could not remove {%s}",
              hash_table_err_to_str(rc), str);

      return;
    }

  LogFullDebug(COMPONENT_STATE,
               "Free {%s}",
               str);

  free_nlm_client(old_value.pdata);
}
Esempio n. 5
0
/**
 *
 * @brief Gets an entry by using its fsdata as a key and caches it if needed.
 *
 * Gets an entry by using its fsdata as a key and caches it if needed.
 *
 * If a cache entry is returned, its refcount is incremented by one.
 *
 * It turns out we do need cache_inode_get_located functionality for
 * cases like lookupp on an entry returning itself when it isn't a
 * root.  Therefore, if the 'associated' parameter is equal to the got
 * cache entry, a reference count is incremented but the structure
 * pointed to by attr is NOT filled in.
 *
 * @param[in]     fsdata     File system data
 * @param[out]    attr       The attributes of the got entry
 * @param[in]     context    FSAL credentials
 * @param[in]     associated Entry that may be equal to the got entry
 * @param[out]    status     Returned status
 *
 * @return If successful, the pointer to the entry; NULL otherwise
 *
 */
cache_entry_t *
cache_inode_get(cache_inode_fsal_data_t *fsdata,
                fsal_attrib_list_t *attr,
                fsal_op_context_t *context,
                cache_entry_t *associated,
                cache_inode_status_t *status)
{
    hash_buffer_t key, value;
    cache_entry_t *entry = NULL;
    fsal_status_t fsal_status = {0, 0};
    cache_inode_create_arg_t create_arg = {
        .newly_created_dir = FALSE
    };
    cache_inode_file_type_t type = UNASSIGNED;
    hash_error_t hrc = 0;
    fsal_attrib_list_t fsal_attributes;
    fsal_handle_t *file_handle;
    struct hash_latch latch;

    /* Set the return default to CACHE_INODE_SUCCESS */
    *status = CACHE_INODE_SUCCESS;

    /* Turn the input to a hash key on our own.
     */
    key.pdata = fsdata->fh_desc.start;
    key.len = fsdata->fh_desc.len;

    hrc = HashTable_GetLatch(fh_to_cache_entry_ht, &key, &value,
                             FALSE,
                             &latch);

    if ((hrc != HASHTABLE_SUCCESS) &&
            (hrc != HASHTABLE_ERROR_NO_SUCH_KEY)) {
        /* This should not happened */
        *status = CACHE_INODE_HASH_TABLE_ERROR;
        LogCrit(COMPONENT_CACHE_INODE,
                "Hash access failed with code %d"
                " - this should not have happened",
                hrc);
        return NULL;
    }

    if (hrc == HASHTABLE_SUCCESS) {
        /* Entry exists in the cache and was found */
        entry = value.pdata;
        /* take an extra reference within the critical section */
        if (cache_inode_lru_ref(entry, LRU_REQ_INITIAL) !=
                CACHE_INODE_SUCCESS) {
            /* Dead entry.  Treat like a lookup failure. */
            entry = NULL;
        } else {
            if (entry == associated) {
                /* Take a quick exit so we don't invert lock
                   ordering. */
                HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);
                return entry;
            }
        }
    }
    HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);

    if (!context) {
        /* Upcalls have no access to fsal_op_context_t,
           so just return the entry without revalidating it or
           creating a new one. */
        if (entry == NULL) {
            *status = CACHE_INODE_NOT_FOUND;
        }
        return entry;
    }

    if (!entry) {
        /* Cache miss, allocate a new entry */
        file_handle = (fsal_handle_t *) fsdata->fh_desc.start;
        /* First, call FSAL to know what the object is */
        fsal_attributes.asked_attributes = cache_inode_params.attrmask;
        fsal_status
            = FSAL_getattrs(file_handle, context, &fsal_attributes);
        if (FSAL_IS_ERROR(fsal_status)) {
            *status = cache_inode_error_convert(fsal_status);
            LogDebug(COMPONENT_CACHE_INODE,
                     "cache_inode_get: cache_inode_status=%u "
                     "fsal_status=%u,%u ", *status,
                     fsal_status.major,
                     fsal_status.minor);
            return NULL;
        }

        /* The type has to be set in the attributes */
        if (!FSAL_TEST_MASK(fsal_attributes.supported_attributes,
                            FSAL_ATTR_TYPE)) {
            *status = CACHE_INODE_FSAL_ERROR;
            return NULL;
        }

        /* Get the cache_inode file type */
        type = cache_inode_fsal_type_convert(fsal_attributes.type);
        if (type == SYMBOLIC_LINK) {
            fsal_attributes.asked_attributes = cache_inode_params.attrmask;
            fsal_status =
                FSAL_readlink(file_handle, context,
                              &create_arg.link_content,
                              &fsal_attributes);

            if (FSAL_IS_ERROR(fsal_status)) {
                *status = cache_inode_error_convert(fsal_status);
                return NULL;
            }
        }
        if ((entry
                = cache_inode_new_entry(fsdata,
                                        &fsal_attributes,
                                        type,
                                        &create_arg,
                                        status)) == NULL) {
            return NULL;
        }

    }

    *status = CACHE_INODE_SUCCESS;

    /* This is the replacement for cache_inode_renew_entry.  Rather
       than calling that function at the start of every cache_inode
       call with the inode locked, we call cache_inode_check trust to
       perform 'heavyweight' (timed expiration of cached attributes,
       getattr-based directory trust) checks the first time after
       getting an inode.  It does all of the checks read-locked and
       only acquires a write lock if there's something requiring a
       change.

       There is a second light-weight check done before use of cached
       data that checks whether the bits saying that inode attributes
       or inode content are trustworthy have been cleared by, for
       example, FSAL_CB.

       To summarize, the current implementation is that policy-based
       trust of validity is checked once per logical series of
       operations at cache_inode_get, and asynchronous trust is
       checked with use (when the attributes are locked for reading,
       for example.) */

    if ((*status = cache_inode_check_trust(entry,
                                           context))
            != CACHE_INODE_SUCCESS) {
        goto out_put;
    }

    /* Set the returned attributes */
    *status = cache_inode_lock_trust_attrs(entry, context);

    /* cache_inode_lock_trust_attrs may fail, in that case, the
       attributes are wrong and pthread_rwlock_unlock can't be called
       again */
    if(*status != CACHE_INODE_SUCCESS)
    {
        goto out_put;
    }
    *attr = entry->attributes;
    pthread_rwlock_unlock(&entry->attr_lock);

    return entry;

out_put:
    cache_inode_put(entry);
    entry = NULL;
    return entry;
} /* cache_inode_get */