LCOV - code coverage report
Current view: top level - port - cpl_vsil_subfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 124 161 77.0 %
Date: 2026-01-23 00:08:21 Functions: 18 22 81.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VSI Virtual File System
       4             :  * Purpose:  Implementation of subfile virtual IO functions.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_vsi.h"
      16             : 
      17             : #include <cerrno>
      18             : #include <cstddef>
      19             : #include <cstring>
      20             : #include <limits>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_multiproc.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi_virtual.h"
      26             : 
      27             : /************************************************************************/
      28             : /* ==================================================================== */
      29             : /*                           VSISubFileHandle                           */
      30             : /* ==================================================================== */
      31             : /************************************************************************/
      32             : 
      33             : class VSISubFileHandle final : public VSIVirtualHandle
      34             : {
      35             :     CPL_DISALLOW_COPY_ASSIGN(VSISubFileHandle)
      36             : 
      37             :   public:
      38             :     VSILFILE *fp = nullptr;
      39             :     vsi_l_offset nSubregionOffset = 0;
      40             :     vsi_l_offset nSubregionSize = 0;
      41             :     bool bAtEOF = false;
      42             :     bool bError = false;
      43             : 
      44         237 :     VSISubFileHandle() = default;
      45             :     ~VSISubFileHandle() override;
      46             : 
      47             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
      48             :     vsi_l_offset Tell() override;
      49             :     size_t Read(void *pBuffer, size_t nBytes) override;
      50             :     size_t Write(const void *pBuffer, size_t nBytes) override;
      51             :     void ClearErr() override;
      52             :     int Eof() override;
      53             :     int Error() override;
      54             :     int Close() override;
      55             : };
      56             : 
      57             : /************************************************************************/
      58             : /* ==================================================================== */
      59             : /*                   VSISubFileFilesystemHandler                        */
      60             : /* ==================================================================== */
      61             : /************************************************************************/
      62             : 
      63             : class VSISubFileFilesystemHandler final : public VSIFilesystemHandler
      64             : {
      65             :     CPL_DISALLOW_COPY_ASSIGN(VSISubFileFilesystemHandler)
      66             : 
      67             :   public:
      68        1788 :     VSISubFileFilesystemHandler() = default;
      69        1126 :     ~VSISubFileFilesystemHandler() override = default;
      70             : 
      71             :     static int DecomposePath(const char *pszPath, CPLString &osFilename,
      72             :                              vsi_l_offset &nSubFileOffset,
      73             :                              vsi_l_offset &nSubFileSize);
      74             : 
      75             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
      76             :                                    const char *pszAccess, bool bSetError,
      77             :                                    CSLConstList /* papszOptions */) override;
      78             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
      79             :              int nFlags) override;
      80             :     int Unlink(const char *pszFilename) override;
      81             :     int Mkdir(const char *pszDirname, long nMode) override;
      82             :     int Rmdir(const char *pszDirname) override;
      83             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
      84             : };
      85             : 
      86             : /************************************************************************/
      87             : /* ==================================================================== */
      88             : /*                             VSISubFileHandle                         */
      89             : /* ==================================================================== */
      90             : /************************************************************************/
      91             : 
      92         474 : VSISubFileHandle::~VSISubFileHandle()
      93             : {
      94         237 :     VSISubFileHandle::Close();
      95         474 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                               Close()                                */
      99             : /************************************************************************/
     100             : 
     101         474 : int VSISubFileHandle::Close()
     102             : 
     103             : {
     104         474 :     if (fp == nullptr)
     105         237 :         return -1;
     106         237 :     int nRet = VSIFCloseL(fp);
     107         237 :     fp = nullptr;
     108             : 
     109         237 :     return nRet;
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                                Seek()                                */
     114             : /************************************************************************/
     115             : 
     116        1518 : int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
     117             : 
     118             : {
     119        1518 :     bAtEOF = false;
     120             : 
     121        1518 :     if (nWhence == SEEK_SET)
     122             :     {
     123        1399 :         if (nOffset >
     124        1399 :             std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
     125           0 :             return -1;
     126        1399 :         nOffset += nSubregionOffset;
     127             :     }
     128         119 :     else if (nWhence == SEEK_CUR)
     129             :     {
     130             :         // handle normally.
     131             :     }
     132          94 :     else if (nWhence == SEEK_END)
     133             :     {
     134          94 :         if (nSubregionSize != 0)
     135             :         {
     136          75 :             nOffset = nSubregionOffset + nSubregionSize;
     137          75 :             nWhence = SEEK_SET;
     138             :         }
     139             :     }
     140             :     else
     141             :     {
     142           0 :         errno = EINVAL;
     143           0 :         return -1;
     144             :     }
     145             : 
     146        1518 :     return VSIFSeekL(fp, nOffset, nWhence);
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                                Tell()                                */
     151             : /************************************************************************/
     152             : 
     153        3373 : vsi_l_offset VSISubFileHandle::Tell()
     154             : 
     155             : {
     156        3373 :     vsi_l_offset nBasePos = VSIFTellL(fp);
     157        3373 :     if (nBasePos >= nSubregionOffset)
     158        3373 :         return nBasePos - nSubregionOffset;
     159           0 :     return 0;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                                Read()                                */
     164             : /************************************************************************/
     165             : 
     166        4064 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nByteToRead)
     167             : 
     168             : {
     169        4064 :     if (nByteToRead == 0)
     170          20 :         return 0;
     171             : 
     172        4044 :     size_t nRet = 0;
     173        4044 :     if (nSubregionSize == 0)
     174             :     {
     175         612 :         nRet = fp->Read(pBuffer, nByteToRead);
     176             :     }
     177             :     else
     178             :     {
     179        3432 :         const vsi_l_offset nCurOffset = fp->Tell();
     180        3432 :         if (nCurOffset >= nSubregionOffset + nSubregionSize)
     181             :         {
     182           5 :             bAtEOF = true;
     183           5 :             return 0;
     184             :         }
     185             : 
     186        3427 :         if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize)
     187             :         {
     188          91 :             nRet = fp->Read(pBuffer,
     189          91 :                             static_cast<size_t>(nSubregionOffset +
     190          91 :                                                 nSubregionSize - nCurOffset));
     191             :         }
     192             :         else
     193             :         {
     194        3336 :             nRet = fp->Read(pBuffer, nByteToRead);
     195             :         }
     196             :     }
     197             : 
     198        4039 :     if (nRet < nByteToRead)
     199             :     {
     200         216 :         if (fp->Eof())
     201         125 :             bAtEOF = true;
     202             :         else /* if (fp->Error()) */
     203          91 :             bError = true;
     204             :     }
     205             : 
     206        4039 :     return nRet;
     207             : }
     208             : 
     209             : /************************************************************************/
     210             : /*                               Write()                                */
     211             : /************************************************************************/
     212             : 
     213        2618 : size_t VSISubFileHandle::Write(const void *pBuffer, size_t nBytes)
     214             : 
     215             : {
     216        2618 :     bAtEOF = false;
     217             : 
     218        2618 :     if (nBytes == 0)
     219           0 :         return 0;
     220             : 
     221        2618 :     if (nSubregionSize == 0)
     222        2618 :         return fp->Write(pBuffer, nBytes);
     223             : 
     224           0 :     const vsi_l_offset nCurOffset = VSIFTellL(fp);
     225           0 :     if (nCurOffset >= nSubregionOffset + nSubregionSize)
     226           0 :         return 0;
     227             : 
     228           0 :     if (nCurOffset + nBytes > nSubregionOffset + nSubregionSize)
     229             :     {
     230           0 :         return fp->Write(pBuffer,
     231           0 :                          static_cast<size_t>(nSubregionOffset + nSubregionSize -
     232           0 :                                              nCurOffset));
     233             :     }
     234             : 
     235           0 :     return fp->Write(pBuffer, nBytes);
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                             ClearErr()                               */
     240             : /************************************************************************/
     241             : 
     242          70 : void VSISubFileHandle::ClearErr()
     243             : 
     244             : {
     245          70 :     fp->ClearErr();
     246          70 :     bAtEOF = false;
     247          70 :     bError = false;
     248          70 : }
     249             : 
     250             : /************************************************************************/
     251             : /*                              Error()                                 */
     252             : /************************************************************************/
     253             : 
     254          90 : int VSISubFileHandle::Error()
     255             : 
     256             : {
     257          90 :     return bError;
     258             : }
     259             : 
     260             : /************************************************************************/
     261             : /*                                Eof()                                 */
     262             : /************************************************************************/
     263             : 
     264          94 : int VSISubFileHandle::Eof()
     265             : 
     266             : {
     267          94 :     return bAtEOF;
     268             : }
     269             : 
     270             : /************************************************************************/
     271             : /* ==================================================================== */
     272             : /*                       VSISubFileFilesystemHandler                    */
     273             : /* ==================================================================== */
     274             : /************************************************************************/
     275             : 
     276             : /************************************************************************/
     277             : /*                           DecomposePath()                            */
     278             : /*                                                                      */
     279             : /*      Parse a path like /vsisubfile/1000_2000,data/abc.tif into an    */
     280             : /*      offset (1000), a size (2000) and a path (data/abc.tif).         */
     281             : /************************************************************************/
     282             : 
     283         328 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
     284             :                                                CPLString &osFilename,
     285             :                                                vsi_l_offset &nSubFileOffset,
     286             :                                                vsi_l_offset &nSubFileSize)
     287             : 
     288             : {
     289         328 :     if (!STARTS_WITH(pszPath, "/vsisubfile/"))
     290           0 :         return FALSE;
     291             : 
     292         328 :     osFilename = "";
     293         328 :     nSubFileOffset = 0;
     294         328 :     nSubFileSize = 0;
     295             : 
     296         328 :     nSubFileOffset =
     297         328 :         CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
     298        2424 :     for (int i = 12; pszPath[i] != '\0'; i++)
     299             :     {
     300        2424 :         if (pszPath[i] == '_' && nSubFileSize == 0)
     301             :         {
     302             :             // -1 is sometimes passed to mean that we don't know the file size
     303             :             // for example when creating a JPEG2000 datastream in a NITF file
     304             :             // Transform it into 0 for correct behavior of Read(), Write() and
     305             :             // Eof().
     306         294 :             if (pszPath[i + 1] == '-')
     307         145 :                 nSubFileSize = 0;
     308             :             else
     309         149 :                 nSubFileSize = CPLScanUIntBig(
     310         149 :                     pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
     311             :         }
     312        2130 :         else if (pszPath[i] == ',')
     313             :         {
     314         328 :             osFilename = pszPath + i + 1;
     315         328 :             return TRUE;
     316             :         }
     317        1802 :         else if (pszPath[i] == '/')
     318             :         {
     319             :             // Missing comma!
     320           0 :             return FALSE;
     321             :         }
     322             :     }
     323             : 
     324           0 :     return FALSE;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                                Open()                                */
     329             : /************************************************************************/
     330             : 
     331             : VSIVirtualHandleUniquePtr
     332         256 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
     333             :                                   const char *pszAccess, bool bSetError,
     334             :                                   CSLConstList papszOptions)
     335             : 
     336             : {
     337         256 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     338           1 :         return nullptr;
     339             : 
     340         510 :     CPLString osSubFilePath;
     341         255 :     vsi_l_offset nOff = 0;
     342         255 :     vsi_l_offset nSize = 0;
     343             : 
     344         255 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     345             :     {
     346           0 :         errno = ENOENT;
     347           0 :         return nullptr;
     348             :     }
     349         255 :     if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
     350             :     {
     351           0 :         return nullptr;
     352             :     }
     353             : 
     354             :     /* -------------------------------------------------------------------- */
     355             :     /*      We can't open the containing file with "w" access, so if        */
     356             :     /*      that is requested use "r+" instead to update in place.          */
     357             :     /* -------------------------------------------------------------------- */
     358         255 :     if (pszAccess[0] == 'w')
     359           5 :         pszAccess = "r+";
     360             : 
     361             :     /* -------------------------------------------------------------------- */
     362             :     /*      Open the underlying file.                                       */
     363             :     /* -------------------------------------------------------------------- */
     364             :     auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess,
     365         510 :                                                bSetError, papszOptions);
     366             : 
     367         255 :     if (fp == nullptr)
     368          18 :         return nullptr;
     369             : 
     370             :     /* -------------------------------------------------------------------- */
     371             :     /*      Setup the file handle on this file.                             */
     372             :     /* -------------------------------------------------------------------- */
     373         474 :     auto poHandle = std::make_unique<VSISubFileHandle>();
     374             : 
     375         237 :     poHandle->fp = fp.release();
     376         237 :     poHandle->nSubregionOffset = nOff;
     377         237 :     poHandle->nSubregionSize = nSize;
     378             : 
     379             :     // In read-only mode validate (offset, size) against underlying file size
     380         237 :     if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
     381             :     {
     382         224 :         if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
     383             :         {
     384           0 :             return nullptr;
     385             :         }
     386         224 :         vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
     387             :         // For a directory, the size will be max(vsi_l_offset) / 2
     388         224 :         if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
     389             :         {
     390           0 :             return nullptr;
     391             :         }
     392         224 :         if (nOff + nSize > nFpSize)
     393             :         {
     394           0 :             nSize = nFpSize - nOff;
     395           0 :             poHandle->nSubregionSize = nSize;
     396             :         }
     397             :     }
     398             : 
     399         237 :     if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
     400             :     {
     401           0 :         poHandle.reset();
     402             :     }
     403             : 
     404         237 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     405             : }
     406             : 
     407             : /************************************************************************/
     408             : /*                                Stat()                                */
     409             : /************************************************************************/
     410             : 
     411          74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
     412             :                                       VSIStatBufL *psStatBuf, int nFlags)
     413             : 
     414             : {
     415          74 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     416           1 :         return -1;
     417             : 
     418         146 :     CPLString osSubFilePath;
     419          73 :     vsi_l_offset nOff = 0;
     420          73 :     vsi_l_offset nSize = 0;
     421             : 
     422          73 :     memset(psStatBuf, 0, sizeof(VSIStatBufL));
     423             : 
     424          73 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     425             :     {
     426           0 :         errno = ENOENT;
     427           0 :         return -1;
     428             :     }
     429             : 
     430          73 :     const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
     431             : 
     432          73 :     if (nResult == 0)
     433             :     {
     434          47 :         if (nSize != 0)
     435           0 :             psStatBuf->st_size = nSize;
     436          47 :         else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
     437          47 :             psStatBuf->st_size -= nOff;
     438             :         else
     439           0 :             psStatBuf->st_size = 0;
     440             :     }
     441             : 
     442          73 :     return nResult;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                               Unlink()                               */
     447             : /************************************************************************/
     448             : 
     449           0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
     450             : {
     451           0 :     errno = EACCES;
     452           0 :     return -1;
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                               Mkdir()                                */
     457             : /************************************************************************/
     458             : 
     459           0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
     460             :                                        long /* nMode */)
     461             : {
     462           0 :     errno = EACCES;
     463           0 :     return -1;
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                               Rmdir()                                */
     468             : /************************************************************************/
     469             : 
     470           0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
     471             : 
     472             : {
     473           0 :     errno = EACCES;
     474           0 :     return -1;
     475             : }
     476             : 
     477             : /************************************************************************/
     478             : /*                             ReadDirEx()                              */
     479             : /************************************************************************/
     480             : 
     481         136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
     482             :                                               int /* nMaxFiles */)
     483             : {
     484         136 :     errno = EACCES;
     485         136 :     return nullptr;
     486             : }
     487             : 
     488             : /************************************************************************/
     489             : /*                 VSIInstallSubFileFilesystemHandler()                 */
     490             : /************************************************************************/
     491             : 
     492             : /*!
     493             :  \brief Install /vsisubfile/ virtual file handler.
     494             : 
     495             :  \verbatim embed:rst
     496             :  See :ref:`/vsisubfile/ documentation <vsisubfile>`
     497             :  \endverbatim
     498             :  */
     499             : 
     500        1788 : void VSIInstallSubFileHandler()
     501             : {
     502        1788 :     VSIFileManager::InstallHandler(
     503        3576 :         "/vsisubfile/", std::make_shared<VSISubFileFilesystemHandler>());
     504        1788 : }

Generated by: LCOV version 1.14