• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15143862414

20 May 2025 05:20PM UTC coverage: 70.927% (+0.006%) from 70.921%
15143862414

Pull #12392

github

web-flow
Merge 6e44293c3 into 36cf82678
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

182 of 202 new or added lines in 3 files covered. (90.1%)

21204 existing lines in 67 files now uncovered.

567713 of 800420 relevant lines covered (70.93%)

235755.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.05
/port/cpl_vsi_mem.cpp
1
/******************************************************************************
2
 *
3
 * Project:  VSI Virtual File System
4
 * Purpose:  Implementation of Memory Buffer virtual IO functions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "cpl_vsi.h"
16
#include "cpl_vsi_virtual.h"
17

18
#include <cerrno>
19
#include <cstddef>
20
#include <cstdlib>
21
#include <cstring>
22
#include <ctime>
23
#if HAVE_FCNTL_H
24
#include <fcntl.h>
25
#endif
26
#if HAVE_SYS_STAT_H
27
#include <sys/stat.h>
28
#endif
29

30
#include <algorithm>
31
#include <atomic>
32
#include <map>
33
#include <string>
34
#include <utility>
35
#include <memory>
36
#include <set>
37

38
#include <mutex>
39
// c++17 or VS2017
40
#if defined(HAVE_SHARED_MUTEX) || _MSC_VER >= 1910
41
#include <shared_mutex>
42
#define CPL_SHARED_MUTEX_TYPE std::shared_mutex
43
#define CPL_SHARED_LOCK std::shared_lock<std::shared_mutex>
44
#define CPL_EXCLUSIVE_LOCK std::unique_lock<std::shared_mutex>
45
#else
46
// Poor-man implementation of std::shared_mutex with an exclusive mutex
47
#define CPL_SHARED_MUTEX_TYPE std::mutex
48
#define CPL_SHARED_LOCK std::lock_guard<std::mutex>
49
#define CPL_EXCLUSIVE_LOCK std::lock_guard<std::mutex>
50
#endif
51

52
#include "cpl_atomic_ops.h"
53
#include "cpl_conv.h"
54
#include "cpl_error.h"
55
#include "cpl_multiproc.h"
56
#include "cpl_string.h"
57

58
//! @cond Doxygen_Suppress
59

60
// szHIDDEN_DIRNAME is for files created by VSIMemGenerateHiddenFilename(pszFilename).
61
// Such files are of the form "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}"
62
//
63
// The high-level design constraint is that "/vsimem/.#!HIDDEN!#." acts as a
64
// "side" hierarchy, but still under the "/vsimem/" namespace, so that code
65
// having special processing of filenames starting with /vsimem/ can still work.
66
// The structure of the returned filename is also such that those files form
67
// independent hierarchies, i.e. the tree generated by a
68
// VSIMemGenerateHiddenFilename() is "invisible" from the one returned by
69
// another call to it.
70
//
71
// As a consequence:
72
// - we don't want ".#!HIDDEN!#." to be listed in VSIReadDir("/vsimem/")
73
// - we don't want content under ""/vsimem/.#!HIDDEN!#" to be deleted by
74
//   VSIRmdirRecursive("/vsimem/")
75
// - we don't want the creation of a file (or directory) called
76
//   "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}"
77
//   to cause the implicit creation of "/vsimem/.#!HIDDEN!#./{counter}" and
78
//   "/vsimem/.#!HIDDEN!#". This is done so that users don't have to care about
79
//   cleaning such implicit directories that are upper in the hierarchy w.r.t.
80
//   to what we return to them.
81
// - But we want the creation of file or directory
82
//   "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}/something_added_by_user"
83
//   to cause "/vsimem/.#!HIDDEN!#./{counter}/{pszFilename}" to be implicitly
84
//   created as a directory, so they can list it, or recursively delete it.
85
// - we want VSIReadDirRecursive("/vsimem/.#!HIDDEN!#.") to list everything
86
//   under it (for debugging purposes)
87
// - we want VSIRmdirRecursive("/vsimem/.#!HIDDEN!#.") to remove everything
88
//   under it (for debugging purposes)
89
//
90

91
constexpr const char *szHIDDEN_DIRNAME = "/vsimem/.#!HIDDEN!#.";
92

93
/*
94
** Notes on Multithreading:
95
**
96
** VSIMemFilesystemHandler: This class maintains a mutex to protect
97
** access and update of the oFileList array which has all the "files" in
98
** the memory filesystem area.  It is expected that multiple threads would
99
** want to create and read different files at the same time and so might
100
** collide access oFileList without the mutex.
101
**
102
** VSIMemFile: A mutex protects accesses to the file
103
**
104
** VSIMemHandle: This is essentially a "current location" representing
105
** on accessor to a file, and is inherently intended only to be used in
106
** a single thread.
107
**
108
** In General:
109
**
110
** Multiple threads accessing the memory filesystem are ok as long as
111
** a given VSIMemHandle (i.e. FILE * at app level) isn't used by multiple
112
** threads at once.
113
*/
114

115
/************************************************************************/
116
/* ==================================================================== */
117
/*                              VSIMemFile                              */
118
/* ==================================================================== */
119
/************************************************************************/
120

121
class VSIMemFile
122
{
123
    CPL_DISALLOW_COPY_ASSIGN(VSIMemFile)
124

125
  public:
126
    CPLString osFilename{};
127

128
    bool bIsDirectory = false;
129

130
    bool bOwnData = true;
131
    GByte *pabyData = nullptr;
132
    vsi_l_offset nLength = 0;
133
    vsi_l_offset nAllocLength = 0;
134
    vsi_l_offset nMaxLength = GUINTBIG_MAX;
135

136
    time_t mTime = 0;
137
    CPL_SHARED_MUTEX_TYPE m_oMutex{};
138

139
    VSIMemFile();
140
    virtual ~VSIMemFile();
141

142
    bool SetLength(vsi_l_offset nNewSize);
143
};
144

145
/************************************************************************/
146
/* ==================================================================== */
147
/*                             VSIMemHandle                             */
148
/* ==================================================================== */
149
/************************************************************************/
150

151
class VSIMemHandle final : public VSIVirtualHandle
152
{
153
    CPL_DISALLOW_COPY_ASSIGN(VSIMemHandle)
154

155
  public:
156
    std::shared_ptr<VSIMemFile> poFile = nullptr;
157
    vsi_l_offset m_nOffset = 0;
158
    bool m_bReadAllowed = false;
159
    bool bUpdate = false;
160
    bool bEOF = false;
161
    bool m_bError = false;
162

163
    VSIMemHandle() = default;
173,066✔
164
    ~VSIMemHandle() override;
165

166
    int Seek(vsi_l_offset nOffset, int nWhence) override;
167
    vsi_l_offset Tell() override;
168
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
169
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
170
    void ClearErr() override;
171
    int Error() override;
172
    int Eof() override;
173
    int Close() override;
174
    int Truncate(vsi_l_offset nNewSize) override;
175

176
    bool HasPRead() const override
722✔
177
    {
178
        return true;
722✔
179
    }
180

181
    size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
182
                 vsi_l_offset /*nOffset*/) const override;
