LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 414 430 96.3 %
Date: 2025-09-10 17:48:50 Functions: 37 37 100.0 %

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

Generated by: LCOV version 1.14