/**
 *
 * cache_inode_lookup_sw: looks up for a name in a directory indicated by a
 * cached entry.
 * 
 * Looks up for a name in a directory indicated by a cached entry. The directory
 * should have been cached before.
 *
 * @param pentry_parent [IN]    entry for the parent directory to be managed.
 * @param name          [IN]    name of the entry that we are looking for in the
 *        cache.
 * @param pattr         [OUT]   attributes for the entry that we have found.
 * @param ht            [IN]    hash table used for the cache, unused in this
 *        call.
 * @param pclient       [INOUT] ressource allocated by the client for the nfs
 * management.
 * @param pcontext         [IN]    FSAL credentials 
 * @param pstatus       [OUT]   returned status.
 * @param use_mutex     [IN]    if TRUE, mutex management is done, not if equal
 * to FALSE.
 * 
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the
 *  entry
 *
 */
cache_entry_t *cache_inode_lookup_sw(cache_entry_t        * pentry_parent,
                                     fsal_name_t          * pname,
                                     cache_inode_policy_t   policy,
                                     fsal_attrib_list_t   * pattr,
                                     hash_table_t         * ht,
                                     cache_inode_client_t * pclient,
                                     fsal_op_context_t    * pcontext,
                                     cache_inode_status_t * pstatus, 
                                     int use_mutex)
{

  cache_inode_dir_entry_t dirent_key[1], *dirent;
  struct avltree_node *dirent_node;
  cache_inode_dir_entry_t *new_dir_entry;
  cache_entry_t *pentry = NULL;
  fsal_status_t fsal_status;
#ifdef _USE_MFSL
  mfsl_object_t object_handle;
#else
  fsal_handle_t object_handle;
#endif
  fsal_handle_t dir_handle;
  fsal_attrib_list_t object_attributes;
  cache_inode_create_arg_t create_arg;
  cache_inode_file_type_t type;
  cache_inode_status_t cache_status;
  cache_inode_fsal_data_t new_entry_fsdata;
  fsal_accessflags_t access_mask = 0;

  memset(&create_arg, 0, sizeof(create_arg));
  memset( (char *)&new_entry_fsdata, 0, sizeof( new_entry_fsdata ) ) ; 

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

  /* stats */
  (pclient->stat.nb_call_total)++;
  (pclient->stat.func_stats.nb_call[CACHE_INODE_LOOKUP])++;

  /* We should not renew entries when !use_mutex (because unless we
   * make the flag explicit (shared vs. exclusive), we don't know
   * whether a mutating operation is safe--and, the caller should have
   * already renewed the entry */
  if(use_mutex == TRUE) {
      P_w(&pentry_parent->lock);

      cache_status = cache_inode_renew_entry(pentry_parent, pattr, ht,
                                             pclient, pcontext, pstatus);
      if(cache_status != CACHE_INODE_SUCCESS)
      {
          V_w(&pentry_parent->lock);
          inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
          LogDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_lookup: returning %d(%s) from cache_inode_renew_entry",
                       *pstatus, cache_inode_err_str(*pstatus));
          return NULL;
      }

      /* RW Lock goes for writer to reader */
      rw_lock_downgrade(&pentry_parent->lock);
  }

  if(pentry_parent->internal_md.type != DIRECTORY)
    {
      /* Parent is no directory base, return NULL */
      *pstatus = CACHE_INODE_NOT_A_DIRECTORY;

      /* stats */
      (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

      if(use_mutex == TRUE)
        V_r(&pentry_parent->lock);

      return NULL;
    }

  /* if name is ".", use the input value */
  if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT))
    {
      pentry = pentry_parent;
    }
  else if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      /* Directory do only have exactly one parent. This a limitation in all FS,
       * which implies that hard link are forbidden on directories (so that
       * they exists only in one dir).  Because of this, the parent list is
       * always limited to one element for a dir.  Clients SHOULD never 
       * 'lookup( .. )' in something that is no dir. */
      pentry =
          cache_inode_lookupp_no_mutex(pentry_parent, ht, pclient, pcontext,
                                       pstatus);
    }
  else
    {
      /* This is a "regular lookup" (not on "." or "..") */

      /* Check is user (as specified by the credentials) is authorized to 
       * lookup the directory or not */
      access_mask = FSAL_MODE_MASK_SET(FSAL_X_OK) |
                    FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR);
      if(cache_inode_access_no_mutex(pentry_parent,
                                     access_mask,
                                     ht,
                                     pclient,
                                     pcontext,
                                     pstatus) != CACHE_INODE_SUCCESS)
        {
          if(use_mutex == TRUE)
            V_r(&pentry_parent->lock);

          (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++;
          return NULL;
        }

      /* We first try avltree_lookup by name.  If that fails, we dispatch to
       * the fsal. */

      FSAL_namecpy(&dirent_key->name, pname);
      dirent_node = avltree_lookup(&dirent_key->node_n,
				   &pentry_parent->object.dir.dentries);
      if (dirent_node) {
      	  dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t,
					node_n);
	  pentry = dirent->pentry;
      }

      if(pentry == NULL)
        {
          LogDebug(COMPONENT_CACHE_INODE, "Cache Miss detected");

          dir_handle = pentry_parent->handle;
          object_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL

#ifdef _USE_MFSL_ASYNC
          if(!mfsl_async_is_object_asynchronous(&pentry_parent->mobject))
            {
              /* If the parent is asynchronous, rely on the content of the cache
               * inode parent entry.
               *
               * /!\ If the fs behind the FSAL is touched in a non-nfs way,
               * there will be huge incoherencies.
               */
#endif                          /* _USE_MFSL_ASYNC */

              fsal_status = MFSL_lookup(&pentry_parent->mobject,
                                        pname,
                                        pcontext,
                                        &pclient->mfsl_context,
                                        &object_handle, &object_attributes, NULL);
#ifdef _USE_MFSL_ASYNC
            }
          else
            {
              LogMidDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_lookup chose to bypass FSAL and trusted his cache for name=%s",
                       pname->name);
              fsal_status.major = ERR_FSAL_NOENT;
              fsal_status.minor = ENOENT;
            }
