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