LCOV - code coverage report
Current view: top level - port - cpl_vsil_plugin.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 60 229 26.2 %
Date: 2026-02-12 06:20:29 Functions: 16 47 34.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for plugins
       5             :  * Author:   Thomas Bonfort, thomas.bonfort@airbus.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2019, Thomas Bonfort <thomas.bonfort@airbus.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_vsil_plugin.h"
      15             : 
      16             : //! @cond Doxygen_Suppress
      17             : #ifndef DOXYGEN_SKIP
      18             : 
      19             : namespace cpl
      20             : {
      21             : 
      22           2 : VSIPluginHandle::VSIPluginHandle(VSIPluginFilesystemHandler *poFSIn,
      23           2 :                                  void *cbDataIn)
      24           2 :     : poFS(poFSIn), cbData(cbDataIn)
      25             : {
      26           2 : }
      27             : 
      28           4 : VSIPluginHandle::~VSIPluginHandle()
      29             : {
      30           2 :     if (cbData)
      31             :     {
      32           0 :         VSIPluginHandle::Close();
      33             :     }
      34           4 : }
      35             : 
      36           0 : int VSIPluginHandle::Seek(vsi_l_offset nOffset, int nWhence)
      37             : {
      38           0 :     return poFS->Seek(cbData, nOffset, nWhence);
      39             : }
      40             : 
      41           0 : vsi_l_offset VSIPluginHandle::Tell()
      42             : {
      43           0 :     return poFS->Tell(cbData);
      44             : }
      45             : 
      46           0 : size_t VSIPluginHandle::Read(void *const pBuffer, size_t const nBytes)
      47             : {
      48           0 :     return poFS->Read(cbData, pBuffer, nBytes);
      49             : }
      50             : 
      51           0 : int VSIPluginHandle::Eof()
      52             : {
      53           0 :     return poFS->Eof(cbData);
      54             : }
      55             : 
      56           0 : int VSIPluginHandle::Error()
      57             : {
      58           0 :     return poFS->Error(cbData);
      59             : }
      60             : 
      61           0 : void VSIPluginHandle::ClearErr()
      62             : {
      63           0 :     poFS->ClearErr(cbData);
      64           0 : }
      65             : 
      66           2 : int VSIPluginHandle::Close()
      67             : {
      68           2 :     int ret = poFS->Close(cbData);
      69           2 :     cbData = nullptr;
      70           2 :     return ret;
      71             : }
      72             : 
      73           0 : int VSIPluginHandle::ReadMultiRange(int nRanges, void **ppData,
      74             :                                     const vsi_l_offset *panOffsets,
      75             :                                     const size_t *panSizes)
      76             : {
      77           0 :     return poFS->ReadMultiRange(cbData, nRanges, ppData, panOffsets, panSizes);
      78             : }
      79             : 
      80           2 : void VSIPluginHandle::AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
      81             :                                  const size_t *panSizes)
      82             : {
      83           2 :     poFS->AdviseRead(cbData, nRanges, panOffsets, panSizes);
      84           2 : }
      85             : 
      86           0 : VSIRangeStatus VSIPluginHandle::GetRangeStatus(vsi_l_offset nOffset,
      87             :                                                vsi_l_offset nLength)
      88             : {
      89           0 :     return poFS->GetRangeStatus(cbData, nOffset, nLength);
      90             : }
      91             : 
      92           0 : size_t VSIPluginHandle::Write(const void *pBuffer, size_t nBytes)
      93             : {
      94           0 :     return poFS->Write(cbData, pBuffer, nBytes);
      95             : }
      96             : 
      97           0 : int VSIPluginHandle::Flush()
      98             : {
      99           0 :     return poFS->Flush(cbData);
     100             : }
     101             : 
     102           0 : int VSIPluginHandle::Truncate(vsi_l_offset nNewSize)
     103             : {
     104           0 :     return poFS->Truncate(cbData, nNewSize);
     105             : }
     106             : 
     107           2 : VSIPluginFilesystemHandler::VSIPluginFilesystemHandler(
     108           2 :     const char *pszPrefix, const VSIFilesystemPluginCallbacksStruct *cbIn)
     109           2 :     : m_Prefix(pszPrefix), m_cb(nullptr)
     110             : {
     111           2 :     m_cb = new VSIFilesystemPluginCallbacksStruct(*cbIn);
     112           2 : }
     113             : 
     114           2 : VSIPluginFilesystemHandler::~VSIPluginFilesystemHandler()
     115             : {
     116           2 :     delete m_cb;
     117           2 : }
     118             : 
     119             : VSIVirtualHandleUniquePtr
     120           3 : VSIPluginFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
     121             :                                  bool bSetError,
     122             :                                  CSLConstList /* papszOptions */)
     123             : {
     124           3 :     if (!IsValidFilename(pszFilename))
     125           0 :         return nullptr;
     126           3 :     void *cbData = m_cb->open(m_cb->pUserData, GetCallbackFilename(pszFilename),
     127           3 :                               pszAccess);
     128           3 :     if (cbData == nullptr)
     129             :     {
     130           1 :         if (bSetError)
     131             :         {
     132           0 :             VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(errno));
     133             :         }
     134           1 :         return nullptr;
     135             :     }
     136           4 :     auto poPluginHandle = std::make_unique<VSIPluginHandle>(this, cbData);
     137           2 :     if (m_cb->nBufferSize == 0)
     138             :     {
     139           2 :         return VSIVirtualHandleUniquePtr(poPluginHandle.release());
     140             :     }
     141             :     else
     142             :     {
     143             :         return VSIVirtualHandleUniquePtr(VSICreateCachedFile(
     144           0 :             poPluginHandle.release(), m_cb->nBufferSize,
     145           0 :             (m_cb->nCacheSize < m_cb->nBufferSize) ? m_cb->nBufferSize
     146           0 :                                                    : m_cb->nCacheSize));
     147             :     }
     148             : }
     149             : 
     150             : const char *
     151           3 : VSIPluginFilesystemHandler::GetCallbackFilename(const char *pszFilename)
     152             : {
     153           3 :     return pszFilename + strlen(m_Prefix);
     154             : }
     155             : 
     156           3 : bool VSIPluginFilesystemHandler::IsValidFilename(const char *pszFilename)
     157             : {
     158           3 :     if (!STARTS_WITH_CI(pszFilename, m_Prefix))
     159           0 :         return false;
     160           3 :     return true;
     161             : }
     162             : 
     163           0 : int VSIPluginFilesystemHandler::Stat(const char *pszFilename,
     164             :                                      VSIStatBufL *pStatBuf, int nFlags)
     165             : {
     166           0 :     if (!IsValidFilename(pszFilename))
     167             :     {
     168           0 :         errno = EBADF;
     169           0 :         return -1;
     170             :     }
     171             : 
     172           0 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     173             : 
     174           0 :     int nRet = 0;
     175           0 :     if (m_cb->stat != nullptr)
     176             :     {
     177           0 :         nRet = m_cb->stat(m_cb->pUserData, GetCallbackFilename(pszFilename),
     178             :                           pStatBuf, nFlags);
     179             :     }
     180             :     else
     181             :     {
     182           0 :         nRet = -1;
     183             :     }
     184           0 :     return nRet;
     185             : }
     186             : 
     187           0 : int VSIPluginFilesystemHandler::Seek(void *pFile, vsi_l_offset nOffset,
     188             :                                      int nWhence)
     189             : {
     190           0 :     if (m_cb->seek != nullptr)
     191             :     {
     192           0 :         return m_cb->seek(pFile, nOffset, nWhence);
     193             :     }
     194           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Seek not implemented for %s plugin",
     195             :              m_Prefix);
     196           0 :     return -1;
     197             : }
     198             : 
     199           0 : vsi_l_offset VSIPluginFilesystemHandler::Tell(void *pFile)
     200             : {
     201           0 :     if (m_cb->tell != nullptr)
     202             :     {
     203           0 :         return m_cb->tell(pFile);
     204             :     }
     205           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Tell not implemented for %s plugin",
     206             :              m_Prefix);
     207           0 :     return -1;
     208             : }
     209             : 
     210           0 : size_t VSIPluginFilesystemHandler::Read(void *pFile, void *pBuffer,
     211             :                                         size_t nBytes)
     212             : {
     213           0 :     if (m_cb->read != nullptr)
     214             :     {
     215           0 :         return m_cb->read(pFile, pBuffer, 1, nBytes);
     216             :     }
     217           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Read not implemented for %s plugin",
     218             :              m_Prefix);
     219           0 :     return -1;
     220             : }
     221             : 
     222           0 : int VSIPluginFilesystemHandler::HasOptimizedReadMultiRange(
     223             :     const char * /*pszPath*/)
     224             : {
     225           0 :     if (m_cb->read_multi_range != nullptr)
     226             :     {
     227           0 :         return TRUE;
     228             :     }
     229           0 :     return FALSE;
     230             : }
     231             : 
     232           0 : VSIRangeStatus VSIPluginFilesystemHandler::GetRangeStatus(void *pFile,
     233             :                                                           vsi_l_offset nOffset,
     234             :                                                           vsi_l_offset nLength)
     235             : {
     236           0 :     if (m_cb->get_range_status != nullptr)
     237             :     {
     238           0 :         return m_cb->get_range_status(pFile, nOffset, nLength);
     239             :     }
     240           0 :     return VSI_RANGE_STATUS_UNKNOWN;
     241             : }
     242             : 
     243           0 : int VSIPluginFilesystemHandler::ReadMultiRange(void *pFile, int nRanges,
     244             :                                                void **ppData,
     245             :                                                const vsi_l_offset *panOffsets,
     246             :                                                const size_t *panSizes)
     247             : {
     248           0 :     if (m_cb->read_multi_range == nullptr)
     249             :     {
     250           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     251             :                  "Read not implemented for %s plugin", m_Prefix);
     252           0 :         return -1;
     253             :     }
     254             :     int iRange;
     255           0 :     int nMergedRanges = 1;
     256           0 :     for (iRange = 0; iRange < nRanges - 1; iRange++)
     257             :     {
     258           0 :         if (panOffsets[iRange] + panSizes[iRange] != panOffsets[iRange + 1])
     259             :         {
     260           0 :             nMergedRanges++;
     261             :         }
     262             :     }
     263           0 :     if (nMergedRanges == nRanges)
     264             :     {
     265           0 :         return m_cb->read_multi_range(pFile, nRanges, ppData, panOffsets,
     266           0 :                                       panSizes);
     267             :     }
     268             : 
     269           0 :     vsi_l_offset *mOffsets = new vsi_l_offset[nMergedRanges];
     270           0 :     size_t *mSizes = new size_t[nMergedRanges];
     271           0 :     char **mData = new char *[nMergedRanges];
     272             : 
     273           0 :     int curRange = 0;
     274           0 :     mSizes[curRange] = panSizes[0];
     275           0 :     mOffsets[curRange] = panOffsets[0];
     276           0 :     for (iRange = 0; iRange < nRanges - 1; iRange++)
     277             :     {
     278           0 :         if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
     279             :         {
     280           0 :             mSizes[curRange] += panSizes[iRange + 1];
     281             :         }
     282             :         else
     283             :         {
     284           0 :             mData[curRange] = new char[mSizes[curRange]];
     285             :             // start a new range
     286           0 :             curRange++;
     287           0 :             mSizes[curRange] = panSizes[iRange + 1];
     288           0 :             mOffsets[curRange] = panOffsets[iRange + 1];
     289             :         }
     290             :     }
     291           0 :     mData[curRange] = new char[mSizes[curRange]];
     292             : 
     293           0 :     int ret = m_cb->read_multi_range(pFile, nMergedRanges,
     294             :                                      reinterpret_cast<void **>(mData), mOffsets,
     295             :                                      mSizes);
     296             : 
     297           0 :     curRange = 0;
     298           0 :     size_t curOffset = panSizes[0];
     299           0 :     memcpy(ppData[0], mData[0], panSizes[0]);
     300           0 :     for (iRange = 0; iRange < nRanges - 1; iRange++)
     301             :     {
     302           0 :         if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
     303             :         {
     304           0 :             memcpy(ppData[iRange + 1], mData[curRange] + curOffset,
     305           0 :                    panSizes[iRange + 1]);
     306           0 :             curOffset += panSizes[iRange + 1];
     307             :         }
     308             :         else
     309             :         {
     310           0 :             curRange++;
     311           0 :             memcpy(ppData[iRange + 1], mData[curRange], panSizes[iRange + 1]);
     312           0 :             curOffset = panSizes[iRange + 1];
     313             :         }
     314             :     }
     315             : 
     316           0 :     delete[] mOffsets;
     317           0 :     delete[] mSizes;
     318           0 :     for (int i = 0; i < nMergedRanges; i++)
     319             :     {
     320           0 :         delete[] mData[i];
     321             :     }
     322           0 :     delete[] mData;
     323             : 
     324           0 :     return ret;
     325             : }
     326             : 
     327           2 : void VSIPluginFilesystemHandler::AdviseRead(void *pFile, int nRanges,
     328             :                                             const vsi_l_offset *panOffsets,
     329             :                                             const size_t *panSizes)
     330             : {
     331           2 :     if (m_cb->advise_read != nullptr)
     332             :     {
     333           1 :         m_cb->advise_read(pFile, nRanges, panOffsets, panSizes);
     334             :     }
     335             :     else
     336             :     {
     337           1 :         if (!m_bWarnedAdviseReadImplemented)
     338             :         {
     339           1 :             m_bWarnedAdviseReadImplemented = true;
     340           1 :             CPLDebug("VSIPlugin", "AdviseRead() not implemented");
     341             :         }
     342             :     }
     343           2 : }
     344             : 
     345           0 : int VSIPluginFilesystemHandler::Eof(void *pFile)
     346             : {
     347           0 :     if (m_cb->eof != nullptr)
     348             :     {
     349           0 :         return m_cb->eof(pFile);
     350             :     }
     351           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Eof not implemented for %s plugin",
     352             :              m_Prefix);
     353           0 :     return -1;
     354             : }
     355             : 
     356           0 : int VSIPluginFilesystemHandler::Error(void *pFile)
     357             : {
     358           0 :     if (m_cb->error)
     359             :     {
     360           0 :         return m_cb->error(pFile);
     361             :     }
     362           0 :     CPLDebug("CPL", "Error() not implemented for %s plugin", m_Prefix);
     363           0 :     return 0;
     364             : }
     365             : 
     366           0 : void VSIPluginFilesystemHandler::ClearErr(void *pFile)
     367             : {
     368           0 :     if (m_cb->clear_err)
     369             :     {
     370           0 :         m_cb->clear_err(pFile);
     371             :     }
     372             :     else
     373             :     {
     374           0 :         CPLDebug("CPL", "ClearErr() not implemented for %s plugin", m_Prefix);
     375             :     }
     376           0 : }
     377             : 
     378           2 : int VSIPluginFilesystemHandler::Close(void *pFile)
     379             : {
     380           2 :     if (m_cb->close != nullptr)
     381             :     {
     382           0 :         return m_cb->close(pFile);
     383             :     }
     384           2 :     CPLError(CE_Failure, CPLE_AppDefined, "Close not implemented for %s plugin",
     385             :              m_Prefix);
     386           2 :     return -1;
     387             : }
     388             : 
     389           0 : size_t VSIPluginFilesystemHandler::Write(void *pFile, const void *psBuffer,
     390             :                                          size_t nBytes)
     391             : {
     392           0 :     if (m_cb->write != nullptr)
     393             :     {
     394           0 :         return m_cb->write(pFile, psBuffer, 1, nBytes);
     395             :     }
     396           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Write not implemented for %s plugin",
     397             :              m_Prefix);
     398           0 :     return -1;
     399             : }
     400             : 
     401           0 : int VSIPluginFilesystemHandler::Flush(void *pFile)
     402             : {
     403           0 :     if (m_cb->flush != nullptr)
     404             :     {
     405           0 :         return m_cb->flush(pFile);
     406             :     }
     407           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Flush not implemented for %s plugin",
     408             :              m_Prefix);
     409           0 :     return -1;
     410             : }
     411             : 
     412           0 : int VSIPluginFilesystemHandler::Truncate(void *pFile, vsi_l_offset nNewSize)
     413             : {
     414           0 :     if (m_cb->truncate != nullptr)
     415             :     {
     416           0 :         return m_cb->truncate(pFile, nNewSize);
     417             :     }
     418           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     419             :              "Truncate not implemented for %s plugin", m_Prefix);
     420           0 :     return -1;
     421             : }
     422             : 
     423           0 : char **VSIPluginFilesystemHandler::ReadDirEx(const char *pszDirname,
     424             :                                              int nMaxFiles)
     425             : {
     426           0 :     if (!IsValidFilename(pszDirname))
     427           0 :         return nullptr;
     428           0 :     if (m_cb->read_dir != nullptr)
     429             :     {
     430           0 :         return m_cb->read_dir(m_cb->pUserData, GetCallbackFilename(pszDirname),
     431           0 :                               nMaxFiles);
     432             :     }
     433           0 :     return nullptr;
     434             : }
     435             : 
     436           0 : char **VSIPluginFilesystemHandler::SiblingFiles(const char *pszFilename)
     437             : {
     438           0 :     if (!IsValidFilename(pszFilename))
     439           0 :         return nullptr;
     440           0 :     if (m_cb->sibling_files != nullptr)
     441             :     {
     442           0 :         return m_cb->sibling_files(m_cb->pUserData,
     443           0 :                                    GetCallbackFilename(pszFilename));
     444             :     }
     445           0 :     return nullptr;
     446             : }
     447             : 
     448           0 : int VSIPluginFilesystemHandler::Unlink(const char *pszFilename)
     449             : {
     450           0 :     if (m_cb->unlink == nullptr || !IsValidFilename(pszFilename))
     451           0 :         return -1;
     452           0 :     return unlink(GetCallbackFilename(pszFilename));
     453             : }
     454             : 
     455           0 : int VSIPluginFilesystemHandler::Rename(const char *oldpath, const char *newpath,
     456             :                                        GDALProgressFunc, void *)
     457             : {
     458           0 :     if (m_cb->rename == nullptr || !IsValidFilename(oldpath) ||
     459           0 :         !IsValidFilename(newpath))
     460           0 :         return -1;
     461           0 :     return m_cb->rename(m_cb->pUserData, GetCallbackFilename(oldpath),
     462           0 :                         GetCallbackFilename(newpath));
     463             : }
     464             : 
     465           0 : int VSIPluginFilesystemHandler::Mkdir(const char *pszDirname, long nMode)
     466             : {
     467           0 :     if (m_cb->mkdir == nullptr || !IsValidFilename(pszDirname))
     468           0 :         return -1;
     469           0 :     return m_cb->mkdir(m_cb->pUserData, GetCallbackFilename(pszDirname), nMode);
     470             : }
     471             : 
     472           0 : int VSIPluginFilesystemHandler::Rmdir(const char *pszDirname)
     473             : {
     474           0 :     if (m_cb->rmdir == nullptr || !IsValidFilename(pszDirname))
     475           0 :         return -1;
     476           0 :     return m_cb->rmdir(m_cb->pUserData, GetCallbackFilename(pszDirname));
     477             : }
     478             : }  // namespace cpl
     479             : 
     480             : #endif  // DOXYGEN_SKIP
     481             : //! @endcond
     482             : 
     483           2 : int VSIInstallPluginHandler(const char *pszPrefix,
     484             :                             const VSIFilesystemPluginCallbacksStruct *poCb)
     485             : {
     486             :     auto poHandler =
     487           2 :         std::make_shared<cpl::VSIPluginFilesystemHandler>(pszPrefix, poCb);
     488             :     // TODO: check pszPrefix starts and ends with a /
     489           2 :     VSIFileManager::InstallHandler(pszPrefix, poHandler);
     490           4 :     return 0;
     491             : }
     492             : 
     493           3 : int VSIRemovePluginHandler(const char *pszPrefix)
     494             : {
     495           3 :     VSIFileManager::RemoveHandler(pszPrefix);
     496           3 :     return 0;
     497             : }
     498             : 
     499             : VSIFilesystemPluginCallbacksStruct *
     500           2 : VSIAllocFilesystemPluginCallbacksStruct(void)
     501             : {
     502             :     return static_cast<VSIFilesystemPluginCallbacksStruct *>(
     503           2 :         VSI_CALLOC_VERBOSE(1, sizeof(VSIFilesystemPluginCallbacksStruct)));
     504             : }
     505             : 
     506           2 : void VSIFreeFilesystemPluginCallbacksStruct(
     507             :     VSIFilesystemPluginCallbacksStruct *poCb)
     508             : {
     509           2 :     CPLFree(poCb);
     510           2 : }

Generated by: LCOV version 1.14