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 192153 : 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 nSize, size_t nMemb) override;
165 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) 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 723 : bool HasPRead() const override
173 : {
174 723 : 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 1785 : explicit VSIMemFilesystemHandler(const char *pszPrefix)
197 1785 : : m_osPrefix(pszPrefix)
198 : {
199 1785 : }
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 104357 : VSIMemFile::VSIMemFile()
238 : {
239 104357 : time(&mTime);
240 104357 : }
241 :
242 : /************************************************************************/
243 : /* ~VSIMemFile() */
244 : /************************************************************************/
245 :
246 101662 : VSIMemFile::~VSIMemFile()
247 : {
248 101662 : if (bOwnData && pabyData)
249 82711 : CPLFree(pabyData);
250 101662 : }
251 :
252 : /************************************************************************/
253 : /* SetLength() */
254 : /************************************************************************/
255 :
256 : // Must be called under exclusive lock
257 1178440 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
258 :
259 : {
260 1178440 : 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 1175630 : 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 140385 : 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 140384 : const vsi_l_offset nNewAlloc =
287 85070 : (nAllocLength == 0 && nNewLength >= 1024 * 1024)
288 140473 : ? nNewLength
289 140295 : : nNewLength + nNewLength / 10 + 5000;
290 140384 : GByte *pabyNewData = nullptr;
291 : if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
292 : nNewAlloc)
293 : {
294 280768 : pabyNewData = static_cast<GByte *>(
295 140384 : nAllocLength == 0
296 85070 : ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
297 55314 : : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
298 : }
299 140384 : 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 140383 : 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 55314 : memset(pabyNewData + nAllocLength, 0,
314 55314 : static_cast<size_t>(nNewAlloc - nAllocLength));
315 : }
316 :
317 140383 : pabyData = pabyNewData;
318 140383 : nAllocLength = nNewAlloc;
319 : }
320 1035240 : else if (nNewLength < nLength)
321 : {
322 3429 : memset(pabyData + nNewLength, 0,
323 3429 : static_cast<size_t>(nLength - nNewLength));
324 : }
325 :
326 1175630 : nLength = nNewLength;
327 1175630 : time(&mTime);
328 :
329 1175620 : return true;
330 : }
331 :
332 : /************************************************************************/
333 : /* ==================================================================== */
334 : /* VSIMemHandle */
335 : /* ==================================================================== */
336 : /************************************************************************/
337 :
338 : /************************************************************************/
339 : /* ~VSIMemHandle() */
340 : /************************************************************************/
341 :
342 384287 : VSIMemHandle::~VSIMemHandle()
343 : {
344 192143 : VSIMemHandle::Close();
345 384287 : }
346 :
347 : /************************************************************************/
348 : /* Close() */
349 : /************************************************************************/
350 :
351 389599 : int VSIMemHandle::Close()
352 :
353 : {
354 389599 : 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 192142 : poFile = nullptr;
362 : }
363 :
364 389594 : return 0;
365 : }
366 :
367 : /************************************************************************/
368 : /* Seek() */
369 : /************************************************************************/
370 :
371 5219880 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
372 :
373 : {
374 : vsi_l_offset nLength;
375 : {
376 5219880 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
377 5219900 : nLength = poFile->nLength;
378 : }
379 :
380 5219890 : if (nWhence == SEEK_CUR)
381 : {
382 : if (nOffset > INT_MAX)
383 : {
384 : // printf("likely negative offset intended\n");
385 : }
386 393210 : m_nOffset += nOffset;
387 : }
388 4826680 : else if (nWhence == SEEK_SET)
389 : {
390 4604900 : m_nOffset = nOffset;
391 : }
392 221776 : else if (nWhence == SEEK_END)
393 : {
394 221777 : m_nOffset = nLength + nOffset;
395 : }
396 : else
397 : {
398 0 : errno = EINVAL;
399 0 : return -1;
400 : }
401 :
402 5219890 : bEOF = false;
403 :
404 5219890 : return 0;
405 : }
406 :
407 : /************************************************************************/
408 : /* Tell() */
409 : /************************************************************************/
410 :
411 3404370 : vsi_l_offset VSIMemHandle::Tell()
412 :
413 : {
414 3404370 : return m_nOffset;
415 : }
416 :
417 : /************************************************************************/
418 : /* Read() */
419 : /************************************************************************/
420 :
421 7785870 : size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
422 :
423 : {
424 7785870 : const vsi_l_offset nOffset = m_nOffset;
425 :
426 7785870 : size_t nBytesToRead = nSize * nCount;
427 7785870 : if (nBytesToRead == 0)
428 1668 : return 0;
429 :
430 7784200 : if (nCount > 0 && nBytesToRead / nCount != nSize)
431 : {
432 0 : bEOF = true;
433 0 : return 0;
434 : }
435 :
436 7784200 : if (!m_bReadAllowed)
437 : {
438 58 : m_bError = true;
439 58 : return 0;
440 : }
441 :
442 7784150 : bool bEOFTmp = bEOF;
443 : // Do not access/modify bEOF under the lock to avoid confusing Coverity
444 : // Scan since we access it in other methods outside of the lock.
445 : const auto DoUnderLock =
446 62251700 : [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
447 : {
448 15568300 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
449 :
450 7784120 : if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
451 : {
452 31645 : bEOFTmp = true;
453 31645 : return false;
454 : }
455 7752490 : if (nBytesToRead + nOffset > poFile->nLength)
456 : {
457 52589 : nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
458 52672 : nCount = nBytesToRead / nSize;
459 52672 : bEOFTmp = true;
460 : }
461 :
462 7752540 : if (nBytesToRead)
463 7752460 : memcpy(pBuffer, poFile->pabyData + nOffset,
464 : static_cast<size_t>(nBytesToRead));
465 7752510 : return true;
466 7784150 : };
467 :
468 7784150 : bool bRet = DoUnderLock();
469 7784220 : bEOF = bEOFTmp;
470 7784220 : if (!bRet)
471 31644 : return 0;
472 :
473 7752570 : m_nOffset += nBytesToRead;
474 :
475 7752570 : return nCount;
476 : }
477 :
478 : /************************************************************************/
479 : /* PRead() */
480 : /************************************************************************/
481 :
482 437 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
483 : vsi_l_offset nOffset) const
484 : {
485 874 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
486 :
487 437 : if (nOffset < poFile->nLength)
488 : {
489 : const size_t nToCopy = static_cast<size_t>(
490 1266 : std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
491 422 : static_cast<vsi_l_offset>(nSize)));
492 422 : memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
493 : nToCopy);
494 422 : return nToCopy;
495 : }
496 15 : return 0;
497 : }
498 :
499 : /************************************************************************/
500 : /* Write() */
501 : /************************************************************************/
502 :
503 1657970 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
504 :
505 : {
506 1657970 : const vsi_l_offset nOffset = m_nOffset;
507 :
508 1657970 : if (!bUpdate)
509 : {
510 1 : errno = EACCES;
511 1 : return 0;
512 : }
513 :
514 1657970 : const size_t nBytesToWrite = nSize * nCount;
515 :
516 : {
517 1657970 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
518 :
519 1657970 : if (nCount > 0 && nBytesToWrite / nCount != nSize)
520 : {
521 0 : return 0;
522 : }
523 1657970 : if (nBytesToWrite + nOffset < nBytesToWrite)
524 : {
525 0 : return 0;
526 : }
527 :
528 1657970 : if (nBytesToWrite + nOffset > poFile->nLength)
529 : {
530 1174350 : if (!poFile->SetLength(nBytesToWrite + nOffset))
531 2789 : return 0;
532 : }
533 :
534 1655190 : if (nBytesToWrite)
535 1645170 : memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
536 :
537 1655190 : time(&poFile->mTime);
538 : }
539 :
540 1655180 : m_nOffset += nBytesToWrite;
541 :
542 1655180 : return nCount;
543 : }
544 :
545 : /************************************************************************/
546 : /* ClearErr() */
547 : /************************************************************************/
548 :
549 25805 : void VSIMemHandle::ClearErr()
550 :
551 : {
552 25805 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
553 25805 : bEOF = false;
554 25805 : m_bError = false;
555 25805 : }
556 :
557 : /************************************************************************/
558 : /* Error() */
559 : /************************************************************************/
560 :
561 29071 : int VSIMemHandle::Error()
562 :
563 : {
564 29071 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
565 58142 : return m_bError ? TRUE : FALSE;
566 : }
567 :
568 : /************************************************************************/
569 : /* Eof() */
570 : /************************************************************************/
571 :
572 134258 : int VSIMemHandle::Eof()
573 :
574 : {
575 134258 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
576 268516 : return bEOF ? TRUE : FALSE;
577 : }
578 :
579 : /************************************************************************/
580 : /* Truncate() */
581 : /************************************************************************/
582 :
583 758 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
584 : {
585 758 : if (!bUpdate)
586 : {
587 1 : errno = EACCES;
588 1 : return -1;
589 : }
590 :
591 1514 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
592 757 : if (poFile->SetLength(nNewSize))
593 737 : return 0;
594 :
595 20 : return -1;
596 : }
597 :
598 : /************************************************************************/
599 : /* ==================================================================== */
600 : /* VSIMemFilesystemHandler */
601 : /* ==================================================================== */
602 : /************************************************************************/
603 :
604 : /************************************************************************/
605 : /* ~VSIMemFilesystemHandler() */
606 : /************************************************************************/
607 :
608 2248 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
609 :
610 : {
611 1124 : oFileList.clear();
612 :
613 1124 : if (hMutex != nullptr)
614 205 : CPLDestroyMutex(hMutex);
615 1124 : hMutex = nullptr;
616 2248 : }
617 :
618 : /************************************************************************/
619 : /* Open() */
620 : /************************************************************************/
621 :
622 : VSIVirtualHandleUniquePtr
623 269291 : VSIMemFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
624 : bool bSetError, CSLConstList /* papszOptions */)
625 :
626 : {
627 538592 : CPLMutexHolder oHolder(&hMutex);
628 807903 : const CPLString osFilename = NormalizePath(pszFilename);
629 269301 : if (osFilename.empty())
630 0 : return nullptr;
631 :
632 269301 : vsi_l_offset nMaxLength = GUINTBIG_MAX;
633 269301 : const size_t iPos = osFilename.find("||maxlength=");
634 269301 : if (iPos != std::string::npos)
635 : {
636 3026 : nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
637 6052 : osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
638 : }
639 :
640 : /* -------------------------------------------------------------------- */
641 : /* Get the filename we are opening, create if needed. */
642 : /* -------------------------------------------------------------------- */
643 538602 : std::shared_ptr<VSIMemFile> poFile = nullptr;
644 269301 : const auto oIter = oFileList.find(osFilename);
645 269301 : if (oIter != oFileList.end())
646 : {
647 96850 : poFile = oIter->second;
648 : }
649 :
650 : // If no file and opening in read, error out.
651 719048 : if (strstr(pszAccess, "w") == nullptr &&
652 269301 : strstr(pszAccess, "a") == nullptr && poFile == nullptr)
653 : {
654 86879 : if (bSetError)
655 : {
656 15273 : VSIError(VSIE_FileError, "No such file or directory");
657 : }
658 86879 : errno = ENOENT;
659 86879 : return nullptr;
660 : }
661 :
662 : // Create.
663 182422 : if (poFile == nullptr)
664 : {
665 85572 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
666 85572 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
667 : {
668 1 : if (bSetError)
669 : {
670 0 : VSIError(VSIE_FileError,
671 : "Could not create directory %s for writing",
672 : osFileDir.c_str());
673 : }
674 1 : errno = ENOENT;
675 1 : return nullptr;
676 : }
677 :
678 85571 : poFile = std::make_shared<VSIMemFile>();
679 85571 : poFile->osFilename = osFilename;
680 85571 : oFileList[poFile->osFilename] = poFile;
681 : #ifdef DEBUG_VERBOSE
682 : CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
683 : static_cast<int>(poFile.use_count()));
684 : #endif
685 85571 : poFile->nMaxLength = nMaxLength;
686 : }
687 : // Overwrite
688 96850 : else if (strstr(pszAccess, "w"))
689 : {
690 3321 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
691 3321 : poFile->SetLength(0);
692 3321 : poFile->nMaxLength = nMaxLength;
693 : }
694 :
695 182421 : if (poFile->bIsDirectory)
696 : {
697 994 : errno = EISDIR;
698 994 : return nullptr;
699 : }
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Setup the file handle on this file. */
703 : /* -------------------------------------------------------------------- */
704 362854 : auto poHandle = std::make_unique<VSIMemHandle>();
705 :
706 181427 : poHandle->poFile = poFile;
707 181427 : poHandle->m_nOffset = 0;
708 181427 : poHandle->bEOF = false;
709 250039 : poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
710 68612 : strchr(pszAccess, 'a');
711 181427 : poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
712 :
713 : #ifdef DEBUG_VERBOSE
714 : CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle.get(),
715 : pszFilename, static_cast<int>(poFile.use_count()));
716 : #endif
717 181427 : if (strchr(pszAccess, 'a'))
718 : {
719 : vsi_l_offset nOffset;
720 : {
721 42 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
722 42 : nOffset = poFile->nLength;
723 : }
724 42 : poHandle->m_nOffset = nOffset;
725 : }
726 :
727 181427 : return VSIVirtualHandleUniquePtr(poHandle.release());
728 : }
729 :
730 : /************************************************************************/
731 : /* Stat() */
732 : /************************************************************************/
733 :
734 611404 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
735 : VSIStatBufL *pStatBuf, int /* nFlags */)
736 :
737 : {
738 1222810 : CPLMutexHolder oHolder(&hMutex);
739 :
740 1834210 : const CPLString osFilename = NormalizePath(pszFilename);
741 :
742 611404 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
743 :
744 611404 : if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
745 : {
746 56948 : pStatBuf->st_size = 0;
747 56948 : pStatBuf->st_mode = S_IFDIR;
748 56948 : return 0;
749 : }
750 :
751 554456 : auto oIter = oFileList.find(osFilename);
752 554456 : if (oIter == oFileList.end())
753 : {
754 478216 : errno = ENOENT;
755 478216 : return -1;
756 : }
757 :
758 152480 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
759 :
760 76240 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
761 :
762 76240 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
763 76240 : if (poFile->bIsDirectory)
764 : {
765 42488 : pStatBuf->st_size = 0;
766 42488 : pStatBuf->st_mode = S_IFDIR;
767 : }
768 : else
769 : {
770 33752 : pStatBuf->st_size = poFile->nLength;
771 33752 : pStatBuf->st_mode = S_IFREG;
772 33752 : if (const char *mtime =
773 33752 : CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
774 : {
775 31 : pStatBuf->st_mtime =
776 31 : static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
777 : }
778 : else
779 : {
780 33721 : pStatBuf->st_mtime = poFile->mTime;
781 : }
782 : }
783 :
784 76240 : return 0;
785 : }
786 :
787 : /************************************************************************/
788 : /* Unlink() */
789 : /************************************************************************/
790 :
791 80443 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
792 :
793 : {
794 160886 : CPLMutexHolder oHolder(&hMutex);
795 160886 : return Unlink_unlocked(pszFilename);
796 : }
797 :
798 : /************************************************************************/
799 : /* Unlink_unlocked() */
800 : /************************************************************************/
801 :
802 90673 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
803 :
804 : {
805 272019 : const CPLString osFilename = NormalizePath(pszFilename);
806 :
807 90673 : auto oIter = oFileList.find(osFilename);
808 90673 : if (oIter == oFileList.end())
809 : {
810 17666 : errno = ENOENT;
811 17666 : return -1;
812 : }
813 :
814 : #ifdef DEBUG_VERBOSE
815 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
816 : CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
817 : static_cast<int>(poFile.use_count()));
818 : #endif
819 73007 : oFileList.erase(oIter);
820 :
821 73007 : return 0;
822 : }
823 :
824 : /************************************************************************/
825 : /* Mkdir() */
826 : /************************************************************************/
827 :
828 98364 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
829 :
830 : {
831 196728 : CPLMutexHolder oHolder(&hMutex);
832 :
833 295092 : const CPLString osPathname = NormalizePath(pszPathname);
834 98364 : if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
835 : {
836 89834 : if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
837 44760 : return 0;
838 : // "/vsimem/.#!HIDDEN!#./{unique_counter}"
839 45074 : else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
840 : std::string::npos)
841 44760 : return 0;
842 :
843 : // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
844 : // accept creating an explicit directory
845 : }
846 :
847 8844 : if (oFileList.find(osPathname) != oFileList.end())
848 : {
849 784 : errno = EEXIST;
850 784 : return -1;
851 : }
852 :
853 8060 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
854 8060 : poFile->osFilename = osPathname;
855 8060 : poFile->bIsDirectory = true;
856 8060 : oFileList[osPathname] = poFile;
857 : #ifdef DEBUG_VERBOSE
858 : CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
859 : static_cast<int>(poFile.use_count()));
860 : #endif
861 8060 : CPL_IGNORE_RET_VAL(poFile);
862 8060 : return 0;
863 : }
864 :
865 : /************************************************************************/
866 : /* Rmdir() */
867 : /************************************************************************/
868 :
869 75 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
870 :
871 : {
872 75 : return Unlink(pszPathname);
873 : }
874 :
875 : /************************************************************************/
876 : /* RmdirRecursive() */
877 : /************************************************************************/
878 :
879 5264 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
880 : {
881 10528 : CPLMutexHolder oHolder(&hMutex);
882 :
883 10528 : const CPLString osPath = NormalizePath(pszDirname);
884 5264 : const size_t nPathLen = osPath.size();
885 5264 : int ret = 0;
886 5264 : if (osPath == "/vsimem")
887 : {
888 : // Clean-up all files under pszDirname, except hidden directories
889 : // if called from "/vsimem"
890 8 : for (auto iter = oFileList.begin(); iter != oFileList.end();
891 : /* no automatic increment */)
892 : {
893 6 : const char *pszFilePath = iter->second->osFilename.c_str();
894 6 : const size_t nFileLen = iter->second->osFilename.size();
895 12 : if (nFileLen > nPathLen &&
896 6 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
897 18 : pszFilePath[nPathLen] == '/' &&
898 6 : !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
899 : {
900 2 : iter = oFileList.erase(iter);
901 : }
902 : else
903 : {
904 4 : ++iter;
905 : }
906 : }
907 : }
908 : else
909 : {
910 5262 : ret = -1;
911 834358 : for (auto iter = oFileList.begin(); iter != oFileList.end();
912 : /* no automatic increment */)
913 : {
914 829096 : const char *pszFilePath = iter->second->osFilename.c_str();
915 829096 : const size_t nFileLen = iter->second->osFilename.size();
916 998455 : if (nFileLen >= nPathLen &&
917 846007 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
918 16911 : (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
919 : {
920 : // If VSIRmdirRecursive() is used correctly, it should at
921 : // least delete the directory on which it has been called
922 22068 : ret = 0;
923 22068 : iter = oFileList.erase(iter);
924 : }
925 : else
926 : {
927 807028 : ++iter;
928 : }
929 : }
930 :
931 : // Make sure that it always succeed on the root hidden directory
932 5262 : if (osPath == szHIDDEN_DIRNAME)
933 3 : ret = 0;
934 : }
935 10528 : return ret;
936 : }
937 :
938 : /************************************************************************/
939 : /* ReadDirEx() */
940 : /************************************************************************/
941 :
942 31491 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
943 :
944 : {
945 62982 : CPLMutexHolder oHolder(&hMutex);
946 :
947 62982 : const CPLString osPath = NormalizePath(pszPath);
948 :
949 31491 : char **papszDir = nullptr;
950 31491 : const size_t nPathLen = osPath.size();
951 :
952 : // In case of really big number of files in the directory, CSLAddString
953 : // can be slow (see #2158). We then directly build the list.
954 31491 : int nItems = 0;
955 31491 : int nAllocatedItems = 0;
956 :
957 31491 : if (osPath == szHIDDEN_DIRNAME)
958 : {
959 : // Special mode for hidden filenames.
960 : // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
961 : // created so they do not appear in oFileList, but their subcontent
962 : // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
963 20 : std::set<std::string> oSetSubDirs;
964 161 : for (const auto &iter : oFileList)
965 : {
966 151 : const char *pszFilePath = iter.second->osFilename.c_str();
967 288 : if (iter.second->osFilename.size() > nPathLen &&
968 137 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
969 : {
970 13 : char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
971 13 : char *pszSlash = strchr(pszItem, '/');
972 13 : if (pszSlash)
973 13 : *pszSlash = 0;
974 13 : if (cpl::contains(oSetSubDirs, pszItem))
975 : {
976 2 : CPLFree(pszItem);
977 2 : continue;
978 : }
979 11 : oSetSubDirs.insert(pszItem);
980 :
981 11 : if (nItems == 0)
982 : {
983 : papszDir =
984 6 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
985 6 : nAllocatedItems = 1;
986 : }
987 5 : else if (nItems >= nAllocatedItems)
988 : {
989 5 : nAllocatedItems = nAllocatedItems * 2;
990 5 : papszDir = static_cast<char **>(CPLRealloc(
991 5 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
992 : }
993 :
994 11 : papszDir[nItems] = pszItem;
995 11 : papszDir[nItems + 1] = nullptr;
996 :
997 11 : nItems++;
998 11 : if (nMaxFiles > 0 && nItems > nMaxFiles)
999 0 : break;
1000 : }
1001 : }
1002 : }
1003 : else
1004 : {
1005 11692400 : for (const auto &iter : oFileList)
1006 : {
1007 11660900 : const char *pszFilePath = iter.second->osFilename.c_str();
1008 11660900 : if (iter.second->osFilename.size() > nPathLen &&
1009 7280370 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
1010 22494700 : pszFilePath[nPathLen] == '/' &&
1011 3553400 : strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
1012 : {
1013 351080 : if (nItems == 0)
1014 : {
1015 : papszDir =
1016 29809 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
1017 29809 : nAllocatedItems = 1;
1018 : }
1019 321271 : else if (nItems >= nAllocatedItems)
1020 : {
1021 47952 : nAllocatedItems = nAllocatedItems * 2;
1022 47952 : papszDir = static_cast<char **>(CPLRealloc(
1023 47952 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1024 : }
1025 :
1026 351080 : papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
1027 351080 : papszDir[nItems + 1] = nullptr;
1028 :
1029 351080 : nItems++;
1030 351080 : if (nMaxFiles > 0 && nItems > nMaxFiles)
1031 1 : break;
1032 : }
1033 : }
1034 : }
1035 :
1036 62982 : return papszDir;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* Rename() */
1041 : /************************************************************************/
1042 :
1043 561 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
1044 : const char *pszNewPath, GDALProgressFunc,
1045 : void *)
1046 :
1047 : {
1048 1122 : CPLMutexHolder oHolder(&hMutex);
1049 :
1050 1683 : const std::string osOldPath = NormalizePath(pszOldPath);
1051 1683 : const std::string osNewPath = NormalizePath(pszNewPath);
1052 561 : if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
1053 3 : return -1;
1054 :
1055 558 : if (osOldPath.compare(osNewPath) == 0)
1056 304 : return 0;
1057 :
1058 254 : if (oFileList.find(osOldPath) == oFileList.end())
1059 : {
1060 0 : errno = ENOENT;
1061 0 : return -1;
1062 : }
1063 :
1064 : std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
1065 254 : oFileList.find(osOldPath);
1066 667 : while (it != oFileList.end() && it->first.find(osOldPath) == 0)
1067 : {
1068 826 : const std::string osRemainder = it->first.substr(osOldPath.size());
1069 413 : if (osRemainder.empty() || osRemainder[0] == '/')
1070 : {
1071 604 : const std::string osNewFullPath = osNewPath + osRemainder;
1072 302 : Unlink_unlocked(osNewFullPath.c_str());
1073 302 : oFileList[osNewFullPath] = it->second;
1074 302 : it->second->osFilename = osNewFullPath;
1075 302 : oFileList.erase(it++);
1076 : }
1077 : else
1078 : {
1079 111 : ++it;
1080 : }
1081 : }
1082 :
1083 254 : return 0;
1084 : }
1085 :
1086 : /************************************************************************/
1087 : /* NormalizePath() */
1088 : /************************************************************************/
1089 :
1090 1153880 : CPLString VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1091 : {
1092 1153880 : CPLString s(in);
1093 1153860 : std::replace(s.begin(), s.end(), '\\', '/');
1094 1153880 : s.replaceAll("//", '/');
1095 1153860 : if (!s.empty() && s.back() == '/')
1096 1646 : s.pop_back();
1097 1153860 : return s;
1098 : }
1099 :
1100 : /************************************************************************/
1101 : /* GetDiskFreeSpace() */
1102 : /************************************************************************/
1103 :
1104 71 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
1105 : {
1106 71 : const GIntBig nRet = CPLGetUsablePhysicalRAM();
1107 71 : if (nRet <= 0)
1108 0 : return -1;
1109 71 : return nRet;
1110 : }
1111 :
1112 : //! @endcond
1113 :
1114 : /************************************************************************/
1115 : /* VSIInstallMemFileHandler() */
1116 : /************************************************************************/
1117 :
1118 : /**
1119 : * \brief Install "memory" file system handler.
1120 : *
1121 : * A special file handler is installed that allows block of memory to be
1122 : * treated as files. All portions of the file system underneath the base
1123 : * path "/vsimem/" will be handled by this driver.
1124 : *
1125 : * Normal VSI*L functions can be used freely to create and destroy memory
1126 : * arrays treating them as if they were real file system objects. Some
1127 : * additional methods exist to efficient create memory file system objects
1128 : * without duplicating original copies of the data or to "steal" the block
1129 : * of memory associated with a memory file.
1130 : *
1131 : * Directory related functions are supported.
1132 : *
1133 : * This code example demonstrates using GDAL to translate from one memory
1134 : * buffer to another.
1135 : *
1136 : * \code
1137 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
1138 : * vsi_l_offset *pnOutDataLength )
1139 : * {
1140 : * // create memory file system object from buffer.
1141 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
1142 : * nInDataLength, FALSE ) );
1143 : *
1144 : * // Open memory buffer for read.
1145 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
1146 : *
1147 : * // Get output format driver.
1148 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1149 : * GDALDatasetH hOutDS;
1150 : *
1151 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
1152 : * NULL, NULL );
1153 : *
1154 : * // close source file, and "unlink" it.
1155 : * GDALClose( hDS );
1156 : * VSIUnlink( "/vsimem/work.dat" );
1157 : *
1158 : * // seize the buffer associated with the output file.
1159 : *
1160 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
1161 : * }
1162 : * \endcode
1163 : */
1164 :
1165 1784 : void VSIInstallMemFileHandler()
1166 : {
1167 1784 : VSIFileManager::InstallHandler("/vsimem/",
1168 1784 : new VSIMemFilesystemHandler("/vsimem/"));
1169 1784 : }
1170 :
1171 : /************************************************************************/
1172 : /* VSIFileFromMemBuffer() */
1173 : /************************************************************************/
1174 :
1175 : /**
1176 : * \brief Create memory "file" from a buffer.
1177 : *
1178 : * A virtual memory file is created from the passed buffer with the indicated
1179 : * filename. Under normal conditions the filename would need to be absolute
1180 : * and within the /vsimem/ portion of the filesystem.
1181 : * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
1182 : * an anonymous file, that is destroyed when the handle is closed.
1183 : *
1184 : * If bTakeOwnership is TRUE, then the memory file system handler will take
1185 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
1186 : * it remains the responsibility of the caller, but should not be freed as
1187 : * long as it might be accessed as a file. In no circumstances does this
1188 : * function take a copy of the pabyData contents.
1189 : *
1190 : * @param pszFilename the filename to be created, or nullptr
1191 : * @param pabyData the data buffer for the file.
1192 : * @param nDataLength the length of buffer in bytes.
1193 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
1194 : *
1195 : * @return open file handle on created file (see VSIFOpenL()).
1196 : */
1197 :
1198 10727 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
1199 : vsi_l_offset nDataLength, int bTakeOwnership)
1200 :
1201 : {
1202 10727 : if (VSIFileManager::GetHandler("") ==
1203 10727 : VSIFileManager::GetHandler("/vsimem/"))
1204 0 : VSIInstallMemFileHandler();
1205 :
1206 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1207 10727 : VSIFileManager::GetHandler("/vsimem/"));
1208 :
1209 : const CPLString osFilename =
1210 41313 : pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
1211 21453 : : std::string();
1212 10726 : if (osFilename == "/vsimem/")
1213 : {
1214 1 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1215 : pszFilename);
1216 1 : return nullptr;
1217 : }
1218 :
1219 : // Try to create the parent directory, if needed, before taking
1220 : // ownership of pabyData.
1221 10725 : if (!osFilename.empty())
1222 : {
1223 9927 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
1224 9928 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
1225 : {
1226 0 : VSIError(VSIE_FileError,
1227 : "Could not create directory %s for writing",
1228 : osFileDir.c_str());
1229 0 : errno = ENOENT;
1230 0 : return nullptr;
1231 : }
1232 : }
1233 :
1234 10726 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
1235 :
1236 10726 : poFile->osFilename = osFilename;
1237 10726 : poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
1238 10726 : poFile->pabyData = pabyData;
1239 10726 : poFile->nLength = nDataLength;
1240 10726 : poFile->nAllocLength = nDataLength;
1241 :
1242 10726 : if (!osFilename.empty())
1243 : {
1244 19856 : CPLMutexHolder oHolder(&poHandler->hMutex);
1245 9928 : poHandler->Unlink_unlocked(osFilename);
1246 9928 : poHandler->oFileList[poFile->osFilename] = poFile;
1247 : #ifdef DEBUG_VERBOSE
1248 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
1249 : poFile->osFilename.c_str(),
1250 : static_cast<int>(poFile.use_count()));
1251 : #endif
1252 : }
1253 :
1254 : /* -------------------------------------------------------------------- */
1255 : /* Setup the file handle on this file. */
1256 : /* -------------------------------------------------------------------- */
1257 10726 : VSIMemHandle *poHandle = new VSIMemHandle;
1258 :
1259 10726 : poHandle->poFile = std::move(poFile);
1260 10726 : poHandle->bUpdate = true;
1261 10726 : poHandle->m_bReadAllowed = true;
1262 10726 : return poHandle;
1263 : }
1264 :
1265 : /************************************************************************/
1266 : /* VSIGetMemFileBuffer() */
1267 : /************************************************************************/
1268 :
1269 : /**
1270 : * \brief Fetch buffer underlying memory file.
1271 : *
1272 : * This function returns a pointer to the memory buffer underlying a
1273 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
1274 : * object will be deleted, and ownership of the buffer will pass to the
1275 : * caller, otherwise the underlying file will remain in existence.
1276 : * bUnlinkAndSeize should only be used for files that own their data
1277 : * (see the bTakeOwnership parameter of VSIFileFromMemBuffer).
1278 : *
1279 : * @param pszFilename the name of the file to grab the buffer of.
1280 : * @param pnDataLength (file) length returned in this variable.
1281 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
1282 : *
1283 : * @return pointer to memory buffer or NULL on failure.
1284 : */
1285 :
1286 36323 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
1287 : int bUnlinkAndSeize)
1288 :
1289 : {
1290 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1291 36323 : VSIFileManager::GetHandler("/vsimem/"));
1292 :
1293 36327 : if (pszFilename == nullptr)
1294 0 : return nullptr;
1295 :
1296 : const std::string osFilename =
1297 108971 : VSIMemFilesystemHandler::NormalizePath(pszFilename);
1298 :
1299 72645 : CPLMutexHolder oHolder(&poHandler->hMutex);
1300 :
1301 36327 : if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
1302 139 : return nullptr;
1303 :
1304 36188 : std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
1305 36188 : GByte *pabyData = poFile->pabyData;
1306 36188 : if (pnDataLength != nullptr)
1307 33475 : *pnDataLength = poFile->nLength;
1308 :
1309 36188 : if (bUnlinkAndSeize)
1310 : {
1311 4430 : if (!poFile->bOwnData)
1312 0 : CPLDebug("VSIMemFile",
1313 : "File doesn't own data in VSIGetMemFileBuffer!");
1314 : else
1315 4430 : poFile->bOwnData = false;
1316 :
1317 4430 : poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
1318 : #ifdef DEBUG_VERBOSE
1319 : CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
1320 : poFile->osFilename.c_str(),
1321 : static_cast<int>(poFile.use_count()));
1322 : #endif
1323 4430 : poFile->pabyData = nullptr;
1324 4430 : poFile->nLength = 0;
1325 4430 : poFile->nAllocLength = 0;
1326 : }
1327 :
1328 36188 : return pabyData;
1329 : }
1330 :
1331 : /************************************************************************/
1332 : /* VSIMemGenerateHiddenFilename() */
1333 : /************************************************************************/
1334 :
1335 : /**
1336 : * \brief Generates a unique filename that can be used with the /vsimem/
1337 : * virtual file system.
1338 : *
1339 : * This function returns a (short-lived) string containing a unique filename,
1340 : * (using an atomic counter), designed for temporary files that must remain
1341 : * invisible for other users working at the "/vsimem/" top-level, i.e.
1342 : * such files are not returned by VSIReadDir("/vsimem/") or
1343 : * VSIReadDirRecursive("/vsimem/)".
1344 : *
1345 : * The function does not create the file per se. Such filename can be used to
1346 : * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
1347 : * a directory with VSIMkdir()
1348 : *
1349 : * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
1350 : * etc. is of the responsibility of the user. The user should not attempt to
1351 : * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
1352 : * on the returned filename, and work only with files at the same level or
1353 : * in subdirectories of what is returned by this function.
1354 : *
1355 : * @param pszFilename the filename to be appended at the end of the returned
1356 : * filename. If not specified, defaults to "unnamed".
1357 : *
1358 : * @return pointer to a short-lived string (rotating buffer of strings in
1359 : * thread-local storage). It is recommended to use CPLStrdup() or std::string()
1360 : * immediately on it.
1361 : *
1362 : * @since GDAL 3.10
1363 : */
1364 44021 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
1365 : {
1366 : static std::atomic<uint32_t> nCounter{0};
1367 44021 : return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
1368 44013 : pszFilename ? pszFilename : "unnamed");
1369 : }
|