Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_vsil_curl_class.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  CPL - Common Portability Library
4
 * Purpose:  Declarations for /vsicurl/ and related file systems
5
 * Author:   Even Rouault, even.rouault at spatialys.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2010-2018, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#ifndef CPL_VSIL_CURL_CLASS_H_INCLUDED
14
#define CPL_VSIL_CURL_CLASS_H_INCLUDED
15
16
#ifdef HAVE_CURL
17
18
#include "cpl_aws.h"
19
#include "cpl_azure.h"
20
#include "cpl_port.h"
21
#include "cpl_json.h"
22
#include "cpl_http.h"
23
#include "cpl_string.h"
24
#include "cpl_vsil_curl_priv.h"
25
#include "cpl_mem_cache.h"
26
#include "cpl_multiproc.h"
27
28
#include "cpl_curl_priv.h"
29
30
#include <algorithm>
31
#include <atomic>
32
#include <condition_variable>
33
#include <set>
34
#include <map>
35
#include <memory>
36
#include <mutex>
37
#include <thread>
38
#include <utility>
39
40
// To avoid aliasing to CopyFile to CopyFileA on Windows
41
#ifdef CopyFile
42
#undef CopyFile
43
#endif
44
45
//! @cond Doxygen_Suppress
46
47
// Leave it for backward compatibility, but deprecate.
48
#define HAVE_CURLINFO_REDIRECT_URL
49
50
void VSICurlStreamingClearCache(void);  // from cpl_vsil_curl_streaming.cpp
51
52
struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
53
                                     const char *const *papszOptions);
54
55
struct curl_slist *VSICurlSetContentTypeFromExt(struct curl_slist *polist,
56
                                                const char *pszPath);
57
58
struct curl_slist *VSICurlSetCreationHeadersFromOptions(
59
    struct curl_slist *headers, CSLConstList papszOptions, const char *pszPath);
60
61
namespace cpl
62
{
63
64
typedef enum
65
{
66
    EXIST_UNKNOWN = -1,
67
    EXIST_NO,
68
    EXIST_YES,
69
} ExistStatus;
70
71
class FileProp
72
{
73
  public:
74
    unsigned int nGenerationAuthParameters = 0;
75
    ExistStatus eExists = EXIST_UNKNOWN;
76
    int nHTTPCode = 0;
77
    vsi_l_offset fileSize = 0;
78
    time_t mTime = 0;
79
    time_t nExpireTimestampLocal = 0;
80
    std::string osRedirectURL{};
81
    bool bHasComputedFileSize = false;
82
    bool bIsDirectory = false;
83
    bool bIsAzureFolder = false;
84
    int nMode = 0;  // st_mode member of struct stat
85
    bool bS3LikeRedirect = false;
86
    std::string ETag{};
87
};
88
89
struct CachedDirList
90
{
91
    bool bGotFileList = false;
92
    unsigned int nGenerationAuthParameters = 0;
93
    CPLStringList oFileList{}; /* only file name without path */
94
};
95
96
struct WriteFuncStruct
97
{
98
    char *pBuffer = nullptr;
99
    size_t nSize = 0;
100
    bool bIsHTTP = false;
101
    bool bMultiRange = false;
102
    vsi_l_offset nStartOffset = 0;
103
    vsi_l_offset nEndOffset = 0;
104
    int nHTTPCode = 0;       // potentially after redirect
105
    int nFirstHTTPCode = 0;  // the one of the redirect
106
    vsi_l_offset nContentLength = 0;
107
    bool bFoundContentRange = false;
108
    bool bError = false;
109
    bool bInterruptDownload = false;
110
    bool bDetectRangeDownloadingError = false;
111
    GIntBig nTimestampDate = 0;  // Corresponds to Date: header field
112
113
    VSILFILE *fp = nullptr;
114
    VSICurlReadCbkFunc pfnReadCbk = nullptr;
115
    void *pReadCbkUserData = nullptr;
116
    bool bInterrupted = false;
117
};
118
119
struct PutData
120
{
121
    const GByte *pabyData = nullptr;
122
    size_t nOff = 0;
123
    size_t nTotalSize = 0;
124
125
    static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
126
                                     void *instream)
127
0
    {
128
0
        PutData *poThis = static_cast<PutData *>(instream);
129
0
        const size_t nSizeMax = size * nitems;
130
0
        const size_t nSizeToWrite =
131
0
            std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
132
0
        memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
133
0
        poThis->nOff += nSizeToWrite;
134
0
        return nSizeToWrite;
135
0
    }
136
};
137
138
/************************************************************************/
139
/*                       VSICurlFilesystemHandler                       */
140
/************************************************************************/
141
142
class VSICurlHandle;
143
144
class VSICurlFilesystemHandlerBase : public VSIFilesystemHandler
145
{
146
    CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBase)
147
148
    struct FilenameOffsetPair
149
    {
150
        std::string filename_;
151
        vsi_l_offset offset_;
152
153
        FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
154
7.27k
            : filename_(filename), offset_(offset)
155
7.27k
        {
156
7.27k
        }
157
158
        bool operator==(const FilenameOffsetPair &other) const
159
0
        {
160
0
            return filename_ == other.filename_ && offset_ == other.offset_;
161
0
        }
162
    };
163
164
    struct FilenameOffsetPairHasher
165
    {
166
        std::size_t operator()(const FilenameOffsetPair &k) const
167
0
        {
168
0
            return std::hash<std::string>()(k.filename_) ^
169
0
                   std::hash<vsi_l_offset>()(k.offset_);
170
0
        }
171
    };
172
173
    using RegionCacheType = lru11::Cache<
