nfs-ganesha 1.4

cache_inode_lookup.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
00022  * 02110-1301 USA
00023  *
00024  * ---------------------------------------
00025  */
00026 
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 "log.h"
00047 #include "abstract_atomic.h"
00048 #include "HashData.h"
00049 #include "HashTable.h"
00050 #include "fsal.h"
00051 #include "cache_inode.h"
00052 #include "cache_inode_avl.h"
00053 #include "cache_inode_weakref.h"
00054 #include "cache_inode_lru.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 
00085 cache_entry_t *
00086 cache_inode_lookup_impl(cache_entry_t *parent,
00087                         fsal_name_t *name,
00088                         fsal_op_context_t *context,
00089                         cache_inode_status_t *status)
00090 {
00091      cache_inode_dir_entry_t dirent_key;
00092      cache_inode_dir_entry_t *dirent = NULL;
00093      cache_entry_t *entry = NULL;
00094      fsal_status_t fsal_status = {0, 0};
00095      fsal_handle_t object_handle;
00096      fsal_attrib_list_t object_attributes;
00097      cache_inode_create_arg_t create_arg = {
00098           .newly_created_dir = FALSE
00099      };
00100      cache_inode_file_type_t type = UNASSIGNED;
00101      cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
00102      cache_inode_fsal_data_t new_entry_fsdata;
00103      cache_inode_dir_entry_t *broken_dirent = NULL;
00104 
00105      memset(&dirent_key, 0, sizeof(dirent_key));
00106      memset(&new_entry_fsdata, 0, sizeof(new_entry_fsdata));
00107      memset(&object_handle, 0, sizeof(object_handle));
00108      memset(&object_attributes, 0, sizeof(object_attributes));
00109 
00110      /* Set the return default to CACHE_INODE_SUCCESS */
00111      *status = CACHE_INODE_SUCCESS;
00112 
00113      if(parent->type != DIRECTORY) {
00114           *status = CACHE_INODE_NOT_A_DIRECTORY;
00115           /* stats */
00116           return NULL;
00117      }
00118 
00119      /* if name is ".", use the input value */
00120      if (!FSAL_namecmp(name, (fsal_name_t *) &FSAL_DOT)) {
00121           entry = parent;
00122           /* Increment the refcount so the caller's decrementing it
00123              doesn't take us below the sentinel count. */
00124           if (cache_inode_lru_ref(entry, 0) !=
00125               CACHE_INODE_SUCCESS) {
00126                /* This cannot actually happen */
00127                LogFatal(COMPONENT_CACHE_INODE,
00128                         "There has been a grave failure in consistency: "
00129                         "Unable to increment reference count on an entry that "
00130                         "we should have referenced.");
00131           }
00132           goto out;
00133      } else if (!FSAL_namecmp(name, (fsal_name_t *) &FSAL_DOT_DOT)) {
00134           /* Directory do only have exactly one parent. This a limitation
00135            * in all FS, which implies that hard link are forbidden on
00136            * directories (so that they exists only in one dir).  Because
00137            * of this, the parent list is always limited to one element for
00138            * a dir.  Clients SHOULD never 'lookup( .. )' in something that
00139            * is no dir. */
00140           entry = cache_inode_lookupp_impl(parent, context, status);
00141           goto out;
00142      } else {
00143           int write_locked = 0;
00144           /* We first try avltree_lookup by name.  If that fails, we
00145            * dispatch to the FSAL. */
00146           FSAL_namecpy(&dirent_key.name, name);
00147           for (write_locked = 0; write_locked < 2; ++write_locked) {
00148                /* If the dirent cache is untrustworthy, don't even ask it */
00149                if (parent->flags & CACHE_INODE_TRUST_CONTENT) {
00150                     dirent = cache_inode_avl_qp_lookup_s(parent,
00151                                                          &dirent_key, 1);
00152                     if (dirent) {
00153                          /* Getting a weakref itself increases the refcount. */
00154                          entry = cache_inode_weakref_get(&dirent->entry,
00155                                                          LRU_REQ_SCAN);
00156                          if (entry == NULL) {
00157                               broken_dirent = dirent;
00158                               break;
00159                          } else {
00160                               /* We have our entry and a valid reference.
00161                                  Declare victory. */
00162                               *status = CACHE_INODE_SUCCESS;
00163                               goto out;
00164                          }
00165                     }
00166                     /* If the dirent cache is both fully populated and
00167                        valid, it can serve negative lookups. */
00168                     if (!dirent &&
00169                         (parent->flags & CACHE_INODE_DIR_POPULATED)) {
00170                          entry = NULL;
00171                          *status = CACHE_INODE_NOT_FOUND;
00172                          goto out;
00173                     }
00174                } else if (write_locked) {
00175                     /* We have the write lock and the content is
00176                        still invalid.  Empty it out and mark it valid
00177                        in preparation for caching the result of this
00178                        lookup. */
00179                     cache_inode_release_dirents(parent,
00180                                                 CACHE_INODE_AVL_BOTH);
00181                     atomic_set_uint32_t_bits(&parent->flags,
00182                                              CACHE_INODE_TRUST_CONTENT);
00183                } else {
00184                     /* Get a write ock and do it again. */
00185                     pthread_rwlock_unlock(&parent->content_lock);
00186                     pthread_rwlock_wrlock(&parent->content_lock);
00187                }
00188           }
00189           assert(entry == NULL);
00190           LogDebug(COMPONENT_CACHE_INODE, "Cache Miss detected");
00191      }
00192 
00193      memset(&object_attributes, 0, sizeof(fsal_attrib_list_t));
00194      object_attributes.asked_attributes = cache_inode_params.attrmask;
00195      fsal_status =
00196           FSAL_lookup(&parent->handle,
00197                       name, context, &object_handle,
00198                       &object_attributes);
00199      if (FSAL_IS_ERROR(fsal_status)) {
00200           if (fsal_status.major == ERR_FSAL_STALE) {
00201                cache_inode_kill_entry(parent);
00202           }
00203           *status = cache_inode_error_convert(fsal_status);
00204           return NULL;
00205      }
00206 
00207      type = cache_inode_fsal_type_convert(object_attributes.type);
00208 
00209      /* If entry is a symlink, cache its target */
00210      if(type == SYMBOLIC_LINK) {
00211           fsal_status =
00212                FSAL_readlink(&object_handle,
00213                              context,
00214                              &create_arg.link_content,
00215                              &object_attributes);
00216 
00217           if(FSAL_IS_ERROR(fsal_status)) {
00218                *status = cache_inode_error_convert(fsal_status);
00219                return NULL;
00220           }
00221      }
00222 
00223      /* Allocation of a new entry in the cache */
00224      new_entry_fsdata.fh_desc.start = (caddr_t) &object_handle;
00225      new_entry_fsdata.fh_desc.len = 0;
00226      FSAL_ExpandHandle(context->export_context,
00227                        FSAL_DIGEST_SIZEOF,
00228                        &new_entry_fsdata.fh_desc);
00229 
00230      if((entry = cache_inode_new_entry(&new_entry_fsdata,
00231                                        &object_attributes,
00232                                        type,
00233                                        &create_arg,
00234                                        status)) == NULL) {
00235           return NULL;
00236      }
00237 
00238      if (broken_dirent) {
00239           /* Directory entry existed, but the weak reference
00240              was broken.  Just update with the new one. */
00241           broken_dirent->entry = entry->weakref;
00242           cache_status = CACHE_INODE_SUCCESS;
00243      } else {
00244           /* Entry was found in the FSAL, add this entry to the
00245              parent directory */
00246           cache_status = cache_inode_add_cached_dirent(parent,
00247                                                        name,
00248                                                        entry,
00249                                                        NULL,
00250                                                        status);
00251           if(cache_status != CACHE_INODE_SUCCESS &&
00252              cache_status != CACHE_INODE_ENTRY_EXISTS) {
00253                return NULL;
00254           }
00255      }
00256 
00257 out:
00258 
00259      return entry;
00260 } /* cache_inode_lookup_impl */
00261 
00280 cache_entry_t *
00281 cache_inode_lookup(cache_entry_t *parent,
00282                    fsal_name_t *name,
00283                    fsal_attrib_list_t *attr,
00284                    fsal_op_context_t *context,
00285                    cache_inode_status_t *status)
00286 {
00287      cache_entry_t *entry = NULL;
00288      fsal_accessflags_t access_mask
00289           = (FSAL_MODE_MASK_SET(FSAL_X_OK) |
00290              FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR));
00291 
00292      if (cache_inode_access(parent,
00293                             access_mask,
00294                             context,
00295                             status) !=
00296          CACHE_INODE_SUCCESS) {
00297           return NULL;
00298      }
00299 
00300      pthread_rwlock_rdlock(&parent->content_lock);
00301      entry = cache_inode_lookup_impl(parent,
00302                                      name,
00303                                      context,
00304                                      status);
00305      pthread_rwlock_unlock(&parent->content_lock);
00306 
00307      if (entry) {
00308           *status = cache_inode_lock_trust_attrs(entry,
00309                                                  context);
00310           if(*status == CACHE_INODE_SUCCESS)
00311             {
00312               *attr = entry->attributes;
00313               pthread_rwlock_unlock(&entry->attr_lock);
00314             }
00315      }
00316      return entry;
00317 } /* cache_inode_lookup */