LCOV - code coverage report
Current view: top level - port - cpl_vsil_subfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 114 163 69.9 %
Date: 2024-04-28 23:18:46 Functions: 17 20 85.0 %

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

Generated by: LCOV version 1.14