174
        FilenameOffsetPair, std::shared_ptr<std::string>, lru11::NullLock,
175
        std::unordered_map<
176
            FilenameOffsetPair,
177
            typename std::list<lru11::KeyValuePair<
178
                FilenameOffsetPair, std::shared_ptr<std::string>>>::iterator,
179
            FilenameOffsetPairHasher>>;
180
181
    std::unique_ptr<RegionCacheType>
182
        m_poRegionCacheDoNotUseDirectly{};  // do not access directly. Use
183
                                            // GetRegionCache();
184
    RegionCacheType *GetRegionCache();
185
186
    // LRU cache that just keeps in memory if this file system handler is
187
    // spposed to know the file properties of a file. The actual cache is a
188
    // shared one among all network file systems.
189
    // The aim of that design is that invalidating /vsis3/foo results in
190
    // /vsis3_streaming/foo to be invalidated as well.
191
    lru11::Cache<std::string, bool> oCacheFileProp;
192
193
    int nCachedFilesInDirList = 0;
194
    lru11::Cache<std::string, CachedDirList> oCacheDirList;
195
196
    char **ParseHTMLFileList(const char *pszFilename, int nMaxFiles,
197
                             char *pszData, bool *pbGotFileList);
198
199
    // Data structure and map to store regions that are in progress, to
200
    // avoid simultaneous downloads of the same region in different threads
201
    // Cf https://github.com/OSGeo/gdal/issues/8041
202
    struct RegionInDownload
203
    {
204
        std::mutex oMutex{};
205
        std::condition_variable oCond{};
206
        bool bDownloadInProgress = false;
207
        int nWaiters = 0;
208
        std::string osData{};
209
    };
210
211
    std::mutex m_oMutex{};
212
    std::map<std::string, std::unique_ptr<RegionInDownload>>
213
        m_oMapRegionInDownload{};
214
215
  protected:
216
    CPLMutex *hMutex = nullptr;
217
218
    virtual VSICurlHandle *CreateFileHandle(const char *pszFilename);
219
    virtual char **GetFileList(const char *pszFilename, int nMaxFiles,
220
                               bool *pbGotFileList);
221
222
    void RegisterEmptyDir(const std::string &osDirname);
223
224
    bool
225
    AnalyseS3FileList(const std::string &osBaseURL, const char *pszXML,
226
                      CPLStringList &osFileList, int nMaxFiles,
227
                      const std::set<std::string> &oSetIgnoredStorageClasses,
228
                      bool &bIsTruncated);
229
230
    void AnalyseSwiftFileList(const std::string &osBaseURL,
231
                              const std::string &osPrefix, const char *pszJson,
232
                              CPLStringList &osFileList, int nMaxFilesThisQuery,
233
                              int nMaxFiles, bool &bIsTruncated,
234
                              std::string &osNextMarker);
235
236
    static const char *GetOptionsStatic();
237
238
    VSICurlFilesystemHandlerBase();
239
240
  public:
241
    ~VSICurlFilesystemHandlerBase() override;
242
243
    static bool IsAllowedFilename(const char *pszFilename);
244
245
    VSIVirtualHandleUniquePtr Open(const char *pszFilename,
246
                                   const char *pszAccess, bool bSetError,
247
                                   CSLConstList /* papszOptions */) override;
248
249
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
250
             int nFlags) override;
251
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
252
    char **SiblingFiles(const char *pszFilename) override;
253
254
    int HasOptimizedReadMultiRange(const char * /* pszPath */) override
255
0
    {
256
0
        return true;
257
0
    }
258
259
    const char *GetActualURL(const char *pszFilename) override;
260
261
    const char *GetOptions() override;
262
263
    char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
264
                           CSLConstList papszOptions) override;
265
266
    char **ReadDirInternal(const char *pszDirname, int nMaxFiles,
267
                           bool *pbGotFileList);
268
    void InvalidateDirContent(const std::string &osDirname);
269
270
    virtual const char *GetDebugKey() const = 0;
271
272
    virtual std::string GetFSPrefix() const = 0;
273
    virtual bool AllowCachedDataFor(const char *pszFilename);
274
275
    bool IsLocal(const char * /* pszPath */) const override
276
0
    {
277
0
        return false;
278
0
    }
279
280
    virtual bool
281
    SupportsSequentialWrite(const char * /* pszPath */,
282
                            bool /* bAllowLocalTempFile */) override
283
0
    {
284
0
        return false;
285
0
    }
286
287
    virtual bool SupportsRandomWrite(const char * /* pszPath */,
288
                                     bool /* bAllowLocalTempFile */) override
289
0
    {
290
0
        return false;
291
0
    }
292
293
    std::shared_ptr<std::string> GetRegion(const char *pszURL,
294
                                           vsi_l_offset nFileOffsetStart);
295
296
    void AddRegion(const char *pszURL, vsi_l_offset nFileOffsetStart,
297
                   size_t nSize, const char *pData);
298
299
    std::pair<bool, std::string>
300
    NotifyStartDownloadRegion(const std::string &osURL,
301
                              vsi_l_offset startOffset, int nBlocks);
302
    void NotifyStopDownloadRegion(const std::string &osURL,
303
                                  vsi_l_offset startOffset, int nBlocks,
304
                                  const std::string &osData);
305
306
    bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
307
    void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
308
    void InvalidateCachedData(const char *pszURL);
309
310
    CURLM *GetCurlMultiHandleFor(const std::string &osURL);
311
312
    virtual void ClearCache();
313
    virtual void PartialClearCache(const char *pszFilename);
314
315
    bool GetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
316
    void SetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
317
    bool ExistsInCacheDirList(const std::string &osDirname, bool *pbIsDir);
