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 168265 : 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 721 : bool HasPRead() const override
176 : {
177 721 : 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<std::string, std::shared_ptr<VSIMemFile>> oFileList{};
197 : CPLMutex *hMutex = nullptr;
198 :
199 1393 : explicit VSIMemFilesystemHandler(const char *pszPrefix)
200 1393 : : m_osPrefix(pszPrefix)
201 : {
202 1393 : }
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 94328 : VSIMemFile::VSIMemFile()
243 : {
244 94328 : time(&mTime);
245 94328 : }
246 :
247 : /************************************************************************/
248 : /* ~VSIMemFile() */
249 : /************************************************************************/
250 :
251 91677 : VSIMemFile::~VSIMemFile()
252 : {
253 91677 : if (bOwnData && pabyData)
254 76099 : CPLFree(pabyData);
255 91677 : }
256 :
257 : /************************************************************************/
258 : /* SetLength() */
259 : /************************************************************************/
260 :
261 : // Must be called under exclusive lock
262 1124040 : bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
263 :
264 : {
265 1124040 : if (nNewLength > nMaxLength)
266 : {
267 4640 : CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
268 4640 : return false;
269 : }
270 :
271 : /* -------------------------------------------------------------------- */
272 : /* Grow underlying array if needed. */
273 : /* -------------------------------------------------------------------- */
274 1119400 : 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 129130 : 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 129129 : const vsi_l_offset nNewAlloc =
292 78751 : (nAllocLength == 0 && nNewLength >= 1024 * 1024)
293 129206 : ? nNewLength
294 129052 : : nNewLength + nNewLength / 10 + 5000;
295 129129 : GByte *pabyNewData = nullptr;
296 : if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
297 : nNewAlloc)
298 : {
299 258258 : pabyNewData = static_cast<GByte *>(
300 129129 : nAllocLength == 0
301 78751 : ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
302 50378 : : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
303 : }
304 129129 : 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 129128 : 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 50378 : memset(pabyNewData + nAllocLength, 0,
319 50378 : static_cast<size_t>(nNewAlloc - nAllocLength));
320 : }
321 :
322 129128 : pabyData = pabyNewData;
323 129128 : nAllocLength = nNewAlloc;
324 : }
325 990266 : else if (nNewLength < nLength)
326 : {
327 3312 : memset(pabyData + nNewLength, 0,
328 3312 : static_cast<size_t>(nLength - nNewLength));
329 : }
330 :
331 1119390 : nLength = nNewLength;
332 1119390 : time(&mTime);
333 :
334 1119390 : return true;
335 : }
336 :
337 : /************************************************************************/
338 : /* ==================================================================== */
339 : /* VSIMemHandle */
340 : /* ==================================================================== */
341 : /************************************************************************/
342 :
343 : /************************************************************************/
344 : /* ~VSIMemHandle() */
345 : /************************************************************************/
346 :
347 336509 : VSIMemHandle::~VSIMemHandle()
348 : {
349 168253 : VSIMemHandle::Close();
350 336511 : }
351 :
352 : /************************************************************************/
353 : /* Close() */
354 : /************************************************************************/
355 :
356 336526 : int VSIMemHandle::Close()
357 :
358 : {
359 336526 : 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 168258 : poFile = nullptr;
367 : }
368 :
369 336523 : return 0;
370 : }
371 :
372 : /************************************************************************/
373 : /* Seek() */
374 : /************************************************************************/
375 :
376 5028480 : int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
377 :
378 : {
379 : vsi_l_offset nLength;
380 : {
381 5028480 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
382 5028420 : nLength = poFile->nLength;
383 : }
384 :
385 5028420 : if (nWhence == SEEK_CUR)
386 : {
387 : if (nOffset > INT_MAX)
388 : {
389 : // printf("likely negative offset intended\n");
390 : }
391 388622 : m_nOffset += nOffset;
392 : }
393 4639800 : else if (nWhence == SEEK_SET)
394 : {
395 4443940 : m_nOffset = nOffset;
396 : }
397 195861 : else if (nWhence == SEEK_END)
398 : {
399 195862 : m_nOffset = nLength + nOffset;
400 : }
401 : else
402 : {
403 0 : errno = EINVAL;
404 0 : return -1;
405 : }
406 :
407 5028430 : bEOF = false;
408 :
409 5028430 : return 0;
410 : }
411 :
412 : /************************************************************************/
413 : /* Tell() */
414 : /************************************************************************/
415 :
416 3291600 : vsi_l_offset VSIMemHandle::Tell()
417 :
418 : {
419 3291600 : return m_nOffset;
420 : }
421 :
422 : /************************************************************************/
423 : /* Read() */
424 : /************************************************************************/
425 :
426 7250520 : size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
427 :
428 : {
429 7250520 : const vsi_l_offset nOffset = m_nOffset;
430 :
431 7250520 : size_t nBytesToRead = nSize * nCount;
432 7250520 : if (nBytesToRead == 0)
433 1587 : return 0;
434 :
435 7248940 : if (nCount > 0 && nBytesToRead / nCount != nSize)
436 : {
437 0 : bEOF = true;
438 0 : return 0;
439 : }
440 :
441 7248940 : if (!m_bReadAllowed)
442 : {
443 58 : m_bError = true;
444 58 : return 0;
445 : }
446 :
447 7248880 : 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 57980500 : [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
452 : {
453 14497800 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
454 :
455 7248880 : if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
456 : {
457 27585 : bEOFTmp = true;
458 27585 : return false;
459 : }
460 7221300 : if (nBytesToRead + nOffset > poFile->nLength)
461 : {
462 49813 : nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
463 49979 : nCount = nBytesToRead / nSize;
464 49979 : bEOFTmp = true;
465 : }
466 :
467 7221420 : if (nBytesToRead)
468 7221270 : memcpy(pBuffer, poFile->pabyData + nOffset,
469 : static_cast<size_t>(nBytesToRead));
470 7221390 : return true;
471 7248880 : };
472 :
473 7248880 : bool bRet = DoUnderLock();
474 7249000 : bEOF = bEOFTmp;
475 7249000 : if (!bRet)
476 27585 : return 0;
477 :
478 7221410 : m_nOffset += nBytesToRead;
479 :
480 7221410 : return nCount;
481 : }
482 :
483 : /************************************************************************/
484 : /* PRead() */
485 : /************************************************************************/
486 :
487 427 : size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
488 : vsi_l_offset nOffset) const
489 : {
490 854 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
491 :
492 427 : if (nOffset < poFile->nLength)
493 : {
494 : const size_t nToCopy = static_cast<size_t>(
495 1236 : std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
496 412 : static_cast<vsi_l_offset>(nSize)));
497 412 : memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
498 : nToCopy);
499 412 : return nToCopy;
500 : }
501 15 : return 0;
502 : }
503 :
504 : /************************************************************************/
505 : /* Write() */
506 : /************************************************************************/
507 :
508 1583450 : size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
509 :
510 : {
511 1583450 : const vsi_l_offset nOffset = m_nOffset;
512 :
513 1583450 : if (!bUpdate)
514 : {
515 1 : errno = EACCES;
516 1 : return 0;
517 : }
518 :
519 1583450 : const size_t nBytesToWrite = nSize * nCount;
520 :
521 : {
522 1583450 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
523 :
524 1583450 : if (nCount > 0 && nBytesToWrite / nCount != nSize)
525 : {
526 0 : return 0;
527 : }
528 1583450 : if (nBytesToWrite + nOffset < nBytesToWrite)
529 : {
530 0 : return 0;
531 : }
532 :
533 1583450 : if (nBytesToWrite + nOffset > poFile->nLength)
534 : {
535 1120100 : if (!poFile->SetLength(nBytesToWrite + nOffset))
536 4622 : return 0;
537 : }
538 :
539 1578800 : if (nBytesToWrite)
540 1568660 : memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
541 :
542 1578780 : time(&poFile->mTime);
543 : }
544 :
545 1578840 : m_nOffset += nBytesToWrite;
546 :
547 1578840 : return nCount;
548 : }
549 :
550 : /************************************************************************/
551 : /* ClearErr() */
552 : /************************************************************************/
553 :
554 25776 : void VSIMemHandle::ClearErr()
555 :
556 : {
557 25776 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
558 25776 : bEOF = false;
559 25776 : m_bError = false;
560 25776 : }
561 :
562 : /************************************************************************/
563 : /* Error() */
564 : /************************************************************************/
565 :
566 28971 : int VSIMemHandle::Error()
567 :
568 : {
569 28971 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
570 57942 : return m_bError ? TRUE : FALSE;
571 : }
572 :
573 : /************************************************************************/
574 : /* Eof() */
575 : /************************************************************************/
576 :
577 133971 : int VSIMemHandle::Eof()
578 :
579 : {
580 133971 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
581 267942 : return bEOF ? TRUE : FALSE;
582 : }
583 :
584 : /************************************************************************/
585 : /* Truncate() */
586 : /************************************************************************/
587 :
588 716 : int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
589 : {
590 716 : if (!bUpdate)
591 : {
592 1 : errno = EACCES;
593 1 : return -1;
594 : }
595 :
596 1430 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
597 715 : if (poFile->SetLength(nNewSize))
598 695 : return 0;
599 :
600 20 : return -1;
601 : }
602 :
603 : /************************************************************************/
604 : /* ==================================================================== */
605 : /* VSIMemFilesystemHandler */
606 : /* ==================================================================== */
607 : /************************************************************************/
608 :
609 : /************************************************************************/
610 : /* ~VSIMemFilesystemHandler() */
611 : /************************************************************************/
612 :
613 1884 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
614 :
615 : {
616 942 : oFileList.clear();
617 :
618 942 : if (hMutex != nullptr)
619 215 : CPLDestroyMutex(hMutex);
620 942 : hMutex = nullptr;
621 1884 : }
622 :
623 : /************************************************************************/
624 : /* Open() */
625 : /************************************************************************/
626 :
627 211652 : VSIVirtualHandle *VSIMemFilesystemHandler::Open(const char *pszFilename,
628 : const char *pszAccess,
629 : bool bSetError,
630 : CSLConstList /* papszOptions */)
631 :
632 : {
633 423307 : CPLMutexHolder oHolder(&hMutex);
634 634965 : const std::string osFilename = NormalizePath(pszFilename);
635 211655 : if (osFilename.empty())
636 0 : return nullptr;
637 :
638 211655 : vsi_l_offset nMaxLength = GUINTBIG_MAX;
639 211655 : const size_t iPos = osFilename.find("||maxlength=");
640 211655 : if (iPos != std::string::npos)
641 : {
642 3258 : nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
643 6516 : osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
644 : }
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Get the filename we are opening, create if needed. */
648 : /* -------------------------------------------------------------------- */
649 423310 : std::shared_ptr<VSIMemFile> poFile = nullptr;
650 211655 : const auto oIter = oFileList.find(osFilename);
651 211655 : if (oIter != oFileList.end())
652 : {
653 79624 : poFile = oIter->second;
654 : }
655 :
656 : // If no file and opening in read, error out.
657 552514 : if (strstr(pszAccess, "w") == nullptr &&
658 211655 : strstr(pszAccess, "a") == nullptr && poFile == nullptr)
659 : {
660 52764 : if (bSetError)
661 : {
662 6914 : VSIError(VSIE_FileError, "No such file or directory");
663 : }
664 52764 : errno = ENOENT;
665 52764 : return nullptr;
666 : }
667 :
668 : // Create.
669 158891 : if (poFile == nullptr)
670 : {
671 79267 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
672 79267 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
673 : {
674 1 : if (bSetError)
675 : {
676 0 : VSIError(VSIE_FileError,
677 : "Could not create directory %s for writing",
678 : osFileDir.c_str());
679 : }
680 1 : errno = ENOENT;
681 1 : return nullptr;
682 : }
683 :
684 79266 : poFile = std::make_shared<VSIMemFile>();
685 79266 : poFile->osFilename = osFilename;
686 79266 : oFileList[poFile->osFilename] = poFile;
687 : #ifdef DEBUG_VERBOSE
688 : CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
689 : static_cast<int>(poFile.use_count()));
690 : #endif
691 79266 : poFile->nMaxLength = nMaxLength;
692 : }
693 : // Overwrite
694 79624 : else if (strstr(pszAccess, "w"))
695 : {
696 3222 : CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
697 3222 : poFile->SetLength(0);
698 3222 : poFile->nMaxLength = nMaxLength;
699 : }
700 :
701 158890 : if (poFile->bIsDirectory)
702 : {
703 960 : errno = EISDIR;
704 960 : return nullptr;
705 : }
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Setup the file handle on this file. */
709 : /* -------------------------------------------------------------------- */
710 157930 : VSIMemHandle *poHandle = new VSIMemHandle;
711 :
712 157930 : poHandle->poFile = poFile;
713 157930 : poHandle->m_nOffset = 0;
714 157930 : poHandle->bEOF = false;
715 214287 : poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
716 56357 : strchr(pszAccess, 'a');
717 157930 : poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
718 :
719 : #ifdef DEBUG_VERBOSE
720 : CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle,
721 : pszFilename, static_cast<int>(poFile.use_count()));
722 : #endif
723 157930 : if (strchr(pszAccess, 'a'))
724 : {
725 : vsi_l_offset nOffset;
726 : {
727 52 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
728 52 : nOffset = poFile->nLength;
729 : }
730 52 : poHandle->m_nOffset = nOffset;
731 : }
732 :
733 157930 : return poHandle;
734 : }
735 :
736 : /************************************************************************/
737 : /* Stat() */
738 : /************************************************************************/
739 :
740 551662 : int VSIMemFilesystemHandler::Stat(const char *pszFilename,
741 : VSIStatBufL *pStatBuf, int /* nFlags */)
742 :
743 : {
744 1103320 : CPLMutexHolder oHolder(&hMutex);
745 :
746 1654990 : const std::string osFilename = NormalizePath(pszFilename);
747 :
748 551662 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
749 :
750 551662 : if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
751 : {
752 56566 : pStatBuf->st_size = 0;
753 56566 : pStatBuf->st_mode = S_IFDIR;
754 56566 : return 0;
755 : }
756 :
757 495096 : auto oIter = oFileList.find(osFilename);
758 495096 : if (oIter == oFileList.end())
759 : {
760 425952 : errno = ENOENT;
761 425952 : return -1;
762 : }
763 :
764 138288 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
765 :
766 69144 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
767 :
768 69144 : CPL_SHARED_LOCK oLock(poFile->m_oMutex);
769 69144 : if (poFile->bIsDirectory)
770 : {
771 36402 : pStatBuf->st_size = 0;
772 36402 : pStatBuf->st_mode = S_IFDIR;
773 : }
774 : else
775 : {
776 32742 : pStatBuf->st_size = poFile->nLength;
777 32742 : pStatBuf->st_mode = S_IFREG;
778 32742 : pStatBuf->st_mtime = poFile->mTime;
779 : }
780 :
781 69144 : return 0;
782 : }
783 :
784 : /************************************************************************/
785 : /* Unlink() */
786 : /************************************************************************/
787 :
788 78116 : int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
789 :
790 : {
791 156232 : CPLMutexHolder oHolder(&hMutex);
792 156232 : return Unlink_unlocked(pszFilename);
793 : }
794 :
795 : /************************************************************************/
796 : /* Unlink_unlocked() */
797 : /************************************************************************/
798 :
799 87960 : int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
800 :
801 : {
802 263880 : const std::string osFilename = NormalizePath(pszFilename);
803 :
804 87960 : auto oIter = oFileList.find(osFilename);
805 87960 : if (oIter == oFileList.end())
806 : {
807 17313 : errno = ENOENT;
808 17313 : return -1;
809 : }
810 :
811 : #ifdef DEBUG_VERBOSE
812 : std::shared_ptr<VSIMemFile> poFile = oIter->second;
813 : CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
814 : static_cast<int>(poFile.use_count()));
815 : #endif
816 70647 : oFileList.erase(oIter);
817 :
818 70647 : return 0;
819 : }
820 :
821 : /************************************************************************/
822 : /* Mkdir() */
823 : /************************************************************************/
824 :
825 93034 : int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
826 :
827 : {
828 186068 : CPLMutexHolder oHolder(&hMutex);
829 :
830 279102 : const std::string osPathname = NormalizePath(pszPathname);
831 93034 : if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
832 : {
833 88466 : if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
834 44080 : return 0;
835 : // "/vsimem/.#!HIDDEN!#./{unique_counter}"
836 44386 : else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
837 : std::string::npos)
838 44080 : return 0;
839 :
840 : // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
841 : // accept creating an explicit directory
842 : }
843 :
844 4874 : if (oFileList.find(osPathname) != oFileList.end())
845 : {
846 147 : errno = EEXIST;
847 147 : return -1;
848 : }
849 :
850 4727 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
851 4727 : poFile->osFilename = osPathname;
852 4727 : poFile->bIsDirectory = true;
853 4727 : oFileList[osPathname] = poFile;
854 : #ifdef DEBUG_VERBOSE
855 : CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
856 : static_cast<int>(poFile.use_count()));
857 : #endif
858 4727 : CPL_IGNORE_RET_VAL(poFile);
859 4727 : return 0;
860 : }
861 :
862 : /************************************************************************/
863 : /* Rmdir() */
864 : /************************************************************************/
865 :
866 87 : int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
867 :
868 : {
869 87 : return Unlink(pszPathname);
870 : }
871 :
872 : /************************************************************************/
873 : /* RmdirRecursive() */
874 : /************************************************************************/
875 :
876 2881 : int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
877 : {
878 5762 : CPLMutexHolder oHolder(&hMutex);
879 :
880 5762 : const CPLString osPath = NormalizePath(pszDirname);
881 2881 : const size_t nPathLen = osPath.size();
882 2881 : int ret = 0;
883 2881 : if (osPath == "/vsimem")
884 : {
885 : // Clean-up all files under pszDirname, except hidden directories
886 : // if called from "/vsimem"
887 8 : for (auto iter = oFileList.begin(); iter != oFileList.end();
888 : /* no automatic increment */)
889 : {
890 6 : const char *pszFilePath = iter->second->osFilename.c_str();
891 6 : const size_t nFileLen = iter->second->osFilename.size();
892 12 : if (nFileLen > nPathLen &&
893 6 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
894 18 : pszFilePath[nPathLen] == '/' &&
895 6 : !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
896 : {
897 2 : iter = oFileList.erase(iter);
898 : }
899 : else
900 : {
901 4 : ++iter;
902 : }
903 : }
904 : }
905 : else
906 : {
907 2879 : ret = -1;
908 408437 : for (auto iter = oFileList.begin(); iter != oFileList.end();
909 : /* no automatic increment */)
910 : {
911 405558 : const char *pszFilePath = iter->second->osFilename.c_str();
912 405558 : const size_t nFileLen = iter->second->osFilename.size();
913 527323 : if (nFileLen >= nPathLen &&
914 417298 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
915 11740 : (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
916 : {
917 : // If VSIRmdirRecursive() is used correctly, it should at
918 : // least delete the directory on which it has been called
919 14513 : ret = 0;
920 14513 : iter = oFileList.erase(iter);
921 : }
922 : else
923 : {
924 391045 : ++iter;
925 : }
926 : }
927 :
928 : // Make sure that it always succeed on the root hidden directory
929 2879 : if (osPath == szHIDDEN_DIRNAME)
930 3 : ret = 0;
931 : }
932 5762 : return ret;
933 : }
934 :
935 : /************************************************************************/
936 : /* ReadDirEx() */
937 : /************************************************************************/
938 :
939 26354 : char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
940 :
941 : {
942 52708 : CPLMutexHolder oHolder(&hMutex);
943 :
944 52708 : const CPLString osPath = NormalizePath(pszPath);
945 :
946 26354 : char **papszDir = nullptr;
947 26354 : const size_t nPathLen = osPath.size();
948 :
949 : // In case of really big number of files in the directory, CSLAddString
950 : // can be slow (see #2158). We then directly build the list.
951 26354 : int nItems = 0;
952 26354 : int nAllocatedItems = 0;
953 :
954 26354 : if (osPath == szHIDDEN_DIRNAME)
955 : {
956 : // Special mode for hidden filenames.
957 : // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
958 : // created so they do not appear in oFileList, but their subcontent
959 : // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
960 20 : std::set<std::string> oSetSubDirs;
961 161 : for (const auto &iter : oFileList)
962 : {
963 151 : const char *pszFilePath = iter.second->osFilename.c_str();
964 290 : if (iter.second->osFilename.size() > nPathLen &&
965 139 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
966 : {
967 15 : char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
968 15 : char *pszSlash = strchr(pszItem, '/');
969 15 : if (pszSlash)
970 15 : *pszSlash = 0;
971 15 : if (cpl::contains(oSetSubDirs, pszItem))
972 : {
973 2 : CPLFree(pszItem);
974 2 : continue;
975 : }
976 13 : oSetSubDirs.insert(pszItem);
977 :
978 13 : if (nItems == 0)
979 : {
980 : papszDir =
981 6 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
982 6 : nAllocatedItems = 1;
983 : }
984 7 : else if (nItems >= nAllocatedItems)
985 : {
986 7 : nAllocatedItems = nAllocatedItems * 2;
987 7 : papszDir = static_cast<char **>(CPLRealloc(
988 7 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
989 : }
990 :
991 13 : papszDir[nItems] = pszItem;
992 13 : papszDir[nItems + 1] = nullptr;
993 :
994 13 : nItems++;
995 13 : if (nMaxFiles > 0 && nItems > nMaxFiles)
996 0 : break;
997 : }
998 : }
999 : }
1000 : else
1001 : {
1002 10521200 : for (const auto &iter : oFileList)
1003 : {
1004 10494900 : const char *pszFilePath = iter.second->osFilename.c_str();
1005 10494900 : if (iter.second->osFilename.size() > nPathLen &&
1006 6839510 : memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
1007 20513000 : pszFilePath[nPathLen] == '/' &&
1008 3178660 : strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
1009 : {
1010 390252 : if (nItems == 0)
1011 : {
1012 : papszDir =
1013 24739 : static_cast<char **>(CPLCalloc(2, sizeof(char *)));
1014 24739 : nAllocatedItems = 1;
1015 : }
1016 365513 : else if (nItems >= nAllocatedItems)
1017 : {
1018 48953 : nAllocatedItems = nAllocatedItems * 2;
1019 48953 : papszDir = static_cast<char **>(CPLRealloc(
1020 48953 : papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1021 : }
1022 :
1023 390252 : papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
1024 390252 : papszDir[nItems + 1] = nullptr;
1025 :
1026 390252 : nItems++;
1027 390252 : if (nMaxFiles > 0 && nItems > nMaxFiles)
1028 1 : break;
1029 : }
1030 : }
1031 : }
1032 :
1033 52708 : return papszDir;
1034 : }
1035 :
1036 : /************************************************************************/
1037 : /* Rename() */
1038 : /************************************************************************/
1039 :
1040 171 : int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
1041 : const char *pszNewPath)
1042 :
1043 : {
1044 342 : CPLMutexHolder oHolder(&hMutex);
1045 :
1046 513 : const std::string osOldPath = NormalizePath(pszOldPath);
1047 513 : const std::string osNewPath = NormalizePath(pszNewPath);
1048 171 : if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
1049 3 : return -1;
1050 :
1051 168 : if (osOldPath.compare(osNewPath) == 0)
1052 0 : return 0;
1053 :
1054 168 : if (oFileList.find(osOldPath) == oFileList.end())
1055 : {
1056 0 : errno = ENOENT;
1057 0 : return -1;
1058 : }
1059 :
1060 : std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
1061 168 : oFileList.find(osOldPath);
1062 528 : while (it != oFileList.end() && it->first.find(osOldPath) == 0)
1063 : {
1064 720 : const std::string osRemainder = it->first.substr(osOldPath.size());
1065 360 : if (osRemainder.empty() || osRemainder[0] == '/')
1066 : {
1067 462 : const std::string osNewFullPath = osNewPath + osRemainder;
1068 231 : Unlink_unlocked(osNewFullPath.c_str());
1069 231 : oFileList[osNewFullPath] = it->second;
1070 231 : it->second->osFilename = osNewFullPath;
1071 231 : oFileList.erase(it++);
1072 : }
1073 : else
1074 : {
1075 129 : ++it;
1076 : }
1077 : }
1078 :
1079 168 : return 0;
1080 : }
1081 :
1082 : /************************************************************************/
1083 : /* NormalizePath() */
1084 : /************************************************************************/
1085 :
1086 1019250 : std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1087 : {
1088 2038490 : CPLString s(in);
1089 1019240 : std::replace(s.begin(), s.end(), '\\', '/');
1090 1019250 : s.replaceAll("//", '/');
1091 1019230 : if (!s.empty() && s.back() == '/')
1092 1616 : s.pop_back();
1093 : #if __GNUC__ >= 13
1094 : // gcc 13 complains about below explicit std::move()
1095 : return s;
1096 : #else
1097 : // Android NDK (and probably other compilers) warn about
1098 : // "warning: local variable 's' will be copied despite being returned by name [-Wreturn-std-move]"
1099 : // if not specifying std::move()
1100 2038490 : return std::move(s);
1101 : #endif
1102 : }
1103 :
1104 : /************************************************************************/
1105 : /* GetDiskFreeSpace() */
1106 : /************************************************************************/
1107 :
1108 70 : GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
1109 : {
1110 70 : const GIntBig nRet = CPLGetUsablePhysicalRAM();
1111 70 : if (nRet <= 0)
1112 0 : return -1;
1113 70 : return nRet;
1114 : }
1115 :
1116 : //! @endcond
1117 :
1118 : /************************************************************************/
1119 : /* VSIInstallMemFileHandler() */
1120 : /************************************************************************/
1121 :
1122 : /**
1123 : * \brief Install "memory" file system handler.
1124 : *
1125 : * A special file handler is installed that allows block of memory to be
1126 : * treated as files. All portions of the file system underneath the base
1127 : * path "/vsimem/" will be handled by this driver.
1128 : *
1129 : * Normal VSI*L functions can be used freely to create and destroy memory
1130 : * arrays treating them as if they were real file system objects. Some
1131 : * additional methods exist to efficient create memory file system objects
1132 : * without duplicating original copies of the data or to "steal" the block
1133 : * of memory associated with a memory file.
1134 : *
1135 : * Directory related functions are supported.
1136 : *
1137 : * This code example demonstrates using GDAL to translate from one memory
1138 : * buffer to another.
1139 : *
1140 : * \code
1141 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
1142 : * vsi_l_offset *pnOutDataLength )
1143 : * {
1144 : * // create memory file system object from buffer.
1145 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
1146 : * nInDataLength, FALSE ) );
1147 : *
1148 : * // Open memory buffer for read.
1149 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
1150 : *
1151 : * // Get output format driver.
1152 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1153 : * GDALDatasetH hOutDS;
1154 : *
1155 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
1156 : * NULL, NULL );
1157 : *
1158 : * // close source file, and "unlink" it.
1159 : * GDALClose( hDS );
1160 : * VSIUnlink( "/vsimem/work.dat" );
1161 : *
1162 : * // seize the buffer associated with the output file.
1163 : *
1164 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
1165 : * }
1166 : * \endcode
1167 : */
1168 :
1169 1392 : void VSIInstallMemFileHandler()
1170 : {
1171 1392 : VSIFileManager::InstallHandler("/vsimem/",
1172 1392 : new VSIMemFilesystemHandler("/vsimem/"));
1173 1392 : }
1174 :
1175 : /************************************************************************/
1176 : /* VSIFileFromMemBuffer() */
1177 : /************************************************************************/
1178 :
1179 : /**
1180 : * \brief Create memory "file" from a buffer.
1181 : *
1182 : * A virtual memory file is created from the passed buffer with the indicated
1183 : * filename. Under normal conditions the filename would need to be absolute
1184 : * and within the /vsimem/ portion of the filesystem.
1185 : * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
1186 : * an anonymous file, that is destroyed when the handle is closed.
1187 : *
1188 : * If bTakeOwnership is TRUE, then the memory file system handler will take
1189 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
1190 : * it remains the responsibility of the caller, but should not be freed as
1191 : * long as it might be accessed as a file. In no circumstances does this
1192 : * function take a copy of the pabyData contents.
1193 : *
1194 : * @param pszFilename the filename to be created, or nullptr
1195 : * @param pabyData the data buffer for the file.
1196 : * @param nDataLength the length of buffer in bytes.
1197 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
1198 : *
1199 : * @return open file handle on created file (see VSIFOpenL()).
1200 : */
1201 :
1202 10336 : VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
1203 : vsi_l_offset nDataLength, int bTakeOwnership)
1204 :
1205 : {
1206 10336 : if (VSIFileManager::GetHandler("") ==
1207 10336 : VSIFileManager::GetHandler("/vsimem/"))
1208 0 : VSIInstallMemFileHandler();
1209 :
1210 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1211 10336 : VSIFileManager::GetHandler("/vsimem/"));
1212 :
1213 : const CPLString osFilename =
1214 30286 : pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
1215 20671 : : std::string();
1216 10336 : if (osFilename == "/vsimem/")
1217 : {
1218 1 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1219 : pszFilename);
1220 1 : return nullptr;
1221 : }
1222 :
1223 : // Try to create the parent directory, if needed, before taking
1224 : // ownership of pabyData.
1225 10334 : if (!osFilename.empty())
1226 : {
1227 9612 : const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
1228 9613 : if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
1229 : {
1230 0 : VSIError(VSIE_FileError,
1231 : "Could not create directory %s for writing",
1232 : osFileDir.c_str());
1233 0 : errno = ENOENT;
1234 0 : return nullptr;
1235 : }
1236 : }
1237 :
1238 10335 : std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
1239 :
1240 10335 : poFile->osFilename = osFilename;
1241 10335 : poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
1242 10335 : poFile->pabyData = pabyData;
1243 10335 : poFile->nLength = nDataLength;
1244 10335 : poFile->nAllocLength = nDataLength;
1245 :
1246 10335 : if (!osFilename.empty())
1247 : {
1248 19226 : CPLMutexHolder oHolder(&poHandler->hMutex);
1249 9613 : poHandler->Unlink_unlocked(osFilename);
1250 9613 : poHandler->oFileList[poFile->osFilename] = poFile;
1251 : #ifdef DEBUG_VERBOSE
1252 : CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
1253 : poFile->osFilename.c_str(),
1254 : static_cast<int>(poFile.use_count()));
1255 : #endif
1256 : }
1257 :
1258 : /* -------------------------------------------------------------------- */
1259 : /* Setup the file handle on this file. */
1260 : /* -------------------------------------------------------------------- */
1261 10335 : VSIMemHandle *poHandle = new VSIMemHandle;
1262 :
1263 10335 : poHandle->poFile = std::move(poFile);
1264 10335 : poHandle->bUpdate = true;
1265 10335 : poHandle->m_bReadAllowed = true;
1266 10335 : return poHandle;
1267 : }
1268 :
1269 : /************************************************************************/
1270 : /* VSIGetMemFileBuffer() */
1271 : /************************************************************************/
1272 :
1273 : /**
1274 : * \brief Fetch buffer underlying memory file.
1275 : *
1276 : * This function returns a pointer to the memory buffer underlying a
1277 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
1278 : * object will be deleted, and ownership of the buffer will pass to the
1279 : * caller otherwise the underlying file will remain in existence.
1280 : *
1281 : * @param pszFilename the name of the file to grab the buffer of.
1282 : * @param pnDataLength (file) length returned in this variable.
1283 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
1284 : *
1285 : * @return pointer to memory buffer or NULL on failure.
1286 : */
1287 :
1288 35749 : GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
1289 : int bUnlinkAndSeize)
1290 :
1291 : {
1292 : VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1293 35749 : VSIFileManager::GetHandler("/vsimem/"));
1294 :
1295 35750 : if (pszFilename == nullptr)
1296 0 : return nullptr;
1297 :
1298 : const std::string osFilename =
1299 107247 : VSIMemFilesystemHandler::NormalizePath(pszFilename);
1300 :
1301 71483 : CPLMutexHolder oHolder(&poHandler->hMutex);
1302 :
1303 35751 : if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
1304 126 : return nullptr;
1305 :
1306 35625 : std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
1307 35625 : GByte *pabyData = poFile->pabyData;
1308 35625 : if (pnDataLength != nullptr)
1309 32927 : *pnDataLength = poFile->nLength;
1310 :
1311 35625 : if (bUnlinkAndSeize)
1312 : {
1313 4407 : if (!poFile->bOwnData)
1314 0 : CPLDebug("VSIMemFile",
1315 : "File doesn't own data in VSIGetMemFileBuffer!");
1316 : else
1317 4407 : poFile->bOwnData = false;
1318 :
1319 4407 : poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
1320 : #ifdef DEBUG_VERBOSE
1321 : CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
1322 : poFile->osFilename.c_str(),
1323 : static_cast<int>(poFile.use_count()));
1324 : #endif
1325 4407 : poFile->pabyData = nullptr;
1326 4407 : poFile->nLength = 0;
1327 4407 : poFile->nAllocLength = 0;
1328 : }
1329 :
1330 35625 : return pabyData;
1331 : }
1332 :
1333 : /************************************************************************/
1334 : /* VSIMemGenerateHiddenFilename() */
1335 : /************************************************************************/
1336 :
1337 : /**
1338 : * \brief Generates a unique filename that can be used with the /vsimem/
1339 : * virtual file system.
1340 : *
1341 : * This function returns a (short-lived) string containing a unique filename,
1342 : * (using an atomic counter), designed for temporary files that must remain
1343 : * invisible for other users working at the "/vsimem/" top-level, i.e.
1344 : * such files are not returned by VSIReadDir("/vsimem/") or
1345 : * VSIReadDirRecursive("/vsimem/)".
1346 : *
1347 : * The function does not create the file per se. Such filename can be used to
1348 : * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
1349 : * a directory with VSIMkdir()
1350 : *
1351 : * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
1352 : * etc. is of the responsibility of the user. The user should not attempt to
1353 : * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
1354 : * on the returned filename, and work only with files at the same level or
1355 : * in subdirectories of what is returned by this function.
1356 : *
1357 : * @param pszFilename the filename to be appended at the end of the returned
1358 : * filename. If not specified, defaults to "unnamed".
1359 : *
1360 : * @return pointer to a short-lived string (rotating buffer of strings in
1361 : * thread-local storage). It is recommended to use CPLStrdup() or std::string()
1362 : * immediately on it.
1363 : *
1364 : * @since GDAL 3.10
1365 : */
1366 43343 : const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
1367 : {
1368 : static std::atomic<uint32_t> nCounter{0};
1369 43343 : return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
1370 43332 : pszFilename ? pszFilename : "unnamed");
1371 : }
|