Coverage Report

Created: 2025-07-23 09:13

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