comparison Common/StoragePlugin.cpp @ 77:80792bb9600e

new HybridMode
author Alain Mazy <am@osimis.io>
date Fri, 14 Oct 2022 10:36:02 +0200
parents ba1be668e475
children d7295e8678d7
comparison
equal deleted inserted replaced
75:ac596874d997 77:80792bb9600e
38 #include <boost/filesystem.hpp> 38 #include <boost/filesystem.hpp>
39 #include <boost/filesystem/fstream.hpp> 39 #include <boost/filesystem/fstream.hpp>
40 40
41 #include "../Common/EncryptionHelpers.h" 41 #include "../Common/EncryptionHelpers.h"
42 #include "../Common/EncryptionConfigurator.h" 42 #include "../Common/EncryptionConfigurator.h"
43 #include "../Common/FileSystemStoragePlugin.h"
43 44
44 #include <Logging.h> 45 #include <Logging.h>
45 #include <SystemToolbox.h> 46 #include <SystemToolbox.h>
46 47
47 static std::unique_ptr<IStoragePlugin> plugin; 48 static std::unique_ptr<IStoragePlugin> primaryPlugin;
49 static std::unique_ptr<IStoragePlugin> secondaryPlugin;
48 50
49 static std::unique_ptr<EncryptionHelpers> crypto; 51 static std::unique_ptr<EncryptionHelpers> crypto;
50 static bool cryptoEnabled = false; 52 static bool cryptoEnabled = false;
51 static std::string fileSystemRootPath; 53 static std::string fileSystemRootPath;
52 static bool migrationFromFileSystemEnabled = false;
53 static std::string objectsRootPath; 54 static std::string objectsRootPath;
55 static std::string hybridModeNameForLogs = "";
56
57 typedef enum
58 {
59 HybridMode_WriteToFileSystem, // write to disk, try to read first from disk and then, from object-storage
60 HybridMode_WriteToObjectStorage, // write to object storage, try to read first from object storage and then, from disk
61 HybridMode_Disabled // read and write only from/to object-storage
62 } HybridMode;
63
64 static HybridMode hybridMode = HybridMode_Disabled;
65
66 static bool IsReadFromDisk()
67 {
68 return hybridMode != HybridMode_Disabled;
69 }
70
71 static bool IsHybridModeEnabled()
72 {
73 return hybridMode != HybridMode_Disabled;
74 }
75
76 typedef void LogErrorFunction(const std::string& message);
77
54 78
55 79
56 static OrthancPluginErrorCode StorageCreate(const char* uuid, 80 static OrthancPluginErrorCode StorageCreate(const char* uuid,
57 const void* content, 81 const void* content,
58 int64_t size, 82 int64_t size,
59 OrthancPluginContentType type) 83 OrthancPluginContentType type)
60 { 84 {
61 try 85 try
62 { 86 {
63 std::unique_ptr<IStoragePlugin::IWriter> writer(plugin->GetWriterForObject(uuid, type, cryptoEnabled)); 87 OrthancPlugins::LogInfo(primaryPlugin->GetNameForLogs() + ": creating attachment " + std::string(uuid) + " of type " + boost::lexical_cast<std::string>(type));
88 std::unique_ptr<IStoragePlugin::IWriter> writer(primaryPlugin->GetWriterForObject(uuid, type, cryptoEnabled));
64 89
65 if (cryptoEnabled) 90 if (cryptoEnabled)
66 { 91 {
67 std::string encryptedFile; 92 std::string encryptedFile;
68 93
70 { 95 {
71 crypto->Encrypt(encryptedFile, (const char*)content, size); 96 crypto->Encrypt(encryptedFile, (const char*)content, size);
72 } 97 }
73 catch (EncryptionException& ex) 98 catch (EncryptionException& ex)
74 { 99 {
75 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while encrypting object " + std::string(uuid) + ": " + ex.what()); 100 OrthancPlugins::LogError(primaryPlugin->GetNameForLogs() + ": error while encrypting object " + std::string(uuid) + ": " + ex.what());
76 return OrthancPluginErrorCode_StorageAreaPlugin; 101 return OrthancPluginErrorCode_StorageAreaPlugin;
77 } 102 }
78 103
79 writer->Write(encryptedFile.data(), encryptedFile.size()); 104 writer->Write(encryptedFile.data(), encryptedFile.size());
80 } 105 }
83 writer->Write(reinterpret_cast<const char*>(content), size); 108 writer->Write(reinterpret_cast<const char*>(content), size);
84 } 109 }
85 } 110 }
86 catch (StoragePluginException& ex) 111 catch (StoragePluginException& ex)
87 { 112 {
88 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while creating object " + std::string(uuid) + ": " + ex.what()); 113 OrthancPlugins::LogError(primaryPlugin->GetNameForLogs() + ": error while creating object " + std::string(uuid) + ": " + ex.what());
89 return OrthancPluginErrorCode_StorageAreaPlugin; 114 return OrthancPluginErrorCode_StorageAreaPlugin;
90 } 115 }
91 116
92 return OrthancPluginErrorCode_Success; 117 return OrthancPluginErrorCode_Success;
93 } 118 }
94 119
120
121 static OrthancPluginErrorCode StorageReadRange(IStoragePlugin* plugin,
122 LogErrorFunction logErrorFunction,
123 OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the range. The memory buffer is allocated and freed by Orthanc. The length of the range of interest corresponds to the size of this buffer.
124 const char* uuid,
125 OrthancPluginContentType type,
126 uint64_t rangeStart)
127 {
128 assert(!cryptoEnabled);
129
130 try
131 {
132 OrthancPlugins::LogInfo(plugin->GetNameForLogs() + ": reading range of attachment " + std::string(uuid) + " of type " + boost::lexical_cast<std::string>(type));
133
134 std::unique_ptr<IStoragePlugin::IReader> reader(plugin->GetReaderForObject(uuid, type, cryptoEnabled));
135 reader->ReadRange(reinterpret_cast<char*>(target->data), target->size, rangeStart);
136
137 return OrthancPluginErrorCode_Success;
138 }
139 catch (StoragePluginException& ex)
140 {
141 logErrorFunction(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + std::string(ex.what()));
142 return OrthancPluginErrorCode_StorageAreaPlugin;
143 }
144 }
95 145
96 static OrthancPluginErrorCode StorageReadRange(OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the range. The memory buffer is allocated and freed by Orthanc. The length of the range of interest corresponds to the size of this buffer. 146 static OrthancPluginErrorCode StorageReadRange(OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the range. The memory buffer is allocated and freed by Orthanc. The length of the range of interest corresponds to the size of this buffer.
97 const char* uuid, 147 const char* uuid,
98 OrthancPluginContentType type, 148 OrthancPluginContentType type,
99 uint64_t rangeStart) 149 uint64_t rangeStart)
100 { 150 {
101 assert(!cryptoEnabled); 151 OrthancPluginErrorCode res = StorageReadRange(primaryPlugin.get(),
102 152 (IsHybridModeEnabled() ? OrthancPlugins::LogWarning : OrthancPlugins::LogError), // log errors as warning on first try
153 target,
154 uuid,
155 type,
156 rangeStart);
157
158 if (res != OrthancPluginErrorCode_Success && IsHybridModeEnabled())
159 {
160 res = StorageReadRange(secondaryPlugin.get(),
161 OrthancPlugins::LogError, // log errors as errors on second try
162 target,
163 uuid,
164 type,
165 rangeStart);
166 }
167 return res;
168 }
169
170
171
172 static OrthancPluginErrorCode StorageReadWhole(IStoragePlugin* plugin,
173 LogErrorFunction logErrorFunction,
174 OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the file. It must be allocated by the plugin using OrthancPluginCreateMemoryBuffer64(). The core of Orthanc will free it.
175 const char* uuid,
176 OrthancPluginContentType type)
177 {
103 try 178 try
104 { 179 {
180 OrthancPlugins::LogInfo(plugin->GetNameForLogs() + ": reading whole attachment " + std::string(uuid) + " of type " + boost::lexical_cast<std::string>(type));
105 std::unique_ptr<IStoragePlugin::IReader> reader(plugin->GetReaderForObject(uuid, type, cryptoEnabled)); 181 std::unique_ptr<IStoragePlugin::IReader> reader(plugin->GetReaderForObject(uuid, type, cryptoEnabled));
106 reader->ReadRange(reinterpret_cast<char*>(target->data), target->size, rangeStart); 182
183 size_t fileSize = reader->GetSize();
184 size_t size;
185
186 if (cryptoEnabled)
187 {
188 size = fileSize - crypto->OVERHEAD_SIZE;
189 }
190 else
191 {
192 size = fileSize;
193 }
194
195 if (size <= 0)
196 {
197 logErrorFunction(plugin->GetNameForLogs() + ": error while reading object " + std::string(uuid) + ", size of file is too small: " + boost::lexical_cast<std::string>(fileSize) + " bytes");
198 return OrthancPluginErrorCode_StorageAreaPlugin;
199 }
200
201 if (OrthancPluginCreateMemoryBuffer64(OrthancPlugins::GetGlobalContext(), target, size) != OrthancPluginErrorCode_Success)
202 {
203 logErrorFunction(plugin->GetNameForLogs() + ": error while reading object " + std::string(uuid) + ", cannot allocate memory of size " + boost::lexical_cast<std::string>(size) + " bytes");
204 return OrthancPluginErrorCode_StorageAreaPlugin;
205 }
206
207 if (cryptoEnabled)
208 {
209 std::vector<char> encrypted(fileSize);
210 reader->ReadWhole(encrypted.data(), fileSize);
211
212 try
213 {
214 crypto->Decrypt(reinterpret_cast<char*>(target->data), encrypted.data(), fileSize);
215 }
216 catch (EncryptionException& ex)
217 {
218 logErrorFunction(plugin->GetNameForLogs() + ": error while decrypting object " + std::string(uuid) + ": " + ex.what());
219 return OrthancPluginErrorCode_StorageAreaPlugin;
220 }
221 }
222 else
223 {
224 reader->ReadWhole(reinterpret_cast<char*>(target->data), fileSize);
225 }
107 } 226 }
108 catch (StoragePluginException& ex) 227 catch (StoragePluginException& ex)
109 { 228 {
110 if (migrationFromFileSystemEnabled) 229 logErrorFunction(plugin->GetNameForLogs() + ": error while decrypting object " + std::string(uuid) + ": " + ex.what());
111 { 230 return OrthancPluginErrorCode_StorageAreaPlugin;
112 try 231 }
113 { 232
114 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");
115 std::string path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath);
116
117 std::string stringBuffer;
118 Orthanc::SystemToolbox::ReadFileRange(stringBuffer, path, rangeStart, rangeStart + target->size, true);
119
120 memcpy(target->data, stringBuffer.data(), stringBuffer.size());
121
122 return OrthancPluginErrorCode_Success;
123 }
124 catch(Orthanc::OrthancException& e)
125 {
126 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + std::string(e.What()));
127 return OrthancPluginErrorCode_StorageAreaPlugin;
128 }
129 }
130 }
131 return OrthancPluginErrorCode_Success; 233 return OrthancPluginErrorCode_Success;
132 } 234 }
133
134 235
135 static OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the file. It must be allocated by the plugin using OrthancPluginCreateMemoryBuffer64(). The core of Orthanc will free it. 236 static OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64* target, // Memory buffer where to store the content of the file. It must be allocated by the plugin using OrthancPluginCreateMemoryBuffer64(). The core of Orthanc will free it.
136 const char* uuid, 237 const char* uuid,
137 OrthancPluginContentType type) 238 OrthancPluginContentType type)
138 { 239 {
139 try 240 OrthancPluginErrorCode res = StorageReadWhole(primaryPlugin.get(),
140 { 241 (IsHybridModeEnabled() ? OrthancPlugins::LogWarning : OrthancPlugins::LogError), // log errors as warning on first try
141 std::unique_ptr<IStoragePlugin::IReader> reader(plugin->GetReaderForObject(uuid, type, cryptoEnabled)); 242 target,
142 243 uuid,
143 size_t fileSize = reader->GetSize(); 244 type);
144 size_t size; 245
145 246 if (res != OrthancPluginErrorCode_Success && IsHybridModeEnabled())
146 if (cryptoEnabled) 247 {
147 { 248 res = StorageReadWhole(secondaryPlugin.get(),
148 size = fileSize - crypto->OVERHEAD_SIZE; 249 OrthancPlugins::LogError, // log errors as errors on second try
149 } 250 target,
150 else 251 uuid,
151 { 252 type);
152 size = fileSize; 253 }
153 } 254 return res;
154
155 if (size <= 0)
156 {
157 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");
158 return OrthancPluginErrorCode_StorageAreaPlugin;
159 }
160
161 if (OrthancPluginCreateMemoryBuffer64(OrthancPlugins::GetGlobalContext(), target, size) != OrthancPluginErrorCode_Success)
162 {
163 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");
164 return OrthancPluginErrorCode_StorageAreaPlugin;
165 }
166
167 if (cryptoEnabled)
168 {
169 std::vector<char> encrypted(fileSize);
170 reader->ReadWhole(encrypted.data(), fileSize);
171
172 try
173 {
174 crypto->Decrypt(reinterpret_cast<char*>(target->data), encrypted.data(), fileSize);
175 }
176 catch (EncryptionException& ex)
177 {
178 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while decrypting object " + std::string(uuid) + ": " + ex.what());
179 return OrthancPluginErrorCode_StorageAreaPlugin;
180 }
181 }
182 else
183 {
184 reader->ReadWhole(reinterpret_cast<char*>(target->data), fileSize);
185 }
186 }
187 catch (StoragePluginException& ex)
188 {
189 if (migrationFromFileSystemEnabled)
190 {
191 try
192 {
193 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");
194 std::string path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath);
195
196 std::string stringBuffer;
197 Orthanc::SystemToolbox::ReadFile(stringBuffer, path);
198
199 if (OrthancPluginCreateMemoryBuffer64(OrthancPlugins::GetGlobalContext(), target, stringBuffer.size()) != OrthancPluginErrorCode_Success)
200 {
201 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ", cannot allocate memory of size " + boost::lexical_cast<std::string>(stringBuffer.size()) + " bytes");
202 return OrthancPluginErrorCode_StorageAreaPlugin;
203 }
204
205 memcpy(target->data, stringBuffer.data(), stringBuffer.size());
206
207 return OrthancPluginErrorCode_Success;
208 }
209 catch(Orthanc::OrthancException& e)
210 {
211 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + std::string(e.What()));
212 return OrthancPluginErrorCode_StorageAreaPlugin;
213 }
214 }
215 else
216 {
217 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while reading object " + std::string(uuid) + ": " + ex.what());
218 return OrthancPluginErrorCode_StorageAreaPlugin;
219 }
220 }
221
222 return OrthancPluginErrorCode_Success;
223 } 255 }
224 256
225 static OrthancPluginErrorCode StorageReadWholeLegacy(void** content, 257 static OrthancPluginErrorCode StorageReadWholeLegacy(void** content,
226 int64_t* size, 258 int64_t* size,
227 const char* uuid, 259 const char* uuid,
238 270
239 return result; 271 return result;
240 } 272 }
241 273
242 274
275 // static bool StorageRemoveFromDisk(const char* uuid,
276 // OrthancPluginContentType type)
277 // {
278 // try
279 // {
280 // namespace fs = boost::filesystem;
281 // bool fileExisted = false;
282 // fs::path path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath);
283
284 // try
285 // {
286 // fs::remove(path);
287 // fileExisted = true;
288 // }
289 // catch (...)
290 // {
291 // // Ignore the error
292 // fileExisted = false;
293 // }
294
295 // // Remove the two parent directories, ignoring the error code if
296 // // these directories are not empty
297
298 // try
299 // {
300 // boost::system::error_code err;
301 // fs::remove(path.parent_path(), err);
302 // fs::remove(path.parent_path().parent_path(), err);
303 // }
304 // catch (...)
305 // {
306 // // Ignore the error
307 // }
308
309 // return fileExisted;
310 // }
311 // catch(Orthanc::OrthancException& e)
312 // {
313 // OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + std::string(e.What()));
314 // return false;
315 // }
316
317 // }
318
319
320 static OrthancPluginErrorCode StorageRemove(IStoragePlugin* plugin,
321 LogErrorFunction logErrorFunction,
322 const char* uuid,
323 OrthancPluginContentType type)
324 {
325 try
326 {
327 OrthancPlugins::LogInfo(plugin->GetNameForLogs() + ": deleting attachment " + std::string(uuid) + " of type " + boost::lexical_cast<std::string>(type));
328 plugin->DeleteObject(uuid, type, cryptoEnabled);
329 if ((plugin == primaryPlugin.get()) && IsHybridModeEnabled())
330 {
331 // not 100% sure the file has been deleted, try the secondary plugin
332 return OrthancPluginErrorCode_StorageAreaPlugin;
333 }
334
335 return OrthancPluginErrorCode_Success;
336 }
337 catch (StoragePluginException& ex)
338 {
339 logErrorFunction(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + std::string(ex.what()));
340 return OrthancPluginErrorCode_StorageAreaPlugin;
341 }
342 }
343
243 static OrthancPluginErrorCode StorageRemove(const char* uuid, 344 static OrthancPluginErrorCode StorageRemove(const char* uuid,
244 OrthancPluginContentType type) 345 OrthancPluginContentType type)
245 { 346 {
246 try 347 OrthancPluginErrorCode res = StorageRemove(primaryPlugin.get(),
247 { 348 (IsHybridModeEnabled() ? OrthancPlugins::LogWarning : OrthancPlugins::LogError), // log errors as warning on first try
248 plugin->DeleteObject(uuid, type, cryptoEnabled); 349 uuid,
249 } 350 type);
250 catch (StoragePluginException& ex) 351
251 { 352 if (res != OrthancPluginErrorCode_Success && IsHybridModeEnabled())
252 if (migrationFromFileSystemEnabled) 353 {
253 { 354 res = StorageRemove(secondaryPlugin.get(),
254 try 355 OrthancPlugins::LogError, // log errors as errors on second try
255 { 356 uuid,
256 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"); 357 type);
257 namespace fs = boost::filesystem; 358 }
258 359 return res;
259 fs::path path = BaseStoragePlugin::GetOrthancFileSystemPath(uuid, fileSystemRootPath);
260
261 try
262 {
263 fs::remove(path);
264 }
265 catch (...)
266 {
267 // Ignore the error
268 }
269
270 // Remove the two parent directories, ignoring the error code if
271 // these directories are not empty
272
273 try
274 {
275 boost::system::error_code err;
276 fs::remove(path.parent_path(), err);
277 fs::remove(path.parent_path().parent_path(), err);
278 }
279 catch (...)
280 {
281 // Ignore the error
282 }
283
284 return OrthancPluginErrorCode_Success;
285 }
286 catch(Orthanc::OrthancException& e)
287 {
288 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + std::string(e.What()));
289 return OrthancPluginErrorCode_StorageAreaPlugin;
290 }
291 }
292 else
293 {
294 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": error while deleting object " + std::string(uuid) + ": " + ex.what());
295 return OrthancPluginErrorCode_StorageAreaPlugin;
296 }
297 }
298
299 return OrthancPluginErrorCode_Success;
300 } 360 }
301 361
302 362
303 extern "C" 363 extern "C"
304 { 364 {
326 return -1; 386 return -1;
327 } 387 }
328 388
329 try 389 try
330 { 390 {
331 plugin.reset(StoragePluginFactory::CreateStoragePlugin(orthancConfig)); 391 const char* pluginSectionName = StoragePluginFactory::GetConfigurationSectionName();
332
333 if (plugin.get() == nullptr)
334 {
335 return -1;
336 }
337
338 const char* pluginSectionName = plugin->GetConfigurationSectionName();
339 static const char* const ENCRYPTION_SECTION = "StorageEncryption"; 392 static const char* const ENCRYPTION_SECTION = "StorageEncryption";
340 393
341 if (orthancConfig.IsSection(pluginSectionName)) 394 if (orthancConfig.IsSection(pluginSectionName))
342 { 395 {
343 OrthancPlugins::OrthancConfiguration pluginSection; 396 OrthancPlugins::OrthancConfiguration pluginSection;
344 orthancConfig.GetSection(pluginSection, pluginSectionName); 397 orthancConfig.GetSection(pluginSection, pluginSectionName);
345 398
346 migrationFromFileSystemEnabled = pluginSection.GetBooleanValue("MigrationFromFileSystemEnabled", false); 399 bool migrationFromFileSystemEnabled = pluginSection.GetBooleanValue("MigrationFromFileSystemEnabled", false);
347 400 std::string hybridModeString = pluginSection.GetStringValue("HybridMode", "Disabled");
348 if (migrationFromFileSystemEnabled) 401
402 if (migrationFromFileSystemEnabled && hybridModeString == "Disabled")
403 {
404 hybridMode = HybridMode_WriteToObjectStorage;
405 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": 'MigrationFromFileSystemEnabled' configuration is deprecated, use 'HybridMode': 'WriteToObjectStorage' instead");
406 }
407 else if (hybridModeString == "WriteToObjectStorage")
408 {
409 hybridMode = HybridMode_WriteToObjectStorage;
410 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": WriteToObjectStorage HybridMode is enabled: writing to object-storage, try reading first from object-storage and, then, from file system");
411 }
412 else if (hybridModeString == "WriteToFileSystem")
413 {
414 hybridMode = HybridMode_WriteToFileSystem;
415 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": WriteToFileSystem HybridMode is enabled: writing to file system, try reading first from file system and, then, from object-storage");
416 }
417 else
418 {
419 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": HybridMode is disabled enabled: writing to object-storage and reading only from object-storage");
420 }
421
422 if (IsReadFromDisk())
349 { 423 {
350 fileSystemRootPath = orthancConfig.GetStringValue("StorageDirectory", "OrthancStorageNotDefined"); 424 fileSystemRootPath = orthancConfig.GetStringValue("StorageDirectory", "OrthancStorageNotDefined");
351 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": migration from file system enabled, source: " + fileSystemRootPath); 425 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": HybridMode: reading from file system is enabled, source: " + fileSystemRootPath);
352 } 426 }
353 427
354 objectsRootPath = pluginSection.GetStringValue("RootPath", std::string()); 428 objectsRootPath = pluginSection.GetStringValue("RootPath", std::string());
355 429
356 if (objectsRootPath.size() >= 1 && objectsRootPath[0] == '/') 430 if (objectsRootPath.size() >= 1 && objectsRootPath[0] == '/')
357 { 431 {
358 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": The RootPath shall not start with a '/': " + objectsRootPath); 432 OrthancPlugins::LogError(std::string(StoragePluginFactory::GetStoragePluginName()) + ": The RootPath shall not start with a '/': " + objectsRootPath);
359 return -1; 433 return -1;
360 } 434 }
361 435
362 plugin->SetRootPath(objectsRootPath); 436 std::string objecstStoragePluginName = StoragePluginFactory::GetStoragePluginName();
437 if (hybridMode == HybridMode_WriteToFileSystem)
438 {
439 objecstStoragePluginName += " (Secondary: object-storage)";
440 }
441 else if (hybridMode == HybridMode_WriteToObjectStorage)
442 {
443 objecstStoragePluginName += " (Primary: object-storage)";
444 }
445
446 std::unique_ptr<IStoragePlugin> objectStoragePlugin(StoragePluginFactory::CreateStoragePlugin(objecstStoragePluginName, orthancConfig));
447
448 if (objectStoragePlugin.get() == nullptr)
449 {
450 return -1;
451 }
452
453 objectStoragePlugin->SetRootPath(objectsRootPath);
454
455 std::unique_ptr<IStoragePlugin> fileSystemStoragePlugin;
456 if (IsHybridModeEnabled())
457 {
458 bool fsync = orthancConfig.GetBooleanValue("SyncStorageArea", true);
459
460 std::string filesystemStoragePluginName = StoragePluginFactory::GetStoragePluginName();
461 if (hybridMode == HybridMode_WriteToFileSystem)
462 {
463 filesystemStoragePluginName += " (Primary: file-system)";
464 }
465 else if (hybridMode == HybridMode_WriteToObjectStorage)
466 {
467 filesystemStoragePluginName += " (Secondary: file-system)";
468 }
469
470 fileSystemStoragePlugin.reset(new FileSystemStoragePlugin(filesystemStoragePluginName, fileSystemRootPath, fsync));
471 }
472
473 if (hybridMode == HybridMode_Disabled || hybridMode == HybridMode_WriteToObjectStorage)
474 {
475 primaryPlugin.reset(objectStoragePlugin.release());
476
477 if (hybridMode == HybridMode_WriteToObjectStorage)
478 {
479 secondaryPlugin.reset(fileSystemStoragePlugin.release());
480 }
481 }
482 else if (hybridMode == HybridMode_WriteToFileSystem)
483 {
484 primaryPlugin.reset(fileSystemStoragePlugin.release());
485 secondaryPlugin.reset(objectStoragePlugin.release());
486 }
363 487
364 if (pluginSection.IsSection(ENCRYPTION_SECTION)) 488 if (pluginSection.IsSection(ENCRYPTION_SECTION))
365 { 489 {
366 OrthancPlugins::OrthancConfiguration cryptoSection; 490 OrthancPlugins::OrthancConfiguration cryptoSection;
367 pluginSection.GetSection(cryptoSection, ENCRYPTION_SECTION); 491 pluginSection.GetSection(cryptoSection, ENCRYPTION_SECTION);
376 } 500 }
377 else 501 else
378 { 502 {
379 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": client-side encryption is disabled"); 503 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + ": client-side encryption is disabled");
380 } 504 }
505
506
381 } 507 }
382 508
383 if (cryptoEnabled) 509 if (cryptoEnabled)
384 { 510 {
385 // with encrypted file, we do not want to support ReadRange. Therefore, we register the old interface 511 // with encrypted file, we do not want to support ReadRange. Therefore, we register the old interface
401 527
402 528
403 ORTHANC_PLUGINS_API void OrthancPluginFinalize() 529 ORTHANC_PLUGINS_API void OrthancPluginFinalize()
404 { 530 {
405 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + " plugin is finalizing"); 531 OrthancPlugins::LogWarning(std::string(StoragePluginFactory::GetStoragePluginName()) + " plugin is finalizing");
406 plugin.reset(); 532 primaryPlugin.reset();
533 secondaryPlugin.reset();
407 Orthanc::FinalizeFramework(); 534 Orthanc::FinalizeFramework();
408 } 535 }
409 536
410 537
411 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() 538 ORTHANC_PLUGINS_API const char* OrthancPluginGetName()