LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 323 341 94.7 %
Date: 2024-04-29 01:40:10 Functions: 32 33 97.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VSI Virtual File System
       4             :  * Purpose:  Implementation of Memory Buffer virtual IO functions.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-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             : #include "cpl_vsi_virtual.h"
      33             : 
      34             : #include <cerrno>
      35             : #include <cstddef>
      36             : #include <cstring>
      37             : #include <ctime>
      38             : #if HAVE_FCNTL_H
      39             : #include <fcntl.h>
      40             : #endif
      41             : #if HAVE_SYS_STAT_H
      42             : #include <sys/stat.h>
      43             : #endif
      44             : 
      45             : #include <algorithm>
      46             : #include <map>
      47             : #include <string>
      48             : #include <utility>
      49             : #include <memory>
      50             : 
      51             : #include <mutex>
      52             : // c++17 or VS2017
      53             : #if defined(HAVE_SHARED_MUTEX) || _MSC_VER >= 1910
      54             : #include <shared_mutex>
      55             : #define CPL_SHARED_MUTEX_TYPE std::shared_mutex
      56             : #define CPL_SHARED_LOCK std::shared_lock<std::shared_mutex>
      57             : #define CPL_EXCLUSIVE_LOCK std::unique_lock<std::shared_mutex>
      58             : #else
      59             : // Poor-man implementation of std::shared_mutex with an exclusive mutex
      60             : #define CPL_SHARED_MUTEX_TYPE std::mutex
      61             : #define CPL_SHARED_LOCK std::lock_guard<std::mutex>
      62             : #define CPL_EXCLUSIVE_LOCK std::lock_guard<std::mutex>
      63             : #endif
      64             : 
      65             : #include "cpl_atomic_ops.h"
      66             : #include "cpl_conv.h"
      67             : #include "cpl_error.h"
      68             : #include "cpl_multiproc.h"
      69             : #include "cpl_string.h"
      70             : 
      71             : //! @cond Doxygen_Suppress
      72             : 
      73             : /*
      74             : ** Notes on Multithreading:
      75             : **
      76             : ** VSIMemFilesystemHandler: This class maintains a mutex to protect
      77             : ** access and update of the oFileList array which has all the "files" in
      78             : ** the memory filesystem area.  It is expected that multiple threads would
      79             : ** want to create and read different files at the same time and so might
      80             : ** collide access oFileList without the mutex.
      81             : **
      82             : ** VSIMemFile: A mutex protects accesses to the file
      83             : **
      84             : ** VSIMemHandle: This is essentially a "current location" representing
      85             : ** on accessor to a file, and is inherently intended only to be used in
      86             : ** a single thread.
      87             : **
      88             : ** In General:
      89             : **
      90             : ** Multiple threads accessing the memory filesystem are ok as long as
      91             : ** a given VSIMemHandle (i.e. FILE * at app level) isn't used by multiple
      92             : ** threads at once.
      93             : */
      94             : 
      95             : /************************************************************************/
      96             : /* ==================================================================== */
      97             : /*                              VSIMemFile                              */
      98             : /* ==================================================================== */
      99             : /************************************************************************/
     100             : 
     101             : class VSIMemFile
     102             : {
     103             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemFile)
     104             : 
     105             :   public:
     106             :     CPLString osFilename{};
     107             : 
     108             :     bool bIsDirectory = false;
     109             : 
     110             :     bool bOwnData = true;
     111             :     GByte *pabyData = nullptr;
     112             :     vsi_l_offset nLength = 0;
     113             :     vsi_l_offset nAllocLength = 0;
     114             :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     115             : 
     116             :     time_t mTime = 0;
     117             :     CPL_SHARED_MUTEX_TYPE m_oMutex{};
     118             : 
     119             :     VSIMemFile();
     120             :     virtual ~VSIMemFile();
     121             : 
     122             :     bool SetLength(vsi_l_offset nNewSize);
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /* ==================================================================== */
     127             : /*                             VSIMemHandle                             */
     128             : /* ==================================================================== */
     129             : /************************************************************************/
     130             : 
     131             : class VSIMemHandle final : public VSIVirtualHandle
     132             : {
     133             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemHandle)
     134             : 
     135             :   public:
     136             :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     137             :     vsi_l_offset m_nOffset = 0;
     138             :     bool bUpdate = false;
     139             :     bool bEOF = false;
     140             :     bool bExtendFileAtNextWrite = false;
     141             : 
     142      158657 :     VSIMemHandle() = default;
     143             :     ~VSIMemHandle() override;
     144             : 
     145             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     146             :     vsi_l_offset Tell() override;
     147             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
     148             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
     149             :     int Eof() override;
     150             :     int Close() override;
     151             :     int Truncate(vsi_l_offset nNewSize) override;
     152             : 
     153           3 :     bool HasPRead() const override
     154             :     {
     155           3 :         return true;
     156             :     }
     157             : 
     158             :     size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
     159             :                  vsi_l_offset /*nOffset*/) const override;
     160             : };
     161             : 
     162             : /************************************************************************/
     163             : /* ==================================================================== */
     164             : /*                       VSIMemFilesystemHandler                        */
     165             : /* ==================================================================== */
     166             : /************************************************************************/
     167             : 
     168             : class VSIMemFilesystemHandler final : public VSIFilesystemHandler
     169             : {
     170             :     const std::string m_osPrefix;
     171             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemFilesystemHandler)
     172             : 
     173             :   public:
     174             :     std::map<CPLString, std::shared_ptr<VSIMemFile>> oFileList{};
     175             :     CPLMutex *hMutex = nullptr;
     176             : 
     177        1229 :     explicit VSIMemFilesystemHandler(const char *pszPrefix)
     178        1229 :         : m_osPrefix(pszPrefix)
     179             :     {
     180        1229 :     }
     181             : 
     182             :     ~VSIMemFilesystemHandler() override;
     183             : 
     184             :     // TODO(schwehr): Fix VSIFileFromMemBuffer so that using is not needed.
     185             :     using VSIFilesystemHandler::Open;
     186             : 
     187             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
     188             :                            bool bSetError,
     189             :                            CSLConstList /* papszOptions */) override;
     190             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     191             :              int nFlags) override;
     192             :     int Unlink(const char *pszFilename) override;
     193             :     int Mkdir(const char *pszDirname, long nMode) override;
     194             :     int Rmdir(const char *pszDirname) override;
     195             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     196             :     int Rename(const char *oldpath, const char *newpath) override;
     197             :     GIntBig GetDiskFreeSpace(const char *pszDirname) override;
     198             : 
     199             :     static std::string NormalizePath(const std::string &in);
     200             : 
     201             :     int Unlink_unlocked(const char *pszFilename);
     202             : 
     203           1 :     VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
     204             :     {
     205           1 :         return new VSIMemFilesystemHandler(pszPrefix);
     206             :     }
     207             : };
     208             : 
     209             : /************************************************************************/
     210             : /* ==================================================================== */
     211             : /*                              VSIMemFile                              */
     212             : /* ==================================================================== */
     213             : /************************************************************************/
     214             : 
     215             : /************************************************************************/
     216             : /*                             VSIMemFile()                             */
     217             : /************************************************************************/
     218             : 
     219       87410 : VSIMemFile::VSIMemFile()
     220             : {
     221       87410 :     time(&mTime);
     222       87410 : }
     223             : 
     224             : /************************************************************************/
     225             : /*                            ~VSIMemFile()                             */
     226             : /************************************************************************/
     227             : 
     228       87115 : VSIMemFile::~VSIMemFile()
     229             : {
     230       87115 :     if (bOwnData && pabyData)
     231       72967 :         CPLFree(pabyData);
     232       87115 : }
     233             : 
     234             : /************************************************************************/
     235             : /*                             SetLength()                              */
     236             : /************************************************************************/
     237             : 
     238             : // Must be called under exclusive lock
     239     1118710 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
     240             : 
     241             : {
     242     1118710 :     if (nNewLength > nMaxLength)
     243             :     {
     244        4632 :         CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
     245        4632 :         return false;
     246             :     }
     247             : 
     248             :     /* -------------------------------------------------------------------- */
     249             :     /*      Grow underlying array if needed.                                */
     250             :     /* -------------------------------------------------------------------- */
     251     1114080 :     if (nNewLength > nAllocLength)
     252             :     {
     253             :         // If we don't own the buffer, we cannot reallocate it because
     254             :         // the return address might be different from the one passed by
     255             :         // the caller. Hence, the caller would not be able to free
     256             :         // the buffer.
     257      116551 :         if (!bOwnData)
     258             :         {
     259           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     260             :                      "Cannot extended in-memory file whose ownership was not "
     261             :                      "transferred");
     262           1 :             return false;
     263             :         }
     264             : 
     265      116550 :         const vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000;
     266      116550 :         GByte *pabyNewData = nullptr;
     267             :         if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
     268             :             nNewAlloc)
     269             :         {
     270             :             pabyNewData = static_cast<GByte *>(
     271      116550 :                 VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
     272             :         }
     273      116551 :         if (pabyNewData == nullptr)
     274             :         {
     275           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     276             :                      "Cannot extend in-memory file to " CPL_FRMT_GUIB
     277             :                      " bytes due to out-of-memory situation",
     278             :                      nNewAlloc);
     279           1 :             return false;
     280             :         }
     281             : 
     282             :         // Clear the new allocated part of the buffer.
     283      116552 :         memset(pabyNewData + nAllocLength, 0,
     284      116552 :                static_cast<size_t>(nNewAlloc - nAllocLength));
     285             : 
     286      116552 :         pabyData = pabyNewData;
     287      116552 :         nAllocLength = nNewAlloc;
     288             :     }
     289      997531 :     else if (nNewLength < nLength)
     290             :     {
     291        5590 :         memset(pabyData + nNewLength, 0,
     292        5590 :                static_cast<size_t>(nLength - nNewLength));
     293             :     }
     294             : 
     295     1114080 :     nLength = nNewLength;
     296     1114080 :     time(&mTime);
     297             : 
     298     1114080 :     return true;
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /* ==================================================================== */
     303             : /*                             VSIMemHandle                             */
     304             : /* ==================================================================== */
     305             : /************************************************************************/
     306             : 
     307             : /************************************************************************/
     308             : /*                            ~VSIMemHandle()                           */
     309             : /************************************************************************/
     310             : 
     311      317270 : VSIMemHandle::~VSIMemHandle()
     312             : {
     313      158629 :     VSIMemHandle::Close();
     314      317281 : }
     315             : 
     316             : /************************************************************************/
     317             : /*                               Close()                                */
     318             : /************************************************************************/
     319             : 
     320      317289 : int VSIMemHandle::Close()
     321             : 
     322             : {
     323      317289 :     if (poFile)
     324             :     {
     325             : #ifdef DEBUG_VERBOSE
     326             :         CPLDebug("VSIMEM", "Closing handle %p on %s: ref_count=%d (before)",
     327             :                  this, poFile->osFilename.c_str(),
     328             :                  static_cast<int>(poFile.use_count()));
     329             : #endif
     330      158638 :         poFile = nullptr;
     331             :     }
     332             : 
     333      317273 :     return 0;
     334             : }
     335             : 
     336             : /************************************************************************/
     337             : /*                                Seek()                                */
     338             : /************************************************************************/
     339             : 
     340     4793460 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
     341             : 
     342             : {
     343     9586900 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     344             : 
     345     4793460 :     bExtendFileAtNextWrite = false;
     346     4793460 :     if (nWhence == SEEK_CUR)
     347             :     {
     348             :         if (nOffset > INT_MAX)
     349             :         {
     350             :             // printf("likely negative offset intended\n");
     351             :         }
     352      376807 :         m_nOffset += nOffset;
     353             :     }
     354     4416650 :     else if (nWhence == SEEK_SET)
     355             :     {
     356     4235460 :         m_nOffset = nOffset;
     357             :     }
     358      181184 :     else if (nWhence == SEEK_END)
     359             :     {
     360      181181 :         m_nOffset = poFile->nLength + nOffset;
     361             :     }
     362             :     else
     363             :     {
     364           3 :         errno = EINVAL;
     365           3 :         return -1;
     366             :     }
     367             : 
     368     4793450 :     bEOF = false;
     369             : 
     370     4793450 :     if (m_nOffset > poFile->nLength)
     371             :     {
     372       53664 :         if (bUpdate)  // Writable files are zero-extended by seek past end.
     373             :         {
     374       53612 :             bExtendFileAtNextWrite = true;
     375             :         }
     376             :     }
     377             : 
     378     4793440 :     return 0;
     379             : }
     380             : 
     381             : /************************************************************************/
     382             : /*                                Tell()                                */
     383             : /************************************************************************/
     384             : 
     385     3168340 : vsi_l_offset VSIMemHandle::Tell()
     386             : 
     387             : {
     388     3168340 :     return m_nOffset;
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                                Read()                                */
     393             : /************************************************************************/
     394             : 
     395     6863470 : size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     396             : 
     397             : {
     398    13726900 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     399             : 
     400     6863470 :     size_t nBytesToRead = nSize * nCount;
     401     6863470 :     if (nBytesToRead == 0)
     402        1560 :         return 0;
     403             : 
     404     6861910 :     if (nCount > 0 && nBytesToRead / nCount != nSize)
     405             :     {
     406           0 :         bEOF = true;
     407           0 :         return 0;
     408             :     }
     409             : 
     410     6861910 :     if (poFile->nLength <= m_nOffset || nBytesToRead + m_nOffset < nBytesToRead)
     411             :     {
     412       25555 :         bEOF = true;
     413       25555 :         return 0;
     414             :     }
     415     6836360 :     if (nBytesToRead + m_nOffset > poFile->nLength)
     416             :     {
     417       45741 :         nBytesToRead = static_cast<size_t>(poFile->nLength - m_nOffset);
     418       45744 :         nCount = nBytesToRead / nSize;
     419       45744 :         bEOF = true;
     420             :     }
     421             : 
     422     6836360 :     if (nBytesToRead)
     423     6836360 :         memcpy(pBuffer, poFile->pabyData + m_nOffset,
     424             :                static_cast<size_t>(nBytesToRead));
     425     6836350 :     m_nOffset += nBytesToRead;
     426             : 
     427     6836350 :     return nCount;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                              PRead()                                 */
     432             : /************************************************************************/
     433             : 
     434          35 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
     435             :                            vsi_l_offset nOffset) const
     436             : {
     437          70 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     438             : 
     439          35 :     if (nOffset < poFile->nLength)
     440             :     {
     441             :         const size_t nToCopy = static_cast<size_t>(
     442         102 :             std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
     443          34 :                      static_cast<vsi_l_offset>(nSize)));
     444          34 :         memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
     445             :                nToCopy);
     446          34 :         return nToCopy;
     447             :     }
     448           1 :     return 0;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                               Write()                                */
     453             : /************************************************************************/
     454             : 
     455     1532100 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
     456             : 
     457             : {
     458     3064190 :     CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     459             : 
     460     1532100 :     if (!bUpdate)
     461             :     {
     462           1 :         errno = EACCES;
     463           1 :         return 0;
     464             :     }
     465     1532100 :     if (bExtendFileAtNextWrite)
     466             :     {
     467       40051 :         bExtendFileAtNextWrite = false;
     468       40051 :         if (!poFile->SetLength(m_nOffset))
     469         180 :             return 0;
     470             :     }
     471             : 
     472     1531920 :     const size_t nBytesToWrite = nSize * nCount;
     473     1531920 :     if (nCount > 0 && nBytesToWrite / nCount != nSize)
     474             :     {
     475           0 :         return 0;
     476             :     }
     477     1531920 :     if (nBytesToWrite + m_nOffset < nBytesToWrite)
     478             :     {
     479           0 :         return 0;
     480             :     }
     481             : 
     482     1531920 :     if (nBytesToWrite + m_nOffset > poFile->nLength)
     483             :     {
     484     1072410 :         if (!poFile->SetLength(nBytesToWrite + m_nOffset))
     485        4434 :             return 0;
     486             :     }
     487             : 
     488     1527470 :     if (nBytesToWrite)
     489     1517470 :         memcpy(poFile->pabyData + m_nOffset, pBuffer, nBytesToWrite);
     490             :     // Coverity seems to be confused by the fact that we access m_nOffset
     491             :     // under a shared lock in most places, except here under an exclusive lock
     492             :     // which is fine
     493             :     // coverity[missing_lock]
     494     1527470 :     m_nOffset += nBytesToWrite;
     495             : 
     496     1527470 :     time(&poFile->mTime);
     497             : 
     498     1527480 :     return nCount;
     499             : }
     500             : 
     501             : /************************************************************************/
     502             : /*                                Eof()                                 */
     503             : /************************************************************************/
     504             : 
     505      147650 : int VSIMemHandle::Eof()
     506             : 
     507             : {
     508      147650 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     509      295300 :     return bEOF;
     510             : }
     511             : 
     512             : /************************************************************************/
     513             : /*                             Truncate()                               */
     514             : /************************************************************************/
     515             : 
     516         688 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
     517             : {
     518         688 :     if (!bUpdate)
     519             :     {
     520           1 :         errno = EACCES;
     521           1 :         return -1;
     522             :     }
     523             : 
     524         687 :     bExtendFileAtNextWrite = false;
     525             : 
     526        1374 :     CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     527         687 :     if (poFile->SetLength(nNewSize))
     528         667 :         return 0;
     529             : 
     530          20 :     return -1;
     531             : }
     532             : 
     533             : /************************************************************************/
     534             : /* ==================================================================== */
     535             : /*                       VSIMemFilesystemHandler                        */
     536             : /* ==================================================================== */
     537             : /************************************************************************/
     538             : 
     539             : /************************************************************************/
     540             : /*                      ~VSIMemFilesystemHandler()                      */
     541             : /************************************************************************/
     542             : 
     543        1706 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
     544             : 
     545             : {
     546         853 :     oFileList.clear();
     547             : 
     548         853 :     if (hMutex != nullptr)
     549         206 :         CPLDestroyMutex(hMutex);
     550         853 :     hMutex = nullptr;
     551        1706 : }
     552             : 
     553             : /************************************************************************/
     554             : /*                                Open()                                */
     555             : /************************************************************************/
     556             : 
     557      203207 : VSIVirtualHandle *VSIMemFilesystemHandler::Open(const char *pszFilename,
     558             :                                                 const char *pszAccess,
     559             :                                                 bool bSetError,
     560             :                                                 CSLConstList /* papszOptions */)
     561             : 
     562             : {
     563      406416 :     CPLMutexHolder oHolder(&hMutex);
     564      609627 :     const CPLString osFilename = NormalizePath(pszFilename);
     565      203209 :     if (osFilename.empty())
     566           0 :         return nullptr;
     567             : 
     568      203209 :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     569      203209 :     const size_t iPos = osFilename.find("||maxlength=");
     570      203209 :     if (iPos != std::string::npos)
     571             :     {
     572        3270 :         nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
     573        6540 :             osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
     574             :     }
     575             : 
     576             :     /* -------------------------------------------------------------------- */
     577             :     /*      Get the filename we are opening, create if needed.              */
     578             :     /* -------------------------------------------------------------------- */
     579      406418 :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     580      203209 :     if (oFileList.find(osFilename) != oFileList.end())
     581             :     {
     582       76558 :         poFile = oFileList[osFilename];
     583             :     }
     584             : 
     585             :     // If no file and opening in read, error out.
     586      530266 :     if (strstr(pszAccess, "w") == nullptr &&
     587      203209 :         strstr(pszAccess, "a") == nullptr && poFile == nullptr)
     588             :     {
     589       52815 :         if (bSetError)
     590             :         {
     591        6831 :             VSIError(VSIE_FileError, "No such file or directory");
     592             :         }
     593       52815 :         errno = ENOENT;
     594       52815 :         return nullptr;
     595             :     }
     596             : 
     597             :     // Create.
     598      150394 :     if (poFile == nullptr)
     599             :     {
     600       73836 :         const char *pszFileDir = CPLGetPath(osFilename.c_str());
     601       73836 :         if (VSIMkdirRecursive(pszFileDir, 0755) == -1)
     602             :         {
     603           1 :             if (bSetError)
     604             :             {
     605           0 :                 VSIError(VSIE_FileError,
     606             :                          "Could not create directory %s for writing",
     607             :                          pszFileDir);
     608             :             }
     609           1 :             errno = ENOENT;
     610           1 :             return nullptr;
     611             :         }
     612             : 
     613       73835 :         poFile = std::make_shared<VSIMemFile>();
     614       73835 :         poFile->osFilename = osFilename;
     615       73835 :         oFileList[poFile->osFilename] = poFile;
     616             : #ifdef DEBUG_VERBOSE
     617             :         CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
     618             :                  static_cast<int>(poFile.use_count()));
     619             : #endif
     620       73835 :         poFile->nMaxLength = nMaxLength;
     621             :     }
     622             :     // Overwrite
     623       76558 :     else if (strstr(pszAccess, "w"))
     624             :     {
     625        5563 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     626        5563 :         poFile->SetLength(0);
     627        5563 :         poFile->nMaxLength = nMaxLength;
     628             :     }
     629             : 
     630      150393 :     if (poFile->bIsDirectory)
     631             :     {
     632         947 :         errno = EISDIR;
     633         947 :         return nullptr;
     634             :     }
     635             : 
     636             :     /* -------------------------------------------------------------------- */
     637             :     /*      Setup the file handle on this file.                             */
     638             :     /* -------------------------------------------------------------------- */
     639      149446 :     VSIMemHandle *poHandle = new VSIMemHandle;
     640             : 
     641      149446 :     poHandle->poFile = poFile;
     642      149446 :     poHandle->m_nOffset = 0;
     643      149446 :     poHandle->bEOF = false;
     644      200805 :     poHandle->bUpdate = strstr(pszAccess, "w") || strstr(pszAccess, "+") ||
     645       51359 :                         strstr(pszAccess, "a");
     646             : 
     647             : #ifdef DEBUG_VERBOSE
     648             :     CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle,
     649             :              pszFilename, static_cast<int>(poFile.use_count()));
     650             : #endif
     651      149446 :     if (strstr(pszAccess, "a"))
     652             :     {
     653          51 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     654          51 :         poHandle->m_nOffset = poFile->nLength;
     655             :     }
     656             : 
     657      149446 :     return poHandle;
     658             : }
     659             : 
     660             : /************************************************************************/
     661             : /*                                Stat()                                */
     662             : /************************************************************************/
     663             : 
     664      398789 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
     665             :                                   VSIStatBufL *pStatBuf, int /* nFlags */)
     666             : 
     667             : {
     668      797578 :     CPLMutexHolder oHolder(&hMutex);
     669             : 
     670     1196370 :     const CPLString osFilename = NormalizePath(pszFilename);
     671             : 
     672      398789 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     673             : 
     674      398789 :     if (osFilename + '/' == m_osPrefix || osFilename == m_osPrefix)
     675             :     {
     676       26956 :         pStatBuf->st_size = 0;
     677       26956 :         pStatBuf->st_mode = S_IFDIR;
     678       26956 :         return 0;
     679             :     }
     680             : 
     681      371833 :     auto oIter = oFileList.find(osFilename);
     682      371833 :     if (oIter == oFileList.end())
     683             :     {
     684      271246 :         errno = ENOENT;
     685      271246 :         return -1;
     686             :     }
     687             : 
     688      201174 :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     689             : 
     690      100587 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     691             : 
     692      100587 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     693      100587 :     if (poFile->bIsDirectory)
     694             :     {
     695       61432 :         pStatBuf->st_size = 0;
     696       61432 :         pStatBuf->st_mode = S_IFDIR;
     697             :     }
     698             :     else
     699             :     {
     700       39155 :         pStatBuf->st_size = poFile->nLength;
     701       39155 :         pStatBuf->st_mode = S_IFREG;
     702       39155 :         pStatBuf->st_mtime = poFile->mTime;
     703             :     }
     704             : 
     705      100587 :     return 0;
     706             : }
     707             : 
     708             : /************************************************************************/
     709             : /*                               Unlink()                               */
     710             : /************************************************************************/
     711             : 
     712       88541 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
     713             : 
     714             : {
     715      177084 :     CPLMutexHolder oHolder(&hMutex);
     716      177086 :     return Unlink_unlocked(pszFilename);
     717             : }
     718             : 
     719             : /************************************************************************/
     720             : /*                           Unlink_unlocked()                          */
     721             : /************************************************************************/
     722             : 
     723       97747 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
     724             : 
     725             : {
     726      293241 :     const CPLString osFilename = NormalizePath(pszFilename);
     727             : 
     728       97747 :     auto oIter = oFileList.find(osFilename);
     729       97747 :     if (oIter == oFileList.end())
     730             :     {
     731       16652 :         errno = ENOENT;
     732       16652 :         return -1;
     733             :     }
     734             : 
     735             : #ifdef DEBUG_VERBOSE
     736             :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     737             :     CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
     738             :              static_cast<int>(poFile.use_count()));
     739             : #endif
     740       81095 :     oFileList.erase(oIter);
     741             : 
     742       81095 :     return 0;
     743             : }
     744             : 
     745             : /************************************************************************/
     746             : /*                               Mkdir()                                */
     747             : /************************************************************************/
     748             : 
     749        4511 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
     750             : 
     751             : {
     752        9022 :     CPLMutexHolder oHolder(&hMutex);
     753             : 
     754       13533 :     const CPLString osPathname = NormalizePath(pszPathname);
     755             : 
     756        4511 :     if (oFileList.find(osPathname) != oFileList.end())
     757             :     {
     758         147 :         errno = EEXIST;
     759         147 :         return -1;
     760             :     }
     761             : 
     762        4364 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
     763        4364 :     poFile->osFilename = osPathname;
     764        4364 :     poFile->bIsDirectory = true;
     765        4364 :     oFileList[osPathname] = poFile;
     766             : #ifdef DEBUG_VERBOSE
     767             :     CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
     768             :              static_cast<int>(poFile.use_count()));
     769             : #endif
     770        4364 :     CPL_IGNORE_RET_VAL(poFile);
     771        4364 :     return 0;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                               Rmdir()                                */
     776             : /************************************************************************/
     777             : 
     778        3713 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
     779             : 
     780             : {
     781        3713 :     return Unlink(pszPathname);
     782             : }
     783             : 
     784             : /************************************************************************/
     785             : /*                             ReadDirEx()                              */
     786             : /************************************************************************/
     787             : 
     788       26438 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
     789             : 
     790             : {
     791       52876 :     CPLMutexHolder oHolder(&hMutex);
     792             : 
     793       52876 :     const CPLString osPath = NormalizePath(pszPath);
     794             : 
     795       26438 :     char **papszDir = nullptr;
     796       26438 :     size_t nPathLen = osPath.size();
     797             : 
     798       26438 :     if (nPathLen > 0 && osPath.back() == '/')
     799           0 :         nPathLen--;
     800             : 
     801             :     // In case of really big number of files in the directory, CSLAddString
     802             :     // can be slow (see #2158). We then directly build the list.
     803       26438 :     int nItems = 0;
     804       26438 :     int nAllocatedItems = 0;
     805             : 
     806     1566230 :     for (const auto &iter : oFileList)
     807             :     {
     808     1539790 :         const char *pszFilePath = iter.second->osFilename.c_str();
     809     1539790 :         if (EQUALN(osPath, pszFilePath, nPathLen) &&
     810     2109920 :             pszFilePath[nPathLen] == '/' &&
     811      570130 :             strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
     812             :         {
     813      367589 :             if (nItems == 0)
     814             :             {
     815       24495 :                 papszDir = static_cast<char **>(CPLCalloc(2, sizeof(char *)));
     816       24495 :                 nAllocatedItems = 1;
     817             :             }
     818      343094 :             else if (nItems >= nAllocatedItems)
     819             :             {
     820       78566 :                 nAllocatedItems = nAllocatedItems * 2;
     821       78566 :                 papszDir = static_cast<char **>(CPLRealloc(
     822       78566 :                     papszDir, (nAllocatedItems + 2) * sizeof(char *)));
     823             :             }
     824             : 
     825      367589 :             papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
     826      367589 :             papszDir[nItems + 1] = nullptr;
     827             : 
     828      367589 :             nItems++;
     829      367589 :             if (nMaxFiles > 0 && nItems > nMaxFiles)
     830           1 :                 break;
     831             :         }
     832             :     }
     833             : 
     834       52876 :     return papszDir;
     835             : }
     836             : 
     837             : /************************************************************************/
     838             : /*                               Rename()                               */
     839             : /************************************************************************/
     840             : 
     841         162 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
     842             :                                     const char *pszNewPath)
     843             : 
     844             : {
     845         324 :     CPLMutexHolder oHolder(&hMutex);
     846             : 
     847         486 :     const CPLString osOldPath = NormalizePath(pszOldPath);
     848         486 :     const CPLString osNewPath = NormalizePath(pszNewPath);
     849         162 :     if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
     850           3 :         return -1;
     851             : 
     852         159 :     if (osOldPath.compare(osNewPath) == 0)
     853           0 :         return 0;
     854             : 
     855         159 :     if (oFileList.find(osOldPath) == oFileList.end())
     856             :     {
     857           0 :         errno = ENOENT;
     858           0 :         return -1;
     859             :     }
     860             : 
     861             :     std::map<CPLString, std::shared_ptr<VSIMemFile>>::iterator it =
     862         159 :         oFileList.find(osOldPath);
     863         502 :     while (it != oFileList.end() && it->first.ifind(osOldPath) == 0)
     864             :     {
     865         686 :         const CPLString osRemainder = it->first.substr(osOldPath.size());
     866         343 :         if (osRemainder.empty() || osRemainder[0] == '/')
     867             :         {
     868         444 :             const CPLString osNewFullPath = osNewPath + osRemainder;
     869         222 :             Unlink_unlocked(osNewFullPath);
     870         222 :             oFileList[osNewFullPath] = it->second;
     871         222 :             it->second->osFilename = osNewFullPath;
     872         222 :             oFileList.erase(it++);
     873             :         }
     874             :         else
     875             :         {
     876         121 :             ++it;
     877             :         }
     878             :     }
     879             : 
     880         159 :     return 0;
     881             : }
     882             : 
     883             : /************************************************************************/
     884             : /*                           NormalizePath()                            */
     885             : /************************************************************************/
     886             : 
     887      775676 : std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
     888             : {
     889     1551350 :     CPLString s(in);
     890      775660 :     std::replace(s.begin(), s.end(), '\\', '/');
     891      775680 :     s.replaceAll("//", '/');
     892      775672 :     if (!s.empty() && s.back() == '/')
     893        1533 :         s.resize(s.size() - 1);
     894             : #if __GNUC__ >= 13
     895             :     // gcc 13 complains about below explicit std::move()
     896             :     return s;
     897             : #else
     898             :     // Android NDK (and probably other compilers) warn about
     899             :     // "warning: local variable 's' will be copied despite being returned by name [-Wreturn-std-move]"
     900             :     // if not specifying std::move()
     901     1551340 :     return std::move(s);
     902             : #endif
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                        GetDiskFreeSpace()                            */
     907             : /************************************************************************/
     908             : 
     909          70 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
     910             : {
     911          70 :     const GIntBig nRet = CPLGetUsablePhysicalRAM();
     912          70 :     if (nRet <= 0)
     913           0 :         return -1;
     914          70 :     return nRet;
     915             : }
     916             : 
     917             : //! @endcond
     918             : 
     919             : /************************************************************************/
     920             : /*                       VSIInstallMemFileHandler()                     */
     921             : /************************************************************************/
     922             : 
     923             : /**
     924             :  * \brief Install "memory" file system handler.
     925             :  *
     926             :  * A special file handler is installed that allows block of memory to be
     927             :  * treated as files.   All portions of the file system underneath the base
     928             :  * path "/vsimem/" will be handled by this driver.
     929             :  *
     930             :  * Normal VSI*L functions can be used freely to create and destroy memory
     931             :  * arrays treating them as if they were real file system objects.  Some
     932             :  * additional methods exist to efficient create memory file system objects
     933             :  * without duplicating original copies of the data or to "steal" the block
     934             :  * of memory associated with a memory file.
     935             :  *
     936             :  * Directory related functions are supported.
     937             :  *
     938             :  * This code example demonstrates using GDAL to translate from one memory
     939             :  * buffer to another.
     940             :  *
     941             :  * \code
     942             :  * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
     943             :  *                             vsi_l_offset *pnOutDataLength )
     944             :  * {
     945             :  *     // create memory file system object from buffer.
     946             :  *     VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
     947             :  *                                       nInDataLength, FALSE ) );
     948             :  *
     949             :  *     // Open memory buffer for read.
     950             :  *     GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
     951             :  *
     952             :  *     // Get output format driver.
     953             :  *     GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
     954             :  *     GDALDatasetH hOutDS;
     955             :  *
     956             :  *     hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
     957             :  *                              NULL, NULL );
     958             :  *
     959             :  *     // close source file, and "unlink" it.
     960             :  *     GDALClose( hDS );
     961             :  *     VSIUnlink( "/vsimem/work.dat" );
     962             :  *
     963             :  *     // seize the buffer associated with the output file.
     964             :  *
     965             :  *     return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
     966             :  * }
     967             :  * \endcode
     968             :  */
     969             : 
     970        1228 : void VSIInstallMemFileHandler()
     971             : {
     972        1228 :     VSIFileManager::InstallHandler("/vsimem/",
     973        1228 :                                    new VSIMemFilesystemHandler("/vsimem/"));
     974        1228 : }
     975             : 
     976             : /************************************************************************/
     977             : /*                        VSIFileFromMemBuffer()                        */
     978             : /************************************************************************/
     979             : 
     980             : /**
     981             :  * \brief Create memory "file" from a buffer.
     982             :  *
     983             :  * A virtual memory file is created from the passed buffer with the indicated
     984             :  * filename.  Under normal conditions the filename would need to be absolute
     985             :  * and within the /vsimem/ portion of the filesystem.
     986             :  * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
     987             :  * an anonymous file, that is destroyed when the handle is closed.
     988             :  *
     989             :  * If bTakeOwnership is TRUE, then the memory file system handler will take
     990             :  * ownership of the buffer, freeing it when the file is deleted.  Otherwise
     991             :  * it remains the responsibility of the caller, but should not be freed as
     992             :  * long as it might be accessed as a file.  In no circumstances does this
     993             :  * function take a copy of the pabyData contents.
     994             :  *
     995             :  * @param pszFilename the filename to be created, or nullptr
     996             :  * @param pabyData the data buffer for the file.
     997             :  * @param nDataLength the length of buffer in bytes.
     998             :  * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
     999             :  *
    1000             :  * @return open file handle on created file (see VSIFOpenL()).
    1001             :  */
    1002             : 
    1003        9212 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
    1004             :                                vsi_l_offset nDataLength, int bTakeOwnership)
    1005             : 
    1006             : {
    1007        9212 :     if (VSIFileManager::GetHandler("") ==
    1008        9212 :         VSIFileManager::GetHandler("/vsimem/"))
    1009           0 :         VSIInstallMemFileHandler();
    1010             : 
    1011             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1012        9212 :         VSIFileManager::GetHandler("/vsimem/"));
    1013             : 
    1014             :     const CPLString osFilename =
    1015       27408 :         pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
    1016       18424 :                     : std::string();
    1017        9212 :     if (osFilename == "/vsimem/")
    1018             :     {
    1019           1 :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
    1020             :                  pszFilename);
    1021           1 :         return nullptr;
    1022             :     }
    1023             : 
    1024             :     // Try to create the parent directory, if needed, before taking
    1025             :     // ownership of pabyData.
    1026        9211 :     if (!osFilename.empty())
    1027             :     {
    1028        8982 :         const char *pszFileDir = CPLGetPath(osFilename.c_str());
    1029        8982 :         if (VSIMkdirRecursive(pszFileDir, 0755) == -1)
    1030             :         {
    1031           0 :             VSIError(VSIE_FileError,
    1032             :                      "Could not create directory %s for writing", pszFileDir);
    1033           0 :             errno = ENOENT;
    1034           0 :             return nullptr;
    1035             :         }
    1036             :     }
    1037             : 
    1038        9211 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
    1039             : 
    1040        9211 :     poFile->osFilename = osFilename;
    1041        9211 :     poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
    1042        9211 :     poFile->pabyData = pabyData;
    1043        9211 :     poFile->nLength = nDataLength;
    1044        9211 :     poFile->nAllocLength = nDataLength;
    1045             : 
    1046        9211 :     if (!osFilename.empty())
    1047             :     {
    1048       17964 :         CPLMutexHolder oHolder(&poHandler->hMutex);
    1049        8982 :         poHandler->Unlink_unlocked(osFilename);
    1050        8982 :         poHandler->oFileList[poFile->osFilename] = poFile;
    1051             : #ifdef DEBUG_VERBOSE
    1052             :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
    1053             :                  poFile->osFilename.c_str(),
    1054             :                  static_cast<int>(poFile.use_count()));
    1055             : #endif
    1056             :     }
    1057             : 
    1058             :     /* -------------------------------------------------------------------- */
    1059             :     /*      Setup the file handle on this file.                             */
    1060             :     /* -------------------------------------------------------------------- */
    1061        9211 :     VSIMemHandle *poHandle = new VSIMemHandle;
    1062             : 
    1063        9211 :     poHandle->poFile = std::move(poFile);
    1064        9211 :     poHandle->bUpdate = true;
    1065        9211 :     return poHandle;
    1066             : }
    1067             : 
    1068             : /************************************************************************/
    1069             : /*                        VSIGetMemFileBuffer()                         */
    1070             : /************************************************************************/
    1071             : 
    1072             : /**
    1073             :  * \brief Fetch buffer underlying memory file.
    1074             :  *
    1075             :  * This function returns a pointer to the memory buffer underlying a
    1076             :  * virtual "in memory" file.  If bUnlinkAndSeize is TRUE the filesystem
    1077             :  * object will be deleted, and ownership of the buffer will pass to the
    1078             :  * caller otherwise the underlying file will remain in existence.
    1079             :  *
    1080             :  * @param pszFilename the name of the file to grab the buffer of.
    1081             :  * @param pnDataLength (file) length returned in this variable.
    1082             :  * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
    1083             :  *
    1084             :  * @return pointer to memory buffer or NULL on failure.
    1085             :  */
    1086             : 
    1087       35675 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
    1088             :                            int bUnlinkAndSeize)
    1089             : 
    1090             : {
    1091             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1092       35675 :         VSIFileManager::GetHandler("/vsimem/"));
    1093             : 
    1094       35677 :     if (pszFilename == nullptr)
    1095           0 :         return nullptr;
    1096             : 
    1097             :     const CPLString osFilename =
    1098      107029 :         VSIMemFilesystemHandler::NormalizePath(pszFilename);
    1099             : 
    1100       71337 :     CPLMutexHolder oHolder(&poHandler->hMutex);
    1101             : 
    1102       35679 :     if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
    1103         126 :         return nullptr;
    1104             : 
    1105       35553 :     std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
    1106       35553 :     GByte *pabyData = poFile->pabyData;
    1107       35553 :     if (pnDataLength != nullptr)
    1108       32840 :         *pnDataLength = poFile->nLength;
    1109             : 
    1110       35553 :     if (bUnlinkAndSeize)
    1111             :     {
    1112        4399 :         if (!poFile->bOwnData)
    1113           0 :             CPLDebug("VSIMemFile",
    1114             :                      "File doesn't own data in VSIGetMemFileBuffer!");
    1115             :         else
    1116        4399 :             poFile->bOwnData = false;
    1117             : 
    1118        4399 :         poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
    1119             : #ifdef DEBUG_VERBOSE
    1120             :         CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
    1121             :                  poFile->osFilename.c_str(),
    1122             :                  static_cast<int>(poFile.use_count()));
    1123             : #endif
    1124        4399 :         poFile->pabyData = nullptr;
    1125        4399 :         poFile->nLength = 0;
    1126        4399 :         poFile->nAllocLength = 0;
    1127             :     }
    1128             : 
    1129       35553 :     return pabyData;
    1130             : }

Generated by: LCOV version 1.14