nfs-ganesha 1.4

cache_inode_readdir.c

Go to the documentation of this file.
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 */