#endif                          /* _USE_MFSL_ASYNC */

#else
          fsal_status =
              FSAL_lookup(&dir_handle, pname, pcontext, &object_handle,
                          &object_attributes);
#endif                          /* _USE_MFSL */

          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);

              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* Stale File Handle to be detected and managed */
              if(fsal_status.major == ERR_FSAL_STALE)
                {
                  cache_inode_status_t kill_status;

                  LogEvent(COMPONENT_CACHE_INODE,
                           "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                           pentry_parent, fsal_status.major, fsal_status.minor);

                  if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status) !=
                     CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_pentry_parent: Could not kill entry %p, status = %u",
                            pentry_parent, kill_status);

                  *pstatus = CACHE_INODE_FSAL_ESTALE;
                }

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

          type = cache_inode_fsal_type_convert(object_attributes.type);

          /* If entry is a symlink, this value for be cached */
          if(type == SYMBOLIC_LINK)
            {
              if( CACHE_INODE_KEEP_CONTENT( policy ) )
#ifdef _USE_MFSL
               {
                fsal_status =
                    MFSL_readlink(&object_handle, pcontext, &pclient->mfsl_context,
                                  &create_arg.link_content, &object_attributes, NULL);
               }
#else
               {
                fsal_status =
                    FSAL_readlink(&object_handle, pcontext, &create_arg.link_content,
                                  &object_attributes);
               }
             else
              { 
                 fsal_status.major = ERR_FSAL_NO_ERROR ;
                 fsal_status.minor = 0 ;
              }
#endif
              if(FSAL_IS_ERROR(fsal_status))
                {
                  *pstatus = cache_inode_error_convert(fsal_status);
                  if(use_mutex == TRUE)
                    V_r(&pentry_parent->lock);

                  /* Stale File Handle to be detected and managed */
                  if(fsal_status.major == ERR_FSAL_STALE)
                    {
                      cache_inode_status_t kill_status;

                      LogEvent(COMPONENT_CACHE_INODE,
                               "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                               pentry_parent, fsal_status.major, fsal_status.minor);

                      if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status)
                         != CACHE_INODE_SUCCESS)
                        LogCrit(COMPONENT_CACHE_INODE,
                                "cache_inode_pentry_parent: Could not kill entry %p, status = %u",
                                pentry_parent, kill_status);

                      *pstatus = CACHE_INODE_FSAL_ESTALE;
                    }

                  /* stats */
                  (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

                  return NULL;
                }
            }

          /* Allocation of a new entry in the cache */
#ifdef _USE_MFSL
          new_entry_fsdata.handle = object_handle.handle;