318
319
    virtual std::string GetURLFromFilename(const std::string &osFilename) const;
320
321
    std::string
322
    GetStreamingFilename(const std::string &osFilename) const override = 0;
323
324
    static std::set<std::string> GetS3IgnoredStorageClasses();
325
};
326
327
class VSICurlFilesystemHandler final : public VSICurlFilesystemHandlerBase
328
{
329
    CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
330
331
  public:
332
84
    VSICurlFilesystemHandler() = default;
333
334
    const char *GetDebugKey() const override
335
233k
    {
336
233k
        return "VSICURL";
337
233k
    }
338
339
    std::string GetFSPrefix() const override
340
2.30M
    {
341
2.30M
        return "/vsicurl/";
342
2.30M
    }
343
344
    std::string
345
    GetStreamingFilename(const std::string &osFilename) const override;
346
347
    std::string
348
    GetHintForPotentiallyRecognizedPath(const std::string &osPath) override;
349
};
350
351
/************************************************************************/
352
/*                            VSICurlHandle                             */
353
/************************************************************************/
354
355
class VSICurlHandle /* non final*/ : public VSIVirtualHandle
356
{
357
    CPL_DISALLOW_COPY_ASSIGN(VSICurlHandle)
358
359
  protected:
360
    VSICurlFilesystemHandlerBase *poFS = nullptr;
361
362
    bool m_bCached = true;
363
364
    mutable FileProp oFileProp{};
365
366
    mutable std::mutex m_oMutex{};
367
    std::string m_osFilename{};  // e.g "/vsicurl/http://example.com/foo"
368
    char *m_pszURL = nullptr;    // e.g "http://example.com/foo"
369
    mutable std::string m_osQueryString{};  // e.g. an Azure SAS
370
371
    CPLStringList m_aosHTTPOptions{};
372
    CPLHTTPRetryParameters
373
        m_oRetryParameters;  // must be initialized in constructor
374
375
    vsi_l_offset lastDownloadedOffset = VSI_L_OFFSET_MAX;
376
    int nBlocksToDownload = 1;
377
378
    bool bStopOnInterruptUntilUninstall = false;
379
    bool bInterrupted = false;
380
    VSICurlReadCbkFunc pfnReadCbk = nullptr;
381
    void *pReadCbkUserData = nullptr;
382
383
    CPLStringList m_aosHeaders{};
384
385
    void DownloadRegionPostProcess(const vsi_l_offset startOffset,
386
                                   const int nBlocks, const char *pBuffer,
387
                                   size_t nSize);
388
389
  private:
390
    vsi_l_offset curOffset = 0;
391
392
    bool bEOF = false;
393
    bool bError = false;
394
395
    virtual std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks);
396
397
    bool m_bUseHead = false;
398
    bool m_bUseRedirectURLIfNoQueryStringParams = false;
399
400
    mutable std::atomic<bool> m_bInterrupt = false;
401
402
    // Specific to Planetary Computer signing:
403
    // https://planetarycomputer.microsoft.com/docs/concepts/sas/
404
    mutable bool m_bPlanetaryComputerURLSigning = false;
405
    mutable std::string m_osPlanetaryComputerCollection{};
406
    void ManagePlanetaryComputerSigning() const;
407
408
    void UpdateQueryString() const;
409
410
    int ReadMultiRangeSingleGet(int nRanges, void **ppData,
411
                                const vsi_l_offset *panOffsets,
412
                                const size_t *panSizes);
413
    std::string GetRedirectURLIfValid(bool &bHasExpired,
414
                                      CPLStringList &aosHTTPOptions) const;
415
416
    void UpdateRedirectInfo(CURL *hCurlHandle,
417
                            const WriteFuncStruct &sWriteFuncHeaderData);
418
419
    // Used by AdviseRead()
420
    struct AdviseReadRange
421
    {
422
        bool bDone = false;
423
        bool bToRetry = true;
424
        double dfSleepDelay = 0.0;
425
        std::mutex oMutex{};
426
        std::condition_variable oCV{};
427
        vsi_l_offset nStartOffset = 0;
428
        size_t nSize = 0;
429
        std::vector<GByte> abyData{};
430
        CPLHTTPRetryContext retryContext;
431
432
        explicit AdviseReadRange(const CPLHTTPRetryParameters &oRetryParameters)
433
0
            : retryContext(oRetryParameters)
434
0
        {
435
0
        }
436
437
        AdviseReadRange(const AdviseReadRange &) = delete;
438
        AdviseReadRange &operator=(const AdviseReadRange &) = delete;
439
        AdviseReadRange(AdviseReadRange &&) = delete;
440
        AdviseReadRange &operator=(AdviseReadRange &&) = delete;
441
    };
442
443
    std::vector<std::unique_ptr<AdviseReadRange>> m_aoAdviseReadRanges{};
444
    std::thread m_oThreadAdviseRead{};
445
    CURLM *m_hCurlMultiHandleForAdviseRead = nullptr;
446
447
  protected:
448
    virtual struct curl_slist *GetCurlHeaders(const std::string & /*osVerb*/,
449
                                              struct curl_slist *psHeaders)
450
168k
    {
451
168k
        return psHeaders;
452
168k
    }
453
454
    virtual bool AllowAutomaticRedirection()
455
171k
    {
456
171k
        return true;
457
171k
    }
458
459
    virtual bool CanRestartOnError(const char *, const char *, bool)
460
5.77k
    {
461
5.77k
        return false;
462
5.77k
    }
463
464
    virtual bool UseLimitRangeGetInsteadOfHead()
465
162k
    {
466
162k
        return false;
467
162k
    }
468
469
    virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
470
                                       int /*response_code*/)
