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: 2025-09-10 17:48:50 Functions: 17 47 36.2 %

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

Generated by: LCOV version 1.14