Mercurial > hg > orthanc-object-storage
view Common/StoragePlugin.cpp @ 21:e568ca940ddd 1.1.0
1.1.0
author | Alain Mazy |
---|---|
date | Mon, 07 Sep 2020 14:13:38 +0200 |
parents | 968eb1c78aed |
children | e1f52b851827 67a7102d8d0f |
line wrap: on
line source
/** * Cloud storage plugins for Orthanc * Copyright (C) 2017-2020 Osimis S.A., Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #if GOOGLE_STORAGE_PLUGIN==1 #include "../Google/GoogleStoragePlugin.h" #define StoragePluginFactory GoogleStoragePluginFactory #elif AZURE_STORAGE_PLUGIN==1 #include "../Azure/AzureBlobStoragePlugin.h" #define StoragePluginFactory AzureBlobStoragePluginFactory #elif AWS_STORAGE_PLUGIN==1 #include "../Aws/AwsS3StoragePlugin.h" #define StoragePluginFactory AwsS3StoragePluginFactory #else #pragma message(error "define a plugin") #endif #include <string.h> #include <stdio.h> #include <string> #include <iostream> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include "../Common/EncryptionHelpers.h" #include "../Common/EncryptionConfigurator.h" #include <SystemToolbox.h> static std::unique_ptr<IStoragePlugin> plugin; static std::unique_ptr<EncryptionHelpers> crypto; static bool cryptoEnabled = false; static std::string fileSystemRootPath; static bool migrationFromFileSystemEnabled = false; static std::string objectsRootPath; // class to free memory allocated by malloc if an exception occurs // This is to avoid an issue in which the blob storage read method // crashed if the buffer was allocated through: // auto buffer = std::unique_ptr<void, void(*)(void*)>{malloc(static_cast<uint64_t>(*size)), free}; class ScopedFree { void* buffer_; public: ScopedFree(void* buffer) : buffer_(buffer) { } ~ScopedFree() { free(buffer_); } void Release() // abandon ownership { buffer_ = nullptr; } }; static OrthancPluginErrorCode StorageCreate(const char* uuid, const void* content, int64_t size, OrthancPluginContentType type) { try { std::unique_ptr<IStoragePlugin::IWriter> writer(plugin->GetWriterForObject(uuid, type, cryptoEnabled)); if (cryptoEnabled) { std::string encryptedFile; try { crypto->Encrypt(encryptedFile, (const char*)content, size); } catch (EncryptionException& ex) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while encrypting object " + std::string(uuid) + ": " + ex.what()); return OrthancPluginErrorCode_StorageAreaPlugin; } writer->Write(encryptedFile.data(), encryptedFile.size()); } else { writer->Write(reinterpret_cast<const char*>(content), size); } } catch (StoragePluginException& ex) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while creating object " + std::string(uuid) + ": " + ex.what()); return OrthancPluginErrorCode_StorageAreaPlugin; } return OrthancPluginErrorCode_Success; } static OrthancPluginErrorCode StorageRead(void** content, int64_t* size, const char* uuid, OrthancPluginContentType type) { try { std::unique_ptr<IStoragePlugin::IReader> reader(plugin->GetReaderForObject(uuid, type, cryptoEnabled)); size_t fileSize = reader->GetSize(); if (cryptoEnabled) { *size = fileSize - crypto->OVERHEAD_SIZE; } else { *size = fileSize; } if (*size <= 0) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ", size of file is too small: " + boost::lexical_cast<std::string>(fileSize) + " bytes"); return OrthancPluginErrorCode_StorageAreaPlugin; } *content = malloc(static_cast<uint64_t>(*size)); ScopedFree freeContent(*content); if (*content == nullptr) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ", cannot allocate memory of size " + boost::lexical_cast<std::string>(*size) + " bytes"); return OrthancPluginErrorCode_StorageAreaPlugin; } if (cryptoEnabled) { std::vector<char> encrypted(fileSize); reader->Read(encrypted.data(), fileSize); try { crypto->Decrypt((char*)(*content), encrypted.data(), fileSize); } catch (EncryptionException& ex) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while decrypting object " + std::string(uuid) + ": " + ex.what()); return OrthancPluginErrorCode_StorageAreaPlugin; } } else { reader->Read(*(reinterpret_cast<char**>(content)), fileSize); } // transmit ownership to content freeContent.Release(); } catch (StoragePluginException& ex) { if (migrationFromFileSystemEnabled) { try { OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + ex.what() + ", will now try to read it from legacy orthanc storage"); std::string path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath); std::string stringBuffer; Orthanc::SystemToolbox::ReadFile(stringBuffer, path); *content = malloc(static_cast<uint64_t>(stringBuffer.size())); ScopedFree freeContent(*content); if (*content == nullptr) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ", cannot allocate memory of size " + boost::lexical_cast<std::string>(*size) + " bytes"); return OrthancPluginErrorCode_StorageAreaPlugin; } *size = stringBuffer.size(); memcpy(*content, stringBuffer.data(), stringBuffer.size()); // transmit ownership to content freeContent.Release(); return OrthancPluginErrorCode_Success; } catch(Orthanc::OrthancException& e) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + std::string(e.What())); return OrthancPluginErrorCode_StorageAreaPlugin; } } else { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + ex.what()); return OrthancPluginErrorCode_StorageAreaPlugin; } } return OrthancPluginErrorCode_Success; } static OrthancPluginErrorCode StorageRemove(const char* uuid, OrthancPluginContentType type) { try { plugin->DeleteObject(uuid, type, cryptoEnabled); } catch (StoragePluginException& ex) { if (migrationFromFileSystemEnabled) { try { OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + ex.what() + ", will now try to delete it from legacy orthanc storage"); namespace fs = boost::filesystem; fs::path path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath); try { fs::remove(path); } catch (...) { // Ignore the error } // Remove the two parent directories, ignoring the error code if // these directories are not empty try { boost::system::error_code err; fs::remove(path.parent_path(), err); fs::remove(path.parent_path().parent_path(), err); } catch (...) { // Ignore the error } return OrthancPluginErrorCode_Success; } catch(Orthanc::OrthancException& e) { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + std::string(e.What())); return OrthancPluginErrorCode_StorageAreaPlugin; } } else { OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + ex.what()); return OrthancPluginErrorCode_StorageAreaPlugin; } } return OrthancPluginErrorCode_Success; } extern "C" { ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) { OrthancPlugins::SetGlobalContext(context); Orthanc::Logging::InitializePluginContext(context); OrthancPlugins::OrthancConfiguration orthancConfig; OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + " plugin is initializing"); /* Check the version of the Orthanc core */ if (OrthancPluginCheckVersion(context) == 0) { char info[1024]; sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", context->orthancVersion, ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); OrthancPlugins::LogError(info); return -1; } plugin.reset(StoragePluginFactory::CreateStoragePlugin(orthancConfig)); if (plugin.get() == nullptr) { return -1; } const char* pluginSectionName = plugin->GetConfigurationSectionName(); static const char* const ENCRYPTION_SECTION = "StorageEncryption"; if (orthancConfig.IsSection(pluginSectionName)) { OrthancPlugins::OrthancConfiguration pluginSection; orthancConfig.GetSection(pluginSection, pluginSectionName); migrationFromFileSystemEnabled = pluginSection.GetBooleanValue("MigrationFromFileSystemEnabled", false); if (migrationFromFileSystemEnabled) { fileSystemRootPath = orthancConfig.GetStringValue("StorageDirectory", "OrthancStorageNotDefined"); OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": migration from file system enabled, source: " + fileSystemRootPath); } objectsRootPath = pluginSection.GetStringValue("RootPath", std::string()); plugin->SetRootPath(objectsRootPath); if (pluginSection.IsSection(ENCRYPTION_SECTION)) { OrthancPlugins::OrthancConfiguration cryptoSection; pluginSection.GetSection(cryptoSection, ENCRYPTION_SECTION); crypto.reset(EncryptionConfigurator::CreateEncryptionHelpers(cryptoSection)); cryptoEnabled = crypto.get() != nullptr; } if (cryptoEnabled) { OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": client-side encryption is enabled"); } else { OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": client-side encryption is disabled"); } } OrthancPluginRegisterStorageArea(context, StorageCreate, StorageRead, StorageRemove); return 0; } ORTHANC_PLUGINS_API void OrthancPluginFinalize() { OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + " plugin is finalizing"); } ORTHANC_PLUGINS_API const char* OrthancPluginGetName() { return StoragePluginFactory::GetStoragePluginName(); } ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() { return PLUGIN_VERSION; } }