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

Generated by: LCOV version 1.14