int cache_inode_gc_fd_func(LRU_entry_t * plru_entry, void *addparam) { cache_entry_t *pentry = NULL; cache_inode_param_gc_t *pgcparam = (cache_inode_param_gc_t *) addparam; cache_inode_status_t status; /* Get the entry */ pentry = (cache_entry_t *) (plru_entry->buffdata.pdata); /* check if a file descriptor is opened on the file for a long time */ if((pentry->internal_md.type == REGULAR_FILE) && (pentry->object.file.open_fd.fileno != 0) && (time(NULL) - pentry->object.file.open_fd.last_op > pgcparam->pclient->retention)) { P_w(&pentry->lock); cache_inode_close(pentry, pgcparam->pclient, &status); V_w(&pentry->lock); pgcparam->nb_to_be_purged--; } /* return true for continuing */ if(pgcparam->nb_to_be_purged == 0) return FALSE; else return TRUE; }
int ___cache_content_invalidate_flushed(LRU_entry_t * plru_entry, void *addparam) { cache_content_entry_t *pentry = NULL; cache_content_client_t *pclient = NULL; pentry = (cache_content_entry_t *) plru_entry->buffdata.pdata; pclient = (cache_content_client_t *) addparam; if(pentry->local_fs_entry.sync_state != SYNC_OK) { /* Entry is not to be set invalid */ return LRU_LIST_DO_NOT_SET_INVALID; } /* Clean up and set the entry invalid */ P_w(&pentry->pentry_inode->lock); pentry->pentry_inode->object.file.pentry_content = NULL; V_w(&pentry->pentry_inode->lock); /* Release the entry */ ReleaseToPool(pentry, &pclient->content_pool); /* Retour de la pentry dans le pool */ return LRU_LIST_SET_INVALID; } /* cache_content_invalidate_flushed */
/** * * HashTable_Del: Remove a (key,val) couple from the hashtable. * * Remove a (key,val) couple from the hashtable. The parameter buffkey contains the key which describes the object to be removed * from the hash table. * * @param ht the hashtable to be used. * @param buffkey a pointeur to an object of type hash_buffer_t which describe the key location in memory. * @param pusedbuffkeydata the key data buffer that was associated with this entry. Not considered if equal to NULL. * * @return HASHTABLE_SUCCESS if successfull\n. * @return HASHTABLE_ERROR_NO_SUCH_KEY is the key was not found. * * @see HashTable_Set * @see HashTable_Init * @see HashTable_Get */ int HashTable_Del(hash_table_t * ht, hash_buffer_t * buffkey, hash_buffer_t * p_usedbuffkey, hash_buffer_t * p_usedbuffdata) { unsigned int hashval; struct rbt_node *pn; int rbt_value = 0; struct rbt_head *tete_rbt; hash_data_t *pdata = NULL; int rc = 0; /* Sanity check */ if(ht == NULL || buffkey == NULL) return HASHTABLE_ERROR_INVALID_ARGUMENT; /* Find the RB Tree to be processed */ hashval = (*(ht->parameter.hash_func_key)) (&ht->parameter, buffkey); /* Find the entry to be deleted */ rbt_value = (*(ht->parameter.hash_func_rbt)) (&ht->parameter, buffkey); /* acquire mutex */ P_w(&(ht->array_lock[hashval])); /* We didn't find anything */ if((rc = Key_Locate(ht, buffkey, hashval, rbt_value, &pn)) != HASHTABLE_SUCCESS) { ht->stat_dynamic[hashval].notfound.nb_del += 1; V_w(&(ht->array_lock[hashval])); return rc; } pdata = (hash_data_t *) RBT_OPAQ(pn); /* Return the key buffer back to the end user if pusedbuffkey isn't NULL */ if(p_usedbuffkey != NULL) *p_usedbuffkey = pdata->buffkey; if(p_usedbuffdata != NULL) *p_usedbuffdata = pdata->buffval; /* Key was found */ tete_rbt = &(ht->array_rbt[hashval]); RBT_UNLINK(tete_rbt, pn); /* the key was located, the deletion is done */ ht->stat_dynamic[hashval].nb_entries -= 1; /* put back the pdata buffer to pool */ RELEASE_PREALLOC(pdata, ht->pdata_prealloc[hashval], next_alloc); /* Put the node back in the table of preallocated nodes (it could be reused) */ RELEASE_PREALLOC(pn, ht->node_prealloc[hashval], next); ht->stat_dynamic[hashval].ok.nb_del += 1; /* release mutex */ V_w(&(ht->array_lock[hashval])); return HASHTABLE_SUCCESS; } /* HashTable_Del */
/** * * cache_inode_gc_suppress_file: suppress a file entry from the cache inode. * * Suppress a file entry from the cache inode. * * @param pentry [IN] pointer to the entry to be suppressed. * * @return LRU_LIST_SET_INVALID if entry is successfully suppressed, LRU_LIST_DO_NOT_SET_INVALID otherwise * * @see LRU_invalidate_by_function * @see LRU_gc_invalid * */ int cache_inode_gc_suppress_file(cache_entry_t * pentry, cache_inode_param_gc_t * pgcparam) { P_w(&pentry->lock); LogFullDebug(COMPONENT_CACHE_INODE_GC, "Entry %p (REGULAR_FILE/SYMBOLIC_LINK) will be garbaged", pentry); /* Set the entry as invalid */ pentry->internal_md.valid_state = INVALID; LogFullDebug(COMPONENT_CACHE_INODE_GC, "****> cache_inode_gc_suppress_file on %p", pentry); /* Remove refences in the parent entries */ if(cache_inode_gc_invalidate_related_dirent(pentry, pgcparam) != LRU_LIST_SET_INVALID) { V_w(&pentry->lock); return LRU_LIST_DO_NOT_SET_INVALID; } /* Clean the entry */ if(cache_inode_gc_clean_entry(pentry, pgcparam) != LRU_LIST_SET_INVALID) return LRU_LIST_DO_NOT_SET_INVALID; /* Mutex has already been freed at destruction time */ return LRU_LIST_SET_INVALID; } /* cache_inode_gc_suppress_file */
/* Remove a child entry */ int NamespaceRemove(ino_t parent_ino, dev_t parent_dev, unsigned int gen, char *name) { int rc; /* lock the namespace for modification */ P_w(&ns_lock); rc = NamespaceRemove_nl(parent_ino, parent_dev, gen, name); V_w(&ns_lock); return rc; }
void nfs4_acl_entry_inc_ref(fsal_acl_t *pacl) { if(!pacl) return; /* Increase ref counter */ P_w(&pacl->lock); pacl->ref++; LogDebug(COMPONENT_NFS_V4_ACL, "nfs4_acl_entry_inc_ref: (acl, ref) = (%p, %u)", pacl, pacl->ref); V_w(&pacl->lock); }
/* Add a child entry */ int NamespaceAdd(ino_t parent_ino, dev_t parent_dev, unsigned int gen, char *name, ino_t entry_ino, dev_t entry_dev, unsigned int *p_new_gen) { int rc; /* lock the namespace for modification */ P_w(&ns_lock); rc = NamespaceAdd_nl(parent_ino, parent_dev, gen, name, entry_ino, entry_dev, p_new_gen); V_w(&ns_lock); return rc; }
void *thread_writter(void *arg) { int duree_sleep = 1; int nb_iter = NB_ITER; while(nb_iter > 0) { P_w(&lock); sleep(duree_sleep); V_w(&lock); nb_iter -= 1; } OkWrite = 1; /* Ecriture concurrente ici, mais on s'en fout (pas d'impact) */ return NULL; } /* thread_writter */
fsal_status_t dumb_fsal_up_invalidate(fsal_up_event_data_t * pevdata) { cache_inode_status_t cache_status; cache_entry_t *pentry = NULL; fsal_attrib_list_t attr; memset(&attr, 0, sizeof(fsal_attrib_list_t)); /* Avoid dir cont check in cache_inode_get() */ pevdata->event_context.fsal_data.cookie = DIR_START; LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DUMB: calling cache_inode_get()"); pentry = cache_inode_get(&pevdata->event_context.fsal_data, cachepol, &attr, pevdata->event_context.ht, NULL, NULL, &cache_status); if(pentry == NULL) { LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DUMB: cache inode get failed."); /* Not an error. Expecting some nodes will not have it in cache in * a cluster. */ ReturnCode(ERR_FSAL_NO_ERROR, 0); } LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DUMB: Invalidate cache found entry %p type %u", pentry, pentry->internal_md.type); /* Lock the entry */ P_w(&pentry->lock); LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DUMB: setting state to STALE"); pentry->internal_md.valid_state = STALE; if(pentry->internal_md.type == DIRECTORY) { pentry->object.dir.has_been_readdir = CACHE_INODE_RENEW_NEEDED; LogDebug(COMPONENT_FSAL_UP, "FSAL_CB_DUMB: Invalidate reset directory."); } V_w(&pentry->lock); ReturnCode(ERR_FSAL_NO_ERROR, 0); }
/* set/update informations about a handle */ int fsal_posixdb_UpdateInodeCache(posixfsal_handle_t * p_handle) /* IN */ { #ifdef _ENABLE_CACHE_PATH unsigned int i; LogDebug(COMPONENT_FSAL, "UpdateInodeCache: inode_id=%llu", p_handle->info.inode); i = hash_cache_path(p_handle->id, p_handle->ts); /* in the handle in cache ? */ P_w(&cache_array[i].entry_lock); if(cache_array[i].is_set && cache_array[i].handle.id == p_handle->id && cache_array[i].handle.ts == p_handle->ts) { /* update its inode info */ cache_array[i].handle.info = p_handle->info; cache_array[i].info_is_set = TRUE; LogDebug(COMPONENT_FSAL, "fsal_posixdb_UpdateInodeCache: %u, %u (existing entry)", (unsigned int)(p_handle->id), (unsigned int)(p_handle->ts)); V_w(&cache_array[i].entry_lock); return TRUE; } LogDebug(COMPONENT_FSAL, "fsal_posixdb_UpdateInodeCache: %u, %u (new entry)", (unsigned int)(p_handle->id), (unsigned int)(p_handle->ts)); /* add it (replace previous handle) */ cache_array[i].is_set = TRUE; cache_array[i].path_is_set = FALSE; cache_array[i].info_is_set = TRUE; cache_array[i].handle = *p_handle; memset(&cache_array[i].path, 0, sizeof(fsal_path_t)); V_w(&cache_array[i].entry_lock); #endif return FALSE; }
void fsal_posixdb_InvalidateCache() { #ifdef _ENABLE_CACHE_PATH unsigned int i; LogDebug(COMPONENT_FSAL, "fsal_posixdb_InvalidateCache"); for(i = 0; i < CACHE_PATH_SIZE; i++) { P_w(&cache_array[i].entry_lock); cache_array[i].is_set = FALSE; cache_array[i].path_is_set = FALSE; cache_array[i].info_is_set = FALSE; cache_array[i].handle.id = 0; cache_array[i].handle.ts = 0; V_w(&cache_array[i].entry_lock); } #endif }
void fsal_posixdb_CachePath(posixfsal_handle_t * p_handle, /* IN */ fsal_path_t * p_path /* IN */ ) { #ifdef _ENABLE_CACHE_PATH unsigned int i; LogDebug(COMPONENT_FSAL, "fsal_posixdb_CachePath: %u, %u = %s", (unsigned int)(p_handle->id), (unsigned int)(p_handle->ts), p_path->path); i = hash_cache_path(p_handle->id, p_handle->ts); /* in the handle already in cache ? */ P_w(&cache_array[i].entry_lock); if(cache_array[i].is_set && cache_array[i].handle.id == p_handle->id && cache_array[i].handle.ts == p_handle->ts) { cache_array[i].path_is_set = TRUE; /* override it */ cache_array[i].path = *p_path; V_w(&cache_array[i].entry_lock); return; } /* add it (replace previous handle) */ cache_array[i].is_set = TRUE; cache_array[i].path_is_set = TRUE; cache_array[i].info_is_set = FALSE; cache_array[i].handle = *p_handle; cache_array[i].path = *p_path; V_w(&cache_array[i].entry_lock); #endif return; }
cache_inode_status_t cache_inode_readlink(cache_entry_t * pentry, fsal_path_t * plink_content, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_status_t fsal_status; fsal_attrib_list_t attr ; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ (pclient->stat.nb_call_total)++; (pclient->stat.func_stats.nb_call[CACHE_INODE_READLINK])++; /* Lock the entry */ P_w(&pentry->lock); if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK])++; V_w(&pentry->lock); return *pstatus; } /* RW_Lock obtained as writer turns to reader */ rw_lock_downgrade(&pentry->lock); switch (pentry->internal_md.type) { case REGULAR_FILE: case DIRECTORY: case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: case UNASSIGNED: case FS_JUNCTION: case RECYCLED: *pstatus = CACHE_INODE_BAD_TYPE; V_r(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1; return *pstatus; break; case SYMBOLIC_LINK: assert(pentry->object.symlink); if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) ) { fsal_status = FSAL_pathcpy(plink_content, &(pentry->object.symlink->content)); /* need copy ctor? */ } else { /* Content is not cached, call FSAL_readlink here */ fsal_status = FSAL_readlink( &pentry->handle, pcontext, plink_content, &attr ) ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readlink: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry, 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_readlink: 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_READLINK] += 1; return *pstatus; } break; } /* Release the entry */ *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_READLINK] += 1; return *pstatus; } /* cache_inode_readlink */
void nfs4_acl_release_entry(fsal_acl_t *pacl, fsal_acl_status_t *pstatus) { fsal_acl_data_t acldata; hash_buffer_t key, old_key; hash_buffer_t old_value; int rc; /* Set the return default to NFS_V4_ACL_SUCCESS */ *pstatus = NFS_V4_ACL_SUCCESS; P_w(&pacl->lock); nfs4_acl_entry_dec_ref(pacl); if(pacl->ref) { V_w(&pacl->lock); return; } else LogDebug(COMPONENT_NFS_V4_ACL, "nfs4_acl_release_entry: free acl %p", pacl); /* Turn the input to a hash key */ acldata.naces = pacl->naces; acldata.aces = pacl->aces; if(nfs4_acldata_2_key(&key, &acldata)) { *pstatus = NFS_V4_ACL_UNAPPROPRIATED_KEY; nfs4_release_acldata_key(&key); V_w(&pacl->lock); return; } /* use the key to delete the entry */ if((rc = HashTable_Del(fsal_acl_hash, &key, &old_key, &old_value)) != HASHTABLE_SUCCESS) { LogCrit(COMPONENT_NFS_V4_ACL, "nfs4_acl_release_entry: entry could not be deleted, status = %d", rc); nfs4_release_acldata_key(&key); *pstatus = NFS_V4_ACL_NOT_FOUND; V_w(&pacl->lock); return; } /* Release the hash key data */ nfs4_release_acldata_key(&old_key); /* Sanity check: old_value.pdata is expected to be equal to pacl, * and is released later in this function */ if((fsal_acl_t *) old_value.pdata != pacl) { LogCrit(COMPONENT_NFS_V4_ACL, "nfs4_acl_release_entry: unexpected pdata %p from hash table (pacl=%p)", old_value.pdata, pacl); } /* Release the current key */ nfs4_release_acldata_key(&key); V_w(&pacl->lock); /* Release acl */ nfs4_acl_free(pacl); }
/** * * cache_inode_create: creates an entry through the cache. * * Creates an entry through the cache. * * @param pentry_parent [IN] pointer to the pentry parent * @param pname [IN] pointer to the name of the object in the destination directory. * @param type [IN] type of the object to be created. * @param mode [IN] mode to be used at file creation * @param pcreate_arg [IN] additional argument for object creation * @param pattr [OUT] attributes for the new object. * @param ht [INOUT] hash table used for the cache. * @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_LRU_ERROR if allocation error occured when validating the entry\n * @return CACHE_INODE_BAD_TYPE either source or destination have incorrect type\n * @return CACHE_INODE_ENTRY_EXISTS entry of that name already exists in destination. * */ cache_entry_t * cache_inode_create(cache_entry_t * pentry_parent, fsal_name_t * pname, cache_inode_file_type_t type, fsal_accessmode_t mode, cache_inode_create_arg_t * pcreate_arg, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { cache_entry_t *pentry = NULL; fsal_status_t fsal_status; #ifdef _USE_MFSL mfsl_object_t object_handle; #else fsal_handle_t object_handle; #endif fsal_attrib_list_t parent_attributes; fsal_attrib_list_t object_attributes; fsal_handle_t dir_handle; cache_inode_fsal_data_t fsal_data; cache_inode_status_t status; struct cache_inode_dir_begin__ *dir_begin; int pnfs_status; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; inc_func_call(pclient, CACHE_INODE_CREATE); /* * Check if the required type is correct, with this * function, we manage file, dir and symlink */ if(type != REGULAR_FILE && type != DIR_BEGINNING && type != SYMBOLIC_LINK && type != SOCKET_FILE && type != FIFO_FILE && type != CHARACTER_FILE && type != BLOCK_FILE) { *pstatus = CACHE_INODE_BAD_TYPE; /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; } /* * Check if caller is allowed to perform the operation */ status = cache_inode_access(pentry_parent, FSAL_W_OK, ht, pclient, pcontext, &status); if (status != CACHE_INODE_SUCCESS) { *pstatus = status; /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); /* pentry is a directory */ return NULL; } /* * Check if an entry of the same name exists */ pentry = cache_inode_lookup(pentry_parent, pname, &object_attributes, ht, pclient, pcontext, pstatus); if (pentry != NULL) { *pstatus = CACHE_INODE_ENTRY_EXISTS; if(pentry->internal_md.type != type) { /* * Incompatible types, returns NULL */ /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; } else { /* stats */ inc_func_success(pclient, CACHE_INODE_CREATE); /* * redondant creation, returned the * previously created entry */ return pentry; } } /* * At this point, the entry was not found, this means * that is doesn't exist is FSAL, we can create it */ /* Get the lock for the parent */ P_w(&pentry_parent->lock); if(pentry_parent->internal_md.type == DIR_BEGINNING) dir_handle = pentry_parent->object.dir_begin.handle; if(pentry_parent->internal_md.type == DIR_CONTINUE) { P_r(&pentry_parent->object.dir_cont.pdir_begin->lock); dir_handle = pentry_parent->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry_parent->object.dir_cont.pdir_begin->lock); } object_attributes.asked_attributes = pclient->attrmask; switch (type) { case REGULAR_FILE: #ifdef _USE_MFSL cache_inode_get_attributes(pentry_parent, &parent_attributes); fsal_status = MFSL_create(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, &object_handle, &object_attributes, &parent_attributes); #else fsal_status = FSAL_create(&dir_handle, pname, pcontext, mode, &object_handle, &object_attributes); #endif break; case DIR_BEGINNING: #ifdef _USE_MFSL cache_inode_get_attributes(pentry_parent, &parent_attributes); fsal_status = MFSL_mkdir(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, &object_handle, &object_attributes, &parent_attributes); #else fsal_status = FSAL_mkdir(&dir_handle, pname, pcontext, mode, &object_handle, &object_attributes); #endif break; case SYMBOLIC_LINK: #ifdef _USE_MFSL cache_inode_get_attributes(pentry_parent, &object_attributes); fsal_status = MFSL_symlink(&pentry_parent->mobject, pname, &pcreate_arg->link_content, pcontext, &pclient->mfsl_context, mode, &object_handle, &object_attributes); #else fsal_status = FSAL_symlink(&dir_handle, pname, &pcreate_arg->link_content, pcontext, mode, &object_handle, &object_attributes); #endif break; case SOCKET_FILE: #ifdef _USE_MFSL fsal_status = MFSL_mknode(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, FSAL_TYPE_SOCK, NULL, /* no dev_t needed for socket file */ &object_handle, &object_attributes); #else fsal_status = FSAL_mknode(&dir_handle, pname, pcontext, mode, FSAL_TYPE_SOCK, NULL, /* no dev_t needed for socket file */ &object_handle, &object_attributes); #endif break; case FIFO_FILE: #ifdef _USE_MFSL fsal_status = MFSL_mknode(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, FSAL_TYPE_FIFO, NULL, /* no dev_t needed for FIFO file */ &object_handle, &object_attributes); #else fsal_status = FSAL_mknode(&dir_handle, pname, pcontext, mode, FSAL_TYPE_FIFO, NULL, /* no dev_t needed for FIFO file */ &object_handle, &object_attributes); #endif break; case BLOCK_FILE: #ifdef _USE_MFSL fsal_status = MFSL_mknode(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, FSAL_TYPE_BLK, &pcreate_arg->dev_spec, &object_handle, &object_attributes); #else fsal_status = FSAL_mknode(&dir_handle, pname, pcontext, mode, FSAL_TYPE_BLK, &pcreate_arg->dev_spec, &object_handle, &object_attributes); #endif break; case CHARACTER_FILE: #ifdef _USE_MFSL fsal_status = MFSL_mknode(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, mode, FSAL_TYPE_CHR, &pcreate_arg->dev_spec, &object_handle, &object_attributes); #else fsal_status = FSAL_mknode(&dir_handle, pname, pcontext, mode, FSAL_TYPE_CHR, &pcreate_arg->dev_spec, &object_handle, &object_attributes); #endif break; default: /* we should never go there */ *pstatus = CACHE_INODE_INCONSISTENT_ENTRY; V_w(&pentry_parent->lock); /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; break; } /* Check for the result */ if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_w(&pentry_parent->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_create: Stale FSAL File Handle " "detected for pentry = %p", pentry_parent); cache_inode_kill_entry(pentry_parent, ht, pclient, &kill_status); if(kill_status != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_create: " "Could not kill entry %p, status = %u", pentry_parent, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; } else { #ifdef _USE_MFSL fsal_data.handle = object_handle.handle; #else fsal_data.handle = object_handle; #endif fsal_data.cookie = DIR_START; pentry = cache_inode_new_entry(&fsal_data, &object_attributes, type, pcreate_arg, NULL, ht, pclient, pcontext, TRUE, /* This is a creation and not a population */ pstatus); if (pentry == NULL) { *pstatus = CACHE_INODE_INSERT_ERROR; V_w(&pentry_parent->lock); /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; } #ifdef _USE_MFSL /* Copy the MFSL object to the cache */ memcpy((char *)&(pentry->mobject), (char *)&object_handle, sizeof(mfsl_object_t)); #endif /* Add this entry to the directory */ status = cache_inode_add_cached_dirent(pentry_parent, pname, pentry, NULL, ht, pclient, pcontext, pstatus); if (status != CACHE_INODE_SUCCESS) { V_w(&pentry_parent->lock); /* stats */ inc_func_err_unrecover(pclient, CACHE_INODE_CREATE); return NULL; } } #ifdef _USE_PNFS if((type == REGULAR_FILE) && (pcreate_arg != NULL) && (pcreate_arg->use_pnfs == TRUE)) { pnfs_status = pnfs_create_ds_file(&pclient->pnfsclient, pentry->object.file.attributes.fileid, &pentry->object.file.pnfs_file.ds_file); if (pnfs_status != NFS4_OK) { V_w(&pentry_parent->lock); LogDebug(COMPONENT_CACHE_INODE, "OPEN PNFS CREATE DS FILE : Error %u", pnfs_status); *pstatus = CACHE_INODE_IO_ERROR; return NULL; } } #endif /* Update the parent cached attributes */ if(pentry_parent->internal_md.type == DIR_BEGINNING) dir_begin = &pentry_parent->object.dir_begin; else dir_begin = &pentry_parent->object.dir_cont.pdir_begin->object.dir_begin; dir_begin->attributes.mtime.seconds = time(NULL); dir_begin->attributes.mtime.nseconds = 0; dir_begin->attributes.ctime = dir_begin->attributes.mtime; /* * if the created object is a directory, it contains a link * to its parent : '..'. Thus the numlink attr must be increased. */ if(type == DIR_BEGINNING) { dir_begin->attributes.numlinks++; } /* Get the attributes in return */ *pattr = object_attributes; /* valid the parent */ *pstatus = cache_inode_valid(pentry_parent, CACHE_INODE_OP_SET, pclient); /* release the lock for the parent */ V_w(&pentry_parent->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) inc_func_err_retryable(pclient, CACHE_INODE_CREATE); else inc_func_success(pclient, CACHE_INODE_CREATE); return pentry; }
/** * * cache_inode_lookup_sw: looks up for a name in a directory indicated by a * cached entry. * * Looks up for a name in a directory indicated by a cached entry. The directory * should have been cached before. * * @param pentry_parent [IN] entry for the parent directory to be managed. * @param name [IN] name of the entry that we are looking for in the * cache. * @param pattr [OUT] attributes for the entry that we have found. * @param ht [IN] hash table used for the cache, unused in this * call. * @param pclient [INOUT] ressource allocated by the client for the nfs * management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * @param use_mutex [IN] if TRUE, mutex management is done, not if equal * to FALSE. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the * entry * */ cache_entry_t *cache_inode_lookup_sw(cache_entry_t * pentry_parent, fsal_name_t * pname, cache_inode_policy_t policy, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus, int use_mutex) { cache_inode_dir_entry_t dirent_key[1], *dirent; struct avltree_node *dirent_node; cache_inode_dir_entry_t *new_dir_entry; cache_entry_t *pentry = NULL; fsal_status_t fsal_status; #ifdef _USE_MFSL mfsl_object_t object_handle; #else fsal_handle_t object_handle; #endif fsal_handle_t dir_handle; fsal_attrib_list_t object_attributes; cache_inode_create_arg_t create_arg; cache_inode_file_type_t type; cache_inode_status_t cache_status; cache_inode_fsal_data_t new_entry_fsdata; fsal_accessflags_t access_mask = 0; memset(&create_arg, 0, sizeof(create_arg)); memset( (char *)&new_entry_fsdata, 0, sizeof( new_entry_fsdata ) ) ; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ (pclient->stat.nb_call_total)++; (pclient->stat.func_stats.nb_call[CACHE_INODE_LOOKUP])++; /* We should not renew entries when !use_mutex (because unless we * make the flag explicit (shared vs. exclusive), we don't know * whether a mutating operation is safe--and, the caller should have * already renewed the entry */ if(use_mutex == TRUE) { P_w(&pentry_parent->lock); cache_status = cache_inode_renew_entry(pentry_parent, pattr, ht, pclient, pcontext, pstatus); if(cache_status != CACHE_INODE_SUCCESS) { V_w(&pentry_parent->lock); inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_lookup: returning %d(%s) from cache_inode_renew_entry", *pstatus, cache_inode_err_str(*pstatus)); return NULL; } /* RW Lock goes for writer to reader */ rw_lock_downgrade(&pentry_parent->lock); } if(pentry_parent->internal_md.type != DIRECTORY) { /* Parent is no directory base, return NULL */ *pstatus = CACHE_INODE_NOT_A_DIRECTORY; /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++; if(use_mutex == TRUE) V_r(&pentry_parent->lock); return NULL; } /* if name is ".", use the input value */ if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT)) { pentry = pentry_parent; } else if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT_DOT)) { /* Directory do only have exactly one parent. This a limitation in all FS, * which implies that hard link are forbidden on directories (so that * they exists only in one dir). Because of this, the parent list is * always limited to one element for a dir. Clients SHOULD never * 'lookup( .. )' in something that is no dir. */ pentry = cache_inode_lookupp_no_mutex(pentry_parent, ht, pclient, pcontext, pstatus); } else { /* This is a "regular lookup" (not on "." or "..") */ /* Check is user (as specified by the credentials) is authorized to * lookup the directory or not */ access_mask = FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR); if(cache_inode_access_no_mutex(pentry_parent, access_mask, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { if(use_mutex == TRUE) V_r(&pentry_parent->lock); (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++; return NULL; } /* We first try avltree_lookup by name. If that fails, we dispatch to * the fsal. */ FSAL_namecpy(&dirent_key->name, pname); dirent_node = avltree_lookup(&dirent_key->node_n, &pentry_parent->object.dir.dentries); if (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); pentry = dirent->pentry; } if(pentry == NULL) { LogDebug(COMPONENT_CACHE_INODE, "Cache Miss detected"); dir_handle = pentry_parent->handle; object_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL #ifdef _USE_MFSL_ASYNC if(!mfsl_async_is_object_asynchronous(&pentry_parent->mobject)) { /* If the parent is asynchronous, rely on the content of the cache * inode parent entry. * * /!\ If the fs behind the FSAL is touched in a non-nfs way, * there will be huge incoherencies. */ #endif /* _USE_MFSL_ASYNC */ fsal_status = MFSL_lookup(&pentry_parent->mobject, pname, pcontext, &pclient->mfsl_context, &object_handle, &object_attributes, NULL); #ifdef _USE_MFSL_ASYNC } else { LogMidDebug(COMPONENT_CACHE_INODE, "cache_inode_lookup chose to bypass FSAL and trusted his cache for name=%s", pname->name); fsal_status.major = ERR_FSAL_NOENT; fsal_status.minor = ENOENT; } #endif /* _USE_MFSL_ASYNC */ #else fsal_status = FSAL_lookup(&dir_handle, pname, pcontext, &object_handle, &object_attributes); #endif /* _USE_MFSL */ if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(use_mutex == TRUE) V_r(&pentry_parent->lock); /* Stale File Handle to be detected and managed */ if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_parent, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_pentry_parent: Could not kill entry %p, status = %u", pentry_parent, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++; return NULL; } type = cache_inode_fsal_type_convert(object_attributes.type); /* If entry is a symlink, this value for be cached */ if(type == SYMBOLIC_LINK) { if( CACHE_INODE_KEEP_CONTENT( policy ) ) #ifdef _USE_MFSL { fsal_status = MFSL_readlink(&object_handle, pcontext, &pclient->mfsl_context, &create_arg.link_content, &object_attributes, NULL); } #else { fsal_status = FSAL_readlink(&object_handle, pcontext, &create_arg.link_content, &object_attributes); } else { fsal_status.major = ERR_FSAL_NO_ERROR ; fsal_status.minor = 0 ; } #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(use_mutex == TRUE) V_r(&pentry_parent->lock); /* Stale File Handle to be detected and managed */ if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_parent, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_pentry_parent: Could not kill entry %p, status = %u", pentry_parent, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++; return NULL; } } /* Allocation of a new entry in the cache */ #ifdef _USE_MFSL new_entry_fsdata.handle = object_handle.handle; #else new_entry_fsdata.handle = object_handle; #endif new_entry_fsdata.cookie = 0; if((pentry = cache_inode_new_entry( &new_entry_fsdata, &object_attributes, type, policy, &create_arg, NULL, ht, pclient, pcontext, FALSE, /* This is a population and not a creation */ pstatus ) ) == NULL ) { if(use_mutex == TRUE) V_r(&pentry_parent->lock); /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++; return NULL; } /* Entry was found in the FSAL, add this entry to the parent * directory */ cache_status = cache_inode_add_cached_dirent(pentry_parent, pname, pentry, ht, &new_dir_entry, pclient, pcontext, pstatus); if(cache_status != CACHE_INODE_SUCCESS && cache_status != CACHE_INODE_ENTRY_EXISTS) { if(use_mutex == TRUE) V_r(&pentry_parent->lock); /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++; return NULL; } } /* cached lookup fail (try fsal) */
/** * * HashTable_Test_And_Set: set a pair (key,value) into the Hash Table. * * Set a (key,val) couple in the hashtable . * * @param ht the hashtable to be used. * @param buffkey a pointeur to an object of type hash_buffer_t which describe the key location in memory. * @param buffval a pointeur to an object of type hash_buffer_t which describe the value location in memory. * @param how a switch to tell if the entry is to be tested or overwritten or not * * @return HASHTABLE_SUCCESS if successfull\n. * @return HASHTABLE_INSERT_MALLOC_ERROR if an error occured during the insertion process. * * @see HashTable_Get * @see HashTable_Init * @see HashTable_Del */ int HashTable_Test_And_Set(hash_table_t * ht, hash_buffer_t * buffkey, hash_buffer_t * buffval, hashtable_set_how_t how) { unsigned int hashval = 0; int rbt_value = 0; struct rbt_head *tete_rbt = NULL; struct rbt_node *qn = NULL; struct rbt_node *pn = NULL; hash_data_t *pdata = NULL; /* Sanity check */ if(ht == NULL) return HASHTABLE_ERROR_INVALID_ARGUMENT; else if(buffkey == NULL) return HASHTABLE_ERROR_INVALID_ARGUMENT; else if(buffval == NULL) return HASHTABLE_ERROR_INVALID_ARGUMENT; /* Find the RB Tree to be used */ hashval = (*(ht->parameter.hash_func_key)) (&ht->parameter, buffkey); tete_rbt = &(ht->array_rbt[hashval]); rbt_value = (*(ht->parameter.hash_func_rbt)) (&ht->parameter, buffkey); LogFullDebug(COMPONENT_HASHTABLE,"Key = %p Value = %p hashval = %u rbt_value = %x\n", buffkey->pdata, buffval->pdata, hashval, rbt_value); /* acquire mutex for protection */ P_w(&(ht->array_lock[hashval])); if(Key_Locate(ht, buffkey, hashval, rbt_value, &pn) == HASHTABLE_SUCCESS) { /* An entry of that key already exists */ if(how == HASHTABLE_SET_HOW_TEST_ONLY) { ht->stat_dynamic[hashval].ok.nb_test += 1; V_w(&(ht->array_lock[hashval])); return HASHTABLE_SUCCESS; } if(how == HASHTABLE_SET_HOW_SET_NO_OVERWRITE) { ht->stat_dynamic[hashval].err.nb_test += 1; V_w(&(ht->array_lock[hashval])); return HASHTABLE_ERROR_KEY_ALREADY_EXISTS; } qn = pn; pdata = RBT_OPAQ(qn); LogFullDebug(COMPONENT_HASHTABLE,"Ecrasement d'une ancienne entree (k=%p,v=%p)\n", buffkey->pdata, buffval->pdata); } else { /* No entry of that key, add it to the trees */ if(how == HASHTABLE_SET_HOW_TEST_ONLY) { ht->stat_dynamic[hashval].notfound.nb_test += 1; V_w(&(ht->array_lock[hashval])); return HASHTABLE_ERROR_NO_SUCH_KEY; } /* Insert a new node in the table */ RBT_FIND(tete_rbt, pn, rbt_value); #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("rbt_node_t"); #endif /* This entry does not exist, create it */ /* First get a new entry in the preallocated node array */ GET_PREALLOC(qn, ht->node_prealloc[hashval], ht->parameter.nb_node_prealloc, rbt_node_t, next); if(qn == NULL) { ht->stat_dynamic[hashval].err.nb_set += 1; V_w(&(ht->array_lock[hashval])); return HASHTABLE_INSERT_MALLOC_ERROR; } #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("hash_data_t"); #endif GET_PREALLOC(pdata, ht->pdata_prealloc[hashval], ht->parameter.nb_node_prealloc, hash_data_t, next_alloc); if(pdata == NULL) { ht->stat_dynamic[hashval].err.nb_set += 1; V_w(&(ht->array_lock[hashval])); return HASHTABLE_INSERT_MALLOC_ERROR; } #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("N/A"); #endif RBT_OPAQ(qn) = pdata; RBT_VALUE(qn) = rbt_value; RBT_INSERT(tete_rbt, qn, pn); LogFullDebug(COMPONENT_HASHTABLE,"Creation d'une nouvelle entree (k=%p,v=%p), qn=%p, pdata=%p\n", buffkey->pdata, buffval->pdata, qn, RBT_OPAQ(qn)); } pdata->buffval.pdata = buffval->pdata; pdata->buffval.len = buffval->len; pdata->buffkey.pdata = buffkey->pdata; pdata->buffkey.len = buffkey->len; ht->stat_dynamic[hashval].nb_entries += 1; ht->stat_dynamic[hashval].ok.nb_set += 1; /* Release mutex */ V_w(&(ht->array_lock[hashval])); return HASHTABLE_SUCCESS; } /* HashTable_Set */
/* Move an entry in the namespace */ int NamespaceRename(ino_t parent_entry_src, dev_t src_dev, unsigned int srcgen, char *name_src, ino_t parent_entry_tgt, dev_t tgt_dev, unsigned int tgtgen, char *name_tgt) { int rc = 0; fsnode_t *p_node; unsigned int new_gen; /* lock the namespace for modification */ P_w(&ns_lock); /* get source node info */ p_node = h_get_lookup(parent_entry_src, src_dev, name_src, &rc); if(!p_node || rc != 0) { V_w(&ns_lock); return rc; } /* check that source != target (if so, do nothing) */ if((parent_entry_src == parent_entry_tgt) && (src_dev == tgt_dev) && !strcmp(name_src, name_tgt)) { V_w(&ns_lock); return 0; } /* try to add new path (with the same gen number) */ new_gen = p_node->inode.generation; rc = NamespaceAdd_nl(parent_entry_tgt, tgt_dev, tgtgen, name_tgt, p_node->inode.inum, p_node->inode.dev, &new_gen); if(rc == EEXIST) { /* remove previous entry */ rc = NamespaceRemove_nl(parent_entry_tgt, tgt_dev, tgtgen, name_tgt); if(rc) { V_w(&ns_lock); return rc; } /* add the new one */ new_gen = p_node->inode.generation; rc = NamespaceAdd_nl(parent_entry_tgt, tgt_dev, tgtgen, name_tgt, p_node->inode.inum, p_node->inode.dev, &new_gen); } if(rc) { V_w(&ns_lock); return rc; } /* remove old path */ rc = NamespaceRemove_nl(parent_entry_src, src_dev, srcgen, name_src); V_w(&ns_lock); return rc; }
/** * * cache_inode_setattrs: set the attributes for an entry located in the cache by its address. * * 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 [INOUT] attributes for the entry that we have found. Out: attributes set. * @param ht [INOUT] hash table used for the cache, unused in this call. * @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_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_setattr(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ 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 *p_object_attributes = NULL; fsal_attrib_list_t result_attributes; fsal_attrib_list_t truncate_attributes; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stat */ pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_SETATTR] += 1; /* Lock the entry */ P_w(&pentry->lock); 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 FS_JUNCTION: case DIRECTORY: pfsal_handle = &pentry->object.dir.handle; break; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: pfsal_handle = &pentry->object.special_obj.handle; break; 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 set the attributes */ /* result_attributes.asked_attributes = pattr->asked_attributes ; */ /* mod Th.Leibovici on 2006/02/13 * We ask back all standard attributes, in case they have been modified * by another program (pftp, rcpd...) */ memset(&result_attributes, 0, sizeof(fsal_attrib_list_t)); result_attributes.asked_attributes = pclient->attrmask; /* end of mod */ #ifdef _USE_MFSL fsal_status = MFSL_setattrs(&pentry->mobject, pcontext, &pclient->mfsl_context, pattr, &result_attributes, NULL); #else fsal_status = FSAL_setattrs(pfsal_handle, pcontext, pattr, &result_attributes); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_w(&pentry->lock); /* stat */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_SETATTR] += 1; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_setattr: Stale FSAL File Handle detected for pentry = %p", pentry); if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_setattr: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } if(pattr->asked_attributes & FSAL_ATTR_SIZE) { truncate_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_truncate(pfsal_handle, pcontext, pattr->filesize, NULL, &truncate_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_w(&pentry->lock); /* stat */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_SETATTR] += 1; if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_setattr: Stale FSAL File Handle detected for pentry = %p", pentry); if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_setattr: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } } /* Keep the new attribute in cache */ switch (pentry->internal_md.type) { case REGULAR_FILE: p_object_attributes = &(pentry->object.file.attributes); break; case SYMBOLIC_LINK: assert(pentry->object.symlink); p_object_attributes = &(pentry->object.symlink->attributes); break; case FS_JUNCTION: case DIRECTORY: p_object_attributes = &(pentry->object.dir.attributes); break; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: p_object_attributes = &(pentry->object.special_obj.attributes); break; 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; } /* Update the cached attributes */ if((result_attributes.asked_attributes & FSAL_ATTR_SIZE) || (result_attributes.asked_attributes & FSAL_ATTR_SPACEUSED)) { if(pentry->internal_md.type == REGULAR_FILE) { if(pentry->object.file.pentry_content == NULL) { /* Operation on a non data cached file */ p_object_attributes->filesize = result_attributes.filesize; p_object_attributes->spaceused = result_attributes.filesize; /* Unclear hook here. BUGAZOMEU */ } else { /* Data cached file */ /* Do not set the p_object_attributes->filesize and p_object_attributes->spaceused in this case * This will lead to a situation where (for example) untar-ing a file will produced invalid files * with a size of 0 despite the fact that they are not empty */ LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_setattr with FSAL_ATTR_SIZE on data cached entry"); } } else if(pattr->asked_attributes & FSAL_ATTR_SIZE) LogCrit(COMPONENT_CACHE_INODE, "WARNING !!! cache_inode_setattr tried to set size on a non REGULAR_FILE type=%d", pentry->internal_md.type); } if(result_attributes.asked_attributes & (FSAL_ATTR_MODE | FSAL_ATTR_OWNER | FSAL_ATTR_GROUP)) { if(result_attributes.asked_attributes & FSAL_ATTR_MODE) p_object_attributes->mode = result_attributes.mode; if(result_attributes.asked_attributes & FSAL_ATTR_OWNER) p_object_attributes->owner = result_attributes.owner; if(result_attributes.asked_attributes & FSAL_ATTR_GROUP) p_object_attributes->group = result_attributes.group; } if(result_attributes.asked_attributes & (FSAL_ATTR_ATIME | FSAL_ATTR_CTIME | FSAL_ATTR_MTIME)) { if(result_attributes.asked_attributes & FSAL_ATTR_ATIME) p_object_attributes->atime = result_attributes.atime; if(result_attributes.asked_attributes & FSAL_ATTR_CTIME) p_object_attributes->ctime = result_attributes.ctime; if(result_attributes.asked_attributes & FSAL_ATTR_MTIME) p_object_attributes->mtime = result_attributes.mtime; } #ifdef _USE_NFS4_ACL if(result_attributes.asked_attributes & FSAL_ATTR_ACL) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_setattr: old acl = %p, new acl = %p", p_object_attributes->acl, result_attributes.acl); /* Release previous acl entry. */ if(p_object_attributes->acl) { fsal_acl_status_t status; nfs4_acl_release_entry(p_object_attributes->acl, &status); if(status != NFS_V4_ACL_SUCCESS) LogEvent(COMPONENT_CACHE_INODE, "cache_inode_setattr: Failed to release old acl:" " status = %d", status); } /* Update with new acl entry. */ p_object_attributes->acl = result_attributes.acl; } #endif /* _USE_NFS4_ACL */ /* Return the attributes as set */ *pattr = *p_object_attributes; /* validate the entry */ *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_SET, pclient); /* Release the entry */ V_w(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_SETATTR] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_SETATTR] += 1; return *pstatus; } /* cache_inode_setattr */
/** * * cache_inode_remove_sw: removes a pentry addressed by its parent pentry and * its FSAL name. Mutex management is switched. * * Removes a pentry addressed by its parent pentry and its FSAL name. Mutex * management is switched. * * @param pentry [IN] entry for the parent directory to be managed. * @param name [IN] name of the entry that we are looking for in the cache. * @param pattr [OUT] attributes for the entry that we have found. * @param ht [IN] hash table used for the cache, unused in this call. * @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_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_remove_sw(cache_entry_t * pentry, /**< Parent entry */ fsal_name_t * pnode_name, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus, int use_mutex) { fsal_status_t fsal_status; cache_entry_t *to_remove_entry; fsal_handle_t fsal_handle_parent; fsal_attrib_list_t remove_attr; fsal_attrib_list_t after_attr; cache_inode_status_t status; cache_content_status_t cache_content_status; int to_remove_numlinks = 0; fsal_accessflags_t access_mask = 0; /* stats */ (pclient->stat.nb_call_total)++; (pclient->stat.func_stats.nb_call[CACHE_INODE_REMOVE])++; /* pentry is a directory */ if(use_mutex) P_w(&pentry->lock); /* Check if caller is allowed to perform the operation */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_DELETE_CHILD); if((status = cache_inode_access_sw(pentry, access_mask, ht, pclient, pcontext, &status, FALSE)) != CACHE_INODE_SUCCESS) { *pstatus = status; /* pentry is a directory */ if(use_mutex) V_w(&pentry->lock); return *pstatus; } /* Looks up for the entry to remove */ if((to_remove_entry = cache_inode_lookup_sw( pentry, pnode_name, CACHE_INODE_JOKER_POLICY, &remove_attr, ht, pclient, pcontext, &status, FALSE)) == NULL) { *pstatus = status; /* pentry is a directory */ if(use_mutex) V_w(&pentry->lock); return *pstatus; } /* lock it */ if(use_mutex) P_w(&to_remove_entry->lock); if(pentry->internal_md.type != DIRECTORY) { if(use_mutex) { V_w(&to_remove_entry->lock); V_w(&pentry->lock); } *pstatus = CACHE_INODE_BAD_TYPE; return *pstatus; } LogDebug(COMPONENT_CACHE_INODE, "---> Cache_inode_remove : %s", pnode_name->name); /* Non-empty directories should not be removed. */ if(to_remove_entry->internal_md.type == DIRECTORY && to_remove_entry->object.dir.has_been_readdir == CACHE_INODE_YES) { if(cache_inode_is_dir_empty(to_remove_entry) != CACHE_INODE_SUCCESS) { if(use_mutex) { V_w(&to_remove_entry->lock); V_w(&pentry->lock); } *pstatus = CACHE_INODE_DIR_NOT_EMPTY; return *pstatus; } } /* pentry->internal_md.type == DIRECTORY */ fsal_handle_parent = pentry->object.dir.handle; if(status == CACHE_INODE_SUCCESS) { /* Remove the file from FSAL */ after_attr.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL cache_inode_get_attributes(pentry, &after_attr); #ifdef _USE_PNFS after_attr.numlinks = remove_attr.numlinks ; /* Hook used to pass nlinks to MFSL_unlink */ if( to_remove_entry->internal_md.type == REGULAR_FILE ) fsal_status = MFSL_unlink(&pentry->mobject, pnode_name, &to_remove_entry->mobject, pcontext, &pclient->mfsl_context, &after_attr, &to_remove_entry->object.file.pnfs_file ); else #endif /* _USE_PNFS */ fsal_status = MFSL_unlink(&pentry->mobject, pnode_name, &to_remove_entry->mobject, pcontext, &pclient->mfsl_context, &after_attr, NULL); #else fsal_status = FSAL_unlink(&fsal_handle_parent, pnode_name, pcontext, &after_attr); #endif /* Set the 'after' attr */ if(pattr != NULL) *pattr = after_attr; if(FSAL_IS_ERROR(fsal_status)) { if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogDebug(COMPONENT_CACHE_INODE, "cache_inode_remove: Stale FSAL FH detected for pentry %p", pentry); if(cache_inode_kill_entry(pentry, WT_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_remove: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } *pstatus = cache_inode_error_convert(fsal_status); if(use_mutex) { V_w(&to_remove_entry->lock); V_w(&pentry->lock); } return *pstatus; } } /* CACHE_INODE_SUCCESS */ else { if(use_mutex) { V_w(&to_remove_entry->lock); V_w(&pentry->lock); } (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_REMOVE])++; return status; } /* Remove the entry from parent dir_entries avl */ cache_inode_remove_cached_dirent(pentry, pnode_name, ht, pclient, &status); LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_remove_cached_dirent: status=%d", status); /* Update the cached attributes */ pentry->object.dir.attributes = after_attr; /* Update the attributes for the removed entry */ if(remove_attr.type != FSAL_TYPE_DIR) { if(remove_attr.numlinks > 1) { switch (to_remove_entry->internal_md.type) { case SYMBOLIC_LINK: assert(to_remove_entry->object.symlink); to_remove_entry->object.symlink->attributes.numlinks -= 1; cache_inode_set_time_current( &to_remove_entry->object.symlink->attributes.ctime ) ; to_remove_numlinks = to_remove_entry->object.symlink->attributes.numlinks; break; case REGULAR_FILE: to_remove_entry->object.file.attributes.numlinks -= 1; cache_inode_set_time_current( &to_remove_entry->object.file.attributes.ctime ) ; to_remove_numlinks = to_remove_entry->object.file.attributes.numlinks; break; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: to_remove_entry->object.special_obj.attributes.numlinks -= 1; cache_inode_set_time_current( &to_remove_entry->object.special_obj.attributes.ctime ) ; to_remove_numlinks = to_remove_entry->object.special_obj.attributes.numlinks; break; default: /* Other objects should not be hard linked */ if(use_mutex) { V_w(&to_remove_entry->lock); V_w(&pentry->lock); } *pstatus = CACHE_INODE_BAD_TYPE; return *pstatus; break; } } } else { /* No hardlink counter to be decremented for a directory: hardlink are not allowed for them */ } /* Now, delete "to_remove_entry" from the cache inode and free its associated resources, but only if * numlinks == 0 */ if(to_remove_numlinks == 0) { /* If pentry is a regular file, data cached, the related data cache entry should be removed as well */ if(to_remove_entry->internal_md.type == REGULAR_FILE) { if(to_remove_entry->object.file.pentry_content != NULL) { /* Something is to be deleted, release the cache data entry */ if(cache_content_release_entry ((cache_content_entry_t *) to_remove_entry->object.file.pentry_content, (cache_content_client_t *) pclient->pcontent_client, &cache_content_status) != CACHE_CONTENT_SUCCESS) { LogEvent(COMPONENT_CACHE_INODE, "pentry %p, named %s could not be released from data cache, status=%d", to_remove_entry, pnode_name->name, cache_content_status); } } } if((*pstatus = cache_inode_clean_internal(to_remove_entry, ht, pclient)) != CACHE_INODE_SUCCESS) { if(use_mutex) { V_w(&pentry->lock); V_w(&to_remove_entry->lock); } LogCrit(COMPONENT_CACHE_INODE, "cache_inode_clean_internal ERROR %d", *pstatus); return *pstatus; } /* Finally put the main pentry back to pool */ if(use_mutex) V_w(&to_remove_entry->lock); /* Destroy the mutex associated with the pentry */ cache_inode_mutex_destroy(to_remove_entry); ReleaseToPool(to_remove_entry, &pclient->pool_entry); } /* to_remove->numlinks == 0 */ /* Validate the entries */ *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_SET, pclient); /* Regular exit */ if(use_mutex) { if(to_remove_numlinks != 0) V_w(&to_remove_entry->lock); /* This was not release yet, it should be done here */ V_w(&pentry->lock); } if(status == CACHE_INODE_SUCCESS) (pclient->stat.func_stats.nb_success[CACHE_INODE_REMOVE])++; else (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_REMOVE])++; return status; } /* cache_inode_remove */
/** * * 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_link: hardlinks a pentry to another. * * Hard links a pentry to another. This is basically a equivalent of FSAL_link in the cache inode layer. * * @param pentry_src [IN] entry pointer the entry to be linked. This can't be a directory. * @param pentry_dir_dest [INOUT] entry pointer for the destination directory in which the link will be created. * @param plink_name [IN] pointer to the name of the object in the destination directory. * @param pattr [OUT] attributes for the linked attributes after the operation. * @param ht [INOUT] hash table used for the cache. * @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_LRU_ERROR if allocation error occured when validating the entry\n * @return CACHE_INODE_BAD_TYPE either source or destination have incorrect type\n * @return CACHE_INODE_ENTRY_EXISTS entry of that name already exists in destination. * */ cache_inode_status_t cache_inode_link(cache_entry_t * pentry_src, cache_entry_t * pentry_dir_dest, fsal_name_t * plink_name, 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_status_t fsal_status; fsal_handle_t handle_src; fsal_handle_t handle_dest; fsal_attrib_list_t link_attributes; #ifdef _USE_MFSL fsal_attrib_list_t dirdest_attributes; #endif cache_inode_status_t status; cache_entry_t *pentry_lookup = NULL; fsal_attrib_list_t lookup_attributes; fsal_size_t save_size = 0; fsal_size_t save_spaceused = 0; fsal_time_t save_mtime = { .seconds = 0, .nseconds = 0 }; fsal_accessflags_t access_mask = 0; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_LINK] += 1; /* Is the destination a directory ? */ if(pentry_dir_dest->internal_md.type != DIR_BEGINNING && pentry_dir_dest->internal_md.type != DIR_CONTINUE) { /* Bad type .... */ *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* Check if caller is allowed to perform the operation */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); if((status = cache_inode_access(pentry_dir_dest, access_mask, ht, pclient, pcontext, &status)) != CACHE_INODE_SUCCESS) { *pstatus = status; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; /* pentry is a directory */ return *pstatus; } /* Check if an entry of the same name doesn't exist in the destination directory */ if((pentry_lookup = cache_inode_lookup(pentry_dir_dest, plink_name, &lookup_attributes, ht, pclient, pcontext, pstatus)) != NULL) { /* There exists such an entry... */ *pstatus = CACHE_INODE_ENTRY_EXISTS; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* The pentry to be hardlinked can't be a DIR_BEGINNING or a DIR_CONTINUE */ if(pentry_src->internal_md.type == DIR_BEGINNING || pentry_src->internal_md.type == DIR_CONTINUE) { /* Bad type .... */ *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } #if 0 if( pentry_src->internal_md.type == REGULAR_FILE ) printf( "=== link === %p | inode=%llu\n", pentry_src, pentry_src->object.file.attributes.fileid ) ; #endif /* At this point, we know that the entry does not exist in destination directory, we know that the * destination is actually a directory and that the source is no directory */ /* Lock the source */ P_w(&pentry_src->lock); /* Lock the target dir */ P_w(&pentry_dir_dest->lock); /* Get the handles */ switch (pentry_src->internal_md.type) { case REGULAR_FILE: handle_src = pentry_src->object.file.handle; break; case SYMBOLIC_LINK: handle_src = pentry_src->object.symlink.handle; break; case FS_JUNCTION: case DIR_BEGINNING: handle_src = pentry_src->object.dir_begin.handle; break; case DIR_CONTINUE: /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont) * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */ P_r(&pentry_src->object.dir_cont.pdir_begin->lock); handle_src = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry_src->object.dir_cont.pdir_begin->lock); break; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: handle_src = pentry_src->object.special_obj.handle; break; case UNASSIGNED: case RECYCLED: LogCrit(COMPONENT_CACHE_INODE, "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s", pentry_src->internal_md.type, __LINE__, __FILE__); *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } switch (pentry_dir_dest->internal_md.type) { case FS_JUNCTION: case DIR_BEGINNING: handle_dest = pentry_dir_dest->object.dir_begin.handle; break; case DIR_CONTINUE: /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont) * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */ P_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock); handle_dest = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock); break; default: LogCrit(COMPONENT_CACHE_INODE, "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s", pentry_src->internal_md.type, __LINE__, __FILE__); *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* If object is a data cached regular file, keeps it mtime and size, STEP 1 */ if((pentry_src->internal_md.type == REGULAR_FILE) && (pentry_src->object.file.pentry_content != NULL)) { save_mtime = pentry_src->object.file.attributes.mtime; save_size = pentry_src->object.file.attributes.filesize; save_spaceused = pentry_src->object.file.attributes.spaceused; } /* Do the link at FSAL level */ link_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL cache_inode_get_attributes(pentry_src, &link_attributes); cache_inode_get_attributes(pentry_dir_dest, &dirdest_attributes); fsal_status = MFSL_link(&pentry_src->mobject, &pentry_dir_dest->mobject, plink_name, pcontext, &pclient->mfsl_context, &link_attributes, NULL); #else fsal_status = FSAL_link(&handle_src, &handle_dest, plink_name, pcontext, &link_attributes); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_w(&pentry_dir_dest->lock); V_w(&pentry_src->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; fsal_status_t getattr_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for at least one in pentry = %p and pentry = %p", pentry_src, pentry_dir_dest); /* Use FSAL_getattrs to find which entry is staled */ getattr_status = FSAL_getattrs(&handle_src, pcontext, &link_attributes); if(getattr_status.major == ERR_FSAL_ACCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for pentry = %p", pentry_src); if(cache_inode_kill_entry(pentry_src, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_link: Could not kill entry %p, status = %u", pentry_src, kill_status); } getattr_status = FSAL_getattrs(&handle_dest, pcontext, &link_attributes); if(getattr_status.major == ERR_FSAL_ACCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for pentry = %p", pentry_dir_dest); if(cache_inode_kill_entry(pentry_dir_dest, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_link: Could not kill entry %p, status = %u", pentry_dir_dest, kill_status); } } *pstatus = CACHE_INODE_FSAL_ESTALE; return *pstatus; } /* If object is a data cached regular file, keeps it mtime and size, STEP 2 */ if((pentry_src->internal_md.type == REGULAR_FILE) && (pentry_src->object.file.pentry_content != NULL)) { link_attributes.mtime = save_mtime; link_attributes.filesize = save_size; link_attributes.spaceused = save_spaceused; } /* Update cached attributes */ cache_inode_set_attributes(pentry_src, &link_attributes); /* Add the new entry in the destination directory */ if(cache_inode_add_cached_dirent(pentry_dir_dest, plink_name, pentry_src, NULL, ht, pclient, pcontext, &status) != CACHE_INODE_SUCCESS) { V_w(&pentry_dir_dest->lock); V_w(&pentry_src->lock); return *pstatus; } /* Regular exit */ /* return the attributes */ *pattr = link_attributes; /* Validate the entries */ *pstatus = cache_inode_valid(pentry_src, CACHE_INODE_OP_SET, pclient); /* Release the target dir */ V_w(&pentry_dir_dest->lock); /* Release the source */ V_w(&pentry_src->lock); /* stats */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_LINK] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_LINK] += 1; return *pstatus; }
void *reaper_thread(void *unused) { hash_table_t *ht = ht_client_id; struct rbt_head *head_rbt; hash_data_t *pdata = NULL; int i, v4; struct rbt_node *pn; nfs_client_id_t *clientp; #ifndef _NO_BUDDY_SYSTEM if((i = BuddyInit(&nfs_param.buddy_param_admin)) != BUDDY_SUCCESS) { /* Failed init */ LogFatal(COMPONENT_MAIN, "Memory manager could not be initialized"); } LogInfo(COMPONENT_MAIN, "Memory manager successfully initialized"); #endif SetNameFunction("reaper_thr"); while(1) { /* Initial wait */ /* TODO: should this be configurable? */ /* sleep(nfs_param.core_param.reaper_delay); */ sleep(reaper_delay); LogFullDebug(COMPONENT_MAIN, "NFS reaper : now checking clients"); /* For each bucket of the hashtable */ for(i = 0; i < ht->parameter.index_size; i++) { head_rbt = &(ht->array_rbt[i]); restart: /* acquire mutex */ P_w(&(ht->array_lock[i])); /* go through all entries in the red-black-tree*/ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); clientp = (nfs_client_id_t *)pdata->buffval.pdata; /* * little hack: only want to reap v4 clients * 4.1 initializess this field to '1' */ v4 = (clientp->create_session_sequence == 0); if (clientp->confirmed != EXPIRED_CLIENT_ID && nfs4_is_lease_expired(clientp) && v4) { V_w(&(ht->array_lock[i])); LogDebug(COMPONENT_MAIN, "NFS reaper: expire client %s", clientp->client_name); nfs_client_id_expire(clientp); goto restart; } if (clientp->confirmed == EXPIRED_CLIENT_ID) { LogDebug(COMPONENT_MAIN, "reaper: client %s already expired", clientp->client_name); } RBT_INCREMENT(pn); } V_w(&(ht->array_lock[i])); } } /* while ( 1 ) */
/** * * cache_inode_get: Gets an entry by using its fsdata as a key and caches it if needed. * * Gets an entry by using its fsdata as a key and caches it if needed. * ASSUMPTION: DIR_CONT entries are always garbabbaged before their related DIR_BEGINNG * * @param fsdata [IN] file system data * @param pattr [OUT] pointer to the attributes for the result. * @param ht [IN] hash table used for the cache, unused in this call. * @param pclient [INOUT] ressource allocated by the client for the nfs management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * * @return the pointer to the entry is successfull, NULL otherwise. * */ cache_entry_t *cache_inode_get(cache_inode_fsal_data_t * pfsdata, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { hash_buffer_t key, value; cache_entry_t *pentry = NULL; fsal_status_t fsal_status; cache_inode_create_arg_t create_arg; cache_inode_file_type_t type; int hrc = 0; fsal_attrib_list_t fsal_attributes; cache_inode_fsal_data_t *ppoolfsdata = NULL; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_GET] += 1; /* Turn the input to a hash key */ if(cache_inode_fsaldata_2_key(&key, pfsdata, pclient)) { *pstatus = CACHE_INODE_UNAPPROPRIATED_KEY; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; ppoolfsdata = (cache_inode_fsal_data_t *) key.pdata; RELEASE_PREALLOC(ppoolfsdata, pclient->pool_key, next_alloc); return NULL; } switch (hrc = HashTable_Get(ht, &key, &value)) { case HASHTABLE_SUCCESS: /* Entry exists in the cache and was found */ pentry = (cache_entry_t *) value.pdata; /* return attributes additionally */ cache_inode_get_attributes(pentry, pattr); break; case HASHTABLE_ERROR_NO_SUCH_KEY: /* Cache miss, allocate a new entry */ /* If we ask for a dir cont (in this case pfsdata.cookie != FSAL_DIR_BEGINNING, we have * a client who performs a readdir in the middle of a directory, when the direcctories * have been garbbage. we must search for the DIR_BEGIN related to this DIR_CONT */ if(pfsdata->cookie != DIR_START) { /* added for sanity check */ LogDebug(COMPONENT_CACHE_INODE_GC, "=======> Pb cache_inode_get: line %u pfsdata->cookie != DIR_START (=%u) on object whose type is %u", __LINE__, pfsdata->cookie, cache_inode_fsal_type_convert(fsal_attributes.type)); pfsdata->cookie = DIR_START; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); /* redo the call */ return cache_inode_get(pfsdata, pattr, ht, pclient, pcontext, pstatus); } /* First, call FSAL to know what the object is */ fsal_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_getattrs(&pfsdata->handle, pcontext, &fsal_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_get: line %u cache_inode_status=%u fsal_status=%u,%u ", __LINE__, *pstatus, fsal_status.major, fsal_status.minor); if(fsal_status.major == ERR_FSAL_STALE) { char handle_str[256]; snprintHandle(handle_str, 256, &pfsdata->handle); LogEvent(COMPONENT_CACHE_INODE_GC,"cache_inode_get: Stale FSAL File Handle %s", handle_str); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* The type has to be set in the attributes */ if(!FSAL_TEST_MASK(fsal_attributes.supported_attributes, FSAL_ATTR_TYPE)) { *pstatus = CACHE_INODE_FSAL_ERROR; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* Get the cache_inode file type */ type = cache_inode_fsal_type_convert(fsal_attributes.type); if(type == SYMBOLIC_LINK) { FSAL_CLEAR_MASK(fsal_attributes.asked_attributes); FSAL_SET_MASK(fsal_attributes.asked_attributes, pclient->attrmask); fsal_status = FSAL_readlink(&pfsdata->handle, pcontext, &create_arg.link_content, &fsal_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_get: Stale FSAL File Handle detected for pentry = %p", pentry); if(cache_inode_kill_entry(pentry, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE_GC,"cache_inode_get: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return NULL; } } /* Add the entry to the cache */ if((pentry = cache_inode_new_entry(pfsdata, &fsal_attributes, type, &create_arg, NULL, /* never used to add a new DIR_CONTINUE within the scope of this function */ ht, pclient, pcontext, FALSE, /* This is a population, not a creation */ pstatus)) == NULL) { /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* Set the returned attributes */ *pattr = fsal_attributes; /* Now, exit the switch/case and returns */ break; default: /* This should not happened */ *pstatus = CACHE_INODE_INVALID_ARGUMENT; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; break; } *pstatus = CACHE_INODE_SUCCESS; /* valid the found entry, if this is not feasable, returns nothing to the client */ P_w(&pentry->lock); if((*pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient)) != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); pentry = NULL; } V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_success[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return pentry; } /* cache_inode_get */
/** * * cache_inode_gc_suppress_directory: suppress a directory entry from the cache inode. * * Suppress a file entry from the cache inode. * * @param pentry [IN] pointer to the entry to be suppressed. * * @return 1 if entry is successfully suppressed, 0 otherwise * * @see LRU_invalidate_by_function * @see LRU_gc_invalid * */ int cache_inode_gc_suppress_directory(cache_entry_t * pentry, cache_inode_param_gc_t * pgcparam) { cache_entry_t *pentry_iter = NULL; cache_entry_t *pentry_iter_save = NULL; P_w(&pentry->lock); pentry->internal_md.valid_state = INVALID; if(cache_inode_is_dir_empty(pentry) != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); LogFullDebug(COMPONENT_CACHE_INODE_GC, "Entry %p (DIR_BEGINNING) is not empty. The dir_chain will not be garbaged now", pentry); return LRU_LIST_DO_NOT_SET_INVALID; /* entry is not to be suppressed */ } /* If we reached this point, the directory contains no active entry, it should be removed from the cache */ LogFullDebug(COMPONENT_CACHE_INODE_GC, "Entry %p (DIR_BEGINNING) and its associated dir_chain will be garbaged", pentry); LogFullDebug(COMPONENT_CACHE_INODE_GC, "****> cache_inode_gc_suppress_directory on %p", pentry); /* Remove refences in the parent entries */ if(cache_inode_gc_invalidate_related_dirent(pentry, pgcparam) != LRU_LIST_SET_INVALID) { V_w(&pentry->lock); return LRU_LIST_DO_NOT_SET_INVALID; } /* Remove the whole dir_chain from the cache */ pentry_iter = pentry->object.dir_begin.pdir_cont; while(pentry_iter != NULL) { pentry_iter_save = pentry_iter->object.dir_cont.pdir_cont; if(cache_inode_gc_clean_entry(pentry_iter, pgcparam) != LRU_LIST_SET_INVALID) { V_w(&pentry->lock); return LRU_LIST_DO_NOT_SET_INVALID; } pentry_iter = pentry_iter_save; } if(cache_inode_gc_clean_entry(pentry, pgcparam) != LRU_LIST_SET_INVALID) { V_w(&pentry->lock); return LRU_LIST_DO_NOT_SET_INVALID; } /* Mutex has already been freed at destruction time */ return LRU_LIST_SET_INVALID; } /* cache_inode_gc_suppress_directory */
/** * * cache_inode_gc_invalidate_related_dirent: sets the related directory entries as invalid. * * sets the related directory entries as invalid. /!\ the parent entry is supposed to be locked. * * @param pentry [INOUT] entry to be managed * @param addparam [IN] additional parameter used for cleaning. * * @return LRU_LIST_SET_INVALID if ok, LRU_LIST_DO_NOT_SET_INVALID otherwise * */ static int cache_inode_gc_invalidate_related_dirent(cache_entry_t * pentry, cache_inode_param_gc_t * pgcparam) { cache_inode_parent_entry_t *parent_iter = NULL; /* Set the cache status as INVALID in the directory entries */ for(parent_iter = pentry->parent_list; parent_iter != NULL; parent_iter = parent_iter->next_parent) { if(parent_iter->parent == NULL) { LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_gc_invalidate_related_dirent: pentry %p has no parent, no dirent to be removed...", pentry); continue; } /* If I reached this point, then parent_iter->parent is not null and is a valid cache_inode pentry */ P_w(&parent_iter->parent->lock); /* Check for type of the parent */ if(parent_iter->parent->internal_md.type != DIR_BEGINNING && parent_iter->parent->internal_md.type != DIR_CONTINUE) { V_w(&parent_iter->parent->lock); /* Major parent incoherency: parent is no directory */ LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_gc_invalidate_related_dirent: major inconcistency. Found an entry whose parent is not a directory"); return LRU_LIST_DO_NOT_SET_INVALID; } /* Set the entry as invalid in the dirent array */ if(parent_iter->parent->internal_md.type == DIR_BEGINNING) { if(parent_iter->subdirpos > CHILDREN_ARRAY_SIZE) { V_w(&parent_iter->parent->lock); LogCrit(COMPONENT_CACHE_INODE_GC, "A known bug occured line %d file %s: pentry=%p type=%u parent_iter->subdirpos=%d, should never exceed %d, entry not removed", __LINE__, __FILE__, pentry, pentry->internal_md.type, parent_iter->subdirpos, CHILDREN_ARRAY_SIZE); return LRU_LIST_DO_NOT_SET_INVALID; } else { parent_iter->parent->object.dir_begin.pdir_data->dir_entries[parent_iter-> subdirpos]. active = INVALID; /* Garbage invalidates the effet of the readdir previously made */ parent_iter->parent->object.dir_begin.has_been_readdir = CACHE_INODE_NO; parent_iter->parent->object.dir_begin.nbactive -= 1; } } else { if(parent_iter->subdirpos > CHILDREN_ARRAY_SIZE) { V_w(&parent_iter->parent->lock); LogCrit(COMPONENT_CACHE_INODE_GC, "A known bug occured line %d file %s: pentry=%p type=%u parent_iter->subdirpos=%d, should never exceed %d, entry not removed", __LINE__, __FILE__, pentry, pentry->internal_md.type, parent_iter->subdirpos, CHILDREN_ARRAY_SIZE); return LRU_LIST_DO_NOT_SET_INVALID; } else { parent_iter->parent->object.dir_cont.pdir_data->dir_entries[parent_iter-> subdirpos]. active = INVALID; parent_iter->parent->object.dir_cont.nbactive -= 1; } } V_w(&parent_iter->parent->lock); } return LRU_LIST_SET_INVALID; } /* cache_inode_gc_invalidate_related_dirent */
cache_inode_status_t cache_inode_commit(cache_entry_t * pentry, uint64_t offset, fsal_size_t count, fsal_attrib_list_t * pfsal_attr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, uint64_t typeofcommit, cache_inode_status_t * pstatus) { cache_inode_status_t status; fsal_seek_t seek_descriptor; fsal_size_t size_io_done; fsal_boolean_t eof; cache_inode_unstable_data_t *udata; fsal_status_t fsal_status; /* Do not use this function is Data Cache is used */ if(pentry->object.file.pentry_content != NULL) { *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } /* If we aren't using the Ganesha write buffer, then we're using the filesystem * write buffer so execute a normal fsal_sync() call. */ if (typeofcommit == FSAL_UNSAFE_WRITE_TO_FS_BUFFER) { P_w(&pentry->lock); /* Can't sync a file descriptor if it's currently closed. */ if(cache_inode_open(pentry, pclient, FSAL_O_WRONLY, pcontext, pstatus) != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1; return *pstatus; } #ifdef _USE_MFSL fsal_status = MFSL_sync(&(pentry->object.file.open_fd.mfsl_fd), NULL); #else fsal_status = FSAL_sync(&(pentry->object.file.open_fd.fd)); #endif if(FSAL_IS_ERROR(fsal_status)) { LogMajor(COMPONENT_CACHE_INODE, "cache_inode_rdwr: fsal_sync() failed: fsal_status.major = %d", fsal_status.major); /* Close the fd that we just opened before the FSAL_sync(). We are already * replying with an error. No need to catch an additional error form * a close? */ cache_inode_close(pentry, pclient, &status); V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1; *pstatus = CACHE_INODE_FSAL_ERROR; return *pstatus; } *pstatus = CACHE_INODE_SUCCESS; /* Close the fd that we just opened before the FSAL_sync() */ if(cache_inode_close(pentry, pclient, pstatus) != CACHE_INODE_SUCCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_rdwr: cache_inode_close = %d", *pstatus); V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_COMMIT] += 1; return *pstatus; } V_w(&pentry->lock); return *pstatus; } /* Ok, it looks like we're using the Ganesha write buffer. This means we * will either be writing to the buffer, or writing a stable write to the * file system if the buffer is already full. */ udata = &pentry->object.file.unstable_data; if(udata->buffer == NULL) { *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } if(count == 0 || count == 0xFFFFFFFFL) { /* Count = 0 means "flush all data to permanent storage */ seek_descriptor.offset = udata->offset; seek_descriptor.whence = FSAL_SEEK_SET; status = cache_inode_rdwr(pentry, CACHE_INODE_WRITE, &seek_descriptor, udata->length, &size_io_done, pfsal_attr, udata->buffer, &eof, ht, pclient, pcontext, TRUE, pstatus); if (status != CACHE_INODE_SUCCESS) return *pstatus; P_w(&pentry->lock); Mem_Free(udata->buffer); udata->buffer = NULL; V_w(&pentry->lock); } else { if(offset < udata->offset) { *pstatus = CACHE_INODE_INVALID_ARGUMENT; return *pstatus; } seek_descriptor.offset = offset; seek_descriptor.whence = FSAL_SEEK_SET; return cache_inode_rdwr(pentry, CACHE_INODE_WRITE, &seek_descriptor, count, &size_io_done, pfsal_attr, (char *)(udata->buffer + offset - udata->offset), &eof, ht, pclient, pcontext, TRUE, pstatus); } /* Regulat exit */ *pstatus = CACHE_INODE_SUCCESS; return *pstatus; }
/** * * cache_inode_getattr: Gets the attributes for a cached entry. * * Gets the attributes for a cached entry. The FSAL attributes are kept in a structure when the entry * is added to the cache. * * @param pentry [IN] entry to be managed. * @param pattr [OUT] pointer to the results * @param ht [IN] hash table used for the cache, unused in this call. * @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_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_getattr(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { cache_inode_status_t status; fsal_handle_t *pfsal_handle = NULL; fsal_status_t fsal_status; /* sanity check */ if(pentry == NULL || pattr == NULL || ht == NULL || pclient == NULL || pcontext == NULL) { *pstatus = CACHE_INODE_INVALID_ARGUMENT; LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning CACHE_INODE_INVALID_ARGUMENT because of bad arg"); return *pstatus; } /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; inc_func_call(pclient, CACHE_INODE_GETATTR); /* Lock the entry */ P_w(&pentry->lock); status = cache_inode_renew_entry(pentry, pattr, ht, pclient, pcontext, pstatus); if(status != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* RW Lock goes for writer to reader */ rw_lock_downgrade(&pentry->lock); cache_inode_get_attributes(pentry, pattr); if(FSAL_TEST_MASK(pattr->asked_attributes, FSAL_ATTR_RDATTR_ERR)) { 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 DIRECTORY: pfsal_handle = &pentry->object.dir.handle; break; case SOCKET_FILE: case FIFO_FILE: case BLOCK_FILE: case CHARACTER_FILE: pfsal_handle = &pentry->object.special_obj.handle; break; case FS_JUNCTION: case UNASSIGNED: case RECYCLED: *pstatus = CACHE_INODE_INVALID_ARGUMENT; LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry - unexpected md_type", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* * An error occured when trying to get * the attributes, they have to be renewed */ #ifdef _USE_MFSL fsal_status = FSAL_getattrs_descriptor(&(cache_inode_fd(pentry)->fsal_file), pfsal_handle, pcontext, pattr); #else fsal_status = FSAL_getattrs_descriptor(cache_inode_fd(pentry), pfsal_handle, pcontext, pattr); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_getattr: Stale FSAL File Handle detected for pentry = %p", pentry); /* Locked flag is set to true to show entry has a read lock */ cache_inode_kill_entry( pentry, WT_LOCK, ht, pclient, &kill_status); if(kill_status != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_getattr: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stat */ inc_func_err_unrecover(pclient, CACHE_INODE_GETATTR); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from FSAL_getattrs_descriptor", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; } /* Set the new attributes */ cache_inode_set_attributes(pentry, pattr); } *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) inc_func_err_retryable(pclient, CACHE_INODE_GETATTR); else inc_func_success(pclient, CACHE_INODE_GETATTR); #ifdef _USE_NFS4_ACL if(isDebug(COMPONENT_NFS_V4_ACL)) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: pentry = %p, acl = %p", pentry, pattr->acl); if(pattr->acl) { fsal_ace_t *pace; for(pace = pattr->acl->aces; pace < pattr->acl->aces + pattr->acl->naces; pace++) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: ace type = 0x%x, flag = 0x%x, perm = 0x%x, special = %d, %s = 0x%x", pace->type, pace->flag, pace->perm, IS_FSAL_ACE_SPECIAL_ID(*pace), GET_FSAL_ACE_WHO_TYPE(*pace), GET_FSAL_ACE_WHO(*pace)); } } } #endif /* _USE_NFS4_ACL */ LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_getattr: returning %d(%s) from cache_inode_valid", *pstatus, cache_inode_err_str(*pstatus)); return *pstatus; }
cache_inode_status_t cache_inode_rdwr(cache_entry_t * pentry, cache_inode_io_direction_t read_or_write, fsal_seek_t * seek_descriptor, fsal_size_t buffer_size, fsal_size_t * pio_size, fsal_attrib_list_t * pfsal_attr, caddr_t buffer, fsal_boolean_t * p_fsal_eof, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, uint64_t stable, cache_inode_status_t * pstatus) { int statindex = 0; cache_content_io_direction_t io_direction; cache_content_status_t cache_content_status; fsal_status_t fsal_status; fsal_openflags_t openflags; fsal_size_t io_size; fsal_attrib_list_t post_write_attr; fsal_status_t fsal_status_getattr; struct stat buffstat; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* For now, only FSAL_SEEK_SET is supported */ if(seek_descriptor->whence != FSAL_SEEK_SET) { LogCrit(COMPONENT_CACHE_INODE, "Implementation trouble: seek_descriptor was not a 'FSAL_SEEK_SET' cursor"); *pstatus = CACHE_INODE_INVALID_ARGUMENT; return *pstatus; } io_size = buffer_size; LogDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: INODE : IO Size = %llu fdsize =%zu seeksize=%zu", buffer_size, sizeof(fsal_file_t), sizeof(fsal_seek_t)); /* stat */ pclient->stat.nb_call_total += 1; if(read_or_write == CACHE_INODE_READ) { statindex = CACHE_INODE_READ_DATA; io_direction = CACHE_CONTENT_READ; openflags = FSAL_O_RDONLY; pclient->stat.func_stats.nb_call[CACHE_INODE_READ_DATA] += 1; } else { statindex = CACHE_INODE_WRITE_DATA; io_direction = CACHE_CONTENT_WRITE; openflags = FSAL_O_WRONLY; pclient->stat.func_stats.nb_call[CACHE_INODE_WRITE_DATA] += 1; } P_w(&pentry->lock); /* IO are done only on REGULAR_FILEs */ if(pentry->internal_md.type != REGULAR_FILE) { *pstatus = CACHE_INODE_BAD_TYPE; V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } /* Non absolute address within the file are not supported (we act only like pread/pwrite) */ if(seek_descriptor->whence != FSAL_SEEK_SET) { *pstatus = CACHE_INODE_INVALID_ARGUMENT; V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } /* Do we use stable or unstable storage ? */ if(stable == FSAL_UNSAFE_WRITE_TO_GANESHA_BUFFER) { /* Data will be stored in memory and not flush to FSAL */ /* If the unstable_data buffer allocated ? */ if(pentry->object.file.unstable_data.buffer == NULL) { if((pentry->object.file.unstable_data.buffer = Mem_Alloc_Label(CACHE_INODE_UNSTABLE_BUFFERSIZE, "Cache_Inode Unstable Buffer")) == NULL) { *pstatus = CACHE_INODE_MALLOC_ERROR; V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } pentry->object.file.unstable_data.offset = seek_descriptor->offset; pentry->object.file.unstable_data.length = buffer_size; memcpy(pentry->object.file.unstable_data.buffer, buffer, buffer_size); /* Set mtime and ctime */ cache_inode_set_time_current( &pentry->attributes.mtime ) ; /* BUGAZOMEU : write operation must NOT modify file's ctime */ pentry->attributes.ctime = pentry->attributes.mtime; *pio_size = buffer_size; } /* if( pentry->object.file.unstable_data.buffer == NULL ) */ else { if((pentry->object.file.unstable_data.offset < seek_descriptor->offset) && (buffer_size + seek_descriptor->offset < CACHE_INODE_UNSTABLE_BUFFERSIZE)) { pentry->object.file.unstable_data.length = buffer_size + seek_descriptor->offset; memcpy((char *)(pentry->object.file.unstable_data.buffer + seek_descriptor->offset), buffer, buffer_size); /* Set mtime and ctime */ cache_inode_set_time_current( &pentry->attributes.mtime ) ; /* BUGAZOMEU : write operation must NOT modify file's ctime */ pentry->attributes.ctime = pentry->attributes.mtime; *pio_size = buffer_size; } else { /* Go back to regular situation */ stable = FSAL_SAFE_WRITE_TO_FS; } } } /* if( stable == FALSE ) */ if(stable == FSAL_SAFE_WRITE_TO_FS || stable == FSAL_UNSAFE_WRITE_TO_FS_BUFFER) { /* Calls file content cache to operate on the cache */ if(pentry->object.file.pentry_content != NULL) { /* Entry is data cached */ cache_content_rdwr(pentry->object.file.pentry_content, io_direction, seek_descriptor, &io_size, pio_size, buffer, p_fsal_eof, &buffstat, (cache_content_client_t *) pclient->pcontent_client, pcontext, &cache_content_status); /* If the entry under resync */ if(cache_content_status == CACHE_CONTENT_LOCAL_CACHE_NOT_FOUND) { /* Data cache gc has removed this entry */ if(cache_content_new_entry(pentry, NULL, (cache_content_client_t *)pclient->pcontent_client, RENEW_ENTRY, pcontext, &cache_content_status) == NULL) { /* Entry could not be recoverd, cache_content_status contains an error, let it be managed by the next block */ LogCrit(COMPONENT_CACHE_INODE, "Read/Write Operation through cache failed with status %d (renew process failed)", cache_content_status); /* Will go to the end of the function on the error clause with cache_content_status describing the error */ } else { /* Entry was successfully renewed */ LogInfo(COMPONENT_CACHE_INODE, "----> File Content Entry %p was successfully renewed", pentry); /* Try to access the content of the file again */ cache_content_rdwr(pentry->object.file.pentry_content, io_direction, seek_descriptor, &io_size, pio_size, buffer, p_fsal_eof, &buffstat, (cache_content_client_t *) pclient->pcontent_client, pcontext, &cache_content_status); /* No management of cache_content_status in case of failure, this will be done * within the next block */ } } if(cache_content_status != CACHE_CONTENT_SUCCESS) { *pstatus = cache_content_error_convert(cache_content_status); V_w(&pentry->lock); LogCrit(COMPONENT_CACHE_INODE, "Read/Write Operation through cache failed with status %d", cache_content_status); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: inode/dc: io_size=%llu, pio_size=%llu, eof=%d, seek=%d.%"PRIu64, io_size, *pio_size, *p_fsal_eof, seek_descriptor->whence, seek_descriptor->offset); LogMidDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: INODE AFTER : IO Size = %llu %llu", io_size, *pio_size); /* Use information from the buffstat to update the file metadata */ pentry->attributes.filesize = buffstat.st_size; pentry->attributes.spaceused = buffstat.st_blksize * buffstat.st_blocks; } else { /* No data cache entry, we operated directly on FSAL */ pentry->attributes.asked_attributes = pclient->attrmask; /* We need to open if we don't have a cached * descriptor or our open flags differs. */ if(cache_inode_open(pentry, pclient, openflags, pcontext, pstatus) != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } /* Call FSAL_read or FSAL_write */ if(read_or_write == CACHE_INODE_READ) { #ifdef _USE_MFSL fsal_status = MFSL_read(&(pentry->object.file.open_fd.mfsl_fd), seek_descriptor, io_size, buffer, pio_size, p_fsal_eof, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_read(&(pentry->object.file.open_fd.fd), seek_descriptor, io_size, buffer, pio_size, p_fsal_eof); #endif } else { #ifdef _USE_MFSL fsal_status = MFSL_write(&(pentry->object.file.open_fd.mfsl_fd), seek_descriptor, io_size, buffer, pio_size, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_write(&(pentry->object.file.open_fd.fd), seek_descriptor, io_size, buffer, pio_size); #endif #if 0 /* Alright, the unstable write is complete. Now if it was supposed to be a stable write * we can sync to the hard drive. */ if(stable == FSAL_SAFE_WRITE_TO_FS) { #ifdef _USE_MFSL fsal_status = MFSL_commit(&(pentry->object.file.open_fd.mfsl_fd), NULL); #else fsal_status = FSAL_commit(&(pentry->object.file.open_fd.fd)); #endif #endif } LogFullDebug(COMPONENT_FSAL, "cache_inode_rdwr: FSAL IO operation returned %d, asked_size=%llu, effective_size=%llu", fsal_status.major, (unsigned long long)io_size, (unsigned long long)*pio_size); if(FSAL_IS_ERROR(fsal_status)) { if(fsal_status.major == ERR_FSAL_DELAY) LogEvent(COMPONENT_CACHE_INODE, "cache_inode_rdwr: FSAL_write returned EBUSY"); else LogDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: fsal_status.major = %d", fsal_status.major); if((fsal_status.major != ERR_FSAL_NOT_OPENED) && (pentry->object.file.open_fd.fileno != 0)) { LogDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: CLOSING pentry %p: fd=%d", pentry, pentry->object.file.open_fd.fileno); #ifdef _USE_MFSL MFSL_close(&(pentry->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL); #else FSAL_close(&(pentry->object.file.open_fd.fd)); #endif *pstatus = cache_inode_error_convert(fsal_status); } else { /* the fd has been close by another thread. * return CACHE_INODE_FSAL_DELAY so the client will * retry with a new fd. */ *pstatus = CACHE_INODE_FSAL_DELAY; } pentry->object.file.open_fd.last_op = 0; pentry->object.file.open_fd.fileno = 0; V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_rdwr: inode/direct: io_size=%llu, pio_size=%llu, eof=%d, seek=%d.%"PRIu64, io_size, *pio_size, *p_fsal_eof, seek_descriptor->whence, seek_descriptor->offset); if(cache_inode_close(pentry, pclient, pstatus) != CACHE_INODE_SUCCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_rdwr: cache_inode_close = %d", *pstatus); V_w(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[statindex] += 1; return *pstatus; } if(read_or_write == CACHE_INODE_WRITE) { /* Do a getattr in order to have update information on filesize * This query is done directly on FSAL (object is not data cached), and result * will be propagated to cache Inode */ /* WARNING: This operation is to be done AFTER FSAL_close (some FSAL, like POSIX, * may not flush data until the file is closed */ /*post_write_attr.asked_attributes = pclient->attrmask ; */ post_write_attr.asked_attributes = FSAL_ATTR_SIZE | FSAL_ATTR_SPACEUSED; fsal_status_getattr = FSAL_getattrs(&(pentry->handle), pcontext, &post_write_attr); /* if failed, the next block will handle the error */ if(FSAL_IS_ERROR(fsal_status_getattr)) fsal_status = fsal_status_getattr; else { /* Update Cache Inode attributes */ pentry->attributes.filesize = post_write_attr.filesize; pentry->attributes.spaceused = post_write_attr.spaceused; } } } /* IO was successfull (through cache content or not), we manually update the times in the attributes */ switch (read_or_write) { case CACHE_INODE_READ: /* Set the atime */ cache_inode_set_time_current( & pentry->attributes.atime ) ; break; case CACHE_INODE_WRITE: /* Set mtime and ctime */ cache_inode_set_time_current( & pentry->attributes.mtime ) ; /* BUGAZOMEU : write operation must NOT modify file's ctime */ pentry->attributes.ctime = pentry->attributes.mtime; break; } } /* if(stable == TRUE ) */ /* Return attributes to caller */ if(pfsal_attr != NULL) *pfsal_attr = pentry->attributes; *pstatus = CACHE_INODE_SUCCESS; /* stat */ if(read_or_write == CACHE_INODE_READ) { *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READ] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_READ] += 1; } else { *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_SET, pclient); if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_WRITE] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_WRITE] += 1; } V_w(&pentry->lock); return *pstatus; } /* cache_inode_rdwr */
int nfs41_op_close(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_close"; cache_inode_state_t *pstate_found = NULL; cache_inode_status_t cache_status; memset(&res_CLOSE4, 0, sizeof(res_CLOSE4)); resp->resop = NFS4_OP_CLOSE; /* If the filehandle is Empty */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_CLOSE4.status = NFS4ERR_NOFILEHANDLE; return res_CLOSE4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_CLOSE4.status = NFS4ERR_BADHANDLE; return res_CLOSE4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_CLOSE4.status = NFS4ERR_FHEXPIRED; return res_CLOSE4.status; } if(data->current_entry == NULL) { res_CLOSE4.status = NFS4ERR_SERVERFAULT; return res_CLOSE4.status; } /* Should not operate on directories */ if(data->current_entry->internal_md.type == DIR_BEGINNING || data->current_entry->internal_md.type == DIR_CONTINUE) { res_CLOSE4.status = NFS4ERR_ISDIR; return res_CLOSE4.status; } /* Object should be a file */ if(data->current_entry->internal_md.type != REGULAR_FILE) { res_CLOSE4.status = NFS4ERR_INVAL; return res_CLOSE4.status; } /* Get the related state */ if(cache_inode_get_state(arg_CLOSE4.open_stateid.other, &pstate_found, data->pclient, &cache_status) != CACHE_INODE_SUCCESS) { if(cache_status == CACHE_INODE_NOT_FOUND) res_CLOSE4.status = NFS4ERR_BAD_STATEID; else res_CLOSE4.status = NFS4ERR_INVAL; return res_CLOSE4.status; } #ifdef _TOTO /* Check is held locks remain */ if(pstate_found->state_data.share.lockheld > 0) { res_CLOSE4.status = NFS4ERR_LOCKS_HELD; return res_CLOSE4.status; } #endif /* Update the seqid for the open_owner */ P(pstate_found->powner->lock); pstate_found->powner->seqid += 1; V(pstate_found->powner->lock); /* Prepare the result */ res_CLOSE4.CLOSE4res_u.open_stateid.seqid = pstate_found->seqid + 1; /* Close the file in FSAL through the cache inode */ P_w(&data->current_entry->lock); if(cache_inode_close(data->current_entry, data->pclient, &cache_status) != CACHE_INODE_SUCCESS) { V_w(&data->current_entry->lock); res_CLOSE4.status = nfs4_Errno(cache_status); return res_CLOSE4.status; } V_w(&data->current_entry->lock); /* File is closed, release the corresponding state */ if(cache_inode_del_state_by_key(arg_CLOSE4.open_stateid.other, data->pclient, &cache_status) != CACHE_INODE_SUCCESS) { res_CLOSE4.status = nfs4_Errno(cache_status); return res_CLOSE4.status; } memcpy(res_CLOSE4.CLOSE4res_u.open_stateid.other, arg_CLOSE4.open_stateid.other, 12);; res_CLOSE4.status = NFS4_OK; return NFS4_OK; } /* nfs41_op_close */