LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 412 425 96.9 %
Date: 2026-02-12 06:20:29 Functions: 38 38 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      195805 :     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         736 :     bool HasPRead() const override
     173             :     {
     174         736 :         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        1790 :     explicit VSIMemFilesystemHandler(const char *pszPrefix)
     197        1790 :         : m_osPrefix(pszPrefix)
     198             :     {
     199        1790 :     }
     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             :     std::string
     220       86606 :     GetCanonicalFilename(const std::string &osFilename) const override
     221             :     {
     222       86606 :         return NormalizePath(osFilename);
     223             :     }
     224             : 
     225             :     int Unlink_unlocked(const char *pszFilename);
     226             : 
     227           1 :     VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
     228             :     {
     229           1 :         return new VSIMemFilesystemHandler(pszPrefix);
     230             :     }
     231             : };
     232             : 
     233             : /************************************************************************/
     234             : /* ==================================================================== */
     235             : /*                              VSIMemFile                              */
     236             : /* ==================================================================== */
     237             : /************************************************************************/
     238             : 
     239             : /************************************************************************/
     240             : /*                             VSIMemFile()                             */
     241             : /************************************************************************/
     242             : 
     243      109277 : VSIMemFile::VSIMemFile()
     244             : {
     245      109277 :     time(&mTime);
     246      109277 : }
     247             : 
     248             : /************************************************************************/
     249             : /*                            ~VSIMemFile()                             */
     250             : /************************************************************************/
     251             : 
     252      104557 : VSIMemFile::~VSIMemFile()
     253             : {
     254      104557 :     if (bOwnData && pabyData)
     255       85314 :         CPLFree(pabyData);
     256      104558 : }
     257             : 
     258             : /************************************************************************/
     259             : /*                             SetLength()                              */
     260             : /************************************************************************/
     261             : 
     262             : // Must be called under exclusive lock
     263     1194980 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
     264             : 
     265             : {
     266     1194980 :     if (nNewLength > nMaxLength)
     267             :     {
     268        2532 :         CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
     269        2532 :         return false;
     270             :     }
     271             : 
     272             :     /* -------------------------------------------------------------------- */
     273             :     /*      Grow underlying array if needed.                                */
     274             :     /* -------------------------------------------------------------------- */
     275     1192450 :     if (nNewLength > nAllocLength)
     276             :     {
     277             :         // If we don't own the buffer, we cannot reallocate it because
     278             :         // the return address might be different from the one passed by
     279             :         // the caller. Hence, the caller would not be able to free
     280             :         // the buffer.
     281      142322 :         if (!bOwnData)
     282             :         {
     283           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     284             :                      "Cannot extended in-memory file whose ownership was not "
     285             :                      "transferred");
     286           1 :             return false;
     287             :         }
     288             : 
     289             :         // If the first allocation is 1 MB or above, just take that value
     290             :         // as the one to allocate
     291             :         // Otherwise slightly reserve more to avoid too frequent reallocations.
     292      142321 :         const vsi_l_offset nNewAlloc =
     293       85946 :             (nAllocLength == 0 && nNewLength >= 1024 * 1024)
     294      142420 :                 ? nNewLength
     295      142222 :                 : nNewLength + nNewLength / 10 + 5000;
     296      142321 :         GByte *pabyNewData = nullptr;
     297             :         if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
     298             :             nNewAlloc)
     299             :         {
     300      284643 :             pabyNewData = static_cast<GByte *>(
     301      142321 :                 nAllocLength == 0
     302       85947 :                     ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
     303       56374 :                     : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
     304             :         }
     305      142322 :         if (pabyNewData == nullptr)
     306             :         {
     307           1 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     308             :                      "Cannot extend in-memory file to " CPL_FRMT_GUIB
     309             :                      " bytes due to out-of-memory situation",
     310             :                      nNewAlloc);
     311           1 :             return false;
     312             :         }
     313             : 
     314      142321 :         if (nAllocLength > 0)
     315             :         {
     316             :             // Clear the new allocated part of the buffer (only needed if
     317             :             // there was already reserved memory, otherwise VSICalloc() has
     318             :             // zeroized it already)
     319       56375 :             memset(pabyNewData + nAllocLength, 0,
     320       56375 :                    static_cast<size_t>(nNewAlloc - nAllocLength));
     321             :         }
     322             : 
     323      142321 :         pabyData = pabyNewData;
     324      142321 :         nAllocLength = nNewAlloc;
     325             :     }
     326     1050130 :     else if (nNewLength < nLength)
     327             :     {
     328        3593 :         memset(pabyData + nNewLength, 0,
     329        3593 :                static_cast<size_t>(nLength - nNewLength));
     330             :     }
     331             : 
     332     1192450 :     nLength = nNewLength;
     333     1192450 :     time(&mTime);
     334             : 
     335     1192440 :     return true;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /* ==================================================================== */
     340             : /*                             VSIMemHandle                             */
     341             : /* ==================================================================== */
     342             : /************************************************************************/
     343             : 
     344             : /************************************************************************/
     345             : /*                           ~VSIMemHandle()                            */
     346             : /************************************************************************/
     347             : 
     348      391591 : VSIMemHandle::~VSIMemHandle()
     349             : {
     350      195792 :     VSIMemHandle::Close();
     351      391593 : }
     352             : 
     353             : /************************************************************************/
     354             : /*                               Close()                                */
     355             : /************************************************************************/
     356             : 
     357      398896 : int VSIMemHandle::Close()
     358             : 
     359             : {
     360      398896 :     if (poFile)
     361             :     {
     362             : #ifdef DEBUG_VERBOSE
     363             :         CPLDebug("VSIMEM", "Closing handle %p on %s: ref_count=%d (before)",
     364             :                  this, poFile->osFilename.c_str(),
     365             :                  static_cast<int>(poFile.use_count()));
     366             : #endif
     367      195797 :         poFile = nullptr;
     368             :     }
     369             : 
     370      398897 :     return 0;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                                Seek()                                */
     375             : /************************************************************************/
     376             : 
     377     5302240 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
     378             : 
     379             : {
     380             :     vsi_l_offset nLength;
     381             :     {
     382     5302240 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     383     5302230 :         nLength = poFile->nLength;
     384             :     }
     385             : 
     386     5302210 :     if (nWhence == SEEK_CUR)
     387             :     {
     388             :         if (nOffset > INT_MAX)
     389             :         {
     390             :             // printf("likely negative offset intended\n");
     391             :         }
     392      391605 :         m_nOffset += nOffset;
     393             :     }
     394     4910610 :     else if (nWhence == SEEK_SET)
     395             :     {
     396     4680200 :         m_nOffset = nOffset;
     397             :     }
     398      230410 :     else if (nWhence == SEEK_END)
     399             :     {
     400      230409 :         m_nOffset = nLength + nOffset;
     401             :     }
     402             :     else
     403             :     {
     404           1 :         errno = EINVAL;
     405           1 :         return -1;
     406             :     }
     407             : 
     408     5302210 :     bEOF = false;
     409             : 
     410     5302210 :     return 0;
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                                Tell()                                */
     415             : /************************************************************************/
     416             : 
     417     3450950 : vsi_l_offset VSIMemHandle::Tell()
     418             : 
     419             : {
     420     3450950 :     return m_nOffset;
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                                Read()                                */
     425             : /************************************************************************/
     426             : 
     427     7833180 : size_t VSIMemHandle::Read(void *pBuffer, size_t nBytesToRead)
     428             : 
     429             : {
     430     7833180 :     const vsi_l_offset nOffset = m_nOffset;
     431             : 
     432     7833180 :     if (nBytesToRead == 0)
     433        1652 :         return 0;
     434             : 
     435     7831530 :     if (!m_bReadAllowed)
     436             :     {
     437          58 :         m_bError = true;
     438          58 :         return 0;
     439             :     }
     440             : 
     441     7831470 :     bool bEOFTmp = bEOF;
     442     7831470 :     size_t nBytesRead = nBytesToRead;
     443             : 
     444             :     // Do not access/modify bEOF under the lock to avoid confusing Coverity
     445             :     // Scan since we access it in other methods outside of the lock.
     446             :     const auto DoUnderLock =
     447    62636400 :         [this, nOffset, pBuffer, &nBytesToRead, &nBytesRead, &bEOFTmp]
     448             :     {
     449    15663600 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     450             : 
     451     7831820 :         if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
     452             :         {
     453       31980 :             bEOFTmp = true;
     454       31980 :             return false;
     455             :         }
     456     7799830 :         if (nBytesToRead + nOffset > poFile->nLength)
     457             :         {
     458       54909 :             nBytesRead = static_cast<size_t>(poFile->nLength - nOffset);
     459       54930 :             bEOFTmp = true;
     460             :         }
     461             : 
     462     7799840 :         if (nBytesToRead)
     463     7799820 :             memcpy(pBuffer, poFile->pabyData + nOffset, nBytesRead);
     464     7799840 :         return true;
     465     7831470 :     };
     466             : 
     467     7831470 :     bool bRet = DoUnderLock();
     468     7831820 :     bEOF = bEOFTmp;
     469     7831820 :     if (!bRet)
     470       31980 :         return 0;
     471             : 
     472     7799840 :     m_nOffset += nBytesRead;
     473             : 
     474     7799840 :     return nBytesRead;
     475             : }
     476             : 
     477             : /************************************************************************/
     478             : /*                               PRead()                                */
     479             : /************************************************************************/
     480             : 
     481         474 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
     482             :                            vsi_l_offset nOffset) const
     483             : {
     484         948 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     485             : 
     486         474 :     if (nOffset < poFile->nLength)
     487             :     {
     488             :         const size_t nToCopy = static_cast<size_t>(
     489        1377 :             std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
     490         459 :                      static_cast<vsi_l_offset>(nSize)));
     491         459 :         memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
     492             :                nToCopy);
     493         459 :         return nToCopy;
     494             :     }
     495          15 :     return 0;
     496             : }
     497             : 
     498             : /************************************************************************/
     499             : /*                               Write()                                */
     500             : /************************************************************************/
     501             : 
     502     1676470 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nBytesToWrite)
     503             : 
     504             : {
     505     1676470 :     const vsi_l_offset nOffset = m_nOffset;
     506             : 
     507     1676470 :     if (!bUpdate)
     508             :     {
     509           1 :         errno = EACCES;
     510           1 :         return 0;
     511             :     }
     512             : 
     513             :     {
     514     1676470 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     515             : 
     516     1676460 :         if (nBytesToWrite + nOffset < nBytesToWrite)
     517             :         {
     518           0 :             return 0;
     519             :         }
     520             : 
     521     1676460 :         if (nBytesToWrite + nOffset > poFile->nLength)
     522             :         {
     523     1190690 :             if (!poFile->SetLength(nBytesToWrite + nOffset))
     524        2514 :                 return 0;
     525             :         }
     526             : 
     527     1673920 :         if (nBytesToWrite)
     528     1663920 :             memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
     529             : 
     530     1673900 :         time(&poFile->mTime);
     531             :     }
     532             : 
     533     1673950 :     m_nOffset += nBytesToWrite;
     534             : 
     535     1673950 :     return nBytesToWrite;
     536             : }
     537             : 
     538             : /************************************************************************/
     539             : /*                              ClearErr()                              */
     540             : /************************************************************************/
     541             : 
     542       47718 : void VSIMemHandle::ClearErr()
     543             : 
     544             : {
     545       47718 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     546       47718 :     bEOF = false;
     547       47718 :     m_bError = false;
     548       47718 : }
     549             : 
     550             : /************************************************************************/
     551             : /*                               Error()                                */
     552             : /************************************************************************/
     553             : 
     554       50490 : int VSIMemHandle::Error()
     555             : 
     556             : {
     557       50490 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     558      100980 :     return m_bError ? TRUE : FALSE;
     559             : }
     560             : 
     561             : /************************************************************************/
     562             : /*                                Eof()                                 */
     563             : /************************************************************************/
     564             : 
     565      149708 : int VSIMemHandle::Eof()
     566             : 
     567             : {
     568      149708 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     569      299416 :     return bEOF ? TRUE : FALSE;
     570             : }
     571             : 
     572             : /************************************************************************/
     573             : /*                              Truncate()                              */
     574             : /************************************************************************/
     575             : 
     576         813 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
     577             : {
     578         813 :     if (!bUpdate)
     579             :     {
     580           1 :         errno = EACCES;
     581           1 :         return -1;
     582             :     }
     583             : 
     584        1624 :     CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     585         812 :     if (poFile->SetLength(nNewSize))
     586         792 :         return 0;
     587             : 
     588          20 :     return -1;
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /* ==================================================================== */
     593             : /*                       VSIMemFilesystemHandler                        */
     594             : /* ==================================================================== */
     595             : /************************************************************************/
     596             : 
     597             : /************************************************************************/
     598             : /*                      ~VSIMemFilesystemHandler()                      */
     599             : /************************************************************************/
     600             : 
     601        1128 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
     602             : 
     603             : {
     604        1127 :     oFileList.clear();
     605             : 
     606        1127 :     if (hMutex != nullptr)
     607         208 :         CPLDestroyMutex(hMutex);
     608        1127 :     hMutex = nullptr;
     609        1128 : }
     610             : 
     611             : /************************************************************************/
     612             : /*                                Open()                                */
     613             : /************************************************************************/
     614             : 
     615             : VSIVirtualHandleUniquePtr
     616      273747 : VSIMemFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
     617             :                               bool bSetError, CSLConstList /* papszOptions */)
     618             : 
     619             : {
     620      547496 :     CPLMutexHolder oHolder(&hMutex);
     621      821247 :     const CPLString osFilename = NormalizePath(pszFilename);
     622      273749 :     if (osFilename.empty())
     623           0 :         return nullptr;
     624             : 
     625      273749 :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     626      273749 :     const size_t iPos = osFilename.find("||maxlength=");
     627      273749 :     if (iPos != std::string::npos)
     628             :     {
     629        3009 :         nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
     630        6018 :             osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
     631             :     }
     632             : 
     633             :     /* -------------------------------------------------------------------- */
     634             :     /*      Get the filename we are opening, create if needed.              */
     635             :     /* -------------------------------------------------------------------- */
     636      547498 :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     637      273749 :     const auto oIter = oFileList.find(osFilename);
     638      273749 :     if (oIter != oFileList.end())
     639             :     {
     640       98549 :         poFile = oIter->second;
     641             :     }
     642             : 
     643             :     // If no file and opening in read, error out.
     644      731364 :     if (strstr(pszAccess, "w") == nullptr &&
     645      273749 :         strstr(pszAccess, "a") == nullptr && poFile == nullptr)
     646             :     {
     647       88750 :         if (bSetError)
     648             :         {
     649       15387 :             VSIError(VSIE_FileError, "No such file or directory");
     650             :         }
     651       88750 :         errno = ENOENT;
     652       88750 :         return nullptr;
     653             :     }
     654             : 
     655             :     // Create.
     656      184999 :     if (poFile == nullptr)
     657             :     {
     658       86450 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
     659       86450 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
     660             :         {
     661           1 :             if (bSetError)
     662             :             {
     663           0 :                 VSIError(VSIE_FileError,
     664             :                          "Could not create directory %s for writing",
     665             :                          osFileDir.c_str());
     666             :             }
     667           1 :             errno = ENOENT;
     668           1 :             return nullptr;
     669             :         }
     670             : 
     671       86449 :         poFile = std::make_shared<VSIMemFile>();
     672       86449 :         poFile->osFilename = osFilename;
     673       86449 :         oFileList[poFile->osFilename] = poFile;
     674             : #ifdef DEBUG_VERBOSE
     675             :         CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
     676             :                  static_cast<int>(poFile.use_count()));
     677             : #endif
     678       86449 :         poFile->nMaxLength = nMaxLength;
     679             :     }
     680             :     // Overwrite
     681       98549 :     else if (strstr(pszAccess, "w"))
     682             :     {
     683        3471 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     684        3471 :         poFile->SetLength(0);
     685        3471 :         poFile->nMaxLength = nMaxLength;
     686             :     }
     687             : 
     688      184998 :     if (poFile->bIsDirectory)
     689             :     {
     690        1641 :         errno = EISDIR;
     691        1641 :         return nullptr;
     692             :     }
     693             : 
     694             :     /* -------------------------------------------------------------------- */
     695             :     /*      Setup the file handle on this file.                             */
     696             :     /* -------------------------------------------------------------------- */
     697      366714 :     auto poHandle = std::make_unique<VSIMemHandle>();
     698             : 
     699      183357 :     poHandle->poFile = poFile;
     700      183357 :     poHandle->m_nOffset = 0;
     701      183357 :     poHandle->bEOF = false;
     702      252849 :     poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
     703       69492 :                         strchr(pszAccess, 'a');
     704      183357 :     poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
     705             : 
     706             : #ifdef DEBUG_VERBOSE
     707             :     CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle.get(),
     708             :              pszFilename, static_cast<int>(poFile.use_count()));
     709             : #endif
     710      183357 :     if (strchr(pszAccess, 'a'))
     711             :     {
     712             :         vsi_l_offset nOffset;
     713             :         {
     714          42 :             CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     715          42 :             nOffset = poFile->nLength;
     716             :         }
     717          42 :         poHandle->m_nOffset = nOffset;
     718             :     }
     719             : 
     720      183357 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     721             : }
     722             : 
     723             : /************************************************************************/
     724             : /*                                Stat()                                */
     725             : /************************************************************************/
     726             : 
     727      582992 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
     728             :                                   VSIStatBufL *pStatBuf, int /* nFlags */)
     729             : 
     730             : {
     731     1165980 :     CPLMutexHolder oHolder(&hMutex);
     732             : 
     733     1748980 :     const CPLString osFilename = NormalizePath(pszFilename);
     734             : 
     735      582992 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     736             : 
     737      582992 :     if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
     738             :     {
     739       56843 :         pStatBuf->st_size = 0;
     740       56843 :         pStatBuf->st_mode = S_IFDIR;
     741       56843 :         return 0;
     742             :     }
     743             : 
     744      526149 :     auto oIter = oFileList.find(osFilename);
     745      526149 :     if (oIter == oFileList.end())
     746             :     {
     747      444476 :         errno = ENOENT;
     748      444476 :         return -1;
     749             :     }
     750             : 
     751      163346 :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     752             : 
     753       81673 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     754             : 
     755       81673 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     756       81673 :     if (poFile->bIsDirectory)
     757             :     {
     758       45930 :         pStatBuf->st_size = 0;
     759       45930 :         pStatBuf->st_mode = S_IFDIR;
     760             :     }
     761             :     else
     762             :     {
     763       35743 :         pStatBuf->st_size = poFile->nLength;
     764       35743 :         pStatBuf->st_mode = S_IFREG;
     765       35743 :         if (const char *mtime =
     766       35743 :                 CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
     767             :         {
     768          32 :             pStatBuf->st_mtime =
     769          32 :                 static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
     770             :         }
     771             :         else
     772             :         {
     773       35711 :             pStatBuf->st_mtime = poFile->mTime;
     774             :         }
     775             :     }
     776             : 
     777       81673 :     return 0;
     778             : }
     779             : 
     780             : /************************************************************************/
     781             : /*                               Unlink()                               */
     782             : /************************************************************************/
     783             : 
     784       81048 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
     785             : 
     786             : {
     787      162096 :     CPLMutexHolder oHolder(&hMutex);
     788      162096 :     return Unlink_unlocked(pszFilename);
     789             : }
     790             : 
     791             : /************************************************************************/
     792             : /*                          Unlink_unlocked()                           */
     793             : /************************************************************************/
     794             : 
     795       93009 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
     796             : 
     797             : {
     798      279027 :     const CPLString osFilename = NormalizePath(pszFilename);
     799             : 
     800       93009 :     auto oIter = oFileList.find(osFilename);
     801       93009 :     if (oIter == oFileList.end())
     802             :     {
     803       17760 :         errno = ENOENT;
     804       17760 :         return -1;
     805             :     }
     806             : 
     807             : #ifdef DEBUG_VERBOSE
     808             :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     809             :     CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
     810             :              static_cast<int>(poFile.use_count()));
     811             : #endif
     812       75249 :     oFileList.erase(oIter);
     813             : 
     814       75249 :     return 0;
     815             : }
     816             : 
     817             : /************************************************************************/
     818             : /*                               Mkdir()                                */
     819             : /************************************************************************/
     820             : 
     821      100847 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
     822             : 
     823             : {
     824      201694 :     CPLMutexHolder oHolder(&hMutex);
     825             : 
     826      302541 :     const CPLString osPathname = NormalizePath(pszPathname);
     827      100847 :     if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
     828             :     {
     829       90003 :         if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
     830       44841 :             return 0;
     831             :         // "/vsimem/.#!HIDDEN!#./{unique_counter}"
     832       45162 :         else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
     833             :                  std::string::npos)
     834       44841 :             return 0;
     835             : 
     836             :         // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
     837             :         // accept creating an explicit directory
     838             :     }
     839             : 
     840       11165 :     if (oFileList.find(osPathname) != oFileList.end())
     841             :     {
     842         785 :         errno = EEXIST;
     843         785 :         return -1;
     844             :     }
     845             : 
     846       10380 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
     847       10380 :     poFile->osFilename = osPathname;
     848       10380 :     poFile->bIsDirectory = true;
     849       10380 :     oFileList[osPathname] = poFile;
     850             : #ifdef DEBUG_VERBOSE
     851             :     CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
     852             :              static_cast<int>(poFile.use_count()));
     853             : #endif
     854       10380 :     CPL_IGNORE_RET_VAL(poFile);
     855       10380 :     return 0;
     856             : }
     857             : 
     858             : /************************************************************************/
     859             : /*                               Rmdir()                                */
     860             : /************************************************************************/
     861             : 
     862          81 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
     863             : 
     864             : {
     865          81 :     return Unlink(pszPathname);
     866             : }
     867             : 
     868             : /************************************************************************/
     869             : /*                           RmdirRecursive()                           */
     870             : /************************************************************************/
     871             : 
     872        5506 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
     873             : {
     874       11012 :     CPLMutexHolder oHolder(&hMutex);
     875             : 
     876       11012 :     const CPLString osPath = NormalizePath(pszDirname);
     877        5506 :     const size_t nPathLen = osPath.size();
     878        5506 :     int ret = 0;
     879        5506 :     if (osPath == "/vsimem")
     880             :     {
     881             :         // Clean-up all files under pszDirname, except hidden directories
     882             :         // if called from "/vsimem"
     883           8 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     884             :              /* no automatic increment */)
     885             :         {
     886           6 :             const char *pszFilePath = iter->second->osFilename.c_str();
     887           6 :             const size_t nFileLen = iter->second->osFilename.size();
     888          12 :             if (nFileLen > nPathLen &&
     889           6 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     890          18 :                 pszFilePath[nPathLen] == '/' &&
     891           6 :                 !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
     892             :             {
     893           2 :                 iter = oFileList.erase(iter);
     894             :             }
     895             :             else
     896             :             {
     897           4 :                 ++iter;
     898             :             }
     899             :         }
     900             :     }
     901             :     else
     902             :     {
     903        5504 :         ret = -1;
     904      935272 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     905             :              /* no automatic increment */)
     906             :         {
     907      929768 :             const char *pszFilePath = iter->second->osFilename.c_str();
     908      929768 :             const size_t nFileLen = iter->second->osFilename.size();
     909     1185780 :             if (nFileLen >= nPathLen &&
     910      949122 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     911       19354 :                 (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
     912             :             {
     913             :                 // If VSIRmdirRecursive() is used correctly, it should at
     914             :                 // least delete the directory on which it has been called
     915       22730 :                 ret = 0;
     916       22730 :                 iter = oFileList.erase(iter);
     917             :             }
     918             :             else
     919             :             {
     920      907038 :                 ++iter;
     921             :             }
     922             :         }
     923             : 
     924             :         // Make sure that it always succeed on the root hidden directory
     925        5504 :         if (osPath == szHIDDEN_DIRNAME)
     926           3 :             ret = 0;
     927             :     }
     928       11012 :     return ret;
     929             : }
     930             : 
     931             : /************************************************************************/
     932             : /*                             ReadDirEx()                              */
     933             : /************************************************************************/
     934             : 
     935       33724 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
     936             : 
     937             : {
     938       67448 :     CPLMutexHolder oHolder(&hMutex);
     939             : 
     940       67448 :     const CPLString osPath = NormalizePath(pszPath);
     941             : 
     942       33724 :     char **papszDir = nullptr;
     943       33724 :     const size_t nPathLen = osPath.size();
     944             : 
     945             :     // In case of really big number of files in the directory, CSLAddString
     946             :     // can be slow (see #2158). We then directly build the list.
     947       33724 :     int nItems = 0;
     948       33724 :     int nAllocatedItems = 0;
     949             : 
     950       33724 :     if (osPath == szHIDDEN_DIRNAME)
     951             :     {
     952             :         // Special mode for hidden filenames.
     953             :         // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
     954             :         // created so they do not appear in oFileList, but their subcontent
     955             :         // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
     956          20 :         std::set<std::string> oSetSubDirs;
     957         161 :         for (const auto &iter : oFileList)
     958             :         {
     959         151 :             const char *pszFilePath = iter.second->osFilename.c_str();
     960         288 :             if (iter.second->osFilename.size() > nPathLen &&
     961         137 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
     962             :             {
     963          13 :                 char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
     964          13 :                 char *pszSlash = strchr(pszItem, '/');
     965          13 :                 if (pszSlash)
     966          13 :                     *pszSlash = 0;
     967          13 :                 if (cpl::contains(oSetSubDirs, pszItem))
     968             :                 {
     969           2 :                     CPLFree(pszItem);
     970           2 :                     continue;
     971             :                 }
     972          11 :                 oSetSubDirs.insert(pszItem);
     973             : 
     974          11 :                 if (nItems == 0)
     975             :                 {
     976             :                     papszDir =
     977           6 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
     978           6 :                     nAllocatedItems = 1;
     979             :                 }
     980           5 :                 else if (nItems >= nAllocatedItems)
     981             :                 {
     982           5 :                     nAllocatedItems = nAllocatedItems * 2;
     983           5 :                     papszDir = static_cast<char **>(CPLRealloc(
     984           5 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
     985             :                 }
     986             : 
     987          11 :                 papszDir[nItems] = pszItem;
     988          11 :                 papszDir[nItems + 1] = nullptr;
     989             : 
     990          11 :                 nItems++;
     991          11 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
     992           0 :                     break;
     993             :             }
     994             :         }
     995             :     }
     996             :     else
     997             :     {
     998    11702200 :         for (const auto &iter : oFileList)
     999             :         {
    1000    11668500 :             const char *pszFilePath = iter.second->osFilename.c_str();
    1001    11668500 :             if (iter.second->osFilename.size() > nPathLen &&
    1002     7336320 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
    1003    22550700 :                 pszFilePath[nPathLen] == '/' &&
    1004     3545960 :                 strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
    1005             :             {
    1006      353295 :                 if (nItems == 0)
    1007             :                 {
    1008             :                     papszDir =
    1009       32021 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
    1010       32021 :                     nAllocatedItems = 1;
    1011             :                 }
    1012      321274 :                 else if (nItems >= nAllocatedItems)
    1013             :                 {
    1014       48035 :                     nAllocatedItems = nAllocatedItems * 2;
    1015       48035 :                     papszDir = static_cast<char **>(CPLRealloc(
    1016       48035 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
    1017             :                 }
    1018             : 
    1019      353295 :                 papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
    1020      353295 :                 papszDir[nItems + 1] = nullptr;
    1021             : 
    1022      353295 :                 nItems++;
    1023      353295 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
    1024           1 :                     break;
    1025             :             }
    1026             :         }
    1027             :     }
    1028             : 
    1029       67448 :     return papszDir;
    1030             : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                               Rename()                               */
    1034             : /************************************************************************/
    1035             : 
    1036         561 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
    1037             :                                     const char *pszNewPath, GDALProgressFunc,
    1038             :                                     void *)
    1039             : 
    1040             : {
    1041        1122 :     CPLMutexHolder oHolder(&hMutex);
    1042             : 
    1043        1683 :     const std::string osOldPath = NormalizePath(pszOldPath);
    1044        1683 :     const std::string osNewPath = NormalizePath(pszNewPath);
    1045         561 :     if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
    1046           3 :         return -1;
    1047             : 
    1048         558 :     if (osOldPath.compare(osNewPath) == 0)
    1049         304 :         return 0;
    1050             : 
    1051         254 :     if (oFileList.find(osOldPath) == oFileList.end())
    1052             :     {
    1053           0 :         errno = ENOENT;
    1054           0 :         return -1;
    1055             :     }
    1056             : 
    1057             :     std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
    1058         254 :         oFileList.find(osOldPath);
    1059         667 :     while (it != oFileList.end() && it->first.find(osOldPath) == 0)
    1060             :     {
    1061         826 :         const std::string osRemainder = it->first.substr(osOldPath.size());
    1062         413 :         if (osRemainder.empty() || osRemainder[0] == '/')
    1063             :         {
    1064         604 :             const std::string osNewFullPath = osNewPath + osRemainder;
    1065         302 :             Unlink_unlocked(osNewFullPath.c_str());
    1066         302 :             oFileList[osNewFullPath] = it->second;
    1067         302 :             it->second->osFilename = osNewFullPath;
    1068         302 :             oFileList.erase(it++);
    1069             :         }
    1070             :         else
    1071             :         {
    1072         111 :             ++it;
    1073             :         }
    1074             :     }
    1075             : 
    1076         254 :     return 0;
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                           NormalizePath()                            */
    1081             : /************************************************************************/
    1082             : 
    1083     1225600 : CPLString VSIMemFilesystemHandler::NormalizePath(const std::string &in)
    1084             : {
    1085     1225600 :     CPLString s(in);
    1086     1225580 :     std::replace(s.begin(), s.end(), '\\', '/');
    1087     1225600 :     s.replaceAll("//", '/');
    1088     1225580 :     if (!s.empty() && s.back() == '/')
    1089        1743 :         s.pop_back();
    1090     1225580 :     return s;
    1091             : }
    1092             : 
    1093             : /************************************************************************/
    1094             : /*                          GetDiskFreeSpace()                          */
    1095             : /************************************************************************/
    1096             : 
    1097          72 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
    1098             : {
    1099          72 :     const GIntBig nRet = CPLGetUsablePhysicalRAM();
    1100          72 :     if (nRet <= 0)
    1101           0 :         return -1;
    1102          72 :     return nRet;
    1103             : }
    1104             : 
    1105             : //! @endcond
    1106             : 
    1107             : /************************************************************************/
    1108             : /*                      VSIInstallMemFileHandler()                      */
    1109             : /************************************************************************/
    1110             : 
    1111             : /**
    1112             :  * \brief Install "memory" file system handler.
    1113             :  *
    1114             :  * A special file handler is installed that allows block of memory to be
    1115             :  * treated as files.   All portions of the file system underneath the base
    1116             :  * path "/vsimem/" will be handled by this driver.
    1117             :  *
    1118             :  * Normal VSI*L functions can be used freely to create and destroy memory
    1119             :  * arrays treating them as if they were real file system objects.  Some
    1120             :  * additional methods exist to efficient create memory file system objects
    1121             :  * without duplicating original copies of the data or to "steal" the block
    1122             :  * of memory associated with a memory file.
    1123             :  *
    1124             :  * Directory related functions are supported.
    1125             :  *
    1126             :  * This code example demonstrates using GDAL to translate from one memory
    1127             :  * buffer to another.
    1128             :  *
    1129             :  * \code
    1130             :  * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
    1131             :  *                             vsi_l_offset *pnOutDataLength )
    1132             :  * {
    1133             :  *     // create memory file system object from buffer.
    1134             :  *     VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
    1135             :  *                                       nInDataLength, FALSE ) );
    1136             :  *
    1137             :  *     // Open memory buffer for read.
    1138             :  *     GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
    1139             :  *
    1140             :  *     // Get output format driver.
    1141             :  *     GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
    1142             :  *     GDALDatasetH hOutDS;
    1143             :  *
    1144             :  *     hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
    1145             :  *                              NULL, NULL );
    1146             :  *
    1147             :  *     // close source file, and "unlink" it.
    1148             :  *     GDALClose( hDS );
    1149             :  *     VSIUnlink( "/vsimem/work.dat" );
    1150             :  *
    1151             :  *     // seize the buffer associated with the output file.
    1152             :  *
    1153             :  *     return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
    1154             :  * }
    1155             :  * \endcode
    1156             :  */
    1157             : 
    1158        1789 : void VSIInstallMemFileHandler()
    1159             : {
    1160        1789 :     VSIFileManager::InstallHandler(
    1161        3578 :         "/vsimem/", std::make_shared<VSIMemFilesystemHandler>("/vsimem/"));
    1162        1789 : }
    1163             : 
    1164             : /************************************************************************/
    1165             : /*                        VSIFileFromMemBuffer()                        */
    1166             : /************************************************************************/
    1167             : 
    1168             : /**
    1169             :  * \brief Create memory "file" from a buffer.
    1170             :  *
    1171             :  * A virtual memory file is created from the passed buffer with the indicated
    1172             :  * filename.  Under normal conditions the filename would need to be absolute
    1173             :  * and within the /vsimem/ portion of the filesystem.
    1174             :  * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
    1175             :  * an anonymous file, that is destroyed when the handle is closed.
    1176             :  *
    1177             :  * If bTakeOwnership is TRUE, then the memory file system handler will take
    1178             :  * ownership of the buffer, freeing it when the file is deleted.  Otherwise
    1179             :  * it remains the responsibility of the caller, but should not be freed as
    1180             :  * long as it might be accessed as a file.  In no circumstances does this
    1181             :  * function take a copy of the pabyData contents.
    1182             :  *
    1183             :  * @param pszFilename the filename to be created, or nullptr
    1184             :  * @param pabyData the data buffer for the file.
    1185             :  * @param nDataLength the length of buffer in bytes.
    1186             :  * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
    1187             :  *
    1188             :  * @return open file handle on created file (see VSIFOpenL()).
    1189             :  */
    1190             : 
    1191       12449 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
    1192             :                                vsi_l_offset nDataLength, int bTakeOwnership)
    1193             : 
    1194             : {
    1195       12449 :     if (VSIFileManager::GetHandler("") ==
    1196       12447 :         VSIFileManager::GetHandler("/vsimem/"))
    1197           0 :         VSIInstallMemFileHandler();
    1198             : 
    1199             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1200       12448 :         VSIFileManager::GetHandler("/vsimem/"));
    1201             : 
    1202             :     const CPLString osFilename =
    1203       48214 :         pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
    1204       24895 :                     : std::string();
    1205       12447 :     if (osFilename == "/vsimem/")
    1206             :     {
    1207           1 :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
    1208             :                  pszFilename);
    1209           1 :         return nullptr;
    1210             :     }
    1211             : 
    1212             :     // Try to create the parent directory, if needed, before taking
    1213             :     // ownership of pabyData.
    1214       12441 :     if (!osFilename.empty())
    1215             :     {
    1216       11653 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
    1217       11653 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
    1218             :         {
    1219           0 :             VSIError(VSIE_FileError,
    1220             :                      "Could not create directory %s for writing",
    1221             :                      osFileDir.c_str());
    1222           0 :             errno = ENOENT;
    1223           0 :             return nullptr;
    1224             :         }
    1225             :     }
    1226             : 
    1227       12451 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
    1228             : 
    1229       12448 :     poFile->osFilename = osFilename;
    1230       12448 :     poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
    1231       12448 :     poFile->pabyData = pabyData;
    1232       12448 :     poFile->nLength = nDataLength;
    1233       12448 :     poFile->nAllocLength = nDataLength;
    1234             : 
    1235       12448 :     if (!osFilename.empty())
    1236             :     {
    1237       23318 :         CPLMutexHolder oHolder(&poHandler->hMutex);
    1238       11659 :         poHandler->Unlink_unlocked(osFilename);
    1239       11659 :         poHandler->oFileList[poFile->osFilename] = poFile;
    1240             : #ifdef DEBUG_VERBOSE
    1241             :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
    1242             :                  poFile->osFilename.c_str(),
    1243             :                  static_cast<int>(poFile.use_count()));
    1244             : #endif
    1245             :     }
    1246             : 
    1247             :     /* -------------------------------------------------------------------- */
    1248             :     /*      Setup the file handle on this file.                             */
    1249             :     /* -------------------------------------------------------------------- */
    1250       12448 :     VSIMemHandle *poHandle = new VSIMemHandle;
    1251             : 
    1252       12448 :     poHandle->poFile = std::move(poFile);
    1253       12448 :     poHandle->bUpdate = true;
    1254       12448 :     poHandle->m_bReadAllowed = true;
    1255       12448 :     return poHandle;
    1256             : }
    1257             : 
    1258             : /************************************************************************/
    1259             : /*                        VSIGetMemFileBuffer()                         */
    1260             : /************************************************************************/
    1261             : 
    1262             : /**
    1263             :  * \brief Fetch buffer underlying memory file.
    1264             :  *
    1265             :  * This function returns a pointer to the memory buffer underlying a
    1266             :  * virtual "in memory" file.  If bUnlinkAndSeize is TRUE the filesystem
    1267             :  * object will be deleted, and ownership of the buffer will pass to the
    1268             :  * caller, otherwise the underlying file will remain in existence.
    1269             :  * bUnlinkAndSeize should only be used for files that own their data
    1270             :  * (see the bTakeOwnership parameter of VSIFileFromMemBuffer).
    1271             :  *
    1272             :  * @param pszFilename the name of the file to grab the buffer of.
    1273             :  * @param pnDataLength (file) length returned in this variable.
    1274             :  * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
    1275             :  *
    1276             :  * @return pointer to memory buffer or NULL on failure.
    1277             :  */
    1278             : 
    1279       36378 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
    1280             :                            int bUnlinkAndSeize)
    1281             : 
    1282             : {
    1283             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1284       36378 :         VSIFileManager::GetHandler("/vsimem/"));
    1285             : 
    1286       36380 :     if (pszFilename == nullptr)
    1287           0 :         return nullptr;
    1288             : 
    1289             :     const std::string osFilename =
    1290      109134 :         VSIMemFilesystemHandler::NormalizePath(pszFilename);
    1291             : 
    1292       72755 :     CPLMutexHolder oHolder(&poHandler->hMutex);
    1293             : 
    1294       36380 :     if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
    1295         139 :         return nullptr;
    1296             : 
    1297       36241 :     std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
    1298       36241 :     GByte *pabyData = poFile->pabyData;
    1299       36241 :     if (pnDataLength != nullptr)
    1300       33525 :         *pnDataLength = poFile->nLength;
    1301             : 
    1302       36241 :     if (bUnlinkAndSeize)
    1303             :     {
    1304        4430 :         if (!poFile->bOwnData)
    1305           0 :             CPLDebug("VSIMemFile",
    1306             :                      "File doesn't own data in VSIGetMemFileBuffer!");
    1307             :         else
    1308        4430 :             poFile->bOwnData = false;
    1309             : 
    1310        4430 :         poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
    1311             : #ifdef DEBUG_VERBOSE
    1312             :         CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
    1313             :                  poFile->osFilename.c_str(),
    1314             :                  static_cast<int>(poFile.use_count()));
    1315             : #endif
    1316        4430 :         poFile->pabyData = nullptr;
    1317        4430 :         poFile->nLength = 0;
    1318        4430 :         poFile->nAllocLength = 0;
    1319             :     }
    1320             : 
    1321       36241 :     return pabyData;
    1322             : }
    1323             : 
    1324             : /************************************************************************/
    1325             : /*                    VSIMemGenerateHiddenFilename()                    */
    1326             : /************************************************************************/
    1327             : 
    1328             : /**
    1329             :  * \brief Generates a unique filename that can be used with the /vsimem/
    1330             :  * virtual file system.
    1331             :  *
    1332             :  * This function returns a (short-lived) string containing a unique filename,
    1333             :  * (using an atomic counter), designed for temporary files that must remain
    1334             :  * invisible for other users working at the "/vsimem/" top-level, i.e.
    1335             :  * such files are not returned by VSIReadDir("/vsimem/") or
    1336             :  * VSIReadDirRecursive("/vsimem/)".
    1337             :  *
    1338             :  * The function does not create the file per se. Such filename can be used to
    1339             :  * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
    1340             :  * a directory with VSIMkdir()
    1341             :  *
    1342             :  * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
    1343             :  * etc. is of the responsibility of the user. The user should not attempt to
    1344             :  * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
    1345             :  * on the returned filename, and work only with files at the same level or
    1346             :  * in subdirectories of what is returned by this function.
    1347             :  *
    1348             :  * @param pszFilename the filename to be appended at the end of the returned
    1349             :  *                    filename. If not specified, defaults to "unnamed".
    1350             :  *
    1351             :  * @return pointer to a short-lived string (rotating buffer of strings in
    1352             :  * thread-local storage). It is recommended to use CPLStrdup() or std::string()
    1353             :  * immediately on it.
    1354             :  *
    1355             :  * @since GDAL 3.10
    1356             :  */
    1357       44099 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
    1358             : {
    1359             :     static std::atomic<uint32_t> nCounter{0};
    1360       44099 :     return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
    1361       44080 :                       pszFilename ? pszFilename : "unnamed");
    1362             : }

Generated by: LCOV version 1.14