Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cpl_vsi_mem.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#define CPL_SHARED_LOCK std::shared_lock<std::shared_mutex>
44
0
#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
0
    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
    bool HasPRead() const override
177
0
    {
178
0
        return true;
179
0
    }
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
    explicit VSIMemFilesystemHandler(const char *pszPrefix)
201
1
        : m_osPrefix(pszPrefix)
202
1
    {
203
1
    }
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
    VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
229
0
    {
230
0
        return new VSIMemFilesystemHandler(pszPrefix);
231
0
    }
232
};
233
234
/************************************************************************/
235
/* ==================================================================== */
236
/*                              VSIMemFile                              */
237
/* ==================================================================== */
238
/************************************************************************/
239
240
/************************************************************************/
241
/*                             VSIMemFile()                             */
242
/************************************************************************/
243
244
VSIMemFile::VSIMemFile()
245
0
{
246
0
    time(&mTime);
247
0
}
248
249
/************************************************************************/
250
/*                            ~VSIMemFile()                             */
251
/************************************************************************/
252
253
VSIMemFile::~VSIMemFile()
254
0
{
255
0
    if (bOwnData && pabyData)
256
0
        CPLFree(pabyData);
257
0
}
258
259
/************************************************************************/
260
/*                             SetLength()                              */
261
/************************************************************************/
262
263
// Must be called under exclusive lock
264
bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
265
266
0
{
267
0
    if (nNewLength > nMaxLength)
268
0
    {
269
0
        CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
270
0
        return false;
271
0
    }
272
273
    /* -------------------------------------------------------------------- */
274
    /*      Grow underlying array if needed.                                */
275
    /* -------------------------------------------------------------------- */
276
0
    if (nNewLength > nAllocLength)
277
0
    {
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
0
        if (!bOwnData)
283
0
        {
284
0
            CPLError(CE_Failure, CPLE_NotSupported,
285
0
                     "Cannot extended in-memory file whose ownership was not "
286
0
                     "transferred");
287
0
            return false;
288
0
        }
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
0
        const vsi_l_offset nNewAlloc =
294
0
            (nAllocLength == 0 && nNewLength >= 1024 * 1024)
295
0
                ? nNewLength
296
0
                : nNewLength + nNewLength / 10 + 5000;
297
0
        GByte *pabyNewData = nullptr;
298
0
        if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
299
0
            nNewAlloc)
300
0
        {
301
0
            pabyNewData = static_cast<GByte *>(
302
0
                nAllocLength == 0
303
0
                    ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
304
0
                    : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
305
0
        }
306
0
        if (pabyNewData == nullptr)
307
0
        {
308
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
309
0
                     "Cannot extend in-memory file to " CPL_FRMT_GUIB
310
0
                     " bytes due to out-of-memory situation",
311
0
                     nNewAlloc);
312
0
            return false;
313
0
        }
314
315
0
        if (nAllocLength > 0)
316
0
        {
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
0
            memset(pabyNewData + nAllocLength, 0,
321
0
                   static_cast<size_t>(nNewAlloc - nAllocLength));
322
0
        }
323
324
0
        pabyData = pabyNewData;
325
0
        nAllocLength = nNewAlloc;
326
0
    }
327
0
    else if (nNewLength < nLength)
328
0
    {
329
0
        memset(pabyData + nNewLength, 0,
330
0
               static_cast<size_t>(nLength - nNewLength));
331
0
    }
332
333
0
    nLength = nNewLength;
334
0
    time(&mTime);
335
336
0
    return true;
