LCOV - code coverage report
Current view: top level - frmts/icechunk - vsiicechunk.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 255 272 93.8 %
Date: 2026-06-19 21:24:00 Functions: 17 18 94.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Icechunk driver
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "icechunkdrivercore.h"
      14             : #include "icechunkutils.h"
      15             : #include "icechunkmanifest.h"
      16             : #include "icechunkrepo.h"
      17             : #include "icechunksnapshot.h"
      18             : 
      19             : #include "cpl_mem_cache.h"
      20             : #include "cpl_vsi_virtual.h"
      21             : 
      22             : #include <cinttypes>
      23             : #include <limits>
      24             : #include <mutex>
      25             : #include <utility>
      26             : 
      27             : namespace gdal::icechunk
      28             : {
      29             : 
      30             : /************************************************************************/
      31             : /*                        VSIIcechunkFileSystem                         */
      32             : /************************************************************************/
      33             : 
      34             : class VSIIcechunkFileSystem final : public VSIFilesystemHandler
      35             : {
      36             :   public:
      37             :     // If the input of SplitFilename() is /vsiicechunk/{/path/to/some/icechunk/repo?branch=my_branch]}/optional_key
      38             :     // - osRootFilenameWithBranchOrTag = "/path/to/some/icechunk/repo?branch=my_branch"
      39             :     // - osRootFilename = "/path/to/some/icechunk/repo"
      40             :     // - osBranchName = "my_branch"
      41             :     // - osKey = "/optional_key"
      42             :     //
      43             :     // If /optional_key is not present, osKey is set to "/"
      44             :     struct Ref
      45             :     {
      46             :         std::string osRootFilenameWithBranchOrTag{};
      47             :         std::string osRootFilename{};
      48             :         std::string osBranchName{};
      49             :         std::string osTagName{};
      50             :         std::string osKey{};
      51             :     };
      52             : 
      53        1872 :     VSIIcechunkFileSystem()
      54        1872 :     {
      55        1872 :         bool *pbInstantiated = &IsFileSystemInstantiated();
      56        1872 :         *pbInstantiated = true;
      57        1872 :     }
      58             : 
      59             :     ~VSIIcechunkFileSystem() override;
      60             : 
      61        5045 :     static bool &IsFileSystemInstantiated()
      62             :     {
      63             :         static bool bIsFileSystemInstantiated = false;
      64        5045 :         return bIsFileSystemInstantiated;
      65             :     }
      66             : 
      67             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
      68             :                                    const char *pszAccess, bool bSetError,
      69             :                                    CSLConstList papszOptions) override;
      70             : 
      71             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
      72             :              int nFlags) override;
      73             : 
      74             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
      75             : 
      76             :     void ClearCaches();
      77             : 
      78             :     char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
      79             :                            CSLConstList papszOptions) override;
      80             : 
      81             :   private:
      82             :     using RepoAndSnapshot = std::pair<std::shared_ptr<IcechunkRepo>,
      83             :                                       std::shared_ptr<IcechunkSnapshot>>;
      84             : 
      85             :     lru11::Cache<std::string, RepoAndSnapshot, std::mutex> m_oCache{};
      86             : 
      87             :     static Ref SplitFilename(const char *pszFilename);
      88             : 
      89             :     RepoAndSnapshot Load(const Ref &ref);
      90             :     std::pair<VSIIcechunkFileSystem::Ref, RepoAndSnapshot>
      91             :     Load(const char *pszFilename);
      92             : 
      93             :     struct FileInfo
      94             :     {
      95             :         bool bIsDir = false;
      96             :         std::string osFilename{};
      97             :         uint64_t nOffset = 0;
      98             :         uint64_t nSize = 0;
      99             :         const void *pabyData = nullptr;
     100             : 
     101             :         // To keep pabyData alive
     102             :         std::shared_ptr<IcechunkFile> dataOwner{};
     103             :     };
     104             : 
     105             :     FileInfo GetFileInfo(const char *pszFilename);
     106             : };
     107             : 
     108             : /************************************************************************/
     109             : /*                       ~VSIIcechunkFileSystem()                       */
     110             : /************************************************************************/
     111             : 
     112        1301 : VSIIcechunkFileSystem::~VSIIcechunkFileSystem()
     113             : {
     114        1301 :     bool *pbInstantiated = &IsFileSystemInstantiated();
     115        1301 :     *pbInstantiated = false;
     116        1301 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                            ClearCaches()                             */
     120             : /************************************************************************/
     121             : 
     122        1006 : void VSIIcechunkFileSystem::ClearCaches()
     123             : {
     124        1006 :     m_oCache.clear();
     125        1006 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                VSIIcechunkFileSystem::SplitFilename()                */
     129             : /************************************************************************/
     130             : 
     131             : /** Decompose a filename into a repository root file name, a branch/tag name
     132             :  * and a key.
     133             :  */
     134             : 
     135             : /*static*/
     136             : VSIIcechunkFileSystem::Ref
     137        8384 : VSIIcechunkFileSystem::SplitFilename(const char *pszFilename)
     138             : {
     139        8384 :     Ref ref{};
     140        8384 :     if (!STARTS_WITH(pszFilename, FS_PREFIX))
     141           0 :         return ref;
     142             : 
     143       16768 :     std::string osRootFilename;
     144             : 
     145        8384 :     pszFilename += strlen(FS_PREFIX);
     146             : 
     147        8384 :     if (*pszFilename == '{')
     148             :     {
     149             :         // Parse /vsiicechunk/{/path/to/some/icechunk/repo[?[branch|tag]=<name>]}[/optional_key]
     150        8383 :         int nLevel = 1;
     151        8383 :         ++pszFilename;
     152      256898 :         for (; *pszFilename; ++pszFilename)
     153             :         {
     154      256897 :             if (*pszFilename == '{')
     155             :             {
     156           0 :                 ++nLevel;
     157             :             }
     158      256897 :             else if (*pszFilename == '}')
     159             :             {
     160        8382 :                 --nLevel;
     161        8382 :                 if (nLevel == 0)
     162             :                 {
     163        8382 :                     ++pszFilename;
     164        8382 :                     break;
     165             :                 }
     166             :             }
     167      248515 :             osRootFilename += *pszFilename;
     168             :         }
     169        8383 :         if (nLevel == 0)
     170             :         {
     171        8382 :             ref.osRootFilenameWithBranchOrTag = osRootFilename;
     172             : 
     173        8382 :             ref.osRootFilename = GetFilenameFromDatasetName(
     174        8382 :                 osRootFilename, ref.osBranchName, ref.osTagName);
     175        8382 :             if (ref.osBranchName.empty() && ref.osTagName.empty())
     176        8382 :                 ref.osBranchName = "main";
     177             : 
     178        8382 :             ref.osKey = *pszFilename == 0 ? "/" : pszFilename;
     179             :         }
     180             :     }
     181        8384 :     if (ref.osRootFilename.empty())
     182             :     {
     183           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     184             :                  "Invalid %s syntax for \"%s\": should be "
     185             :                  "%s{/path/to/some/icechunk/repo[?[branch|tag]=<name>]}[/"
     186             :                  "optional_key]",
     187             :                  FS_PREFIX, pszFilename, FS_PREFIX);
     188             :     }
     189        8384 :     return ref;
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                    VSIIcechunkFileSystem::Load()                     */
     194             : /************************************************************************/
     195             : 
     196             : /** Load the repo and snapshot files associated to the passed ref.
     197             :  *
     198             :  * Uses an internal cache.
     199             :  */
     200             : VSIIcechunkFileSystem::RepoAndSnapshot
     201        8382 : VSIIcechunkFileSystem::Load(const Ref &ref)
     202             : {
     203        8382 :     VSIIcechunkFileSystem::RepoAndSnapshot repoAndSnapshot;
     204        8382 :     if (!m_oCache.tryGet(ref.osRootFilenameWithBranchOrTag, repoAndSnapshot))
     205             :     {
     206        4248 :         auto repo = IcechunkRepo::Open(ref.osRootFilename.c_str());
     207        2124 :         if (repo)
     208             :         {
     209        2123 :             auto snapshot = !ref.osBranchName.empty()
     210        2123 :                                 ? repo->OpenSnapshotOnBranch(ref.osBranchName)
     211        4246 :                                 : repo->OpenSnapshotOnTag(ref.osTagName);
     212        2123 :             if (snapshot)
     213             :             {
     214        2123 :                 repoAndSnapshot.first = std::move(repo);
     215        2123 :                 repoAndSnapshot.second = std::move(snapshot);
     216        2123 :                 m_oCache.insert(ref.osRootFilenameWithBranchOrTag,
     217             :                                 repoAndSnapshot);
     218             :             }
     219             :         }
     220             :     }
     221        8382 :     return repoAndSnapshot;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                    VSIIcechunkFileSystem::Load()                     */
     226             : /************************************************************************/
     227             : 
     228             : /** Load the repo and snapshot files associated to the passed filename.
     229             :  *
     230             :  * Uses an internal cache.
     231             :  */
     232             : std::pair<VSIIcechunkFileSystem::Ref, VSIIcechunkFileSystem::RepoAndSnapshot>
     233        8384 : VSIIcechunkFileSystem::Load(const char *pszFilename)
     234             : {
     235       16768 :     const auto ref = SplitFilename(pszFilename);
     236        8384 :     if (ref.osRootFilename.empty())
     237           2 :         return {};
     238             : 
     239       16764 :     return {ref, Load(ref)};
     240             : }
     241             : 
     242             : /************************************************************************/
     243             : /*                          GetChunkIndices()                           */
     244             : /************************************************************************/
     245             : 
     246        8116 : static ChunkIdx GetChunkIndices(const IcechunkSnapshot::Node &node,
     247             :                                 const char *pszChunkIndices)
     248             : {
     249             :     const CPLStringList aosChunkIdx(
     250       16232 :         CSLTokenizeString2(pszChunkIndices, "/", 0));
     251       16232 :     ChunkIdx anChunkIdx;
     252        8116 :     if (static_cast<size_t>(aosChunkIdx.size()) <= node.numChunks.size())
     253             :     {
     254       16261 :         for (int i = 0; i < aosChunkIdx.size(); ++i)
     255             :         {
     256        8153 :             if (CPLGetValueType(aosChunkIdx[i]) != CPL_VALUE_INTEGER)
     257           0 :                 return {};
     258        8153 :             const int nIdx = atoi(aosChunkIdx[i]);
     259        8153 :             if (nIdx < 0 || static_cast<unsigned>(nIdx) >= node.numChunks[i])
     260           4 :                 return {};
     261        8149 :             anChunkIdx.push_back(static_cast<unsigned>(nIdx));
     262             :         }
     263             :     }
     264        8112 :     return anChunkIdx;
     265             : }
     266             : 
     267             : /************************************************************************/
     268             : /*                            GetChunkRef()                             */
     269             : /************************************************************************/
     270             : 
     271             : static std::pair<std::shared_ptr<IcechunkManifest>,
     272             :                  const IcechunkManifest::ChunkRef *>
     273        8103 : GetChunkRef(const IcechunkRepo &repo, const IcechunkSnapshot &snapshot,
     274             :             const IcechunkSnapshot::Node &node, const ChunkIdx &anChunkIdx)
     275             : {
     276        8103 :     const auto *manifestId = node.findManifestIdForChunk(anChunkIdx);
     277        8103 :     if (!manifestId)
     278             :     {
     279             :         // This is not necessary an error. This can happen for sparse chunks.
     280           1 :         return {};
     281             :     }
     282             : 
     283        8102 :     const auto *manifestInfo = snapshot.GetManifestInfoFromId(*manifestId);
     284        8102 :     if (!manifestInfo)
     285             :     {
     286           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     287             :                  "Manifest %s not referenced in snapshot %s",
     288           2 :                  CrockfordBase32Encode(*manifestId).c_str(),
     289           1 :                  snapshot.GetFilename().c_str());
     290           1 :         return {};
     291             :     }
     292             : 
     293             :     auto manifest =
     294        8101 :         repo.OpenManifest(manifestInfo->strId, manifestInfo->sizeBytes,
     295       16202 :                           manifestInfo->numChunkRefs);
     296        8101 :     if (!manifest)
     297             :     {
     298             :         // OpenManifest() will have emitted an error
     299          15 :         return {};
     300             :     }
     301             : 
     302        8086 :     const auto *chunkRef = manifest->GetChunkRef(node.id, anChunkIdx);
     303        8086 :     return {std::move(manifest), chunkRef};
     304             : }
     305             : 
     306             : /************************************************************************/
     307             : /*                          GetChunkFilename()                          */
     308             : /************************************************************************/
     309             : 
     310        4030 : static std::string GetChunkFilename(const IcechunkManifest &manifest,
     311             :                                     const IcechunkManifest::ChunkRef &chunkRef)
     312             : {
     313        4030 :     std::string osChunkFilename;
     314        4030 :     if (!chunkRef.chunkId.empty())
     315             :     {
     316           4 :         osChunkFilename = manifest.GetChunkFilename(chunkRef.chunkId);
     317             :     }
     318             :     else
     319             :     {
     320             :         static const struct
     321             :         {
     322             :             const char *pszStandardPrefix;
     323             :             const char *pszVSIPrefix;
     324             :         } asPrefixes[] = {
     325             :             {"s3://", "/vsis3/"},
     326             :             {"gs://", "/vsigs/"},
     327             :             {"gcs://", "/vsigs/"},
     328             :             {"az://", "/vsiaz/"},
     329             :             {"azure://", "/vsiaz/"},
     330             :             {"http://", "/vsicurl/http://"},
     331             :             {"https://", "/vsicurl/https://"},
     332             :         };
     333             : 
     334        4059 :         for (const auto &sPrefix : asPrefixes)
     335             :         {
     336        4056 :             if (cpl::starts_with(chunkRef.location, sPrefix.pszStandardPrefix))
     337             :             {
     338        8046 :                 osChunkFilename = std::string(sPrefix.pszVSIPrefix)
     339        4023 :                                       .append(chunkRef.location.substr(
     340        4023 :                                           strlen(sPrefix.pszStandardPrefix)));
     341        4023 :                 break;
     342             :             }
     343             :         }
     344        4026 :         if (osChunkFilename.empty())
     345             :         {
     346           3 :             if (CPLTestBool(CPLGetConfigOption(
     347             :                     "ICECHUNK_ALLOW_LOCAL_CHUNK_LOCATION", "NO")))
     348             :             {
     349           1 :                 osChunkFilename = chunkRef.location;
     350             :             }
     351             :             else
     352             :             {
     353           2 :                 CPLError(
     354             :                     CE_Failure, CPLE_AppDefined,
     355             :                     "Access to non-network chunk location '%s' disabled by "
     356             :                     "default. Set the ICECHUNK_ALLOW_LOCAL_CHUNK_LOCATION "
     357             :                     "cICECHUNK_ALLOW_LOCAL_CHUNK_LOCATION configuration option "
     358             :                     "to YES to enable it.",
     359             :                     chunkRef.location.c_str());
     360             :             }
     361             :         }
     362             :     }
     363        4030 :     return osChunkFilename;
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                 VSIIcechunkFileSystem::GetFileInfo()                 */
     368             : /************************************************************************/
     369             : 
     370             : VSIIcechunkFileSystem::FileInfo
     371        8366 : VSIIcechunkFileSystem::GetFileInfo(const char *pszFilename)
     372             : {
     373        8366 :     FileInfo info;
     374             : 
     375       16732 :     auto [ref, repoAndSnapshot] = Load(pszFilename);
     376        8366 :     const auto &[repo, snapshot] = repoAndSnapshot;
     377        8366 :     if (!snapshot)
     378           3 :         return info;
     379             : 
     380             :     // Deal with chunk directory
     381        8363 :     const auto nLastCPos = ref.osKey.rfind("/c");
     382        8363 :     if (nLastCPos != std::string::npos)
     383             :     {
     384        8125 :         const std::string osTmpKey = ref.osKey.substr(0, nLastCPos);
     385        8125 :         const auto *nodePtr = snapshot->GetNodeFromPath(osTmpKey);
     386        8125 :         if (nodePtr && nodePtr->isArray)
     387             :         {
     388        8123 :             const auto &node = *nodePtr;
     389       16246 :             ChunkIdx anChunkIdx;
     390        8123 :             if (nLastCPos + 2 == ref.osKey.size())
     391             :             {
     392          13 :                 info.bIsDir = !node.numChunks.empty();
     393             :             }
     394             :             else
     395             :             {
     396        8110 :                 if (node.numChunks.empty())
     397           3 :                     return info;
     398       16214 :                 anChunkIdx = GetChunkIndices(
     399       24321 :                     node, ref.osKey.substr(nLastCPos + 2).c_str());
     400             :             }
     401             : 
     402        8120 :             if (!info.bIsDir)
     403             :             {
     404        8116 :                 if (anChunkIdx.empty() && !node.numChunks.empty())
     405             :                 {
     406             :                     // wrong path
     407             :                 }
     408        8110 :                 else if (anChunkIdx.size() < node.numChunks.size())
     409             :                 {
     410           7 :                     info.bIsDir = true;
     411             :                 }
     412             :                 else
     413             :                 {
     414        8103 :                     const auto [manifest, chunkRef] =
     415       16206 :                         GetChunkRef(*repo, *snapshot, node, anChunkIdx);
     416        8103 :                     if (chunkRef)
     417             :                     {
     418        8083 :                         if (chunkRef->length)
     419             :                         {
     420             :                             info.osFilename =
     421        4030 :                                 GetChunkFilename(*manifest, *chunkRef);
     422        4030 :                             if (!info.osFilename.empty())
     423             :                             {
     424        4028 :                                 info.nOffset = chunkRef->offset;
     425        4028 :                                 info.nSize = chunkRef->length;
     426             :                             }
     427             :                         }
     428             :                         else
     429             :                         {
     430        4053 :                             info.nSize = chunkRef->inlineContent.size();
     431        4053 :                             info.pabyData = chunkRef->inlineContent.data();
     432        4053 :                             info.dataOwner = std::move(manifest);
     433             :                         }
     434             :                     }
     435             :                 }
     436             :             }
     437             : 
     438        8120 :             return info;
     439             :         }
     440             :     }
     441             : 
     442         240 :     bool bIsZarrDotJson = false;
     443         480 :     std::string key = ref.osKey;
     444         240 :     if (cpl::ends_with(key, "/zarr.json"))
     445             :     {
     446         186 :         bIsZarrDotJson = true;
     447         186 :         key.resize(key.size() - strlen("/zarr.json"));
     448         186 :         if (key.empty())
     449          88 :             key = "/";
     450             :     }
     451             : 
     452         240 :     if (key == "/" && snapshot->GetNodeCount() <= 1)
     453             :     {
     454           2 :         info.bIsDir = true;
     455             :     }
     456         238 :     else if (const auto *node = snapshot->GetNodeFromPath(key))
     457             :     {
     458         170 :         if (bIsZarrDotJson)
     459             :         {
     460         164 :             info.nSize = node->content.size();
     461         164 :             info.pabyData = node->content.data();
     462         164 :             info.dataOwner = std::move(snapshot);
     463             :         }
     464             :         else
     465             :         {
     466           6 :             info.bIsDir = true;
     467             :         }
     468             :     }
     469             : 
     470         240 :     return info;
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                    VSIIcechunkFileSystem::Open()                     */
     475             : /************************************************************************/
     476             : 
     477             : VSIVirtualHandleUniquePtr
     478         159 : VSIIcechunkFileSystem::Open(const char *pszFilename, const char *pszAccess,
     479             :                             bool /* bSetError */,
     480             :                             CSLConstList /* papszOptions */)
     481             : {
     482         159 :     CPLDebugOnly("VSIIcechunkFileSystem", "Open(%s)", pszFilename);
     483         159 :     if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "rb") != 0)
     484           0 :         return nullptr;
     485             : 
     486         318 :     auto info = GetFileInfo(pszFilename);
     487         159 :     if (!info.osFilename.empty())
     488             :     {
     489             :         CPLConfigOptionSetter oSetter("GDAL_DISABLE_READDIR_ON_OPEN",
     490          26 :                                       "EMPTY_DIR", false);
     491             : 
     492             :         const std::string osSubfileName =
     493             :             CPLSPrintf("/vsisubfile/%" PRIu64 "_%" PRIu64 ",%s", info.nOffset,
     494          26 :                        info.nSize, info.osFilename.c_str());
     495          26 :         auto fp = VSIFilesystemHandler::OpenStatic(osSubfileName.c_str(), "rb");
     496          26 :         if (fp)
     497             :         {
     498             :             VSIStatBufL sStat;
     499          52 :             if (VSIStatL(info.osFilename.c_str(), &sStat) != 0 ||
     500          26 :                 info.nOffset + info.nSize >
     501          26 :                     static_cast<uint64_t>(sStat.st_size))
     502             :             {
     503           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     504             :                          "(offset,length)=(%" PRIu64 ",%" PRIu64
     505             :                          ") beyond %s size",
     506             :                          info.nOffset, info.nSize, info.osFilename.c_str());
     507             :             }
     508             :             else
     509             :             {
     510          25 :                 return fp;
     511             :             }
     512             :         }
     513             :     }
     514         133 :     else if (info.dataOwner)
     515             :     {
     516         106 :         const size_t nSize = static_cast<size_t>(info.nSize);
     517             :         if constexpr (sizeof(size_t) < sizeof(uint64_t))
     518             :         {
     519             :             // Guaranteed because info.nSize is the result of container.size()
     520             :             CPLAssert(nSize == info.nSize);
     521             :         }
     522         106 :         GByte *pabyData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nSize));
     523         106 :         if (pabyData)
     524             :         {
     525         106 :             memcpy(pabyData, info.pabyData, nSize);
     526             :             return VSIVirtualHandleUniquePtr(
     527             :                 VSIFileFromMemBuffer(nullptr, pabyData, nSize,
     528         106 :                                      /* bTakeOwnership = */ true));
     529             :         }
     530             :     }
     531             : 
     532          28 :     return nullptr;
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                    VSIIcechunkFileSystem::Stat()                     */
     537             : /************************************************************************/
     538             : 
     539         190 : int VSIIcechunkFileSystem::Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     540             :                                 int /* nFlags */)
     541             : {
     542         190 :     CPLDebugOnly("VSIIcechunkFileSystem", "Stat(%s)", pszFilename);
     543         190 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     544             : 
     545         190 :     int nRet = -1;
     546         190 :     auto info = GetFileInfo(pszFilename);
     547         190 :     if (!info.osFilename.empty())
     548             :     {
     549             :         CPLConfigOptionSetter oSetter("GDAL_DISABLE_READDIR_ON_OPEN",
     550           0 :                                       "EMPTY_DIR", false);
     551           0 :         nRet = VSIStatL(info.osFilename.c_str(), pStatBuf);
     552           0 :         if (nRet == 0)
     553             :         {
     554           0 :             if (info.nOffset + info.nSize >
     555           0 :                 static_cast<uint64_t>(pStatBuf->st_size))
     556             :             {
     557           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     558             :                          "(offset,length)=(%" PRIu64 ",%" PRIu64
     559             :                          ") beyond %s size",
     560             :                          info.nOffset, info.nSize, info.osFilename.c_str());
     561           0 :                 nRet = -1;
     562             :             }
     563             :             else
     564             :             {
     565           0 :                 pStatBuf->st_size = info.nSize;
     566             :             }
     567             :         }
     568             :     }
     569         190 :     else if (info.dataOwner)
     570             :     {
     571         106 :         nRet = VSIStatL(info.dataOwner->GetFilename().c_str(), pStatBuf);
     572         106 :         if (nRet == 0)
     573             :         {
     574         106 :             pStatBuf->st_mode = S_IFREG;
     575         106 :             pStatBuf->st_size = info.nSize;
     576             :         }
     577             :     }
     578          84 :     else if (info.bIsDir)
     579             :     {
     580          16 :         nRet = 0;
     581          16 :         pStatBuf->st_mode = S_IFDIR;
     582             :     }
     583             : 
     584         380 :     return nRet;
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*               VSIIcechunkFileSystem::GetFileMetadata()               */
     589             : /************************************************************************/
     590             : 
     591        8018 : char **VSIIcechunkFileSystem::GetFileMetadata(const char *pszFilename,
     592             :                                               const char *pszDomain,
     593             :                                               CSLConstList /* papszOptions */)
     594             : {
     595        8018 :     if (!pszDomain || !EQUAL(pszDomain, "CHUNK_INFO"))
     596           1 :         return nullptr;
     597             : 
     598       16034 :     CPLStringList aosMetadata;
     599             : 
     600       16034 :     auto info = GetFileInfo(pszFilename);
     601        8017 :     if (!info.osFilename.empty())
     602             :     {
     603        4002 :         aosMetadata.SetNameValue("SIZE", CPLSPrintf("%" PRIu64, info.nSize));
     604             :         aosMetadata.SetNameValue("OFFSET",
     605        4002 :                                  CPLSPrintf("%" PRIu64, info.nOffset));
     606        4002 :         aosMetadata.SetNameValue("FILENAME", info.osFilename.c_str());
     607             :     }
     608        4015 :     else if (info.dataOwner)
     609             :     {
     610        4005 :         aosMetadata.SetNameValue("SIZE", CPLSPrintf("%" PRIu64, info.nSize));
     611        8010 :         if (info.nSize <
     612        4005 :             static_cast<size_t>(std::numeric_limits<int>::max() - 1))
     613             :         {
     614             :             char *pszBase64 =
     615        8010 :                 CPLBase64Encode(static_cast<int>(info.nSize),
     616        4005 :                                 static_cast<const GByte *>(info.pabyData));
     617        4005 :             aosMetadata.SetNameValue("BASE64", pszBase64);
     618        4005 :             CPLFree(pszBase64);
     619             :         }
     620             :     }
     621             : 
     622        8017 :     return aosMetadata.StealList();
     623             : }
     624             : 
     625             : /************************************************************************/
     626             : /*                  VSIIcechunkFileSystem::ReadDirEx()                  */
     627             : /************************************************************************/
     628             : 
     629          18 : char **VSIIcechunkFileSystem::ReadDirEx(const char *pszDirname, int nMaxFiles)
     630             : {
     631          18 :     CPLDebugOnly("VSIIcechunkFileSystem", "ReadDirEx(%s, %d)", pszDirname,
     632             :                  nMaxFiles);
     633             : 
     634          36 :     auto [ref, repoAndSnapshot] = Load(pszDirname);
     635          18 :     const auto &[repo, snapshot] = repoAndSnapshot;
     636          18 :     if (!snapshot)
     637           0 :         return nullptr;
     638             : 
     639          36 :     CPLStringList aosFiles;
     640             : 
     641             :     // Deal with chunk directory
     642          18 :     const auto nLastCPos = ref.osKey.rfind("/c");
     643          18 :     if (nLastCPos != std::string::npos)
     644             :     {
     645          10 :         const std::string osTmpKey = ref.osKey.substr(0, nLastCPos);
     646          10 :         const auto *nodePtr = snapshot->GetNodeFromPath(osTmpKey);
     647          10 :         if (nodePtr && nodePtr->isArray)
     648             :         {
     649          10 :             const auto &node = *nodePtr;
     650             : 
     651          10 :             if (!(nLastCPos + 2 == ref.osKey.size() && node.numChunks.empty()))
     652             :             {
     653             :                 auto anChunkIdx = GetChunkIndices(
     654          18 :                     node, ref.osKey.substr(nLastCPos + 2).c_str());
     655          18 :                 if (anChunkIdx.size() < node.numChunks.size() &&
     656           9 :                     !(anChunkIdx.empty() && nLastCPos + 2 != ref.osKey.size()))
     657             :                 {
     658           7 :                     const size_t iDim = anChunkIdx.size();
     659           7 :                     anChunkIdx.push_back(0);
     660          31 :                     for (uint32_t i = 0; i < node.numChunks[iDim]; ++i)
     661             :                     {
     662          24 :                         if (iDim + 1 == node.numChunks.size())
     663             :                         {
     664          18 :                             anChunkIdx.back() = i;
     665          18 :                             if (!node.findManifestIdForChunk(anChunkIdx))
     666           0 :                                 continue;
     667             :                         }
     668          24 :                         aosFiles.push_back(std::to_string(i));
     669          24 :                         if (nMaxFiles > 0 && aosFiles.size() == nMaxFiles)
     670           0 :                             break;
     671             :                     }
     672             :                 }
     673             :             }
     674             : 
     675          10 :             return aosFiles.StealList();
     676             :         }
     677             :     }
     678             : 
     679           8 :     CPLAssert(ref.osKey == "/" ||
     680             :               (!ref.osKey.empty() && ref.osKey.back() != '/'));
     681             :     const std::string refKeyWithTrailingSlash =
     682          16 :         ref.osKey == "/" ? ref.osKey : std::string(ref.osKey).append("/");
     683          22 :     for (const auto &node : snapshot->GetNodes())
     684             :     {
     685          14 :         CPLAssert(node.path == "/" ||
     686             :                   (!node.path.empty() && node.path.back() != '/'));
     687          14 :         if (node.path == ref.osKey)
     688             :         {
     689           6 :             aosFiles.push_back("zarr.json");
     690           6 :             if (node.isArray)
     691             :             {
     692           2 :                 aosFiles.push_back("c");
     693             :             }
     694             :         }
     695           8 :         else if (cpl::starts_with(node.path, refKeyWithTrailingSlash))
     696             :         {
     697             :             std::string dirName =
     698           8 :                 node.path.substr(refKeyWithTrailingSlash.size());
     699           4 :             if (dirName.find('/') == std::string::npos)
     700             :             {
     701           4 :                 aosFiles.push_back(dirName);
     702             :             }
     703             :         }
     704          14 :         if (nMaxFiles > 0 && aosFiles.size() == nMaxFiles)
     705           0 :             break;
     706             :     }
     707             : 
     708           8 :     return aosFiles.StealList();
     709             : }
     710             : 
     711             : /************************************************************************/
     712             : /*                    VSIInstallIcechunkFileSystem()                    */
     713             : /************************************************************************/
     714             : 
     715        1872 : void VSIInstallIcechunkFileSystem()
     716             : {
     717             :     static std::mutex oMutex;
     718        3744 :     std::lock_guard<std::mutex> oLock(oMutex);
     719             :     // cppcheck-suppress knownConditionTrueFalse
     720        1872 :     if (!VSIIcechunkFileSystem::IsFileSystemInstantiated())
     721             :     {
     722        1872 :         VSIFileManager::InstallHandler(
     723        3744 :             FS_PREFIX, std::make_shared<VSIIcechunkFileSystem>());
     724             :     }
     725        1872 : }
     726             : 
     727             : /************************************************************************/
     728             : /*                  VSIIcechunkFileSystemClearCaches()                  */
     729             : /************************************************************************/
     730             : 
     731        1006 : void VSIIcechunkFileSystemClearCaches()
     732             : {
     733           0 :     auto poFS = dynamic_cast<VSIIcechunkFileSystem *>(
     734        1006 :         VSIFileManager::GetHandler(FS_PREFIX));
     735        1006 :     if (poFS)
     736        1006 :         poFS->ClearCaches();
     737        1006 : }
     738             : 
     739             : }  // namespace gdal::icechunk

Generated by: LCOV version 1.14