LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 408 423 96.5 %
Date: 2026-01-23 00:08:21 Functions: 37 37 100.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             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_vsi.h"
      16             : #include "cpl_vsi_virtual.h"
      17             : 
      18             : #include <cerrno>
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <ctime>
      23             : 
      24             : #include <fcntl.h>
      25             : 
      26             : #include <algorithm>
      27             : #include <atomic>
      28             : #include <map>
      29             : #include <string>
      30             : #include <utility>
      31             : #include <memory>
      32             : #include <set>
      33             : 
      34             : #include <mutex>
      35             : // c++17 or VS2017
      36             : #if defined(HAVE_SHARED_MUTEX) || _MSC_VER >= 1910
      37             : #include <shared_mutex>
      38             : #define CPL_SHARED_MUTEX_TYPE std::shared_mutex
      39             : #define CPL_SHARED_LOCK std::shared_lock<std::shared_mutex>
      40             : #define CPL_EXCLUSIVE_LOCK std::unique_lock<std::shared_mutex>
      41             : #else
      42             : // Poor-man implementation of std::shared_mutex with an exclusive mutex
      43             : #define CPL_SHARED_MUTEX_TYPE std::mutex
      44             : #define CPL_SHARED_LOCK std::lock_guard<std::mutex>
      45             : #define CPL_EXCLUSIVE_LOCK std::lock_guard<std::mutex>
      46             : #endif
      47             : 
      48             : #include "cpl_atomic_ops.h"
      49             : #include "cpl_conv.h"
      50             : #include "cpl_error.h"
      51             : #include "cpl_multiproc.h"
      52             : #include "cpl_string.h"
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : 
      56             : // szHIDDEN_DIRNAME is for files created by VSIMemGenerateHiddenFilename(pszFilename).
      57             : // Such files are of the form "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}"
      58             : //
      59             : // The high-level design constraint is that "/vsimem/.#!HIDDEN!#." acts as a
      60             : // "side" hierarchy, but still under the "/vsimem/" namespace, so that code
      61             : // having special processing of filenames starting with /vsimem/ can still work.
      62             : // The structure of the returned filename is also such that those files form
      63             : // independent hierarchies, i.e. the tree generated by a
      64             : // VSIMemGenerateHiddenFilename() is "invisible" from the one returned by
      65             : // another call to it.
      66             : //
      67             : // As a consequence:
      68             : // - we don't want ".#!HIDDEN!#." to be listed in VSIReadDir("/vsimem/")
      69             : // - we don't want content under ""/vsimem/.#!HIDDEN!#" to be deleted by
      70             : //   VSIRmdirRecursive("/vsimem/")
      71             : // - we don't want the creation of a file (or directory) called
      72             : //   "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}"
      73             : //   to cause the implicit creation of "/vsimem/.#!HIDDEN!#./{counter}" and
      74             : //   "/vsimem/.#!HIDDEN!#". This is done so that users don't have to care about
      75             : //   cleaning such implicit directories that are upper in the hierarchy w.r.t.
      76             : //   to what we return to them.
      77             : // - But we want the creation of file or directory
      78             : //   "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}/something_added_by_user"
      79             : //   to cause "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}" to be implicitly
      80             : //   created as a directory, so they can list it, or recursively delete it.
      81             : // - we want VSIReadDirRecursive("/vsimem/.#!HIDDEN!#.") to list everything
      82             : //   under it (for debugging purposes)
      83             : // - we want VSIRmdirRecursive("/vsimem/.#!HIDDEN!#.") to remove everything
      84             : //   under it (for debugging purposes)
      85             : //
      86             : 
      87             : constexpr const char *szHIDDEN_DIRNAME = "/vsimem/.#!HIDDEN!#.";
      88             : 
      89             : /*
      90             : ** Notes on Multithreading:
      91             : **
      92             : ** VSIMemFilesystemHandler: This class maintains a mutex to protect
      93             : ** access and update of the oFileList array which has all the "files" in
      94             : ** the memory filesystem area.  It is expected that multiple threads would
      95             : ** want to create and read different files at the same time and so might
      96             : ** collide access oFileList without the mutex.
      97             : **
      98             : ** VSIMemFile: A mutex protects accesses to the file
      99             : **
     100             : ** VSIMemHandle: This is essentially a "current location" representing
     101             : ** on accessor to a file, and is inherently intended only to be used in
     102             : ** a single thread.
     103             : **
     104             : ** In General:
     105             : **
     106             : ** Multiple threads accessing the memory filesystem are ok as long as
     107             : ** a given VSIMemHandle (i.e. FILE * at app level) isn't used by multiple
     108             : ** threads at once.
     109             : */
     110             : 
     111             : /************************************************************************/
     112             : /* ==================================================================== */
     113             : /*                              VSIMemFile                              */
     114             : /* ==================================================================== */
     115             : /************************************************************************/
     116             : 
     117             : class VSIMemFile final
     118             : {
     119             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemFile)
     120             : 
     121             :   public:
     122             :     CPLString osFilename{};
     123             : 
     124             :     bool bIsDirectory = false;
     125             : 
     126             :     bool bOwnData = true;
     127             :     GByte *pabyData = nullptr;
     128             :     vsi_l_offset nLength = 0;
     129             :     vsi_l_offset nAllocLength = 0;
     130             :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     131             : 
     132             :     time_t mTime = 0;
     133             :     CPL_SHARED_MUTEX_TYPE m_oMutex{};
     134             : 
     135             :     VSIMemFile();
     136             :     ~VSIMemFile();
     137             : 
     138             :     bool SetLength(vsi_l_offset nNewSize);
     139             : };
     140             : 
     141             : /************************************************************************/
     142             : /* ==================================================================== */
     143             : /*                             VSIMemHandle                             */
     144             : /* ==================================================================== */
     145             : /************************************************************************/
     146             : 
     147             : class VSIMemHandle final : public VSIVirtualHandle
     148             : {
     149             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemHandle)
     150             : 
     151             :   public:
     152             :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     153             :     vsi_l_offset m_nOffset = 0;
     154             :     bool m_bReadAllowed = false;
     155             :     bool bUpdate = false;
     156             :     bool bEOF = false;
     157             :     bool m_bError = false;
     158             : 
     159      193204 :     VSIMemHandle() = default;
     160             :     ~VSIMemHandle() override;
     161             : 
     162             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     163             :     vsi_l_offset Tell() override;
     164             :     size_t Read(void *pBuffer, size_t nBytes) override;
     165             :     size_t Write(const void *pBuffer, size_t nBytes) override;
     166             :     void ClearErr() override;
     167             :     int Error() override;
     168             :     int Eof() override;
     169             :     int Close() override;
     170             :     int Truncate(vsi_l_offset nNewSize) override;
     171             : 
     172         730 :     bool HasPRead() const override
     173             :     {
     174         730 :         return true;
     175             :     }
     176             : 
     177             :     size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
     178             :                  vsi_l_offset /*nOffset*/) const override;
     179             : };
     180             : 
     181             : /************************************************************************/
     182             : /* ==================================================================== */
     183             : /*                       VSIMemFilesystemHandler                        */
     184             : /* ==================================================================== */
     185             : /************************************************************************/
     186             : 
     187             : class VSIMemFilesystemHandler final : public VSIFilesystemHandler
     188             : {
     189             :     const std::string m_osPrefix;
     190             :     CPL_DISALLOW_COPY_ASSIGN(VSIMemFilesystemHandler)
     191             : 
     192             :   public:
     193             :     std::map<std::string, std::shared_ptr<VSIMemFile>> oFileList{};
     194             :     CPLMutex *hMutex = nullptr;
     195             : 
     196        1789 :     explicit VSIMemFilesystemHandler(const char *pszPrefix)
     197        1789 :         : m_osPrefix(pszPrefix)
     198             :     {
     199        1789 :     }
     200             : 
     201             :     ~VSIMemFilesystemHandler() override;
     202             : 
     203             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
     204             :                                    const char *pszAccess, bool bSetError,
     205             :                                    CSLConstList /* papszOptions */) override;
     206             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     207             :              int nFlags) override;
     208             :     int Unlink(const char *pszFilename) override;
     209             :     int Mkdir(const char *pszDirname, long nMode) override;
     210             :     int Rmdir(const char *pszDirname) override;
     211             :     int RmdirRecursive(const char *pszDirname) override;
     212             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     213             :     int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
     214             :                void *) override;
     215             :     GIntBig GetDiskFreeSpace(const char *pszDirname) override;
     216             : 
     217             :     static CPLString NormalizePath(const std::string &in);
     218             : 
     219             :     int Unlink_unlocked(const char *pszFilename);
     220             : 
     221           1 :     VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
     222             :     {
     223           1 :         return new VSIMemFilesystemHandler(pszPrefix);
     224             :     }
     225             : };
     226             : 
     227             : /************************************************************************/
     228             : /* ==================================================================== */
     229             : /*                              VSIMemFile                              */
     230             : /* ==================================================================== */
     231             : /************************************************************************/
     232             : 
     233             : /************************************************************************/
     234             : /*                             VSIMemFile()                             */
     235             : /************************************************************************/
     236             : 
     237      106737 : VSIMemFile::VSIMemFile()
     238             : {
     239      106737 :     time(&mTime);
     240      106737 : }
     241             : 
     242             : /************************************************************************/
     243             : /*                            ~VSIMemFile()                             */
     244             : /************************************************************************/
     245             : 
     246      104042 : VSIMemFile::~VSIMemFile()
     247             : {
     248      104042 :     if (bOwnData && pabyData)
     249       84940 :         CPLFree(pabyData);
     250      104041 : }
     251             : 
     252             : /************************************************************************/
     253             : /*                             SetLength()                              */
     254             : /************************************************************************/
     255             : 
     256             : // Must be called under exclusive lock
     257     1193060 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
     258             : 
     259             : {
     260     1193060 :     if (nNewLength > nMaxLength)
     261             :     {
     262        2807 :         CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
     263        2807 :         return false;
     264             :     }
     265             : 
     266             :     /* -------------------------------------------------------------------- */
     267             :     /*      Grow underlying array if needed.                                */
     268             :     /* -------------------------------------------------------------------- */
     269     1190260 :     if (nNewLength > nAllocLength)
     270             :     {
     271             :         // If we don't own the buffer, we cannot reallocate it because
     272             :         // the return address might be different from the one passed by
     273             :         // the caller. Hence, the caller would not be able to free
     274             :         // the buffer.
     275      141610 :         if (!bOwnData)
     276             :         {
     277           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     278             :                      "Cannot extended in-memory file whose ownership was not "
     279             :                      "transferred");
     280           1 :             return false;
     281             :         }
     282             : 
     283             :         // If the first allocation is 1 MB or above, just take that value
     284             :         // as the one to allocate
     285             :         // Otherwise slightly reserve more to avoid too frequent reallocations.
     286      141609 :         const vsi_l_offset nNewAlloc =
     287       85618 :             (nAllocLength == 0 && nNewLength >= 1024 * 1024)
     288      141708 :                 ? nNewLength
     289      141510 :                 : nNewLength + nNewLength / 10 + 5000;
     290      141609 :         GByte *pabyNewData = nullptr;
     291             :         if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
     292             :             nNewAlloc)
     293             :         {
     294      283218 :             pabyNewData = static_cast<GByte *>(
     295      141609 :                 nAllocLength == 0
     296       85618 :                     ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
     297       55991 :                     : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
     298             :         }
     299      141609 :         if (pabyNewData == nullptr)
     300             :         {
     301           1 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     302             :                      "Cannot extend in-memory file to " CPL_FRMT_GUIB
     303             :                      " bytes due to out-of-memory situation",
     304             :                      nNewAlloc);
     305           1 :             return false;
     306             :         }
     307             : 
     308      141608 :         if (nAllocLength > 0)
     309             :         {
     310             :             // Clear the new allocated part of the buffer (only needed if
     311             :             // there was already reserved memory, otherwise VSICalloc() has
     312             :             // zeroized it already)
     313       55991 :             memset(pabyNewData + nAllocLength, 0,
     314       55991 :                    static_cast<size_t>(nNewAlloc - nAllocLength));
     315             :         }
     316             : 
     317      141608 :         pabyData = pabyNewData;
     318      141608 :         nAllocLength = nNewAlloc;
     319             :     }
     320     1048650 :     else if (nNewLength < nLength)
     321             :     {
     322        3587 :         memset(pabyData + nNewLength, 0,
     323        3587 :                static_cast<size_t>(nLength - nNewLength));
     324             :     }
     325             : 
     326     1190260 :     nLength = nNewLength;
     327     1190260 :     time(&mTime);
     328             : 
     329     1190250 :     return true;
     330             : }
     331             : 
     332             : /************************************************************************/
     333             : /* ==================================================================== */
     334             : /*                             VSIMemHandle                             */
     335             : /* ==================================================================== */
     336             : /************************************************************************/
     337             : 
     338             : /************************************************************************/
     339             : /*                            ~VSIMemHandle()                           */
     340             : /************************************************************************/
     341             : 
     342      386373 : VSIMemHandle::~VSIMemHandle()
     343             : {
     344      193186 :     VSIMemHandle::Close();
     345      386374 : }
     346             : 
     347             : /************************************************************************/
     348             : /*                               Close()                                */
     349             : /************************************************************************/
     350             : 
     351      393688 : int VSIMemHandle::Close()
     352             : 
     353             : {
     354      393688 :     if (poFile)
     355             :     {
     356             : #ifdef DEBUG_VERBOSE
     357             :         CPLDebug("VSIMEM", "Closing handle %p on %s: ref_count=%d (before)",
     358             :                  this, poFile->osFilename.c_str(),
     359             :                  static_cast<int>(poFile.use_count()));
     360             : #endif
     361      193185 :         poFile = nullptr;
     362             :     }
     363             : 
     364      393678 :     return 0;
     365             : }
     366             : 
     367             : /************************************************************************/
     368             : /*                                Seek()                                */
     369             : /************************************************************************/
     370             : 
     371     5270130 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
     372             : 
     373             : {
     374             :     vsi_l_offset nLength;
     375             :     {
     376     5270130 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     377     5270110 :         nLength = poFile->nLength;
     378             :     }
     379             : 
     380     5270120 :     if (nWhence == SEEK_CUR)
     381             :     {
     382             :         if (nOffset > INT_MAX)
     383             :         {
     384             :             // printf("likely negative offset intended\n");
     385             :         }
     386      394780 :         m_nOffset += nOffset;
     387             :     }
     388     4875340 :     else if (nWhence == SEEK_SET)
     389             :     {
     390     4648080 :         m_nOffset = nOffset;
     391             :     }
     392      227267 :     else if (nWhence == SEEK_END)
     393             :     {
     394      227267 :         m_nOffset = nLength + nOffset;
     395             :     }
     396             :     else
     397             :     {
     398           0 :         errno = EINVAL;
     399           0 :         return -1;
     400             :     }
     401             : 
     402     5270120 :     bEOF = false;
     403             : 
     404     5270120 :     return 0;
     405             : }
     406             : 
     407             : /************************************************************************/
     408             : /*                                Tell()                                */
     409             : /************************************************************************/
     410             : 
     411     3441320 : vsi_l_offset VSIMemHandle::Tell()
     412             : 
     413             : {
     414     3441320 :     return m_nOffset;
     415             : }
     416             : 
     417             : /************************************************************************/
     418             : /*                                Read()                                */
     419             : /************************************************************************/
     420             : 
     421     7823320 : size_t VSIMemHandle::Read(void *pBuffer, size_t nBytesToRead)
     422             : 
     423             : {
     424     7823320 :     const vsi_l_offset nOffset = m_nOffset;
     425             : 
     426     7823320 :     if (nBytesToRead == 0)
     427        1649 :         return 0;
     428             : 
     429     7821670 :     if (!m_bReadAllowed)
     430             :     {
     431          58 :         m_bError = true;
     432          58 :         return 0;
     433             :     }
     434             : 
     435     7821610 :     bool bEOFTmp = bEOF;
     436     7821610 :     size_t nBytesRead = nBytesToRead;
     437             : 
     438             :     // Do not access/modify bEOF under the lock to avoid confusing Coverity
     439             :     // Scan since we access it in other methods outside of the lock.
     440             :     const auto DoUnderLock =
     441    62555200 :         [this, nOffset, pBuffer, &nBytesToRead, &nBytesRead, &bEOFTmp]
     442             :     {
     443    15643200 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     444             : 
     445     7821610 :         if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
     446             :         {
     447       31846 :             bEOFTmp = true;
     448       31846 :             return false;
     449             :         }
     450     7789760 :         if (nBytesToRead + nOffset > poFile->nLength)
     451             :         {
     452       54856 :             nBytesRead = static_cast<size_t>(poFile->nLength - nOffset);
     453       54861 :             bEOFTmp = true;
     454             :         }
     455             : 
     456     7789760 :         if (nBytesToRead)
     457     7789760 :             memcpy(pBuffer, poFile->pabyData + nOffset, nBytesRead);
     458     7789770 :         return true;
     459     7821610 :     };
     460             : 
     461     7821610 :     bool bRet = DoUnderLock();
     462     7821610 :     bEOF = bEOFTmp;
     463     7821610 :     if (!bRet)
     464       31846 :         return 0;
     465             : 
     466     7789770 :     m_nOffset += nBytesRead;
     467             : 
     468     7789770 :     return nBytesRead;
     469             : }
     470             : 
     471             : /************************************************************************/
     472             : /*                              PRead()                                 */
     473             : /************************************************************************/
     474             : 
     475         456 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
     476             :                            vsi_l_offset nOffset) const
     477             : {
     478         912 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     479             : 
     480         456 :     if (nOffset < poFile->nLength)
     481             :     {
     482             :         const size_t nToCopy = static_cast<size_t>(
     483        1323 :             std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
     484         441 :                      static_cast<vsi_l_offset>(nSize)));
     485         441 :         memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
     486             :                nToCopy);
     487         441 :         return nToCopy;
     488             :     }
     489          15 :     return 0;
     490             : }
     491             : 
     492             : /************************************************************************/
     493             : /*                               Write()                                */
     494             : /************************************************************************/
     495             : 
     496     1673780 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nBytesToWrite)
     497             : 
     498             : {
     499     1673780 :     const vsi_l_offset nOffset = m_nOffset;
     500             : 
     501     1673780 :     if (!bUpdate)
     502             :     {
     503           1 :         errno = EACCES;
     504           1 :         return 0;
     505             :     }
     506             : 
     507             :     {
     508     1673780 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     509             : 
     510     1673780 :         if (nBytesToWrite + nOffset < nBytesToWrite)
     511             :         {
     512           0 :             return 0;
     513             :         }
     514             : 
     515     1673780 :         if (nBytesToWrite + nOffset > poFile->nLength)
     516             :         {
     517     1188780 :             if (!poFile->SetLength(nBytesToWrite + nOffset))
     518        2789 :                 return 0;
     519             :         }
     520             : 
     521     1670970 :         if (nBytesToWrite)
     522     1660970 :             memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
     523             : 
     524     1670960 :         time(&poFile->mTime);
     525             :     }
     526             : 
     527     1670980 :     m_nOffset += nBytesToWrite;
     528             : 
     529     1670980 :     return nBytesToWrite;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                             ClearErr()                               */
     534             : /************************************************************************/
     535             : 
     536       47647 : void VSIMemHandle::ClearErr()
     537             : 
     538             : {
     539       47647 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     540       47647 :     bEOF = false;
     541       47647 :     m_bError = false;
     542       47646 : }
     543             : 
     544             : /************************************************************************/
     545             : /*                              Error()                                 */
     546             : /************************************************************************/
     547             : 
     548       50414 : int VSIMemHandle::Error()
     549             : 
     550             : {
     551       50414 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     552      100830 :     return m_bError ? TRUE : FALSE;
     553             : }
     554             : 
     555             : /************************************************************************/
     556             : /*                                Eof()                                 */
     557             : /************************************************************************/
     558             : 
     559      149623 : int VSIMemHandle::Eof()
     560             : 
     561             : {
     562      149623 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     563      299245 :     return bEOF ? TRUE : FALSE;
     564             : }
     565             : 
     566             : /************************************************************************/
     567             : /*                             Truncate()                               */
     568             : /************************************************************************/
     569             : 
     570         809 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
     571             : {
     572         809 :     if (!bUpdate)
     573             :     {
     574           1 :         errno = EACCES;
     575           1 :         return -1;
     576             :     }
     577             : 
     578        1616 :     CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     579         808 :     if (poFile->SetLength(nNewSize))
     580         788 :         return 0;
     581             : 
     582          20 :     return -1;
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /* ==================================================================== */
     587             : /*                       VSIMemFilesystemHandler                        */
     588             : /* ==================================================================== */
     589             : /************************************************************************/
     590             : 
     591             : /************************************************************************/
     592             : /*                      ~VSIMemFilesystemHandler()                      */
     593             : /************************************************************************/
     594             : 
     595        1128 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
     596             : 
     597             : {
     598        1127 :     oFileList.clear();
     599             : 
     600        1127 :     if (hMutex != nullptr)
     601         208 :         CPLDestroyMutex(hMutex);
     602        1127 :     hMutex = nullptr;
     603        1128 : }
     604             : 
     605             : /************************************************************************/
     606             : /*                                Open()                                */
     607             : /************************************************************************/
     608             : 
     609             : VSIVirtualHandleUniquePtr
     610      270649 : VSIMemFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
     611             :                               bool bSetError, CSLConstList /* papszOptions */)
     612             : 
     613             : {
     614      541302 :     CPLMutexHolder oHolder(&hMutex);
     615      811959 :     const CPLString osFilename = NormalizePath(pszFilename);
     616      270653 :     if (osFilename.empty())
     617           0 :         return nullptr;
     618             : 
     619      270653 :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     620      270653 :     const size_t iPos = osFilename.find("||maxlength=");
     621      270653 :     if (iPos != std::string::npos)
     622             :     {
     623        3026 :         nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
     624        6052 :             osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
     625             :     }
     626             : 
     627             :     /* -------------------------------------------------------------------- */
     628             :     /*      Get the filename we are opening, create if needed.              */
     629             :     /* -------------------------------------------------------------------- */
     630      541306 :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     631      270653 :     const auto oIter = oFileList.find(osFilename);
     632      270653 :     if (oIter != oFileList.end())
     633             :     {
     634       96299 :         poFile = oIter->second;
     635             :     }
     636             : 
     637             :     // If no file and opening in read, error out.
     638      722408 :     if (strstr(pszAccess, "w") == nullptr &&
     639      270653 :         strstr(pszAccess, "a") == nullptr && poFile == nullptr)
     640             :     {
     641       88234 :         if (bSetError)
     642             :         {
     643       15365 :             VSIError(VSIE_FileError, "No such file or directory");
     644             :         }
     645       88234 :         errno = ENOENT;
     646       88234 :         return nullptr;
     647             :     }
     648             : 
     649             :     // Create.
     650      182419 :     if (poFile == nullptr)
     651             :     {
     652       86120 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
     653       86120 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
     654             :         {
     655           1 :             if (bSetError)
     656             :             {
     657           0 :                 VSIError(VSIE_FileError,
     658             :                          "Could not create directory %s for writing",
     659             :                          osFileDir.c_str());
     660             :             }
     661           1 :             errno = ENOENT;
     662           1 :             return nullptr;
     663             :         }
     664             : 
     665       86119 :         poFile = std::make_shared<VSIMemFile>();
     666       86119 :         poFile->osFilename = osFilename;
     667       86119 :         oFileList[poFile->osFilename] = poFile;
     668             : #ifdef DEBUG_VERBOSE
     669             :         CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
     670             :                  static_cast<int>(poFile.use_count()));
     671             : #endif
     672       86119 :         poFile->nMaxLength = nMaxLength;
     673             :     }
     674             :     // Overwrite
     675       96299 :     else if (strstr(pszAccess, "w"))
     676             :     {
     677        3469 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     678        3469 :         poFile->SetLength(0);
     679        3469 :         poFile->nMaxLength = nMaxLength;
     680             :     }
     681             : 
     682      182418 :     if (poFile->bIsDirectory)
     683             :     {
     684        1625 :         errno = EISDIR;
     685        1625 :         return nullptr;
     686             :     }
     687             : 
     688             :     /* -------------------------------------------------------------------- */
     689             :     /*      Setup the file handle on this file.                             */
     690             :     /* -------------------------------------------------------------------- */
     691      361586 :     auto poHandle = std::make_unique<VSIMemHandle>();
     692             : 
     693      180793 :     poHandle->poFile = poFile;
     694      180793 :     poHandle->m_nOffset = 0;
     695      180793 :     poHandle->bEOF = false;
     696      248063 :     poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
     697       67270 :                         strchr(pszAccess, 'a');
     698      180793 :     poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
     699             : 
     700             : #ifdef DEBUG_VERBOSE
     701             :     CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle.get(),
     702             :              pszFilename, static_cast<int>(poFile.use_count()));
     703             : #endif
     704      180793 :     if (strchr(pszAccess, 'a'))
     705             :     {
     706             :         vsi_l_offset nOffset;
     707             :         {
     708          42 :             CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     709          42 :             nOffset = poFile->nLength;
     710             :         }
     711          42 :         poHandle->m_nOffset = nOffset;
     712             :     }
     713             : 
     714      180793 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     715             : }
     716             : 
     717             : /************************************************************************/
     718             : /*                                Stat()                                */
     719             : /************************************************************************/
     720             : 
     721      579782 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
     722             :                                   VSIStatBufL *pStatBuf, int /* nFlags */)
     723             : 
     724             : {
     725     1159560 :     CPLMutexHolder oHolder(&hMutex);
     726             : 
     727     1739350 :     const CPLString osFilename = NormalizePath(pszFilename);
     728             : 
     729      579782 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     730             : 
     731      579782 :     if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
     732             :     {
     733       57025 :         pStatBuf->st_size = 0;
     734       57025 :         pStatBuf->st_mode = S_IFDIR;
     735       57025 :         return 0;
     736             :     }
     737             : 
     738      522757 :     auto oIter = oFileList.find(osFilename);
     739      522757 :     if (oIter == oFileList.end())
     740             :     {
     741      441767 :         errno = ENOENT;
     742      441767 :         return -1;
     743             :     }
     744             : 
     745      161980 :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     746             : 
     747       80990 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     748             : 
     749       80990 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     750       80990 :     if (poFile->bIsDirectory)
     751             :     {
     752       45321 :         pStatBuf->st_size = 0;
     753       45321 :         pStatBuf->st_mode = S_IFDIR;
     754             :     }
     755             :     else
     756             :     {
     757       35669 :         pStatBuf->st_size = poFile->nLength;
     758       35669 :         pStatBuf->st_mode = S_IFREG;
     759       35669 :         if (const char *mtime =
     760       35669 :                 CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
     761             :         {
     762          31 :             pStatBuf->st_mtime =
     763          31 :                 static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
     764             :         }
     765             :         else
     766             :         {
     767       35638 :             pStatBuf->st_mtime = poFile->mTime;
     768             :         }
     769             :     }
     770             : 
     771       80990 :     return 0;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                               Unlink()                               */
     776             : /************************************************************************/
     777             : 
     778       80832 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
     779             : 
     780             : {
     781      161664 :     CPLMutexHolder oHolder(&hMutex);
     782      161664 :     return Unlink_unlocked(pszFilename);
     783             : }
     784             : 
     785             : /************************************************************************/
     786             : /*                           Unlink_unlocked()                          */
     787             : /************************************************************************/
     788             : 
     789       92747 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
     790             : 
     791             : {
     792      278241 :     const CPLString osFilename = NormalizePath(pszFilename);
     793             : 
     794       92747 :     auto oIter = oFileList.find(osFilename);
     795       92747 :     if (oIter == oFileList.end())
     796             :     {
     797       17710 :         errno = ENOENT;
     798       17710 :         return -1;
     799             :     }
     800             : 
     801             : #ifdef DEBUG_VERBOSE
     802             :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     803             :     CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
     804             :              static_cast<int>(poFile.use_count()));
     805             : #endif
     806       75037 :     oFileList.erase(oIter);
     807             : 
     808       75037 :     return 0;
     809             : }
     810             : 
     811             : /************************************************************************/
     812             : /*                               Mkdir()                                */
     813             : /************************************************************************/
     814             : 
     815       98650 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
     816             : 
     817             : {
     818      197300 :     CPLMutexHolder oHolder(&hMutex);
     819             : 
     820      295950 :     const CPLString osPathname = NormalizePath(pszPathname);
     821       98650 :     if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
     822             :     {
     823       89973 :         if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
     824       44829 :             return 0;
     825             :         // "/vsimem/.#!HIDDEN!#./{unique_counter}"
     826       45144 :         else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
     827             :                  std::string::npos)
     828       44829 :             return 0;
     829             : 
     830             :         // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
     831             :         // accept creating an explicit directory
     832             :     }
     833             : 
     834        8992 :     if (oFileList.find(osPathname) != oFileList.end())
     835             :     {
     836         785 :         errno = EEXIST;
     837         785 :         return -1;
     838             :     }
     839             : 
     840        8207 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
     841        8207 :     poFile->osFilename = osPathname;
     842        8207 :     poFile->bIsDirectory = true;
     843        8207 :     oFileList[osPathname] = poFile;
     844             : #ifdef DEBUG_VERBOSE
     845             :     CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
     846             :              static_cast<int>(poFile.use_count()));
     847             : #endif
     848        8207 :     CPL_IGNORE_RET_VAL(poFile);
     849        8207 :     return 0;
     850             : }
     851             : 
     852             : /************************************************************************/
     853             : /*                               Rmdir()                                */
     854             : /************************************************************************/
     855             : 
     856          75 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
     857             : 
     858             : {
     859          75 :     return Unlink(pszPathname);
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                          RmdirRecursive()                            */
     864             : /************************************************************************/
     865             : 
     866        5389 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
     867             : {
     868       10778 :     CPLMutexHolder oHolder(&hMutex);
     869             : 
     870       10778 :     const CPLString osPath = NormalizePath(pszDirname);
     871        5389 :     const size_t nPathLen = osPath.size();
     872        5389 :     int ret = 0;
     873        5389 :     if (osPath == "/vsimem")
     874             :     {
     875             :         // Clean-up all files under pszDirname, except hidden directories
     876             :         // if called from "/vsimem"
     877           8 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     878             :              /* no automatic increment */)
     879             :         {
     880           6 :             const char *pszFilePath = iter->second->osFilename.c_str();
     881           6 :             const size_t nFileLen = iter->second->osFilename.size();
     882          12 :             if (nFileLen > nPathLen &&
     883           6 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     884          18 :                 pszFilePath[nPathLen] == '/' &&
     885           6 :                 !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
     886             :             {
     887           2 :                 iter = oFileList.erase(iter);
     888             :             }
     889             :             else
     890             :             {
     891           4 :                 ++iter;
     892             :             }
     893             :         }
     894             :     }
     895             :     else
     896             :     {
     897        5387 :         ret = -1;
     898      840761 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     899             :              /* no automatic increment */)
     900             :         {
     901      835374 :             const char *pszFilePath = iter->second->osFilename.c_str();
     902      835374 :             const size_t nFileLen = iter->second->osFilename.size();
     903     1007930 :             if (nFileLen >= nPathLen &&
     904      852508 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     905       17134 :                 (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
     906             :             {
     907             :                 // If VSIRmdirRecursive() is used correctly, it should at
     908             :                 // least delete the directory on which it has been called
     909       22416 :                 ret = 0;
     910       22416 :                 iter = oFileList.erase(iter);
     911             :             }
     912             :             else
     913             :             {
     914      812958 :                 ++iter;
     915             :             }
     916             :         }
     917             : 
     918             :         // Make sure that it always succeed on the root hidden directory
     919        5387 :         if (osPath == szHIDDEN_DIRNAME)
     920           3 :             ret = 0;
     921             :     }
     922       10778 :     return ret;
     923             : }
     924             : 
     925             : /************************************************************************/
     926             : /*                             ReadDirEx()                              */
     927             : /************************************************************************/
     928             : 
     929       33638 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
     930             : 
     931             : {
     932       67276 :     CPLMutexHolder oHolder(&hMutex);
     933             : 
     934       67276 :     const CPLString osPath = NormalizePath(pszPath);
     935             : 
     936       33638 :     char **papszDir = nullptr;
     937       33638 :     const size_t nPathLen = osPath.size();
     938             : 
     939             :     // In case of really big number of files in the directory, CSLAddString
     940             :     // can be slow (see #2158). We then directly build the list.
     941       33638 :     int nItems = 0;
     942       33638 :     int nAllocatedItems = 0;
     943             : 
     944       33638 :     if (osPath == szHIDDEN_DIRNAME)
     945             :     {
     946             :         // Special mode for hidden filenames.
     947             :         // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
     948             :         // created so they do not appear in oFileList, but their subcontent
     949             :         // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
     950          20 :         std::set<std::string> oSetSubDirs;
     951         161 :         for (const auto &iter : oFileList)
     952             :         {
     953         151 :             const char *pszFilePath = iter.second->osFilename.c_str();
     954         288 :             if (iter.second->osFilename.size() > nPathLen &&
     955         137 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
     956             :             {
     957          13 :                 char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
     958          13 :                 char *pszSlash = strchr(pszItem, '/');
     959          13 :                 if (pszSlash)
     960          13 :                     *pszSlash = 0;
     961          13 :                 if (cpl::contains(oSetSubDirs, pszItem))
     962             :                 {
     963           2 :                     CPLFree(pszItem);
     964           2 :                     continue;
     965             :                 }
     966          11 :                 oSetSubDirs.insert(pszItem);
     967             : 
     968          11 :                 if (nItems == 0)
     969             :                 {
     970             :                     papszDir =
     971           6 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
     972           6 :                     nAllocatedItems = 1;
     973             :                 }
     974           5 :                 else if (nItems >= nAllocatedItems)
     975             :                 {
     976           5 :                     nAllocatedItems = nAllocatedItems * 2;
     977           5 :                     papszDir = static_cast<char **>(CPLRealloc(
     978           5 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
     979             :                 }
     980             : 
     981          11 :                 papszDir[nItems] = pszItem;
     982          11 :                 papszDir[nItems + 1] = nullptr;
     983             : 
     984          11 :                 nItems++;
     985          11 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
     986           0 :                     break;
     987             :             }
     988             :         }
     989             :     }
     990             :     else
     991             :     {
     992    11750800 :         for (const auto &iter : oFileList)
     993             :         {
     994    11717100 :             const char *pszFilePath = iter.second->osFilename.c_str();
     995    11717100 :             if (iter.second->osFilename.size() > nPathLen &&
     996     7333520 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     997    22606500 :                 pszFilePath[nPathLen] == '/' &&
     998     3555800 :                 strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
     999             :             {
    1000      353365 :                 if (nItems == 0)
    1001             :                 {
    1002             :                     papszDir =
    1003       31947 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
    1004       31947 :                     nAllocatedItems = 1;
    1005             :                 }
    1006      321418 :                 else if (nItems >= nAllocatedItems)
    1007             :                 {
    1008       48047 :                     nAllocatedItems = nAllocatedItems * 2;
    1009       48047 :                     papszDir = static_cast<char **>(CPLRealloc(
    1010       48047 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
    1011             :                 }
    1012             : 
    1013      353365 :                 papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
    1014      353365 :                 papszDir[nItems + 1] = nullptr;
    1015             : 
    1016      353365 :                 nItems++;
    1017      353365 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
    1018           1 :                     break;
    1019             :             }
    1020             :         }
    1021             :     }
    1022             : 
    1023       67276 :     return papszDir;
    1024             : }
    1025             : 
    1026             : /************************************************************************/
    1027             : /*                               Rename()                               */
    1028             : /************************************************************************/
    1029             : 
    1030         561 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
    1031             :                                     const char *pszNewPath, GDALProgressFunc,
    1032             :                                     void *)
    1033             : 
    1034             : {
    1035        1122 :     CPLMutexHolder oHolder(&hMutex);
    1036             : 
    1037        1683 :     const std::string osOldPath = NormalizePath(pszOldPath);
    1038        1683 :     const std::string osNewPath = NormalizePath(pszNewPath);
    1039         561 :     if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
    1040           3 :         return -1;
    1041             : 
    1042         558 :     if (osOldPath.compare(osNewPath) == 0)
    1043         304 :         return 0;
    1044             : 
    1045         254 :     if (oFileList.find(osOldPath) == oFileList.end())
    1046             :     {
    1047           0 :         errno = ENOENT;
    1048           0 :         return -1;
    1049             :     }
    1050             : 
    1051             :     std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
    1052         254 :         oFileList.find(osOldPath);
    1053         667 :     while (it != oFileList.end() && it->first.find(osOldPath) == 0)
    1054             :     {
    1055         826 :         const std::string osRemainder = it->first.substr(osOldPath.size());
    1056         413 :         if (osRemainder.empty() || osRemainder[0] == '/')
    1057             :         {
    1058         604 :             const std::string osNewFullPath = osNewPath + osRemainder;
    1059         302 :             Unlink_unlocked(osNewFullPath.c_str());
    1060         302 :             oFileList[osNewFullPath] = it->second;
    1061         302 :             it->second->osFilename = osNewFullPath;
    1062         302 :             oFileList.erase(it++);
    1063             :         }
    1064             :         else
    1065             :         {
    1066         111 :             ++it;
    1067             :         }
    1068             :     }
    1069             : 
    1070         254 :     return 0;
    1071             : }
    1072             : 
    1073             : /************************************************************************/
    1074             : /*                           NormalizePath()                            */
    1075             : /************************************************************************/
    1076             : 
    1077     1129970 : CPLString VSIMemFilesystemHandler::NormalizePath(const std::string &in)
    1078             : {
    1079     1129970 :     CPLString s(in);
    1080     1129960 :     std::replace(s.begin(), s.end(), '\\', '/');
    1081     1129980 :     s.replaceAll("//", '/');
    1082     1129950 :     if (!s.empty() && s.back() == '/')
    1083        1711 :         s.pop_back();
    1084     1129950 :     return s;
    1085             : }
    1086             : 
    1087             : /************************************************************************/
    1088             : /*                        GetDiskFreeSpace()                            */
    1089             : /************************************************************************/
    1090             : 
    1091          72 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
    1092             : {
    1093          72 :     const GIntBig nRet = CPLGetUsablePhysicalRAM();
    1094          72 :     if (nRet <= 0)
    1095           0 :         return -1;
    1096          72 :     return nRet;
    1097             : }
    1098             : 
    1099             : //! @endcond
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                       VSIInstallMemFileHandler()                     */
    1103             : /************************************************************************/
    1104             : 
    1105             : /**
    1106             :  * \brief Install "memory" file system handler.
    1107             :  *
    1108             :  * A special file handler is installed that allows block of memory to be
    1109             :  * treated as files.   All portions of the file system underneath the base
    1110             :  * path "/vsimem/" will be handled by this driver.
    1111             :  *
    1112             :  * Normal VSI*L functions can be used freely to create and destroy memory
    1113             :  * arrays treating them as if they were real file system objects.  Some
    1114             :  * additional methods exist to efficient create memory file system objects
    1115             :  * without duplicating original copies of the data or to "steal" the block
    1116             :  * of memory associated with a memory file.
    1117             :  *
    1118             :  * Directory related functions are supported.
    1119             :  *
    1120             :  * This code example demonstrates using GDAL to translate from one memory
    1121             :  * buffer to another.
    1122             :  *
    1123             :  * \code
    1124             :  * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
    1125             :  *                             vsi_l_offset *pnOutDataLength )
    1126             :  * {
    1127             :  *     // create memory file system object from buffer.
    1128             :  *     VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
    1129             :  *                                       nInDataLength, FALSE ) );
    1130             :  *
    1131             :  *     // Open memory buffer for read.
    1132             :  *     GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
    1133             :  *
    1134             :  *     // Get output format driver.
    1135             :  *     GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
    1136             :  *     GDALDatasetH hOutDS;
    1137             :  *
    1138             :  *     hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
    1139             :  *                              NULL, NULL );
    1140             :  *
    1141             :  *     // close source file, and "unlink" it.
    1142             :  *     GDALClose( hDS );
    1143             :  *     VSIUnlink( "/vsimem/work.dat" );
    1144             :  *
    1145             :  *     // seize the buffer associated with the output file.
    1146             :  *
    1147             :  *     return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
    1148             :  * }
    1149             :  * \endcode
    1150             :  */
    1151             : 
    1152        1788 : void VSIInstallMemFileHandler()
    1153             : {
    1154        1788 :     VSIFileManager::InstallHandler(
    1155        3576 :         "/vsimem/", std::make_shared<VSIMemFilesystemHandler>("/vsimem/"));
    1156        1788 : }
    1157             : 
    1158             : /************************************************************************/
    1159             : /*                        VSIFileFromMemBuffer()                        */
    1160             : /************************************************************************/
    1161             : 
    1162             : /**
    1163             :  * \brief Create memory "file" from a buffer.
    1164             :  *
    1165             :  * A virtual memory file is created from the passed buffer with the indicated
    1166             :  * filename.  Under normal conditions the filename would need to be absolute
    1167             :  * and within the /vsimem/ portion of the filesystem.
    1168             :  * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
    1169             :  * an anonymous file, that is destroyed when the handle is closed.
    1170             :  *
    1171             :  * If bTakeOwnership is TRUE, then the memory file system handler will take
    1172             :  * ownership of the buffer, freeing it when the file is deleted.  Otherwise
    1173             :  * it remains the responsibility of the caller, but should not be freed as
    1174             :  * long as it might be accessed as a file.  In no circumstances does this
    1175             :  * function take a copy of the pabyData contents.
    1176             :  *
    1177             :  * @param pszFilename the filename to be created, or nullptr
    1178             :  * @param pabyData the data buffer for the file.
    1179             :  * @param nDataLength the length of buffer in bytes.
    1180             :  * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
    1181             :  *
    1182             :  * @return open file handle on created file (see VSIFOpenL()).
    1183             :  */
    1184             : 
    1185       12412 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
    1186             :                                vsi_l_offset nDataLength, int bTakeOwnership)
    1187             : 
    1188             : {
    1189       12412 :     if (VSIFileManager::GetHandler("") ==
    1190       12412 :         VSIFileManager::GetHandler("/vsimem/"))
    1191           0 :         VSIInstallMemFileHandler();
    1192             : 
    1193             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1194       12412 :         VSIFileManager::GetHandler("/vsimem/"));
    1195             : 
    1196             :     const CPLString osFilename =
    1197       48049 :         pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
    1198       24823 :                     : std::string();
    1199       12411 :     if (osFilename == "/vsimem/")
    1200             :     {
    1201           1 :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
    1202             :                  pszFilename);
    1203           1 :         return nullptr;
    1204             :     }
    1205             : 
    1206             :     // Try to create the parent directory, if needed, before taking
    1207             :     // ownership of pabyData.
    1208       12409 :     if (!osFilename.empty())
    1209             :     {
    1210       11610 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
    1211       11610 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
    1212             :         {
    1213           0 :             VSIError(VSIE_FileError,
    1214             :                      "Could not create directory %s for writing",
    1215             :                      osFileDir.c_str());
    1216           0 :             errno = ENOENT;
    1217           0 :             return nullptr;
    1218             :         }
    1219             :     }
    1220             : 
    1221       12411 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
    1222             : 
    1223       12411 :     poFile->osFilename = osFilename;
    1224       12411 :     poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
    1225       12411 :     poFile->pabyData = pabyData;
    1226       12411 :     poFile->nLength = nDataLength;
    1227       12411 :     poFile->nAllocLength = nDataLength;
    1228             : 
    1229       12411 :     if (!osFilename.empty())
    1230             :     {
    1231       23226 :         CPLMutexHolder oHolder(&poHandler->hMutex);
    1232       11613 :         poHandler->Unlink_unlocked(osFilename);
    1233       11613 :         poHandler->oFileList[poFile->osFilename] = poFile;
    1234             : #ifdef DEBUG_VERBOSE
    1235             :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
    1236             :                  poFile->osFilename.c_str(),
    1237             :                  static_cast<int>(poFile.use_count()));
    1238             : #endif
    1239             :     }
    1240             : 
    1241             :     /* -------------------------------------------------------------------- */
    1242             :     /*      Setup the file handle on this file.                             */
    1243             :     /* -------------------------------------------------------------------- */
    1244       12411 :     VSIMemHandle *poHandle = new VSIMemHandle;
    1245             : 
    1246       12411 :     poHandle->poFile = std::move(poFile);
    1247       12411 :     poHandle->bUpdate = true;
    1248       12411 :     poHandle->m_bReadAllowed = true;
    1249       12411 :     return poHandle;
    1250             : }
    1251             : 
    1252             : /************************************************************************/
    1253             : /*                        VSIGetMemFileBuffer()                         */
    1254             : /************************************************************************/
    1255             : 
    1256             : /**
    1257             :  * \brief Fetch buffer underlying memory file.
    1258             :  *
    1259             :  * This function returns a pointer to the memory buffer underlying a
    1260             :  * virtual "in memory" file.  If bUnlinkAndSeize is TRUE the filesystem
    1261             :  * object will be deleted, and ownership of the buffer will pass to the
    1262             :  * caller, otherwise the underlying file will remain in existence.
    1263             :  * bUnlinkAndSeize should only be used for files that own their data
    1264             :  * (see the bTakeOwnership parameter of VSIFileFromMemBuffer).
    1265             :  *
    1266             :  * @param pszFilename the name of the file to grab the buffer of.
    1267             :  * @param pnDataLength (file) length returned in this variable.
    1268             :  * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
    1269             :  *
    1270             :  * @return pointer to memory buffer or NULL on failure.
    1271             :  */
    1272             : 
    1273       36377 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
    1274             :                            int bUnlinkAndSeize)
    1275             : 
    1276             : {
    1277             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1278       36377 :         VSIFileManager::GetHandler("/vsimem/"));
    1279             : 
    1280       36379 :     if (pszFilename == nullptr)
    1281           0 :         return nullptr;
    1282             : 
    1283             :     const std::string osFilename =
    1284      109124 :         VSIMemFilesystemHandler::NormalizePath(pszFilename);
    1285             : 
    1286       72748 :     CPLMutexHolder oHolder(&poHandler->hMutex);
    1287             : 
    1288       36381 :     if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
    1289         139 :         return nullptr;
    1290             : 
    1291       36242 :     std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
    1292       36242 :     GByte *pabyData = poFile->pabyData;
    1293       36242 :     if (pnDataLength != nullptr)
    1294       33525 :         *pnDataLength = poFile->nLength;
    1295             : 
    1296       36242 :     if (bUnlinkAndSeize)
    1297             :     {
    1298        4430 :         if (!poFile->bOwnData)
    1299           0 :             CPLDebug("VSIMemFile",
    1300             :                      "File doesn't own data in VSIGetMemFileBuffer!");
    1301             :         else
    1302        4430 :             poFile->bOwnData = false;
    1303             : 
    1304        4430 :         poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
    1305             : #ifdef DEBUG_VERBOSE
    1306             :         CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
    1307             :                  poFile->osFilename.c_str(),
    1308             :                  static_cast<int>(poFile.use_count()));
    1309             : #endif
    1310        4430 :         poFile->pabyData = nullptr;
    1311        4430 :         poFile->nLength = 0;
    1312        4430 :         poFile->nAllocLength = 0;
    1313             :     }
    1314             : 
    1315       36242 :     return pabyData;
    1316             : }
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                    VSIMemGenerateHiddenFilename()                    */
    1320             : /************************************************************************/
    1321             : 
    1322             : /**
    1323             :  * \brief Generates a unique filename that can be used with the /vsimem/
    1324             :  * virtual file system.
    1325             :  *
    1326             :  * This function returns a (short-lived) string containing a unique filename,
    1327             :  * (using an atomic counter), designed for temporary files that must remain
    1328             :  * invisible for other users working at the "/vsimem/" top-level, i.e.
    1329             :  * such files are not returned by VSIReadDir("/vsimem/") or
    1330             :  * VSIReadDirRecursive("/vsimem/)".
    1331             :  *
    1332             :  * The function does not create the file per se. Such filename can be used to
    1333             :  * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
    1334             :  * a directory with VSIMkdir()
    1335             :  *
    1336             :  * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
    1337             :  * etc. is of the responsibility of the user. The user should not attempt to
    1338             :  * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
    1339             :  * on the returned filename, and work only with files at the same level or
    1340             :  * in subdirectories of what is returned by this function.
    1341             :  *
    1342             :  * @param pszFilename the filename to be appended at the end of the returned
    1343             :  *                    filename. If not specified, defaults to "unnamed".
    1344             :  *
    1345             :  * @return pointer to a short-lived string (rotating buffer of strings in
    1346             :  * thread-local storage). It is recommended to use CPLStrdup() or std::string()
    1347             :  * immediately on it.
    1348             :  *
    1349             :  * @since GDAL 3.10
    1350             :  */
    1351       44089 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
    1352             : {
    1353             :     static std::atomic<uint32_t> nCounter{0};
    1354       44089 :     return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
    1355       44088 :                       pszFilename ? pszFilename : "unnamed");
    1356             : }

Generated by: LCOV version 1.14