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

Generated by: LCOV version 1.14