183
};
184

185
/************************************************************************/
186
/* ==================================================================== */
187
/*                       VSIMemFilesystemHandler                        */
188
/* ==================================================================== */
189
/************************************************************************/
190

191
class VSIMemFilesystemHandler final : public VSIFilesystemHandler
192
{
193
    const std::string m_osPrefix;
194
    CPL_DISALLOW_COPY_ASSIGN(VSIMemFilesystemHandler)
195

196
  public:
197
    std::map<std::string, std::shared_ptr<VSIMemFile>> oFileList{};
198
    CPLMutex *hMutex = nullptr;
199

200
    explicit VSIMemFilesystemHandler(const char *pszPrefix)
1,619✔
201
        : m_osPrefix(pszPrefix)
1,619✔
202
    {
203
    }
1,619✔
204

205
    ~VSIMemFilesystemHandler() override;
206

207
    // TODO(schwehr): Fix VSIFileFromMemBuffer so that using is not needed.
208
    using VSIFilesystemHandler::Open;
209

210
    VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
211
                           bool bSetError,
212
                           CSLConstList /* papszOptions */) override;
213
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
214
             int nFlags) override;
215
    int Unlink(const char *pszFilename) override;
216
    int Mkdir(const char *pszDirname, long nMode) override;
217
    int Rmdir(const char *pszDirname) override;
218
    int RmdirRecursive(const char *pszDirname) override;
219
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
220
    int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
221
               void *) override;
222
    GIntBig GetDiskFreeSpace(const char *pszDirname) override;
223

224
    static std::string NormalizePath(const std::string &in);
225

226
    int Unlink_unlocked(const char *pszFilename);
227

228
    VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
1✔
229
    {
230
        return new VSIMemFilesystemHandler(pszPrefix);
1✔
231
    }
232
};
233

234
/************************************************************************/
235
/* ==================================================================== */
236
/*                              VSIMemFile                              */
237
/* ==================================================================== */
238
/************************************************************************/
239

240
/************************************************************************/
241
/*                             VSIMemFile()                             */
242
/************************************************************************/
243

244
VSIMemFile::VSIMemFile()
98,594✔
245
{
246
    time(&mTime);
98,594✔
247
}
98,594✔
248

249
/************************************************************************/
250
/*                            ~VSIMemFile()                             */
251
/************************************************************************/
252

253
VSIMemFile::~VSIMemFile()
95,932✔
254
{
255
    if (bOwnData && pabyData)
95,932✔
256
        CPLFree(pabyData);
78,868✔
257
}
95,932✔
258

259
/************************************************************************/
260
/*                             SetLength()                              */
261
/************************************************************************/
262

263
// Must be called under exclusive lock
264
bool VSIMemFile::SetLength(vsi_l_offset nNewLength)
1,146,420✔
265

266
{
267
    if (nNewLength > nMaxLength)
1,146,420✔
268
    {
269
        CPLError(CE_Failure, CPLE_NotSupported, "Maximum file size reached!");
2,778✔
270
        return false;
2,778✔
271
    }
272

273
    /* -------------------------------------------------------------------- */
274
    /*      Grow underlying array if needed.                                */
275
    /* -------------------------------------------------------------------- */
276
    if (nNewLength > nAllocLength)
1,143,640✔
277
    {
278
        // If we don't own the buffer, we cannot reallocate it because
279
        // the return address might be different from the one passed by
280
        // the caller. Hence, the caller would not be able to free
281
        // the buffer.
282
        if (!bOwnData)
134,419✔
283
        {
284
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
285
                     "Cannot extended in-memory file whose ownership was not "
286
                     "transferred");
287
            return false;
1✔
288
        }
289

290
        // If the first allocation is 1 MB or above, just take that value
291
        // as the one to allocate
292
        // Otherwise slightly reserve more to avoid too frequent reallocations.
293
        const vsi_l_offset nNewAlloc =
134,418✔
294
            (nAllocLength == 0 && nNewLength >= 1024 * 1024)
81,269✔
295
                ? nNewLength
134,507✔
296
                : nNewLength + nNewLength / 10 + 5000;
134,329✔
297
        GByte *pabyNewData = nullptr;
134,418✔
298
        if (static_cast<vsi_l_offset>(static_cast<size_t>(nNewAlloc)) ==
299
            nNewAlloc)
300
        {
301
            pabyNewData = static_cast<GByte *>(
268,836✔
302
                nAllocLength == 0
134,418✔
303
                    ? VSICalloc(1, static_cast<size_t>(nNewAlloc))
81,269✔
304
                    : VSIRealloc(pabyData, static_cast<size_t>(nNewAlloc)));
53,149✔
305
        }
306
        if (pabyNewData == nullptr)
134,418✔
307
        {
308
            CPLError(CE_Failure, CPLE_OutOfMemory,
1✔
309
                     "Cannot extend in-memory file to " CPL_FRMT_GUIB
310
                     " bytes due to out-of-memory situation",
311
                     nNewAlloc);
312
            return false;
1✔
313
        }
314

315
        if (nAllocLength > 0)
134,417✔
316
        {
317
            // Clear the new allocated part of the buffer (only needed if
318
            // there was already reserved memory, otherwise VSICalloc() has
319
            // zeroized it already)
320
            memset(pabyNewData + nAllocLength, 0,
53,149✔
321
                   static_cast<size_t>(nNewAlloc - nAllocLength));
53,149✔
322
        }
323

324
        pabyData = pabyNewData;
134,417✔
325
        nAllocLength = nNewAlloc;
134,417✔
326
    }
327
    else if (nNewLength < nLength)
1,009,220✔
328
    {
329
        memset(pabyData + nNewLength, 0,
3,342✔
330
               static_cast<size_t>(nLength - nNewLength));
3,342✔
331
    }
332

333
    nLength = nNewLength;
1,143,640✔
334
    time(&mTime);
1,143,640✔
335

336
    return true;
1,143,640✔
337
}
338

339
/************************************************************************/
340
/* ==================================================================== */
341
/*                             VSIMemHandle                             */
342
/* ==================================================================== */
343
/************************************************************************/
344

345
/************************************************************************/
346
/*                            ~VSIMemHandle()                           */
347
/************************************************************************/
348

349
VSIMemHandle::~VSIMemHandle()
346,119✔
350
{
351
    VSIMemHandle::Close();
173,057✔
352
}
346,119✔
353

354
/************************************************************************/
355
/*                               Close()                                */
356
/************************************************************************/
357

358
int VSIMemHandle::Close()
346,130✔
359