#else
          new_entry_fsdata.handle = object_handle;
#endif
          new_entry_fsdata.cookie = 0;

          if((pentry = cache_inode_new_entry( &new_entry_fsdata, 
                                              &object_attributes,
                                              type, 
                                              policy,
                                              &create_arg, 
                                              NULL, 
                                              ht, 
                                              pclient, 
                                              pcontext, 
                                              FALSE,      /* This is a population and not a creation */
                                              pstatus ) ) == NULL )
            {
              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

          /* Entry was found in the FSAL, add this entry to the parent
           * directory */
          cache_status = cache_inode_add_cached_dirent(pentry_parent,
                                                       pname,
                                                       pentry,
                                                       ht,
						       &new_dir_entry,
						       pclient,
						       pcontext,
						       pstatus);

          if(cache_status != CACHE_INODE_SUCCESS
             && cache_status != CACHE_INODE_ENTRY_EXISTS)
            {
              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

        } /* cached lookup fail (try fsal) */
Example #2
0
/**
 *
 * cache_inode_add_cached_dirent: Adds a directory entry to a cached directory.
 *
 * Adds a directory entry to a cached directory. This is use when creating a
 * new entry through nfs and keep it to the cache. It also allocates and caches
 * the entry.  This function can be call iteratively, within a loop (like what
 * is done in cache_inode_readdir_populate).  In this case, pentry_parent should
 * be set to the value returned in *pentry_next.  This function should never be
 * used for managing a junction.
 *
 * @param pentry_parent [INOUT] cache entry representing the directory to be
 *                              managed.
 * @param name          [IN]    name of the entry to add.
 * @param pentry_added  [IN]    the pentry added to the dirent array
 * @param pentry_next   [OUT]   the next pentry to use for next call.
 * @param ht            [IN]    hash table used for the cache, unused in this
 *                              call.
 * @param pclient       [INOUT] resource allocated by the client for the nfs
 *                              management.
 * @param pstatus       [OUT]   returned status.
 *
 * @return the DIRECTORY that contain this entry in its array_dirent\n
 * @return NULL if failed, see *pstatus for error's meaning.
 *
 */
cache_inode_status_t cache_inode_add_cached_dirent(
    cache_entry_t * pentry_parent,
    fsal_name_t * pname,
    cache_entry_t * pentry_added,
    hash_table_t * ht,
    cache_inode_dir_entry_t **pnew_dir_entry,
    cache_inode_client_t * pclient,
    fsal_op_context_t * pcontext,
    cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;
  cache_inode_parent_entry_t *next_parent_entry = NULL;
  cache_inode_dir_entry_t *new_dir_entry = NULL;
  struct avltree_node *tmpnode;

  *pstatus = CACHE_INODE_SUCCESS;

  /* Sanity check */
  if(pentry_parent->internal_md.type != DIRECTORY)
    {
 
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }
    
  /* in cache inode avl, we always insert on pentry_parent */
  GetFromPool(new_dir_entry, &pclient->pool_dir_entry, cache_inode_dir_entry_t);

  if(new_dir_entry == NULL)
    {
      *pstatus = CACHE_INODE_MALLOC_ERROR;
      return *pstatus;
    }

  fsal_status = FSAL_namecpy(&new_dir_entry->name, pname);
  if(FSAL_IS_ERROR(fsal_status))
  {
    *pstatus = CACHE_INODE_FSAL_ERROR;
    return *pstatus;
  }

  /* still need the parent list */
  GetFromPool(next_parent_entry, &pclient->pool_parent,
              cache_inode_parent_entry_t);
  if(next_parent_entry == NULL)
    {
      *pstatus = CACHE_INODE_MALLOC_ERROR;
      return *pstatus;
    }

  /* Init the next_parent_entry variable */
  next_parent_entry->parent = NULL;
  next_parent_entry->next_parent = NULL;

  /* add to avl */
  tmpnode = avltree_insert(&new_dir_entry->node_n,
			   &pentry_parent->object.dir.dentries);
  if (tmpnode) {
  	/* collision, tree not updated--release both pool objects and return
         * err */
	ReleaseToPool(next_parent_entry, &pclient->pool_parent);
	ReleaseToPool(new_dir_entry, &pclient->pool_dir_entry);
	*pstatus = CACHE_INODE_ENTRY_EXISTS;
	return *pstatus;
  }

  *pnew_dir_entry = new_dir_entry;

  /* we're going to succeed */
  pentry_parent->object.dir.nbactive++;  
  new_dir_entry->pentry = pentry_added;

  /* link with the parent entry (insert as first entry) */
  next_parent_entry->parent = pentry_parent;
  next_parent_entry->next_parent = pentry_added->parent_list;
  pentry_added->parent_list = next_parent_entry;

  return *pstatus;
}                               /* cache_inode_add_cached_dirent */
Example #3
0
/**
 *
 * cache_inode_readdir_nonamecache: Reads a directory without populating the name cache (no dirents created).
 *
 * Reads a directory without populating the name cache (no dirents created).
 *
 * @param pentry [IN] entry for the parent directory to be read.
 * @param cookie [IN] cookie for the readdir operation (basically the offset).
 * @param nbwanted [IN] Maximum number of directory entries wanted.
 * @param peod_met [OUT] A flag to know if end of directory was met during this call.
 * @param dirent_array [OUT] the resulting array of found directory entries.
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param unlock [OUT] the caller shall release read-lock on pentry when done
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 *
 */
static cache_inode_status_t cache_inode_readdir_nonamecache( cache_entry_t * pentry_dir,
                                                             cache_inode_policy_t policy,
                                                             uint64_t cookie,
                                                             unsigned int nbwanted,
                                                             unsigned int *pnbfound,
                                                             uint64_t *pend_cookie,
                                                             cache_inode_endofdir_t *peod_met,
                                                             cache_inode_dir_entry_t **dirent_array,
                                                             hash_table_t *ht,
                                                             int *unlock,
                                                             cache_inode_client_t *pclient,
                                                             fsal_op_context_t *pcontext,
                                                             cache_inode_status_t *pstatus)
{
  fsal_dir_t fsal_dirhandle;
  fsal_status_t fsal_status;
  fsal_attrib_list_t dir_attributes;

  fsal_cookie_t begin_cookie;
  fsal_cookie_t end_cookie;
  fsal_count_t iter;
  fsal_boolean_t fsal_eod;
  fsal_dirent_t fsal_dirent_array[FSAL_READDIR_SIZE + 20];

  cache_inode_fsal_data_t entry_fsdata;

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

  /* Only DIRECTORY entries are concerned */
  if(pentry_dir->internal_md.type != DIRECTORY)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  LogFullDebug(COMPONENT_NFS_READDIR,
               "About to readdir in  cache_inode_readdir_nonamecache: pentry=%p "
	       "cookie=%"PRIu64, pentry_dir, cookie ) ;

  /* Open the directory */
  dir_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
  fsal_status = MFSL_opendir(&pentry_dir->mobject,
                             pcontext,
                             &pclient->mfsl_context, &fsal_dirhandle, &dir_attributes, NULL);
#else
  fsal_status = FSAL_opendir(&pentry_dir->object.dir.handle,
                             pcontext, &fsal_dirhandle, &dir_attributes);
#endif

  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);

      if(fsal_status.major == ERR_FSAL_STALE)
        {
          cache_inode_status_t kill_status;

          LogEvent(COMPONENT_CACHE_INODE,
                   "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                   pentry_dir, fsal_status.major, fsal_status.minor);

          if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) !=
             CACHE_INODE_SUCCESS)
            LogCrit(COMPONENT_CACHE_INODE,
                    "cache_inode_readdir: Could not kill entry %p, status = %u",
                    pentry_dir, kill_status);

          *pstatus = CACHE_INODE_FSAL_ESTALE;
        }
      return *pstatus;
    }

  /* Loop for readding the directory */
  // memcpy( &(begin_cookie.data), &cookie, sizeof( uint64_t ) ) ;
  FSAL_SET_COOKIE_BY_OFFSET( begin_cookie, cookie ) ;
  fsal_eod = FALSE;

