Coverage Report

Created: 2025-08-11 09:23

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