360
{
361
    if (poFile)
346,130✔
362
    {
363
#ifdef DEBUG_VERBOSE
364
        CPLDebug("VSIMEM", "Closing handle %p on %s: ref_count=%d (before)",
365
                 this, poFile->osFilename.c_str(),
366
                 static_cast<int>(poFile.use_count()));
367
#endif
368
        poFile = nullptr;
173,054✔
369
    }
370

371
    return 0;
346,128✔
372
}
373

374
/************************************************************************/
375
/*                                Seek()                                */
376
/************************************************************************/
377

378
int VSIMemHandle::Seek(vsi_l_offset nOffset, int nWhence)
5,098,380✔
379

380
{
381
    vsi_l_offset nLength;
382
    {
383
        CPL_SHARED_LOCK oLock(poFile->m_oMutex);
5,098,380✔
384
        nLength = poFile->nLength;
5,098,360✔
385
    }
386

387
    if (nWhence == SEEK_CUR)
5,098,370✔
388
    {
389
        if (nOffset > INT_MAX)
390
        {
391
            // printf("likely negative offset intended\n");
392
        }
393
        m_nOffset += nOffset;
392,337✔
394
    }
395
    else if (nWhence == SEEK_SET)
4,706,030✔
396
    {
397
        m_nOffset = nOffset;
4,503,530✔
398
    }
399
    else if (nWhence == SEEK_END)
202,503✔
400
    {
401
        m_nOffset = nLength + nOffset;
202,504✔
402
    }
403
    else
404
    {
UNCOV
405
        errno = EINVAL;
×
UNCOV
406
        return -1;
×
407
    }
408

409
    bEOF = false;
5,098,370✔
410

411
    return 0;
5,098,370✔
412
}
413

414
/************************************************************************/
415
/*                                Tell()                                */
416
/************************************************************************/
417

418
vsi_l_offset VSIMemHandle::Tell()
3,329,970✔
419

420
{
421
    return m_nOffset;
3,329,970✔
422
}
423

424
/************************************************************************/
425
/*                                Read()                                */
426
/************************************************************************/
427

428
size_t VSIMemHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
7,619,430✔
429

430
{
431
    const vsi_l_offset nOffset = m_nOffset;
7,619,430✔
432

433
    size_t nBytesToRead = nSize * nCount;
7,619,430✔
434
    if (nBytesToRead == 0)
7,619,430✔
435
        return 0;
1,604✔
436

437
    if (nCount > 0 && nBytesToRead / nCount != nSize)
7,617,830✔
438
    {
439
        bEOF = true;
×
440
        return 0;
×
441
    }
442

443
    if (!m_bReadAllowed)
7,617,830✔
444
    {
445
        m_bError = true;
58✔
446
        return 0;
58✔
447
    }
448

449
    bool bEOFTmp = bEOF;
7,617,770✔
450
    // Do not access/modify bEOF under the lock to avoid confusing Coverity
451
    // Scan since we access it in other methods outside of the lock.
452
    const auto DoUnderLock =
453
        [this, nOffset, pBuffer, nSize, &nBytesToRead, &nCount, &bEOFTmp]
60,931,800✔
454
    {
455
        CPL_SHARED_LOCK oLock(poFile->m_oMutex);
15,235,500✔
456

457
        if (poFile->nLength <= nOffset || nBytesToRead + nOffset < nBytesToRead)
7,617,770✔
458
        {
459
            bEOFTmp = true;
27,891✔
460
            return false;
27,891✔
461
        }
462
        if (nBytesToRead + nOffset > poFile->nLength)
7,589,870✔
463
        {
464
            nBytesToRead = static_cast<size_t>(poFile->nLength - nOffset);
50,606✔
465
            nCount = nBytesToRead / nSize;
50,629✔
466
            bEOFTmp = true;
50,629✔
467
        }
468

469
        if (nBytesToRead)
7,589,890✔
470
            memcpy(pBuffer, poFile->pabyData + nOffset,
7,589,860✔
471
                   static_cast<size_t>(nBytesToRead));
472
        return true;
7,589,890✔
473
    };
7,617,770✔
474

475
    bool bRet = DoUnderLock();
7,617,770✔
476
    bEOF = bEOFTmp;
7,617,780✔
477
    if (!bRet)
7,617,780✔
478
        return 0;
27,891✔
479

480
    m_nOffset += nBytesToRead;
7,589,890✔
481

482
    return nCount;
7,589,890✔
483
}
484

485
/************************************************************************/
486
/*                              PRead()                                 */
487
/************************************************************************/
488

489
size_t VSIMemHandle::PRead(void *pBuffer, size_t nSize,
427✔
490
                           vsi_l_offset nOffset) const
491
{
492
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
854✔
493

494
    if (nOffset < poFile->nLength)
427✔
495
    {
496
        const size_t nToCopy = static_cast<size_t>(
497
            std::min(static_cast<vsi_l_offset>(poFile->nLength - nOffset),
1,236✔
498
                     static_cast<vsi_l_offset>(nSize)));
412✔
499
        memcpy(pBuffer, poFile->pabyData + static_cast<size_t>(nOffset),
412✔
500
               nToCopy);
501
        return nToCopy;
412✔
502
    }
503
    return 0;
15✔
504
}
505

506
/************************************************************************/
507
/*                               Write()                                */
508
/************************************************************************/
509

510
size_t VSIMemHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
1,611,530✔
511

512
{
513
    const vsi_l_offset nOffset = m_nOffset;
1,611,530✔
514

515
    if (!bUpdate)
1,611,530✔
516
    {
517
        errno = EACCES;
1✔
518
        return 0;
1✔
519
    }
520

521
    const size_t nBytesToWrite = nSize * nCount;
1,611,530✔
522

523
    {
524
        CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
1,611,530✔
525

526
        if (nCount > 0 && nBytesToWrite / nCount != nSize)
1,611,520✔
527
        {
528
            return 0;
×
529
        }
530
        if (nBytesToWrite + nOffset < nBytesToWrite)
1,611,520✔
531
        {
532
            return 0;
×
533
        }
534

535
        if (nBytesToWrite + nOffset > poFile->nLength)
1,611,520✔
536
        {
537
            if (!poFile->SetLength(nBytesToWrite + nOffset))
1,142,440✔
538
                return 0;
2,760✔
539
        }
540

541
        if (nBytesToWrite)
1,608,770✔
542
            memcpy(poFile->pabyData + nOffset, pBuffer, nBytesToWrite);
1,598,590✔
543

544
        time(&poFile->mTime);
1,608,760✔
545
    }
546

547
    m_nOffset += nBytesToWrite;
1,608,780✔
548

549
    return nCount;
1,608,780✔
550
}
551

552
/************************************************************************/
553
/*                             ClearErr()                               */
554
/************************************************************************/
555

556
void VSIMemHandle::ClearErr()
25,793✔
557

