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