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