471
161k
    {
472
161k
        return false;
473
161k
    }
474
475
    virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
476
0
    {
477
0
    }
478
479
    void SetURL(const char *pszURL);
480
481
    virtual bool Authenticate(const char * /* pszFilename */)
482
0
    {
483
0
        return false;
484
0
    }
485
486
  public:
487
    VSICurlHandle(VSICurlFilesystemHandlerBase *poFS, const char *pszFilename,
488
                  const char *pszURLIn = nullptr);
489
    ~VSICurlHandle() override;
490
491
    int Seek(vsi_l_offset nOffset, int nWhence) override;
492
    vsi_l_offset Tell() override;
493
    size_t Read(void *pBuffer, size_t nBytes) override;
494
    int ReadMultiRange(int nRanges, void **ppData,
495
                       const vsi_l_offset *panOffsets,
496
                       const size_t *panSizes) override;
497
    size_t Write(const void *pBuffer, size_t nBytes) override;
498
    void ClearErr() override;
499
    int Eof() override;
500
    int Error() override;
501
    int Flush() override;
502
    int Close() override;
503
504
    void Interrupt() override
505
0
    {
506
0
        m_bInterrupt = true;
507
0
    }
508
509
    bool HasPRead() const override
510
0
    {
511
0
        return true;
512
0
    }
513
514
    size_t PRead(void *pBuffer, size_t nSize,
515
                 vsi_l_offset nOffset) const override;
516
517
    void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
518
                    const size_t *panSizes) override;
519
520
    size_t GetAdviseReadTotalBytesLimit() const override;
521
522
    bool IsKnownFileSize() const
523
124k
    {
524
124k
        return oFileProp.bHasComputedFileSize;
525
124k
    }
526
527
    vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
528
529
    virtual vsi_l_offset GetFileSize(bool bSetError)
530
190k
    {
531
190k
        return GetFileSizeOrHeaders(bSetError, false);
532
190k
    }
533
534
    bool Exists(bool bSetError);
535
536
    bool IsDirectory() const
537
129k
    {
538
129k
        return oFileProp.bIsDirectory;
539
129k
    }
540
541
    int GetMode() const
542
124k
    {
543
124k
        return oFileProp.nMode;
544
124k
    }
545
546
    time_t GetMTime() const
547
124k
    {
548
124k
        return oFileProp.mTime;
549
124k
    }
550
551
    const CPLStringList &GetHeaders()
552
0
    {
553
0
        return m_aosHeaders;
554
0
    }
555
556
    int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
557
                       int bStopOnInterruptUntilUninstall);
558
    int UninstallReadCbk();
559
560
    const char *GetURL() const
561
0
    {
562
0
        return m_pszURL;
563
0
    }
564
565
    //! Whether caching of file content and metadata should persist after file closing
566
    void SetCache(bool bCache)
567
86.6k
    {
568
86.6k
        m_bCached = bCache;
569
86.6k
    }
570
};
571
572
/************************************************************************/
573
/*                 VSICurlFilesystemHandlerBaseWritable                 */
574
/************************************************************************/
575
576
class VSICurlFilesystemHandlerBaseWritable /* non final */
577
    : public VSICurlFilesystemHandlerBase
578
{
579
    CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
580
581
  protected:
582
588
    VSICurlFilesystemHandlerBaseWritable() = default;
583
584
    virtual VSIVirtualHandleUniquePtr
585
    CreateWriteHandle(const char *pszFilename, CSLConstList papszOptions) = 0;
586
587
  public:
588
    VSIVirtualHandleUniquePtr Open(const char *pszFilename,
589
                                   const char *pszAccess, bool bSetError,
590
                                   CSLConstList /* papszOptions */) override;
591
592
    bool SupportsSequentialWrite(const char * /* pszPath */,
593
                                 bool /* bAllowLocalTempFile */) override
594
0
    {
595
0
        return true;
596
0
    }
597
598
    bool SupportsRandomWrite(const char * /* pszPath */,
599
                             bool /* bAllowLocalTempFile */) override;
600
};
601
602
/************************************************************************/
603
/*                         IVSIS3LikeFSHandler                          */
604
/************************************************************************/
605
606
class IVSIS3LikeFSHandler /* non final */
607
    : public VSICurlFilesystemHandlerBaseWritable
608
{
609
    CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandler)
610
611
    virtual int MkdirInternal(const char *pszDirname, long nMode,
612
                              bool bDoStatCheck);
613
614
  protected:
615
    char **GetFileList(const char *pszFilename, int nMaxFiles,
616
                       bool *pbGotFileList) override;
617
618
    virtual IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
619
                                                       bool bAllowNoObject) = 0;
620
621
    virtual int CopyObject(const char *oldpath, const char *newpath,
622
                           CSLConstList papszMetadata);
623
624
    int RmdirRecursiveInternal(const char *pszDirname, int nBatchSize);
625
626
    virtual bool
627
    IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
628
0
    {
629
0
        return false;
630
0
    }
631
632
504
    IVSIS3LikeFSHandler() = default;
633
634
  public:
635
    int Unlink(const char *pszFilename) override;
636
    int Mkdir(const char *pszDirname, long nMode) override;
637
    int Rmdir(const char *pszDirname) override;
638
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
639
             int nFlags) override;
640
    int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
641
               void *) override;
642
643
    virtual int CopyFile(const char *pszSource, const char *pszTarget,
644
                         VSILFILE *fpSource, vsi_l_offset nSourceSize,
645
                         const char *const *papszOptions,
646
                         GDALProgressFunc pProgressFunc,
647
                         void *pProgressData) override;
648
649
    virtual int DeleteObject(const char *pszFilename);
