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 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 */