Coverage Report

Created: 2025-11-15 08:43

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