LCOV - code coverage report
Current view: top level - port - cpl_vsi_mem.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 407 426 95.5 %
Date: 2025-01-18 12:42:00 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      168265 :     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         721 :     bool HasPRead() const override
     176             :     {
     177         721 :         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<std::string, std::shared_ptr<VSIMemFile>> oFileList{};
     197             :     CPLMutex *hMutex = nullptr;
     198             : 
     199        1393 :     explicit VSIMemFilesystemHandler(const char *pszPrefix)
     200        1393 :         : m_osPrefix(pszPrefix)
     201             :     {
     202        1393 :     }
     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       94328 : VSIMemFile::VSIMemFile()
     243             : {
     244       94328 :     time(&mTime);
     245       94328 : }
     246             : 
     247             : /************************************************************************/
     248             : /*                            ~VSIMemFile()                             */
     249             : /************************************************************************/
     250             : 
     251       91677 : VSIMemFile::~VSIMemFile()
     252             : {
     253       91677 :     if (bOwnData && pabyData)
     254       76099 :         CPLFree(pabyData);
     255       91677 : }
     256             : 
     257             : /************************************************************************/
     258             : /*                             SetLength()                              */
     259             : /************************************************************************/
     260             : 
     261             : // Must be called under exclusive lock
     262     1124040 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
     263             : 
     264             : {
     265     1124040 :     if (nNewLength > nMaxLength)
     266             :     {
     267        4640 :         CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
     268        4640 :         return false;
     269             :     }
     270             : 
     271             :     /* -------------------------------------------------------------------- */
     272             :     /*      Grow underlying array if needed.                                */
     273             :     /* -------------------------------------------------------------------- */
     274     1119400 :     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      129130 :         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      129129 :         const vsi_l_offset nNewAlloc =
     292       78751 :             (nAllocLength == 0 && nNewLength >= 1024 * 1024)
     293      129206 :                 ? nNewLength
     294      129052 :                 : nNewLength + nNewLength / 10 + 5000;
     295      129129 :         GByte *pabyNewData = nullptr;
     296             :         if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
     297             :             nNewAlloc)
     298             :         {
     299      258258 :             pabyNewData = static_cast<GByte *>(
     300      129129 :                 nAllocLength == 0
     301       78751 :                     ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
     302       50378 :                     : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
     303             :         }
     304      129129 :         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      129128 :         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       50378 :             memset(pabyNewData + nAllocLength, 0,
     319       50378 :                    static_cast<size_t>(nNewAlloc - nAllocLength));
     320             :         }
     321             : 
     322      129128 :         pabyData = pabyNewData;
     323      129128 :         nAllocLength = nNewAlloc;
     324             :     }
     325      990266 :     else if (nNewLength < nLength)
     326             :     {
     327        3312 :         memset(pabyData + nNewLength, 0,
     328        3312 :                static_cast<size_t>(nLength - nNewLength));
     329             :     }
     330             : 
     331     1119390 :     nLength = nNewLength;
     332     1119390 :     time(&mTime);
     333             : 
     334     1119390 :     return true;
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /* ==================================================================== */
     339             : /*                             VSIMemHandle                             */
     340             : /* ==================================================================== */
     341             : /************************************************************************/
     342             : 
     343             : /************************************************************************/
     344             : /*                            ~VSIMemHandle()                           */
     345             : /************************************************************************/
     346             : 
     347      336509 : VSIMemHandle::~VSIMemHandle()
     348             : {
     349      168253 :     VSIMemHandle::Close();
     350      336511 : }
     351             : 
     352             : /************************************************************************/
     353             : /*                               Close()                                */
     354             : /************************************************************************/
     355             : 
     356      336526 : int VSIMemHandle::Close()
     357             : 
     358             : {
     359      336526 :     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      168258 :         poFile = nullptr;
     367             :     }
     368             : 
     369      336523 :     return 0;
     370             : }
     371             : 
     372             : /************************************************************************/
     373             : /*                                Seek()                                */
     374             : /************************************************************************/
     375             : 
     376     5028480 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
     377             : 
     378             : {
     379             :     vsi_l_offset nLength;
     380             :     {
     381     5028480 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     382     5028420 :         nLength = poFile->nLength;
     383             :     }
     384             : 
     385     5028420 :     if (nWhence == SEEK_CUR)
     386             :     {
     387             :         if (nOffset > INT_MAX)
     388             :         {
     389             :             // printf("likely negative offset intended\n");
     390             :         }
     391      388622 :         m_nOffset += nOffset;
     392             :     }
     393     4639800 :     else if (nWhence == SEEK_SET)
     394             :     {
     395     4443940 :         m_nOffset = nOffset;
     396             :     }
     397      195861 :     else if (nWhence == SEEK_END)
     398             :     {
     399      195862 :         m_nOffset = nLength + nOffset;
     400             :     }
     401             :     else
     402             :     {
     403           0 :         errno = EINVAL;
     404           0 :         return -1;
     405             :     }
     406             : 
     407     5028430 :     bEOF = false;
     408             : 
     409     5028430 :     return 0;
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                                Tell()                                */
     414             : /************************************************************************/
     415             : 
     416     3291600 : vsi_l_offset VSIMemHandle::Tell()
     417             : 
     418             : {
     419     3291600 :     return m_nOffset;
     420             : }
     421             : 
     422             : /************************************************************************/
     423             : /*                                Read()                                */
     424             : /************************************************************************/
     425             : 
     426     7250520 : size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     427             : 
     428             : {
     429     7250520 :     const vsi_l_offset nOffset = m_nOffset;
     430             : 
     431     7250520 :     size_t nBytesToRead = nSize * nCount;
     432     7250520 :     if (nBytesToRead == 0)
     433        1587 :         return 0;
     434             : 
     435     7248940 :     if (nCount > 0 && nBytesToRead / nCount != nSize)
     436             :     {
     437           0 :         bEOF = true;
     438           0 :         return 0;
     439             :     }
     440             : 
     441     7248940 :     if (!m_bReadAllowed)
     442             :     {
     443          58 :         m_bError = true;
     444          58 :         return 0;
     445             :     }
     446             : 
     447     7248880 :     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    57980500 :         [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
     452             :     {
     453    14497800 :         CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     454             : 
     455     7248880 :         if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
     456             :         {
     457       27585 :             bEOFTmp = true;
     458       27585 :             return false;
     459             :         }
     460     7221300 :         if (nBytesToRead + nOffset > poFile->nLength)
     461             :         {
     462       49813 :             nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
     463       49979 :             nCount = nBytesToRead / nSize;
     464       49979 :             bEOFTmp = true;
     465             :         }
     466             : 
     467     7221420 :         if (nBytesToRead)
     468     7221270 :             memcpy(pBuffer, poFile->pabyData + nOffset,
     469             :                    static_cast<size_t>(nBytesToRead));
     470     7221390 :         return true;
     471     7248880 :     };
     472             : 
     473     7248880 :     bool bRet = DoUnderLock();
     474     7249000 :     bEOF = bEOFTmp;
     475     7249000 :     if (!bRet)
     476       27585 :         return 0;
     477             : 
     478     7221410 :     m_nOffset += nBytesToRead;
     479             : 
     480     7221410 :     return nCount;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*                              PRead()                                 */
     485             : /************************************************************************/
     486             : 
     487         427 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
     488             :                            vsi_l_offset nOffset) const
     489             : {
     490         854 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     491             : 
     492         427 :     if (nOffset < poFile->nLength)
     493             :     {
     494             :         const size_t nToCopy = static_cast<size_t>(
     495        1236 :             std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
     496         412 :                      static_cast<vsi_l_offset>(nSize)));
     497         412 :         memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
     498             :                nToCopy);
     499         412 :         return nToCopy;
     500             :     }
     501          15 :     return 0;
     502             : }
     503             : 
     504             : /************************************************************************/
     505             : /*                               Write()                                */
     506             : /************************************************************************/
     507             : 
     508     1583450 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
     509             : 
     510             : {
     511     1583450 :     const vsi_l_offset nOffset = m_nOffset;
     512             : 
     513     1583450 :     if (!bUpdate)
     514             :     {
     515           1 :         errno = EACCES;
     516           1 :         return 0;
     517             :     }
     518             : 
     519     1583450 :     const size_t nBytesToWrite = nSize * nCount;
     520             : 
     521             :     {
     522     1583450 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     523             : 
     524     1583450 :         if (nCount > 0 && nBytesToWrite / nCount != nSize)
     525             :         {
     526           0 :             return 0;
     527             :         }
     528     1583450 :         if (nBytesToWrite + nOffset < nBytesToWrite)
     529             :         {
     530           0 :             return 0;
     531             :         }
     532             : 
     533     1583450 :         if (nBytesToWrite + nOffset > poFile->nLength)
     534             :         {
     535     1120100 :             if (!poFile->SetLength(nBytesToWrite + nOffset))
     536        4622 :                 return 0;
     537             :         }
     538             : 
     539     1578800 :         if (nBytesToWrite)
     540     1568660 :             memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
     541             : 
     542     1578780 :         time(&poFile->mTime);
     543             :     }
     544             : 
     545     1578840 :     m_nOffset += nBytesToWrite;
     546             : 
     547     1578840 :     return nCount;
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                             ClearErr()                               */
     552             : /************************************************************************/
     553             : 
     554       25776 : void VSIMemHandle::ClearErr()
     555             : 
     556             : {
     557       25776 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     558       25776 :     bEOF = false;
     559       25776 :     m_bError = false;
     560       25776 : }
     561             : 
     562             : /************************************************************************/
     563             : /*                              Error()                                 */
     564             : /************************************************************************/
     565             : 
     566       28971 : int VSIMemHandle::Error()
     567             : 
     568             : {
     569       28971 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     570       57942 :     return m_bError ? TRUE : FALSE;
     571             : }
     572             : 
     573             : /************************************************************************/
     574             : /*                                Eof()                                 */
     575             : /************************************************************************/
     576             : 
     577      133971 : int VSIMemHandle::Eof()
     578             : 
     579             : {
     580      133971 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     581      267942 :     return bEOF ? TRUE : FALSE;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                             Truncate()                               */
     586             : /************************************************************************/
     587             : 
     588         716 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
     589             : {
     590         716 :     if (!bUpdate)
     591             :     {
     592           1 :         errno = EACCES;
     593           1 :         return -1;
     594             :     }
     595             : 
     596        1430 :     CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     597         715 :     if (poFile->SetLength(nNewSize))
     598         695 :         return 0;
     599             : 
     600          20 :     return -1;
     601             : }
     602             : 
     603             : /************************************************************************/
     604             : /* ==================================================================== */
     605             : /*                       VSIMemFilesystemHandler                        */
     606             : /* ==================================================================== */
     607             : /************************************************************************/
     608             : 
     609             : /************************************************************************/
     610             : /*                      ~VSIMemFilesystemHandler()                      */
     611             : /************************************************************************/
     612             : 
     613        1884 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
     614             : 
     615             : {
     616         942 :     oFileList.clear();
     617             : 
     618         942 :     if (hMutex != nullptr)
     619         215 :         CPLDestroyMutex(hMutex);
     620         942 :     hMutex = nullptr;
     621        1884 : }
     622             : 
     623             : /************************************************************************/
     624             : /*                                Open()                                */
     625             : /************************************************************************/
     626             : 
     627      211652 : VSIVirtualHandle *VSIMemFilesystemHandler::Open(const char *pszFilename,
     628             :                                                 const char *pszAccess,
     629             :                                                 bool bSetError,
     630             :                                                 CSLConstList /* papszOptions */)
     631             : 
     632             : {
     633      423307 :     CPLMutexHolder oHolder(&hMutex);
     634      634965 :     const std::string osFilename = NormalizePath(pszFilename);
     635      211655 :     if (osFilename.empty())
     636           0 :         return nullptr;
     637             : 
     638      211655 :     vsi_l_offset nMaxLength = GUINTBIG_MAX;
     639      211655 :     const size_t iPos = osFilename.find("||maxlength=");
     640      211655 :     if (iPos != std::string::npos)
     641             :     {
     642        3258 :         nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
     643        6516 :             osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
     644             :     }
     645             : 
     646             :     /* -------------------------------------------------------------------- */
     647             :     /*      Get the filename we are opening, create if needed.              */
     648             :     /* -------------------------------------------------------------------- */
     649      423310 :     std::shared_ptr<VSIMemFile> poFile = nullptr;
     650      211655 :     const auto oIter = oFileList.find(osFilename);
     651      211655 :     if (oIter != oFileList.end())
     652             :     {
     653       79624 :         poFile = oIter->second;
     654             :     }
     655             : 
     656             :     // If no file and opening in read, error out.
     657      552514 :     if (strstr(pszAccess, "w") == nullptr &&
     658      211655 :         strstr(pszAccess, "a") == nullptr && poFile == nullptr)
     659             :     {
     660       52764 :         if (bSetError)
     661             :         {
     662        6914 :             VSIError(VSIE_FileError, "No such file or directory");
     663             :         }
     664       52764 :         errno = ENOENT;
     665       52764 :         return nullptr;
     666             :     }
     667             : 
     668             :     // Create.
     669      158891 :     if (poFile == nullptr)
     670             :     {
     671       79267 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
     672       79267 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
     673             :         {
     674           1 :             if (bSetError)
     675             :             {
     676           0 :                 VSIError(VSIE_FileError,
     677             :                          "Could not create directory %s for writing",
     678             :                          osFileDir.c_str());
     679             :             }
     680           1 :             errno = ENOENT;
     681           1 :             return nullptr;
     682             :         }
     683             : 
     684       79266 :         poFile = std::make_shared<VSIMemFile>();
     685       79266 :         poFile->osFilename = osFilename;
     686       79266 :         oFileList[poFile->osFilename] = poFile;
     687             : #ifdef DEBUG_VERBOSE
     688             :         CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
     689             :                  static_cast<int>(poFile.use_count()));
     690             : #endif
     691       79266 :         poFile->nMaxLength = nMaxLength;
     692             :     }
     693             :     // Overwrite
     694       79624 :     else if (strstr(pszAccess, "w"))
     695             :     {
     696        3222 :         CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
     697        3222 :         poFile->SetLength(0);
     698        3222 :         poFile->nMaxLength = nMaxLength;
     699             :     }
     700             : 
     701      158890 :     if (poFile->bIsDirectory)
     702             :     {
     703         960 :         errno = EISDIR;
     704         960 :         return nullptr;
     705             :     }
     706             : 
     707             :     /* -------------------------------------------------------------------- */
     708             :     /*      Setup the file handle on this file.                             */
     709             :     /* -------------------------------------------------------------------- */
     710      157930 :     VSIMemHandle *poHandle = new VSIMemHandle;
     711             : 
     712      157930 :     poHandle->poFile = poFile;
     713      157930 :     poHandle->m_nOffset = 0;
     714      157930 :     poHandle->bEOF = false;
     715      214287 :     poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
     716       56357 :                         strchr(pszAccess, 'a');
     717      157930 :     poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
     718             : 
     719             : #ifdef DEBUG_VERBOSE
     720             :     CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle,
     721             :              pszFilename, static_cast<int>(poFile.use_count()));
     722             : #endif
     723      157930 :     if (strchr(pszAccess, 'a'))
     724             :     {
     725             :         vsi_l_offset nOffset;
     726             :         {
     727          52 :             CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     728          52 :             nOffset = poFile->nLength;
     729             :         }
     730          52 :         poHandle->m_nOffset = nOffset;
     731             :     }
     732             : 
     733      157930 :     return poHandle;
     734             : }
     735             : 
     736             : /************************************************************************/
     737             : /*                                Stat()                                */
     738             : /************************************************************************/
     739             : 
     740      551662 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
     741             :                                   VSIStatBufL *pStatBuf, int /* nFlags */)
     742             : 
     743             : {
     744     1103320 :     CPLMutexHolder oHolder(&hMutex);
     745             : 
     746     1654990 :     const std::string osFilename = NormalizePath(pszFilename);
     747             : 
     748      551662 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     749             : 
     750      551662 :     if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
     751             :     {
     752       56566 :         pStatBuf->st_size = 0;
     753       56566 :         pStatBuf->st_mode = S_IFDIR;
     754       56566 :         return 0;
     755             :     }
     756             : 
     757      495096 :     auto oIter = oFileList.find(osFilename);
     758      495096 :     if (oIter == oFileList.end())
     759             :     {
     760      425952 :         errno = ENOENT;
     761      425952 :         return -1;
     762             :     }
     763             : 
     764      138288 :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     765             : 
     766       69144 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     767             : 
     768       69144 :     CPL_SHARED_LOCK oLock(poFile->m_oMutex);
     769       69144 :     if (poFile->bIsDirectory)
     770             :     {
     771       36402 :         pStatBuf->st_size = 0;
     772       36402 :         pStatBuf->st_mode = S_IFDIR;
     773             :     }
     774             :     else
     775             :     {
     776       32742 :         pStatBuf->st_size = poFile->nLength;
     777       32742 :         pStatBuf->st_mode = S_IFREG;
     778       32742 :         pStatBuf->st_mtime = poFile->mTime;
     779             :     }
     780             : 
     781       69144 :     return 0;
     782             : }
     783             : 
     784             : /************************************************************************/
     785             : /*                               Unlink()                               */
     786             : /************************************************************************/
     787             : 
     788       78116 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
     789             : 
     790             : {
     791      156232 :     CPLMutexHolder oHolder(&hMutex);
     792      156232 :     return Unlink_unlocked(pszFilename);
     793             : }
     794             : 
     795             : /************************************************************************/
     796             : /*                           Unlink_unlocked()                          */
     797             : /************************************************************************/
     798             : 
     799       87960 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
     800             : 
     801             : {
     802      263880 :     const std::string osFilename = NormalizePath(pszFilename);
     803             : 
     804       87960 :     auto oIter = oFileList.find(osFilename);
     805       87960 :     if (oIter == oFileList.end())
     806             :     {
     807       17313 :         errno = ENOENT;
     808       17313 :         return -1;
     809             :     }
     810             : 
     811             : #ifdef DEBUG_VERBOSE
     812             :     std::shared_ptr<VSIMemFile> poFile = oIter->second;
     813             :     CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
     814             :              static_cast<int>(poFile.use_count()));
     815             : #endif
     816       70647 :     oFileList.erase(oIter);
     817             : 
     818       70647 :     return 0;
     819             : }
     820             : 
     821             : /************************************************************************/
     822             : /*                               Mkdir()                                */
     823             : /************************************************************************/
     824             : 
     825       93034 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
     826             : 
     827             : {
     828      186068 :     CPLMutexHolder oHolder(&hMutex);
     829             : 
     830      279102 :     const std::string osPathname = NormalizePath(pszPathname);
     831       93034 :     if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
     832             :     {
     833       88466 :         if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
     834       44080 :             return 0;
     835             :         // "/vsimem/.#!HIDDEN!#./{unique_counter}"
     836       44386 :         else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
     837             :                  std::string::npos)
     838       44080 :             return 0;
     839             : 
     840             :         // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
     841             :         // accept creating an explicit directory
     842             :     }
     843             : 
     844        4874 :     if (oFileList.find(osPathname) != oFileList.end())
     845             :     {
     846         147 :         errno = EEXIST;
     847         147 :         return -1;
     848             :     }
     849             : 
     850        4727 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
     851        4727 :     poFile->osFilename = osPathname;
     852        4727 :     poFile->bIsDirectory = true;
     853        4727 :     oFileList[osPathname] = poFile;
     854             : #ifdef DEBUG_VERBOSE
     855             :     CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
     856             :              static_cast<int>(poFile.use_count()));
     857             : #endif
     858        4727 :     CPL_IGNORE_RET_VAL(poFile);
     859        4727 :     return 0;
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                               Rmdir()                                */
     864             : /************************************************************************/
     865             : 
     866          87 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
     867             : 
     868             : {
     869          87 :     return Unlink(pszPathname);
     870             : }
     871             : 
     872             : /************************************************************************/
     873             : /*                          RmdirRecursive()                            */
     874             : /************************************************************************/
     875             : 
     876        2881 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
     877             : {
     878        5762 :     CPLMutexHolder oHolder(&hMutex);
     879             : 
     880        5762 :     const CPLString osPath = NormalizePath(pszDirname);
     881        2881 :     const size_t nPathLen = osPath.size();
     882        2881 :     int ret = 0;
     883        2881 :     if (osPath == "/vsimem")
     884             :     {
     885             :         // Clean-up all files under pszDirname, except hidden directories
     886             :         // if called from "/vsimem"
     887           8 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     888             :              /* no automatic increment */)
     889             :         {
     890           6 :             const char *pszFilePath = iter->second->osFilename.c_str();
     891           6 :             const size_t nFileLen = iter->second->osFilename.size();
     892          12 :             if (nFileLen > nPathLen &&
     893           6 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     894          18 :                 pszFilePath[nPathLen] == '/' &&
     895           6 :                 !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
     896             :             {
     897           2 :                 iter = oFileList.erase(iter);
     898             :             }
     899             :             else
     900             :             {
     901           4 :                 ++iter;
     902             :             }
     903             :         }
     904             :     }
     905             :     else
     906             :     {
     907        2879 :         ret = -1;
     908      408437 :         for (auto iter = oFileList.begin(); iter != oFileList.end();
     909             :              /* no automatic increment */)
     910             :         {
     911      405558 :             const char *pszFilePath = iter->second->osFilename.c_str();
     912      405558 :             const size_t nFileLen = iter->second->osFilename.size();
     913      527323 :             if (nFileLen >= nPathLen &&
     914      417298 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
     915       11740 :                 (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
     916             :             {
     917             :                 // If VSIRmdirRecursive() is used correctly, it should at
     918             :                 // least delete the directory on which it has been called
     919       14513 :                 ret = 0;
     920       14513 :                 iter = oFileList.erase(iter);
     921             :             }
     922             :             else
     923             :             {
     924      391045 :                 ++iter;
     925             :             }
     926             :         }
     927             : 
     928             :         // Make sure that it always succeed on the root hidden directory
     929        2879 :         if (osPath == szHIDDEN_DIRNAME)
     930           3 :             ret = 0;
     931             :     }
     932        5762 :     return ret;
     933             : }
     934             : 
     935             : /************************************************************************/
     936             : /*                             ReadDirEx()                              */
     937             : /************************************************************************/
     938             : 
     939       26354 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
     940             : 
     941             : {
     942       52708 :     CPLMutexHolder oHolder(&hMutex);
     943             : 
     944       52708 :     const CPLString osPath = NormalizePath(pszPath);
     945             : 
     946       26354 :     char **papszDir = nullptr;
     947       26354 :     const size_t nPathLen = osPath.size();
     948             : 
     949             :     // In case of really big number of files in the directory, CSLAddString
     950             :     // can be slow (see #2158). We then directly build the list.
     951       26354 :     int nItems = 0;
     952       26354 :     int nAllocatedItems = 0;
     953             : 
     954       26354 :     if (osPath == szHIDDEN_DIRNAME)
     955             :     {
     956             :         // Special mode for hidden filenames.
     957             :         // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
     958             :         // created so they do not appear in oFileList, but their subcontent
     959             :         // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
     960          20 :         std::set<std::string> oSetSubDirs;
     961         161 :         for (const auto &iter : oFileList)
     962             :         {
     963         151 :             const char *pszFilePath = iter.second->osFilename.c_str();
     964         290 :             if (iter.second->osFilename.size() > nPathLen &&
     965         139 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
     966             :             {
     967          15 :                 char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
     968          15 :                 char *pszSlash = strchr(pszItem, '/');
     969          15 :                 if (pszSlash)
     970          15 :                     *pszSlash = 0;
     971          15 :                 if (cpl::contains(oSetSubDirs, pszItem))
     972             :                 {
     973           2 :                     CPLFree(pszItem);
     974           2 :                     continue;
     975             :                 }
     976          13 :                 oSetSubDirs.insert(pszItem);
     977             : 
     978          13 :                 if (nItems == 0)
     979             :                 {
     980             :                     papszDir =
     981           6 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
     982           6 :                     nAllocatedItems = 1;
     983             :                 }
     984           7 :                 else if (nItems >= nAllocatedItems)
     985             :                 {
     986           7 :                     nAllocatedItems = nAllocatedItems * 2;
     987           7 :                     papszDir = static_cast<char **>(CPLRealloc(
     988           7 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
     989             :                 }
     990             : 
     991          13 :                 papszDir[nItems] = pszItem;
     992          13 :                 papszDir[nItems + 1] = nullptr;
     993             : 
     994          13 :                 nItems++;
     995          13 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
     996           0 :                     break;
     997             :             }
     998             :         }
     999             :     }
    1000             :     else
    1001             :     {
    1002    10521200 :         for (const auto &iter : oFileList)
    1003             :         {
    1004    10494900 :             const char *pszFilePath = iter.second->osFilename.c_str();
    1005    10494900 :             if (iter.second->osFilename.size() > nPathLen &&
    1006     6839510 :                 memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
    1007    20513000 :                 pszFilePath[nPathLen] == '/' &&
    1008     3178660 :                 strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
    1009             :             {
    1010      390252 :                 if (nItems == 0)
    1011             :                 {
    1012             :                     papszDir =
    1013       24739 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
    1014       24739 :                     nAllocatedItems = 1;
    1015             :                 }
    1016      365513 :                 else if (nItems >= nAllocatedItems)
    1017             :                 {
    1018       48953 :                     nAllocatedItems = nAllocatedItems * 2;
    1019       48953 :                     papszDir = static_cast<char **>(CPLRealloc(
    1020       48953 :                         papszDir, (nAllocatedItems + 2) * sizeof(char *)));
    1021             :                 }
    1022             : 
    1023      390252 :                 papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
    1024      390252 :                 papszDir[nItems + 1] = nullptr;
    1025             : 
    1026      390252 :                 nItems++;
    1027      390252 :                 if (nMaxFiles > 0 && nItems > nMaxFiles)
    1028           1 :                     break;
    1029             :             }
    1030             :         }
    1031             :     }
    1032             : 
    1033       52708 :     return papszDir;
    1034             : }
    1035             : 
    1036             : /************************************************************************/
    1037             : /*                               Rename()                               */
    1038             : /************************************************************************/
    1039             : 
    1040         171 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
    1041             :                                     const char *pszNewPath)
    1042             : 
    1043             : {
    1044         342 :     CPLMutexHolder oHolder(&hMutex);
    1045             : 
    1046         513 :     const std::string osOldPath = NormalizePath(pszOldPath);
    1047         513 :     const std::string osNewPath = NormalizePath(pszNewPath);
    1048         171 :     if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
    1049           3 :         return -1;
    1050             : 
    1051         168 :     if (osOldPath.compare(osNewPath) == 0)
    1052           0 :         return 0;
    1053             : 
    1054         168 :     if (oFileList.find(osOldPath) == oFileList.end())
    1055             :     {
    1056           0 :         errno = ENOENT;
    1057           0 :         return -1;
    1058             :     }
    1059             : 
    1060             :     std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
    1061         168 :         oFileList.find(osOldPath);
    1062         528 :     while (it != oFileList.end() && it->first.find(osOldPath) == 0)
    1063             :     {
    1064         720 :         const std::string osRemainder = it->first.substr(osOldPath.size());
    1065         360 :         if (osRemainder.empty() || osRemainder[0] == '/')
    1066             :         {
    1067         462 :             const std::string osNewFullPath = osNewPath + osRemainder;
    1068         231 :             Unlink_unlocked(osNewFullPath.c_str());
    1069         231 :             oFileList[osNewFullPath] = it->second;
    1070         231 :             it->second->osFilename = osNewFullPath;
    1071         231 :             oFileList.erase(it++);
    1072             :         }
    1073             :         else
    1074             :         {
    1075         129 :             ++it;
    1076             :         }
    1077             :     }
    1078             : 
    1079         168 :     return 0;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                           NormalizePath()                            */
    1084             : /************************************************************************/
    1085             : 
    1086     1019250 : std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
    1087             : {
    1088     2038490 :     CPLString s(in);
    1089     1019240 :     std::replace(s.begin(), s.end(), '\\', '/');
    1090     1019250 :     s.replaceAll("//", '/');
    1091     1019230 :     if (!s.empty() && s.back() == '/')
    1092        1616 :         s.pop_back();
    1093             : #if __GNUC__ >= 13
    1094             :     // gcc 13 complains about below explicit std::move()
    1095             :     return s;
    1096             : #else
    1097             :     // Android NDK (and probably other compilers) warn about
    1098             :     // "warning: local variable 's' will be copied despite being returned by name [-Wreturn-std-move]"
    1099             :     // if not specifying std::move()
    1100     2038490 :     return std::move(s);
    1101             : #endif
    1102             : }
    1103             : 
    1104             : /************************************************************************/
    1105             : /*                        GetDiskFreeSpace()                            */
    1106             : /************************************************************************/
    1107             : 
    1108          70 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
    1109             : {
    1110          70 :     const GIntBig nRet = CPLGetUsablePhysicalRAM();
    1111          70 :     if (nRet <= 0)
    1112           0 :         return -1;
    1113          70 :     return nRet;
    1114             : }
    1115             : 
    1116             : //! @endcond
    1117             : 
    1118             : /************************************************************************/
    1119             : /*                       VSIInstallMemFileHandler()                     */
    1120             : /************************************************************************/
    1121             : 
    1122             : /**
    1123             :  * \brief Install "memory" file system handler.
    1124             :  *
    1125             :  * A special file handler is installed that allows block of memory to be
    1126             :  * treated as files.   All portions of the file system underneath the base
    1127             :  * path "/vsimem/" will be handled by this driver.
    1128             :  *
    1129             :  * Normal VSI*L functions can be used freely to create and destroy memory
    1130             :  * arrays treating them as if they were real file system objects.  Some
    1131             :  * additional methods exist to efficient create memory file system objects
    1132             :  * without duplicating original copies of the data or to "steal" the block
    1133             :  * of memory associated with a memory file.
    1134             :  *
    1135             :  * Directory related functions are supported.
    1136             :  *
    1137             :  * This code example demonstrates using GDAL to translate from one memory
    1138             :  * buffer to another.
    1139             :  *
    1140             :  * \code
    1141             :  * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
    1142             :  *                             vsi_l_offset *pnOutDataLength )
    1143             :  * {
    1144             :  *     // create memory file system object from buffer.
    1145             :  *     VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
    1146             :  *                                       nInDataLength, FALSE ) );
    1147             :  *
    1148             :  *     // Open memory buffer for read.
    1149             :  *     GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
    1150             :  *
    1151             :  *     // Get output format driver.
    1152             :  *     GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
    1153             :  *     GDALDatasetH hOutDS;
    1154             :  *
    1155             :  *     hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
    1156             :  *                              NULL, NULL );
    1157             :  *
    1158             :  *     // close source file, and "unlink" it.
    1159             :  *     GDALClose( hDS );
    1160             :  *     VSIUnlink( "/vsimem/work.dat" );
    1161             :  *
    1162             :  *     // seize the buffer associated with the output file.
    1163             :  *
    1164             :  *     return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
    1165             :  * }
    1166             :  * \endcode
    1167             :  */
    1168             : 
    1169        1392 : void VSIInstallMemFileHandler()
    1170             : {
    1171        1392 :     VSIFileManager::InstallHandler("/vsimem/",
    1172        1392 :                                    new VSIMemFilesystemHandler("/vsimem/"));
    1173        1392 : }
    1174             : 
    1175             : /************************************************************************/
    1176             : /*                        VSIFileFromMemBuffer()                        */
    1177             : /************************************************************************/
    1178             : 
    1179             : /**
    1180             :  * \brief Create memory "file" from a buffer.
    1181             :  *
    1182             :  * A virtual memory file is created from the passed buffer with the indicated
    1183             :  * filename.  Under normal conditions the filename would need to be absolute
    1184             :  * and within the /vsimem/ portion of the filesystem.
    1185             :  * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
    1186             :  * an anonymous file, that is destroyed when the handle is closed.
    1187             :  *
    1188             :  * If bTakeOwnership is TRUE, then the memory file system handler will take
    1189             :  * ownership of the buffer, freeing it when the file is deleted.  Otherwise
    1190             :  * it remains the responsibility of the caller, but should not be freed as
    1191             :  * long as it might be accessed as a file.  In no circumstances does this
    1192             :  * function take a copy of the pabyData contents.
    1193             :  *
    1194             :  * @param pszFilename the filename to be created, or nullptr
    1195             :  * @param pabyData the data buffer for the file.
    1196             :  * @param nDataLength the length of buffer in bytes.
    1197             :  * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
    1198             :  *
    1199             :  * @return open file handle on created file (see VSIFOpenL()).
    1200             :  */
    1201             : 
    1202       10336 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
    1203             :                                vsi_l_offset nDataLength, int bTakeOwnership)
    1204             : 
    1205             : {
    1206       10336 :     if (VSIFileManager::GetHandler("") ==
    1207       10336 :         VSIFileManager::GetHandler("/vsimem/"))
    1208           0 :         VSIInstallMemFileHandler();
    1209             : 
    1210             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1211       10336 :         VSIFileManager::GetHandler("/vsimem/"));
    1212             : 
    1213             :     const CPLString osFilename =
    1214       30286 :         pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
    1215       20671 :                     : std::string();
    1216       10336 :     if (osFilename == "/vsimem/")
    1217             :     {
    1218           1 :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
    1219             :                  pszFilename);
    1220           1 :         return nullptr;
    1221             :     }
    1222             : 
    1223             :     // Try to create the parent directory, if needed, before taking
    1224             :     // ownership of pabyData.
    1225       10334 :     if (!osFilename.empty())
    1226             :     {
    1227        9612 :         const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
    1228        9613 :         if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
    1229             :         {
    1230           0 :             VSIError(VSIE_FileError,
    1231             :                      "Could not create directory %s for writing",
    1232             :                      osFileDir.c_str());
    1233           0 :             errno = ENOENT;
    1234           0 :             return nullptr;
    1235             :         }
    1236             :     }
    1237             : 
    1238       10335 :     std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
    1239             : 
    1240       10335 :     poFile->osFilename = osFilename;
    1241       10335 :     poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
    1242       10335 :     poFile->pabyData = pabyData;
    1243       10335 :     poFile->nLength = nDataLength;
    1244       10335 :     poFile->nAllocLength = nDataLength;
    1245             : 
    1246       10335 :     if (!osFilename.empty())
    1247             :     {
    1248       19226 :         CPLMutexHolder oHolder(&poHandler->hMutex);
    1249        9613 :         poHandler->Unlink_unlocked(osFilename);
    1250        9613 :         poHandler->oFileList[poFile->osFilename] = poFile;
    1251             : #ifdef DEBUG_VERBOSE
    1252             :         CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
    1253             :                  poFile->osFilename.c_str(),
    1254             :                  static_cast<int>(poFile.use_count()));
    1255             : #endif
    1256             :     }
    1257             : 
    1258             :     /* -------------------------------------------------------------------- */
    1259             :     /*      Setup the file handle on this file.                             */
    1260             :     /* -------------------------------------------------------------------- */
    1261       10335 :     VSIMemHandle *poHandle = new VSIMemHandle;
    1262             : 
    1263       10335 :     poHandle->poFile = std::move(poFile);
    1264       10335 :     poHandle->bUpdate = true;
    1265       10335 :     poHandle->m_bReadAllowed = true;
    1266       10335 :     return poHandle;
    1267             : }
    1268             : 
    1269             : /************************************************************************/
    1270             : /*                        VSIGetMemFileBuffer()                         */
    1271             : /************************************************************************/
    1272             : 
    1273             : /**
    1274             :  * \brief Fetch buffer underlying memory file.
    1275             :  *
    1276             :  * This function returns a pointer to the memory buffer underlying a
    1277             :  * virtual "in memory" file.  If bUnlinkAndSeize is TRUE the filesystem
    1278             :  * object will be deleted, and ownership of the buffer will pass to the
    1279             :  * caller otherwise the underlying file will remain in existence.
    1280             :  *
    1281             :  * @param pszFilename the name of the file to grab the buffer of.
    1282             :  * @param pnDataLength (file) length returned in this variable.
    1283             :  * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
    1284             :  *
    1285             :  * @return pointer to memory buffer or NULL on failure.
    1286             :  */
    1287             : 
    1288       35749 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
    1289             :                            int bUnlinkAndSeize)
    1290             : 
    1291             : {
    1292             :     VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
    1293       35749 :         VSIFileManager::GetHandler("/vsimem/"));
    1294             : 
    1295       35750 :     if (pszFilename == nullptr)
    1296           0 :         return nullptr;
    1297             : 
    1298             :     const std::string osFilename =
    1299      107247 :         VSIMemFilesystemHandler::NormalizePath(pszFilename);
    1300             : 
    1301       71483 :     CPLMutexHolder oHolder(&poHandler->hMutex);
    1302             : 
    1303       35751 :     if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
    1304         126 :         return nullptr;
    1305             : 
    1306       35625 :     std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
    1307       35625 :     GByte *pabyData = poFile->pabyData;
    1308       35625 :     if (pnDataLength != nullptr)
    1309       32927 :         *pnDataLength = poFile->nLength;
    1310             : 
    1311       35625 :     if (bUnlinkAndSeize)
    1312             :     {
    1313        4407 :         if (!poFile->bOwnData)
    1314           0 :             CPLDebug("VSIMemFile",
    1315             :                      "File doesn't own data in VSIGetMemFileBuffer!");
    1316             :         else
    1317        4407 :             poFile->bOwnData = false;
    1318             : 
    1319        4407 :         poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
    1320             : #ifdef DEBUG_VERBOSE
    1321             :         CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
    1322             :                  poFile->osFilename.c_str(),
    1323             :                  static_cast<int>(poFile.use_count()));
    1324             : #endif
    1325        4407 :         poFile->pabyData = nullptr;
    1326        4407 :         poFile->nLength = 0;
    1327        4407 :         poFile->nAllocLength = 0;
    1328             :     }
    1329             : 
    1330       35625 :     return pabyData;
    1331             : }
    1332             : 
    1333             : /************************************************************************/
    1334             : /*                    VSIMemGenerateHiddenFilename()                    */
    1335             : /************************************************************************/
    1336             : 
    1337             : /**
    1338             :  * \brief Generates a unique filename that can be used with the /vsimem/
    1339             :  * virtual file system.
    1340             :  *
    1341             :  * This function returns a (short-lived) string containing a unique filename,
    1342             :  * (using an atomic counter), designed for temporary files that must remain
    1343             :  * invisible for other users working at the "/vsimem/" top-level, i.e.
    1344             :  * such files are not returned by VSIReadDir("/vsimem/") or
    1345             :  * VSIReadDirRecursive("/vsimem/)".
    1346             :  *
    1347             :  * The function does not create the file per se. Such filename can be used to
    1348             :  * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
    1349             :  * a directory with VSIMkdir()
    1350             :  *
    1351             :  * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
    1352             :  * etc. is of the responsibility of the user. The user should not attempt to
    1353             :  * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
    1354             :  * on the returned filename, and work only with files at the same level or
    1355             :  * in subdirectories of what is returned by this function.
    1356             :  *
    1357             :  * @param pszFilename the filename to be appended at the end of the returned
    1358             :  *                    filename. If not specified, defaults to "unnamed".
    1359             :  *
    1360             :  * @return pointer to a short-lived string (rotating buffer of strings in
    1361             :  * thread-local storage). It is recommended to use CPLStrdup() or std::string()
    1362             :  * immediately on it.
    1363             :  *
    1364             :  * @since GDAL 3.10
    1365             :  */
    1366       43343 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
    1367             : {
    1368             :     static std::atomic<uint32_t> nCounter{0};
    1369       43343 :     return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
    1370       43332 :                       pszFilename ? pszFilename : "unnamed");
    1371             : }

Generated by: LCOV version 1.14