558
{
559
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
25,793✔
560
    bEOF = false;
25,793✔
561
    m_bError = false;
25,793✔
562
}
25,793✔
563

564
/************************************************************************/
565
/*                              Error()                                 */
566
/************************************************************************/
567

568
int VSIMemHandle::Error()
29,033✔
569

570
{
571
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
29,033✔
572
    return m_bError ? TRUE : FALSE;
58,066✔
573
}
574

575
/************************************************************************/
576
/*                                Eof()                                 */
577
/************************************************************************/
578

579
int VSIMemHandle::Eof()
133,629✔
580

581
{
582
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
133,629✔
583
    return bEOF ? TRUE : FALSE;
267,258✔
584
}
585

586
/************************************************************************/
587
/*                             Truncate()                               */
588
/************************************************************************/
589

590
int VSIMemHandle::Truncate(vsi_l_offset nNewSize)
745✔
591
{
592
    if (!bUpdate)
745✔
593
    {
594
        errno = EACCES;
1✔
595
        return -1;
1✔
596
    }
597

598
    CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
1,488✔
599
    if (poFile->SetLength(nNewSize))
744✔
600
        return 0;
724✔
601

602
    return -1;
20✔
603
}
604

605
/************************************************************************/
606
/* ==================================================================== */
607
/*                       VSIMemFilesystemHandler                        */
608
/* ==================================================================== */
609
/************************************************************************/
610

611
/************************************************************************/
612
/*                      ~VSIMemFilesystemHandler()                      */
613
/************************************************************************/
614

615
VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
2,252✔
616

617
{
618
    oFileList.clear();
1,126✔
619

620
    if (hMutex != nullptr)
1,126✔
621
        CPLDestroyMutex(hMutex);
200✔
622
    hMutex = nullptr;
1,126✔
623
}
2,252✔
624

625
/************************************************************************/
626
/*                                Open()                                */
627
/************************************************************************/
628

629
VSIVirtualHandle *VSIMemFilesystemHandler::Open(const char *pszFilename,
219,195✔
630
                                                const char *pszAccess,
631
                                                bool bSetError,
632
                                                CSLConstList /* papszOptions */)
633

634
{
635
    CPLMutexHolder oHolder(&hMutex);
438,392✔
636
    const std::string osFilename = NormalizePath(pszFilename);
657,591✔
637
    if (osFilename.empty())
219,197✔
638
        return nullptr;
×
639

640
    vsi_l_offset nMaxLength = GUINTBIG_MAX;
219,197✔
641
    const size_t iPos = osFilename.find("||maxlength=");
219,197✔
642
    if (iPos != std::string::npos)
219,197✔
643
    {
644
        nMaxLength = static_cast<vsi_l_offset>(CPLAtoGIntBig(
2,334✔
645
            osFilename.substr(iPos + strlen("||maxlength=")).c_str()));
4,668✔
646
    }
647

648
    /* -------------------------------------------------------------------- */
649
    /*      Get the filename we are opening, create if needed.              */
650
    /* -------------------------------------------------------------------- */
651
    std::shared_ptr<VSIMemFile> poFile = nullptr;
438,394✔
652
    const auto oIter = oFileList.find(osFilename);
219,197✔
653
    if (oIter != oFileList.end())
219,197✔
654
    {
655
        poFile = oIter->second;
81,709✔
656
    }
657

658
    // If no file and opening in read, error out.
659
    if (strstr(pszAccess, "w") == nullptr &&
572,651✔
660
        strstr(pszAccess, "a") == nullptr && poFile == nullptr)
219,197✔
661
    {
662
        if (bSetError)
55,745✔
663
        {
664
            VSIError(VSIE_FileError, "No such file or directory");
7,059✔
665
        }
666
        errno = ENOENT;
55,745✔
667
        return nullptr;
55,745✔
668
    }
669

670
    // Create.
671
    if (poFile == nullptr)
163,452✔
672
    {
673
        const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
81,743✔
674
        if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
81,743✔
675
        {
676
            if (bSetError)
1✔
677
            {
678
                VSIError(VSIE_FileError,
×
679
                         "Could not create directory %s for writing",
680
                         osFileDir.c_str());
681
            }
682
            errno = ENOENT;
1✔
683
            return nullptr;
1✔
684
        }
685

686
        poFile = std::make_shared<VSIMemFile>();
81,742✔
687
        poFile->osFilename = osFilename;
81,742✔
688
        oFileList[poFile->osFilename] = poFile;
81,742✔
689
#ifdef DEBUG_VERBOSE
690
        CPLDebug("VSIMEM", "Creating file %s: ref_count=%d", pszFilename,
691
                 static_cast<int>(poFile.use_count()));
692
#endif
693
        poFile->nMaxLength = nMaxLength;
81,742✔
694
    }
695
    // Overwrite
696
    else if (strstr(pszAccess, "w"))
81,709✔
697
    {
698
        CPL_EXCLUSIVE_LOCK oLock(poFile->m_oMutex);
3,235✔
699
        poFile->SetLength(0);
3,235✔
700
        poFile->nMaxLength = nMaxLength;
3,235✔
701
    }
702

703
    if (poFile->bIsDirectory)
163,451✔
704
    {
705
        errno = EISDIR;
986✔
706
        return nullptr;
986✔
707
    }
708

709
    /* -------------------------------------------------------------------- */
710
    /*      Setup the file handle on this file.                             */
711
    /* -------------------------------------------------------------------- */
712
    VSIMemHandle *poHandle = new VSIMemHandle;
162,465✔
713

714
    poHandle->poFile = poFile;
162,465✔
715
    poHandle->m_nOffset = 0;
162,465✔
716
    poHandle->bEOF = false;
162,465✔
717
    poHandle->bUpdate = strchr(pszAccess, 'w') || strchr(pszAccess, '+') ||
220,706✔
718
                        strchr(pszAccess, 'a');
58,241✔
719
    poHandle->m_bReadAllowed = strchr(pszAccess, 'r') || strchr(pszAccess, '+');
162,465✔
720

721
#ifdef DEBUG_VERBOSE
722
    CPLDebug("VSIMEM", "Opening handle %p on %s: ref_count=%d", poHandle,
723
             pszFilename, static_cast<int>(poFile.use_count()));
724
#endif
725
    if (strchr(pszAccess, 'a'))
162,465✔
726
    {
727
        vsi_l_offset nOffset;
728
        {
729
            CPL_SHARED_LOCK oLock(poFile->m_oMutex);
42✔
730
            nOffset = poFile->nLength;
42✔
731
        }
732
        poHandle->m_nOffset = nOffset;
42✔
733
    }
734

735
    return poHandle;
162,465✔
736
}
737

738
/************************************************************************/
739
/*                                Stat()                                */
740
/************************************************************************/
741

742
int VSIMemFilesystemHandler::Stat(const char *pszFilename,
569,603✔
743
                                  VSIStatBufL *pStatBuf, int /* nFlags */)
