Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "zarr.h"
14 :
15 : #include <algorithm>
16 : #include <cassert>
17 : #include <limits>
18 : #include <map>
19 : #include <set>
20 :
21 : /************************************************************************/
22 : /* ~ZarrGroupBase() */
23 : /************************************************************************/
24 :
25 2417 : ZarrGroupBase::~ZarrGroupBase()
26 : {
27 2417 : CPL_IGNORE_RET_VAL(ZarrGroupBase::Close());
28 2417 : }
29 :
30 : /************************************************************************/
31 : /* Close() */
32 : /************************************************************************/
33 :
34 8009 : bool ZarrGroupBase::Close()
35 : {
36 8009 : bool ret = true;
37 :
38 9302 : for (auto &kv : m_oMapGroups)
39 : {
40 1293 : ret = kv.second->Close() && ret;
41 : }
42 :
43 16027 : for (auto &kv : m_oMapMDArrays)
44 : {
45 8018 : ret = kv.second->Flush() && ret;
46 : }
47 8009 : return ret;
48 : }
49 :
50 : /************************************************************************/
51 : /* Flush() */
52 : /************************************************************************/
53 :
54 42 : bool ZarrGroupBase::Flush()
55 : {
56 42 : bool ret = true;
57 :
58 169 : for (auto &kv : m_oMapMDArrays)
59 : {
60 127 : ret = kv.second->Flush() && ret;
61 : }
62 42 : return ret;
63 : }
64 :
65 : /************************************************************************/
66 : /* GetMDArrayNames() */
67 : /************************************************************************/
68 :
69 1446 : std::vector<std::string> ZarrGroupBase::GetMDArrayNames(CSLConstList) const
70 : {
71 1446 : if (!CheckValidAndErrorOutIfNot())
72 0 : return {};
73 :
74 1446 : if (!m_bDirectoryExplored)
75 354 : ExploreDirectory();
76 :
77 1446 : return m_aosArrays;
78 : }
79 :
80 : /************************************************************************/
81 : /* RegisterArray() */
82 : /************************************************************************/
83 :
84 2414 : void ZarrGroupBase::RegisterArray(const std::shared_ptr<ZarrArray> &array) const
85 : {
86 2414 : m_oMapMDArrays[array->GetName()] = array;
87 2414 : if (!cpl::contains(m_oSetArrayNames, array->GetName()))
88 : {
89 2324 : m_oSetArrayNames.insert(array->GetName());
90 2324 : m_aosArrays.emplace_back(array->GetName());
91 : }
92 4828 : array->RegisterGroup(
93 4828 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()));
94 2414 : }
95 :
96 : /************************************************************************/
97 : /* GetGroupNames() */
98 : /************************************************************************/
99 :
100 419 : std::vector<std::string> ZarrGroupBase::GetGroupNames(CSLConstList) const
101 : {
102 419 : if (!CheckValidAndErrorOutIfNot())
103 0 : return {};
104 :
105 419 : if (!m_bDirectoryExplored)
106 38 : ExploreDirectory();
107 :
108 419 : return m_aosGroups;
109 : }
110 :
111 : /************************************************************************/
112 : /* DeleteGroup() */
113 : /************************************************************************/
114 :
115 20 : bool ZarrGroupBase::DeleteGroup(const std::string &osName,
116 : CSLConstList /*papszOptions*/)
117 : {
118 20 : if (!CheckValidAndErrorOutIfNot())
119 0 : return false;
120 :
121 20 : if (!m_bUpdatable)
122 : {
123 6 : CPLError(CE_Failure, CPLE_NotSupported,
124 : "Dataset not open in update mode");
125 6 : return false;
126 : }
127 14 : if (CPLHasPathTraversal(osName.c_str()))
128 : {
129 0 : CPLError(CE_Failure, CPLE_AppDefined, "Path traversal detected in %s",
130 : osName.c_str());
131 0 : return false;
132 : }
133 14 : GetGroupNames();
134 :
135 14 : auto oIterNames = std::find(m_aosGroups.begin(), m_aosGroups.end(), osName);
136 14 : if (oIterNames == m_aosGroups.end())
137 : {
138 6 : CPLError(CE_Failure, CPLE_AppDefined,
139 : "Group %s is not a sub-group of this group", osName.c_str());
140 6 : return false;
141 : }
142 :
143 : const std::string osSubDirName =
144 16 : CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
145 8 : if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
146 : {
147 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
148 : osSubDirName.c_str());
149 0 : return false;
150 : }
151 :
152 8 : m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
153 :
154 8 : m_oSetGroupNames.erase(osName);
155 8 : m_aosGroups.erase(oIterNames);
156 :
157 8 : auto oIter = m_oMapGroups.find(osName);
158 8 : if (oIter != m_oMapGroups.end())
159 : {
160 6 : oIter->second->Deleted();
161 6 : m_oMapGroups.erase(oIter);
162 : }
163 :
164 8 : return true;
165 : }
166 :
167 : /************************************************************************/
168 : /* NotifyChildrenOfDeletion() */
169 : /************************************************************************/
170 :
171 8 : void ZarrGroupBase::NotifyChildrenOfDeletion()
172 : {
173 10 : for (const auto &oIter : m_oMapGroups)
174 2 : oIter.second->ParentDeleted();
175 :
176 14 : for (const auto &oIter : m_oMapMDArrays)
177 6 : oIter.second->ParentDeleted();
178 :
179 8 : m_oAttrGroup.ParentDeleted();
180 :
181 16 : for (const auto &oIter : m_oMapDimensions)
182 8 : oIter.second->ParentDeleted();
183 8 : }
184 :
185 : /************************************************************************/
186 : /* ZarrGroupBase::CreateAttribute() */
187 : /************************************************************************/
188 :
189 95 : std::shared_ptr<GDALAttribute> ZarrGroupBase::CreateAttribute(
190 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
191 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
192 : {
193 95 : if (!CheckValidAndErrorOutIfNot())
194 0 : return nullptr;
195 :
196 95 : if (!m_bUpdatable)
197 : {
198 6 : CPLError(CE_Failure, CPLE_NotSupported,
199 : "Dataset not open in update mode");
200 6 : return nullptr;
201 : }
202 89 : if (anDimensions.size() >= 2)
203 : {
204 4 : CPLError(CE_Failure, CPLE_NotSupported,
205 : "Cannot create attributes of dimension >= 2");
206 4 : return nullptr;
207 : }
208 85 : LoadAttributes();
209 : return m_oAttrGroup.CreateAttribute(osName, anDimensions, oDataType,
210 85 : papszOptions);
211 : }
212 :
213 : /************************************************************************/
214 : /* ZarrGroupBase::DeleteAttribute() */
215 : /************************************************************************/
216 :
217 18 : bool ZarrGroupBase::DeleteAttribute(const std::string &osName, CSLConstList)
218 : {
219 18 : if (!CheckValidAndErrorOutIfNot())
220 0 : return false;
221 :
222 18 : if (!m_bUpdatable)
223 : {
224 6 : CPLError(CE_Failure, CPLE_NotSupported,
225 : "Dataset not open in update mode");
226 6 : return false;
227 : }
228 :
229 12 : LoadAttributes();
230 12 : return m_oAttrGroup.DeleteAttribute(osName);
231 : }
232 :
233 : /************************************************************************/
234 : /* GetDimensions() */
235 : /************************************************************************/
236 :
237 : std::vector<std::shared_ptr<GDALDimension>>
238 748 : ZarrGroupBase::GetDimensions(CSLConstList) const
239 : {
240 748 : if (!CheckValidAndErrorOutIfNot())
241 0 : return {};
242 :
243 748 : if (!m_bReadFromConsolidatedMetadata && !m_bDimensionsInstantiated)
244 : {
245 347 : m_bDimensionsInstantiated = true;
246 : // We need to instantiate arrays to discover dimensions
247 694 : const auto aosArrays = GetMDArrayNames();
248 381 : for (const auto &osArray : aosArrays)
249 : {
250 34 : OpenMDArray(osArray);
251 : }
252 : }
253 :
254 1496 : std::vector<std::shared_ptr<GDALDimension>> oRes;
255 1311 : for (const auto &oIter : m_oMapDimensions)
256 : {
257 563 : oRes.push_back(oIter.second);
258 : }
259 748 : return oRes;
260 : }
261 :
262 : /************************************************************************/
263 : /* DeleteMDArray() */
264 : /************************************************************************/
265 :
266 18 : bool ZarrGroupBase::DeleteMDArray(const std::string &osName,
267 : CSLConstList /*papszOptions*/)
268 : {
269 18 : if (!CheckValidAndErrorOutIfNot())
270 0 : return false;
271 :
272 18 : if (!m_bUpdatable)
273 : {
274 6 : CPLError(CE_Failure, CPLE_NotSupported,
275 : "Dataset not open in update mode");
276 6 : return false;
277 : }
278 12 : if (CPLHasPathTraversal(osName.c_str()))
279 : {
280 0 : CPLError(CE_Failure, CPLE_AppDefined, "Path traversal detected in %s",
281 : osName.c_str());
282 0 : return false;
283 : }
284 12 : GetMDArrayNames();
285 :
286 12 : auto oIterNames = std::find(m_aosArrays.begin(), m_aosArrays.end(), osName);
287 12 : if (oIterNames == m_aosArrays.end())
288 : {
289 6 : CPLError(CE_Failure, CPLE_AppDefined,
290 : "Array %s is not an array of this group", osName.c_str());
291 6 : return false;
292 : }
293 :
294 : const std::string osSubDirName =
295 12 : CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
296 6 : if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
297 : {
298 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
299 : osSubDirName.c_str());
300 0 : return false;
301 : }
302 :
303 6 : m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
304 :
305 6 : m_oSetArrayNames.erase(osName);
306 6 : m_aosArrays.erase(oIterNames);
307 :
308 6 : auto oIter = m_oMapMDArrays.find(osName);
309 6 : if (oIter != m_oMapMDArrays.end())
310 : {
311 4 : oIter->second->Deleted();
312 4 : m_oMapMDArrays.erase(oIter);
313 : }
314 :
315 6 : return true;
316 : }
317 :
318 : /************************************************************************/
319 : /* CreateDimension() */
320 : /************************************************************************/
321 :
322 670 : std::shared_ptr<GDALDimension> ZarrGroupBase::CreateDimension(
323 : const std::string &osName, const std::string &osType,
324 : const std::string &osDirection, GUInt64 nSize, CSLConstList)
325 : {
326 670 : if (!CheckValidAndErrorOutIfNot())
327 0 : return nullptr;
328 :
329 670 : if (osName.empty())
330 : {
331 0 : CPLError(CE_Failure, CPLE_NotSupported,
332 : "Empty dimension name not supported");
333 0 : return nullptr;
334 : }
335 670 : GetDimensions(nullptr);
336 :
337 670 : if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
338 : {
339 0 : CPLError(CE_Failure, CPLE_AppDefined,
340 : "A dimension with same name (%s) already exists in group %s",
341 0 : osName.c_str(), GetFullName().c_str());
342 0 : return nullptr;
343 : }
344 : auto newDim(std::make_shared<ZarrDimension>(
345 670 : m_poSharedResource,
346 1340 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()), GetFullName(),
347 1340 : osName, osType, osDirection, nSize));
348 670 : newDim->SetXArrayDimension();
349 670 : m_oMapDimensions[osName] = newDim;
350 670 : return newDim;
351 : }
352 :
353 : /************************************************************************/
354 : /* RenameDimension() */
355 : /************************************************************************/
356 :
357 12 : bool ZarrGroupBase::RenameDimension(const std::string &osOldName,
358 : const std::string &osNewName)
359 : {
360 12 : if (m_oMapDimensions.find(osNewName) != m_oMapDimensions.end())
361 : {
362 6 : CPLError(CE_Failure, CPLE_AppDefined,
363 : "A dimension with same name already exists");
364 6 : return false;
365 : }
366 6 : auto oIter = m_oMapDimensions.find(osOldName);
367 6 : if (oIter == m_oMapDimensions.end())
368 : {
369 0 : CPLAssert(false);
370 : return false;
371 : }
372 6 : auto poDim = std::move(oIter->second);
373 6 : m_oMapDimensions.erase(oIter);
374 6 : m_oMapDimensions[osNewName] = std::move(poDim);
375 6 : return true;
376 : }
377 :
378 : /************************************************************************/
379 : /* ZarrGroupBase::UpdateDimensionSize() */
380 : /************************************************************************/
381 :
382 7 : void ZarrGroupBase::UpdateDimensionSize(
383 : const std::shared_ptr<GDALDimension> &poUpdatedDim)
384 : {
385 14 : const auto aosGroupNames = GetGroupNames();
386 7 : for (const auto &osGroupName : aosGroupNames)
387 : {
388 0 : auto poSubGroup = OpenZarrGroup(osGroupName);
389 0 : if (poSubGroup)
390 : {
391 0 : poSubGroup->UpdateDimensionSize(poUpdatedDim);
392 : }
393 : }
394 14 : const auto aosArrayNames = GetMDArrayNames();
395 22 : for (const auto &osArrayName : aosArrayNames)
396 : {
397 : // Disable checks that size of variables referenced by _ARRAY_DIMENSIONS
398 : // are consistent with array shapes, as we are in the middle of updating
399 : // things
400 15 : m_bDimSizeInUpdate = true;
401 30 : auto poArray = OpenZarrArray(osArrayName);
402 15 : m_bDimSizeInUpdate = false;
403 15 : if (poArray)
404 : {
405 39 : for (auto &poDim : poArray->GetDimensions())
406 : {
407 24 : if (poDim->GetFullName() == poUpdatedDim->GetFullName())
408 : {
409 : auto poModifiableDim =
410 30 : std::dynamic_pointer_cast<ZarrDimension>(poDim);
411 15 : CPLAssert(poModifiableDim);
412 15 : poModifiableDim->SetSize(poUpdatedDim->GetSize());
413 15 : poArray->SetDefinitionModified(true);
414 : }
415 : }
416 : }
417 : }
418 7 : }
419 :
420 : /************************************************************************/
421 : /* ZarrGroupBase::NotifyArrayRenamed() */
422 : /************************************************************************/
423 :
424 6 : void ZarrGroupBase::NotifyArrayRenamed(const std::string &osOldName,
425 : const std::string &osNewName)
426 : {
427 6 : for (auto &osName : m_aosArrays)
428 : {
429 6 : if (osName == osOldName)
430 : {
431 6 : osName = osNewName;
432 6 : break;
433 : }
434 : }
435 :
436 6 : auto oIter = m_oMapMDArrays.find(osOldName);
437 6 : if (oIter != m_oMapMDArrays.end())
438 : {
439 6 : auto poArray = std::move(oIter->second);
440 6 : m_oMapMDArrays.erase(oIter);
441 6 : m_oMapMDArrays[osNewName] = std::move(poArray);
442 : }
443 6 : }
444 :
445 : /************************************************************************/
446 : /* IsValidObjectName() */
447 : /************************************************************************/
448 :
449 : /* static */
450 772 : bool ZarrGroupBase::IsValidObjectName(const std::string &osName)
451 : {
452 2230 : return !(osName.empty() || osName == "." || osName == ".." ||
453 1458 : osName.find('/') != std::string::npos ||
454 1442 : osName.find('\\') != std::string::npos ||
455 717 : osName.find(':') != std::string::npos ||
456 1481 : STARTS_WITH(osName.c_str(), ".z"));
457 : }
458 :
459 : /************************************************************************/
460 : /* CheckArrayOrGroupWithSameNameDoesNotExist() */
461 : /************************************************************************/
462 :
463 27 : bool ZarrGroupBase::CheckArrayOrGroupWithSameNameDoesNotExist(
464 : const std::string &osName) const
465 : {
466 54 : const auto groupNames = GetGroupNames();
467 27 : if (std::find(groupNames.begin(), groupNames.end(), osName) !=
468 54 : groupNames.end())
469 : {
470 9 : CPLError(CE_Failure, CPLE_AppDefined,
471 : "A group with same name already exists");
472 9 : return false;
473 : }
474 :
475 36 : const auto arrayNames = GetMDArrayNames();
476 18 : if (std::find(arrayNames.begin(), arrayNames.end(), osName) !=
477 36 : arrayNames.end())
478 : {
479 6 : CPLError(CE_Failure, CPLE_AppDefined,
480 : "An array with same name already exists");
481 6 : return false;
482 : }
483 :
484 12 : return true;
485 : }
486 :
487 : /************************************************************************/
488 : /* Rename() */
489 : /************************************************************************/
490 :
491 36 : bool ZarrGroupBase::Rename(const std::string &osNewName)
492 : {
493 36 : if (!CheckValidAndErrorOutIfNot())
494 3 : return false;
495 :
496 33 : if (!m_bUpdatable)
497 : {
498 6 : CPLError(CE_Failure, CPLE_NotSupported,
499 : "Dataset not open in update mode");
500 6 : return false;
501 : }
502 27 : if (!IsValidObjectName(osNewName))
503 : {
504 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid group name");
505 6 : return false;
506 : }
507 21 : if (m_osName == "/")
508 : {
509 6 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot rename root group");
510 6 : return false;
511 : }
512 :
513 30 : auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
514 15 : if (pParent)
515 : {
516 15 : if (!pParent->CheckArrayOrGroupWithSameNameDoesNotExist(osNewName))
517 9 : return false;
518 : }
519 :
520 12 : std::string osNewDirectoryName(m_osDirectoryName);
521 6 : osNewDirectoryName.resize(osNewDirectoryName.size() - m_osName.size());
522 6 : osNewDirectoryName += osNewName;
523 :
524 6 : if (VSIRename(m_osDirectoryName.c_str(), osNewDirectoryName.c_str()) != 0)
525 : {
526 0 : CPLError(CE_Failure, CPLE_AppDefined, "Renaming of %s to %s failed",
527 : m_osDirectoryName.c_str(), osNewDirectoryName.c_str());
528 0 : return false;
529 : }
530 :
531 6 : if (pParent)
532 : {
533 6 : auto oIter = pParent->m_oMapGroups.find(m_osName);
534 6 : if (oIter != pParent->m_oMapGroups.end())
535 : {
536 6 : pParent->m_oMapGroups.erase(oIter);
537 6 : CPLAssert(m_pSelf.lock());
538 12 : pParent->m_oMapGroups[osNewName] =
539 18 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
540 : }
541 :
542 6 : for (auto &osName : pParent->m_aosGroups)
543 : {
544 6 : if (osName == m_osName)
545 : {
546 6 : osName = osNewName;
547 6 : break;
548 : }
549 : }
550 : }
551 :
552 6 : m_poSharedResource->RenameZMetadataRecursive(m_osDirectoryName,
553 : osNewDirectoryName);
554 :
555 6 : m_osDirectoryName = std::move(osNewDirectoryName);
556 :
557 6 : BaseRename(osNewName);
558 :
559 6 : return true;
560 : }
561 :
562 : /************************************************************************/
563 : /* ParentRenamed() */
564 : /************************************************************************/
565 :
566 4 : void ZarrGroupBase::ParentRenamed(const std::string &osNewParentFullName)
567 : {
568 8 : auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
569 : // The parent necessarily exist, since it notified us
570 4 : CPLAssert(pParent);
571 :
572 8 : m_osDirectoryName = CPLFormFilenameSafe(pParent->m_osDirectoryName.c_str(),
573 4 : m_osName.c_str(), nullptr);
574 :
575 4 : GDALGroup::ParentRenamed(osNewParentFullName);
576 4 : }
577 :
578 : /************************************************************************/
579 : /* NotifyChildrenOfRenaming() */
580 : /************************************************************************/
581 :
582 10 : void ZarrGroupBase::NotifyChildrenOfRenaming()
583 : {
584 14 : for (const auto &oIter : m_oMapGroups)
585 4 : oIter.second->ParentRenamed(m_osFullName);
586 :
587 19 : for (const auto &oIter : m_oMapMDArrays)
588 9 : oIter.second->ParentRenamed(m_osFullName);
589 :
590 10 : m_oAttrGroup.ParentRenamed(m_osFullName);
591 :
592 16 : for (const auto &oIter : m_oMapDimensions)
593 6 : oIter.second->ParentRenamed(m_osFullName);
594 10 : }
595 :
596 : /************************************************************************/
597 : /* ZarrGroupBase::GetParentGroup() */
598 : /************************************************************************/
599 :
600 43 : std::shared_ptr<ZarrGroupBase> ZarrGroupBase::GetParentGroup() const
601 : {
602 43 : std::shared_ptr<ZarrGroupBase> poGroup = m_poParent.lock();
603 43 : if (!poGroup)
604 : {
605 2 : if (auto poRootGroup = m_poSharedResource->GetRootGroup())
606 : {
607 1 : const auto nPos = m_osFullName.rfind('/');
608 1 : if (nPos != std::string::npos)
609 : {
610 2 : poGroup = std::dynamic_pointer_cast<ZarrGroupBase>(
611 3 : poRootGroup->OpenGroupFromFullname(m_osFullName.substr(
612 3 : 0, std::max(static_cast<size_t>(1), nPos))));
613 : }
614 : }
615 : }
616 43 : return poGroup;
617 : }
|