#ifdef _USE_MFSL
  fsal_status = MFSL_readdir( &fsal_dirhandle,
                              begin_cookie,
                              pclient->attrmask,
                              nbwanted * sizeof(fsal_dirent_t),
                              fsal_dirent_array,
                              &end_cookie,
                              (fsal_count_t *)pnbfound, 
                              &fsal_eod, 
                              &pclient->mfsl_context, 
                              NULL);
#else
  fsal_status = FSAL_readdir( &fsal_dirhandle,
                              begin_cookie,
                              pclient->attrmask,
                              nbwanted * sizeof(fsal_dirent_t),
                              fsal_dirent_array,
                              &end_cookie, 
                              (fsal_count_t *)pnbfound, 
                              &fsal_eod);
#endif
  if(FSAL_IS_ERROR(fsal_status))
   {
      *pstatus = cache_inode_error_convert(fsal_status);
      return *pstatus;
   }

  for( iter = 0 ; iter < *pnbfound ; iter ++ )
   {
      /* cache_inode_readdir does not return . or .. */
      if(!FSAL_namecmp(&(fsal_dirent_array[iter].name), (fsal_name_t *) & FSAL_DOT) ||
         !FSAL_namecmp(&(fsal_dirent_array[iter].name), (fsal_name_t *) & FSAL_DOT_DOT))
              continue;

      /* Get the related pentry without populating the name cache (but eventually populating the attrs cache */
      entry_fsdata.handle = fsal_dirent_array[iter].handle;
      entry_fsdata.cookie = 0; /* XXX needed? */

      /* Allocate a dirent to be returned to the client */
      /** @todo Make sure this piece of memory once the data are used */
      GetFromPool( dirent_array[iter], &pclient->pool_dir_entry, cache_inode_dir_entry_t);
      if( dirent_array[iter] == NULL ) 
       {
         *pstatus = CACHE_INODE_MALLOC_ERROR;
         return *pstatus;
       }


      /* fills in the dirent_array */
      if( ( dirent_array[iter]->pentry= cache_inode_get( &entry_fsdata,
                                                         policy,
                                                         &fsal_dirent_array[iter].attributes,
                                                         ht,
                                                         pclient,
                                                         pcontext,
                                                         pstatus ) ) == NULL )
          return *pstatus ;

      fsal_status = FSAL_namecpy( &dirent_array[iter]->name, &fsal_dirent_array[iter].name ) ;
      if(FSAL_IS_ERROR(fsal_status))
       {
         *pstatus = cache_inode_error_convert(fsal_status);
         return *pstatus;
       }
  
      (void) FSAL_cookie_to_uint64( &fsal_dirent_array[iter].handle,
                                    pcontext, 
                                    &fsal_dirent_array[iter].cookie,
                                    &dirent_array[iter]->fsal_cookie);

       dirent_array[iter]->cookie = dirent_array[iter]->fsal_cookie ;

   } /* for( iter = 0 ; iter < nbfound ; iter ++ ) */

  if( fsal_eod == TRUE )
    *peod_met = END_OF_DIR ;
  else
    *peod_met = TO_BE_CONTINUED ;

  /* Do not forget to set returned end cookie */
  //memcpy( pend_cookie, &(end_cookie.data), sizeof( uint64_t ) ) ; 
  FSAL_SET_POFFSET_BY_COOKIE( end_cookie, pend_cookie ) ;

  LogFullDebug(COMPONENT_NFS_READDIR,
               "End of readdir in  cache_inode_readdir_nonamecache: pentry=%p "
	       "cookie=%"PRIu64, pentry_dir, *pend_cookie ) ;


  /* Close the directory */