744

745
{
746
    CPLMutexHolder oHolder(&hMutex);
1,139,210✔
747

748
    const std::string osFilename = NormalizePath(pszFilename);
1,708,810✔
749

750
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
569,604✔
751

752
    if (osFilename == m_osPrefix || osFilename + '/' == m_osPrefix)
569,604✔
753
    {
754
        pStatBuf->st_size = 0;
56,958✔
755
        pStatBuf->st_mode = S_IFDIR;
56,958✔
756
        return 0;
56,958✔
757
    }
758

759
    auto oIter = oFileList.find(osFilename);
512,646✔
760
    if (oIter == oFileList.end())
512,646✔
761
    {
762
        errno = ENOENT;
441,109✔
763
        return -1;
441,109✔
764
    }
765

766
    std::shared_ptr<VSIMemFile> poFile = oIter->second;
143,074✔
767

768
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
71,537✔
769

770
    CPL_SHARED_LOCK oLock(poFile->m_oMutex);
71,537✔
771
    if (poFile->bIsDirectory)
71,537✔
772
    {
773
        pStatBuf->st_size = 0;
38,272✔
774
        pStatBuf->st_mode = S_IFDIR;
38,272✔
775
    }
776
    else
777
    {
778
        pStatBuf->st_size = poFile->nLength;
33,265✔
779
        pStatBuf->st_mode = S_IFREG;
33,265✔
780
        if (const char *mtime =
33,265✔
781
                CPLGetConfigOption("CPL_VSI_MEM_MTIME", nullptr))
33,265✔
782
        {
783
            pStatBuf->st_mtime =
31✔
784
                static_cast<time_t>(std::strtoll(mtime, nullptr, 10));
31✔
785
        }
786
        else
787
        {
788
            pStatBuf->st_mtime = poFile->mTime;
33,234✔
789
        }
790
    }
791

792
    return 0;
71,537✔
793
}
794

795
/************************************************************************/
796
/*                               Unlink()                               */
797
/************************************************************************/
798

799
int VSIMemFilesystemHandler::Unlink(const char *pszFilename)
79,415✔
800

801
{
802
    CPLMutexHolder oHolder(&hMutex);
158,830✔
803
    return Unlink_unlocked(pszFilename);
158,830✔
804
}
805

806
/************************************************************************/
807
/*                           Unlink_unlocked()                          */
808
/************************************************************************/
809

810
int VSIMemFilesystemHandler::Unlink_unlocked(const char *pszFilename)
89,752✔
811

812
{
813
    const std::string osFilename = NormalizePath(pszFilename);
269,256✔
814

815
    auto oIter = oFileList.find(osFilename);
89,752✔
816
    if (oIter == oFileList.end())
89,752✔
817
    {
818
        errno = ENOENT;
17,737✔
819
        return -1;
17,737✔
820
    }
821

822
#ifdef DEBUG_VERBOSE
823
    std::shared_ptr<VSIMemFile> poFile = oIter->second;
824
    CPLDebug("VSIMEM", "Unlink %s: ref_count=%d (before)", pszFilename,
825
             static_cast<int>(poFile.use_count()));
826
#endif
827
    oFileList.erase(oIter);
72,015✔
828

829
    return 0;
72,015✔
830
}
831

832
/************************************************************************/
833
/*                               Mkdir()                                */
834
/************************************************************************/
835

836
int VSIMemFilesystemHandler::Mkdir(const char *pszPathname, long /* nMode */)
96,030✔
837

838
{
839
    CPLMutexHolder oHolder(&hMutex);
192,060✔
840

841
    const std::string osPathname = NormalizePath(pszPathname);
288,090✔
842
    if (STARTS_WITH(osPathname.c_str(), szHIDDEN_DIRNAME))
96,030✔
843
    {
844
        if (osPathname.size() == strlen(szHIDDEN_DIRNAME))
89,460✔
845
            return 0;
44,575✔
846
        // "/vsimem/.#!HIDDEN!#./{unique_counter}"
847
        else if (osPathname.find('/', strlen(szHIDDEN_DIRNAME) + 1) ==
44,885✔
848
                 std::string::npos)
849
            return 0;
44,575✔
850

851
        // If "/vsimem/.#!HIDDEN!#./{unique_counter}/user_directory", then
852
        // accept creating an explicit directory
853
    }
854

855
    if (oFileList.find(osPathname) != oFileList.end())
6,880✔
856
    {
857
        errno = EEXIST;
629✔
858
        return -1;
629✔
859
    }
860

861
    std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
6,251✔
862
    poFile->osFilename = osPathname;
6,251✔
863
    poFile->bIsDirectory = true;
6,251✔
864
    oFileList[osPathname] = poFile;
6,251✔
865
#ifdef DEBUG_VERBOSE
866
    CPLDebug("VSIMEM", "Mkdir on %s: ref_count=%d", pszPathname,
867
             static_cast<int>(poFile.use_count()));
868
#endif
869
    CPL_IGNORE_RET_VAL(poFile);
6,251✔
870
    return 0;
6,251✔
871
}
872

873
/************************************************************************/
874
/*                               Rmdir()                                */
875
/************************************************************************/
876

877
int VSIMemFilesystemHandler::Rmdir(const char *pszPathname)
75✔
878

879
{
880
    return Unlink(pszPathname);
75✔
881
}
882

883
/************************************************************************/
884
/*                          RmdirRecursive()                            */
885
/************************************************************************/
886

887
int VSIMemFilesystemHandler::RmdirRecursive(const char *pszDirname)
3,671✔
888
{
889
    CPLMutexHolder oHolder(&hMutex);
7,342✔
890

891
    const CPLString osPath = NormalizePath(pszDirname);
7,342✔
892
    const size_t nPathLen = osPath.size();
3,671✔
893
    int ret = 0;
3,671✔
894
    if (osPath == "/vsimem")
3,671✔
895
    {
896
        // Clean-up all files under pszDirname, except hidden directories
897
        // if called from "/vsimem"
898
        for (auto iter = oFileList.begin(); iter != oFileList.end();
8✔
899
             /* no automatic increment */)
900
        {
901
            const char *pszFilePath = iter->second->osFilename.c_str();
6✔
902
            const size_t nFileLen = iter->second->osFilename.size();
6✔
903
            if (nFileLen > nPathLen &&
12✔
904
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
6✔
905
                pszFilePath[nPathLen] == '/' &&
18✔
906
                !STARTS_WITH(pszFilePath, szHIDDEN_DIRNAME))
6✔
907
            {
908
                iter = oFileList.erase(iter);
2✔
909
            }
910
            else
911
            {
912
                ++iter;
4✔
913
            }
914
        }
915
    }
916
    else
917
    {
918
        ret = -1;
3,669✔
919
        for (auto iter = oFileList.begin(); iter != oFileList.end();
448,926✔
920
             /* no automatic increment */)
921
        {
922
            const char *pszFilePath = iter->second->osFilename.c_str();
445,257✔
923
            const size_t nFileLen = iter->second->osFilename.size();
445,257✔
924
            if (nFileLen >= nPathLen &&
533,814✔
925
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
459,019✔
926
                (nFileLen == nPathLen || pszFilePath[nPathLen] == '/'))
13,762✔
927
            {
928
                // If VSIRmdirRecursive() is used correctly, it should at
929
                // least delete the directory on which it has been called
930
                ret = 0;
17,326✔
931
                iter = oFileList.erase(iter);
17,326✔
932
            }
933
            else
934
            {
935
                ++iter;
427,931✔
936
            }
937
        }
938

939
        // Make sure that it always succeed on the root hidden directory
940
        if (osPath == szHIDDEN_DIRNAME)
3,669✔
941
            ret = 0;
3✔
942
    }
943
    return ret;
7,342✔
944
}
945

