/** * * 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) */
/** * * 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 */
/** * * 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 */
/** * * 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 */