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