Coverage Report

Created: 2026-02-14 06:52

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