Coverage Report

Created: 2025-08-28 06:57

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