946
/************************************************************************/
947
/*                             ReadDirEx()                              */
948
/************************************************************************/
949

950
char **VSIMemFilesystemHandler::ReadDirEx(const char *pszPath, int nMaxFiles)
27,027✔
951

952
{
953
    CPLMutexHolder oHolder(&hMutex);
54,054✔
954

955
    const CPLString osPath = NormalizePath(pszPath);
54,054✔
956

957
    char **papszDir = nullptr;
27,027✔
958
    const size_t nPathLen = osPath.size();
27,027✔
959

960
    // In case of really big number of files in the directory, CSLAddString
961
    // can be slow (see #2158). We then directly build the list.
962
    int nItems = 0;
27,027✔
963
    int nAllocatedItems = 0;
27,027✔
964

965
    if (osPath == szHIDDEN_DIRNAME)
27,027✔
966
    {
967
        // Special mode for hidden filenames.
968
        // "/vsimem/.#!HIDDEN!#./{counter}" subdirectories are not explicitly
969
        // created so they do not appear in oFileList, but their subcontent
970
        // (e.g "/vsimem/.#!HIDDEN!#./{counter}/foo") does
971
        std::set<std::string> oSetSubDirs;
20✔
972
        for (const auto &iter : oFileList)
161✔
973
        {
974
            const char *pszFilePath = iter.second->osFilename.c_str();
151✔
975
            if (iter.second->osFilename.size() > nPathLen &&
290✔
976
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0)
139✔
977
            {
978
                char *pszItem = CPLStrdup(pszFilePath + nPathLen + 1);
15✔
979
                char *pszSlash = strchr(pszItem, '/');
15✔
980
                if (pszSlash)
15✔
981
                    *pszSlash = 0;
15✔
982
                if (cpl::contains(oSetSubDirs, pszItem))
15✔
983
                {
984
                    CPLFree(pszItem);
2✔
985
                    continue;
2✔
986
                }
987
                oSetSubDirs.insert(pszItem);
13✔
988

989
                if (nItems == 0)
13✔
990
                {
991
                    papszDir =
992
                        static_cast<char **>(CPLCalloc(2, sizeof(char *)));
6✔
993
                    nAllocatedItems = 1;
6✔
994
                }
995
                else if (nItems >= nAllocatedItems)
7✔
996
                {
997
                    nAllocatedItems = nAllocatedItems * 2;
7✔
998
                    papszDir = static_cast<char **>(CPLRealloc(
7✔
999
                        papszDir, (nAllocatedItems + 2) * sizeof(char *)));
7✔
1000
                }
1001

1002
                papszDir[nItems] = pszItem;
13✔
1003
                papszDir[nItems + 1] = nullptr;
13✔
1004

1005
                nItems++;
13✔
1006
                if (nMaxFiles > 0 && nItems > nMaxFiles)
13✔
1007
                    break;
×
1008
            }
1009
        }
1010
    }
1011
    else
1012
    {
1013
        for (const auto &iter : oFileList)
10,522,200✔
1014
        {
1015
            const char *pszFilePath = iter.second->osFilename.c_str();
10,495,200✔
1016
            if (iter.second->osFilename.size() > nPathLen &&
10,495,200✔
1017
                memcmp(pszFilePath, osPath.c_str(), nPathLen) == 0 &&
6,780,040✔
1018
                pszFilePath[nPathLen] == '/' &&
20,442,800✔
1019
                strstr(pszFilePath + nPathLen + 1, "/") == nullptr)
3,167,570✔
1020
            {
1021
                if (nItems == 0)
382,450✔
1022
                {
1023
                    papszDir =
1024
                        static_cast<char **>(CPLCalloc(2, sizeof(char *)));
25,475✔
1025
                    nAllocatedItems = 1;
25,475✔
1026
                }
1027
                else if (nItems >= nAllocatedItems)
356,975✔
1028
                {
1029
                    nAllocatedItems = nAllocatedItems * 2;
48,058✔
1030
                    papszDir = static_cast<char **>(CPLRealloc(
48,058✔
1031
                        papszDir, (nAllocatedItems + 2) * sizeof(char *)));
48,058✔
1032
                }
1033

1034
                papszDir[nItems] = CPLStrdup(pszFilePath + nPathLen + 1);
382,450✔
1035
                papszDir[nItems + 1] = nullptr;
382,450✔
1036

1037
                nItems++;
382,450✔
1038
                if (nMaxFiles > 0 && nItems > nMaxFiles)
382,450✔
1039
                    break;
1✔
1040
            }
1041
        }
1042
    }
1043

1044
    return papszDir;
54,054✔
1045
}
1046

1047
/************************************************************************/
1048
/*                               Rename()                               */
1049
/************************************************************************/
1050

1051
int VSIMemFilesystemHandler::Rename(const char *pszOldPath,
467✔
1052
                                    const char *pszNewPath, GDALProgressFunc,
1053
                                    void *)
1054

1055
{
1056
    CPLMutexHolder oHolder(&hMutex);
934✔
1057

1058
    const std::string osOldPath = NormalizePath(pszOldPath);
1,401✔
1059
    const std::string osNewPath = NormalizePath(pszNewPath);
1,401✔
1060
    if (!STARTS_WITH(pszNewPath, m_osPrefix.c_str()))
467✔
1061
        return -1;
3✔
1062

1063
    if (osOldPath.compare(osNewPath) == 0)
464✔
1064
        return 0;
×
1065

1066
    if (oFileList.find(osOldPath) == oFileList.end())
464✔
1067
    {
1068
        errno = ENOENT;
6✔
1069
        return -1;
6✔
1070
    }
1071

1072
    std::map<std::string, std::shared_ptr<VSIMemFile>>::iterator it =
1073
        oFileList.find(osOldPath);
458✔
1074
    while (it != oFileList.end() && it->first.find(osOldPath) == 0)
1,110✔
1075
    {
1076
        const std::string osRemainder = it->first.substr(osOldPath.size());
1,304✔
1077
        if (osRemainder.empty() || osRemainder[0] == '/')
652✔
1078
        {
1079
            const std::string osNewFullPath = osNewPath + osRemainder;
1,042✔
1080
            Unlink_unlocked(osNewFullPath.c_str());
521✔
1081
            oFileList[osNewFullPath] = it->second;
521✔
1082
            it->second->osFilename = osNewFullPath;
521✔
1083
            oFileList.erase(it++);
521✔
1084
        }
1085
        else
1086
        {
1087
            ++it;
131✔
1088
        }
1089
    }
1090

1091
    return 0;
458✔
1092
}
1093

