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 191396 : 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 722 : bool HasPRead() const override
173 : {
174 722 : 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 1755 : explicit VSIMemFilesystemHandler(const char *pszPrefix)
197 1755 : : m_osPrefix(pszPrefix)
198 : {
199 1755 : }
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 std::string 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 103705 : VSIMemFile::VSIMemFile()
238 : {
239 103698 : time(&mTime);
240 103707 : }
241 :
242 : /************************************************************************/
243 : /* ~VSIMemFile() */
244 : /************************************************************************/
245 :
246 101021 : VSIMemFile::~VSIMemFile()
247 : {
248 101021 : if (bOwnData && pabyData)
249 82408 : CPLFree(pabyData);
250 101017 : }
251 :
252 : /************************************************************************/
253 : /* SetLength() */
254 : /************************************************************************/
255 :
256 : // Must be called under exclusive lock
257 1174960 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
258 :
259 : {
260 1174960 : 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 1172160 : 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 139564 : 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 139563 : const vsi_l_offset nNewAlloc =
287 84795 : (nAllocLength == 0 && nNewLength >= 1024 * 1024)
288 139652 : ? nNewLength
289 139474 : : nNewLength + nNewLength / 10 + 5000;
290 139563 : GByte *pabyNewData = nullptr;
291 : if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
292 : nNewAlloc)
293 : {
294 279128 : pabyNewData = static_cast<GByte *>(
295 139563 : nAllocLength == 0
296 84794 : ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
297 54769 : : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
298 : }
299 139565 : 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 139564 : 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 54769 : memset(pabyNewData + nAllocLength, 0,
314 54769 : static_cast<size_t>(nNewAlloc - nAllocLength));
315 : }
316 :
317 139564 : pabyData = pabyNewData;
318 139564 : nAllocLength = nNewAlloc;
319 : }
320 1032590 : else if (nNewLength < nLength)
321 : {
322 3431 : memset(pabyData + nNewLength, 0,
323 3431 : static_cast<size_t>(nLength - nNewLength));
324 : }
325 :
326 1172160 : nLength = nNewLength;
327 1172160 : time(&mTime);
328 :
329 1172150 : return true;
330 : }
331 :
332 : /************************************************************************/
333 : /* ==================================================================== */
334 : /* VSIMemHandle */
335 : /* ==================================================================== */
336 : /************************************************************************/
337 :
338 : /************************************************************************/
339 : /* ~VSIMemHandle() */
340 : /************************************************************************/
341 :
342 382751 : VSIMemHandle::~VSIMemHandle()
343 : {
344 191369 : VSIMemHandle::Close();
345 382758 : }
346 :
347 : /************************************************************************/
348 : /* Close() */
349 : /************************************************************************/
350 :
351 388069 : int VSIMemHandle::Close()
352 :
353 : {
354 388069 : 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 191375 : poFile = nullptr;
362 : }
363 :
364 388060 : return 0;
365 : }
366 :
367 : /************************************************************************/
368 : /* Seek() */
369 : /************************************************************************/
370 :
371 5182230 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
372 :
373 : {
374 : vsi_l_offset nLength;
375 : {
376 5182230 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
377 5182200 : nLength = poFile->nLength;
378 : }
379 :
380 5182200 : if (nWhence == SEEK_CUR)
381 : {
382 : if (nOffset > INT_MAX)
383 : {
384 : // printf("likely negative offset intended\n");
385 : }
386 392727 : m_nOffset += nOffset;
387 : }
388 4789480 : else if (nWhence == SEEK_SET)
389 : {
390 4576800 : m_nOffset = nOffset;
391 : }
392 212676 : else if (nWhence == SEEK_END)
393 : {
394 212675 : m_nOffset = nLength + nOffset;
395 : }
396 : else
397 : {
398 1 : errno = EINVAL;
399 1 : return -1;
400 : }
401 :
402 5182200 : bEOF = false;
403 :
404 5182200 : return 0;
405 : }
406 :
407 : /************************************************************************/
408 : /* Tell() */
409 : /************************************************************************/
410 :
411 3378550 : vsi_l_offset VSIMemHandle::Tell()
412 :
413 : {
414 3378550 : return m_nOffset;
415 : }
416 :
417 : /************************************************************************/
418 : /* Read() */
419 : /************************************************************************/
420 :
421 7746950 : size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
422 :
423 : {
424 7746950 : const vsi_l_offset nOffset = m_nOffset;
425 :
426 7746950 : size_t nBytesToRead = nSize * nCount;
427 7746950 : if (nBytesToRead == 0)
428 1658 : return 0;
429 :
430 7745300 : if (nCount > 0 && nBytesToRead / nCount != nSize)
431 : {
432 0 : bEOF = true;
433 0 : return 0;
434 : }
435 :
436 7745300 : if (!m_bReadAllowed)
437 : {
438 58 : m_bError = true;
439 58 : return 0;
440 : }
441 :
442 7745240 : 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 61953400 : [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
447 : {
448 15490400 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
449 :
450 7745230 : if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
451 : {
452 28309 : bEOFTmp = true;
453 28309 : return false;
454 : }
455 7716920 : if (nBytesToRead + nOffset > poFile->nLength)
456 : {
457 52417 : nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
458 52425 : nCount = nBytesToRead / nSize;
459 52425 : bEOFTmp = true;
460 : }
461 :
462 7716920 : if (nBytesToRead)
463 7716920 : memcpy(pBuffer, poFile->pabyData + nOffset,
464 : static_cast<size_t>(nBytesToRead));
465 7716920 : return true;
466 7745240 : };
467 :
468 7745240 : bool bRet = DoUnderLock();
469 7745230 : bEOF = bEOFTmp;
470 7745230 : if (!bRet)
471 28312 : return 0;
472 :
473 7716920 : m_nOffset += nBytesToRead;
474 :
475 7716920 : return nCount;
476 : }
477 :
478 : /************************************************************************/
479 : /* PRead() */
480 : /************************************************************************/
481 :
482 427 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
483 : vsi_l_offset nOffset) const
484 : {
485 854 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
486 :
487 427 : if (nOffset < poFile->nLength)
488 : {
489 : const size_t nToCopy = static_cast<size_t>(
490 1236 : std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
491 412 : static_cast<vsi_l_offset>(nSize)));
492 412 : memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
493 : nToCopy);
494 412 : return nToCopy;
495 : }
496 15 : return 0;
497 : }
498 :
499 : /************************************************************************/
500 : /* Write() */
501 : /************************************************************************/
502 :
503 1654210 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
504 :
505 : {
506 1654210 : const vsi_l_offset nOffset = m_nOffset;
507 :
508 1654210 : if (!bUpdate)
509 : {
510 1 : errno = EACCES;
511 1 : return 0;
512 : }
513 :
514 1654210 : const size_t nBytesToWrite = nSize * nCount;
515 :
516 : {
517 1654210 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
518 :
519 1654210 : if (nCount > 0 && nBytesToWrite / nCount != nSize)
520 : {
521 0 : return 0;
522 : }
523 1654210 : if (nBytesToWrite + nOffset < nBytesToWrite)
524 : {
525 0 : return 0;
526 : }
527 :
528 1654210 : if (nBytesToWrite + nOffset > poFile->nLength)
529 : {
530 1170870 : if (!poFile->SetLength(nBytesToWrite + nOffset))
531 2789 : return 0;
532 : }
533 :
534 1651420 : if (nBytesToWrite)
535 1641190 : memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
536 :
537 1651410 : time(&poFile->mTime);
538 : }
539 :
540 1651440 : m_nOffset += nBytesToWrite;
541 :
542 1651440 : return nCount;
543 : }
544 :
545 : /************************************************************************/
546 : /* ClearErr() */
547 : /************************************************************************/
548 :
549 25803 : void VSIMemHandle::ClearErr()
550 :
551 : {
552 25803 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
553 25803 : bEOF = false;
554 25803 : m_bError = false;
555 25803 : }
556 :
557 : /************************************************************************/
558 : /* Error() */
559 : /************************************************************************/
560 :
561 29054 : int VSIMemHandle::Error()
562 :
563 : {
564 29054 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
565 58108 : return m_bError ? TRUE : FALSE;
566 : }
567 :
568 : /************************************************************************/
569 : /* Eof() */
570 : /************************************************************************/
571 :
572 134182 : int VSIMemHandle::Eof()
573 :
574 : {
575 134182 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
576 268364 : return bEOF ? TRUE : FALSE;
577 : }
578 :
579 : /************************************************************************/
580 : /* Truncate() */
581 : /************************************************************************/
582 :
583 752 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
584 : {
585 752 : if (!bUpdate)
586 : {
587 1 : errno = EACCES;
588 1 : return -1;
589 : }
590 :
591 1502 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
592 751 : if (poFile->SetLength(nNewSize))
593 731 : return 0;
594 :
595 20 : return -1;
596 : }
597 :
598 : /************************************************************************/
599 : /* ==================================================================== */
600 : /* VSIMemFilesystemHandler */
601 : /* ==================================================================== */
602 : /************************************************************************/
603 :
604 : /************************************************************************/
605 : /* ~VSIMemFilesystemHandler() */
606 : /************************************************************************/
607 :
608 2244 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
609 :
610 : {
611 1122 : oFileList.clear();
612 :
613 1122 : if (hMutex != nullptr)
614 204 : CPLDestroyMutex(hMutex);
615 1122 : hMutex = nullptr;
616 2244 : }
617 :
618 : /************************************************************************/
619 : /* Open() */
620 : /************************************************************************/
621 :
622 : VSIVirtualHandleUniquePtr
623 248255 : VSIMemFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
624 : bool bSetError, CSLConstList /* papszOptions */)
625 :
626 : {
627 496514 : CPLMutexHolder oHolder(&hMutex);
628 744777 : const std::string osFilename = NormalizePath(pszFilename);
629 248259 : if (osFilename.empty())
630 0 : return nullptr;
631 :
632 248259 : vsi_l_offset nMaxLength = GUINTBIG_MAX;
633 248259 : const size_t iPos = osFilename.find("||maxlength=");
634 248259 : if (iPos != std::string::npos)
635 : {
636 2425 : nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
637 4850 : osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
638 : }
639 :
640 : /* -------------------------------------------------------------------- */
641 : /* Get the filename we are opening, create if needed. */
642 : /* -------------------------------------------------------------------- */
643 496518 : std::shared_ptr<VSIMemFile> poFile = nullptr;
644 248259 : const auto oIter = oFileList.find(osFilename);
645 248259 : if (oIter != oFileList.end())
646 : {
647 96429 : poFile = oIter->second;
648 : }
649 :
650 : // If no file and opening in read, error out.
651 656202 : if (strstr(pszAccess, "w") == nullptr &&
652 248259 : strstr(pszAccess, "a") == nullptr && poFile == nullptr)
653 : {
654 66540 : if (bSetError)
655 : {
656 15174 : VSIError(VSIE_FileError, "No such file or directory");
657 : }
658 66540 : errno = ENOENT;
659 66540 : return nullptr;
660 : }
661 :
662 : // Create.
663 181719 : if (poFile == nullptr)
664 : {
665 85290 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
666 85290 : 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 85289 : poFile = std::make_shared<VSIMemFile>();
679 85289 : poFile->osFilename = osFilename;
680 85289 : 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 85289 : poFile->nMaxLength = nMaxLength;
686 : }
687 : // Overwrite
688 96429 : else if (strstr(pszAccess, "w"))
689 : {
690 3323 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
691 3323 : poFile->SetLength(0);
692 3323 : poFile->nMaxLength = nMaxLength;
693 : }
694 :
695 181718 : if (poFile->bIsDirectory)
696 : {
697 995 : errno = EISDIR;
698 995 : return nullptr;
699 : }
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Setup the file handle on this file. */
703 : /* -------------------------------------------------------------------- */
704 361446 : auto poHandle = std::make_unique<VSIMemHandle>();
705 :
706 180723 : poHandle->poFile = poFile;
707 180723 : poHandle->m_nOffset = 0;
708 180723 : poHandle->bEOF = false;
709 248917 : poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
710 68194 : strchr(pszAccess, 'a');
711 180723 : 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,
715 : pszFilename, static_cast<int>(poFile.use_count()));
716 : #endif
717 180723 : 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 180723 : return VSIVirtualHandleUniquePtr(poHandle.release());
728 : }
729 :
730 : /************************************************************************/
731 : /* Stat() */
732 : /************************************************************************/
733 :
734 592050 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
735 : VSIStatBufL *pStatBuf, int /* nFlags */)
736 :
737 : {
738 1184100 : CPLMutexHolder oHolder(&hMutex);
739 :
740 1776150 : const std::string osFilename = NormalizePath(pszFilename);
741 :
742 592051 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
743 :
744 592051 : if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
745 : {
746 57010 : pStatBuf->st_size = 0;
747 57010 : pStatBuf->st_mode = S_IFDIR;
748 57010 : return 0;
749 : }
750 :
751 535041 : auto oIter = oFileList.find(osFilename);
752 535041 : if (oIter == oFileList.end())
753 : {
754 459307 : errno = ENOENT;
755 459307 : return -1;
756 : }
757 :
758 151468 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
759 :
760 75734 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
761 :
762 75734 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
763 75734 : if (poFile->bIsDirectory)
764 : {
765 42083 : pStatBuf->st_size = 0;
766 42083 : pStatBuf->st_mode = S_IFDIR;
767 : }
768 : else
769 : {
770 33651 : pStatBuf->st_size = poFile->nLength;
771 33651 : pStatBuf->st_mode = S_IFREG;
772 33651 : if (const char *mtime =
773 33651 : 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 33620 : pStatBuf->st_mtime = poFile->mTime;
781 : }
782 : }
783 :
784 75734 : return 0;
785 : }
786 :
787 : /************************************************************************/
788 : /* Unlink() */
789 : /************************************************************************/
790 :
791 80423 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
792 :
793 : {
794 160853 : CPLMutexHolder oHolder(&hMutex);
795 160860 : return Unlink_unlocked(pszFilename);
796 : }
797 :
798 : /************************************************************************/
799 : /* Unlink_unlocked() */
800 : /************************************************************************/
801 :
802 90618 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
803 :
804 : {
805 271854 : const std::string osFilename = NormalizePath(pszFilename);
806 :
807 90618 : auto oIter = oFileList.find(osFilename);
808 90618 : if (oIter == oFileList.end())
809 : {
810 17622 : errno = ENOENT;
811 17622 : 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 72996 : oFileList.erase(oIter);
820 :
821 72996 : return 0;
822 : }
823 :
824 : /************************************************************************/
825 : /* Mkdir() */
826 : /************************************************************************/
827 :
828 97996 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
829 :
830 : {
831 195992 : CPLMutexHolder oHolder(&hMutex);
832 :
833 293988 : const std::string osPathname = NormalizePath(pszPathname);
834 97996 : if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
835 : {
836 89777 : if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
837 44733 : return 0;
838 : // "/vsimem/.#!HIDDEN!#./{unique_counter}"
839 45044 : else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
840 : std::string::npos)
841 44733 : return 0;
842 :
843 : // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
844 : // accept creating an explicit directory
845 : }
846 :
847 8530 : if (oFileList.find(osPathname) != oFileList.end())
848 : {
849 781 : errno = EEXIST;
850 781 : return -1;
851 : }
852 :
853 7749 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
854 7749 : poFile->osFilename = osPathname;
855 7749 : poFile->bIsDirectory = true;
856 7749 : 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 7749 : CPL_IGNORE_RET_VAL(poFile);
862 7749 : return 0;
863 : }
864 :
865 : /************************************************************************/
866 : /* Rmdir() */
867 : /************************************************************************/
868 :
869 76 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
870 :
871 : {
872 76 : return Unlink(pszPathname);
873 : }
874 :
875 : /************************************************************************/
876 : /* RmdirRecursive() */
877 : /************************************************************************/
878 :
879 5019 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
880 : {
881 10038 : CPLMutexHolder oHolder(&hMutex);
882 :
883 10038 : const CPLString osPath = NormalizePath(pszDirname);
884 5019 : const size_t nPathLen = osPath.size();
885 5019 : int ret = 0;
886 5019 : 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 5017 : ret = -1;
911 802190 : for (auto iter = oFileList.begin(); iter != oFileList.end();
912 : /* no automatic increment */)
913 : {
914 797173 : const char *pszFilePath = iter->second->osFilename.c_str();
915 797173 : const size_t nFileLen = iter->second->osFilename.size();
916 960689 : if (nFileLen >= nPathLen &&
917 813724 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
918 16551 : (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 21463 : ret = 0;
923 21463 : iter = oFileList.erase(iter);
924 : }
925 : else
926 : {
927 775710 : ++iter;
928 : }
929 : }
930 :
931 : // Make sure that it always succeed on the root hidden directory
932 5017 : if (osPath == szHIDDEN_DIRNAME)
933 3 : ret = 0;
934 : }
935 10038 : return ret;
936 : }
937 :
938 : /************************************************************************/
939 : /* ReadDirEx() */
940 : /************************************************************************/
941 :
942 28856 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
943 :
944 : {
945 57713 : CPLMutexHolder oHolder(&hMutex);
946 :
947 57714 : const CPLString osPath = NormalizePath(pszPath);
948 :
949 28857 : char **papszDir = nullptr;
950 28857 : 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 28857 : int nItems = 0;
955 28857 : int nAllocatedItems = 0;
956 :
957 28857 : 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 10599100 : for (const auto &iter : oFileList)
1006 : {
1007 10570300 : const char *pszFilePath = iter.second->osFilename.c_str();
1008 10570300 : if (iter.second->osFilename.size() > nPathLen &&
1009 6477080 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
1010 19839600 : pszFilePath[nPathLen] == '/' &&
1011 2792290 : strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
1012 : {
1013 371706 : if (nItems == 0)
1014 : {
1015 : papszDir =
1016 27185 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
1017 27185 : nAllocatedItems = 1;
1018 : }
1019 344521 : else if (nItems >= nAllocatedItems)
1020 : {
1021 48408 : nAllocatedItems = nAllocatedItems * 2;
1022 48408 : papszDir = static_cast<char **>(CPLRealloc(
1023 48408 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1024 : }
1025 :
1026 371706 : papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
1027 371706 : papszDir[nItems + 1] = nullptr;
1028 :
1029 371706 : nItems++;
1030 371706 : if (nMaxFiles > 0 && nItems > nMaxFiles)
1031 1 : break;
1032 : }
1033 : }
1034 : }
1035 :
1036 57714 : 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 1110120 : std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1091 : {
1092 2220190 : CPLString s(in);
1093 1110080 : std::replace(s.begin(), s.end(), '\\', '/');
1094 1110110 : s.replaceAll("//", '/');
1095 1110100 : if (!s.empty() && s.back() == '/')
1096 1648 : s.pop_back();
1097 : #if __GNUC__ >= 13
1098 : // gcc 13 complains about below explicit std::move()
1099 : return s;
1100 : #else
1101 : // Android NDK (and probably other compilers) warn about
1102 : // "warning: local variable 's' will be copied despite being returned by name [-Wreturn-std-move]"
1103 : // if not specifying std::move()
1104 2220140 : return std::move(s);
1105 : #endif
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* GetDiskFreeSpace() */
1110 : /************************************************************************/
1111 :
1112 71 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
1113 : {
1114 71 : const GIntBig nRet = CPLGetUsablePhysicalRAM();
1115 71 : if (nRet <= 0)
1116 0 : return -1;
1117 71 : return nRet;
1118 : }
1119 :
1120 : //! @endcond
1121 :
1122 : /************************************************************************/
1123 : /* VSIInstallMemFileHandler() */
1124 : /************************************************************************/
1125 :
1126 : /**
1127 : * \brief Install "memory" file system handler.
1128 : *
1129 : * A special file handler is installed that allows block of memory to be
1130 : * treated as files. All portions of the file system underneath the base
1131 : * path "/vsimem/" will be handled by this driver.
1132 : *
1133 : * Normal VSI*L functions can be used freely to create and destroy memory
1134 : * arrays treating them as if they were real file system objects. Some
1135 : * additional methods exist to efficient create memory file system objects
1136 : * without duplicating original copies of the data or to "steal" the block
1137 : * of memory associated with a memory file.
1138 : *
1139 : * Directory related functions are supported.
1140 : *
1141 : * This code example demonstrates using GDAL to translate from one memory
1142 : * buffer to another.
1143 : *
1144 : * \code
1145 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
1146 : * vsi_l_offset *pnOutDataLength )
1147 : * {
1148 : * // create memory file system object from buffer.
1149 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
1150 : * nInDataLength, FALSE ) );
1151 : *
1152 : * // Open memory buffer for read.
1153 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
1154 : *
1155 : * // Get output format driver.
1156 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1157 : * GDALDatasetH hOutDS;
1158 : *
1159 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
1160 : * NULL, NULL );
1161 : *
1162 : * // close source file, and "unlink" it.
1163 : * GDALClose( hDS );
1164 : * VSIUnlink( "/vsimem/work.dat" );
1165 : *
1166 : * // seize the buffer associated with the output file.
1167 : *
1168 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
1169 : * }
1170 : * \endcode
1171 : */
1172 :
1173 1754 : void VSIInstallMemFileHandler()
1174 : {
1175 1754 : VSIFileManager::InstallHandler("/vsimem/",
1176 1754 : new VSIMemFilesystemHandler("/vsimem/"));
1177 1754 : }
1178 :
1179 : /************************************************************************/
1180 : /* VSIFileFromMemBuffer() */
1181 : /************************************************************************/
1182 :
1183 : /**
1184 : * \brief Create memory "file" from a buffer.
1185 : *
1186 : * A virtual memory file is created from the passed buffer with the indicated
1187 : * filename. Under normal conditions the filename would need to be absolute
1188 : * and within the /vsimem/ portion of the filesystem.
1189 : * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
1190 : * an anonymous file, that is destroyed when the handle is closed.
1191 : *
1192 : * If bTakeOwnership is TRUE, then the memory file system handler will take
1193 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
1194 : * it remains the responsibility of the caller, but should not be freed as
1195 : * long as it might be accessed as a file. In no circumstances does this
1196 : * function take a copy of the pabyData contents.
1197 : *
1198 : * @param pszFilename the filename to be created, or nullptr
1199 : * @param pabyData the data buffer for the file.
1200 : * @param nDataLength the length of buffer in bytes.
1201 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
1202 : *
1203 : * @return open file handle on created file (see VSIFOpenL()).
1204 : */
1205 :
1206 10669 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
1207 : vsi_l_offset nDataLength, int bTakeOwnership)
1208 :
1209 : {
1210 10669 : if (VSIFileManager::GetHandler("") ==
1211 10671 : VSIFileManager::GetHandler("/vsimem/"))
1212 0 : VSIInstallMemFileHandler();
1213 :
1214 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1215 10673 : VSIFileManager::GetHandler("/vsimem/"));
1216 :
1217 : const CPLString osFilename =
1218 31204 : pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
1219 21307 : : std::string();
1220 10653 : if (osFilename == "/vsimem/")
1221 : {
1222 1 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1223 : pszFilename);
1224 1 : return nullptr;
1225 : }
1226 :
1227 : // Try to create the parent directory, if needed, before taking
1228 : // ownership of pabyData.
1229 10610 : if (!osFilename.empty())
1230 : {
1231 9837 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
1232 9833 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
1233 : {
1234 0 : VSIError(VSIE_FileError,
1235 : "Could not create directory %s for writing",
1236 : osFileDir.c_str());
1237 0 : errno = ENOENT;
1238 0 : return nullptr;
1239 : }
1240 : }
1241 :
1242 10660 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
1243 :
1244 10668 : poFile->osFilename = osFilename;
1245 10662 : poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
1246 10667 : poFile->pabyData = pabyData;
1247 10669 : poFile->nLength = nDataLength;
1248 10671 : poFile->nAllocLength = nDataLength;
1249 :
1250 10668 : if (!osFilename.empty())
1251 : {
1252 19772 : CPLMutexHolder oHolder(&poHandler->hMutex);
1253 9886 : poHandler->Unlink_unlocked(osFilename);
1254 9886 : poHandler->oFileList[poFile->osFilename] = poFile;
1255 : #ifdef DEBUG_VERBOSE
1256 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
1257 : poFile->osFilename.c_str(),
1258 : static_cast<int>(poFile.use_count()));
1259 : #endif
1260 : }
1261 :
1262 : /* -------------------------------------------------------------------- */
1263 : /* Setup the file handle on this file. */
1264 : /* -------------------------------------------------------------------- */
1265 10668 : VSIMemHandle *poHandle = new VSIMemHandle;
1266 :
1267 10663 : poHandle->poFile = std::move(poFile);
1268 10663 : poHandle->bUpdate = true;
1269 10663 : poHandle->m_bReadAllowed = true;
1270 10663 : return poHandle;
1271 : }
1272 :
1273 : /************************************************************************/
1274 : /* VSIGetMemFileBuffer() */
1275 : /************************************************************************/
1276 :
1277 : /**
1278 : * \brief Fetch buffer underlying memory file.
1279 : *
1280 : * This function returns a pointer to the memory buffer underlying a
1281 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
1282 : * object will be deleted, and ownership of the buffer will pass to the
1283 : * caller, otherwise the underlying file will remain in existence.
1284 : * bUnlinkAndSeize should only be used for files that own their data
1285 : * (see the bTakeOwnership parameter of VSIFileFromMemBuffer).
1286 : *
1287 : * @param pszFilename the name of the file to grab the buffer of.
1288 : * @param pnDataLength (file) length returned in this variable.
1289 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
1290 : *
1291 : * @return pointer to memory buffer or NULL on failure.
1292 : */
1293 :
1294 36308 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
1295 : int bUnlinkAndSeize)
1296 :
1297 : {
1298 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1299 36308 : VSIFileManager::GetHandler("/vsimem/"));
1300 :
1301 36307 : if (pszFilename == nullptr)
1302 0 : return nullptr;
1303 :
1304 : const std::string osFilename =
1305 108923 : VSIMemFilesystemHandler::NormalizePath(pszFilename);
1306 :
1307 72613 : CPLMutexHolder oHolder(&poHandler->hMutex);
1308 :
1309 36309 : if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
1310 139 : return nullptr;
1311 :
1312 36170 : std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
1313 36170 : GByte *pabyData = poFile->pabyData;
1314 36170 : if (pnDataLength != nullptr)
1315 33457 : *pnDataLength = poFile->nLength;
1316 :
1317 36170 : if (bUnlinkAndSeize)
1318 : {
1319 4418 : if (!poFile->bOwnData)
1320 0 : CPLDebug("VSIMemFile",
1321 : "File doesn't own data in VSIGetMemFileBuffer!");
1322 : else
1323 4418 : poFile->bOwnData = false;
1324 :
1325 4418 : poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
1326 : #ifdef DEBUG_VERBOSE
1327 : CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
1328 : poFile->osFilename.c_str(),
1329 : static_cast<int>(poFile.use_count()));
1330 : #endif
1331 4418 : poFile->pabyData = nullptr;
1332 4418 : poFile->nLength = 0;
1333 4418 : poFile->nAllocLength = 0;
1334 : }
1335 :
1336 36170 : return pabyData;
1337 : }
1338 :
1339 : /************************************************************************/
1340 : /* VSIMemGenerateHiddenFilename() */
1341 : /************************************************************************/
1342 :
1343 : /**
1344 : * \brief Generates a unique filename that can be used with the /vsimem/
1345 : * virtual file system.
1346 : *
1347 : * This function returns a (short-lived) string containing a unique filename,
1348 : * (using an atomic counter), designed for temporary files that must remain
1349 : * invisible for other users working at the "/vsimem/" top-level, i.e.
1350 : * such files are not returned by VSIReadDir("/vsimem/") or
1351 : * VSIReadDirRecursive("/vsimem/)".
1352 : *
1353 : * The function does not create the file per se. Such filename can be used to
1354 : * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
1355 : * a directory with VSIMkdir()
1356 : *
1357 : * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
1358 : * etc. is of the responsibility of the user. The user should not attempt to
1359 : * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
1360 : * on the returned filename, and work only with files at the same level or
1361 : * in subdirectories of what is returned by this function.
1362 : *
1363 : * @param pszFilename the filename to be appended at the end of the returned
1364 : * filename. If not specified, defaults to "unnamed".
1365 : *
1366 : * @return pointer to a short-lived string (rotating buffer of strings in
1367 : * thread-local storage). It is recommended to use CPLStrdup() or std::string()
1368 : * immediately on it.
1369 : *
1370 : * @since GDAL 3.10
1371 : */
1372 43989 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
1373 : {
1374 : static std::atomic<uint32_t> nCounter{0};
1375 43989 : return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
1376 43973 : pszFilename ? pszFilename : "unnamed");
1377 : }
|