/** * @brief Implement actual work of removing file * * Actually remove an entry from the directory. Assume that the * directory contents and attributes are locked for writes. The * attribute lock is released unless keep_md_lock is TRUE. * * @param[in] entry Entry for the parent directory to be managed. * @param[in] name Name of the entry that we are looking for in the cache. * @param[in] context FSAL credentials * @param[in] status Returned status * @param[in] flags Flags to control lock retention * * @return CACHE_INODE_SUCCESS if operation is a success * */ cache_inode_status_t cache_inode_remove_impl(cache_entry_t *entry, fsal_name_t *name, fsal_op_context_t *context, cache_inode_status_t *status, uint32_t flags) { cache_entry_t *to_remove_entry = NULL; fsal_status_t fsal_status = {0, 0}; #ifdef _USE_NFS4_ACL fsal_acl_t *saved_acl = NULL; fsal_acl_status_t acl_status = 0; #endif /* _USE_NFS4_ACL */ if(entry->type != DIRECTORY) { *status = CACHE_INODE_BAD_TYPE; goto out; } if (!(flags & CACHE_INODE_FLAG_CONTENT_HAVE)) { pthread_rwlock_rdlock(&entry->content_lock); flags |= CACHE_INODE_FLAG_CONTENT_HAVE; } /* Factor this somewhat. In the case where the directory hasn't been populated, the entry may not exist in the cache and we'd be bringing it in just to dispose of it. */ /* Looks up for the entry to remove */ if ((to_remove_entry = cache_inode_lookup_impl(entry, name, context, status)) == NULL) { goto out; } /* Lock the attributes (so we can decrement the link count) */ pthread_rwlock_wrlock(&to_remove_entry->attr_lock); LogDebug(COMPONENT_CACHE_INODE, "---> Cache_inode_remove : %s", name->name); #ifdef _USE_NFS4_ACL saved_acl = entry->attributes.acl; #endif /* _USE_NFS4_ACL */ fsal_status = FSAL_unlink(&entry->handle, name, context, &entry->attributes); if (FSAL_IS_ERROR(fsal_status)) { *status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { cache_inode_kill_entry(entry); } goto unlock; } else { #ifdef _USE_NFS4_ACL /* Decrement refcount on saved ACL */ nfs4_acl_release_entry(saved_acl, &acl_status); if (acl_status != NFS_V4_ACL_SUCCESS) { LogCrit(COMPONENT_CACHE_INODE, "Failed to release old acl, status=%d", acl_status); } #endif /* _USE_NFS4_ACL */ } cache_inode_fixup_md(entry); if ((flags & CACHE_INODE_FLAG_ATTR_HAVE) && !(flags & CACHE_INODE_FLAG_ATTR_HOLD)) { pthread_rwlock_unlock(&entry->attr_lock); } /* Remove the entry from parent dir_entries avl */ cache_inode_remove_cached_dirent(entry, name, status); LogFullDebug(COMPONENT_CACHE_INODE, "cache_inode_remove_cached_dirent: status=%d", *status); /* Update the attributes for the removed entry */ if ((to_remove_entry->type != DIRECTORY) && (to_remove_entry->attributes.numlinks > 1)) { if ((*status = cache_inode_refresh_attrs(to_remove_entry, context)) != CACHE_INODE_SUCCESS) { goto unlock; } } else { /* Otherwise our count is zero, or it was an empty directory. */ to_remove_entry->attributes.numlinks = 0; } /* Now, delete "to_remove_entry" from the cache inode and free its associated resources, but only if numlinks == 0 */ if (to_remove_entry->attributes.numlinks == 0) { /* Destroy the entry when everyone's references to it have been relinquished. Most likely now. */ pthread_rwlock_unlock(&to_remove_entry->attr_lock); /* Kill off the sentinel reference (and mark the entry so it doesn't get recycled while a reference exists.) */ cache_inode_lru_kill(to_remove_entry); } else { unlock: pthread_rwlock_unlock(&to_remove_entry->attr_lock); } out: if ((flags & CACHE_INODE_FLAG_CONTENT_HAVE) && !(flags & CACHE_INODE_FLAG_CONTENT_HOLD)) { pthread_rwlock_unlock(&entry->content_lock); } /* This is for the reference taken by lookup */ if (to_remove_entry) { cache_inode_put(to_remove_entry); } return *status; }
/** * @brief Copy file content. * * @param[in] src_entry File to copy from * @param[in] src_offset Offset start from the source file * @param[in] dst_entry Destination file to copy to * @param[in] dst_offset Offset in the dest file * @param[out] count Requested bytes to copy * @param[out] copied Bytes successfully copied * * @return CACHE_INODE_SUCCESS or various errors */ cache_inode_status_t cache_inode_copy(cache_entry_t *src_entry, uint64_t src_offset, cache_entry_t *dst_entry, uint64_t dst_offset, uint64_t count, uint64_t *copied) { fsal_status_t fsal_status = { 0, 0 }; cache_inode_status_t status; /** * To avoid deadlock, we always lock the entry with a smaller address * before the locking the other entry. Note that "content_lock" * protects "cache content" instead of file content. So only reader * lock is needed for either file. */ if ((size_t)src_entry < (size_t)dst_entry) { PTHREAD_RWLOCK_rdlock(&src_entry->content_lock); PTHREAD_RWLOCK_rdlock(&dst_entry->content_lock); } else { PTHREAD_RWLOCK_rdlock(&dst_entry->content_lock); PTHREAD_RWLOCK_rdlock(&src_entry->content_lock); } if (!is_open(src_entry) || !is_open(dst_entry)) { LogEvent(COMPONENT_CACHE_INODE, "Cannot copy between files that are not open"); status = NFS4ERR_OPENMODE; goto out; } if (count == UINT64_MAX) { count = src_entry->obj_handle->attrs->filesize - src_offset; LogDebug(COMPONENT_CACHE_INODE, "0-count has an effective value of %zu", count); } fsal_status = src_entry->obj_handle->obj_ops.copy(src_entry->obj_handle, src_offset, dst_entry->obj_handle, dst_offset, count, copied); if (FSAL_IS_ERROR(fsal_status)) { *copied = 0; status = cache_inode_error_convert(fsal_status); LogEvent(COMPONENT_CACHE_INODE, "File copy failed: major = %d, minor = %d", fsal_status.major, fsal_status.minor); goto out; } /* Update dest file after coping to it. */ PTHREAD_RWLOCK_wrlock(&dst_entry->attr_lock); status = cache_inode_refresh_attrs(dst_entry); PTHREAD_RWLOCK_unlock(&dst_entry->attr_lock); out: if ((size_t)src_entry < (size_t)dst_entry) { PTHREAD_RWLOCK_unlock(&dst_entry->content_lock); PTHREAD_RWLOCK_unlock(&src_entry->content_lock); } else { PTHREAD_RWLOCK_unlock(&src_entry->content_lock); PTHREAD_RWLOCK_unlock(&dst_entry->content_lock); } return status; }