LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 415 430 96.5 %
Date: 2025-05-24 03:54:53 Functions: 37 38 97.4 %

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

Generated by: LCOV version 1.14