#ifdef _USE_MFSL
  fsal_status = MFSL_closedir(&fsal_dirhandle, &pclient->mfsl_context, NULL);
#else
  fsal_status = FSAL_closedir(&fsal_dirhandle);
#endif
  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);
      return *pstatus;
    }

  return CACHE_INODE_SUCCESS ;
} /* cache_inode_readdir_nomanecache */
Example #4
0
/**
 *
 * cache_inode_operate_cached_dirent: locates a dirent in the cached dirent,
 * and perform an operation on it.
 *
 * Looks up for an dirent in the cached dirent. Thus function searches only in
 * the entries listed in the dir_entries array. Some entries may be missing but
 * existing and not be cached (if no readdir was ever performed on the entry for
 * example. This function provides a way to operate on the dirent.
 *
 * @param pentry_parent [IN] directory entry to be searched.
 * @param name [IN] name for the searched entry.
 * @param newname [IN] newname if function is used to rename a dirent
 * @param pclient [INOUT] resource allocated by the client for the nfs management.
 * @param dirent_op [IN] operation (ADD, LOOKUP or REMOVE) to do on the dirent
 *        if found.
 * @pstatus [OUT] returned status.
 *
 * @return the found entry if its exists and NULL if it is not in the dirent
 *         cache. REMOVE always returns NULL.
 *
 */
