cache_inode_status_t cache_inode_readlink(cache_entry_t * pentry, fsal_path_t * plink_content, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_status_t fsal_status; fsal_attrib_list_t attr ; /* 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_READLINK])++; /* Lock the entry */ P_w(&pentry->lock); if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK])++; V_w(&pentry->lock); return *pstatus; } /* RW_Lock obtained as writer turns to reader */ rw_lock_downgrade(&pentry->lock); switch (pentry->internal_md.type) { case REGULAR_FILE: case DIRECTORY: case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: case UNASSIGNED: case FS_JUNCTION: case RECYCLED: *pstatus = CACHE_INODE_BAD_TYPE; V_r(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1; return *pstatus; break; case SYMBOLIC_LINK: assert(pentry->object.symlink); if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) ) { fsal_status = FSAL_pathcpy(plink_content, &(pentry->object.symlink->content)); /* need copy ctor? */ } else { /* Content is not cached, call FSAL_readlink here */ fsal_status = FSAL_readlink( &pentry->handle, pcontext, plink_content, &attr ) ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readlink: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readlink: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1; return *pstatus; } break; } /* Release the entry */ *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_READLINK] += 1; return *pstatus; } /* cache_inode_readlink */
/** * * cache_inode_lookupp_sw: looks up (and caches) the parent directory for a directory. A switches tells is mutex are use. * * Looks up (and caches) the parent directory for a directory. * * @param pentry [IN] entry whose parent is to be obtained. * @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 are use, not otherwise. * * @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_lookupp_sw(cache_entry_t * pentry, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus, int use_mutex) { cache_entry_t *pentry_parent = NULL; fsal_status_t fsal_status; fsal_attrib_list_t object_attributes; cache_inode_fsal_data_t fsdata; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_LOOKUP] += 1; /* The entry should be a directory */ if(use_mutex) P_r(&pentry->lock); if(pentry->internal_md.type != DIR_BEGINNING) { if(use_mutex) V_r(&pentry->lock); *pstatus = CACHE_INODE_BAD_TYPE; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1; return NULL; } /* Renew the entry (to avoid having it being garbagged */ if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR] += 1; return NULL; } /* Does the parent belongs to the cache ? */ if(pentry->parent_list && pentry->parent_list->parent) { /* YES, the parent is cached, use the pentry that we have found */ pentry_parent = pentry->parent_list->parent; } else { /* NO, the parent is not cached, query FSAL to get it and cache the result */ object_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_lookup(&pentry->object.dir_begin.handle, (fsal_name_t *) & FSAL_DOT_DOT, pcontext, &fsdata.handle, &object_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(use_mutex) V_r(&pentry->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_lookupp: Stale FSAL FH detected for pentry %p", pentry); if(cache_inode_kill_entry(pentry, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE,"cache_inode_remove: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1; return NULL; } /* Call cache_inode_get to populate the cache with the parent entry */ fsdata.cookie = 0; if((pentry_parent = cache_inode_get(&fsdata, &object_attributes, ht, pclient, pcontext, pstatus)) == NULL) { if(use_mutex) V_r(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1; return NULL; } } *pstatus = cache_inode_valid(pentry_parent, CACHE_INODE_OP_GET, pclient); if(use_mutex) V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_LOOKUPP] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_LOOKUPP] += 1; return pentry_parent; } /* cache_inode_lookupp_sw */
/** * * 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_readdir: Reads a directory. * * Looks up for a name in a directory indicated by a cached entry. The * directory should have been cached before. * * NEW: pending new (C-language) callback based dirent unpacking into caller * structures, we eliminate copies by returning dir entries by pointer. To * permit this, we introduce lock donation. If new int pointer argument * unlock is 1 on return, the calling thread holds pentry read-locked and * must release this lock after dirent processing. * * This is the only function in the cache_inode_readdir.c file that manages MT * safety on a directory cache entry. * * @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 * @return CACHE_INODE_BAD_TYPE if entry is not related to a directory\n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_readdir(cache_entry_t * dir_pentry, 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) { cache_inode_dir_entry_t dirent_key[1], *dirent; struct avltree_node *dirent_node; fsal_accessflags_t access_mask = 0; uint64_t inoff = cookie; int i = 0; /* Guide to parameters: * the first cookie is parameter 'cookie' * number of entries queried is set by parameter 'nbwanted' * number of found entries before eod is return is '*pnbfound' * '*peod_met' is set if end of directory is encountered */ /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; dirent = NULL; /* Set initial value of unlock */ *unlock = FALSE; /* end cookie initial value is the begin cookie */ LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: setting pend_cookie to cookie=%" PRIu64, cookie); *pend_cookie = cookie; /* stats */ pclient->stat.nb_call_total++; (pclient->stat.func_stats.nb_call[CACHE_INODE_READDIR])++; LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: parameters are cookie=%"PRIu64 "nbwanted=%u", cookie, nbwanted); /* Sanity check */ if(nbwanted == 0) { /* Asking for nothing is not a crime !!!!! * build a 'dummy' return in this case */ *pstatus = CACHE_INODE_SUCCESS; *pnbfound = 0; *peod_met = TO_BE_CONTINUED; /* stats */ (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; return *pstatus; } /* Force dir content invalidation if policy enforced no name cache */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) return cache_inode_readdir_nonamecache( dir_pentry, policy, cookie, nbwanted, pnbfound, pend_cookie, peod_met, dirent_array, ht, unlock, pclient, pcontext, pstatus ) ; P_w(&dir_pentry->lock); /* Renew the entry (to avoid having it being garbagged */ if(cache_inode_renew_entry(dir_pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++; V_w(&dir_pentry->lock); return *pstatus; } /* readdir can be done only with a directory */ if(dir_pentry->internal_md.type != DIRECTORY) { V_w(&dir_pentry->lock); *pstatus = CACHE_INODE_BAD_TYPE; /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; return *pstatus; } /* Check is user (as specified by the credentials) is authorized to read * the directory or not */ access_mask = FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR); if(cache_inode_access_no_mutex(dir_pentry, access_mask, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { V_w(&dir_pentry->lock); (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; return *pstatus; } /* Is the directory fully cached (this is done if a readdir call is done on the directory) */ if(dir_pentry->object.dir.has_been_readdir != CACHE_INODE_YES) { /* populate the cache */ if(cache_inode_readdir_populate(dir_pentry, policy, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; V_w(&dir_pentry->lock); return *pstatus; } } /* deal with dentry cache invalidates */ revalidate_cookie_cache(dir_pentry, pclient); /* Downgrade Writer lock to a reader one. */ rw_lock_downgrade(&dir_pentry->lock); /* deal with initial cookie value: * 1. cookie is invalid (-should- be checked by caller) * 2. cookie is 0 (first cookie) -- ok * 3. cookie is > than highest dirent position (error) * 4. cookie <= highest dirent position but > highest cached cookie * (currently equivalent to #2, because we pre-populate the cookie avl) * 5. cookie is in cached range -- ok */ if (cookie > 0) { if (cookie < 3) { *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } if ((inoff-3) > avltree_size(&dir_pentry->object.dir.dentries)) { LogCrit(COMPONENT_NFS_V4, "Bad initial cookie %"PRIu64, inoff); *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } /* we assert this can now succeed */ dirent_key->cookie = inoff; dirent_node = avltree_lookup(&dirent_key->node_c, &dir_pentry->object.dir.cookies); if (! dirent_node) { LogCrit(COMPONENT_NFS_READDIR, "%s: seek to cookie=%"PRIu64" fail", __func__, inoff); *pstatus = CACHE_INODE_NOT_FOUND; V_r(&dir_pentry->lock); return *pstatus; } /* switch avls */ dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_c); dirent_node = &dirent->node_n; /* client wants the cookie -after- the last we sent, and * the Linux 3.0 and 3.1.0-rc7 clients misbehave if we * resend the last one */ dirent_node = avltree_next(dirent_node); } else { /* initial readdir */ dirent_node = avltree_first(&dir_pentry->object.dir.dentries); } LogFullDebug(COMPONENT_NFS_READDIR, "About to readdir in cache_inode_readdir: pentry=%p " "cookie=%"PRIu64, dir_pentry, cookie); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *pnbfound = 0; *peod_met = TO_BE_CONTINUED; for(i = 0; i < nbwanted; ++i) { if (!dirent_node) break; dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); dirent_array[i] = dirent; (*pnbfound)++; dirent_node = avltree_next(dirent_node); } if (*pnbfound > 0) { if (!dirent) { LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: " "UNEXPECTED CASE: dirent is NULL whereas nbfound>0"); *pstatus = CACHE_INODE_INCONSISTENT_ENTRY; return CACHE_INODE_INCONSISTENT_ENTRY; } *pend_cookie = dirent->cookie; } if (! dirent_node) *peod_met = END_OF_DIR; *pstatus = cache_inode_valid(dir_pentry, CACHE_INODE_OP_GET, pclient); /* stats */ if(*pstatus != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; V_r(&dir_pentry->lock); } else { (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; *unlock = TRUE; } return *pstatus; } /* cache_inode_readdir */
/** * * cache_inode_getattr: Gets the attributes for a cached entry. * * Gets the attributes for a cached entry. The FSAL attributes are kept in a structure when the entry * is added to the cache. * * @param pentry [IN] entry to be managed. * @param pattr [OUT] pointer to the results * @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. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_getattr(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { cache_inode_status_t status; fsal_handle_t *pfsal_handle = NULL; fsal_status_t fsal_status; /* sanity check */ if(pentry == NULL || pattr == NULL || ht == NULL || pclient == NULL || pcontext == NULL) { *pstatus = CACHE_INODE_INVALID_ARGUMENT; LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning CACHE_INODE_INVALID_ARGUMENT because of bad arg"); return *pstatus; } /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; inc_func_call(pclient, CACHE_INODE_GETATTR); /* Lock the entry */ P_w(&pentry->lock); status = cache_inode_renew_entry(pentry, pattr, ht, pclient, pcontext, pstatus); if(status != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* RW Lock goes for writer to reader */ rw_lock_downgrade(&pentry->lock); cache_inode_get_attributes(pentry, pattr); if(FSAL_TEST_MASK(pattr->asked_attributes, FSAL_ATTR_RDATTR_ERR)) { switch (pentry->internal_md.type) { case REGULAR_FILE: pfsal_handle = &pentry->object.file.handle; break; case SYMBOLIC_LINK: assert(pentry->object.symlink); pfsal_handle = &pentry->object.symlink->handle; break; case DIRECTORY: pfsal_handle = &pentry->object.dir.handle; break; case SOCKET_FILE: case FIFO_FILE: case BLOCK_FILE: case CHARACTER_FILE: pfsal_handle = &pentry->object.special_obj.handle; break; case FS_JUNCTION: case UNASSIGNED: case RECYCLED: *pstatus = CACHE_INODE_INVALID_ARGUMENT; LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry - unexpected md_type", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* * An error occured when trying to get * the attributes, they have to be renewed */ #ifdef _USE_MFSL fsal_status = FSAL_getattrs_descriptor(&(cache_inode_fd(pentry)->fsal_file), pfsal_handle, pcontext, pattr); #else fsal_status = FSAL_getattrs_descriptor(cache_inode_fd(pentry), pfsal_handle, pcontext, pattr); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_getattr: Stale FSAL File Handle detected for pentry = %p", pentry); /* Locked flag is set to true to show entry has a read lock */ cache_inode_kill_entry( pentry, WT_LOCK, ht, pclient, &kill_status); if(kill_status != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_getattr: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stat */ inc_func_err_unrecover(pclient, CACHE_INODE_GETATTR); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from FSAL_getattrs_descriptor", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* Set the new attributes */ cache_inode_set_attributes(pentry, pattr); } *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); else inc_func_success(pclient, CACHE_INODE_GETATTR); #ifdef _USE_NFS4_ACL if(isDebug(COMPONENT_NFS_V4_ACL)) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: pentry = %p, acl = %p", pentry, pattr->acl); if(pattr->acl) { fsal_ace_t *pace; for(pace = pattr->acl->aces; pace < pattr->acl->aces + pattr->acl->naces; pace++) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: ace type = 0x%x, flag = 0x%x, perm = 0x%x, special = %d, %s = 0x%x", pace->type, pace->flag, pace->perm, IS_FSAL_ACE_SPECIAL_ID(*pace), GET_FSAL_ACE_WHO_TYPE(*pace), GET_FSAL_ACE_WHO(*pace)); } } } #endif /* _USE_NFS4_ACL */ LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_valid", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; }
/** * * cache_inode_getattr: Gets the attributes for a cached entry. * * Gets the attributes for a cached entry. The FSAL attributes are kept in a structure when the entry * is added to the cache. * * @param pentry [IN] entry to be managed. * @param pattr [OUT] pointer to the results * @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. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_getattr(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { cache_inode_status_t status; fsal_handle_t *pfsal_handle; fsal_status_t fsal_status; /* sanity check */ if(pentry == NULL || pattr == NULL || ht == NULL || pclient == NULL || pcontext == NULL) { *pstatus = CACHE_INODE_INVALID_ARGUMENT; return *pstatus; } /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; inc_func_call(pclient, CACHE_INODE_GETATTR); /* Lock the entry */ P_w(&pentry->lock); status = cache_inode_renew_entry(pentry, pattr, ht, pclient, pcontext, pstatus); if(status != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); return *pstatus; } /* RW Lock goes for writer to reader */ rw_lock_downgrade(&pentry->lock); cache_inode_get_attributes(pentry, pattr); if(FSAL_TEST_MASK(pattr->asked_attributes, FSAL_ATTR_RDATTR_ERR)) { switch (pentry->internal_md.type) { case REGULAR_FILE: pfsal_handle = &pentry->object.file.handle; break; case SYMBOLIC_LINK: pfsal_handle = &pentry->object.symlink.handle; break; case DIR_BEGINNING: pfsal_handle = &pentry->object.dir_begin.handle; break; case DIR_CONTINUE: /* * lock the related dir_begin (dir begin are garbagge * collected AFTER their related dir_cont) * this means that if a DIR_CONTINUE exists, * its pdir pointer is not endless */ P_r(&pentry->object.dir_cont.pdir_begin->lock); pfsal_handle = &pentry->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry->object.dir_cont.pdir_begin->lock); break; case SOCKET_FILE: case FIFO_FILE: case BLOCK_FILE: case CHARACTER_FILE: pfsal_handle = &pentry->object.special_obj.handle; break; } /* * An error occured when trying to get * the attributes, they have to be renewed */ fsal_status = FSAL_getattrs(pfsal_handle, pcontext, pattr); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_getattr: Stale FSAL File " "Handle detected for pentry = %p", pentry); cache_inode_kill_entry(pentry, ht, pclient, &kill_status); if(kill_status != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE_GC, "cache_inode_getattr: Could not kill " "entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stat */ inc_func_err_unrecover(pclient, CACHE_INODE_GETATTR); return *pstatus; } /* Set the new attributes */ cache_inode_set_attributes(pentry, pattr); } *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); else inc_func_success(pclient, CACHE_INODE_GETATTR); return *pstatus; }