650
651
    virtual int *DeleteObjectBatch(CSLConstList papszFilesOrDirs);
652
653
    bool Sync(const char *pszSource, const char *pszTarget,
654
              const char *const *papszOptions, GDALProgressFunc pProgressFunc,
655
              void *pProgressData, char ***ppapszOutputs) override;
656
657
    VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
658
                    const char *const *papszOptions) override;
659
};
660
661
/************************************************************************/
662
/*                IVSIS3LikeFSHandlerWithMultipartUpload                */
663
/************************************************************************/
664
665
class IVSIS3LikeFSHandlerWithMultipartUpload /* non final */
666
    : public IVSIS3LikeFSHandler
667
{
668
    CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandlerWithMultipartUpload)
669
670
  protected:
671
420
    IVSIS3LikeFSHandlerWithMultipartUpload() = default;
672
673
  public:
674
    virtual bool SupportsNonSequentialMultipartUpload() const
675
0
    {
676
0
        return true;
677
0
    }
678
679
    virtual bool SupportsParallelMultipartUpload() const
680
0
    {
681
0
        return true;
682
0
    }
683
684
    virtual bool SupportsMultipartAbort() const = 0;
685
686
    size_t GetUploadChunkSizeInBytes(const char *pszFilename,
687
                                     const char *pszSpecifiedValInBytes);
688
689
    virtual int CopyFileRestartable(const char *pszSource,
690
                                    const char *pszTarget,
691
                                    const char *pszInputPayload,
692
                                    char **ppszOutputPayload,
693
                                    CSLConstList papszOptions,
694
                                    GDALProgressFunc pProgressFunc,
695
                                    void *pProgressData) override;
696
697
    //! Maximum number of parts for multipart upload
698
    // Limit currently used by S3 and GS.
699
    // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
700
    // and https://cloud.google.com/storage/quotas#requests
701
    virtual int GetMaximumPartCount()
702
0
    {
703
0
        return 10000;
704
0
    }
705
706
    //! Minimum size of a part for multipart upload (except last one), in MiB.
707
    // Limit currently used by S3 and GS.
708
    // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
709
    // and https://cloud.google.com/storage/quotas#requests
710
    virtual int GetMinimumPartSizeInMiB()
711
0
    {
712
0
        return 5;
713
0
    }
714
715
    //! Maximum size of a part for multipart upload, in MiB.
716
    // Limit currently used by S3 and GS.
717
    // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
718
    // and https://cloud.google.com/storage/quotas#requests
719
    virtual int GetMaximumPartSizeInMiB()
720
0
    {
721
0
#if SIZEOF_VOIDP == 8
722
0
        return 5 * 1024;
723
#else
724
        // Cannot be larger than 4, otherwise integer overflow would occur
725
        // 1 GiB is the maximum reasonable value on a 32-bit machine
726
        return 1 * 1024;
727
#endif
728
0
    }
729
730
    //! Default size of a part for multipart upload, in MiB.
731
    virtual int GetDefaultPartSizeInMiB()
732
0
    {
733
0
        return 50;
734
0
    }
735
736
    virtual std::string
737
    InitiateMultipartUpload(const std::string &osFilename,
738
                            IVSIS3LikeHandleHelper *poS3HandleHelper,
739
                            const CPLHTTPRetryParameters &oRetryParameters,
740
                            CSLConstList papszOptions);
741
742
    virtual std::string
743
    UploadPart(const std::string &osFilename, int nPartNumber,
744
               const std::string &osUploadID, vsi_l_offset nPosition,
745
               const void *pabyBuffer, size_t nBufferSize,
746
               IVSIS3LikeHandleHelper *poS3HandleHelper,
747
               const CPLHTTPRetryParameters &oRetryParameters,
748
               CSLConstList papszOptions);
749
750
    virtual bool CompleteMultipart(
751
        const std::string &osFilename, const std::string &osUploadID,
752
        const std::vector<std::string> &aosEtags, vsi_l_offset nTotalSize,
753
        IVSIS3LikeHandleHelper *poS3HandleHelper,
754
        const CPLHTTPRetryParameters &oRetryParameters);
755
756
    virtual bool AbortMultipart(const std::string &osFilename,
757
                                const std::string &osUploadID,
758
                                IVSIS3LikeHandleHelper *poS3HandleHelper,
759
                                const CPLHTTPRetryParameters &oRetryParameters);
760
761
    bool AbortPendingUploads(const char *pszFilename) override;
762
763
    bool MultipartUploadGetCapabilities(int *pbNonSequentialUploadSupported,
764
                                        int *pbParallelUploadSupported,
765
                                        int *pbAbortSupported,
766
                                        size_t *pnMinPartSize,
767
                                        size_t *pnMaxPartSize,
768
                                        int *pnMaxPartCount) override;
769
770
    char *MultipartUploadStart(const char *pszFilename,
771
                               CSLConstList papszOptions) override;
772
773
    char *MultipartUploadAddPart(const char *pszFilename,
774
                                 const char *pszUploadId, int nPartNumber,
775
                                 vsi_l_offset nFileOffset, const void *pData,
776
                                 size_t nDataLength,
777
                                 CSLConstList papszOptions) override;
778
779
    bool MultipartUploadEnd(const char *pszFilename, const char *pszUploadId,
780
                            size_t nPartIdsCount,
781
                            const char *const *apszPartIds,
782
                            vsi_l_offset nTotalSize,
783
                            CSLConstList papszOptions) override;
784
785
    bool MultipartUploadAbort(const char *pszFilename, const char *pszUploadId,
786
                              CSLConstList papszOptions) override;
