LCOV - code coverage report
Current view: top level - port - cpl_vsil_subfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 125 166 75.3 %
Date: 2025-09-10 17:48:50 Functions: 19 22 86.4 %

          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         232 :     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 nSize, size_t nMemb) override;
      50             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) 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        1754 :     VSISubFileFilesystemHandler() = default;
      69        2242 :     ~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         464 : VSISubFileHandle::~VSISubFileHandle()
      93             : {
      94         232 :     VSISubFileHandle::Close();
      95         464 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                               Close()                                */
      99             : /************************************************************************/
     100             : 
     101         464 : int VSISubFileHandle::Close()
     102             : 
     103             : {
     104         464 :     if (fp == nullptr)
     105         232 :         return -1;
     106         232 :     int nRet = VSIFCloseL(fp);
     107         232 :     fp = nullptr;
     108             : 
     109         232 :     return nRet;
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                                Seek()                                */
     114             : /************************************************************************/
     115             : 
     116        1516 : int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
     117             : 
     118             : {
     119        1516 :     bAtEOF = false;
     120             : 
     121        1516 :     if (nWhence == SEEK_SET)
     122             :     {
     123        1398 :         if (nOffset >
     124        1398 :             std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
     125           0 :             return -1;
     126        1398 :         nOffset += nSubregionOffset;
     127             :     }
     128         118 :     else if (nWhence == SEEK_CUR)
     129             :     {
     130             :         // handle normally.
     131             :     }
     132          93 :     else if (nWhence == SEEK_END)
     133             :     {
     134          93 :         if (nSubregionSize != 0)
     135             :         {
     136          74 :             nOffset = nSubregionOffset + nSubregionSize;
     137          74 :             nWhence = SEEK_SET;
     138             :         }
     139             :     }
     140             :     else
     141             :     {
     142           0 :         errno = EINVAL;
     143           0 :         return -1;
     144             :     }
     145             : 
     146        1516 :     return VSIFSeekL(fp, nOffset, nWhence);
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                                Tell()                                */
     151             : /************************************************************************/
     152             : 
     153        3304 : vsi_l_offset VSISubFileHandle::Tell()
     154             : 
     155             : {
     156        3304 :     vsi_l_offset nBasePos = VSIFTellL(fp);
     157        3304 :     if (nBasePos >= nSubregionOffset)
     158        3304 :         return nBasePos - nSubregionOffset;
     159           0 :     return 0;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                                Read()                                */
     164             : /************************************************************************/
     165             : 
     166        4059 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     167             : 
     168             : {
     169        4059 :     size_t nRet = 0;
     170        4059 :     if (nSubregionSize == 0)
     171             :     {
     172         632 :         nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
     173             :     }
     174             :     else
     175             :     {
     176        3427 :         if (nSize == 0)
     177           0 :             return 0;
     178             : 
     179        3427 :         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     180        3427 :         if (nCurOffset >= nSubregionOffset + nSubregionSize)
     181             :         {
     182           5 :             bAtEOF = true;
     183           5 :             return 0;
     184             :         }
     185             : 
     186        3422 :         const size_t nByteToRead = nSize * nCount;
     187        3422 :         if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize)
     188             :         {
     189             :             const int nRead = static_cast<int>(
     190         182 :                 VSIFReadL(pBuffer, 1,
     191          91 :                           static_cast<size_t>(nSubregionOffset +
     192          91 :                                               nSubregionSize - nCurOffset),
     193          91 :                           fp));
     194          91 :             nRet = nRead / nSize;
     195             :         }
     196             :         else
     197             :         {
     198        3331 :             nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
     199             :         }
     200             :     }
     201             : 
     202        4054 :     if (nRet < nCount)
     203             :     {
     204         216 :         if (fp->Eof())
     205         125 :             bAtEOF = true;
     206             :         else /* if (fp->Error()) */
     207          91 :             bError = true;
     208             :     }
     209             : 
     210        4054 :     return nRet;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                               Write()                                */
     215             : /************************************************************************/
     216             : 
     217        2618 : size_t VSISubFileHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
     218             : 
     219             : {
     220        2618 :     bAtEOF = false;
     221             : 
     222        2618 :     if (nSubregionSize == 0)
     223        2618 :         return VSIFWriteL(pBuffer, nSize, nCount, fp);
     224             : 
     225           0 :     if (nSize == 0)
     226           0 :         return 0;
     227             : 
     228           0 :     const vsi_l_offset nCurOffset = VSIFTellL(fp);
     229           0 :     if (nCurOffset >= nSubregionOffset + nSubregionSize)
     230           0 :         return 0;
     231             : 
     232           0 :     const size_t nByteToWrite = nSize * nCount;
     233           0 :     if (nCurOffset + nByteToWrite > nSubregionOffset + nSubregionSize)
     234             :     {
     235           0 :         const int nWritten = static_cast<int>(VSIFWriteL(
     236             :             pBuffer, 1,
     237           0 :             static_cast<size_t>(nSubregionOffset + nSubregionSize - nCurOffset),
     238           0 :             fp));
     239           0 :         return nWritten / nSize;
     240             :     }
     241             : 
     242           0 :     return VSIFWriteL(pBuffer, nSize, nCount, fp);
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                             ClearErr()                               */
     247             : /************************************************************************/
     248             : 
     249           2 : void VSISubFileHandle::ClearErr()
     250             : 
     251             : {
     252           2 :     fp->ClearErr();
     253           2 :     bAtEOF = false;
     254           2 :     bError = false;
     255           2 : }
     256             : 
     257             : /************************************************************************/
     258             : /*                              Error()                                 */
     259             : /************************************************************************/
     260             : 
     261          22 : int VSISubFileHandle::Error()
     262             : 
     263             : {
     264          22 :     return bError;
     265             : }
     266             : 
     267             : /************************************************************************/
     268             : /*                                Eof()                                 */
     269             : /************************************************************************/
     270             : 
     271          26 : int VSISubFileHandle::Eof()
     272             : 
     273             : {
     274          26 :     return bAtEOF;
     275             : }
     276             : 
     277             : /************************************************************************/
     278             : /* ==================================================================== */
     279             : /*                       VSISubFileFilesystemHandler                    */
     280             : /* ==================================================================== */
     281             : /************************************************************************/
     282             : 
     283             : /************************************************************************/
     284             : /*                           DecomposePath()                            */
     285             : /*                                                                      */
     286             : /*      Parse a path like /vsisubfile/1000_2000,data/abc.tif into an    */
     287             : /*      offset (1000), a size (2000) and a path (data/abc.tif).         */
     288             : /************************************************************************/
     289             : 
     290         383 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
     291             :                                                CPLString &osFilename,
     292             :                                                vsi_l_offset &nSubFileOffset,
     293             :                                                vsi_l_offset &nSubFileSize)
     294             : 
     295             : {
     296         383 :     if (!STARTS_WITH(pszPath, "/vsisubfile/"))
     297           0 :         return FALSE;
     298             : 
     299         383 :     osFilename = "";
     300         383 :     nSubFileOffset = 0;
     301         383 :     nSubFileSize = 0;
     302             : 
     303         383 :     nSubFileOffset =
     304         383 :         CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
     305        2821 :     for (int i = 12; pszPath[i] != '\0'; i++)
     306             :     {
     307        2821 :         if (pszPath[i] == '_' && nSubFileSize == 0)
     308             :         {
     309             :             // -1 is sometimes passed to mean that we don't know the file size
     310             :             // for example when creating a JPEG2000 datastream in a NITF file
     311             :             // Transform it into 0 for correct behavior of Read(), Write() and
     312             :             // Eof().
     313         349 :             if (pszPath[i + 1] == '-')
     314         205 :                 nSubFileSize = 0;
     315             :             else
     316         144 :                 nSubFileSize = CPLScanUIntBig(
     317         144 :                     pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
     318             :         }
     319        2472 :         else if (pszPath[i] == ',')
     320             :         {
     321         383 :             osFilename = pszPath + i + 1;
     322         383 :             return TRUE;
     323             :         }
     324        2089 :         else if (pszPath[i] == '/')
     325             :         {
     326             :             // Missing comma!
     327           0 :             return FALSE;
     328             :         }
     329             :     }
     330             : 
     331           0 :     return FALSE;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                                Open()                                */
     336             : /************************************************************************/
     337             : 
     338             : VSIVirtualHandleUniquePtr
     339         311 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
     340             :                                   const char *pszAccess, bool /* bSetError */,
     341             :                                   CSLConstList /* papszOptions */)
     342             : 
     343             : {
     344         311 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     345           1 :         return nullptr;
     346             : 
     347         620 :     CPLString osSubFilePath;
     348         310 :     vsi_l_offset nOff = 0;
     349         310 :     vsi_l_offset nSize = 0;
     350             : 
     351         310 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     352             :     {
     353           0 :         errno = ENOENT;
     354           0 :         return nullptr;
     355             :     }
     356         310 :     if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
     357             :     {
     358           0 :         return nullptr;
     359             :     }
     360             : 
     361             :     /* -------------------------------------------------------------------- */
     362             :     /*      We can't open the containing file with "w" access, so if        */
     363             :     /*      that is requested use "r+" instead to update in place.          */
     364             :     /* -------------------------------------------------------------------- */
     365         310 :     if (pszAccess[0] == 'w')
     366           5 :         pszAccess = "r+";
     367             : 
     368             :     /* -------------------------------------------------------------------- */
     369             :     /*      Open the underlying file.                                       */
     370             :     /* -------------------------------------------------------------------- */
     371         620 :     auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess);
     372             : 
     373         310 :     if (fp == nullptr)
     374          78 :         return nullptr;
     375             : 
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*      Setup the file handle on this file.                             */
     378             :     /* -------------------------------------------------------------------- */
     379         464 :     auto poHandle = std::make_unique<VSISubFileHandle>();
     380             : 
     381         232 :     poHandle->fp = fp.release();
     382         232 :     poHandle->nSubregionOffset = nOff;
     383         232 :     poHandle->nSubregionSize = nSize;
     384             : 
     385             :     // In read-only mode validate (offset, size) against underlying file size
     386         232 :     if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
     387             :     {
     388         219 :         if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
     389             :         {
     390           0 :             return nullptr;
     391             :         }
     392         219 :         vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
     393             :         // For a directory, the size will be max(vsi_l_offset) / 2
     394         219 :         if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
     395             :         {
     396           0 :             return nullptr;
     397             :         }
     398         219 :         if (nOff + nSize > nFpSize)
     399             :         {
     400           0 :             nSize = nFpSize - nOff;
     401           0 :             poHandle->nSubregionSize = nSize;
     402             :         }
     403             :     }
     404             : 
     405         232 :     if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
     406             :     {
     407           0 :         poHandle.reset();
     408             :     }
     409             : 
     410         232 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                                Stat()                                */
     415             : /************************************************************************/
     416             : 
     417          74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
     418             :                                       VSIStatBufL *psStatBuf, int nFlags)
     419             : 
     420             : {
     421          74 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     422           1 :         return -1;
     423             : 
     424         146 :     CPLString osSubFilePath;
     425          73 :     vsi_l_offset nOff = 0;
     426          73 :     vsi_l_offset nSize = 0;
     427             : 
     428          73 :     memset(psStatBuf, 0, sizeof(VSIStatBufL));
     429             : 
     430          73 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     431             :     {
     432           0 :         errno = ENOENT;
     433           0 :         return -1;
     434             :     }
     435             : 
     436          73 :     const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
     437             : 
     438          73 :     if (nResult == 0)
     439             :     {
     440          47 :         if (nSize != 0)
     441           0 :             psStatBuf->st_size = nSize;
     442          47 :         else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
     443          47 :             psStatBuf->st_size -= nOff;
     444             :         else
     445           0 :             psStatBuf->st_size = 0;
     446             :     }
     447             : 
     448          73 :     return nResult;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                               Unlink()                               */
     453             : /************************************************************************/
     454             : 
     455           0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
     456             : {
     457           0 :     errno = EACCES;
     458           0 :     return -1;
     459             : }
     460             : 
     461             : /************************************************************************/
     462             : /*                               Mkdir()                                */
     463             : /************************************************************************/
     464             : 
     465           0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
     466             :                                        long /* nMode */)
     467             : {
     468           0 :     errno = EACCES;
     469           0 :     return -1;
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                               Rmdir()                                */
     474             : /************************************************************************/
     475             : 
     476           0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
     477             : 
     478             : {
     479           0 :     errno = EACCES;
     480           0 :     return -1;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*                             ReadDirEx()                              */
     485             : /************************************************************************/
     486             : 
     487         136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
     488             :                                               int /* nMaxFiles */)
     489             : {
     490         136 :     errno = EACCES;
     491         136 :     return nullptr;
     492             : }
     493             : 
     494             : /************************************************************************/
     495             : /*                 VSIInstallSubFileFilesystemHandler()                 */
     496             : /************************************************************************/
     497             : 
     498             : /*!
     499             :  \brief Install /vsisubfile/ virtual file handler.
     500             : 
     501             :  \verbatim embed:rst
     502             :  See :ref:`/vsisubfile/ documentation <vsisubfile>`
     503             :  \endverbatim
     504             :  */
     505             : 
     506        1754 : void VSIInstallSubFileHandler()
     507             : {
     508        1754 :     VSIFileManager::InstallHandler("/vsisubfile/",
     509        1754 :                                    new VSISubFileFilesystemHandler);
     510        1754 : }

Generated by: LCOV version 1.14