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-10-17 19:28:35 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         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 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        1763 :     VSISubFileFilesystemHandler() = default;
      69        2244 :     ~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        3305 : vsi_l_offset VSISubFileHandle::Tell()
     154             : 
     155             : {
     156        3305 :     vsi_l_offset nBasePos = VSIFTellL(fp);
     157        3305 :     if (nBasePos >= nSubregionOffset)
     158        3305 :         return nBasePos - nSubregionOffset;
     159           0 :     return 0;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                                Read()                                */
     164             : /************************************************************************/
     165             : 
     166        4064 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     167             : 
     168             : {
     169        4064 :     size_t nRet = 0;
     170        4064 :     if (nSubregionSize == 0)
     171             :     {
     172         632 :         nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
     173             :     }
     174             :     else
     175             :     {
     176        3432 :         if (nSize == 0)
     177           0 :             return 0;
     178             : 
     179        3432 :         const vsi_l_offset nCurOffset = VSIFTellL(fp);
     180        3432 :         if (nCurOffset >= nSubregionOffset + nSubregionSize)
     181             :         {
     182           5 :             bAtEOF = true;
     183           5 :             return 0;
     184             :         }
     185             : 
     186        3427 :         const size_t nByteToRead = nSize * nCount;
     187        3427 :         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        3336 :             nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
     199             :         }
     200             :     }
     201             : 
     202        4059 :     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        4059 :     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         328 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
     291             :                                                CPLString &osFilename,
     292             :                                                vsi_l_offset &nSubFileOffset,
     293             :                                                vsi_l_offset &nSubFileSize)
     294             : 
     295             : {
     296         328 :     if (!STARTS_WITH(pszPath, "/vsisubfile/"))
     297           0 :         return FALSE;
     298             : 
     299         328 :     osFilename = "";
     300         328 :     nSubFileOffset = 0;
     301         328 :     nSubFileSize = 0;
     302             : 
     303         328 :     nSubFileOffset =
     304         328 :         CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
     305        2424 :     for (int i = 12; pszPath[i] != '\0'; i++)
     306             :     {
     307        2424 :         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         294 :             if (pszPath[i + 1] == '-')
     314         145 :                 nSubFileSize = 0;
     315             :             else
     316         149 :                 nSubFileSize = CPLScanUIntBig(
     317         149 :                     pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
     318             :         }
     319        2130 :         else if (pszPath[i] == ',')
     320             :         {
     321         328 :             osFilename = pszPath + i + 1;
     322         328 :             return TRUE;
     323             :         }
     324        1802 :         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         256 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
     340             :                                   const char *pszAccess, bool bSetError,
     341             :                                   CSLConstList papszOptions)
     342             : 
     343             : {
     344         256 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     345           1 :         return nullptr;
     346             : 
     347         510 :     CPLString osSubFilePath;
     348         255 :     vsi_l_offset nOff = 0;
     349         255 :     vsi_l_offset nSize = 0;
     350             : 
     351         255 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     352             :     {
     353           0 :         errno = ENOENT;
     354           0 :         return nullptr;
     355             :     }
     356         255 :     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         255 :     if (pszAccess[0] == 'w')
     366           5 :         pszAccess = "r+";
     367             : 
     368             :     /* -------------------------------------------------------------------- */
     369             :     /*      Open the underlying file.                                       */
     370             :     /* -------------------------------------------------------------------- */
     371             :     auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess,
     372         510 :                                                bSetError, papszOptions);
     373             : 
     374         255 :     if (fp == nullptr)
     375          18 :         return nullptr;
     376             : 
     377             :     /* -------------------------------------------------------------------- */
     378             :     /*      Setup the file handle on this file.                             */
     379             :     /* -------------------------------------------------------------------- */
     380         474 :     auto poHandle = std::make_unique<VSISubFileHandle>();
     381             : 
     382         237 :     poHandle->fp = fp.release();
     383         237 :     poHandle->nSubregionOffset = nOff;
     384         237 :     poHandle->nSubregionSize = nSize;
     385             : 
     386             :     // In read-only mode validate (offset, size) against underlying file size
     387         237 :     if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
     388             :     {
     389         224 :         if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
     390             :         {
     391           0 :             return nullptr;
     392             :         }
     393         224 :         vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
     394             :         // For a directory, the size will be max(vsi_l_offset) / 2
     395         224 :         if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
     396             :         {
     397           0 :             return nullptr;
     398             :         }
     399         224 :         if (nOff + nSize > nFpSize)
     400             :         {
     401           0 :             nSize = nFpSize - nOff;
     402           0 :             poHandle->nSubregionSize = nSize;
     403             :         }
     404             :     }
     405             : 
     406         237 :     if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
     407             :     {
     408           0 :         poHandle.reset();
     409             :     }
     410             : 
     411         237 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     412             : }
     413             : 
     414             : /************************************************************************/
     415             : /*                                Stat()                                */
     416             : /************************************************************************/
     417             : 
     418          74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
     419             :                                       VSIStatBufL *psStatBuf, int nFlags)
     420             : 
     421             : {
     422          74 :     if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
     423           1 :         return -1;
     424             : 
     425         146 :     CPLString osSubFilePath;
     426          73 :     vsi_l_offset nOff = 0;
     427          73 :     vsi_l_offset nSize = 0;
     428             : 
     429          73 :     memset(psStatBuf, 0, sizeof(VSIStatBufL));
     430             : 
     431          73 :     if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
     432             :     {
     433           0 :         errno = ENOENT;
     434           0 :         return -1;
     435             :     }
     436             : 
     437          73 :     const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
     438             : 
     439          73 :     if (nResult == 0)
     440             :     {
     441          47 :         if (nSize != 0)
     442           0 :             psStatBuf->st_size = nSize;
     443          47 :         else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
     444          47 :             psStatBuf->st_size -= nOff;
     445             :         else
     446           0 :             psStatBuf->st_size = 0;
     447             :     }
     448             : 
     449          73 :     return nResult;
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /*                               Unlink()                               */
     454             : /************************************************************************/
     455             : 
     456           0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
     457             : {
     458           0 :     errno = EACCES;
     459           0 :     return -1;
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*                               Mkdir()                                */
     464             : /************************************************************************/
     465             : 
     466           0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
     467             :                                        long /* nMode */)
     468             : {
     469           0 :     errno = EACCES;
     470           0 :     return -1;
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                               Rmdir()                                */
     475             : /************************************************************************/
     476             : 
     477           0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
     478             : 
     479             : {
     480           0 :     errno = EACCES;
     481           0 :     return -1;
     482             : }
     483             : 
     484             : /************************************************************************/
     485             : /*                             ReadDirEx()                              */
     486             : /************************************************************************/
     487             : 
     488         136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
     489             :                                               int /* nMaxFiles */)
     490             : {
     491         136 :     errno = EACCES;
     492         136 :     return nullptr;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                 VSIInstallSubFileFilesystemHandler()                 */
     497             : /************************************************************************/
     498             : 
     499             : /*!
     500             :  \brief Install /vsisubfile/ virtual file handler.
     501             : 
     502             :  \verbatim embed:rst
     503             :  See :ref:`/vsisubfile/ documentation <vsisubfile>`
     504             :  \endverbatim
     505             :  */
     506             : 
     507        1763 : void VSIInstallSubFileHandler()
     508             : {
     509        1763 :     VSIFileManager::InstallHandler("/vsisubfile/",
     510        1763 :                                    new VSISubFileFilesystemHandler);
     511        1763 : }

Generated by: LCOV version 1.14