787
};
788
789
/************************************************************************/
790
/*                           IVSIS3LikeHandle                           */
791
/************************************************************************/
792
793
class IVSIS3LikeHandle /* non final */ : public VSICurlHandle
794
{
795
    CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
796
797
  protected:
798
    bool UseLimitRangeGetInsteadOfHead() override
799
5.14k
    {
800
5.14k
        return true;
801
5.14k
    }
802
803
    bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
804
2.61k
    {
805
        // A bit dirty, but on S3, a GET on a existing directory returns a 416
806
2.61k
        return response_code == 416 && EQUAL(pszVerb, "GET") &&
807
0
               std::string(m_pszURL).back() == '/';
808
2.61k
    }
809
810
    void ProcessGetFileSizeResult(const char *pszContent) override
811
0
    {
812
0
        oFileProp.bIsDirectory =
813
0
            strstr(pszContent, "ListBucketResult") != nullptr;
814
0
    }
815
816
  public:
817
    IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
818
                     const char *pszFilename, const char *pszURLIn)
819
71.5k
        : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
820
71.5k
    {
821
71.5k
    }
822
823
    ~IVSIS3LikeHandle() override;
824
};
825
826
/************************************************************************/
827
/*                       VSIMultipartWriteHandle                        */
828
/************************************************************************/
829
830
class VSIMultipartWriteHandle final : public VSIVirtualHandle
831
{
832
    CPL_DISALLOW_COPY_ASSIGN(VSIMultipartWriteHandle)
833
834
    IVSIS3LikeFSHandlerWithMultipartUpload *m_poFS = nullptr;
835
    std::string m_osFilename{};
836
    IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
837
    CPLStringList m_aosOptions{};
838
    CPLStringList m_aosHTTPOptions{};
839
    CPLHTTPRetryParameters m_oRetryParameters;
840
841
    vsi_l_offset m_nCurOffset = 0;
842
    size_t m_nBufferOff = 0;
843
    size_t m_nBufferSize = 0;
844
    bool m_bClosed = false;
845
    GByte *m_pabyBuffer = nullptr;
846
    std::string m_osUploadID{};
847
    int m_nPartNumber = 0;
848
    std::vector<std::string> m_aosEtags{};
849
    bool m_bError = false;
850
851
    WriteFuncStruct m_sWriteFuncHeaderData{};
852
853
    bool UploadPart();
854
    bool DoSinglePartPUT();
855
856
    void InvalidateParentDirectory();
857
858
  public:
859
    VSIMultipartWriteHandle(IVSIS3LikeFSHandlerWithMultipartUpload *poFS,
860
                            const char *pszFilename,
861
                            IVSIS3LikeHandleHelper *poS3HandleHelper,
862
                            CSLConstList papszOptions);
863
    ~VSIMultipartWriteHandle() override;
864
865
    int Seek(vsi_l_offset nOffset, int nWhence) override;
866
    vsi_l_offset Tell() override;
867
    size_t Read(void *pBuffer, size_t nBytes) override;
868
    size_t Write(const void *pBuffer, size_t nBytes) override;
869
870
    void ClearErr() override
871
0
    {
872
0
    }
873
874
    int Error() override
875
0
    {
876
0
        return FALSE;
877
0
    }
878
879
    int Eof() override
880
0
    {
881
0
        return FALSE;
882
0
    }
883
884
    int Close() override;
885
886
    bool IsOK()
887
0
    {
888
0
        return m_pabyBuffer != nullptr;
889
0
    }
890
};
891
892
/************************************************************************/
893
/*                       VSIChunkedWriteHandle()                        */
894
/************************************************************************/
895
896
/** Class with Write() append-only implementation using
897
 * "Transfer-Encoding: chunked" writing
898
 */
899
class VSIChunkedWriteHandle final : public VSIVirtualHandle
900
{
901
    CPL_DISALLOW_COPY_ASSIGN(VSIChunkedWriteHandle)
902
903
    IVSIS3LikeFSHandler *m_poFS = nullptr;
904
    std::string m_osFilename{};
905
    IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
906
    CPLStringList m_aosOptions{};
907
    CPLStringList m_aosHTTPOptions{};
908
    CPLHTTPRetryParameters m_oRetryParameters;
909
910
    vsi_l_offset m_nCurOffset = 0;
911
    size_t m_nBufferOff = 0;
912
    bool m_bError = false;
913
    bool m_bClosed = false;
914
915
    CURLM *m_hCurlMulti = nullptr;
916
    CURL *m_hCurl = nullptr;
917
    const void *m_pBuffer = nullptr;
918
    std::string m_osCurlErrBuf{};
919
    size_t m_nChunkedBufferOff = 0;
920
    size_t m_nChunkedBufferSize = 0;
921
    size_t m_nWrittenInPUT = 0;
922
923
    WriteFuncStruct m_sWriteFuncHeaderData{};
924
925
    static size_t ReadCallBackBufferChunked(char *buffer, size_t size,
926
                                            size_t nitems, void *instream);
927
    int FinishChunkedTransfer();
928
929
    bool DoEmptyPUT();
930
931
    void InvalidateParentDirectory();
932
933
  public:
934
    VSIChunkedWriteHandle(IVSIS3LikeFSHandler *poFS, const char *pszFilename,
935
                          IVSIS3LikeHandleHelper *poS3HandleHelper,
936
                          CSLConstList papszOptions);
937
    ~VSIChunkedWriteHandle() override;
938
939
    int Seek(vsi_l_offset nOffset, int nWhence) override;
940
    vsi_l_offset Tell() override;
941
    size_t Read(void *pBuffer, size_t nBytes) override;
942
    size_t Write(const void *pBuffer, size_t nBytes) override;
943
944
    void ClearErr() override
945
0
    {
946
0
    }
947
948
    int Error() override
949
0
    {
950
0
        return FALSE;
951
0
    }
952
953
    int Eof() override
954
0
    {
955
0
        return FALSE;
956
0
    }
957
958
    int Close() override;
959
};
960
961
/************************************************************************/
962
/*                         VSIAppendWriteHandle                         */
963
/************************************************************************/
964
965
class VSIAppendWriteHandle CPL_NON_FINAL : public VSIVirtualHandle
966
{
967
    CPL_DISALLOW_COPY_ASSIGN(VSIAppendWriteHandle)
968
969
  protected:
970
    VSICurlFilesystemHandlerBase *m_poFS = nullptr;
971
    std::string m_osFSPrefix{};
972
    std::string m_osFilename{};
973
    CPLHTTPRetryParameters m_oRetryParameters{};
974
975
    vsi_l_offset m_nCurOffset = 0;
976
    int m_nBufferOff = 0;
977
    int m_nBufferSize = 0;
978
    int m_nBufferOffReadCallback = 0;
979
    bool m_bClosed = false;
980
    GByte *m_pabyBuffer = nullptr;
981
    bool m_bError = false;
982
983
    static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
984
                                     void *instream);
