LCOV - code coverage report
Current view: top level - port - cpl_vsil_sparsefile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 145 170 85.3 %
Date: 2024-04-29 01:40:10 Functions: 15 20 75.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VSI Virtual File System
       4             :  * Purpose:  Implementation of sparse file virtual io driver.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2010-2013, 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 <cstdlib>
      36             : #include <cstring>
      37             : 
      38             : #if HAVE_FCNTL_H
      39             : #include <fcntl.h>
      40             : #endif
      41             : 
      42             : #include <algorithm>
      43             : #include <map>
      44             : #include <memory>
      45             : #include <vector>
      46             : 
      47             : #include "cpl_conv.h"
      48             : #include "cpl_error.h"
      49             : #include "cpl_minixml.h"
      50             : #include "cpl_multiproc.h"
      51             : #include "cpl_string.h"
      52             : #include "cpl_vsi_virtual.h"
      53             : 
      54             : class SFRegion
      55             : {
      56             :   public:
      57             :     CPLString osFilename{};
      58             :     VSILFILE *fp = nullptr;
      59             :     GUIntBig nDstOffset = 0;
      60             :     GUIntBig nSrcOffset = 0;
      61             :     GUIntBig nLength = 0;
      62             :     GByte byValue = 0;
      63             :     bool bTriedOpen = false;
      64             : };
      65             : 
      66             : /************************************************************************/
      67             : /* ==================================================================== */
      68             : /*                         VSISparseFileHandle                          */
      69             : /* ==================================================================== */
      70             : /************************************************************************/
      71             : 
      72             : class VSISparseFileFilesystemHandler;
      73             : 
      74             : class VSISparseFileHandle : public VSIVirtualHandle
      75             : {
      76             :     CPL_DISALLOW_COPY_ASSIGN(VSISparseFileHandle)
      77             : 
      78             :     VSISparseFileFilesystemHandler *m_poFS = nullptr;
      79             :     bool bEOF = false;
      80             : 
      81             :   public:
      82          72 :     explicit VSISparseFileHandle(VSISparseFileFilesystemHandler *poFS)
      83          72 :         : m_poFS(poFS)
      84             :     {
      85          72 :     }
      86             : 
      87             :     GUIntBig nOverallLength = 0;
      88             :     GUIntBig nCurOffset = 0;
      89             : 
      90             :     std::vector<SFRegion> aoRegions{};
      91             : 
      92             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
      93             :     vsi_l_offset Tell() override;
      94             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
      95             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
      96             :     int Eof() override;
      97             :     int Close() override;
      98             : };
      99             : 
     100             : /************************************************************************/
     101             : /* ==================================================================== */
     102             : /*                   VSISparseFileFilesystemHandler                     */
     103             : /* ==================================================================== */
     104             : /************************************************************************/
     105             : 
     106             : class VSISparseFileFilesystemHandler : public VSIFilesystemHandler
     107             : {
     108             :     std::map<GIntBig, int> oRecOpenCount{};
     109             :     CPL_DISALLOW_COPY_ASSIGN(VSISparseFileFilesystemHandler)
     110             : 
     111             :   public:
     112        1228 :     VSISparseFileFilesystemHandler() = default;
     113        1704 :     ~VSISparseFileFilesystemHandler() override = default;
     114             : 
     115             :     int DecomposePath(const char *pszPath, CPLString &osFilename,
     116             :                       vsi_l_offset &nSparseFileOffset,
     117             :                       vsi_l_offset &nSparseFileSize);
     118             : 
     119             :     // TODO(schwehr): Fix VSISparseFileFilesystemHandler::Stat to not need
     120             :     // using.
     121             :     using VSIFilesystemHandler::Open;
     122             : 
     123             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
     124             :                            bool bSetError,
     125             :                            CSLConstList /* papszOptions */) override;
     126             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     127             :              int nFlags) override;
     128             :     int Unlink(const char *pszFilename) override;
     129             :     int Mkdir(const char *pszDirname, long nMode) override;
     130             :     int Rmdir(const char *pszDirname) override;
     131             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     132             : 
     133         233 :     int GetRecCounter()
     134             :     {
     135         233 :         return oRecOpenCount[CPLGetPID()];
     136             :     }
     137             : 
     138        7124 :     void IncRecCounter()
     139             :     {
     140        7124 :         oRecOpenCount[CPLGetPID()]++;
     141        7124 :     }
     142             : 
     143        7124 :     void DecRecCounter()
     144             :     {
     145        7124 :         oRecOpenCount[CPLGetPID()]--;
     146        7124 :     }
     147             : };
     148             : 
     149             : /************************************************************************/
     150             : /* ==================================================================== */
     151             : /*                             VSISparseFileHandle                      */
     152             : /* ==================================================================== */
     153             : /************************************************************************/
     154             : 
     155             : /************************************************************************/
     156             : /*                               Close()                                */
     157             : /************************************************************************/
     158             : 
     159          65 : int VSISparseFileHandle::Close()
     160             : 
     161             : {
     162         212 :     for (unsigned int i = 0; i < aoRegions.size(); i++)
     163             :     {
     164         147 :         if (aoRegions[i].fp != nullptr)
     165         121 :             CPL_IGNORE_RET_VAL(VSIFCloseL(aoRegions[i].fp));
     166             :     }
     167             : 
     168          65 :     return 0;
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                                Seek()                                */
     173             : /************************************************************************/
     174             : 
     175         336 : int VSISparseFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
     176             : 
     177             : {
     178         336 :     bEOF = false;
     179         336 :     if (nWhence == SEEK_SET)
     180         265 :         nCurOffset = nOffset;
     181          71 :     else if (nWhence == SEEK_CUR)
     182             :     {
     183          60 :         nCurOffset += nOffset;
     184             :     }
     185          11 :     else if (nWhence == SEEK_END)
     186             :     {
     187          11 :         nCurOffset = nOverallLength + nOffset;
     188             :     }
     189             :     else
     190             :     {
     191           0 :         errno = EINVAL;
     192           0 :         return -1;
     193             :     }
     194             : 
     195         336 :     return 0;
     196             : }
     197             : 
     198             : /************************************************************************/
     199             : /*                                Tell()                                */
     200             : /************************************************************************/
     201             : 
     202          79 : vsi_l_offset VSISparseFileHandle::Tell()
     203             : 
     204             : {
     205          79 :     return nCurOffset;
     206             : }
     207             : 
     208             : /************************************************************************/
     209             : /*                                Read()                                */
     210             : /************************************************************************/
     211             : 
     212        7156 : size_t VSISparseFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     213             : 
     214             : {
     215        7156 :     if (nCurOffset >= nOverallLength)
     216             :     {
     217           5 :         bEOF = true;
     218           5 :         return 0;
     219             :     }
     220             : 
     221             :     /* -------------------------------------------------------------------- */
     222             :     /*      Find what region we are in, searching linearly from the         */
     223             :     /*      start.                                                          */
     224             :     /* -------------------------------------------------------------------- */
     225        7151 :     unsigned int iRegion = 0;  // Used after for.
     226             : 
     227       22233 :     for (; iRegion < aoRegions.size(); iRegion++)
     228             :     {
     229       44461 :         if (nCurOffset >= aoRegions[iRegion].nDstOffset &&
     230       22230 :             nCurOffset <
     231       22230 :                 aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength)
     232        7149 :             break;
     233             :     }
     234             : 
     235        7151 :     size_t nBytesRequested = nSize * nCount;
     236        7151 :     if (nBytesRequested == 0)
     237             :     {
     238           0 :         return 0;
     239             :     }
     240        7151 :     if (nCurOffset + nBytesRequested > nOverallLength)
     241             :     {
     242          12 :         nBytesRequested = static_cast<size_t>(nOverallLength - nCurOffset);
     243          12 :         bEOF = true;
     244             :     }
     245             : 
     246             :     /* -------------------------------------------------------------------- */
     247             :     /*      Default to zeroing the buffer if no corresponding region was    */
     248             :     /*      found.                                                          */
     249             :     /* -------------------------------------------------------------------- */
     250        7151 :     if (iRegion == aoRegions.size())
     251             :     {
     252           2 :         memset(pBuffer, 0, nBytesRequested);
     253           2 :         nCurOffset += nBytesRequested;
     254           2 :         return nBytesRequested / nSize;
     255             :     }
     256             : 
     257             :     /* -------------------------------------------------------------------- */
     258             :     /*      If this request crosses region boundaries, split it into two    */
     259             :     /*      requests.                                                       */
     260             :     /* -------------------------------------------------------------------- */
     261        7149 :     size_t nBytesReturnCount = 0;
     262             :     const GUIntBig nEndOffsetOfRegion =
     263        7149 :         aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength;
     264             : 
     265        7149 :     if (nCurOffset + nBytesRequested > nEndOffsetOfRegion)
     266             :     {
     267          86 :         const size_t nExtraBytes = static_cast<size_t>(
     268          86 :             nCurOffset + nBytesRequested - nEndOffsetOfRegion);
     269             :         // Recurse to get the rest of the request.
     270             : 
     271          86 :         const GUIntBig nCurOffsetSave = nCurOffset;
     272          86 :         nCurOffset += nBytesRequested - nExtraBytes;
     273          86 :         bool bEOFSave = bEOF;
     274          86 :         bEOF = false;
     275         172 :         const size_t nBytesRead = this->Read(static_cast<char *>(pBuffer) +
     276          86 :                                                  nBytesRequested - nExtraBytes,
     277          86 :                                              1, nExtraBytes);
     278          86 :         nCurOffset = nCurOffsetSave;
     279          86 :         bEOF = bEOFSave;
     280             : 
     281          86 :         nBytesReturnCount += nBytesRead;
     282          86 :         nBytesRequested -= nExtraBytes;
     283             :     }
     284             : 
     285             :     /* -------------------------------------------------------------------- */
     286             :     /*      Handle a constant region.                                       */
     287             :     /* -------------------------------------------------------------------- */
     288        7149 :     if (aoRegions[iRegion].osFilename.empty())
     289             :     {
     290          25 :         memset(pBuffer, aoRegions[iRegion].byValue,
     291             :                static_cast<size_t>(nBytesRequested));
     292             : 
     293          25 :         nBytesReturnCount += nBytesRequested;
     294             :     }
     295             : 
     296             :     /* -------------------------------------------------------------------- */
     297             :     /*      Otherwise handle as a file.                                     */
     298             :     /* -------------------------------------------------------------------- */
     299             :     else
     300             :     {
     301        7124 :         if (aoRegions[iRegion].fp == nullptr)
     302             :         {
     303         121 :             if (!aoRegions[iRegion].bTriedOpen)
     304             :             {
     305         242 :                 aoRegions[iRegion].fp =
     306         121 :                     VSIFOpenL(aoRegions[iRegion].osFilename, "r");
     307         121 :                 if (aoRegions[iRegion].fp == nullptr)
     308             :                 {
     309           0 :                     CPLDebug("/vsisparse/", "Failed to open '%s'.",
     310           0 :                              aoRegions[iRegion].osFilename.c_str());
     311             :                 }
     312         121 :                 aoRegions[iRegion].bTriedOpen = true;
     313             :             }
     314         121 :             if (aoRegions[iRegion].fp == nullptr)
     315             :             {
     316           0 :                 return 0;
     317             :             }
     318             :         }
     319             : 
     320        7124 :         if (VSIFSeekL(aoRegions[iRegion].fp,
     321        7124 :                       nCurOffset - aoRegions[iRegion].nDstOffset +
     322        7124 :                           aoRegions[iRegion].nSrcOffset,
     323        7124 :                       SEEK_SET) != 0)
     324           0 :             return 0;
     325             : 
     326        7124 :         m_poFS->IncRecCounter();
     327             :         const size_t nBytesRead =
     328        7124 :             VSIFReadL(pBuffer, 1, static_cast<size_t>(nBytesRequested),
     329        7124 :                       aoRegions[iRegion].fp);
     330        7124 :         m_poFS->DecRecCounter();
     331             : 
     332        7124 :         nBytesReturnCount += nBytesRead;
     333             :     }
     334             : 
     335        7149 :     nCurOffset += nBytesReturnCount;
     336             : 
     337        7149 :     return nBytesReturnCount / nSize;
     338             : }
     339             : 
     340             : /************************************************************************/
     341             : /*                               Write()                                */
     342             : /************************************************************************/
     343             : 
     344           0 : size_t VSISparseFileHandle::Write(const void * /* pBuffer */,
     345             :                                   size_t /* nSize */, size_t /* nCount */)
     346             : {
     347           0 :     errno = EBADF;
     348           0 :     return 0;
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                                Eof()                                 */
     353             : /************************************************************************/
     354             : 
     355           0 : int VSISparseFileHandle::Eof()
     356             : 
     357             : {
     358           0 :     return bEOF ? 1 : 0;
     359             : }
     360             : 
     361             : /************************************************************************/
     362             : /* ==================================================================== */
     363             : /*                       VSISparseFileFilesystemHandler                 */
     364             : /* ==================================================================== */
     365             : /************************************************************************/
     366             : 
     367             : /************************************************************************/
     368             : /*                                Open()                                */
     369             : /************************************************************************/
     370             : 
     371         235 : VSIVirtualHandle *VSISparseFileFilesystemHandler::Open(
     372             :     const char *pszFilename, const char *pszAccess, bool /* bSetError */,
     373             :     CSLConstList /* papszOptions */)
     374             : 
     375             : {
     376         235 :     if (!STARTS_WITH_CI(pszFilename, "/vsisparse/"))
     377           2 :         return nullptr;
     378             : 
     379         233 :     if (!EQUAL(pszAccess, "r") && !EQUAL(pszAccess, "rb"))
     380             :     {
     381           0 :         errno = EACCES;
     382           0 :         return nullptr;
     383             :     }
     384             : 
     385             :     // Arbitrary number.
     386         233 :     if (GetRecCounter() == 32)
     387           0 :         return nullptr;
     388             : 
     389         466 :     const CPLString osSparseFilePath = pszFilename + 11;
     390             : 
     391             :     /* -------------------------------------------------------------------- */
     392             :     /*      Does this file even exist?                                      */
     393             :     /* -------------------------------------------------------------------- */
     394         233 :     VSILFILE *fp = VSIFOpenL(osSparseFilePath, "r");
     395         233 :     if (fp == nullptr)
     396         161 :         return nullptr;
     397          72 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     398             : 
     399             :     /* -------------------------------------------------------------------- */
     400             :     /*      Read the XML file.                                              */
     401             :     /* -------------------------------------------------------------------- */
     402          72 :     CPLXMLNode *psXMLRoot = CPLParseXMLFile(osSparseFilePath);
     403             : 
     404          72 :     if (psXMLRoot == nullptr)
     405           0 :         return nullptr;
     406             : 
     407             :     /* -------------------------------------------------------------------- */
     408             :     /*      Setup the file handle on this file.                             */
     409             :     /* -------------------------------------------------------------------- */
     410          72 :     VSISparseFileHandle *poHandle = new VSISparseFileHandle(this);
     411             : 
     412             :     /* -------------------------------------------------------------------- */
     413             :     /*      Translate the desired fields out of the XML tree.               */
     414             :     /* -------------------------------------------------------------------- */
     415         273 :     for (CPLXMLNode *psRegion = psXMLRoot->psChild; psRegion != nullptr;
     416         201 :          psRegion = psRegion->psNext)
     417             :     {
     418         201 :         if (psRegion->eType != CXT_Element)
     419          29 :             continue;
     420             : 
     421         200 :         if (!EQUAL(psRegion->pszValue, "SubfileRegion") &&
     422          60 :             !EQUAL(psRegion->pszValue, "ConstantRegion"))
     423          28 :             continue;
     424             : 
     425         344 :         SFRegion oRegion;
     426             : 
     427         172 :         oRegion.osFilename = CPLGetXMLValue(psRegion, "Filename", "");
     428         172 :         if (atoi(CPLGetXMLValue(psRegion, "Filename.relative", "0")) != 0)
     429             :         {
     430          34 :             const CPLString osSFPath = CPLGetPath(osSparseFilePath);
     431             :             oRegion.osFilename =
     432          34 :                 CPLFormFilename(osSFPath, oRegion.osFilename, nullptr);
     433             :         }
     434             : 
     435             :         // TODO(schwehr): Symbolic constant and an explanation for 32.
     436         172 :         oRegion.nDstOffset = CPLScanUIntBig(
     437             :             CPLGetXMLValue(psRegion, "DestinationOffset", "0"), 32);
     438             : 
     439         172 :         oRegion.nSrcOffset =
     440         172 :             CPLScanUIntBig(CPLGetXMLValue(psRegion, "SourceOffset", "0"), 32);
     441             : 
     442         172 :         oRegion.nLength =
     443         172 :             CPLScanUIntBig(CPLGetXMLValue(psRegion, "RegionLength", "0"), 32);
     444             : 
     445         172 :         oRegion.byValue =
     446         172 :             static_cast<GByte>(atoi(CPLGetXMLValue(psRegion, "Value", "0")));
     447             : 
     448         172 :         poHandle->aoRegions.push_back(oRegion);
     449             :     }
     450             : 
     451             :     /* -------------------------------------------------------------------- */
     452             :     /*      Get sparse file length, use maximum bound of regions if not     */
     453             :     /*      explicit in file.                                               */
     454             :     /* -------------------------------------------------------------------- */
     455          72 :     poHandle->nOverallLength =
     456          72 :         CPLScanUIntBig(CPLGetXMLValue(psXMLRoot, "Length", "0"), 32);
     457          72 :     if (poHandle->nOverallLength == 0)
     458             :     {
     459         132 :         for (unsigned int i = 0; i < poHandle->aoRegions.size(); i++)
     460             :         {
     461          88 :             poHandle->nOverallLength = std::max(
     462         176 :                 poHandle->nOverallLength, poHandle->aoRegions[i].nDstOffset +
     463          88 :                                               poHandle->aoRegions[i].nLength);
     464             :         }
     465             :     }
     466             : 
     467          72 :     CPLDestroyXMLNode(psXMLRoot);
     468             : 
     469          72 :     return poHandle;
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                                Stat()                                */
     474             : /************************************************************************/
     475             : 
     476          53 : int VSISparseFileFilesystemHandler::Stat(const char *pszFilename,
     477             :                                          VSIStatBufL *psStatBuf, int nFlags)
     478             : 
     479             : {
     480             :     // TODO(schwehr): Fix this so that the using statement is not needed.
     481             :     // Will just adding the bool for bSetError be okay?
     482          53 :     VSIVirtualHandle *poFile = Open(pszFilename, "r");
     483             : 
     484          53 :     memset(psStatBuf, 0, sizeof(VSIStatBufL));
     485             : 
     486          53 :     if (poFile == nullptr)
     487          46 :         return -1;
     488             : 
     489           7 :     poFile->Seek(0, SEEK_END);
     490           7 :     const vsi_l_offset nLength = poFile->Tell();
     491           7 :     delete poFile;
     492             : 
     493             :     const int nResult =
     494           7 :         VSIStatExL(pszFilename + strlen("/vsisparse/"), psStatBuf, nFlags);
     495             : 
     496           7 :     psStatBuf->st_size = nLength;
     497             : 
     498           7 :     return nResult;
     499             : }
     500             : 
     501             : /************************************************************************/
     502             : /*                               Unlink()                               */
     503             : /************************************************************************/
     504             : 
     505           0 : int VSISparseFileFilesystemHandler::Unlink(const char * /* pszFilename */)
     506             : {
     507           0 :     errno = EACCES;
     508           0 :     return -1;
     509             : }
     510             : 
     511             : /************************************************************************/
     512             : /*                               Mkdir()                                */
     513             : /************************************************************************/
     514             : 
     515           0 : int VSISparseFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
     516             :                                           long /* nMode */)
     517             : {
     518           0 :     errno = EACCES;
     519           0 :     return -1;
     520             : }
     521             : 
     522             : /************************************************************************/
     523             : /*                               Rmdir()                                */
     524             : /************************************************************************/
     525             : 
     526           0 : int VSISparseFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
     527             : {
     528           0 :     errno = EACCES;
     529           0 :     return -1;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                              ReadDirEx()                             */
     534             : /************************************************************************/
     535             : 
     536          15 : char **VSISparseFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
     537             :                                                  int /* nMaxFiles */)
     538             : {
     539          15 :     errno = EACCES;
     540          15 :     return nullptr;
     541             : }
     542             : 
     543             : /************************************************************************/
     544             : /*                 VSIInstallSparseFileFilesystemHandler()              */
     545             : /************************************************************************/
     546             : 
     547             : /*!
     548             :  \brief Install /vsisparse/ virtual file handler.
     549             : 
     550             :  \verbatim embed:rst
     551             :  See :ref:`/vsisparse/ documentation <vsisparse>`
     552             :  \endverbatim
     553             :  */
     554             : 
     555        1228 : void VSIInstallSparseFileHandler()
     556             : {
     557        1228 :     VSIFileManager::InstallHandler("/vsisparse/",
     558        1228 :                                    new VSISparseFileFilesystemHandler);
     559        1228 : }

Generated by: LCOV version 1.14