cache_entry_t *cache_inode_operate_cached_dirent(cache_entry_t * pentry_parent,
                                                 fsal_name_t * pname,
                                                 fsal_name_t * newname,
						 cache_inode_client_t * pclient,
                                                 cache_inode_dirent_op_t dirent_op,
                                                 cache_inode_status_t * pstatus)
{
  cache_entry_t *pentry = NULL;
  cache_inode_dir_entry_t dirent_key[1], *dirent;
  struct avltree_node *dirent_node, *tmpnode;
  LRU_List_state_t vstate;

  /* Directory mutation generally invalidates outstanding 
   * readdirs, hence any cached cookies, so in these cases we 
   * clear the cookie avl */

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

  /* Sanity check */
  if(pentry_parent->internal_md.type != DIRECTORY)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return NULL;
    }

  /* If no active entry, do nothing */
  if (pentry_parent->object.dir.nbactive == 0) {
      *pstatus = CACHE_INODE_NOT_FOUND;
      return NULL;
  }

  FSAL_namecpy(&dirent_key->name, pname);
  dirent_node = avltree_lookup(&dirent_key->node_n,
			       &pentry_parent->object.dir.dentries);
  if (! dirent_node) {
      *pstatus = CACHE_INODE_NOT_FOUND; /* Right error code (see above)? */
      return NULL;
  }

  /* unpack avl node */
  dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t,
				node_n);

  /* check state of cached dirent */
  vstate = dirent->pentry->internal_md.valid_state;
  if (vstate == VALID || vstate == STALE) {
  
      if (vstate == STALE)
      	LogDebug(COMPONENT_NFS_READDIR,
		"DIRECTORY: found STALE cache entry");

	/* Entry was found */
        pentry = dirent->pentry;
        *pstatus = CACHE_INODE_SUCCESS;
  }

  /* Did we find something */
  if(pentry != NULL)
    {
      /* Yes, we did ! */
      switch (dirent_op)
        {
        case CACHE_INODE_DIRENT_OP_REMOVE:
	    avltree_remove(&dirent->node_n,
                           &pentry_parent->object.dir.dentries);
	    /* release to pool */
	    ReleaseToPool(dirent, &pclient->pool_dir_entry);
	    pentry_parent->object.dir.nbactive--;
	    *pstatus = CACHE_INODE_SUCCESS;
          break;

        case CACHE_INODE_DIRENT_OP_RENAME:
	  /* change the installed inode only the rename can succeed */
	  FSAL_namecpy(&dirent_key->name, newname);
  	  tmpnode = avltree_lookup(&dirent_key->node_n,
				   &pentry_parent->object.dir.dentries);
	  if (tmpnode) {
	    /* rename would cause a collision */
	    *pstatus = CACHE_INODE_ENTRY_EXISTS;

	  } else {
	      /* remove, rename, and re-insert the object with new keys */
	      avltree_remove(&dirent->node_n,
                             &pentry_parent->object.dir.dentries);

	      FSAL_namecpy(&dirent->name, newname);
	      tmpnode = avltree_insert(&dirent->node_n,
				       &pentry_parent->object.dir.dentries);
	      if (tmpnode) {
		  /* collision, tree state unchanged--this won't happen */
		  *pstatus = CACHE_INODE_ENTRY_EXISTS;

		  /* still, try to revert the change in place */
		  FSAL_namecpy(&dirent->name, pname);
		  tmpnode = avltree_insert(&dirent->node_n,
					   &pentry_parent->object.dir.dentries);
	      } else {
		  *pstatus = CACHE_INODE_SUCCESS;
	      }
	  } /* !found */
          break;

        default:
          /* Should never occurs, in any case, it cost nothing to handle
	   * this situation */
          *pstatus = CACHE_INODE_INVALID_ARGUMENT;
          break;

        }                       /* switch */
    }

  if (*pstatus == CACHE_INODE_SUCCESS) {
      /* As noted, if a mutating operation was performed, we must
       * invalidate cached cookies. */
      cache_inode_release_dirents(
          pentry_parent, pclient, CACHE_INODE_AVL_COOKIES);

      /* Someone has to repopulate the avl cookie cache.  Populating it
       * lazily is ok, but the logic to do it makes supporting simultaneous
       * readers more involved.  Another approach would be to do it in the
       * background, scheduled from here. */
  }

  return pentry;
}                               /* cache_inode_operate_cached_dirent */