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