337
0
}
338
339
/************************************************************************/
340
/* ==================================================================== */
341
/*                             VSIMemHandle                             */
342
/* ==================================================================== */
343
/************************************************************************/
344
345
/************************************************************************/
346
/*                            ~VSIMemHandle()                           */
347
/************************************************************************/
348
349
VSIMemHandle::~VSIMemHandle()
350
0
{
351
0
    VSIMemHandle::Close();
352
0
}
353
354
/************************************************************************/
355
/*                               Close()                                */
356
/************************************************************************/
357
358
int VSIMemHandle::Close()
359
360
0
{
361
0
    if (poFile)
362
0
    {
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
0
        poFile = nullptr;
369
0
    }
370
371
0
    return 0;
372
0
}
373
374
/************************************************************************/
375
/*                                Seek()                                */
376
/************************************************************************/
377
378
int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
379
380
0
{
381
0
    vsi_l_offset nLength;
382
0
    {
383
0
        CPL_SHARED_LOCK oLock(poFile->m_oMutex);
384
0
        nLength = poFile->nLength;
385
0
    }
386
387
0
    if (nWhence == SEEK_CUR)
388
0
    {
389
0
        if (nOffset > INT_MAX)
390
0
        {
391
            // printf("likely negative offset intended\n");
392
0
        }
393
0
        m_nOffset += nOffset;
394
0
    }
395
0
    else if (nWhence == SEEK_SET)
396
0
    {
397
0
        m_nOffset = nOffset;
398
0
    }
399
0
    else if (nWhence == SEEK_END)
400
0
    {
401
0
        m_nOffset = nLength + nOffset;
402
0
    }
403
0
    else
404
0
    {
405
0
        errno = EINVAL;
406
0
        return -1;
407
0
    }
408
409
0
    bEOF = false;
410
411
0
    return 0;
412
0
}
413
414
/************************************************************************/
415
/*                                Tell()                                */
416
/************************************************************************/
417
418
vsi_l_offset VSIMemHandle::Tell()
419
420
0
{
421
0
    return m_nOffset;
422
0
}
423
424
/************************************************************************/
425
/*                                Read()                                */
426
/************************************************************************/
427
428
size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
429
430
0
{
431
0
    const vsi_l_offset nOffset = m_nOffset;
432
433
0
    size_t nBytesToRead = nSize * nCount;
434
0
    if (nBytesToRead == 0)
435
0
        return 0;
436
437
0
    if (nCount > 0 && nBytesToRead / nCount != nSize)
438
0
    {
439
0
        bEOF = true;
440
0
        return 0;
441
0
    }
442
443
0
    if (!m_bReadAllowed)
444
0
    {
445
0
        m_bError = true;
446
0
        return 0;
447
0
    }
448
449
0
    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
0
    const auto DoUnderLock =
453
0
        [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
454
0
    {
455
0
        CPL_SHARED_LOCK oLock(poFile->m_oMutex);
456
457
0
        if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
458
0
        {
459
0
            bEOFTmp = true;
460
0
            return false;
461
0
        }
462
0
        if (nBytesToRead + nOffset > poFile->nLength)
463
0
        {
464
0
            nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
465
0
            nCount = nBytesToRead / nSize;
466
0
            bEOFTmp = true;
467
0
        }
468
469
0
        if (nBytesToRead)
470
0
            memcpy(pBuffer, poFile->pabyData + nOffset,
471
0
                   static_cast<size_t>(nBytesToRead));
472
0
        return true;
473
0
    };
474
475
0
    bool bRet = DoUnderLock();
476
0
    bEOF = bEOFTmp;
477
0
    if (!bRet)
478
0
        return 0;
479
480
0
    m_nOffset += nBytesToRead;
481
482
0
    return nCount;
483
0
}
484
485
/************************************************************************/
486
/*                              PRead()                                 */
487
/************************************************************************/
488
489
size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
490
                           vsi_l_offset nOffset) const
491
0
{
492
0
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
493
494
0
    if (nOffset < poFile->nLength)
495
0
    {
496
0
        const size_t nToCopy = static_cast<size_t>(
497
0
            std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
498
0
                     static_cast<vsi_l_offset>(nSize)));
499
0
        memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
500
0
               nToCopy);
501
0
        return nToCopy;
502
0
    }
503
0
    return 0;