1094
/************************************************************************/
1095
/*                           NormalizePath()                            */
1096
/************************************************************************/
1097

1098
std::string VSIMemFilesystemHandler::NormalizePath(const std::string &in)
1,052,310✔
1099
{
1100
    CPLString s(in);
2,104,610✔
1101
    std::replace(s.begin(), s.end(), '\\', '/');
1,052,300✔
1102
    s.replaceAll("//", '/');
1,052,310✔
1103
    if (!s.empty() && s.back() == '/')
1,052,310✔
1104
        s.pop_back();
1,642✔
1105
#if __GNUC__ >= 13
1106
    // gcc 13 complains about below explicit std::move()
1107
    return s;
1108
#else
1109
    // Android NDK (and probably other compilers) warn about
1110
    // "warning: local variable 's' will be copied despite being returned by name [-Wreturn-std-move]"
1111
    // if not specifying std::move()
1112
    return std::move(s);
2,104,600✔
1113
#endif
1114
}
1115

1116
/************************************************************************/
1117
/*                        GetDiskFreeSpace()                            */
1118
/************************************************************************/
1119

1120
GIntBig VSIMemFilesystemHandler::GetDiskFreeSpace(const char * /*pszDirname*/)
70✔
1121
{
1122
    const GIntBig nRet = CPLGetUsablePhysicalRAM();
70✔
1123
    if (nRet <= 0)
70✔
1124
        return -1;
×
1125
    return nRet;
70✔
1126
}
1127

1128
//! @endcond
1129

1130
/************************************************************************/
1131
/*                       VSIInstallMemFileHandler()                     */
1132
/************************************************************************/
1133

1134
/**
1135
 * \brief Install "memory" file system handler.
1136
 *
1137
 * A special file handler is installed that allows block of memory to be
1138
 * treated as files.   All portions of the file system underneath the base
1139
 * path "/vsimem/" will be handled by this driver.
1140
 *
1141
 * Normal VSI*L functions can be used freely to create and destroy memory
1142
 * arrays treating them as if they were real file system objects.  Some
1143
 * additional methods exist to efficient create memory file system objects
1144
 * without duplicating original copies of the data or to "steal" the block
1145
 * of memory associated with a memory file.
1146
 *
1147
 * Directory related functions are supported.
1148
 *
1149
 * This code example demonstrates using GDAL to translate from one memory
1150
 * buffer to another.
1151
 *
1152
 * \code
1153
 * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
1154
 *                             vsi_l_offset *pnOutDataLength )
1155
 * {
1156
 *     // create memory file system object from buffer.
1157
 *     VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
1158
 *                                       nInDataLength, FALSE ) );
1159
 *
1160
 *     // Open memory buffer for read.
1161
 *     GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
1162
 *
1163
 *     // Get output format driver.
1164
 *     GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1165
 *     GDALDatasetH hOutDS;
1166
 *
1167
 *     hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
1168
 *                              NULL, NULL );
1169
 *
1170
 *     // close source file, and "unlink" it.
1171
 *     GDALClose( hDS );
1172
 *     VSIUnlink( "/vsimem/work.dat" );
1173
 *
1174
 *     // seize the buffer associated with the output file.
1175
 *
1176
 *     return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
1177
 * }
1178
 * \endcode
1179
 */
1180

1181
void VSIInstallMemFileHandler()
1,618✔
1182
{
1183
    VSIFileManager::InstallHandler("/vsimem/",
1,618✔
1184
                                   new VSIMemFilesystemHandler("/vsimem/"));
1,618✔
1185
}
1,618✔
1186

1187
/************************************************************************/
1188
/*                        VSIFileFromMemBuffer()                        */
1189
/************************************************************************/
1190

1191
/**
1192
 * \brief Create memory "file" from a buffer.
1193
 *
1194
 * A virtual memory file is created from the passed buffer with the indicated
1195
 * filename.  Under normal conditions the filename would need to be absolute
1196
 * and within the /vsimem/ portion of the filesystem.
1197
 * Starting with GDAL 3.6, nullptr can also be passed as pszFilename to mean
1198
 * an anonymous file, that is destroyed when the handle is closed.
1199
 *
1200
 * If bTakeOwnership is TRUE, then the memory file system handler will take
1201
 * ownership of the buffer, freeing it when the file is deleted.  Otherwise
1202
 * it remains the responsibility of the caller, but should not be freed as
1203
 * long as it might be accessed as a file.  In no circumstances does this
1204
 * function take a copy of the pabyData contents.
1205
 *
1206
 * @param pszFilename the filename to be created, or nullptr
1207
 * @param pabyData the data buffer for the file.
1208
 * @param nDataLength the length of buffer in bytes.
1209
 * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
1210
 *
1211
 * @return open file handle on created file (see VSIFOpenL()).
1212
 */
1213

1214
VSILFILE *VSIFileFromMemBuffer(const char *pszFilename, GByte *pabyData,
10,602✔
1215
                               vsi_l_offset nDataLength, int bTakeOwnership)
1216

1217
{
1218
    if (VSIFileManager::GetHandler("") ==
10,602✔
1219
        VSIFileManager::GetHandler("/vsimem/"))
10,602✔
1220
        VSIInstallMemFileHandler();
×
1221

1222
    VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1223
        VSIFileManager::GetHandler("/vsimem/"));
10,602✔
1224

1225
    const CPLString osFilename =
1226
        pszFilename ? VSIMemFilesystemHandler::NormalizePath(pszFilename)
31,021✔
1227
                    : std::string();
21,204✔
1228
    if (osFilename == "/vsimem/")
10,602✔
1229
    {
1230
        CPLDebug("VSIMEM", "VSIFileFromMemBuffer(): illegal filename: %s",
1✔
1231
                 pszFilename);
1232
        return nullptr;
1✔
1233
    }
1234

1235
    // Try to create the parent directory, if needed, before taking
1236
    // ownership of pabyData.
1237
    if (!osFilename.empty())
10,601✔
1238
    {
1239
        const std::string osFileDir = CPLGetPathSafe(osFilename.c_str());
9,816✔
1240
        if (VSIMkdirRecursive(osFileDir.c_str(), 0755) == -1)
9,816✔
1241
        {
1242
            VSIError(VSIE_FileError,
×
1243
                     "Could not create directory %s for writing",
1244
                     osFileDir.c_str());
1245
            errno = ENOENT;
×
1246
            return nullptr;
×
1247
        }
1248
    }
