nfs-ganesha 1.4
|
00001 /* 00002 * vim:expandtab:shiftwidth=8:tabstop=8: 00003 * 00004 * Copyright CEA/DAM/DIF (2008) 00005 * contributeur : Philippe DENIEL philippe.deniel@cea.fr 00006 * Thomas LEIBOVICI thomas.leibovici@cea.fr 00007 * 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 3 of the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00022 * 00023 * --------------------------------------- 00024 */ 00025 00038 #ifdef HAVE_CONFIG_H 00039 #include "config.h" 00040 #endif 00041 00042 #ifdef _SOLARIS 00043 #include "solaris_port.h" 00044 #endif /* _SOLARIS */ 00045 00046 #include "abstract_atomic.h" 00047 #include "log.h" 00048 #include "HashData.h" 00049 #include "HashTable.h" 00050 #include "fsal.h" 00051 #include "cache_inode.h" 00052 #include "cache_inode_lru.h" 00053 #include "cache_inode_avl.h" 00054 #include "cache_inode_weakref.h" 00055 00056 #include <unistd.h> 00057 #include <sys/types.h> 00058 #include <sys/param.h> 00059 #include <time.h> 00060 #include <pthread.h> 00061 #include <assert.h> 00062 00075 cache_inode_status_t 00076 cache_inode_invalidate_all_cached_dirent(cache_entry_t *entry, 00077 cache_inode_status_t *status) 00078 { 00079 /* Set the return default to CACHE_INODE_SUCCESS */ 00080 *status = CACHE_INODE_SUCCESS; 00081 00082 /* Only DIRECTORY entries are concerned */ 00083 if (entry->type != DIRECTORY) { 00084 *status = CACHE_INODE_BAD_TYPE; 00085 return *status; 00086 } 00087 00088 /* Get ride of entries cached in the DIRECTORY */ 00089 cache_inode_release_dirents(entry, CACHE_INODE_AVL_BOTH); 00090 00091 /* Mark directory as not populated */ 00092 atomic_clear_uint32_t_bits(&entry->flags, (CACHE_INODE_DIR_POPULATED | 00093 CACHE_INODE_TRUST_CONTENT)); 00094 *status = CACHE_INODE_SUCCESS; 00095 00096 return *status; 00097 } /* cache_inode_invalidate_all_cached_dirent */ 00098 00099 00125 cache_inode_status_t 00126 cache_inode_operate_cached_dirent(cache_entry_t *directory, 00127 fsal_name_t *name, 00128 fsal_name_t *newname, 00129 cache_inode_dirent_op_t dirent_op) 00130 { 00131 cache_inode_dir_entry_t dirent_key[1], *dirent, *dirent2, *dirent3; 00132 cache_inode_status_t status = CACHE_INODE_SUCCESS; 00133 int code = 0; 00134 00135 /* Sanity check */ 00136 if(directory->type != DIRECTORY) { 00137 status = CACHE_INODE_BAD_TYPE; 00138 goto out; 00139 } 00140 00141 /* If no active entry, do nothing */ 00142 if (directory->object.dir.nbactive == 0) { 00143 if (!((directory->flags & CACHE_INODE_TRUST_CONTENT) && 00144 (directory->flags & CACHE_INODE_DIR_POPULATED))) { 00145 /* We cannot serve negative lookups. */ 00146 /* status == CACHE_INODE_SUCCESS; */ 00147 } else { 00148 status = CACHE_INODE_NOT_FOUND; 00149 } 00150 goto out; 00151 } 00152 00153 FSAL_namecpy(&dirent_key->name, name); 00154 dirent = cache_inode_avl_qp_lookup_s(directory, dirent_key, 1); 00155 if ((!dirent) || (dirent->flags & DIR_ENTRY_FLAG_DELETED)) { 00156 if (!((directory->flags & CACHE_INODE_TRUST_CONTENT) && 00157 (directory->flags & CACHE_INODE_DIR_POPULATED))) { 00158 /* We cannot serve negative lookups. */ 00159 /* status == CACHE_INODE_SUCCESS; */ 00160 } else { 00161 status = CACHE_INODE_NOT_FOUND; 00162 } 00163 goto out; 00164 } 00165 00166 00167 /* We perform operations anyway even if CACHE_INODE_TRUST_CONTENT 00168 is clear. That way future upcalls can call in to this 00169 function to update the content to be correct. We just don't 00170 ever return a not found or exists error. */ 00171 00172 switch (dirent_op) { 00173 case CACHE_INODE_DIRENT_OP_REMOVE: 00174 /* mark deleted */ 00175 avl_dirent_set_deleted(directory, dirent); 00176 directory->object.dir.nbactive--; 00177 break; 00178 00179 case CACHE_INODE_DIRENT_OP_RENAME: 00180 FSAL_namecpy(&dirent_key->name, newname); 00181 dirent2 = cache_inode_avl_qp_lookup_s(directory, 00182 dirent_key, 1); 00183 if (dirent2) { 00184 /* rename would cause a collision */ 00185 if (directory->flags & 00186 CACHE_INODE_TRUST_CONTENT) { 00187 /* We are not up to date. */ 00188 /* status == CACHE_INODE_SUCCESS; */ 00189 } else { 00190 status = CACHE_INODE_ENTRY_EXISTS; 00191 } 00192 } else { 00193 /* try to rename--no longer in-place */ 00194 avl_dirent_set_deleted(directory, dirent); 00195 dirent3 = pool_alloc(cache_inode_dir_entry_pool, NULL); 00196 FSAL_namecpy(&dirent3->name, newname); 00197 dirent3->flags = DIR_ENTRY_FLAG_NONE; 00198 dirent3->entry = dirent->entry; 00199 code = cache_inode_avl_qp_insert(directory, dirent3); 00200 switch (code) { 00201 case 0: 00202 /* CACHE_INODE_SUCCESS */ 00203 break; 00204 case 1: 00205 /* we reused an existing dirent, dirent has been deep 00206 * copied, dispose it */ 00207 pool_free(cache_inode_dir_entry_pool, dirent3); 00208 /* CACHE_INODE_SUCCESS */ 00209 break; 00210 case -1: 00211 /* collision, tree state unchanged (unlikely) */ 00212 status = CACHE_INODE_ENTRY_EXISTS; 00213 /* dirent is on persist tree, undelete it */ 00214 avl_dirent_clear_deleted(directory, dirent); 00215 /* dirent3 was never inserted */ 00216 pool_free(cache_inode_dir_entry_pool, dirent3); 00217 default: 00218 LogCrit(COMPONENT_NFS_READDIR, 00219 "DIRECTORY: insert error renaming dirent " 00220 "(%s, %s)", 00221 name->name, newname->name); 00222 status = CACHE_INODE_INSERT_ERROR; 00223 break; 00224 } 00225 } /* !found */ 00226 break; 00227 00228 default: 00229 /* Should never occur, in any case, it costs nothing to handle 00230 * this situation */ 00231 status = CACHE_INODE_INVALID_ARGUMENT; 00232 break; 00233 00234 } /* switch */ 00235 00236 out: 00237 return (status); 00238 } /* cache_inode_operate_cached_dirent */ 00239 00259 cache_inode_status_t 00260 cache_inode_add_cached_dirent(cache_entry_t *parent, 00261 fsal_name_t *name, 00262 cache_entry_t *entry, 00263 cache_inode_dir_entry_t **dir_entry, 00264 cache_inode_status_t *status) 00265 { 00266 cache_inode_dir_entry_t *new_dir_entry = NULL; 00267 int code = 0; 00268 00269 *status = CACHE_INODE_SUCCESS; 00270 00271 /* Sanity check */ 00272 if(parent->type != DIRECTORY) { 00273 *status = CACHE_INODE_BAD_TYPE; 00274 return *status; 00275 } 00276 00277 /* in cache inode avl, we always insert on pentry_parent */ 00278 new_dir_entry = pool_alloc(cache_inode_dir_entry_pool, NULL); 00279 if(new_dir_entry == NULL) { 00280 *status = CACHE_INODE_MALLOC_ERROR; 00281 return *status; 00282 } 00283 00284 new_dir_entry->flags = DIR_ENTRY_FLAG_NONE; 00285 00286 FSAL_namecpy(&new_dir_entry->name, name); 00287 new_dir_entry->entry = entry->weakref; 00288 00289 /* add to avl */ 00290 code = cache_inode_avl_qp_insert(parent, new_dir_entry); 00291 switch (code) { 00292 case 0: 00293 /* CACHE_INODE_SUCCESS */ 00294 break; 00295 case 1: 00296 /* we reused an existing dirent, dirent has been deep 00297 * copied, dispose it */ 00298 pool_free(cache_inode_dir_entry_pool, new_dir_entry); 00299 /* CACHE_INODE_SUCCESS */ 00300 break; 00301 default: 00302 /* collision, tree not updated--release both pool objects and return 00303 * err */ 00304 pool_free(cache_inode_dir_entry_pool, new_dir_entry); 00305 *status = CACHE_INODE_ENTRY_EXISTS; 00306 return *status; 00307 break; 00308 } 00309 00310 if (dir_entry) { 00311 *dir_entry = new_dir_entry; 00312 } 00313 00314 /* we're going to succeed */ 00315 parent->object.dir.nbactive++; 00316 00317 return *status; 00318 } /* cache_inode_add_cached_dirent */ 00319 00336 cache_inode_status_t 00337 cache_inode_remove_cached_dirent(cache_entry_t *directory, 00338 fsal_name_t *name, 00339 cache_inode_status_t *status) 00340 { 00341 /* Set the return default to CACHE_INODE_SUCCESS */ 00342 *status = CACHE_INODE_SUCCESS; 00343 00344 /* Sanity check */ 00345 if(directory->type != DIRECTORY) 00346 { 00347 *status = CACHE_INODE_BAD_TYPE; 00348 return *status; 00349 } 00350 00351 *status 00352 = cache_inode_operate_cached_dirent(directory, 00353 name, 00354 NULL, 00355 CACHE_INODE_DIRENT_OP_REMOVE); 00356 return (*status); 00357 00358 } /* cache_inode_remove_cached_dirent */ 00359 00373 cache_inode_status_t 00374 cache_inode_readdir_populate(cache_entry_t *directory, 00375 fsal_op_context_t *context, 00376 cache_inode_status_t *status) 00377 { 00378 fsal_dir_t dir_handle; 00379 fsal_status_t fsal_status; 00380 fsal_attrib_list_t dir_attributes; 00381 00382 fsal_cookie_t begin_cookie; 00383 fsal_cookie_t end_cookie; 00384 fsal_count_t found = 0; 00385 uint32_t iter = 0; 00386 fsal_boolean_t eod = FALSE; 00387 00388 cache_entry_t *entry = NULL; 00389 fsal_attrib_list_t object_attributes; 00390 00391 cache_inode_create_arg_t create_arg = { 00392 .newly_created_dir = FALSE 00393 }; 00394 cache_inode_file_type_t type = UNASSIGNED; 00395 cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; 00396 fsal_dirent_t array_dirent[FSAL_READDIR_SIZE + 20]; 00397 cache_inode_fsal_data_t new_entry_fsdata; 00398 cache_inode_dir_entry_t *new_dir_entry = NULL; 00399 uint64_t i = 0; 00400 00401 /* Set the return default to CACHE_INODE_SUCCESS */ 00402 *status = CACHE_INODE_SUCCESS; 00403 00404 /* Only DIRECTORY entries are concerned */ 00405 if(directory->type != DIRECTORY) 00406 { 00407 *status = CACHE_INODE_BAD_TYPE; 00408 return *status; 00409 } 00410 00411 if((directory->flags & CACHE_INODE_DIR_POPULATED) && 00412 (directory->flags & CACHE_INODE_TRUST_CONTENT)) 00413 { 00414 *status = CACHE_INODE_SUCCESS; 00415 return *status; 00416 } 00417 00418 /* Invalidate all the dirents */ 00419 if(cache_inode_invalidate_all_cached_dirent(directory, 00420 status) != CACHE_INODE_SUCCESS) 00421 return *status; 00422 00423 /* Open the directory */ 00424 dir_attributes.asked_attributes = cache_inode_params.attrmask; 00425 fsal_status = FSAL_opendir(&directory->handle, 00426 context, &dir_handle, &dir_attributes); 00427 if(FSAL_IS_ERROR(fsal_status)) 00428 { 00429 *status = cache_inode_error_convert(fsal_status); 00430 if (fsal_status.major == ERR_FSAL_STALE) { 00431 cache_inode_kill_entry(directory); 00432 } 00433 return *status; 00434 } 00435 00436 /* Loop for readding the directory */ 00437 FSAL_SET_COOKIE_BEGINNING(begin_cookie); 00438 FSAL_SET_COOKIE_BEGINNING(end_cookie); 00439 eod = FALSE; 00440 00441 do 00442 { 00443 fsal_status 00444 = FSAL_readdir(&dir_handle, 00445 begin_cookie, 00446 cache_inode_params.attrmask, 00447 FSAL_READDIR_SIZE * sizeof(fsal_dirent_t), 00448 array_dirent, &end_cookie, &found, &eod); 00449 00450 if(FSAL_IS_ERROR(fsal_status)) 00451 { 00452 *status = cache_inode_error_convert(fsal_status); 00453 goto bail; 00454 } 00455 00456 for(iter = 0; iter < found; iter++) 00457 { 00458 LogMidDebug(COMPONENT_CACHE_INODE, 00459 "cache readdir populate found entry %s", 00460 array_dirent[iter].name.name); 00461 00462 /* It is not needed to cache '.' and '..' */ 00463 if(!FSAL_namecmp(&(array_dirent[iter].name), 00464 (fsal_name_t *) & FSAL_DOT) || 00465 !FSAL_namecmp(&(array_dirent[iter].name), 00466 (fsal_name_t *) & FSAL_DOT_DOT)) 00467 { 00468 LogMidDebug(COMPONENT_CACHE_INODE, 00469 "cache readdir populate : do not cache . and .."); 00470 continue; 00471 } 00472 00473 /* If dir entry is a symbolic link, its content has to be read */ 00474 if((type = 00475 cache_inode_fsal_type_convert(array_dirent[iter] 00476 .attributes.type)) 00477 == SYMBOLIC_LINK) 00478 { 00479 /* Let's read the link for caching its value */ 00480 object_attributes.asked_attributes = cache_inode_params.attrmask; 00481 fsal_status 00482 = FSAL_readlink(&array_dirent[iter].handle, 00483 context, 00484 &create_arg.link_content, &object_attributes); 00485 00486 if(FSAL_IS_ERROR(fsal_status)) 00487 { 00488 *status = cache_inode_error_convert(fsal_status); 00489 if (fsal_status.major == ERR_FSAL_STALE) { 00490 cache_inode_kill_entry(directory); 00491 } 00492 goto bail; 00493 } 00494 } 00495 else 00496 { 00497 create_arg.newly_created_dir = FALSE; 00498 } 00499 00500 /* Try adding the entry, if it exists then this existing entry is 00501 returned */ 00502 new_entry_fsdata.fh_desc.start 00503 = (caddr_t)(&array_dirent[iter].handle); 00504 new_entry_fsdata.fh_desc.len = 0; 00505 FSAL_ExpandHandle(context->export_context, 00506 FSAL_DIGEST_SIZEOF, 00507 &new_entry_fsdata.fh_desc); 00508 00509 if((entry 00510 = cache_inode_new_entry(&new_entry_fsdata, 00511 &array_dirent[iter].attributes, 00512 type, 00513 &create_arg, 00514 status)) == NULL) 00515 goto bail; 00516 cache_status 00517 = cache_inode_add_cached_dirent(directory, 00518 &(array_dirent[iter].name), 00519 entry, 00520 &new_dir_entry, 00521 status); 00522 00523 /* Once the weakref is stored in the directory entry, we 00524 can release the reference we took on the entry. */ 00525 cache_inode_lru_unref(entry, 0); 00526 00527 if(cache_status != CACHE_INODE_SUCCESS 00528 && cache_status != CACHE_INODE_ENTRY_EXISTS) 00529 goto bail; 00530 00531 /* 00532 * Remember the FSAL readdir cookie associated with this 00533 * dirent. This is needed for partial directory reads. 00534 * 00535 * to_uint64 should be a lightweight operation--it is in the 00536 * current default implementation. 00537 * 00538 * I'm ignoring the status because the default operation is 00539 * a memcpy-- we already -have- the cookie. */ 00540 00541 if (cache_status != CACHE_INODE_ENTRY_EXISTS) 00542 FSAL_cookie_to_uint64(&array_dirent[iter].handle, 00543 context, &array_dirent[iter].cookie, 00544 &new_dir_entry->fsal_cookie); 00545 } /* iter */ 00546 00547 /* Get prepared for next step */ 00548 begin_cookie = end_cookie; 00549 00550 /* next offset */ 00551 i++; 00552 } 00553 while(eod != TRUE); 00554 00555 /* Close the directory */ 00556 fsal_status = FSAL_closedir(&dir_handle); 00557 if(FSAL_IS_ERROR(fsal_status)) 00558 { 00559 *status = cache_inode_error_convert(fsal_status); 00560 return *status; 00561 } 00562 00563 /* End of work */ 00564 atomic_set_uint32_t_bits(&directory->flags, 00565 (CACHE_INODE_DIR_POPULATED | 00566 CACHE_INODE_TRUST_CONTENT)); 00567 *status = CACHE_INODE_SUCCESS; 00568 return *status; 00569 00570 bail: 00571 /* Close the directory */ 00572 FSAL_closedir(&dir_handle); 00573 return *status; 00574 00575 } /* cache_inode_readdir_populate */ 00576 00600 cache_inode_status_t 00601 cache_inode_readdir(cache_entry_t *directory, 00602 uint64_t cookie, 00603 unsigned int *nbfound, 00604 bool_t *eod_met, 00605 fsal_op_context_t *context, 00606 cache_inode_readdir_cb_t cb, 00607 void *cb_opaque, 00608 cache_inode_status_t *status) 00609 { 00610 /* The entry being examined */ 00611 cache_inode_dir_entry_t *dirent = NULL; 00612 /* The node in the tree being traversed */ 00613 struct avltree_node *dirent_node; 00614 /* The access mask corresponding to permission to list directory 00615 entries */ 00616 const fsal_accessflags_t access_mask 00617 = (FSAL_MODE_MASK_SET(FSAL_R_OK) | 00618 FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR)); 00619 /* True if the most recently traversed directory entry has been 00620 added to the caller's result. */ 00621 bool_t in_result = TRUE; 00622 00623 /* Set the return default to CACHE_INODE_SUCCESS */ 00624 *status = CACHE_INODE_SUCCESS; 00625 00626 /* readdir can be done only with a directory */ 00627 if (directory->type != DIRECTORY) { 00628 *status = CACHE_INODE_BAD_TYPE; 00629 /* no lock acquired so far, just return status */ 00630 return *status; 00631 } 00632 00633 /* cache_inode_lock_trust_attrs can return an error, and no lock will be 00634 acquired */ 00635 *status = cache_inode_lock_trust_attrs(directory, context); 00636 if (*status != CACHE_INODE_SUCCESS) 00637 return *status; 00638 00639 /* Check if user (as specified by the credentials) is authorized to read 00640 * the directory or not */ 00641 if (cache_inode_access_no_mutex(directory, 00642 access_mask, 00643 context, 00644 status) 00645 != CACHE_INODE_SUCCESS) { 00646 goto unlock_attrs; 00647 } 00648 00649 if (!((directory->flags & CACHE_INODE_TRUST_CONTENT) && 00650 (directory->flags & CACHE_INODE_DIR_POPULATED))) { 00651 pthread_rwlock_wrlock(&directory->content_lock); 00652 pthread_rwlock_unlock(&directory->attr_lock); 00653 if (cache_inode_readdir_populate(directory, 00654 context, 00655 status) 00656 != CACHE_INODE_SUCCESS) { 00657 goto unlock_dir; 00658 } 00659 } else { 00660 pthread_rwlock_rdlock(&directory->content_lock); 00661 pthread_rwlock_unlock(&directory->attr_lock); 00662 } 00663 00664 /* deal with initial cookie value: 00665 * 1. cookie is invalid (-should- be checked by caller) 00666 * 2. cookie is 0 (first cookie) -- ok 00667 * 3. cookie is > than highest dirent position (error) 00668 * 4. cookie <= highest dirent position but > highest cached cookie 00669 * (currently equivalent to #2, because we pre-populate the cookie avl) 00670 * 5. cookie is in cached range -- ok */ 00671 00672 if (cookie > 0) { 00673 /* N.B., cache_inode_avl_qp_insert_s ensures k > 2 */ 00674 if (cookie < 3) { 00675 *status = CACHE_INODE_BAD_COOKIE; 00676 goto unlock_dir; 00677 } 00678 00679 /* we assert this can now succeed */ 00680 dirent = cache_inode_avl_lookup_k(directory, cookie, 00681 CACHE_INODE_FLAG_NEXT_ACTIVE); 00682 if (!dirent) { 00683 LogFullDebug(COMPONENT_NFS_READDIR, 00684 "%s: seek to cookie=%"PRIu64" fail", 00685 __func__, cookie); 00686 *status = CACHE_INODE_NOT_FOUND; 00687 goto unlock_dir; 00688 } 00689 00690 /* dirent is the NEXT entry to return, since we sent 00691 * CACHE_INODE_FLAG_NEXT_ACTIVE */ 00692 dirent_node = &dirent->node_hk; 00693 00694 } else { 00695 /* initial readdir */ 00696 dirent_node = avltree_first(&directory->object.dir.avl.t); 00697 } 00698 00699 LogFullDebug(COMPONENT_NFS_READDIR, 00700 "About to readdir in cache_inode_readdir: directory=%p " 00701 "cookie=%"PRIu64" collisions %d", 00702 directory, 00703 cookie, 00704 directory->object.dir.avl.collisions); 00705 00706 /* Now satisfy the request from the cached readdir--stop when either 00707 * the requested sequence or dirent sequence is exhausted */ 00708 *nbfound = 0; 00709 *eod_met = FALSE; 00710 00711 while (in_result && dirent_node) { 00712 cache_entry_t *entry = NULL; 00713 cache_inode_status_t lookup_status = 0; 00714 00715 dirent = avltree_container_of(dirent_node, 00716 cache_inode_dir_entry_t, 00717 node_hk); 00718 00719 if ((entry 00720 = cache_inode_weakref_get(&dirent->entry, 00721 LRU_REQ_SCAN)) 00722 == NULL) { 00723 /* Entry fell out of the cache, load it back in. */ 00724 if ((entry 00725 = cache_inode_lookup_impl(directory, 00726 &dirent->name, 00727 context, 00728 &lookup_status)) 00729 == NULL) { 00730 if (lookup_status == CACHE_INODE_NOT_FOUND) { 00731 /* Directory changed out from under us. 00732 Invalidate it, skip the name, and keep 00733 going. */ 00734 atomic_clear_uint32_t_bits(&directory->flags, 00735 CACHE_INODE_TRUST_CONTENT); 00736 continue; 00737 } else { 00738 /* Something is more seriously wrong, 00739 probably an inconsistency. */ 00740 *status = lookup_status; 00741 goto unlock_dir; 00742 } 00743 } 00744 } 00745 00746 LogFullDebug(COMPONENT_NFS_READDIR, 00747 "cache_inode_readdir: dirent=%p name=%s " 00748 "cookie=%"PRIu64" (probes %d)", 00749 dirent, dirent->name.name, 00750 dirent->hk.k, dirent->hk.p); 00751 00752 *status = cache_inode_lock_trust_attrs(entry, context); 00753 if (*status != CACHE_INODE_SUCCESS) 00754 { 00755 cache_inode_lru_unref(entry, 0); 00756 goto unlock_dir; 00757 } 00758 00759 in_result = cb(cb_opaque, 00760 dirent->name.name, 00761 &entry->handle, 00762 &entry->attributes, 00763 dirent->hk.k); 00764 (*nbfound)++; 00765 pthread_rwlock_unlock(&entry->attr_lock); 00766 cache_inode_lru_unref(entry, 0); 00767 if (!in_result) { 00768 break; 00769 } 00770 dirent_node = avltree_next(dirent_node); 00771 } 00772 00773 /* We have reached the last node and every node traversed was 00774 added to the result */; 00775 00776 if (!dirent_node && in_result) { 00777 *eod_met = TRUE; 00778 } else { 00779 *eod_met = FALSE; 00780 } 00781 00782 unlock_dir: 00783 00784 pthread_rwlock_unlock(&directory->content_lock); 00785 return *status; 00786 00787 unlock_attrs: 00788 00789 pthread_rwlock_unlock(&directory->attr_lock); 00790 return *status; 00791 } /* cache_inode_readdir */