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 195805 : 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 736 : bool HasPRead() const override
173 : {
174 736 : 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 1790 : explicit VSIMemFilesystemHandler(const char *pszPrefix)
197 1790 : : m_osPrefix(pszPrefix)
198 : {
199 1790 : }
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 : std::string
220 86606 : GetCanonicalFilename(const std::string &osFilename) const override
221 : {
222 86606 : return NormalizePath(osFilename);
223 : }
224 :
225 : int Unlink_unlocked(const char *pszFilename);
226 :
227 1 : VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
228 : {
229 1 : return new VSIMemFilesystemHandler(pszPrefix);
230 : }
231 : };
232 :
233 : /************************************************************************/
234 : /* ==================================================================== */
235 : /* VSIMemFile */
236 : /* ==================================================================== */
237 : /************************************************************************/
238 :
239 : /************************************************************************/
240 : /* VSIMemFile() */
241 : /************************************************************************/
242 :
243 109277 : VSIMemFile::VSIMemFile()
244 : {
245 109277 : time(&mTime);
246 109277 : }
247 :
248 : /************************************************************************/
249 : /* ~VSIMemFile() */
250 : /************************************************************************/
251 :
252 104557 : VSIMemFile::~VSIMemFile()
253 : {
254 104557 : if (bOwnData && pabyData)
255 85314 : CPLFree(pabyData);
256 104558 : }
257 :
258 : /************************************************************************/
259 : /* SetLength() */
260 : /************************************************************************/
261 :
262 : // Must be called under exclusive lock
263 1194980 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
264 :
265 : {
266 1194980 : if (nNewLength > nMaxLength)
267 : {
268 2532 : CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
269 2532 : return false;
270 : }
271 :
272 : /* -------------------------------------------------------------------- */
273 : /* Grow underlying array if needed. */
274 : /* -------------------------------------------------------------------- */
275 1192450 : if (nNewLength > nAllocLength)
276 : {
277 : // If we don't own the buffer, we cannot reallocate it because
278 : // the return address might be different from the one passed by
279 : // the caller. Hence, the caller would not be able to free
280 : // the buffer.
281 142322 : if (!bOwnData)
282 : {
283 1 : CPLError(CE_Failure, CPLE_NotSupported,
284 : "Cannot extended in-memory file whose ownership was not "
285 : "transferred");
286 1 : return false;
287 : }
288 :
289 : // If the first allocation is 1 MB or above, just take that value
290 : // as the one to allocate
291 : // Otherwise slightly reserve more to avoid too frequent reallocations.
292 142321 : const vsi_l_offset nNewAlloc =
293 85946 : (nAllocLength == 0 && nNewLength >= 1024 * 1024)
294 142420 : ? nNewLength
295 142222 : : nNewLength + nNewLength / 10 + 5000;
296 142321 : GByte *pabyNewData = nullptr;
297 : if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
298 : nNewAlloc)
299 : {
300 284643 : pabyNewData = static_cast<GByte *>(
301 142321 : nAllocLength == 0
302 85947 : ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
303 56374 : : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
304 : }
305 142322 : if (pabyNewData == nullptr)
306 : {
307 1 : CPLError(CE_Failure, CPLE_OutOfMemory,
308 : "Cannot extend in-memory file to " CPL_FRMT_GUIB
309 : " bytes due to out-of-memory situation",
310 : nNewAlloc);
311 1 : return false;
312 : }
313 :
314 142321 : if (nAllocLength > 0)
315 : {
316 : // Clear the new allocated part of the buffer (only needed if
317 : // there was already reserved memory, otherwise VSICalloc() has
318 : // zeroized it already)
319 56375 : memset(pabyNewData + nAllocLength, 0,
320 56375 : static_cast<size_t>(nNewAlloc - nAllocLength));
321 : }
322 :
323 142321 : pabyData = pabyNewData;
324 142321 : nAllocLength = nNewAlloc;
325 : }
326 1050130 : else if (nNewLength < nLength)
327 : {
328 3593 : memset(pabyData + nNewLength, 0,
329 3593 : static_cast<size_t>(nLength - nNewLength));
330 : }
331 :
332 1192450 : nLength = nNewLength;
333 1192450 : time(&mTime);
334 :
335 1192440 : return true;
336 : }
337 :
338 : /************************************************************************/
339 : /* ==================================================================== */
340 : /* VSIMemHandle */
341 : /* ==================================================================== */
342 : /************************************************************************/
343 :
344 : /************************************************************************/
345 : /* ~VSIMemHandle() */
346 : /************************************************************************/
347 :
348 391591 : VSIMemHandle::~VSIMemHandle()
349 : {
350 195792 : VSIMemHandle::Close();
351 391593 : }
352 :
353 : /************************************************************************/
354 : /* Close() */
355 : /************************************************************************/
356 :
357 398896 : int VSIMemHandle::Close()
358 :
359 : {
360 398896 : if (poFile)
361 : {
362 : #ifdef DEBUG_VERBOSE
363 : CPLDebug("VSIMEM", "Closing handle %p on %s: ref_count=%d (before)",
364 : this, poFile->osFilename.c_str(),
365 : static_cast<int>(poFile.use_count()));
366 : #endif
367 195797 : poFile = nullptr;
368 : }
369 :
370 398897 : return 0;
371 : }
372 :
373 : /************************************************************************/
374 : /* Seek() */
375 : /************************************************************************/
376 :
377 5302240 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
378 :
379 : {
380 : vsi_l_offset nLength;
381 : {
382 5302240 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
383 5302230 : nLength = poFile->nLength;
384 : }
385 :
386 5302210 : if (nWhence == SEEK_CUR)
387 : {
388 : if (nOffset > INT_MAX)
389 : {
390 : // printf("likely negative offset intended\n");
391 : }
392 391605 : m_nOffset += nOffset;
393 : }
394 4910610 : else if (nWhence == SEEK_SET)
395 : {
396 4680200 : m_nOffset = nOffset;
397 : }
398 230410 : else if (nWhence == SEEK_END)
399 : {
400 230409 : m_nOffset = nLength + nOffset;
401 : }
402 : else
403 : {
404 1 : errno = EINVAL;
405 1 : return -1;
406 : }
407 :
408 5302210 : bEOF = false;
409 :
410 5302210 : return 0;
411 : }
412 :
413 : /************************************************************************/
414 : /* Tell() */
415 : /************************************************************************/
416 :
417 3450950 : vsi_l_offset VSIMemHandle::Tell()
418 :
419 : {
420 3450950 : return m_nOffset;
421 : }
422 :
423 : /************************************************************************/
424 : /* Read() */
425 : /************************************************************************/
426 :
427 7833180 : size_t VSIMemHandle::Read(void *pBuffer, size_t nBytesToRead)
428 :
429 : {
430 7833180 : const vsi_l_offset nOffset = m_nOffset;
431 :
432 7833180 : if (nBytesToRead == 0)
433 1652 : return 0;
434 :
435 7831530 : if (!m_bReadAllowed)
436 : {
437 58 : m_bError = true;
438 58 : return 0;
439 : }
440 :
441 7831470 : bool bEOFTmp = bEOF;
442 7831470 : size_t nBytesRead = nBytesToRead;
443 :
444 : // Do not access/modify bEOF under the lock to avoid confusing Coverity
445 : // Scan since we access it in other methods outside of the lock.
446 : const auto DoUnderLock =
447 62636400 : [this, nOffset, pBuffer, &nBytesToRead, &nBytesRead, &bEOFTmp]
448 : {
449 15663600 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
450 :
451 7831820 : if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
452 : {
453 31980 : bEOFTmp = true;
454 31980 : return false;
455 : }
456 7799830 : if (nBytesToRead + nOffset > poFile->nLength)
457 : {
458 54909 : nBytesRead = static_cast<size_t>(poFile->nLength - nOffset);
459 54930 : bEOFTmp = true;
460 : }
461 :
462 7799840 : if (nBytesToRead)
463 7799820 : memcpy(pBuffer, poFile->pabyData + nOffset, nBytesRead);
464 7799840 : return true;
465 7831470 : };
466 :
467 7831470 : bool bRet = DoUnderLock();
468 7831820 : bEOF = bEOFTmp;
469 7831820 : if (!bRet)
470 31980 : return 0;
471 :
472 7799840 : m_nOffset += nBytesRead;
473 :
474 7799840 : return nBytesRead;
475 : }
476 :
477 : /************************************************************************/
478 : /* PRead() */
479 : /************************************************************************/
480 :
481 474 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
482 : vsi_l_offset nOffset) const
483 : {
484 948 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
485 :
486 474 : if (nOffset < poFile->nLength)
487 : {
488 : const size_t nToCopy = static_cast<size_t>(
489 1377 : std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
490 459 : static_cast<vsi_l_offset>(nSize)));
491 459 : memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
492 : nToCopy);
493 459 : return nToCopy;
494 : }
495 15 : return 0;
496 : }
497 :
498 : /************************************************************************/
499 : /* Write() */
500 : /************************************************************************/
501 :
502 1676470 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nBytesToWrite)
503 :
504 : {
505 1676470 : const vsi_l_offset nOffset = m_nOffset;
506 :
507 1676470 : if (!bUpdate)
508 : {
509 1 : errno = EACCES;
510 1 : return 0;
511 : }
512 :
513 : {
514 1676470 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
515 :
516 1676460 : if (nBytesToWrite + nOffset < nBytesToWrite)
517 : {
518 0 : return 0;
519 : }
520 :
521 1676460 : if (nBytesToWrite + nOffset > poFile->nLength)
522 : {
523 1190690 : if (!poFile->SetLength(nBytesToWrite + nOffset))
524 2514 : return 0;
525 : }
526 :
527 1673920 : if (nBytesToWrite)
528 1663920 : memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
529 :
530 1673900 : time(&poFile->mTime);
531 : }
532 :
533 1673950 : m_nOffset += nBytesToWrite;
534 :
535 1673950 : return nBytesToWrite;
536 : }
537 :
538 : /************************************************************************/
539 : /* ClearErr() */
540 : /************************************************************************/
541 :
542 47718 : void VSIMemHandle::ClearErr()
543 :
544 : {
545 47718 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
546 47718 : bEOF = false;
547 47718 : m_bError = false;
548 47718 : }
549 :
550 : /************************************************************************/
551 : /* Error() */
552 : /************************************************************************/
553 :
554 50490 : int VSIMemHandle::Error()
555 :
556 : {
557 50490 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
558 100980 : return m_bError ? TRUE : FALSE;
559 : }
560 :
561 : /************************************************************************/
562 : /* Eof() */
563 : /************************************************************************/
564 :
565 149708 : int VSIMemHandle::Eof()
566 :
567 : {
568 149708 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
569 299416 : return bEOF ? TRUE : FALSE;
570 : }
571 :
572 : /************************************************************************/
573 : /* Truncate() */
574 : /************************************************************************/
575 :
576 813 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
577 : {
578 813 : if (!bUpdate)
579 : {
580 1 : errno = EACCES;
581 1 : return -1;
582 : }
583 :
584 1624 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
585 812 : if (poFile->SetLength(nNewSize))
586 792 : return 0;
587 :
588 20 : return -1;
589 : }
590 :
591 : /************************************************************************/
592 : /* ==================================================================== */
593 : /* VSIMemFilesystemHandler */
594 : /* ==================================================================== */
595 : /************************************************************************/
596 :
597 : /************************************************************************/
598 : /* ~VSIMemFilesystemHandler() */
599 : /************************************************************************/
600 :
601 1128 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
602 :
603 : {
604 1127 : oFileList.clear();
605 :
606 1127 : if (hMutex != nullptr)
607 208 : CPLDestroyMutex(hMutex);
608 1127 : hMutex = nullptr;
609 1128 : }
610 :
611 : /************************************************************************/
612 : /* Open() */
613 : /************************************************************************/
614 :
615 : VSIVirtualHandleUniquePtr
616 273747 : VSIMemFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
617 : bool bSetError, CSLConstList /* papszOptions */)
618 :
619 : {
620 547496 : CPLMutexHolder oHolder(&hMutex);
621 821247 : const CPLString osFilename = NormalizePath(pszFilename);
622 273749 : if (osFilename.empty())
623 0 : return nullptr;
624 :
625 273749 : vsi_l_offset nMaxLength = GUINTBIG_MAX;
626 273749 : const size_t iPos = osFilename.find("||maxlength=");
627 273749 : if (iPos != std::string::npos)
628 : {
629 3009 : nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
630 6018 : osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
631 : }
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Get the filename we are opening, create if needed. */
635 : /* -------------------------------------------------------------------- */
636 547498 : std::shared_ptr<VSIMemFile> poFile = nullptr;
637 273749 : const auto oIter = oFileList.find(osFilename);
638 273749 : if (oIter != oFileList.end())
639 : {
640 98549 : poFile = oIter->second;
641 : }
642 :
643 : // If no file and opening in read, error out.
644 731364 : if (strstr(pszAccess, "w") == nullptr &&
645 273749 : strstr(pszAccess, "a") == nullptr && poFile == nullptr)
646 : {
647 88750 : if (bSetError)
648 : {
649 15387 : VSIError(VSIE_FileError, "No such file or directory");
650 : }
651 88750 : errno = ENOENT;
652 88750 : return nullptr;
653 : }
654 :
655 : // Create.
656 184999 : if (poFile == nullptr)
657 : {
658 86450 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
659 86450 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
660 : {
661 1 : if (bSetError)
662 : {
663 0 : VSIError(VSIE_FileError,
664 : "Could not create directory %s for writing",
665 : osFileDir.c_str());
666 : }
667 1 : errno = ENOENT;
668 1 : return nullptr;
669 : }
670 :
671 86449 : poFile = std::make_shared<VSIMemFile>();
672 86449 : poFile->osFilename = osFilename;
673 86449 : oFileList[poFile->osFilename] = poFile;
674 : #ifdef DEBUG_VERBOSE
675 : CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
676 : static_cast<int>(poFile.use_count()));
677 : #endif
678 86449 : poFile->nMaxLength = nMaxLength;
679 : }
680 : // Overwrite
681 98549 : else if (strstr(pszAccess, "w"))
682 : {
683 3471 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
684 3471 : poFile->SetLength(0);
685 3471 : poFile->nMaxLength = nMaxLength;
686 : }
687 :
688 184998 : if (poFile->bIsDirectory)
689 : {
690 1641 : errno = EISDIR;
691 1641 : return nullptr;
692 : }
693 :
694 : /* -------------------------------------------------------------------- */
695 : /* Setup the file handle on this file. */
696 : /* -------------------------------------------------------------------- */
697 366714 : auto poHandle = std::make_unique<VSIMemHandle>();
698 :
699 183357 : poHandle->poFile = poFile;
700 183357 : poHandle->m_nOffset = 0;
701 183357 : poHandle->bEOF = false;
702 252849 : poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
703 69492 : strchr(pszAccess, 'a');
704 183357 : poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
705 :
706 : #ifdef DEBUG_VERBOSE
707 : CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle.get(),
708 : pszFilename, static_cast<int>(poFile.use_count()));
709 : #endif
710 183357 : if (strchr(pszAccess, 'a'))
711 : {
712 : vsi_l_offset nOffset;
713 : {
714 42 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
715 42 : nOffset = poFile->nLength;
716 : }
717 42 : poHandle->m_nOffset = nOffset;
718 : }
719 :
720 183357 : return VSIVirtualHandleUniquePtr(poHandle.release());
721 : }
722 :
723 : /************************************************************************/
724 : /* Stat() */
725 : /************************************************************************/
726 :
727 582992 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
728 : VSIStatBufL *pStatBuf, int /* nFlags */)
729 :
730 : {
731 1165980 : CPLMutexHolder oHolder(&hMutex);
732 :
733 1748980 : const CPLString osFilename = NormalizePath(pszFilename);
734 :
735 582992 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
736 :
737 582992 : if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
738 : {
739 56843 : pStatBuf->st_size = 0;
740 56843 : pStatBuf->st_mode = S_IFDIR;
741 56843 : return 0;
742 : }
743 :
744 526149 : auto oIter = oFileList.find(osFilename);
745 526149 : if (oIter == oFileList.end())
746 : {
747 444476 : errno = ENOENT;
748 444476 : return -1;
749 : }
750 :
751 163346 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
752 :
753 81673 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
754 :
755 81673 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
756 81673 : if (poFile->bIsDirectory)
757 : {
758 45930 : pStatBuf->st_size = 0;
759 45930 : pStatBuf->st_mode = S_IFDIR;
760 : }
761 : else
762 : {
763 35743 : pStatBuf->st_size = poFile->nLength;
764 35743 : pStatBuf->st_mode = S_IFREG;
765 35743 : if (const char *mtime =
766 35743 : CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
767 : {
768 32 : pStatBuf->st_mtime =
769 32 : static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
770 : }
771 : else
772 : {
773 35711 : pStatBuf->st_mtime = poFile->mTime;
774 : }
775 : }
776 :
777 81673 : return 0;
778 : }
779 :
780 : /************************************************************************/
781 : /* Unlink() */
782 : /************************************************************************/
783 :
784 81048 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
785 :
786 : {
787 162096 : CPLMutexHolder oHolder(&hMutex);
788 162096 : return Unlink_unlocked(pszFilename);
789 : }
790 :
791 : /************************************************************************/
792 : /* Unlink_unlocked() */
793 : /************************************************************************/
794 :
795 93009 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
796 :
797 : {
798 279027 : const CPLString osFilename = NormalizePath(pszFilename);
799 :
800 93009 : auto oIter = oFileList.find(osFilename);
801 93009 : if (oIter == oFileList.end())
802 : {
803 17760 : errno = ENOENT;
804 17760 : return -1;
805 : }
806 :
807 : #ifdef DEBUG_VERBOSE
808 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
809 : CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
810 : static_cast<int>(poFile.use_count()));
811 : #endif
812 75249 : oFileList.erase(oIter);
813 :
814 75249 : return 0;
815 : }
816 :
817 : /************************************************************************/
818 : /* Mkdir() */
819 : /************************************************************************/
820 :
821 100847 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
822 :
823 : {
824 201694 : CPLMutexHolder oHolder(&hMutex);
825 :
826 302541 : const CPLString osPathname = NormalizePath(pszPathname);
827 100847 : if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
828 : {
829 90003 : if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
830 44841 : return 0;
831 : // "/vsimem/.#!HIDDEN!#./{unique_counter}"
832 45162 : else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
833 : std::string::npos)
834 44841 : return 0;
835 :
836 : // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
837 : // accept creating an explicit directory
838 : }
839 :
840 11165 : if (oFileList.find(osPathname) != oFileList.end())
841 : {
842 785 : errno = EEXIST;
843 785 : return -1;
844 : }
845 :
846 10380 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
847 10380 : poFile->osFilename = osPathname;
848 10380 : poFile->bIsDirectory = true;
849 10380 : oFileList[osPathname] = poFile;
850 : #ifdef DEBUG_VERBOSE
851 : CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
852 : static_cast<int>(poFile.use_count()));
853 : #endif
854 10380 : CPL_IGNORE_RET_VAL(poFile);
855 10380 : return 0;
856 : }
857 :
858 : /************************************************************************/
859 : /* Rmdir() */
860 : /************************************************************************/
861 :
862 81 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
863 :
864 : {
865 81 : return Unlink(pszPathname);
866 : }
867 :
868 : /************************************************************************/
869 : /* RmdirRecursive() */
870 : /************************************************************************/
871 :
872 5506 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
873 : {
874 11012 : CPLMutexHolder oHolder(&hMutex);
875 :
876 11012 : const CPLString osPath = NormalizePath(pszDirname);
877 5506 : const size_t nPathLen = osPath.size();
878 5506 : int ret = 0;
879 5506 : if (osPath == "/vsimem")
880 : {
881 : // Clean-up all files under pszDirname, except hidden directories
882 : // if called from "/vsimem"
883 8 : for (auto iter = oFileList.begin(); iter != oFileList.end();
884 : /* no automatic increment */)
885 : {
886 6 : const char *pszFilePath = iter->second->osFilename.c_str();
887 6 : const size_t nFileLen = iter->second->osFilename.size();
888 12 : if (nFileLen > nPathLen &&
889 6 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
890 18 : pszFilePath[nPathLen] == '/' &&
891 6 : !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
892 : {
893 2 : iter = oFileList.erase(iter);
894 : }
895 : else
896 : {
897 4 : ++iter;
898 : }
899 : }
900 : }
901 : else
902 : {
903 5504 : ret = -1;
904 935272 : for (auto iter = oFileList.begin(); iter != oFileList.end();
905 : /* no automatic increment */)
906 : {
907 929768 : const char *pszFilePath = iter->second->osFilename.c_str();
908 929768 : const size_t nFileLen = iter->second->osFilename.size();
909 1185780 : if (nFileLen >= nPathLen &&
910 949122 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
911 19354 : (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
912 : {
913 : // If VSIRmdirRecursive() is used correctly, it should at
914 : // least delete the directory on which it has been called
915 22730 : ret = 0;
916 22730 : iter = oFileList.erase(iter);
917 : }
918 : else
919 : {
920 907038 : ++iter;
921 : }
922 : }
923 :
924 : // Make sure that it always succeed on the root hidden directory
925 5504 : if (osPath == szHIDDEN_DIRNAME)
926 3 : ret = 0;
927 : }
928 11012 : return ret;
929 : }
930 :
931 : /************************************************************************/
932 : /* ReadDirEx() */
933 : /************************************************************************/
934 :
935 33724 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
936 :
937 : {
938 67448 : CPLMutexHolder oHolder(&hMutex);
939 :
940 67448 : const CPLString osPath = NormalizePath(pszPath);
941 :
942 33724 : char **papszDir = nullptr;
943 33724 : const size_t nPathLen = osPath.size();
944 :
945 : // In case of really big number of files in the directory, CSLAddString
946 : // can be slow (see #2158). We then directly build the list.
947 33724 : int nItems = 0;
948 33724 : int nAllocatedItems = 0;
949 :
950 33724 : if (osPath == szHIDDEN_DIRNAME)
951 : {
952 : // Special mode for hidden filenames.
953 : // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
954 : // created so they do not appear in oFileList, but their subcontent
955 : // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
956 20 : std::set<std::string> oSetSubDirs;
957 161 : for (const auto &iter : oFileList)
958 : {
959 151 : const char *pszFilePath = iter.second->osFilename.c_str();
960 288 : if (iter.second->osFilename.size() > nPathLen &&
961 137 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
962 : {
963 13 : char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
964 13 : char *pszSlash = strchr(pszItem, '/');
965 13 : if (pszSlash)
966 13 : *pszSlash = 0;
967 13 : if (cpl::contains(oSetSubDirs, pszItem))
968 : {
969 2 : CPLFree(pszItem);
970 2 : continue;
971 : }
972 11 : oSetSubDirs.insert(pszItem);
973 :
974 11 : if (nItems == 0)
975 : {
976 : papszDir =
977 6 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
978 6 : nAllocatedItems = 1;
979 : }
980 5 : else if (nItems >= nAllocatedItems)
981 : {
982 5 : nAllocatedItems = nAllocatedItems * 2;
983 5 : papszDir = static_cast<char **>(CPLRealloc(
984 5 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
985 : }
986 :
987 11 : papszDir[nItems] = pszItem;
988 11 : papszDir[nItems + 1] = nullptr;
989 :
990 11 : nItems++;
991 11 : if (nMaxFiles > 0 && nItems > nMaxFiles)
992 0 : break;
993 : }
994 : }
995 : }
996 : else
997 : {
998 11702200 : for (const auto &iter : oFileList)
999 : {
1000 11668500 : const char *pszFilePath = iter.second->osFilename.c_str();
1001 11668500 : if (iter.second->osFilename.size() > nPathLen &&
1002 7336320 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
1003 22550700 : pszFilePath[nPathLen] == '/' &&
1004 3545960 : strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
1005 : {
1006 353295 : if (nItems == 0)
1007 : {
1008 : papszDir =
1009 32021 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
1010 32021 : nAllocatedItems = 1;
1011 : }
1012 321274 : else if (nItems >= nAllocatedItems)
1013 : {
1014 48035 : nAllocatedItems = nAllocatedItems * 2;
1015 48035 : papszDir = static_cast<char **>(CPLRealloc(
1016 48035 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1017 : }
1018 :
1019 353295 : papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
1020 353295 : papszDir[nItems + 1] = nullptr;
1021 :
1022 353295 : nItems++;
1023 353295 : if (nMaxFiles > 0 && nItems > nMaxFiles)
1024 1 : break;
1025 : }
1026 : }
1027 : }
1028 :
1029 67448 : return papszDir;
1030 : }
1031 :
1032 : /************************************************************************/
1033 : /* Rename() */
1034 : /************************************************************************/
1035 :
1036 561 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
1037 : const char *pszNewPath, GDALProgressFunc,
1038 : void *)
1039 :
1040 : {
1041 1122 : CPLMutexHolder oHolder(&hMutex);
1042 :
1043 1683 : const std::string osOldPath = NormalizePath(pszOldPath);
1044 1683 : const std::string osNewPath = NormalizePath(pszNewPath);
1045 561 : if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
1046 3 : return -1;
1047 :
1048 558 : if (osOldPath.compare(osNewPath) == 0)
1049 304 : return 0;
1050 :
1051 254 : if (oFileList.find(osOldPath) == oFileList.end())
1052 : {
1053 0 : errno = ENOENT;
1054 0 : return -1;
1055 : }
1056 :
1057 : std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
1058 254 : oFileList.find(osOldPath);
1059 667 : while (it != oFileList.end() && it->first.find(osOldPath) == 0)
1060 : {
1061 826 : const std::string osRemainder = it->first.substr(osOldPath.size());
1062 413 : if (osRemainder.empty() || osRemainder[0] == '/')
1063 : {
1064 604 : const std::string osNewFullPath = osNewPath + osRemainder;
1065 302 : Unlink_unlocked(osNewFullPath.c_str());
1066 302 : oFileList[osNewFullPath] = it->second;
1067 302 : it->second->osFilename = osNewFullPath;
1068 302 : oFileList.erase(it++);
1069 : }
1070 : else
1071 : {
1072 111 : ++it;
1073 : }
1074 : }
1075 :
1076 254 : return 0;
1077 : }
1078 :
1079 : /************************************************************************/
1080 : /* NormalizePath() */
1081 : /************************************************************************/
1082 :
1083 1225600 : CPLString VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1084 : {
1085 1225600 : CPLString s(in);
1086 1225580 : std::replace(s.begin(), s.end(), '\\', '/');
1087 1225600 : s.replaceAll("//", '/');
1088 1225580 : if (!s.empty() && s.back() == '/')
1089 1743 : s.pop_back();
1090 1225580 : return s;
1091 : }
1092 :
1093 : /************************************************************************/
1094 : /* GetDiskFreeSpace() */
1095 : /************************************************************************/
1096 :
1097 72 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
1098 : {
1099 72 : const GIntBig nRet = CPLGetUsablePhysicalRAM();
1100 72 : if (nRet <= 0)
1101 0 : return -1;
1102 72 : return nRet;
1103 : }
1104 :
1105 : //! @endcond
1106 :
1107 : /************************************************************************/
1108 : /* VSIInstallMemFileHandler() */
1109 : /************************************************************************/
1110 :
1111 : /**
1112 : * \brief Install "memory" file system handler.
1113 : *
1114 : * A special file handler is installed that allows block of memory to be
1115 : * treated as files. All portions of the file system underneath the base
1116 : * path "/vsimem/" will be handled by this driver.
1117 : *
1118 : * Normal VSI*L functions can be used freely to create and destroy memory
1119 : * arrays treating them as if they were real file system objects. Some
1120 : * additional methods exist to efficient create memory file system objects
1121 : * without duplicating original copies of the data or to "steal" the block
1122 : * of memory associated with a memory file.
1123 : *
1124 : * Directory related functions are supported.
1125 : *
1126 : * This code example demonstrates using GDAL to translate from one memory
1127 : * buffer to another.
1128 : *
1129 : * \code
1130 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
1131 : * vsi_l_offset *pnOutDataLength )
1132 : * {
1133 : * // create memory file system object from buffer.
1134 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
1135 : * nInDataLength, FALSE ) );
1136 : *
1137 : * // Open memory buffer for read.
1138 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
1139 : *
1140 : * // Get output format driver.
1141 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1142 : * GDALDatasetH hOutDS;
1143 : *
1144 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
1145 : * NULL, NULL );
1146 : *
1147 : * // close source file, and "unlink" it.
1148 : * GDALClose( hDS );
1149 : * VSIUnlink( "/vsimem/work.dat" );
1150 : *
1151 : * // seize the buffer associated with the output file.
1152 : *
1153 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
1154 : * }
1155 : * \endcode
1156 : */
1157 :
1158 1789 : void VSIInstallMemFileHandler()
1159 : {
1160 1789 : VSIFileManager::InstallHandler(
1161 3578 : "/vsimem/", std::make_shared<VSIMemFilesystemHandler>("/vsimem/"));
1162 1789 : }
1163 :
1164 : /************************************************************************/
1165 : /* VSIFileFromMemBuffer() */
1166 : /************************************************************************/
1167 :
1168 : /**
1169 : * \brief Create memory "file" from a buffer.
1170 : *
1171 : * A virtual memory file is created from the passed buffer with the indicated
1172 : * filename. Under normal conditions the filename would need to be absolute
1173 : * and within the /vsimem/ portion of the filesystem.
1174 : * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
1175 : * an anonymous file, that is destroyed when the handle is closed.
1176 : *
1177 : * If bTakeOwnership is TRUE, then the memory file system handler will take
1178 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
1179 : * it remains the responsibility of the caller, but should not be freed as
1180 : * long as it might be accessed as a file. In no circumstances does this
1181 : * function take a copy of the pabyData contents.
1182 : *
1183 : * @param pszFilename the filename to be created, or nullptr
1184 : * @param pabyData the data buffer for the file.
1185 : * @param nDataLength the length of buffer in bytes.
1186 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
1187 : *
1188 : * @return open file handle on created file (see VSIFOpenL()).
1189 : */
1190 :
1191 12449 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
1192 : vsi_l_offset nDataLength, int bTakeOwnership)
1193 :
1194 : {
1195 12449 : if (VSIFileManager::GetHandler("") ==
1196 12447 : VSIFileManager::GetHandler("/vsimem/"))
1197 0 : VSIInstallMemFileHandler();
1198 :
1199 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1200 12448 : VSIFileManager::GetHandler("/vsimem/"));
1201 :
1202 : const CPLString osFilename =
1203 48214 : pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
1204 24895 : : std::string();
1205 12447 : if (osFilename == "/vsimem/")
1206 : {
1207 1 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1208 : pszFilename);
1209 1 : return nullptr;
1210 : }
1211 :
1212 : // Try to create the parent directory, if needed, before taking
1213 : // ownership of pabyData.
1214 12441 : if (!osFilename.empty())
1215 : {
1216 11653 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
1217 11653 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
1218 : {
1219 0 : VSIError(VSIE_FileError,
1220 : "Could not create directory %s for writing",
1221 : osFileDir.c_str());
1222 0 : errno = ENOENT;
1223 0 : return nullptr;
1224 : }
1225 : }
1226 :
1227 12451 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
1228 :
1229 12448 : poFile->osFilename = osFilename;
1230 12448 : poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
1231 12448 : poFile->pabyData = pabyData;
1232 12448 : poFile->nLength = nDataLength;
1233 12448 : poFile->nAllocLength = nDataLength;
1234 :
1235 12448 : if (!osFilename.empty())
1236 : {
1237 23318 : CPLMutexHolder oHolder(&poHandler->hMutex);
1238 11659 : poHandler->Unlink_unlocked(osFilename);
1239 11659 : poHandler->oFileList[poFile->osFilename] = poFile;
1240 : #ifdef DEBUG_VERBOSE
1241 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
1242 : poFile->osFilename.c_str(),
1243 : static_cast<int>(poFile.use_count()));
1244 : #endif
1245 : }
1246 :
1247 : /* -------------------------------------------------------------------- */
1248 : /* Setup the file handle on this file. */
1249 : /* -------------------------------------------------------------------- */
1250 12448 : VSIMemHandle *poHandle = new VSIMemHandle;
1251 :
1252 12448 : poHandle->poFile = std::move(poFile);
1253 12448 : poHandle->bUpdate = true;
1254 12448 : poHandle->m_bReadAllowed = true;
1255 12448 : return poHandle;
1256 : }
1257 :
1258 : /************************************************************************/
1259 : /* VSIGetMemFileBuffer() */
1260 : /************************************************************************/
1261 :
1262 : /**
1263 : * \brief Fetch buffer underlying memory file.
1264 : *
1265 : * This function returns a pointer to the memory buffer underlying a
1266 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
1267 : * object will be deleted, and ownership of the buffer will pass to the
1268 : * caller, otherwise the underlying file will remain in existence.
1269 : * bUnlinkAndSeize should only be used for files that own their data
1270 : * (see the bTakeOwnership parameter of VSIFileFromMemBuffer).
1271 : *
1272 : * @param pszFilename the name of the file to grab the buffer of.
1273 : * @param pnDataLength (file) length returned in this variable.
1274 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
1275 : *
1276 : * @return pointer to memory buffer or NULL on failure.
1277 : */
1278 :
1279 36378 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
1280 : int bUnlinkAndSeize)
1281 :
1282 : {
1283 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1284 36378 : VSIFileManager::GetHandler("/vsimem/"));
1285 :
1286 36380 : if (pszFilename == nullptr)
1287 0 : return nullptr;
1288 :
1289 : const std::string osFilename =
1290 109134 : VSIMemFilesystemHandler::NormalizePath(pszFilename);
1291 :
1292 72755 : CPLMutexHolder oHolder(&poHandler->hMutex);
1293 :
1294 36380 : if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
1295 139 : return nullptr;
1296 :
1297 36241 : std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
1298 36241 : GByte *pabyData = poFile->pabyData;
1299 36241 : if (pnDataLength != nullptr)
1300 33525 : *pnDataLength = poFile->nLength;
1301 :
1302 36241 : if (bUnlinkAndSeize)
1303 : {
1304 4430 : if (!poFile->bOwnData)
1305 0 : CPLDebug("VSIMemFile",
1306 : "File doesn't own data in VSIGetMemFileBuffer!");
1307 : else
1308 4430 : poFile->bOwnData = false;
1309 :
1310 4430 : poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
1311 : #ifdef DEBUG_VERBOSE
1312 : CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
1313 : poFile->osFilename.c_str(),
1314 : static_cast<int>(poFile.use_count()));
1315 : #endif
1316 4430 : poFile->pabyData = nullptr;
1317 4430 : poFile->nLength = 0;
1318 4430 : poFile->nAllocLength = 0;
1319 : }
1320 :
1321 36241 : return pabyData;
1322 : }
1323 :
1324 : /************************************************************************/
1325 : /* VSIMemGenerateHiddenFilename() */
1326 : /************************************************************************/
1327 :
1328 : /**
1329 : * \brief Generates a unique filename that can be used with the /vsimem/
1330 : * virtual file system.
1331 : *
1332 : * This function returns a (short-lived) string containing a unique filename,
1333 : * (using an atomic counter), designed for temporary files that must remain
1334 : * invisible for other users working at the "/vsimem/" top-level, i.e.
1335 : * such files are not returned by VSIReadDir("/vsimem/") or
1336 : * VSIReadDirRecursive("/vsimem/)".
1337 : *
1338 : * The function does not create the file per se. Such filename can be used to
1339 : * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
1340 : * a directory with VSIMkdir()
1341 : *
1342 : * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
1343 : * etc. is of the responsibility of the user. The user should not attempt to
1344 : * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
1345 : * on the returned filename, and work only with files at the same level or
1346 : * in subdirectories of what is returned by this function.
1347 : *
1348 : * @param pszFilename the filename to be appended at the end of the returned
1349 : * filename. If not specified, defaults to "unnamed".
1350 : *
1351 : * @return pointer to a short-lived string (rotating buffer of strings in
1352 : * thread-local storage). It is recommended to use CPLStrdup() or std::string()
1353 : * immediately on it.
1354 : *
1355 : * @since GDAL 3.10
1356 : */
1357 44099 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
1358 : {
1359 : static std::atomic<uint32_t> nCounter{0};
1360 44099 : return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
1361 44080 : pszFilename ? pszFilename : "unnamed");
1362 : }
|