EVOLUTION-MANAGER
Edit File: cpl_vsi_mem.cpp
/****************************************************************************** * * Project: VSI Virtual File System * Purpose: Implementation of Memory Buffer virtual IO functions. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com> * Copyright (c) 2007-2014, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "cpl_vsi.h" #include "cpl_vsi_virtual.h" #include <cerrno> #include <cstddef> #include <cstring> #include <ctime> #if HAVE_FCNTL_H # include <fcntl.h> #endif #if HAVE_SYS_STAT_H # include <sys/stat.h> #endif #include <algorithm> #include <map> #include <string> #include <utility> #include "cpl_atomic_ops.h" #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_multiproc.h" #include "cpl_string.h" //! @cond Doxygen_Suppress CPL_CVSID("$Id: cpl_vsi_mem.cpp cc40f285204d363d762804d7b9b4cd136384e85a 2018-11-08 23:36:59 +0100 Even Rouault $") /* ** Notes on Multithreading: ** ** VSIMemFilesystemHandler: This class maintains a mutex to protect ** access and update of the oFileList array which has all the "files" in ** the memory filesystem area. It is expected that multiple threads would ** want to create and read different files at the same time and so might ** collide access oFileList without the mutex. ** ** VSIMemFile: In theory we could allow different threads to update the ** the same memory file, but for simplicity we restrict to single writer, ** multiple reader as an expectation on the application code (not enforced ** here), which means we don't need to do any protection of this class. ** ** VSIMemHandle: This is essentially a "current location" representing ** on accessor to a file, and is inherently intended only to be used in ** a single thread. ** ** In General: ** ** Multiple threads accessing the memory filesystem are ok as long as ** 1) A given VSIMemHandle (i.e. FILE * at app level) isn't used by multiple ** threads at once. ** 2) A given memory file isn't accessed by more than one thread unless ** all threads are just reading. */ /************************************************************************/ /* ==================================================================== */ /* VSIMemFile */ /* ==================================================================== */ /************************************************************************/ class VSIMemFile { CPL_DISALLOW_COPY_ASSIGN(VSIMemFile) public: CPLString osFilename{}; volatile int nRefCount = 0; bool bIsDirectory = false; bool bOwnData = true; GByte *pabyData = nullptr; vsi_l_offset nLength = 0; vsi_l_offset nAllocLength = 0; vsi_l_offset nMaxLength = GUINTBIG_MAX; time_t mTime = 0; VSIMemFile(); virtual ~VSIMemFile(); bool SetLength( vsi_l_offset nNewSize ); }; /************************************************************************/ /* ==================================================================== */ /* VSIMemHandle */ /* ==================================================================== */ /************************************************************************/ class VSIMemHandle final : public VSIVirtualHandle { CPL_DISALLOW_COPY_ASSIGN(VSIMemHandle) public: VSIMemFile *poFile = nullptr; vsi_l_offset m_nOffset = 0; bool bUpdate = false; bool bEOF = false; bool bExtendFileAtNextWrite = false; VSIMemHandle() = default; ~VSIMemHandle() override = default; int Seek( vsi_l_offset nOffset, int nWhence ) override; vsi_l_offset Tell() override; size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) override; size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ) override; int Eof() override; int Close() override; int Truncate( vsi_l_offset nNewSize ) override; }; /************************************************************************/ /* ==================================================================== */ /* VSIMemFilesystemHandler */ /* ==================================================================== */ /************************************************************************/ class VSIMemFilesystemHandler final : public VSIFilesystemHandler { CPL_DISALLOW_COPY_ASSIGN(VSIMemFilesystemHandler) public: std::map<CPLString, VSIMemFile*> oFileList{}; CPLMutex *hMutex = nullptr; VSIMemFilesystemHandler() = default; ~VSIMemFilesystemHandler() override; // TODO(schwehr): Fix VSIFileFromMemBuffer so that using is not needed. using VSIFilesystemHandler::Open; VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess, bool bSetError ) override; int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ) override; int Unlink( const char *pszFilename ) override; int Mkdir( const char *pszDirname, long nMode ) override; int Rmdir( const char *pszDirname ) override; char **ReadDirEx( const char *pszDirname, int nMaxFiles ) override; int Rename( const char *oldpath, const char *newpath ) override; GIntBig GetDiskFreeSpace( const char* pszDirname ) override; static std::string NormalizePath( const std::string &in ); int Unlink_unlocked( const char *pszFilename ); }; /************************************************************************/ /* ==================================================================== */ /* VSIMemFile */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* VSIMemFile() */ /************************************************************************/ VSIMemFile::VSIMemFile() { time(&mTime); } /************************************************************************/ /* ~VSIMemFile() */ /************************************************************************/ VSIMemFile::~VSIMemFile() { if( nRefCount != 0 ) CPLError( CE_Warning, CPLE_AppDefined, "Memory file %s deleted with %d references.", osFilename.c_str(), nRefCount ); if( bOwnData && pabyData ) CPLFree( pabyData ); } /************************************************************************/ /* SetLength() */ /************************************************************************/ bool VSIMemFile::SetLength( vsi_l_offset nNewLength ) { if( nNewLength > nMaxLength ) { CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!"); return false; } /* -------------------------------------------------------------------- */ /* Grow underlying array if needed. */ /* -------------------------------------------------------------------- */ if( nNewLength > nAllocLength ) { // If we don't own the buffer, we cannot reallocate it because // the return address might be different from the one passed by // the caller. Hence, the caller would not be able to free // the buffer. if( !bOwnData ) { CPLError( CE_Failure, CPLE_NotSupported, "Cannot extended in-memory file whose ownership was not " "transferred" ); return false; } const vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000; GByte *pabyNewData = nullptr; if( static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) == nNewAlloc ) { pabyNewData = static_cast<GByte *>( VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc) )); } if( pabyNewData == nullptr ) { CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot extend in-memory file to " CPL_FRMT_GUIB " bytes due to out-of-memory situation", nNewAlloc); return false; } // Clear the new allocated part of the buffer. memset(pabyNewData + nAllocLength, 0, static_cast<size_t>(nNewAlloc - nAllocLength)); pabyData = pabyNewData; nAllocLength = nNewAlloc; } nLength = nNewLength; time(&mTime); return true; } /************************************************************************/ /* ==================================================================== */ /* VSIMemHandle */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* Close() */ /************************************************************************/ int VSIMemHandle::Close() { if( CPLAtomicDec(&(poFile->nRefCount)) == 0 ) delete poFile; poFile = nullptr; return 0; } /************************************************************************/ /* Seek() */ /************************************************************************/ int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence ) { bExtendFileAtNextWrite = false; if( nWhence == SEEK_CUR ) { if( nOffset > INT_MAX ) { //printf("likely negative offset intended\n"); } m_nOffset += nOffset; } else if( nWhence == SEEK_SET ) { m_nOffset = nOffset; } else if( nWhence == SEEK_END ) { m_nOffset = poFile->nLength + nOffset; } else { errno = EINVAL; return -1; } bEOF = false; if( m_nOffset > poFile->nLength ) { if( bUpdate ) // Writable files are zero-extended by seek past end. { bExtendFileAtNextWrite = true; } } return 0; } /************************************************************************/ /* Tell() */ /************************************************************************/ vsi_l_offset VSIMemHandle::Tell() { return m_nOffset; } /************************************************************************/ /* Read() */ /************************************************************************/ size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) { size_t nBytesToRead = nSize * nCount; if( nCount > 0 && nBytesToRead / nCount != nSize ) { bEOF = true; return 0; } if( poFile->nLength <= m_nOffset || nBytesToRead + m_nOffset < nBytesToRead ) { bEOF = true; return 0; } if( nBytesToRead + m_nOffset > poFile->nLength ) { nBytesToRead = static_cast<size_t>(poFile->nLength - m_nOffset); nCount = nBytesToRead / nSize; bEOF = true; } if( nBytesToRead ) memcpy( pBuffer, poFile->pabyData + m_nOffset, static_cast<size_t>(nBytesToRead) ); m_nOffset += nBytesToRead; return nCount; } /************************************************************************/ /* Write() */ /************************************************************************/ size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) { if( !bUpdate ) { errno = EACCES; return 0; } if( bExtendFileAtNextWrite ) { bExtendFileAtNextWrite = false; if( !poFile->SetLength( m_nOffset ) ) return 0; } const size_t nBytesToWrite = nSize * nCount; if( nCount > 0 && nBytesToWrite / nCount != nSize ) { return 0; } if( nBytesToWrite + m_nOffset < nBytesToWrite ) { return 0; } if( nBytesToWrite + m_nOffset > poFile->nLength ) { if( !poFile->SetLength( nBytesToWrite + m_nOffset ) ) return 0; } if( nBytesToWrite ) memcpy( poFile->pabyData + m_nOffset, pBuffer, nBytesToWrite ); m_nOffset += nBytesToWrite; time(&poFile->mTime); return nCount; } /************************************************************************/ /* Eof() */ /************************************************************************/ int VSIMemHandle::Eof() { return bEOF; } /************************************************************************/ /* Truncate() */ /************************************************************************/ int VSIMemHandle::Truncate( vsi_l_offset nNewSize ) { if( !bUpdate ) { errno = EACCES; return -1; } bExtendFileAtNextWrite = false; if( poFile->SetLength( nNewSize ) ) return 0; return -1; } /************************************************************************/ /* ==================================================================== */ /* VSIMemFilesystemHandler */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* ~VSIMemFilesystemHandler() */ /************************************************************************/ VSIMemFilesystemHandler::~VSIMemFilesystemHandler() { for( const auto &iter : oFileList ) { CPLAtomicDec(&iter.second->nRefCount); delete iter.second; } if( hMutex != nullptr ) CPLDestroyMutex( hMutex ); hMutex = nullptr; } /************************************************************************/ /* Open() */ /************************************************************************/ VSIVirtualHandle * VSIMemFilesystemHandler::Open( const char *pszFilename, const char *pszAccess, bool bSetError ) { CPLMutexHolder oHolder( &hMutex ); const CPLString osFilename = NormalizePath(pszFilename); if( osFilename.empty() ) return nullptr; vsi_l_offset nMaxLength = GUINTBIG_MAX; const size_t iPos = osFilename.find("||maxlength="); if( iPos != std::string::npos ) { nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig( osFilename.substr(iPos + strlen("||maxlength=")).c_str())); } /* -------------------------------------------------------------------- */ /* Get the filename we are opening, create if needed. */ /* -------------------------------------------------------------------- */ VSIMemFile *poFile = nullptr; if( oFileList.find(osFilename) != oFileList.end() ) poFile = oFileList[osFilename]; // If no file and opening in read, error out. if( strstr(pszAccess, "w") == nullptr && strstr(pszAccess, "a") == nullptr && poFile == nullptr ) { if( bSetError ) { VSIError(VSIE_FileError, "No such file or directory"); } errno = ENOENT; return nullptr; } // Create. if( poFile == nullptr ) { poFile = new VSIMemFile; poFile->osFilename = osFilename; oFileList[poFile->osFilename] = poFile; CPLAtomicInc(&(poFile->nRefCount)); // For file list. poFile->nMaxLength = nMaxLength; } // Overwrite else if( strstr(pszAccess, "w") ) { poFile->SetLength(0); poFile->nMaxLength = nMaxLength; } if( poFile->bIsDirectory ) { errno = EISDIR; return nullptr; } /* -------------------------------------------------------------------- */ /* Setup the file handle on this file. */ /* -------------------------------------------------------------------- */ VSIMemHandle *poHandle = new VSIMemHandle; poHandle->poFile = poFile; poHandle->m_nOffset = 0; poHandle->bEOF = false; poHandle->bUpdate = strstr(pszAccess, "w") || strstr(pszAccess, "+") || strstr(pszAccess, "a"); CPLAtomicInc(&(poFile->nRefCount)); if( strstr(pszAccess, "a") ) poHandle->m_nOffset = poFile->nLength; return poHandle; } /************************************************************************/ /* Stat() */ /************************************************************************/ int VSIMemFilesystemHandler::Stat( const char * pszFilename, VSIStatBufL * pStatBuf, int /* nFlags */ ) { CPLMutexHolder oHolder( &hMutex ); const CPLString osFilename = NormalizePath(pszFilename); memset( pStatBuf, 0, sizeof(VSIStatBufL) ); if( osFilename == "/vsimem" || osFilename == "/vsimem/" ) { pStatBuf->st_size = 0; pStatBuf->st_mode = S_IFDIR; return 0; } if( oFileList.find(osFilename) == oFileList.end() ) { errno = ENOENT; return -1; } VSIMemFile *poFile = oFileList[osFilename]; memset( pStatBuf, 0, sizeof(VSIStatBufL) ); if( poFile->bIsDirectory ) { pStatBuf->st_size = 0; pStatBuf->st_mode = S_IFDIR; } else { pStatBuf->st_size = poFile->nLength; pStatBuf->st_mode = S_IFREG; pStatBuf->st_mtime = poFile->mTime; } return 0; } /************************************************************************/ /* Unlink() */ /************************************************************************/ int VSIMemFilesystemHandler::Unlink( const char * pszFilename ) { CPLMutexHolder oHolder( &hMutex ); return Unlink_unlocked(pszFilename); } /************************************************************************/ /* Unlink_unlocked() */ /************************************************************************/ int VSIMemFilesystemHandler::Unlink_unlocked( const char * pszFilename ) { const CPLString osFilename = NormalizePath(pszFilename); if( oFileList.find(osFilename) == oFileList.end() ) { errno = ENOENT; return -1; } VSIMemFile *poFile = oFileList[osFilename]; if( CPLAtomicDec(&(poFile->nRefCount)) == 0 ) delete poFile; oFileList.erase( oFileList.find(osFilename) ); return 0; } /************************************************************************/ /* Mkdir() */ /************************************************************************/ int VSIMemFilesystemHandler::Mkdir( const char * pszPathname, long /* nMode */ ) { CPLMutexHolder oHolder( &hMutex ); const CPLString osPathname = NormalizePath(pszPathname); if( oFileList.find(osPathname) != oFileList.end() ) { errno = EEXIST; return -1; } VSIMemFile *poFile = new VSIMemFile; poFile->osFilename = osPathname; poFile->bIsDirectory = true; oFileList[osPathname] = poFile; CPLAtomicInc(&(poFile->nRefCount)); // Referenced by file list. return 0; } /************************************************************************/ /* Rmdir() */ /************************************************************************/ int VSIMemFilesystemHandler::Rmdir( const char * pszPathname ) { return Unlink( pszPathname ); } /************************************************************************/ /* ReadDirEx() */ /************************************************************************/ char **VSIMemFilesystemHandler::ReadDirEx( const char *pszPath, int nMaxFiles ) { CPLMutexHolder oHolder( &hMutex ); const CPLString osPath = NormalizePath(pszPath); char **papszDir = nullptr; size_t nPathLen = osPath.size(); if( nPathLen > 0 && osPath.back() == '/' ) nPathLen--; // In case of really big number of files in the directory, CSLAddString // can be slow (see #2158). We then directly build the list. int nItems = 0; int nAllocatedItems = 0; for( const auto& iter : oFileList ) { const char *pszFilePath = iter.second->osFilename.c_str(); if( EQUALN(osPath, pszFilePath, nPathLen) && pszFilePath[nPathLen] == '/' && strstr(pszFilePath+nPathLen+1, "/") == nullptr ) { if( nItems == 0 ) { papszDir = static_cast<char**>(CPLCalloc(2, sizeof(char*))); nAllocatedItems = 1; } else if( nItems >= nAllocatedItems ) { nAllocatedItems = nAllocatedItems * 2; papszDir = static_cast<char**>( CPLRealloc(papszDir, (nAllocatedItems + 2)*sizeof(char*)) ); } papszDir[nItems] = CPLStrdup(pszFilePath+nPathLen+1); papszDir[nItems+1] = nullptr; nItems++; if( nMaxFiles > 0 && nItems > nMaxFiles ) break; } } return papszDir; } /************************************************************************/ /* Rename() */ /************************************************************************/ int VSIMemFilesystemHandler::Rename( const char *pszOldPath, const char *pszNewPath ) { CPLMutexHolder oHolder( &hMutex ); const CPLString osOldPath = NormalizePath(pszOldPath); const CPLString osNewPath = NormalizePath(pszNewPath); if( osOldPath.compare(osNewPath) == 0 ) return 0; if( oFileList.find(osOldPath) == oFileList.end() ) { errno = ENOENT; return -1; } std::map<CPLString, VSIMemFile*>::iterator it = oFileList.find(osOldPath); while( it != oFileList.end() && it->first.ifind(osOldPath) == 0 ) { const CPLString osRemainder = it->first.substr(osOldPath.size()); if( osRemainder.empty() || osRemainder[0] == '/' ) { const CPLString osNewFullPath = osNewPath + osRemainder; Unlink_unlocked(osNewFullPath); oFileList[osNewFullPath] = it->second; it->second->osFilename = osNewFullPath; oFileList.erase(it++); } else { ++it; } } return 0; } /************************************************************************/ /* NormalizePath() */ /************************************************************************/ std::string VSIMemFilesystemHandler::NormalizePath( const std::string &in ) { std::string s(in); std::replace(s.begin(), s.end(), '\\', '/'); return s; } /************************************************************************/ /* GetDiskFreeSpace() */ /************************************************************************/ GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace( const char* /*pszDirname*/ ) { const GIntBig nRet = CPLGetUsablePhysicalRAM(); if( nRet <= 0 ) return -1; return nRet; } //! @endcond /************************************************************************/ /* VSIInstallMemFileHandler() */ /************************************************************************/ /** * \brief Install "memory" file system handler. * * A special file handler is installed that allows block of memory to be * treated as files. All portions of the file system underneath the base * path "/vsimem/" will be handled by this driver. * * Normal VSI*L functions can be used freely to create and destroy memory * arrays treating them as if they were real file system objects. Some * additional methods exist to efficient create memory file system objects * without duplicating original copies of the data or to "steal" the block * of memory associated with a memory file. * * Directory related functions are supported. * * This code example demonstrates using GDAL to translate from one memory * buffer to another. * * \code * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength, * vsi_l_offset *pnOutDataLength ) * { * // create memory file system object from buffer. * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData, * nInDataLength, FALSE ) ); * * // Open memory buffer for read. * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly ); * * // Get output format driver. * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" ); * GDALDatasetH hOutDS; * * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL, * NULL, NULL ); * * // close source file, and "unlink" it. * GDALClose( hDS ); * VSIUnlink( "/vsimem/work.dat" ); * * // seize the buffer associated with the output file. * * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE ); * } * \endcode */ void VSIInstallMemFileHandler() { VSIFileManager::InstallHandler( "/vsimem/", new VSIMemFilesystemHandler ); } /************************************************************************/ /* VSIFileFromMemBuffer() */ /************************************************************************/ /** * \brief Create memory "file" from a buffer. * * A virtual memory file is created from the passed buffer with the indicated * filename. Under normal conditions the filename would need to be absolute * and within the /vsimem/ portion of the filesystem. * * If bTakeOwnership is TRUE, then the memory file system handler will take * ownership of the buffer, freeing it when the file is deleted. Otherwise * it remains the responsibility of the caller, but should not be freed as * long as it might be accessed as a file. In no circumstances does this * function take a copy of the pabyData contents. * * @param pszFilename the filename to be created. * @param pabyData the data buffer for the file. * @param nDataLength the length of buffer in bytes. * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE. * * @return open file handle on created file (see VSIFOpenL()). */ VSILFILE *VSIFileFromMemBuffer( const char *pszFilename, GByte *pabyData, vsi_l_offset nDataLength, int bTakeOwnership ) { if( VSIFileManager::GetHandler("") == VSIFileManager::GetHandler("/vsimem/") ) VSIInstallMemFileHandler(); VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>( VSIFileManager::GetHandler("/vsimem/")); if( pszFilename == nullptr ) return nullptr; const CPLString osFilename = VSIMemFilesystemHandler::NormalizePath(pszFilename); if( osFilename.empty() ) return nullptr; VSIMemFile *poFile = new VSIMemFile; poFile->osFilename = osFilename; poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership); poFile->pabyData = pabyData; poFile->nLength = nDataLength; poFile->nAllocLength = nDataLength; { CPLMutexHolder oHolder( &poHandler->hMutex ); poHandler->Unlink_unlocked(osFilename); poHandler->oFileList[poFile->osFilename] = poFile; CPLAtomicInc(&(poFile->nRefCount)); } // TODO(schwehr): Fix this so that the using statement is not needed. // Will just adding the bool for bSetError be okay? return reinterpret_cast<VSILFILE *>( poHandler->Open( osFilename, "r+" ) ); } /************************************************************************/ /* VSIGetMemFileBuffer() */ /************************************************************************/ /** * \brief Fetch buffer underlying memory file. * * This function returns a pointer to the memory buffer underlying a * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem * object will be deleted, and ownership of the buffer will pass to the * caller otherwise the underlying file will remain in existence. * * @param pszFilename the name of the file to grab the buffer of. * @param pnDataLength (file) length returned in this variable. * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered. * * @return pointer to memory buffer or NULL on failure. */ GByte *VSIGetMemFileBuffer( const char *pszFilename, vsi_l_offset *pnDataLength, int bUnlinkAndSeize ) { VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>( VSIFileManager::GetHandler("/vsimem/")); if( pszFilename == nullptr ) return nullptr; const CPLString osFilename = VSIMemFilesystemHandler::NormalizePath(pszFilename); CPLMutexHolder oHolder( &poHandler->hMutex ); if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() ) return nullptr; VSIMemFile *poFile = poHandler->oFileList[osFilename]; GByte *pabyData = poFile->pabyData; if( pnDataLength != nullptr ) *pnDataLength = poFile->nLength; if( bUnlinkAndSeize ) { if( !poFile->bOwnData ) CPLDebug( "VSIMemFile", "File doesn't own data in VSIGetMemFileBuffer!" ); else poFile->bOwnData = false; poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) ); CPLAtomicDec(&(poFile->nRefCount)); delete poFile; } return pabyData; }