void check_min_1(void) { avl_unit_val_t *v; struct avltree_node *node; avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); insert_long_val(&avl_tree_1, 4); insert_long_val(&avl_tree_1, 10); insert_long_val(&avl_tree_1, 10010); insert_long_val(&avl_tree_1, 267); insert_long_val(&avl_tree_1, 3382); insert_long_val(&avl_tree_1, 22); insert_long_val(&avl_tree_1, 82); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (4 + 1)); /* insert new min */ insert_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (3 + 1)); /* delete current min */ delete_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (4 + 1)); }
static void debug_print_dirents(cache_entry_t *dir_pentry) { struct avltree_node *dirent_node; cache_inode_dir_entry_t *dirent; int size, ix; size = avltree_size(&dir_pentry->object.dir.cookies); LogCrit(COMPONENT_CACHE_INODE, "cookie avl size: %d", size); dirent_node = avltree_first(&dir_pentry->object.dir.cookies); ix = 0; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_c); LogCrit(COMPONENT_CACHE_INODE, "cookie: ix %d %p (%s, %"PRIu64")", ix, dirent, dirent->name.name, dirent->cookie); dirent_node = avltree_next(dirent_node); ++ix; } size = avltree_size(&dir_pentry->object.dir.dentries); LogCrit(COMPONENT_CACHE_INODE, "name avl size: %d", size); dirent_node = avltree_first(&dir_pentry->object.dir.dentries); ix = 0; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); LogCrit(COMPONENT_CACHE_INODE, "name: ix %d %p (%s, %"PRIu64")", ix, dirent, dirent->name.name, dirent->cookie); dirent_node = avltree_next(dirent_node); ++ix; } }
void gweakref_destroy(gweakref_table_t *wt) { struct avltree_node *node, *onode; gweakref_partition_t *wp; gweakref_priv_t *tref; int ix; /* quiesce the server, then... */ for (ix = 0; ix < wt->npart; ++ix) { wp = &wt->partition[ix]; onode = NULL; node = avltree_first(&wp->t); do { if (onode) { tref = avltree_container_of(onode, gweakref_priv_t, node_k); gsh_free(tref); } } while ((onode = node) && (node = avltree_next(node))); if (onode) { tref = avltree_container_of(onode, gweakref_priv_t, node_k); gsh_free(tref); } avltree_init(&wp->t, wk_cmpf, 0 /* must be 0 */); if (wp->cache) gsh_free(wp->cache); } gsh_free(wt->partition); gsh_free(wt); }
void avltree_clean(struct avltree *tree) { struct avltree_node *i; for (i = avltree_first(tree); i; i = avltree_next(i)) i->tree = NULL; avltree_init(tree, tree->cmp_fn); }
void check_min_2(void) { avl_unit_val_t *v; unsigned long mval, rv; struct avltree_node *node; int ix; srand(time(0)); avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); mval = ULONG_MAX; for (ix = 0; ix < 100000; ix++) { rv = rand(); /* in solaris avl, inserting an value that compares equal * to an already inserted value is illegal */ insert_long_val_safe(&avl_tree_1, rv); if ((mval < 0) || (rv < mval)) mval = rv; } node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); printf("rv: %lu mval: %lu val: %lu\n", rv, mval, v->val - 1); CU_ASSERT(v->val == (mval + 1)); }
void avltree_foreach(struct avltree *tree, avltree_call_fn_t call) { struct avltree_node * i; struct avltree_node * n; for (i = avltree_first(tree); i; ) { n = avltree_next(i); call(i); i = n; } }
static int mmo_get_objid(const void *ptr) { const char *key; mmo_data_t *data; if ((key = avltree_first(objects, (void *)&data))) do { if (data->data == ptr) { return atoi(key); } } while ((key = avltree_next(objects, key, (void *)&data))); return 0; }
/** * * revalidate_cookie_cache: sync cookie avl offsets with the dentry name avl. * * If no cookies are cached, add those of any dirents in the name avl. The * entry is assumed to be write locked. * * @param pentry [IN] entry for the parent directory to be read. * * @return void * */ static void revalidate_cookie_cache(cache_entry_t *dir_pentry, cache_inode_client_t *pclient) { struct avltree_node *dirent_node; cache_inode_dir_entry_t *dirent; int size_n, size_c, ix; /* we'll try to add entries to any directory whose cookie * avl cache is currently empty (mutating dirent operations * clear it) */ #if 0 if (avltree_size(&dir_pentry->object.dir.cookies) > 0) return; #else size_c = avltree_size(&dir_pentry->object.dir.cookies); size_n = avltree_size(&dir_pentry->object.dir.dentries); if (size_c == size_n) return; /* the following is safe to call arbitrarily many times, with * CACHE_INODE_AVL_COOKIES -only-. */ cache_inode_release_dirents(dir_pentry, pclient, CACHE_INODE_AVL_COOKIES); #endif dirent_node = avltree_first(&dir_pentry->object.dir.dentries); dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); ix = 3; /* first non-reserved cookie value */ dirent_node = &dirent->node_n; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); dirent->cookie = ix; /* XXX we could optimize this somewhat (saving an internal * lookup in avltree_insert), by adding an avl append * operation */ (void) avltree_insert(&dirent->node_c, &dir_pentry->object.dir.cookies); dirent_node = avltree_next(dirent_node); ++ix; } return; }
void trav_tree_10000(void) { int ntrav = 0; struct avltree_node *node; avl_unit_val_t *v; node = avltree_first(&avl_tree_10000); while (node) { ntrav++; v = avltree_container_of(node, avl_unit_val_t, node_k); if ((ntrav % 1000) == 0) printf("Node at %p key: %lu val: %lu (%d)\n", v, v->key, v->val, ntrav); node = avltree_next(node); } CU_ASSERT_EQUAL(ntrav, 10000); }
void avl_unit_clear_tree(struct avltree *t) { avl_unit_val_t *v; struct avltree_node *node, *next_node; if (avltree_size(t) < 1) return; node = avltree_first(t); while (node) { next_node = avltree_next(node); v = avltree_container_of(node, avl_unit_val_t, node_k); avltree_remove(&v->node_k, &avl_tree_1); avl_unit_free_val(v); node = next_node; } }
static void mmo_release_avltree(avltree_t *tree, const int is_object) { const char *key; char *name; if ((key = avltree_first(tree, (void *)&name))) { do { if (name == NULL) continue; if (is_object) { mmo_data_t *data = (mmo_data_t *)name; if (data->name) xfree(data->name); } xfree(name); } while ((key = avltree_next(tree, key, (void *)&name))); } avltree_done(tree); }
/** * @brief Destroy the package. */ void cih_pkgdestroy(void) { /* Index over partitions */ int ix = 0; /* Destroy the partitions, warning if not empty */ for (ix = 0; ix < cih_fhcache.npart; ++ix) { if (avltree_first(&cih_fhcache.partition[ix].t) != NULL) LogMajor(COMPONENT_CACHE_INODE, "Cache inode AVL tree not empty"); PTHREAD_RWLOCK_destroy(&cih_fhcache.partition[ix].lock); gsh_free(cih_fhcache.partition[ix].cache); } /* Destroy the partition table */ gsh_free(cih_fhcache.partition); cih_fhcache.partition = NULL; initialized = false; }
void check_delete_1(void) { struct avltree_node *node; avl_unit_val_t *v, *v2; avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); insert_long_val(&avl_tree_1, 4); insert_long_val(&avl_tree_1, 1); insert_long_val(&avl_tree_1, 10010); insert_long_val(&avl_tree_1, 267); insert_long_val(&avl_tree_1, 3382); insert_long_val(&avl_tree_1, 22); insert_long_val(&avl_tree_1, 82); insert_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (1 + 1)); delete_long_val(&avl_tree_1, 1); /* new key */ v = avl_unit_new_val(4); v->key = 4; node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (4 + 1)); delete_long_val(&avl_tree_1, 267); v->key = 3382; node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (3382 + 1)); avl_unit_free_val(v); }
/** * * 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_status_t cache_inode_readdir(cache_entry_t *directory, uint64_t cookie, unsigned int *nbfound, bool *eod_met, attrmask_t attrmask, cache_inode_getattr_cb_t cb, void *opaque) { /* The entry being examined */ cache_inode_dir_entry_t *dirent = NULL; /* The node in the tree being traversed */ struct avltree_node *dirent_node; /* The access mask corresponding to permission to list directory entries */ fsal_accessflags_t access_mask = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR)); fsal_accessflags_t access_mask_attr = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE)); cache_inode_status_t status = CACHE_INODE_SUCCESS; cache_inode_status_t attr_status; struct cache_inode_readdir_cb_parms cb_parms = { opaque, NULL, true, 0, true }; bool retry_stale = true; LogFullDebug(COMPONENT_NFS_READDIR, "Enter...."); /* readdir can be done only with a directory */ if (directory->type != DIRECTORY) { status = CACHE_INODE_NOT_A_DIRECTORY; /* no lock acquired so far, just return status */ LogFullDebug(COMPONENT_NFS_READDIR, "Not a directory"); return status; } /* cache_inode_lock_trust_attrs can return an error, and no lock will * be acquired */ status = cache_inode_lock_trust_attrs(directory, false); if (status != CACHE_INODE_SUCCESS) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs status=%s", cache_inode_err_str(status)); return status; } /* Adjust access mask if ACL is asked for. * NOTE: We intentionally do NOT check ACE4_READ_ATTR. */ if ((attrmask & ATTR_ACL) != 0) { access_mask |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); access_mask_attr |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); } /* Check if user (as specified by the credentials) is authorized to read * the directory or not */ status = cache_inode_access_no_mutex(directory, access_mask); if (status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "permission check for directory status=%s", cache_inode_err_str(status)); PTHREAD_RWLOCK_unlock(&directory->attr_lock); return status; } if (attrmask != 0) { /* Check for access permission to get attributes */ attr_status = cache_inode_access_no_mutex(directory, access_mask_attr); if (attr_status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "permission check for attributes " "status=%s", cache_inode_err_str(attr_status)); } } else /* No attributes requested, we don't need permission */ attr_status = CACHE_INODE_SUCCESS; PTHREAD_RWLOCK_rdlock(&directory->content_lock); PTHREAD_RWLOCK_unlock(&directory->attr_lock); if (! ((directory->flags & CACHE_INODE_TRUST_CONTENT) && (directory->flags & CACHE_INODE_DIR_POPULATED))) { PTHREAD_RWLOCK_unlock(&directory->content_lock); PTHREAD_RWLOCK_wrlock(&directory->content_lock); status = cache_inode_readdir_populate(directory); if (status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "cache_inode_readdir_populate status=%s", cache_inode_err_str(status)); goto unlock_dir; } } /* 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) { /* N.B., cache_inode_avl_qp_insert_s ensures k > 2 */ if (cookie < 3) { status = CACHE_INODE_BAD_COOKIE; LogFullDebug(COMPONENT_NFS_READDIR, "Bad cookie"); goto unlock_dir; } /* we assert this can now succeed */ dirent = cache_inode_avl_lookup_k(directory, cookie, CACHE_INODE_FLAG_NEXT_ACTIVE); if (!dirent) { /* Linux (3.4, etc) has been observed to send readdir * at the offset of the last entry's cookie, and * returns no dirents to userland if that readdir * notfound or badcookie. */ if (cache_inode_avl_lookup_k (directory, cookie, CACHE_INODE_FLAG_NONE)) { /* yup, it was the last entry */ LogFullDebug(COMPONENT_NFS_READDIR, "EOD because empty result"); *eod_met = true; goto unlock_dir; } LogFullDebug(COMPONENT_NFS_READDIR, "seek to cookie=%" PRIu64 " fail", cookie); status = CACHE_INODE_BAD_COOKIE; goto unlock_dir; } /* dirent is the NEXT entry to return, since we sent * CACHE_INODE_FLAG_NEXT_ACTIVE */ dirent_node = &dirent->node_hk; } else { /* initial readdir */ dirent_node = avltree_first(&directory->object.dir.avl.t); } LogFullDebug(COMPONENT_NFS_READDIR, "About to readdir in cache_inode_readdir: directory=%p " "cookie=%" PRIu64 " collisions %d", directory, cookie, directory->object.dir.avl.collisions); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *nbfound = 0; *eod_met = false; for (; cb_parms.in_result && dirent_node; dirent_node = avltree_next(dirent_node)) { cache_entry_t *entry = NULL; cache_inode_status_t tmp_status = 0; dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_hk); estale_retry: LogFullDebug(COMPONENT_NFS_READDIR, "Lookup direct %s", dirent->name); entry = cache_inode_get_keyed(&dirent->ckey, CIG_KEYED_FLAG_NONE, &tmp_status); if (!entry) { LogFullDebug(COMPONENT_NFS_READDIR, "Lookup returned %s", cache_inode_err_str(tmp_status)); if (retry_stale && tmp_status == CACHE_INODE_ESTALE) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - retrying entry", cache_inode_err_str(tmp_status), dirent->name); retry_stale = false; /* only one retry per * dirent */ goto estale_retry; } if (tmp_status == CACHE_INODE_NOT_FOUND || tmp_status == CACHE_INODE_ESTALE) { /* Directory changed out from under us. Invalidate it, skip the name, and keep going. */ atomic_clear_uint32_t_bits( &directory->flags, CACHE_INODE_TRUST_CONTENT); LogDebug(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - skipping entry", cache_inode_err_str(tmp_status), dirent->name); continue; } else { /* Something is more seriously wrong, probably an inconsistency. */ status = tmp_status; LogCrit(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - bailing out", cache_inode_err_str(status), dirent->name); goto unlock_dir; } } LogFullDebug(COMPONENT_NFS_READDIR, "cache_inode_readdir: dirent=%p name=%s " "cookie=%" PRIu64 " (probes %d)", dirent, dirent->name, dirent->hk.k, dirent->hk.p); cb_parms.name = dirent->name; cb_parms.attr_allowed = attr_status == CACHE_INODE_SUCCESS; cb_parms.cookie = dirent->hk.k; tmp_status = cache_inode_getattr(entry, &cb_parms, cb, CB_ORIGINAL); if (tmp_status != CACHE_INODE_SUCCESS) { cache_inode_lru_unref(entry, LRU_FLAG_NONE); if (tmp_status == CACHE_INODE_ESTALE) { if (retry_stale) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_getattr returned " "%s for %s - retrying entry", cache_inode_err_str (tmp_status), dirent->name); retry_stale = false; /* only one retry * per dirent */ goto estale_retry; } /* Directory changed out from under us. Invalidate it, skip the name, and keep going. */ atomic_clear_uint32_t_bits( &directory->flags, CACHE_INODE_TRUST_CONTENT); LogDebug(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs " "returned %s for %s - skipping entry", cache_inode_err_str(tmp_status), dirent->name); continue; } status = tmp_status; LogCrit(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs returned %s for " "%s - bailing out", cache_inode_err_str(status), dirent->name); goto unlock_dir; } (*nbfound)++; cache_inode_lru_unref(entry, LRU_FLAG_NONE); if (!cb_parms.in_result) { LogDebug(COMPONENT_NFS_READDIR, "bailing out due to entry not in result"); break; } } /* We have reached the last node and every node traversed was added to the result */ LogDebug(COMPONENT_NFS_READDIR, "dirent_node = %p, nbfound = %u, in_result = %s", dirent_node, *nbfound, cb_parms.in_result ? "TRUE" : "FALSE"); if (!dirent_node && cb_parms.in_result) *eod_met = true; else *eod_met = false; unlock_dir: PTHREAD_RWLOCK_unlock(&directory->content_lock); return status; } /* cache_inode_readdir */
/** * * cache_inode_renew_entry: Renews the attributes for an entry. * * Sets the attributes for an entry located in the cache by its address. Attributes are provided * with compliance to the underlying FSAL semantics. Attributes that are set are returned in "*pattr". * * @param pentry_parent [IN] entry for the parent directory to be managed. * @param pattr [OUT] renewed attributes for the entry that we have found. * @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 Other errors shows a FSAL error. * */ cache_inode_status_t cache_inode_renew_entry(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_handle_t *pfsal_handle = NULL; fsal_status_t fsal_status; fsal_attrib_list_t object_attributes; fsal_path_t link_content; time_t current_time = time(NULL); time_t entry_time = pentry->internal_md.refresh_time; /* If we do nothing (no expiration) then everything is all right */ *pstatus = CACHE_INODE_SUCCESS; if(isFullDebug(COMPONENT_CACHE_INODE)) { char *type; char grace[20], grace2[20]; unsigned int elapsed = (unsigned int)current_time - (unsigned int)entry_time; int print = 1; cache_inode_expire_to_str(pclient->expire_type_attr, pclient->grace_period_attr, grace); switch(pentry->internal_md.type) { case UNASSIGNED: type = "UNASSIGNED"; break; case REGULAR_FILE: type = "REGULAR_FILE"; break; case CHARACTER_FILE: type = "CHARACTER_FILE"; break; case BLOCK_FILE: type = "BLOCK_FILE"; break; case SYMBOLIC_LINK: print = 0; cache_inode_expire_to_str(pclient->expire_type_link, pclient->grace_period_link, grace2); LogDebug(COMPONENT_CACHE_INODE, "Renew Entry test of %p for SYMBOLIC_LINK elapsed time=%u, grace_period_attr=%s, grace_period_link=%s", pentry, elapsed, grace, grace2); break; case SOCKET_FILE: type = "SOCKET_FILE"; break; case FIFO_FILE: type = "FIFO_FILE"; break; case DIRECTORY: print = 0; cache_inode_expire_to_str(pclient->expire_type_dirent, pclient->grace_period_dirent, grace2); LogDebug(COMPONENT_CACHE_INODE, "Renew Entry test of %p for DIRECTORY elapsed time=%u, grace_period_attr=%s, grace_period_dirent=%s, has_been_readdir=%u", pentry, elapsed, grace, grace2, pentry->object.dir.has_been_readdir); break; case FS_JUNCTION: type = "FS_JUNCTION"; break; case RECYCLED: type = "RECYCLED"; break; default: type = "UNKNOWN"; break; } if(print) { LogDebug(COMPONENT_CACHE_INODE, "Renew Entry test of %p for %s elapsed time=%u, grace_period_attr=%s", pentry, type, elapsed, grace); } } /* An entry that is a regular file with an associated File Content Entry won't * expire until data exists in File Content Cache, to avoid attributes incoherency */ /* @todo: BUGAZOMEU: I got serious doubts on the following blocks: possible trouble if using data caching */ if(pentry->internal_md.type == REGULAR_FILE && pentry->object.file.pentry_content != NULL) { /* Successfull exit without having nothing to do ... */ LogDebug(COMPONENT_CACHE_INODE, "Entry %p is a REGULAR_FILE with associated data cached %p, no expiration", pentry, pentry->object.file.pentry_content); *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry use getattr/mtime checking %d, is dir " "beginning %d, has bit in mask %d, has been readdir %d state %d", pclient->getattr_dir_invalidation, pentry->internal_md.type == DIRECTORY, (int) FSAL_TEST_MASK(pclient->attrmask, FSAL_ATTR_MTIME), pentry->object.dir.has_been_readdir, pentry->internal_md.valid_state); /* Do we use getattr/mtime checking */ if(pclient->getattr_dir_invalidation && pentry->internal_md.type == DIRECTORY && FSAL_TEST_MASK(pclient->attrmask, FSAL_ATTR_MTIME) /*&& pentry->object.dir.has_been_readdir == CACHE_INODE_YES*/) { /* This checking is to be done ... */ LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry testing directory mtime"); pfsal_handle = &pentry->object.dir.handle; /* Call FSAL to get the attributes */ object_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes); 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_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)", pentry, __LINE__, 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_renew_entry: 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_RENEW_ENTRY])++; LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: returning %d (%s) from FSAL_getattrs for getattr/mtime checking", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Entry=%p, type=%d, Cached Time=%d, FSAL Time=%d", pentry, pentry->internal_md.type, pentry->object.dir.attributes.mtime.seconds, object_attributes.mtime.seconds); /* Compare the FSAL mtime and the cached mtime */ if(pentry->object.dir.attributes.mtime.seconds < object_attributes.mtime.seconds) { /* Cached directory content is obsolete, it must be renewed */ cache_inode_set_attributes(pentry, &object_attributes); /* Return the attributes as set */ if(pattr != NULL) *pattr = object_attributes; /* Set the directory content as "to be renewed" */ /* Next call to cache_inode_readdir will repopulate the dirent array */ pentry->object.dir.has_been_readdir = CACHE_INODE_RENEW_NEEDED; /* Set the refresh time for the cache entry */ pentry->internal_md.refresh_time = time(NULL); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: cached directory content for entry %p must be renewed, due to getattr mismatch", pentry); if(cache_inode_invalidate_all_cached_dirent(pentry, ht, pclient, pstatus) != CACHE_INODE_SUCCESS) { /* Should never happen */ LogCrit(COMPONENT_CACHE_INODE, "cache_inode_invalidate_all_cached_dirent returned %d (%s)", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } } /* if( pentry->object.dir.attributes.mtime < object_attributes.asked_attributes.mtime ) */ } /* if( pclient->getattr_dir_invalidation && ... */ /* Check for dir content expiration and/or staleness */ if(pentry->internal_md.type == DIRECTORY && pclient->expire_type_dirent != CACHE_INODE_EXPIRE_NEVER && pentry->object.dir.has_been_readdir == CACHE_INODE_YES && ((current_time - entry_time >= pclient->grace_period_dirent) || (pentry->internal_md.valid_state == STALE))) { /* Would be better if state was a flag that we could and/or the bits but * in any case we need to get rid of stale so we only go through here * once. */ if ( pentry->internal_md.valid_state == STALE ) pentry->internal_md.valid_state = VALID; /* stats */ (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++; /* Log */ LogDebug(COMPONENT_CACHE_INODE, "Case 1: cached directory entries for entry %p must be renewed" " (has been readdir)", pentry); if(isFullDebug(COMPONENT_CACHE_INODE)) { char name[1024]; struct avltree_node *d_node; cache_inode_dir_entry_t *d_dirent; int i = 0; d_node = avltree_first(&pentry->object.dir.dentries); do { d_dirent = avltree_container_of(d_node, cache_inode_dir_entry_t, node_n); if (d_dirent->pentry->internal_md.valid_state == VALID) { FSAL_name2str(&(d_dirent->name), name, 1023); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Entry %d %s", i, name); } i++; } while ((d_node = avltree_next(d_node))); } /* Do the getattr if it had not being done before */ if(pfsal_handle == NULL) { pfsal_handle = &pentry->object.dir.handle; /* Call FSAL to get the attributes */ object_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)", pentry, __LINE__,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_renew_entry: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for directory entries (1)", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } } cache_inode_set_attributes(pentry, &object_attributes); /* Return the attributes as set */ if(pattr != NULL) *pattr = object_attributes; /* Set the directory content as "to be renewed" */ /* Next call to cache_inode_readdir will repopulate the dirent array */ pentry->object.dir.has_been_readdir = CACHE_INODE_RENEW_NEEDED; /* Set the refresh time for the cache entry */ pentry->internal_md.refresh_time = time(NULL); } /* if( pentry->internal_md.type == DIRECTORY && ... */ /* if the directory has not been readdir, only update its attributes */ else if(pentry->internal_md.type == DIRECTORY && pclient->expire_type_attr != CACHE_INODE_EXPIRE_NEVER && pentry->object.dir.has_been_readdir != CACHE_INODE_YES && ((current_time - entry_time >= pclient->grace_period_attr) || (pentry->internal_md.valid_state == STALE))) { /* Would be better if state was a flag that we could and/or the bits but * in any case we need to get rid of stale so we only go through here * once. */ if ( pentry->internal_md.valid_state == STALE ) pentry->internal_md.valid_state = VALID; /* stats */ (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++; /* Log */ LogDebug(COMPONENT_CACHE_INODE, "Case 2: cached directory entries for entry %p must be renewed (has not been readdir)", pentry); if(isFullDebug(COMPONENT_CACHE_INODE)) { char name[1024]; struct avltree_node *d_node; cache_inode_dir_entry_t *d_dirent; int i = 0; d_node = avltree_first(&pentry->object.dir.dentries); do { d_dirent = avltree_container_of(d_node, cache_inode_dir_entry_t, node_n); if (d_dirent->pentry->internal_md.valid_state == VALID) { FSAL_name2str(&(d_dirent->name), name, 1023); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Entry %d %s", i, name); } i++; } while ((d_node = avltree_next(d_node))); } pfsal_handle = &pentry->object.dir.handle; /* Call FSAL to get the attributes */ object_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)", pentry, __LINE__,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_renew_entry: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for directory entries (2)", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } cache_inode_set_attributes(pentry, &object_attributes); /* Return the attributes as set */ if(pattr != NULL) *pattr = object_attributes; /* Set the refresh time for the cache entry */ pentry->internal_md.refresh_time = time(NULL); } /* else if( pentry->internal_md.type == DIRECTORY && ... */ /* Check for attributes expiration in other cases */ else if(pentry->internal_md.type != DIRECTORY && pclient->expire_type_attr != CACHE_INODE_EXPIRE_NEVER && ((current_time - entry_time >= pclient->grace_period_attr) || (pentry->internal_md.valid_state == STALE))) { /* Would be better if state was a flag that we could and/or the bits but * in any case we need to get rid of stale so we only go through here * once. */ if ( pentry->internal_md.valid_state == STALE ) pentry->internal_md.valid_state = VALID; /* stats */ (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++; /* Log */ LogDebug(COMPONENT_CACHE_INODE, "Attributes for entry %p must be renewed", pentry); 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 SOCKET_FILE: case FIFO_FILE: case CHARACTER_FILE: case BLOCK_FILE: pfsal_handle = &pentry->object.special_obj.handle; break; case DIRECTORY: case FS_JUNCTION: case UNASSIGNED: case RECYCLED: LogCrit(COMPONENT_CACHE_INODE, "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s", pentry->internal_md.type, __LINE__, __FILE__); *pstatus = CACHE_INODE_BAD_TYPE; return *pstatus; } /* Call FSAL to get the attributes */ object_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL fsal_status = FSAL_getattrs_descriptor(&(cache_inode_fd(pentry)->fsal_file), pfsal_handle, pcontext, &object_attributes); #else fsal_status = FSAL_getattrs_descriptor(cache_inode_fd(pentry), pfsal_handle, pcontext, &object_attributes); #endif if(FSAL_IS_ERROR(fsal_status) && fsal_status.major == ERR_FSAL_NOT_OPENED) { //TODO: LOOKATME !!!!! fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes); } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)", pentry, __LINE__,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_renew_entry: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for non directories", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* Keep the new attribute in cache */ cache_inode_set_attributes(pentry, &object_attributes); /* Return the attributes as set */ if(pattr != NULL) *pattr = object_attributes; /* Set the refresh time for the cache entry */ pentry->internal_md.refresh_time = time(NULL); } /* if( pentry->internal_md.type != DIR_CONTINUE && ... */ /* Check for link content expiration */ if(pentry->internal_md.type == SYMBOLIC_LINK && pclient->expire_type_link != CACHE_INODE_EXPIRE_NEVER && ((current_time - entry_time >= pclient->grace_period_link) || (pentry->internal_md.valid_state == STALE))) { assert(pentry->object.symlink); pfsal_handle = &pentry->object.symlink->handle; /* Would be better if state was a flag that we could and/or the bits but * in any case we need to get rid of stale so we only go through here * once. */ if ( pentry->internal_md.valid_state == STALE ) pentry->internal_md.valid_state = VALID; assert(pentry->object.symlink); pfsal_handle = &pentry->object.symlink->handle; /* Log */ LogDebug(COMPONENT_CACHE_INODE, "cached link content for entry %p must be renewed", pentry); FSAL_CLEAR_MASK(object_attributes.asked_attributes); FSAL_SET_MASK(object_attributes.asked_attributes, pclient->attrmask); if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) ) { #ifdef _USE_MFSL fsal_status = MFSL_readlink(&pentry->mobject, pcontext, &pclient->mfsl_context, &link_content, &object_attributes, NULL); #else fsal_status = FSAL_readlink(pfsal_handle, pcontext, &link_content, &object_attributes); #endif } else { fsal_status.major = ERR_FSAL_NO_ERROR ; fsal_status.minor = 0 ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY] += 1; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)", pentry, __LINE__,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_renew_entry: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } } else { assert(pentry->object.symlink); fsal_status = FSAL_pathcpy(&pentry->object.symlink->content, &link_content); /* copy ctor? */ if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY] += 1; } } /* Set the refresh time for the cache entry */ pentry->internal_md.refresh_time = time(NULL); } /* if( pentry->internal_md.type == SYMBOLIC_LINK && ... */ LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry returning %d (%s)", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* cache_inode_renew_entry */