985
    virtual bool Send(bool bIsLastBlock) = 0;
986
987
  public:
988
    VSIAppendWriteHandle(VSICurlFilesystemHandlerBase *poFS,
989
                         const char *pszFSPrefix, const char *pszFilename,
990
                         int nChunkSize);
991
    ~VSIAppendWriteHandle() override;
992
993
    int Seek(vsi_l_offset nOffset, int nWhence) override;
994
    vsi_l_offset Tell() override;
995
    size_t Read(void *pBuffer, size_t nBytes) override;
996
    size_t Write(const void *pBuffer, size_t nBytes) override;
997
998
    void ClearErr() override
999
0
    {
1000
0
    }
1001
1002
    int Error() override
1003
0
    {
1004
0
        return FALSE;
1005
0
    }
1006
1007
    int Eof() override
1008
0
    {
1009
0
        return FALSE;
1010
0
    }
1011
1012
    int Close() override;
1013
1014
    bool IsOK()
1015
0
    {
1016
0
        return m_pabyBuffer != nullptr;
1017
0
    }
1018
};
1019
1020
/************************************************************************/
1021
/*                    VSIDIRWithMissingDirSynthesis                     */
1022
/************************************************************************/
1023
1024
struct VSIDIRWithMissingDirSynthesis /* non final */ : public VSIDIR
1025
{
1026
    std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
1027
1028
  protected:
1029
    ~VSIDIRWithMissingDirSynthesis() override;
1030
1031
    std::vector<std::string> m_aosSubpathsStack{};
1032
1033
    void SynthetizeMissingDirectories(const std::string &osCurSubdir,
1034
                                      bool bAddEntryForThisSubdir);
1035
};
1036
1037
/************************************************************************/
1038
/*                             VSIDIRS3Like                             */
1039
/************************************************************************/
1040
1041
struct VSIDIRS3Like /* non final */ : public VSIDIRWithMissingDirSynthesis
1042
{
1043
    const std::string m_osDirName;
1044
1045
    int nRecurseDepth = 0;
1046
1047
    std::string osNextMarker{};
1048
    int nPos = 0;
1049
1050
    std::string osBucket{};
1051
    std::string osObjectKey{};
1052
    VSICurlFilesystemHandlerBase *poFS = nullptr;
1053
    IVSIS3LikeFSHandler *poS3FS = nullptr;
1054
    std::unique_ptr<IVSIS3LikeHandleHelper> poHandleHelper{};
1055
    int nMaxFiles = 0;
1056
    bool bCacheEntries = true;
1057
    bool m_bSynthetizeMissingDirectories = false;
1058
    std::string m_osFilterPrefix{};
1059
1060
    // used when listing only the file system prefix
1061
    std::unique_ptr<VSIDIR, decltype(&VSICloseDir)> m_subdir{nullptr,
1062
                                                             VSICloseDir};
1063
1064
    VSIDIRS3Like(const std::string &osDirName, IVSIS3LikeFSHandler *poFSIn)
1065
3.35k
        : m_osDirName(osDirName), poFS(poFSIn), poS3FS(poFSIn)
1066
3.35k
    {
1067
3.35k
    }
1068
1069
    VSIDIRS3Like(const std::string &osDirName,
1070
                 VSICurlFilesystemHandlerBase *poFSIn)
1071
0
        : m_osDirName(osDirName), poFS(poFSIn)
1072
0
    {
1073
0
    }
1074
1075
    VSIDIRS3Like(const VSIDIRS3Like &) = delete;
1076
    VSIDIRS3Like &operator=(const VSIDIRS3Like &) = delete;
1077
1078
    const VSIDIREntry *NextDirEntry() override;
1079
1080
    virtual bool IssueListDir() = 0;
1081
    void clear();
1082
};
1083
1084
/************************************************************************/
1085
/*                          CurlRequestHelper                           */
1086
/************************************************************************/
1087
1088
struct CurlRequestHelper
1089
{
1090
    WriteFuncStruct sWriteFuncData{};
1091
    WriteFuncStruct sWriteFuncHeaderData{};
1092
    char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1093
1094
    CurlRequestHelper();
1095
    ~CurlRequestHelper();
1096
    long perform(CURL *hCurlHandle,
1097
                 struct curl_slist *headers,  // ownership transferred
1098
                 VSICurlFilesystemHandlerBase *poFS,
1099
                 IVSIS3LikeHandleHelper *poS3HandleHelper);
1100
};
1101
1102
/************************************************************************/
1103
/*                       NetworkStatisticsLogger                        */
1104
/************************************************************************/
1105
1106
class NetworkStatisticsLogger
1107
{
1108
    static int gnEnabled;
1109
    static NetworkStatisticsLogger gInstance;
1110
1111
93
    NetworkStatisticsLogger() = default;
1112
1113
    std::mutex m_mutex{};
1114
1115
    struct Counters
1116
    {
1117
        GIntBig nHEAD = 0;
1118
        GIntBig nGET = 0;
1119
        GIntBig nPUT = 0;
1120
        GIntBig nPOST = 0;
1121
        GIntBig nDELETE = 0;
1122
        GIntBig nGETDownloadedBytes = 0;
1123
        GIntBig nPUTUploadedBytes = 0;
1124
        GIntBig nPOSTDownloadedBytes = 0;
1125
        GIntBig nPOSTUploadedBytes = 0;
1126
    };
1127
1128
    enum class ContextPathType
1129
    {
1130
        FILESYSTEM,
1131
        FILE,
1132
        ACTION,
1133
    };
1134
1135
    struct ContextPathItem
1136
    {
1137
        ContextPathType eType;
1138
        std::string osName;
1139
1140
        ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
1141
0
            : eType(eTypeIn), osName(osNameIn)
1142
0
        {
1143
0
        }
1144
1145
        bool operator<(const ContextPathItem &other) const
1146
0
        {
1147
0
            if (static_cast<int>(eType) < static_cast<int>(other.eType))
1148
0
                return true;
1149
0
            if (static_cast<int>(eType) > static_cast<int>(other.eType))
1150
0
                return false;
1151
0
            return osName < other.osName;
1152
0
        }
1153
    };
1154
1155
    struct Stats
1156
    {
1157
        Counters counters{};
1158
        std::map<ContextPathItem, Stats> children{};
1159
1160
        void AsJSON(CPLJSONObject &oJSON) const;
1161
    };
1162
1163
    // Workaround bug in Coverity Scan
1164
    // coverity[generated_default_constructor_used_in_field_initializer]
1165
    Stats m_stats{};
1166
    std::map<GIntBig, std::vector<ContextPathItem>>
1167
        m_mapThreadIdToContextPath{};
1168
1169
    static void ReadEnabled();
1170
1171
    std::vector<Counters *> GetCountersForContext();
1172
1173
  public:
1174
    static inline bool IsEnabled()
1175
2.64M
    {
1176
2.64M
        if (gnEnabled < 0)
1177
16
        {
1178
16
            ReadEnabled();
1179
16
        }
1180
2.64M
        return gnEnabled == TRUE;
1181
2.64M
    }
1182
1183
    static void EnterFileSystem(const char *pszName);
1184
1185
    static void LeaveFileSystem();
1186
1187
    static void EnterFile(const char *pszName);
1188
1189
    static void LeaveFile();
1190
1191
    static void EnterAction(const char *pszName);
1192
1193
    static void LeaveAction();
1194
1195
    static void LogHEAD();
1196
1197
    static void LogGET(size_t nDownloadedBytes);
1198
1199
    static void LogPUT(size_t nUploadedBytes);
1200
1201
    static void LogPOST(size_t nUploadedBytes, size_t nDownloadedBytes);
1202
1203
    static void LogDELETE();
1204
1205
    static void Reset();
1206
1207
    static std::string GetReportAsSerializedJSON();
1208
};
1209
1210
struct NetworkStatisticsFileSystem
1211
{
1212
    inline explicit NetworkStatisticsFileSystem(const char *pszName)
1213
555k
    {
1214
555k
        NetworkStatisticsLogger::EnterFileSystem(pszName);
1215
555k
    }
1216
1217
    inline ~NetworkStatisticsFileSystem()
1218
555k
    {
1219
555k
        NetworkStatisticsLogger::LeaveFileSystem();
1220
555k
    }
1221
};
1222
1223
struct NetworkStatisticsFile
1224
{
1225
    inline explicit NetworkStatisticsFile(const char *pszName)
1226
122k
    {
1227
122k
        NetworkStatisticsLogger::EnterFile(pszName);
1228
122k
    }
1229
1230
    inline ~NetworkStatisticsFile()
1231
122k
    {
1232
122k
        NetworkStatisticsLogger::LeaveFile();
1233
122k
    }
1234
};
1235
1236
struct NetworkStatisticsAction
1237
{
1238
    inline explicit NetworkStatisticsAction(const char *pszName)
1239
555k
    {
1240
555k
        NetworkStatisticsLogger::EnterAction(pszName);
1241
555k
    }
1242
1243
    inline ~NetworkStatisticsAction()
1244
555k
    {
1245
555k
        NetworkStatisticsLogger::LeaveAction();
1246
555k
    }
1247
};
1248
1249
}  // namespace cpl
1250
1251
int VSICURLGetDownloadChunkSize();
1252
1253
void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1254
                                VSICurlReadCbkFunc pfnReadCbk,
1255
                                void *pReadCbkUserData);
1256
size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1257
                              void *req);
1258
void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr,
1259
                         std::atomic<bool> *pbInterrupt = nullptr);
1260
void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1261
1262
int VSICurlParseUnixPermissions(const char *pszPermissions);
1263
1264
// Cache of file properties (size, etc.)
1265
bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1266
void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1267
void VSICURLInvalidateCachedFileProp(const char *pszURL);
1268
void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1269
void VSICURLDestroyCacheFileProp();
1270
1271
void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1272
1273
//! @endcond
1274
1275
#endif  // HAVE_CURL
1276
1277
#endif  // CPL_VSIL_CURL_CLASS_H_INCLUDED