504
0
}
505
506
/************************************************************************/
507
/*                               Write()                                */
508
/************************************************************************/
509
510
size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
511
512
0
{
513
0
    const vsi_l_offset nOffset = m_nOffset;
514
515
0
    if (!bUpdate)
516
0
    {
517
0
        errno = EACCES;
518
0
        return 0;
519
0
    }
520
521
0
    const size_t nBytesToWrite = nSize * nCount;
522
523
0
    {
524
0
        CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
525
526
0
        if (nCount > 0 && nBytesToWrite / nCount != nSize)
527
0
        {
528
0
            return 0;
529
0
        }
530
0
        if (nBytesToWrite + nOffset < nBytesToWrite)
531
0
        {
532
0
            return 0;
533
0
        }
534
535
0
        if (nBytesToWrite + nOffset > poFile->nLength)
536
0
        {
537
0
            if (!poFile->SetLength(nBytesToWrite + nOffset))
538
0
                return 0;
539
0
        }
540
541
0
        if (nBytesToWrite)
542
0
            memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
543
544
0
        time(&poFile->mTime);
545
0
    }
546
547
0
    m_nOffset += nBytesToWrite;
548
549
0
    return nCount;
550
0
}
551
552
/************************************************************************/
553
/*                             ClearErr()                               */
554
/************************************************************************/
555
556
void VSIMemHandle::ClearErr()
557
558
0
{
559
0
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
560
0
    bEOF = false;
561
0
    m_bError = false;
562
0
}
563
564
/************************************************************************/
565
/*                              Error()                                 */
566
/************************************************************************/
567
568
int VSIMemHandle::Error()
569
570
0
{
571
0
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
572
0
    return m_bError ? TRUE : FALSE;
573
0
}
574
575
/************************************************************************/
576
/*                                Eof()                                 */
577
/************************************************************************/
578
579
int VSIMemHandle::Eof()
580
581
0
{
582
0
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
583
0
    return bEOF ? TRUE : FALSE;
584
0
}
585
586
/************************************************************************/
587
/*                             Truncate()                               */
588
/************************************************************************/
589
590
int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
591
0
{
592
0
    if (!bUpdate)
593
0
    {
594
0
        errno = EACCES;
595
0
        return -1;
596
0
    }
597
598
0
    CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
599
0
    if (poFile->SetLength(nNewSize))
600
0
        return 0;
601
602
0
    return -1;
603
0
}
604
605
/************************************************************************/
606
/* ==================================================================== */
607
/*                       VSIMemFilesystemHandler                        */
608
/* ==================================================================== */
609
/************************************************************************/
610
611
/************************************************************************/
612
/*                      ~VSIMemFilesystemHandler()                      */
613
/************************************************************************/
614
615
VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
616
617
0
{
618
0
    oFileList.clear();
619
620
0
    if (hMutex != nullptr)
621
0
        CPLDestroyMutex(hMutex);
622
0
    hMutex = nullptr;
623
0
}
624
625
/************************************************************************/
626
/*                                Open()                                */
627
/************************************************************************/
628
629
VSIVirtualHandle *VSIMemFilesystemHandler::Open(const char *pszFilename,
630
                                                const char *pszAccess,
631
                                                bool bSetError,
632
                                                CSLConstList /* papszOptions */)
633
634
0
{
635
0
    CPLMutexHolder oHolder(&hMutex);
636
0
    const std::string osFilename = NormalizePath(pszFilename);
637
0
    if (osFilename.empty())
638
0
        return nullptr;
639
640
0
    vsi_l_offset nMaxLength = GUINTBIG_MAX;
641
0
    const size_t iPos = osFilename.find("||maxlength=");
642
0
    if (iPos != std::string::npos)
643
0
    {
644
0
        nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
645
0
            osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
646
0
    }
647
648
    /* -------------------------------------------------------------------- */
649
    /*      Get the filename we are opening, create if needed.              */
650
    /* -------------------------------------------------------------------- */
651
0
    std::shared_ptr<VSIMemFile> poFile = nullptr;
652
0
    const auto oIter = oFileList.find(osFilename);
653
0
    if (oIter != oFileList.end())
654
0
    {
655
0
        poFile = oIter->second;
656
0
    }
657
658
    // If no file and opening in read, error out.
659
0
    if (strstr(pszAccess, "w") == nullptr &&
660
0
        strstr(pszAccess, "a") == nullptr && poFile == nullptr)
661
0
    {
662
0
        if (bSetError)
663
0
        {
664
0
            VSIError(VSIE_FileError, "No such file or directory");
665
0
        }
666
0
        errno = ENOENT;
667
0
        return nullptr;
668
0
    }
669
670
    // Create.
671
0
    if (poFile == nullptr)
672
0
    {
673
0
        const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
674
0
        if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
675
0
        {
676
0
            if (bSetError)
677
0
            {
678
0
                VSIError(VSIE_FileError,
679
0
                         "Could not create directory %s for writing",
680
0
                         osFileDir.c_str());
681
0
            }
682
0
            errno = ENOENT;
683
0
            return nullptr;
684
0
        }
685
686
0
        poFile = std::make_shared<VSIMemFile>();
687
0
        poFile->osFilename = osFilename;
688
0
        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
0
        poFile->nMaxLength = nMaxLength;
694
0
    }
695
    // Overwrite
696
0
    else if (strstr(pszAccess, "w"))
697
0
    {
698
0
        CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
699
0
        poFile->SetLength(0);
700
0
        poFile->nMaxLength = nMaxLength;
701
0
    }
702
703
0
    if (poFile->bIsDirectory)
704
0
    {
705
0
        errno = EISDIR;
706
0
        return nullptr;
707
0
    }
708
709
    /* -------------------------------------------------------------------- */
710
    /*      Setup the file handle on this file.                             */
711
    /* -------------------------------------------------------------------- */
712
0
    VSIMemHandle *poHandle = new VSIMemHandle;
713
714
0
    poHandle->poFile = poFile;
715
0
    poHandle->m_nOffset = 0;
716
0
    poHandle->bEOF = false;
717
0
    poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
718
0
                        strchr(pszAccess, 'a');
719
0
    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
0
    if (strchr(pszAccess, 'a'))
726
0
    {
727
0
        vsi_l_offset nOffset;
728
0
        {
729
0
            CPL_SHARED_LOCK oLock(poFile->m_oMutex);
730
0
            nOffset = poFile->nLength;
731
0
        }
732
0
        poHandle->m_nOffset = nOffset;
733
0
    }
734
735
0
    return poHandle;
736
0
}
737
738
/************************************************************************/
739
/*                                Stat()                                */
740
/************************************************************************/
741
742
int VSIMemFilesystemHandler::Stat(const char *pszFilename,
743
                                  VSIStatBufL *pStatBuf, int /* nFlags */)