1249

1250
    std::shared_ptr<VSIMemFile> poFile = std::make_shared<VSIMemFile>();
10,601✔
1251

1252
    poFile->osFilename = osFilename;
10,601✔
1253
    poFile->bOwnData = CPL_TO_BOOL(bTakeOwnership);
10,601✔
1254
    poFile->pabyData = pabyData;
10,601✔
1255
    poFile->nLength = nDataLength;
10,601✔
1256
    poFile->nAllocLength = nDataLength;
10,601✔
1257

1258
    if (!osFilename.empty())
10,601✔
1259
    {
1260
        CPLMutexHolder oHolder(&poHandler->hMutex);
19,632✔
1261
        poHandler->Unlink_unlocked(osFilename);
9,816✔
1262
        poHandler->oFileList[poFile->osFilename] = poFile;
9,816✔
1263
#ifdef DEBUG_VERBOSE
1264
        CPLDebug("VSIMEM", "VSIFileFromMemBuffer() %s: ref_count=%d (after)",
1265
                 poFile->osFilename.c_str(),
1266
                 static_cast<int>(poFile.use_count()));
1267
#endif
1268
    }
1269

1270
    /* -------------------------------------------------------------------- */
1271
    /*      Setup the file handle on this file.                             */
1272
    /* -------------------------------------------------------------------- */
1273
    VSIMemHandle *poHandle = new VSIMemHandle;
10,601✔
1274

1275
    poHandle->poFile = std::move(poFile);
10,601✔
1276
    poHandle->bUpdate = true;
10,601✔
1277
    poHandle->m_bReadAllowed = true;
10,601✔
1278
    return poHandle;
10,601✔
1279
}
1280

1281
/************************************************************************/
1282
/*                        VSIGetMemFileBuffer()                         */
1283
/************************************************************************/
1284

1285
/**
1286
 * \brief Fetch buffer underlying memory file.
1287
 *
1288
 * This function returns a pointer to the memory buffer underlying a
1289
 * virtual "in memory" file.  If bUnlinkAndSeize is TRUE the filesystem
1290
 * object will be deleted, and ownership of the buffer will pass to the
1291
 * caller otherwise the underlying file will remain in existence.
1292
 *
1293
 * @param pszFilename the name of the file to grab the buffer of.
1294
 * @param pnDataLength (file) length returned in this variable.
1295
 * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
1296
 *
1297
 * @return pointer to memory buffer or NULL on failure.
1298
 */
1299

1300
GByte *VSIGetMemFileBuffer(const char *pszFilename, vsi_l_offset *pnDataLength,
36,282✔
1301
                           int bUnlinkAndSeize)
1302

1303
{
1304
    VSIMemFilesystemHandler *poHandler = static_cast<VSIMemFilesystemHandler *>(
1305
        VSIFileManager::GetHandler("/vsimem/"));
36,282✔
1306

1307
    if (pszFilename == nullptr)
36,281✔
1308
        return nullptr;
×
1309

1310
    const std::string osFilename =
1311
        VSIMemFilesystemHandler::NormalizePath(pszFilename);
108,837✔
1312

1313
    CPLMutexHolder oHolder(&poHandler->hMutex);
72,550✔
1314

1315
    if (poHandler->oFileList.find(osFilename) == poHandler->oFileList.end())
36,282✔
1316
        return nullptr;
139✔
1317

1318
    std::shared_ptr<VSIMemFile> poFile = poHandler->oFileList[osFilename];
36,143✔
1319
    GByte *pabyData = poFile->pabyData;
36,143✔
1320
    if (pnDataLength != nullptr)
36,143✔
1321
        *pnDataLength = poFile->nLength;
33,436✔
1322

1323
    if (bUnlinkAndSeize)
36,143✔
1324
    {
1325
        if (!poFile->bOwnData)
4,415✔
1326
            CPLDebug("VSIMemFile",
×
1327
                     "File doesn't own data in VSIGetMemFileBuffer!");
1328
        else
1329
            poFile->bOwnData = false;
4,415✔
1330

1331
        poHandler->oFileList.erase(poHandler->oFileList.find(osFilename));
4,415✔
1332
#ifdef DEBUG_VERBOSE
1333
        CPLDebug("VSIMEM", "VSIGetMemFileBuffer() %s: ref_count=%d (before)",
1334
                 poFile->osFilename.c_str(),
1335
                 static_cast<int>(poFile.use_count()));
1336
#endif
1337
        poFile->pabyData = nullptr;
4,415✔
1338
        poFile->nLength = 0;
4,415✔
1339
        poFile->nAllocLength = 0;
4,415✔
1340
    }
1341

1342
    return pabyData;
36,143✔
1343
}
1344

1345
/************************************************************************/
1346
/*                    VSIMemGenerateHiddenFilename()                    */
1347
/************************************************************************/
1348

1349
/**
1350
 * \brief Generates a unique filename that can be used with the /vsimem/
1351
 * virtual file system.
1352
 *
1353
 * This function returns a (short-lived) string containing a unique filename,
1354
 * (using an atomic counter), designed for temporary files that must remain
1355
 * invisible for other users working at the "/vsimem/" top-level, i.e.
1356
 * such files are not returned by VSIReadDir("/vsimem/") or
1357
 * VSIReadDirRecursive("/vsimem/)".
1358
 *
1359
 * The function does not create the file per se. Such filename can be used to
1360
 * create a regular file with VSIFOpenL() or VSIFileFromMemBuffer(), or create
1361
 * a directory with VSIMkdir()
1362
 *
1363
 * Once created, deletion of those files using VSIUnlink(), VSIRmdirRecursive(),
1364
 * etc. is of the responsibility of the user. The user should not attempt to
1365
 * work with the "parent" directory returned by CPLGetPath() / CPLGetDirname()
1366
 * on the returned filename, and work only with files at the same level or
1367
 * in subdirectories of what is returned by this function.
1368
 *
1369
 * @param pszFilename the filename to be appended at the end of the returned
1370
 *                    filename. If not specified, defaults to "unnamed".
1371
 *
1372
 * @return pointer to a short-lived string (rotating buffer of strings in
1373
 * thread-local storage). It is recommended to use CPLStrdup() or std::string()
1374
 * immediately on it.
1375
 *
1376
 * @since GDAL 3.10
1377
 */
1378
const char *VSIMemGenerateHiddenFilename(const char *pszFilename)
43,833✔
1379
{
1380
    static std::atomic<uint32_t> nCounter{0};
1381
    return CPLSPrintf("%s/%u/%s", szHIDDEN_DIRNAME, ++nCounter,
43,833✔
1382
                      pszFilename ? pszFilename : "unnamed");
43,830✔
1383
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc