/** * @brief Renames an entry * * This function calls the FSAL to rename a file, then mirrors the * operation in the cache. * * @param[in] dir_src The source directory * @param[in] oldname The current name of the file * @param[in] dir_dest The destination directory * @param[in] newname The name to be assigned to the object * * @retval CACHE_INODE_SUCCESS if operation is a success. * @retval CACHE_INODE_NOT_FOUND if source object does not exist * @retval CACHE_INODE_ENTRY_EXISTS on collision. * @retval CACHE_INODE_NOT_A_DIRECTORY if dir_src or dir_dest is not a * directory. * @retval CACHE_INODE_BADNAME if either name is "." or ".." */ cache_inode_status_t cache_inode_rename(cache_entry_t *dir_src, const char *oldname, cache_entry_t *dir_dest, const char *newname) { fsal_status_t fsal_status = { 0, 0 }; cache_entry_t *lookup_src = NULL; cache_entry_t *lookup_dst = NULL; cache_inode_status_t status = CACHE_INODE_SUCCESS; cache_inode_status_t status_ref_dir_src = CACHE_INODE_SUCCESS; cache_inode_status_t status_ref_dir_dst = CACHE_INODE_SUCCESS; cache_inode_status_t status_ref_dst = CACHE_INODE_SUCCESS; if ((dir_src->type != DIRECTORY) || (dir_dest->type != DIRECTORY)) { status = CACHE_INODE_NOT_A_DIRECTORY; goto out; } /* Check for . and .. on oldname and newname. */ if (!strcmp(oldname, ".") || !strcmp(oldname, "..") || !strcmp(newname, ".") || !strcmp(newname, "..")) { status = CACHE_INODE_BADNAME; goto out; } /* Check for object existence in source directory */ status = cache_inode_lookup_impl(dir_src, oldname, &lookup_src); if (lookup_src == NULL) { /* If FSAL FH is stale, then this was managed in * cache_inode_lookup */ if (status != CACHE_INODE_FSAL_ESTALE) status = CACHE_INODE_NOT_FOUND; LogEvent(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : source doesn't exist", dir_src, oldname, dir_dest, newname); goto out; } /* Do not rename a junction node or an export root. */ if (lookup_src->type == DIRECTORY) { /* Get attr_lock for looking at junction_export */ PTHREAD_RWLOCK_rdlock(&lookup_src->attr_lock); if (lookup_src->object.dir.junction_export != NULL || atomic_fetch_int32_t(&lookup_src->exp_root_refcount) != 0) { /* Trying to rename an export mount point */ LogCrit(COMPONENT_CACHE_INODE, "Attempt to rename export %s", oldname); /* Release attr_lock */ PTHREAD_RWLOCK_unlock(&lookup_src->attr_lock); status = CACHE_INODE_DIR_NOT_EMPTY; goto out; } /* Release attr_lock */ PTHREAD_RWLOCK_unlock(&lookup_src->attr_lock); } /* Check if an object with the new name exists in the destination directory */ status = cache_inode_lookup_impl(dir_dest, newname, &lookup_dst); if (status == CACHE_INODE_SUCCESS) { LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : destination already exists", dir_src, oldname, dir_dest, newname); } if (status == CACHE_INODE_NOT_FOUND) status = CACHE_INODE_SUCCESS; if (status == CACHE_INODE_FSAL_ESTALE) { LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : stale destination", dir_src, oldname, dir_dest, newname); } if (lookup_src == lookup_dst) { /* Nothing to do according to POSIX and NFS3/4 * If from and to both refer to the same file (they might be * hard links of each other), then RENAME should perform no * action and return success */ LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : same file so skipping out", dir_src, oldname, dir_dest, newname); goto out; } /* Perform the rename operation in FSAL before doing anything in the * cache. Indeed, if the FSAL_rename fails unexpectly, the cache would * be inconsistent! * * We do almost no checking before making call because we want to return * error based on the files actually present in the directories, not * what we have in our cache. */ LogFullDebug(COMPONENT_CACHE_INODE, "about to call FSAL rename"); fsal_status = dir_src->obj_handle->ops->rename(dir_src->obj_handle, oldname, dir_dest->obj_handle, newname); LogFullDebug(COMPONENT_CACHE_INODE, "returned from FSAL rename"); status_ref_dir_src = cache_inode_refresh_attrs_locked(dir_src); if (dir_src != dir_dest) status_ref_dir_dst = cache_inode_refresh_attrs_locked(dir_dest); LogFullDebug(COMPONENT_CACHE_INODE, "done refreshing attributes"); if (FSAL_IS_ERROR(fsal_status)) { status = cache_inode_error_convert(fsal_status); LogFullDebug(COMPONENT_CACHE_INODE, "FSAL rename failed with %s", cache_inode_err_str(status)); goto out; } if (lookup_dst) { /* Force a refresh of the overwritten inode */ status_ref_dst = cache_inode_refresh_attrs_locked(lookup_dst); if (status_ref_dst == CACHE_INODE_FSAL_ESTALE) status_ref_dst = CACHE_INODE_SUCCESS; } status = status_ref_dir_src; if (status == CACHE_INODE_SUCCESS) status = status_ref_dir_dst; if (status == CACHE_INODE_SUCCESS) status = status_ref_dst; if (status != CACHE_INODE_SUCCESS) goto out; /* Must take locks on directories now, * because if another thread checks source and destination existence * in the same time, it will try to do the same checks... * and it will have the same conclusion !!! */ src_dest_lock(dir_src, dir_dest); if (lookup_dst) { /* Remove the entry from parent dir_entries avl */ status_ref_dir_dst = cache_inode_remove_cached_dirent(dir_dest, newname); if (status_ref_dir_dst != CACHE_INODE_SUCCESS) { LogDebug(COMPONENT_CACHE_INODE, "remove entry failed with status %s", cache_inode_err_str(status_ref_dir_dst)); cache_inode_invalidate_all_cached_dirent(dir_dest); } } if (dir_src == dir_dest) { /* if the rename operation is made within the same dir, then we * use an optimization: cache_inode_rename_dirent is used * instead of adding/removing dirent. This limits the use of * resource in this case */ LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : source and target " "directory the same", dir_src, oldname, dir_dest, newname); cache_inode_status_t tmp_status = cache_inode_rename_cached_dirent(dir_dest, oldname, newname); if (tmp_status != CACHE_INODE_SUCCESS) { /* We're obviously out of date. Throw out the cached directory */ cache_inode_invalidate_all_cached_dirent(dir_dest); } } else { cache_inode_status_t tmp_status = CACHE_INODE_SUCCESS; LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : moving entry", dir_src, oldname, dir_dest, newname); /* We may have a cache entry for the destination * filename. If we do, we must delete it : it is stale. */ tmp_status = cache_inode_remove_cached_dirent(dir_dest, newname); if (tmp_status != CACHE_INODE_SUCCESS && tmp_status != CACHE_INODE_NOT_FOUND) { LogDebug(COMPONENT_CACHE_INODE, "Remove stale dirent returned %s", cache_inode_err_str(tmp_status)); cache_inode_invalidate_all_cached_dirent(dir_dest); } tmp_status = cache_inode_add_cached_dirent(dir_dest, newname, lookup_src, NULL); if (tmp_status != CACHE_INODE_SUCCESS) { /* We're obviously out of date. Throw out the cached directory */ LogCrit(COMPONENT_CACHE_INODE, "Add dirent returned %s", cache_inode_err_str(tmp_status)); cache_inode_invalidate_all_cached_dirent(dir_dest); } /* Remove the old entry */ tmp_status = cache_inode_remove_cached_dirent(dir_src, oldname); if (tmp_status != CACHE_INODE_SUCCESS && tmp_status != CACHE_INODE_NOT_FOUND) { LogDebug(COMPONENT_CACHE_INODE, "Remove old dirent returned %s", cache_inode_err_str(tmp_status)); cache_inode_invalidate_all_cached_dirent(dir_src); } } /* unlock entries */ src_dest_unlock(dir_src, dir_dest); out: if (lookup_src) cache_inode_put(lookup_src); if (lookup_dst) cache_inode_put(lookup_dst); return status; }
/** * * 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) */
/** * * 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_entry_t * cache_inode_create(cache_entry_t *parent, fsal_name_t *name, cache_inode_file_type_t type, fsal_accessmode_t mode, cache_inode_create_arg_t *create_arg, fsal_attrib_list_t *attr, fsal_op_context_t *context, cache_inode_status_t *status) { cache_entry_t *entry = NULL; fsal_status_t fsal_status = {0, 0}; fsal_handle_t object_handle; fsal_attrib_list_t object_attributes; cache_inode_fsal_data_t fsal_data; cache_inode_create_arg_t zero_create_arg; memset(&zero_create_arg, 0, sizeof(zero_create_arg)); memset(&fsal_data, 0, sizeof(fsal_data)); memset(&object_handle, 0, sizeof(object_handle)); if (create_arg == NULL) { create_arg = &zero_create_arg; } /* Set the return default to CACHE_INODE_SUCCESS */ *status = CACHE_INODE_SUCCESS; if ((type != REGULAR_FILE) && (type != DIRECTORY) && (type != SYMBOLIC_LINK) && (type != SOCKET_FILE) && (type != FIFO_FILE) && (type != CHARACTER_FILE) && (type != BLOCK_FILE)) { *status = CACHE_INODE_BAD_TYPE; entry = NULL; goto out; } /* Check if an entry of the same name exists */ entry = cache_inode_lookup(parent, name, attr, context, status); if (entry != NULL) { *status = CACHE_INODE_ENTRY_EXISTS; if (entry->type != type) { /* Incompatible types, returns NULL */ cache_inode_lru_unref(entry, LRU_FLAG_NONE); entry = NULL; goto out; } else { goto out; } } /* The entry doesn't exist, so we can create it. */ object_attributes.asked_attributes = cache_inode_params.attrmask; switch (type) { case REGULAR_FILE: fsal_status = FSAL_create(&parent->handle, name, context, mode, &object_handle, &object_attributes); break; case DIRECTORY: fsal_status = FSAL_mkdir(&parent->handle, name, context, mode, &object_handle, &object_attributes); break; case SYMBOLIC_LINK: fsal_status = FSAL_symlink(&parent->handle, name, &create_arg->link_content, context, mode, &object_handle, &object_attributes); break; case SOCKET_FILE: fsal_status = FSAL_mknode(&parent->handle, name, context, mode, FSAL_TYPE_SOCK, NULL, &object_handle, &object_attributes); break; case FIFO_FILE: fsal_status = FSAL_mknode(&parent->handle, name, context, mode, FSAL_TYPE_FIFO, NULL, &object_handle, &object_attributes); break; case BLOCK_FILE: fsal_status = FSAL_mknode(&parent->handle, name, context, mode, FSAL_TYPE_BLK, &create_arg->dev_spec, &object_handle, &object_attributes); break; case CHARACTER_FILE: fsal_status = FSAL_mknode(&parent->handle, name, context, mode, FSAL_TYPE_CHR, &create_arg->dev_spec, &object_handle, &object_attributes); break; default: /* we should never go there */ *status = CACHE_INODE_INCONSISTENT_ENTRY; entry = NULL; goto out; break; } /* Check for the result */ if (FSAL_IS_ERROR(fsal_status)) { if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE on create type %d", type); cache_inode_kill_entry(parent); } *status = cache_inode_error_convert(fsal_status); entry = NULL; goto out; } fsal_data.fh_desc.start = (caddr_t) &object_handle; fsal_data.fh_desc.len = 0; FSAL_ExpandHandle(context->export_context, FSAL_DIGEST_SIZEOF, &fsal_data.fh_desc); entry = cache_inode_new_entry(&fsal_data, &object_attributes, type, create_arg, status); if (entry == NULL) { *status = CACHE_INODE_INSERT_ERROR; return NULL; } PTHREAD_RWLOCK_WRLOCK(&parent->content_lock); /* Add this entry to the directory (also takes an internal ref) */ cache_inode_add_cached_dirent(parent, name, entry, NULL, status); PTHREAD_RWLOCK_UNLOCK(&parent->content_lock); if (*status != CACHE_INODE_SUCCESS) { cache_inode_lru_unref(entry, LRU_FLAG_NONE); entry = NULL; goto out; } PTHREAD_RWLOCK_WRLOCK(&parent->attr_lock); /* Update the parent cached attributes */ cache_inode_set_time_current(&parent->attributes.mtime); parent->attributes.ctime = parent->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 == DIRECTORY) { ++(parent->attributes.numlinks); } PTHREAD_RWLOCK_UNLOCK(&parent->attr_lock); /* Copy up the child attributes */ *attr = object_attributes; *status = CACHE_INODE_SUCCESS; out: return entry; }
/** * * cache_inode_readdir_populate: fully reads a directory in FSAL and caches * the related entries. * * fully reads a directory in FSAL and caches the related entries. No MT * safety managed here !! * * @param pentry [IN] entry for the parent directory to be read. This must be * a DIRECTORY * @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. * */ cache_inode_status_t cache_inode_readdir_populate( cache_entry_t * pentry_dir, cache_inode_policy_t policy, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_dir_t fsal_dirhandle; fsal_status_t fsal_status; fsal_attrib_list_t dir_attributes; fsal_cookie_t begin_cookie; fsal_cookie_t end_cookie; fsal_count_t nbfound; fsal_count_t iter; fsal_boolean_t fsal_eod; cache_entry_t *pentry = NULL; cache_entry_t *pentry_parent = pentry_dir; 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; fsal_dirent_t array_dirent[FSAL_READDIR_SIZE + 20]; cache_inode_fsal_data_t new_entry_fsdata; cache_inode_dir_entry_t *new_dir_entry = NULL; uint64_t i = 0; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* Only DIRECTORY entries are concerned */ if(pentry_dir->internal_md.type != DIRECTORY) { *pstatus = CACHE_INODE_BAD_TYPE; return *pstatus; } #ifdef _USE_MFSL_ASYNC /* If entry is asynchronous (via MFSL), it should not be repopulated until it is synced */ if(MFSL_ASYNC_is_synced(&pentry_dir->mobject) == FALSE) { /* Directory is asynchronous, do not repopulate it and let it * in the state 'has_been_readdir == FALSE' */ *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } #endif /* If directory is already populated , there is no job to do */ if(pentry_dir->object.dir.has_been_readdir == CACHE_INODE_YES) { *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } /* Invalidate all the dirents */ if(cache_inode_invalidate_all_cached_dirent(pentry_dir, ht, pclient, pstatus) != CACHE_INODE_SUCCESS) return *pstatus; /* Open the directory */ dir_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL fsal_status = MFSL_opendir(&pentry_dir->mobject, pcontext, &pclient->mfsl_context, &fsal_dirhandle, &dir_attributes, NULL); #else fsal_status = FSAL_opendir(&pentry_dir->object.dir.handle, pcontext, &fsal_dirhandle, &dir_attributes); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_dir, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: Could not kill entry %p, status = %u", pentry_dir, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } /* Loop for readding the directory */ FSAL_SET_COOKIE_BEGINNING(begin_cookie); FSAL_SET_COOKIE_BEGINNING(end_cookie); fsal_eod = FALSE; do { #ifdef _USE_MFSL fsal_status = MFSL_readdir(&fsal_dirhandle, begin_cookie, pclient->attrmask, FSAL_READDIR_SIZE * sizeof(fsal_dirent_t), array_dirent, &end_cookie, &nbfound, &fsal_eod, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_readdir(&fsal_dirhandle, begin_cookie, pclient->attrmask, FSAL_READDIR_SIZE * sizeof(fsal_dirent_t), array_dirent, &end_cookie, &nbfound, &fsal_eod); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); return *pstatus; } for(iter = 0; iter < nbfound; iter++) { LogFullDebug(COMPONENT_NFS_READDIR, "cache readdir populate found entry %s", array_dirent[iter].name.name); /* It is not needed to cache '.' and '..' */ if(!FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT_DOT)) { LogFullDebug(COMPONENT_NFS_READDIR, "cache readdir populate : do not cache . and .."); continue; } /* If dir entry is a symbolic link, its content has to be read */ if((type = cache_inode_fsal_type_convert(array_dirent[iter].attributes.type)) == SYMBOLIC_LINK) { #ifdef _USE_MFSL mfsl_object_t tmp_mfsl; #endif /* Let's read the link for caching its value */ object_attributes.asked_attributes = pclient->attrmask; if( CACHE_INODE_KEEP_CONTENT( pentry_dir->policy ) ) { #ifdef _USE_MFSL tmp_mfsl.handle = array_dirent[iter].handle; fsal_status = MFSL_readlink(&tmp_mfsl, pcontext, &pclient->mfsl_context, &create_arg.link_content, &object_attributes, NULL); #else fsal_status = FSAL_readlink(&array_dirent[iter].handle, pcontext, &create_arg.link_content, &object_attributes); #endif } else { fsal_status.major = ERR_FSAL_NO_ERROR ; fsal_status.minor = 0 ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_dir, fsal_status.major, fsal_status.minor ); if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: Could not kill entry %p, status = %u", pentry_dir, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } } /* Try adding the entry, if it exists then this existing entry is returned */ new_entry_fsdata.handle = array_dirent[iter].handle; new_entry_fsdata.cookie = 0; /* XXX needed? */ if((pentry = cache_inode_new_entry( &new_entry_fsdata, &array_dirent[iter].attributes, type, policy, &create_arg, NULL, ht, pclient, pcontext, FALSE, /* This is population and no creation */ pstatus)) == NULL) return *pstatus; cache_status = cache_inode_add_cached_dirent( pentry_parent, &(array_dirent[iter].name), pentry, ht, &new_dir_entry, pclient, pcontext, pstatus); if(cache_status != CACHE_INODE_SUCCESS && cache_status != CACHE_INODE_ENTRY_EXISTS) return *pstatus; /* * Remember the FSAL readdir cookie associated with this dirent. This * is needed for partial directory reads. * * to_uint64 should be a lightweight operation--it is in the current * default implementation. We think the right thing -should- happen * therefore with if _USE_MFSL. * * I'm ignoring the status because the default operation is a memcmp-- * we lready -have- the cookie. */ if (cache_status != CACHE_INODE_ENTRY_EXISTS) { (void) FSAL_cookie_to_uint64(&array_dirent[iter].handle, pcontext, &array_dirent[iter].cookie, &new_dir_entry->fsal_cookie); /* we are filling in all entries, and the cookie avl was * cleared before adding dirents */ new_dir_entry->cookie = i; /* still an offset */ (void) avltree_insert(&new_dir_entry->node_c, &pentry_parent->object.dir.cookies); } /* !exist */ } /* iter */ /* Get prepared for next step */ begin_cookie = end_cookie; /* next offset */ i++; } while(fsal_eod != TRUE); /* Close the directory */ #ifdef _USE_MFSL fsal_status = MFSL_closedir(&fsal_dirhandle, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_closedir(&fsal_dirhandle); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); return *pstatus; } /* End of work */ pentry_dir->object.dir.has_been_readdir = CACHE_INODE_YES; *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } /* cache_inode_readdir_populate */
/** * * 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; }
cache_inode_status_t cache_inode_create(cache_entry_t *parent, const char *name, object_file_type_t type, uint32_t mode, cache_inode_create_arg_t *create_arg, struct req_op_context *req_ctx, cache_entry_t **entry) { cache_inode_status_t status = CACHE_INODE_SUCCESS; fsal_status_t fsal_status = {0, 0}; struct fsal_obj_handle *object_handle; struct attrlist object_attributes; struct fsal_obj_handle *dir_handle; cache_inode_create_arg_t zero_create_arg; fsal_accessflags_t access_mask = 0; memset(&zero_create_arg, 0, sizeof(zero_create_arg)); memset(&object_attributes, 0, sizeof(object_attributes)); if (create_arg == NULL) { create_arg = &zero_create_arg; } if ((type != REGULAR_FILE) && (type != DIRECTORY) && (type != SYMBOLIC_LINK) && (type != SOCKET_FILE) && (type != FIFO_FILE) && (type != CHARACTER_FILE) && (type != BLOCK_FILE)) { status = CACHE_INODE_BAD_TYPE; *entry = NULL; goto out; } /* * 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 | FSAL_ACE_PERM_ADD_SUBDIRECTORY); status = cache_inode_access(parent, access_mask, req_ctx); if (status != CACHE_INODE_SUCCESS) { *entry = NULL; goto out; } /* Try to create it first */ dir_handle = parent->obj_handle; /* we pass in attributes to the create. We will get them back below */ FSAL_SET_MASK(object_attributes.mask, ATTR_MODE|ATTR_OWNER|ATTR_GROUP); object_attributes.owner = req_ctx->creds->caller_uid; object_attributes.group = req_ctx->creds->caller_gid; /* be more selective? */ object_attributes.mode = mode; switch (type) { case REGULAR_FILE: fsal_status = dir_handle->ops->create(dir_handle, req_ctx, name, &object_attributes, &object_handle); break; case DIRECTORY: fsal_status = dir_handle->ops->mkdir(dir_handle, req_ctx, name, &object_attributes, &object_handle); break; case SYMBOLIC_LINK: fsal_status = dir_handle->ops->symlink(dir_handle, req_ctx, name, create_arg->link_content, &object_attributes, &object_handle); break; case SOCKET_FILE: case FIFO_FILE: fsal_status = dir_handle->ops->mknode(dir_handle, req_ctx, name, type, NULL, /* no dev_t needed */ &object_attributes, &object_handle); break; case BLOCK_FILE: case CHARACTER_FILE: fsal_status = dir_handle->ops->mknode(dir_handle, req_ctx, name, type, &create_arg->dev_spec, &object_attributes, &object_handle); break; default: /* we should never go there */ status = CACHE_INODE_INCONSISTENT_ENTRY; *entry = NULL; goto out; break; } cache_inode_refresh_attrs_locked(parent, req_ctx); /* Check for the result */ if (FSAL_IS_ERROR(fsal_status)) { if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE on create type %d", type); cache_inode_kill_entry(parent); } else if (fsal_status.major == ERR_FSAL_EXIST) { /* Already exists. Check if type if correct */ status = cache_inode_lookup(parent, name, req_ctx, entry); if (*entry != NULL) { status = CACHE_INODE_ENTRY_EXISTS; if ((*entry)->type != type) { /* Incompatible types, returns NULL */ cache_inode_put(*entry); *entry = NULL; } goto out; } if (status == CACHE_INODE_NOT_FOUND) { /* Too bad, FSAL insist the file exists when we try to * create it, but lookup couldn't find it, retry. */ status = CACHE_INODE_INCONSISTENT_ENTRY; goto out; } } status = cache_inode_error_convert(fsal_status); *entry = NULL; goto out; } status = cache_inode_new_entry(object_handle, CACHE_INODE_FLAG_CREATE, entry); if (*entry == NULL) { goto out; } PTHREAD_RWLOCK_wrlock(&parent->content_lock); /* Add this entry to the directory (also takes an internal ref) */ status = cache_inode_add_cached_dirent(parent, name, *entry, NULL); PTHREAD_RWLOCK_unlock(&parent->content_lock); if (status != CACHE_INODE_SUCCESS) { cache_inode_put(*entry); *entry = NULL; goto out; } out: return status; }
static bool populate_dirent(const char *name, void *dir_state, fsal_cookie_t cookie) { struct cache_inode_populate_cb_state *state = (struct cache_inode_populate_cb_state *)dir_state; struct fsal_obj_handle *entry_hdl; cache_inode_dir_entry_t *new_dir_entry = NULL; cache_entry_t *cache_entry = NULL; fsal_status_t fsal_status = { 0, 0 }; struct fsal_obj_handle *dir_hdl = state->directory->obj_handle; fsal_status = dir_hdl->obj_ops.lookup(dir_hdl, name, &entry_hdl); if (FSAL_IS_ERROR(fsal_status)) { *state->status = cache_inode_error_convert(fsal_status); if (*state->status == CACHE_INODE_FSAL_XDEV) { LogInfo(COMPONENT_NFS_READDIR, "Ignoring XDEV entry %s", name); *state->status = CACHE_INODE_SUCCESS; return true; } LogInfo(COMPONENT_CACHE_INODE, "Lookup failed on %s in dir %p with %s", name, dir_hdl, cache_inode_err_str(*state->status)); return !cache_param.retry_readdir; } LogFullDebug(COMPONENT_NFS_READDIR, "Creating entry for %s", name); *state->status = cache_inode_new_entry(entry_hdl, CACHE_INODE_FLAG_NONE, &cache_entry); if (cache_entry == NULL) { *state->status = CACHE_INODE_NOT_FOUND; /* we do not free entry_hdl because it is consumed by cache_inode_new_entry */ LogEvent(COMPONENT_NFS_READDIR, "cache_inode_new_entry failed with %s", cache_inode_err_str(*state->status)); return false; } if (cache_entry->type == DIRECTORY) { /* Insert Parent's key */ cache_inode_key_dup(&cache_entry->object.dir.parent, &state->directory->fh_hk.key); } *state->status = cache_inode_add_cached_dirent(state->directory, name, cache_entry, &new_dir_entry); /* return initial ref */ cache_inode_put(cache_entry); if ((*state->status != CACHE_INODE_SUCCESS) && (*state->status != CACHE_INODE_ENTRY_EXISTS)) { LogEvent(COMPONENT_NFS_READDIR, "cache_inode_add_cached_dirent failed with %s", cache_inode_err_str(*state->status)); return false; } return true; }