744
745
0
{
746
0
    CPLMutexHolder oHolder(&hMutex);
747
748
0
    const std::string osFilename = NormalizePath(pszFilename);
749
750
0
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
751
752
0
    if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
753
0
    {
754
0
        pStatBuf->st_size = 0;
755
0
        pStatBuf->st_mode = S_IFDIR;
756
0
        return 0;
757
0
    }
758
759
0
    auto oIter = oFileList.find(osFilename);
760
0
    if (oIter == oFileList.end())
761
0
    {
762
0
        errno = ENOENT;
763
0
        return -1;
764
0
    }
765
766
0
    std::shared_ptr<VSIMemFile> poFile = oIter->second;
767
768
0
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
769
770
0
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
771
0
    if (poFile->bIsDirectory)
772
0
    {
773
0
        pStatBuf->st_size = 0;
774
0
        pStatBuf->st_mode = S_IFDIR;
775
0
    }
776
0
    else
777
0
    {
778
0
        pStatBuf->st_size = poFile->nLength;
779
0
        pStatBuf->st_mode = S_IFREG;
780
0
        if (const char *mtime =
781
0
                CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
782
0
        {
783
0
            pStatBuf->st_mtime =
784
0
                static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
785
0
        }
786
0
        else
787
0
        {
788
0
            pStatBuf->st_mtime = poFile->mTime;
789
0
        }
790
0
    }
791
792
0
    return 0;
793
0
}
794
795
/************************************************************************/
796
/*                               Unlink()                               */
797
/************************************************************************/
798
799
int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
800
801
0
{
802
0
    CPLMutexHolder oHolder(&hMutex);
803
0
    return Unlink_unlocked(pszFilename);
804
0
}
805
806
/************************************************************************/
807
/*                           Unlink_unlocked()                          */
808
/************************************************************************/
809
810
int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
811
812
0
{
813
0
    const std::string osFilename = NormalizePath(pszFilename);
814
815
0
    auto oIter = oFileList.find(osFilename);
816
0
    if (oIter == oFileList.end())
817
0
    {
818
0
        errno = ENOENT;
819
0
        return -1;
820
0
    }
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
0
    oFileList.erase(oIter);
828
829
0
    return 0;
830
0
}
831
832
/************************************************************************/
833
/*                               Mkdir()                                */
834
/************************************************************************/
835
836
int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
837
838
0
{
839
0
    CPLMutexHolder oHolder(&hMutex);
840
841
0
    const std::string osPathname = NormalizePath(pszPathname);
842
0
    if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
843
0
    {
844
0
        if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
845
0
            return 0;
846
        // "/vsimem/.#!HIDDEN!#./{unique_counter}"
847
0
        else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
848
0
                 std::string::npos)
849
0
            return 0;
850
851
        // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
852
        // accept creating an explicit directory
853
0
    }
854
855
0
    if (oFileList.find(osPathname) != oFileList.end())
856
0
    {
857
0
        errno = EEXIST;
858
0
        return -1;
859
0
    }
860
861
0
    std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
862
0
    poFile->osFilename = osPathname;
863
0
    poFile->bIsDirectory = true;
864
0
    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
0
    CPL_IGNORE_RET_VAL(poFile);
870
0
    return 0;
871
0
}
872
873
/************************************************************************/
874
/*                               Rmdir()                                */
875
/************************************************************************/
876
877
int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
878
879
0
{
880
0
    return Unlink(pszPathname);
881
0
}
882
883
/************************************************************************/
884
/*                          RmdirRecursive()                            */
885
/************************************************************************/
886
887
int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
888
0
{
889
0
    CPLMutexHolder oHolder(&hMutex);
890
891
0
    const CPLString osPath = NormalizePath(pszDirname);
892
0
    const size_t nPathLen = osPath.size();
893
0
    int ret = 0;
894
0
    if (osPath == "/vsimem")
895
0
    {
896
        // Clean-up all files under pszDirname, except hidden directories
897
        // if called from "/vsimem"
898
0
        for (auto iter = oFileList.begin(); iter != oFileList.end();
899
0
             /* no automatic increment */)
900
0
        {
901
0
            const char *pszFilePath = iter->second->osFilename.c_str();
902
0
            const size_t nFileLen = iter->second->osFilename.size();
903
0
            if (nFileLen > nPathLen &&
904
0
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
905
0
                pszFilePath[nPathLen] == '/' &&
906
0
                !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
907
0
            {
908
0
                iter = oFileList.erase(iter);
909
0
            }
910
0
            else
911
0
            {
912
0
                ++iter;
913
0
            }
914
0
        }
915
0
    }
916
0
    else
917
0
    {
918
0
        ret = -1;
919
0
        for (auto iter = oFileList.begin(); iter != oFileList.end();
920
0
             /* no automatic increment */)
921
0
        {
922
0
            const char *pszFilePath = iter->second->osFilename.c_str();
923
0
            const size_t nFileLen = iter->second->osFilename.size();
924
0
            if (nFileLen >= nPathLen &&
925
0
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
926
0
                (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
927
0
            {
928
                // If VSIRmdirRecursive() is used correctly, it should at
929
                // least delete the directory on which it has been called
930
0
                ret = 0;
931
0
                iter = oFileList.erase(iter);
932
0
            }
933
0
            else
934
0
            {
935
0
                ++iter;
936
0
            }
937
0
        }
938
939
        // Make sure that it always succeed on the root hidden directory
940
0
        if (osPath == szHIDDEN_DIRNAME)
941
0
            ret = 0;
942
0
    }
943
0
    return ret;
944
0
}
945
946
/************************************************************************/
947
/*                             ReadDirEx()                              */
948
/************************************************************************/
949
950
char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
951
952
0
{
953
0
    CPLMutexHolder oHolder(&hMutex);
954
955
0
    const CPLString osPath = NormalizePath(pszPath);
956
957
0
    char **papszDir = nullptr;
958
0
    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
0
    int nItems = 0;
963
0
    int nAllocatedItems = 0;
964
965
0
    if (osPath == szHIDDEN_DIRNAME)
966
0
    {
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
0
        std::set<std::string> oSetSubDirs;
972
0
        for (const auto &iter : oFileList)
973
0
        {
974
0
            const char *pszFilePath = iter.second->osFilename.c_str();
975
0
            if (iter.second->osFilename.size() > nPathLen &&
976
0
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
977
0
            {
978
0
                char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
979
0
                char *pszSlash = strchr(pszItem, '/');
980
0
                if (pszSlash)
981
0
                    *pszSlash = 0;
982
0
                if (cpl::contains(oSetSubDirs, pszItem))
983
0
                {
984
0
                    CPLFree(pszItem);
985
0
                    continue;
986
0
                }
987
0
                oSetSubDirs.insert(pszItem);
988
989
0
                if (nItems == 0)
990
0
                {
991
0
                    papszDir =
992
0
                        static_cast<char **>(CPLCalloc(2, sizeof(char *)));
993
0
                    nAllocatedItems = 1;
994
0
                }
995
0
                else if (nItems >= nAllocatedItems)
996
0
                {
997
0
                    nAllocatedItems = nAllocatedItems * 2;
998
0
                    papszDir = static_cast<char **>(CPLRealloc(
999
0
                        papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1000
0
                }
1001
1002
0
                papszDir[nItems] = pszItem;
1003
0
                papszDir[nItems + 1] = nullptr;
1004
1005
0
                nItems++;
1006
0
                if (nMaxFiles > 0 && nItems > nMaxFiles)
1007
0
                    break;
1008
0
            }
1009
0
        }
1010
0
    }
1011
0
    else
1012
0
    {
1013
0
        for (const auto &iter : oFileList)
1014
0
        {
1015
0
            const char *pszFilePath = iter.second->osFilename.c_str();
1016
0
            if (iter.second->osFilename.size() > nPathLen &&
1017
0
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
1018
0
                pszFilePath[nPathLen] == '/' &&
1019
0
                strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
1020
0
            {
1021
0
                if (nItems == 0)
1022
0
                {
1023
0
                    papszDir =
1024
0
                        static_cast<char **>(CPLCalloc(2, sizeof(char *)));
1025
0
                    nAllocatedItems = 1;
1026
0
                }
1027
0
                else if (nItems >= nAllocatedItems)
1028
0
                {
1029
0
                    nAllocatedItems = nAllocatedItems * 2;
1030
0
                    papszDir = static_cast<char **>(CPLRealloc(
1031
0
                        papszDir, (nAllocatedItems + 2) * sizeof(char *)));
1032
0
                }
1033
1034
0
                papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
1035
0
                papszDir[nItems + 1] = nullptr;
1036
1037
0
                nItems++;
1038
0
                if (nMaxFiles > 0 && nItems > nMaxFiles)
1039
0
                    break;
1040
0
            }
1041
0
        }
1042
0
    }
1043
1044
0
    return papszDir;
1045
0
}
1046
1047
/************************************************************************/
1048
/*                               Rename()                               */
1049
/************************************************************************/
1050
1051
int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
1052
                                    const char *pszNewPath, GDALProgressFunc,
1053
                                    void *)
1054
1055
0
{
1056
0
    CPLMutexHolder oHolder(&hMutex);
1057
1058
0
    const std::string osOldPath = NormalizePath(pszOldPath);
1059
0
    const std::string osNewPath = NormalizePath(pszNewPath);
1060
0
    if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
1061
0
        return -1;
1062
1063
0
    if (osOldPath.compare(osNewPath) == 0)
1064
0
        return 0;
1065
1066
0
    if (oFileList.find(osOldPath) == oFileList.end())
1067
0
    {
1068
0
        errno = ENOENT;
1069
0
        return -1;
1070
0
    }
1071
1072
0
    std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
1073
0
        oFileList.find(osOldPath);
1074
0
    while (it != oFileList.end() && it->first.find(osOldPath) == 0)
1075
0
    {
1076
0
        const std::string osRemainder = it->first.substr(osOldPath.size());
1077
0
        if (osRemainder.empty() || osRemainder[0] == '/')
1078
0
        {
1079
0
            const std::string osNewFullPath = osNewPath + osRemainder;
1080
0
            Unlink_unlocked(osNewFullPath.c_str());
1081
0
            oFileList[osNewFullPath] = it->second;
1082
0
            it->second->osFilename = osNewFullPath;
1083
0
            oFileList.erase(it++);
1084
0
        }
1085
0
        else
1086
0
        {
1087
0
            ++it;
1088
0
        }
1089
0
    }
1090
1091
0
    return 0;
1092
0
}
1093
1094
/************************************************************************/
1095
/*                           NormalizePath()                            */
1096
/************************************************************************/
1097
1098
std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1099
0
{
1100
0
    CPLString s(in);
1101
0
    std::replace(s.begin(), s.end(), '\\', '/');
1102
0
    s.replaceAll("//", '/');
1103
0
    if (!s.empty() && s.back() == '/')
1104
0
        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
0
    return std::move(s);
1113
0
#endif
1114
0
}
1115
1116
/************************************************************************/
1117
/*                        GetDiskFreeSpace()                            */
1118
/************************************************************************/
1119
1120
GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
1121
0
{
1122
0
    const GIntBig nRet = CPLGetUsablePhysicalRAM();
1123
0
    if (nRet <= 0)
1124
0
        return -1;
1125
0
    return nRet;
1126
0
}
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
void VSIInstallMemFileHandler()
1182
1
{
1183
1
    VSIFileManager::InstallHandler("/vsimem/",
1184
1
                                   new VSIMemFilesystemHandler("/vsimem/"));
1185
1
}
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
VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
1215
                               vsi_l_offset nDataLength, int bTakeOwnership)
1216
1217
0
{
1218
0
    if (VSIFileManager::GetHandler("") ==
1219
0
        VSIFileManager::GetHandler("/vsimem/"))
1220
0
        VSIInstallMemFileHandler();
1221
1222
0
    VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1223
0
        VSIFileManager::GetHandler("/vsimem/"));
1224
1225
0
    const CPLString osFilename =
1226
0
        pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
1227
0
                    : std::string();
1228
0
    if (osFilename == "/vsimem/")
1229
0
    {
1230
0
        CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1231
0
                 pszFilename);
1232
0
        return nullptr;
1233
0
    }
1234
1235
    // Try to create the parent directory, if needed, before taking
1236
    // ownership of pabyData.
1237
0
    if (!osFilename.empty())
1238
0
    {
1239
0
        const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
1240
0
        if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
1241
0
        {
1242
0
            VSIError(VSIE_FileError,
1243
0
                     "Could not create directory %s for writing",
1244
0
                     osFileDir.c_str());
1245
0
            errno = ENOENT;
1246
0
            return nullptr;
1247
0
        }
1248
0
    }
1249
1250
0
    std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
1251
1252
0
    poFile->osFilename = osFilename;
1253
0
    poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
1254
0
    poFile->pabyData = pabyData;
1255
0
    poFile->nLength = nDataLength;
1256
0
    poFile->nAllocLength = nDataLength;
1257
1258
0
    if (!osFilename.empty())
1259
0
    {
1260
0
        CPLMutexHolder oHolder(&poHandler->hMutex);
1261
0
        poHandler->Unlink_unlocked(osFilename);
1262
0
        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
0
    }
1269
1270
    /* -------------------------------------------------------------------- */
1271
    /*      Setup the file handle on this file.                             */
1272
    /* -------------------------------------------------------------------- */
1273
0
    VSIMemHandle *poHandle = new VSIMemHandle;
1274
1275
0
    poHandle->poFile = std::move(poFile);
1276
0
    poHandle->bUpdate = true;
1277
0
    poHandle->m_bReadAllowed = true;
1278
0
    return poHandle;
1279
0
}
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
GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
1301
                           int bUnlinkAndSeize)
1302
1303
0
{
1304
0
    VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1305
0
        VSIFileManager::GetHandler("/vsimem/"));
1306
1307
0
    if (pszFilename == nullptr)
1308
0
        return nullptr;
1309
1310
0
    const std::string osFilename =
1311
0
        VSIMemFilesystemHandler::NormalizePath(pszFilename);
1312
1313
0
    CPLMutexHolder oHolder(&poHandler->hMutex);
1314
1315
0
    if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
1316
0
        return nullptr;
1317
1318
0
    std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
1319
0
    GByte *pabyData = poFile->pabyData;
1320
0
    if (pnDataLength != nullptr)
1321
0
        *pnDataLength = poFile->nLength;
1322
1323
0
    if (bUnlinkAndSeize)
1324
0
    {
1325
0
        if (!poFile->bOwnData)
1326
0
            CPLDebug("VSIMemFile",
1327
0
                     "File doesn't own data in VSIGetMemFileBuffer!");
1328
0
        else
1329
0
            poFile->bOwnData = false;
1330
1331
0
        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
0
        poFile->pabyData = nullptr;
1338
0
        poFile->nLength = 0;
1339
0
        poFile->nAllocLength = 0;
1340
0
    }
1341
1342
0
    return pabyData;
1343
0
}
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
const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
1379
0
{
1380
0
    static std::atomic<uint32_t> nCounter{0};
1381
0
    return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
1382
0
                      pszFilename ? pszFilename : "unnamed");
1383
0
}