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