Line data Source code
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_string.h"
23 : #include "cpl_vsil_curl_priv.h"
24 : #include "cpl_mem_cache.h"
25 :
26 : #include "cpl_curl_priv.h"
27 :
28 : #include <algorithm>
29 : #include <atomic>
30 : #include <condition_variable>
31 : #include <set>
32 : #include <map>
33 : #include <memory>
34 : #include <mutex>
35 : #include <thread>
36 : #include <utility>
37 :
38 : // To avoid aliasing to CopyFile to CopyFileA on Windows
39 : #ifdef CopyFile
40 : #undef CopyFile
41 : #endif
42 :
43 : //! @cond Doxygen_Suppress
44 :
45 : // Leave it for backward compatibility, but deprecate.
46 : #define HAVE_CURLINFO_REDIRECT_URL
47 :
48 : void VSICurlStreamingClearCache(void); // from cpl_vsil_curl_streaming.cpp
49 :
50 : struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
51 : const char *const *papszOptions);
52 : struct curl_slist *VSICurlMergeHeaders(struct curl_slist *poDest,
53 : struct curl_slist *poSrcToDestroy);
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 36 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
126 : void *instream)
127 : {
128 36 : PutData *poThis = static_cast<PutData *>(instream);
129 36 : const size_t nSizeMax = size * nitems;
130 : const size_t nSizeToWrite =
131 36 : std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
132 36 : memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
133 36 : poThis->nOff += nSizeToWrite;
134 36 : return nSizeToWrite;
135 : }
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 47401 : FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
154 47401 : : filename_(filename), offset_(offset)
155 : {
156 47401 : }
157 :
158 46414 : bool operator==(const FilenameOffsetPair &other) const
159 : {
160 46414 : return filename_ == other.filename_ && offset_ == other.offset_;
161 : }
162 : };
163 :
164 : struct FilenameOffsetPairHasher
165 : {
166 48113 : std::size_t operator()(const FilenameOffsetPair &k) const
167 : {
168 48113 : return std::hash<std::string>()(k.filename_) ^
169 48113 : std::hash<vsi_l_offset>()(k.offset_);
170 : }
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 : VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
246 : bool bSetError,
247 : CSLConstList /* papszOptions */) override;
248 :
249 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
250 : int nFlags) override;
251 : int Unlink(const char *pszFilename) override;
252 : int Rename(const char *oldpath, const char *newpath) override;
253 : int Mkdir(const char *pszDirname, long nMode) override;
254 : int Rmdir(const char *pszDirname) override;
255 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
256 : char **SiblingFiles(const char *pszFilename) override;
257 :
258 14 : int HasOptimizedReadMultiRange(const char * /* pszPath */) override
259 : {
260 14 : return true;
261 : }
262 :
263 : const char *GetActualURL(const char *pszFilename) override;
264 :
265 : const char *GetOptions() override;
266 :
267 : char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
268 : CSLConstList papszOptions) override;
269 :
270 : char **ReadDirInternal(const char *pszDirname, int nMaxFiles,
271 : bool *pbGotFileList);
272 : void InvalidateDirContent(const std::string &osDirname);
273 :
274 : virtual const char *GetDebugKey() const = 0;
275 :
276 : virtual std::string GetFSPrefix() const = 0;
277 : virtual bool AllowCachedDataFor(const char *pszFilename);
278 :
279 2 : virtual bool IsLocal(const char * /* pszPath */) override
280 : {
281 2 : return false;
282 : }
283 :
284 : virtual bool
285 2 : SupportsSequentialWrite(const char * /* pszPath */,
286 : bool /* bAllowLocalTempFile */) override
287 : {
288 2 : return false;
289 : }
290 :
291 1 : virtual bool SupportsRandomWrite(const char * /* pszPath */,
292 : bool /* bAllowLocalTempFile */) override
293 : {
294 1 : return false;
295 : }
296 :
297 : std::shared_ptr<std::string> GetRegion(const char *pszURL,
298 : vsi_l_offset nFileOffsetStart);
299 :
300 : void AddRegion(const char *pszURL, vsi_l_offset nFileOffsetStart,
301 : size_t nSize, const char *pData);
302 :
303 : std::pair<bool, std::string>
304 : NotifyStartDownloadRegion(const std::string &osURL,
305 : vsi_l_offset startOffset, int nBlocks);
306 : void NotifyStopDownloadRegion(const std::string &osURL,
307 : vsi_l_offset startOffset, int nBlocks,
308 : const std::string &osData);
309 :
310 : bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
311 : void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
312 : void InvalidateCachedData(const char *pszURL);
313 :
314 : CURLM *GetCurlMultiHandleFor(const std::string &osURL);
315 :
316 : virtual void ClearCache();
317 : virtual void PartialClearCache(const char *pszFilename);
318 :
319 : bool GetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
320 : void SetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
321 : bool ExistsInCacheDirList(const std::string &osDirname, bool *pbIsDir);
322 :
323 : virtual std::string GetURLFromFilename(const std::string &osFilename) const;
324 :
325 : std::string
326 : GetStreamingFilename(const std::string &osFilename) const override = 0;
327 :
328 : static std::set<std::string> GetS3IgnoredStorageClasses();
329 : };
330 :
331 : class VSICurlFilesystemHandler : public VSICurlFilesystemHandlerBase
332 : {
333 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
334 :
335 : public:
336 1392 : VSICurlFilesystemHandler() = default;
337 :
338 863 : const char *GetDebugKey() const override
339 : {
340 863 : return "VSICURL";
341 : }
342 :
343 48528 : std::string GetFSPrefix() const override
344 : {
345 48528 : return "/vsicurl/";
346 : }
347 :
348 : std::string
349 : GetStreamingFilename(const std::string &osFilename) const override;
350 : };
351 :
352 : /************************************************************************/
353 : /* VSICurlHandle */
354 : /************************************************************************/
355 :
356 : class VSICurlHandle : public VSIVirtualHandle
357 : {
358 : CPL_DISALLOW_COPY_ASSIGN(VSICurlHandle)
359 :
360 : protected:
361 : VSICurlFilesystemHandlerBase *poFS = nullptr;
362 :
363 : bool m_bCached = true;
364 :
365 : mutable FileProp oFileProp{};
366 :
367 : mutable std::mutex m_oMutex{};
368 : std::string m_osFilename{}; // e.g "/vsicurl/http://example.com/foo"
369 : char *m_pszURL = nullptr; // e.g "http://example.com/foo"
370 : mutable std::string m_osQueryString{}; // e.g. an Azure SAS
371 :
372 : CPLStringList m_aosHTTPOptions{};
373 : CPLHTTPRetryParameters
374 : m_oRetryParameters; // must be initialized in constructor
375 :
376 : vsi_l_offset lastDownloadedOffset = VSI_L_OFFSET_MAX;
377 : int nBlocksToDownload = 1;
378 :
379 : bool bStopOnInterruptUntilUninstall = false;
380 : bool bInterrupted = false;
381 : VSICurlReadCbkFunc pfnReadCbk = nullptr;
382 : void *pReadCbkUserData = nullptr;
383 :
384 : CPLStringList m_aosHeaders{};
385 :
386 : void DownloadRegionPostProcess(const vsi_l_offset startOffset,
387 : const int nBlocks, const char *pBuffer,
388 : size_t nSize);
389 :
390 : private:
391 : vsi_l_offset curOffset = 0;
392 :
393 : bool bEOF = false;
394 : bool bError = false;
395 :
396 : virtual std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks);
397 :
398 : bool m_bUseHead = false;
399 : bool m_bUseRedirectURLIfNoQueryStringParams = false;
400 :
401 : mutable std::atomic<bool> m_bInterrupt = false;
402 :
403 : // Specific to Planetary Computer signing:
404 : // https://planetarycomputer.microsoft.com/docs/concepts/sas/
405 : mutable bool m_bPlanetaryComputerURLSigning = false;
406 : mutable std::string m_osPlanetaryComputerCollection{};
407 : void ManagePlanetaryComputerSigning() const;
408 :
409 : void UpdateQueryString() const;
410 :
411 : int ReadMultiRangeSingleGet(int nRanges, void **ppData,
412 : const vsi_l_offset *panOffsets,
413 : const size_t *panSizes);
414 : std::string GetRedirectURLIfValid(bool &bHasExpired,
415 : CPLStringList &aosHTTPOptions) const;
416 :
417 : void UpdateRedirectInfo(CURL *hCurlHandle,
418 : const WriteFuncStruct &sWriteFuncHeaderData);
419 :
420 : // Used by AdviseRead()
421 : struct AdviseReadRange
422 : {
423 : bool bDone = false;
424 : std::mutex oMutex{};
425 : std::condition_variable oCV{};
426 : vsi_l_offset nStartOffset = 0;
427 : size_t nSize = 0;
428 : std::vector<GByte> abyData{};
429 : };
430 :
431 : std::vector<std::unique_ptr<AdviseReadRange>> m_aoAdviseReadRanges{};
432 : std::thread m_oThreadAdviseRead{};
433 :
434 : protected:
435 : virtual struct curl_slist *
436 525 : GetCurlHeaders(const std::string & /*osVerb*/,
437 : const struct curl_slist * /* psExistingHeaders */)
438 : {
439 525 : return nullptr;
440 : }
441 :
442 594 : virtual bool AllowAutomaticRedirection()
443 : {
444 594 : return true;
445 : }
446 :
447 1 : virtual bool CanRestartOnError(const char *, const char *, bool)
448 : {
449 1 : return false;
450 : }
451 :
452 718 : virtual bool UseLimitRangeGetInsteadOfHead()
453 : {
454 718 : return false;
455 : }
456 :
457 261 : virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
458 : int /*response_code*/)
459 : {
460 261 : return false;
461 : }
462 :
463 123 : virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
464 : {
465 123 : }
466 :
467 : void SetURL(const char *pszURL);
468 :
469 0 : virtual bool Authenticate(const char * /* pszFilename */)
470 : {
471 0 : return false;
472 : }
473 :
474 : public:
475 : VSICurlHandle(VSICurlFilesystemHandlerBase *poFS, const char *pszFilename,
476 : const char *pszURLIn = nullptr);
477 : ~VSICurlHandle() override;
478 :
479 : int Seek(vsi_l_offset nOffset, int nWhence) override;
480 : vsi_l_offset Tell() override;
481 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
482 : int ReadMultiRange(int nRanges, void **ppData,
483 : const vsi_l_offset *panOffsets,
484 : const size_t *panSizes) override;
485 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
486 : void ClearErr() override;
487 : int Eof() override;
488 : int Error() override;
489 : int Flush() override;
490 : int Close() override;
491 :
492 0 : void Interrupt() override
493 : {
494 0 : m_bInterrupt = true;
495 0 : }
496 :
497 14 : bool HasPRead() const override
498 : {
499 14 : return true;
500 : }
501 :
502 : size_t PRead(void *pBuffer, size_t nSize,
503 : vsi_l_offset nOffset) const override;
504 :
505 : void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
506 : const size_t *panSizes) override;
507 :
508 : size_t GetAdviseReadTotalBytesLimit() const override;
509 :
510 577 : bool IsKnownFileSize() const
511 : {
512 577 : return oFileProp.bHasComputedFileSize;
513 : }
514 :
515 : vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
516 :
517 937 : virtual vsi_l_offset GetFileSize(bool bSetError)
518 : {
519 937 : return GetFileSizeOrHeaders(bSetError, false);
520 : }
521 :
522 : bool Exists(bool bSetError);
523 :
524 703 : bool IsDirectory() const
525 : {
526 703 : return oFileProp.bIsDirectory;
527 : }
528 :
529 577 : int GetMode() const
530 : {
531 577 : return oFileProp.nMode;
532 : }
533 :
534 577 : time_t GetMTime() const
535 : {
536 577 : return oFileProp.mTime;
537 : }
538 :
539 4 : const CPLStringList &GetHeaders()
540 : {
541 4 : return m_aosHeaders;
542 : }
543 :
544 : int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
545 : int bStopOnInterruptUntilUninstall);
546 : int UninstallReadCbk();
547 :
548 5 : const char *GetURL() const
549 : {
550 5 : return m_pszURL;
551 : }
552 : };
553 :
554 : /************************************************************************/
555 : /* VSICurlFilesystemHandlerBaseWritable */
556 : /************************************************************************/
557 :
558 : class VSICurlFilesystemHandlerBaseWritable : public VSICurlFilesystemHandlerBase
559 : {
560 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
561 :
562 : protected:
563 9744 : VSICurlFilesystemHandlerBaseWritable() = default;
564 :
565 : virtual VSIVirtualHandleUniquePtr
566 : CreateWriteHandle(const char *pszFilename, CSLConstList papszOptions) = 0;
567 :
568 : public:
569 : VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
570 : bool bSetError, CSLConstList papszOptions) override;
571 :
572 1 : bool SupportsSequentialWrite(const char * /* pszPath */,
573 : bool /* bAllowLocalTempFile */) override
574 : {
575 1 : return true;
576 : }
577 :
578 : bool SupportsRandomWrite(const char * /* pszPath */,
579 : bool /* bAllowLocalTempFile */) override;
580 : };
581 :
582 : /************************************************************************/
583 : /* IVSIS3LikeFSHandler */
584 : /************************************************************************/
585 :
586 : class IVSIS3LikeFSHandler : public VSICurlFilesystemHandlerBaseWritable
587 : {
588 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandler)
589 :
590 : virtual int MkdirInternal(const char *pszDirname, long nMode,
591 : bool bDoStatCheck);
592 :
593 : protected:
594 : char **GetFileList(const char *pszFilename, int nMaxFiles,
595 : bool *pbGotFileList) override;
596 :
597 : virtual IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
598 : bool bAllowNoObject) = 0;
599 :
600 : virtual int CopyObject(const char *oldpath, const char *newpath,
601 : CSLConstList papszMetadata);
602 :
603 : int RmdirRecursiveInternal(const char *pszDirname, int nBatchSize);
604 :
605 : virtual bool
606 0 : IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
607 : {
608 0 : return false;
609 : }
610 :
611 8352 : IVSIS3LikeFSHandler() = default;
612 :
613 : public:
614 : int Unlink(const char *pszFilename) override;
615 : int Mkdir(const char *pszDirname, long nMode) override;
616 : int Rmdir(const char *pszDirname) override;
617 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
618 : int nFlags) override;
619 : int Rename(const char *oldpath, const char *newpath) override;
620 :
621 : virtual int CopyFile(const char *pszSource, const char *pszTarget,
622 : VSILFILE *fpSource, vsi_l_offset nSourceSize,
623 : const char *const *papszOptions,
624 : GDALProgressFunc pProgressFunc,
625 : void *pProgressData) override;
626 :
627 : virtual int DeleteObject(const char *pszFilename);
628 :
629 : virtual int *DeleteObjectBatch(CSLConstList papszFilesOrDirs);
630 :
631 : bool Sync(const char *pszSource, const char *pszTarget,
632 : const char *const *papszOptions, GDALProgressFunc pProgressFunc,
633 : void *pProgressData, char ***ppapszOutputs) override;
634 :
635 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
636 : const char *const *papszOptions) override;
637 : };
638 :
639 : /************************************************************************/
640 : /* IVSIS3LikeFSHandlerWithMultipartUpload */
641 : /************************************************************************/
642 :
643 : class IVSIS3LikeFSHandlerWithMultipartUpload : public IVSIS3LikeFSHandler
644 : {
645 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandlerWithMultipartUpload)
646 :
647 : protected:
648 6960 : IVSIS3LikeFSHandlerWithMultipartUpload() = default;
649 :
650 : public:
651 5 : virtual bool SupportsNonSequentialMultipartUpload() const
652 : {
653 5 : return true;
654 : }
655 :
656 36 : virtual bool SupportsParallelMultipartUpload() const
657 : {
658 36 : return true;
659 : }
660 :
661 : virtual bool SupportsMultipartAbort() const = 0;
662 :
663 : size_t GetUploadChunkSizeInBytes(const char *pszFilename,
664 : const char *pszSpecifiedValInBytes);
665 :
666 : virtual int CopyFileRestartable(const char *pszSource,
667 : const char *pszTarget,
668 : const char *pszInputPayload,
669 : char **ppszOutputPayload,
670 : CSLConstList papszOptions,
671 : GDALProgressFunc pProgressFunc,
672 : void *pProgressData) override;
673 :
674 : //! Maximum number of parts for multipart upload
675 : // Limit currently used by S3 and GS.
676 : // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
677 : // and https://cloud.google.com/storage/quotas#requests
678 20 : virtual int GetMaximumPartCount()
679 : {
680 20 : return 10000;
681 : }
682 :
683 : //! Minimum size of a part for multipart upload (except last one), in MiB.
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 5 : virtual int GetMinimumPartSizeInMiB()
688 : {
689 5 : return 5;
690 : }
691 :
692 : //! Maximum size of a part for multipart upload, 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 60 : virtual int GetMaximumPartSizeInMiB()
697 : {
698 : #if SIZEOF_VOIDP == 8
699 60 : return 5 * 1024;
700 : #else
701 : // Cannot be larger than 4, otherwise integer overflow would occur
702 : // 1 GiB is the maximum reasonable value on a 32-bit machine
703 : return 1 * 1024;
704 : #endif
705 : }
706 :
707 : //! Default size of a part for multipart upload, in MiB.
708 53 : virtual int GetDefaultPartSizeInMiB()
709 : {
710 53 : return 50;
711 : }
712 :
713 : virtual std::string
714 : InitiateMultipartUpload(const std::string &osFilename,
715 : IVSIS3LikeHandleHelper *poS3HandleHelper,
716 : const CPLHTTPRetryParameters &oRetryParameters,
717 : CSLConstList papszOptions);
718 :
719 : virtual std::string
720 : UploadPart(const std::string &osFilename, int nPartNumber,
721 : const std::string &osUploadID, vsi_l_offset nPosition,
722 : const void *pabyBuffer, size_t nBufferSize,
723 : IVSIS3LikeHandleHelper *poS3HandleHelper,
724 : const CPLHTTPRetryParameters &oRetryParameters,
725 : CSLConstList papszOptions);
726 :
727 : virtual bool CompleteMultipart(
728 : const std::string &osFilename, const std::string &osUploadID,
729 : const std::vector<std::string> &aosEtags, vsi_l_offset nTotalSize,
730 : IVSIS3LikeHandleHelper *poS3HandleHelper,
731 : const CPLHTTPRetryParameters &oRetryParameters);
732 :
733 : virtual bool AbortMultipart(const std::string &osFilename,
734 : const std::string &osUploadID,
735 : IVSIS3LikeHandleHelper *poS3HandleHelper,
736 : const CPLHTTPRetryParameters &oRetryParameters);
737 :
738 : bool AbortPendingUploads(const char *pszFilename) override;
739 :
740 : bool MultipartUploadGetCapabilities(int *pbNonSequentialUploadSupported,
741 : int *pbParallelUploadSupported,
742 : int *pbAbortSupported,
743 : size_t *pnMinPartSize,
744 : size_t *pnMaxPartSize,
745 : int *pnMaxPartCount) override;
746 :
747 : char *MultipartUploadStart(const char *pszFilename,
748 : CSLConstList papszOptions) override;
749 :
750 : char *MultipartUploadAddPart(const char *pszFilename,
751 : const char *pszUploadId, int nPartNumber,
752 : vsi_l_offset nFileOffset, const void *pData,
753 : size_t nDataLength,
754 : CSLConstList papszOptions) override;
755 :
756 : bool MultipartUploadEnd(const char *pszFilename, const char *pszUploadId,
757 : size_t nPartIdsCount,
758 : const char *const *apszPartIds,
759 : vsi_l_offset nTotalSize,
760 : CSLConstList papszOptions) override;
761 :
762 : bool MultipartUploadAbort(const char *pszFilename, const char *pszUploadId,
763 : CSLConstList papszOptions) override;
764 : };
765 :
766 : /************************************************************************/
767 : /* IVSIS3LikeHandle */
768 : /************************************************************************/
769 :
770 : class IVSIS3LikeHandle : public VSICurlHandle
771 : {
772 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
773 :
774 : protected:
775 291 : bool UseLimitRangeGetInsteadOfHead() override
776 : {
777 291 : return true;
778 : }
779 :
780 107 : bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
781 : {
782 : // A bit dirty, but on S3, a GET on a existing directory returns a 416
783 111 : return response_code == 416 && EQUAL(pszVerb, "GET") &&
784 111 : std::string(m_pszURL).back() == '/';
785 : }
786 :
787 52 : void ProcessGetFileSizeResult(const char *pszContent) override
788 : {
789 52 : oFileProp.bIsDirectory =
790 52 : strstr(pszContent, "ListBucketResult") != nullptr;
791 52 : }
792 :
793 : public:
794 255 : IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
795 : const char *pszFilename, const char *pszURLIn)
796 255 : : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
797 : {
798 255 : }
799 :
800 255 : ~IVSIS3LikeHandle() override
801 255 : {
802 255 : }
803 : };
804 :
805 : /************************************************************************/
806 : /* VSIMultipartWriteHandle */
807 : /************************************************************************/
808 :
809 : class VSIMultipartWriteHandle final : public VSIVirtualHandle
810 : {
811 : CPL_DISALLOW_COPY_ASSIGN(VSIMultipartWriteHandle)
812 :
813 : IVSIS3LikeFSHandlerWithMultipartUpload *m_poFS = nullptr;
814 : std::string m_osFilename{};
815 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
816 : CPLStringList m_aosOptions{};
817 : CPLStringList m_aosHTTPOptions{};
818 : CPLHTTPRetryParameters m_oRetryParameters;
819 :
820 : vsi_l_offset m_nCurOffset = 0;
821 : size_t m_nBufferOff = 0;
822 : size_t m_nBufferSize = 0;
823 : bool m_bClosed = false;
824 : GByte *m_pabyBuffer = nullptr;
825 : std::string m_osUploadID{};
826 : int m_nPartNumber = 0;
827 : std::vector<std::string> m_aosEtags{};
828 : bool m_bError = false;
829 :
830 : WriteFuncStruct m_sWriteFuncHeaderData{};
831 :
832 : bool UploadPart();
833 : bool DoSinglePartPUT();
834 :
835 : void InvalidateParentDirectory();
836 :
837 : public:
838 : VSIMultipartWriteHandle(IVSIS3LikeFSHandlerWithMultipartUpload *poFS,
839 : const char *pszFilename,
840 : IVSIS3LikeHandleHelper *poS3HandleHelper,
841 : CSLConstList papszOptions);
842 : ~VSIMultipartWriteHandle() override;
843 :
844 : int Seek(vsi_l_offset nOffset, int nWhence) override;
845 : vsi_l_offset Tell() override;
846 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
847 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
848 :
849 0 : void ClearErr() override
850 : {
851 0 : }
852 :
853 0 : int Error() override
854 : {
855 0 : return FALSE;
856 : }
857 :
858 0 : int Eof() override
859 : {
860 0 : return FALSE;
861 : }
862 :
863 : int Close() override;
864 :
865 41 : bool IsOK()
866 : {
867 41 : return m_pabyBuffer != nullptr;
868 : }
869 : };
870 :
871 : /************************************************************************/
872 : /* VSIChunkedWriteHandle() */
873 : /************************************************************************/
874 :
875 : /** Class with Write() append-only implementation using
876 : * "Transfer-Encoding: chunked" writing
877 : */
878 : class VSIChunkedWriteHandle final : public VSIVirtualHandle
879 : {
880 : CPL_DISALLOW_COPY_ASSIGN(VSIChunkedWriteHandle)
881 :
882 : IVSIS3LikeFSHandler *m_poFS = nullptr;
883 : std::string m_osFilename{};
884 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
885 : CPLStringList m_aosOptions{};
886 : CPLStringList m_aosHTTPOptions{};
887 : CPLHTTPRetryParameters m_oRetryParameters;
888 :
889 : vsi_l_offset m_nCurOffset = 0;
890 : size_t m_nBufferOff = 0;
891 : bool m_bError = false;
892 : bool m_bClosed = false;
893 :
894 : CURLM *m_hCurlMulti = nullptr;
895 : CURL *m_hCurl = nullptr;
896 : const void *m_pBuffer = nullptr;
897 : std::string m_osCurlErrBuf{};
898 : size_t m_nChunkedBufferOff = 0;
899 : size_t m_nChunkedBufferSize = 0;
900 : size_t m_nWrittenInPUT = 0;
901 :
902 : WriteFuncStruct m_sWriteFuncHeaderData{};
903 :
904 : static size_t ReadCallBackBufferChunked(char *buffer, size_t size,
905 : size_t nitems, void *instream);
906 : int FinishChunkedTransfer();
907 :
908 : bool DoEmptyPUT();
909 :
910 : void InvalidateParentDirectory();
911 :
912 : public:
913 : VSIChunkedWriteHandle(IVSIS3LikeFSHandler *poFS, const char *pszFilename,
914 : IVSIS3LikeHandleHelper *poS3HandleHelper,
915 : CSLConstList papszOptions);
916 : virtual ~VSIChunkedWriteHandle();
917 :
918 : int Seek(vsi_l_offset nOffset, int nWhence) override;
919 : vsi_l_offset Tell() override;
920 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
921 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
922 :
923 0 : void ClearErr() override
924 : {
925 0 : }
926 :
927 0 : int Error() override
928 : {
929 0 : return FALSE;
930 : }
931 :
932 0 : int Eof() override
933 : {
934 0 : return FALSE;
935 : }
936 :
937 : int Close() override;
938 : };
939 :
940 : /************************************************************************/
941 : /* VSIAppendWriteHandle */
942 : /************************************************************************/
943 :
944 : class VSIAppendWriteHandle CPL_NON_FINAL : public VSIVirtualHandle
945 : {
946 : CPL_DISALLOW_COPY_ASSIGN(VSIAppendWriteHandle)
947 :
948 : protected:
949 : VSICurlFilesystemHandlerBase *m_poFS = nullptr;
950 : std::string m_osFSPrefix{};
951 : std::string m_osFilename{};
952 : CPLHTTPRetryParameters m_oRetryParameters{};
953 :
954 : vsi_l_offset m_nCurOffset = 0;
955 : int m_nBufferOff = 0;
956 : int m_nBufferSize = 0;
957 : int m_nBufferOffReadCallback = 0;
958 : bool m_bClosed = false;
959 : GByte *m_pabyBuffer = nullptr;
960 : bool m_bError = false;
961 :
962 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
963 : void *instream);
964 : virtual bool Send(bool bIsLastBlock) = 0;
965 :
966 : public:
967 : VSIAppendWriteHandle(VSICurlFilesystemHandlerBase *poFS,
968 : const char *pszFSPrefix, const char *pszFilename,
969 : int nChunkSize);
970 : virtual ~VSIAppendWriteHandle();
971 :
972 : int Seek(vsi_l_offset nOffset, int nWhence) override;
973 : vsi_l_offset Tell() override;
974 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
975 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
976 :
977 0 : void ClearErr() override
978 : {
979 0 : }
980 :
981 0 : int Error() override
982 : {
983 0 : return FALSE;
984 : }
985 :
986 0 : int Eof() override
987 : {
988 0 : return FALSE;
989 : }
990 :
991 : int Close() override;
992 :
993 10 : bool IsOK()
994 : {
995 10 : return m_pabyBuffer != nullptr;
996 : }
997 : };
998 :
999 : /************************************************************************/
1000 : /* VSIDIRWithMissingDirSynthesis */
1001 : /************************************************************************/
1002 :
1003 : struct VSIDIRWithMissingDirSynthesis : public VSIDIR
1004 : {
1005 : std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
1006 :
1007 : protected:
1008 : std::vector<std::string> m_aosSubpathsStack{};
1009 :
1010 : void SynthetizeMissingDirectories(const std::string &osCurSubdir,
1011 : bool bAddEntryForThisSubdir);
1012 : };
1013 :
1014 : /************************************************************************/
1015 : /* CurlRequestHelper */
1016 : /************************************************************************/
1017 :
1018 : struct CurlRequestHelper
1019 : {
1020 : WriteFuncStruct sWriteFuncData{};
1021 : WriteFuncStruct sWriteFuncHeaderData{};
1022 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1023 :
1024 : CurlRequestHelper();
1025 : ~CurlRequestHelper();
1026 : long perform(CURL *hCurlHandle,
1027 : struct curl_slist *headers, // ownership transferred
1028 : VSICurlFilesystemHandlerBase *poFS,
1029 : IVSIS3LikeHandleHelper *poS3HandleHelper);
1030 : };
1031 :
1032 : /************************************************************************/
1033 : /* NetworkStatisticsLogger */
1034 : /************************************************************************/
1035 :
1036 : class NetworkStatisticsLogger
1037 : {
1038 : static int gnEnabled;
1039 : static NetworkStatisticsLogger gInstance;
1040 :
1041 1414 : NetworkStatisticsLogger() = default;
1042 :
1043 : std::mutex m_mutex{};
1044 :
1045 : struct Counters
1046 : {
1047 : GIntBig nHEAD = 0;
1048 : GIntBig nGET = 0;
1049 : GIntBig nPUT = 0;
1050 : GIntBig nPOST = 0;
1051 : GIntBig nDELETE = 0;
1052 : GIntBig nGETDownloadedBytes = 0;
1053 : GIntBig nPUTUploadedBytes = 0;
1054 : GIntBig nPOSTDownloadedBytes = 0;
1055 : GIntBig nPOSTUploadedBytes = 0;
1056 : };
1057 :
1058 : enum class ContextPathType
1059 : {
1060 : FILESYSTEM,
1061 : FILE,
1062 : ACTION,
1063 : };
1064 :
1065 : struct ContextPathItem
1066 : {
1067 : ContextPathType eType;
1068 : std::string osName;
1069 :
1070 3 : ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
1071 3 : : eType(eTypeIn), osName(osNameIn)
1072 : {
1073 3 : }
1074 :
1075 0 : bool operator<(const ContextPathItem &other) const
1076 : {
1077 0 : if (static_cast<int>(eType) < static_cast<int>(other.eType))
1078 0 : return true;
1079 0 : if (static_cast<int>(eType) > static_cast<int>(other.eType))
1080 0 : return false;
1081 0 : return osName < other.osName;
1082 : }
1083 : };
1084 :
1085 : struct Stats
1086 : {
1087 : Counters counters{};
1088 : std::map<ContextPathItem, Stats> children{};
1089 :
1090 : void AsJSON(CPLJSONObject &oJSON) const;
1091 : };
1092 :
1093 : // Workaround bug in Coverity Scan
1094 : // coverity[generated_default_constructor_used_in_field_initializer]
1095 : Stats m_stats{};
1096 : std::map<GIntBig, std::vector<ContextPathItem>>
1097 : m_mapThreadIdToContextPath{};
1098 :
1099 : static void ReadEnabled();
1100 :
1101 : std::vector<Counters *> GetCountersForContext();
1102 :
1103 : public:
1104 286026 : static inline bool IsEnabled()
1105 : {
1106 286026 : if (gnEnabled < 0)
1107 : {
1108 6 : ReadEnabled();
1109 : }
1110 286026 : return gnEnabled == TRUE;
1111 : }
1112 :
1113 : static void EnterFileSystem(const char *pszName);
1114 :
1115 : static void LeaveFileSystem();
1116 :
1117 : static void EnterFile(const char *pszName);
1118 :
1119 : static void LeaveFile();
1120 :
1121 : static void EnterAction(const char *pszName);
1122 :
1123 : static void LeaveAction();
1124 :
1125 : static void LogHEAD();
1126 :
1127 : static void LogGET(size_t nDownloadedBytes);
1128 :
1129 : static void LogPUT(size_t nUploadedBytes);
1130 :
1131 : static void LogPOST(size_t nUploadedBytes, size_t nDownloadedBytes);
1132 :
1133 : static void LogDELETE();
1134 :
1135 : static void Reset();
1136 :
1137 : static std::string GetReportAsSerializedJSON();
1138 : };
1139 :
1140 : struct NetworkStatisticsFileSystem
1141 : {
1142 47950 : inline explicit NetworkStatisticsFileSystem(const char *pszName)
1143 : {
1144 47950 : NetworkStatisticsLogger::EnterFileSystem(pszName);
1145 47950 : }
1146 :
1147 47950 : inline ~NetworkStatisticsFileSystem()
1148 : {
1149 47950 : NetworkStatisticsLogger::LeaveFileSystem();
1150 47950 : }
1151 : };
1152 :
1153 : struct NetworkStatisticsFile
1154 : {
1155 46482 : inline explicit NetworkStatisticsFile(const char *pszName)
1156 : {
1157 46482 : NetworkStatisticsLogger::EnterFile(pszName);
1158 46482 : }
1159 :
1160 46482 : inline ~NetworkStatisticsFile()
1161 : {
1162 46482 : NetworkStatisticsLogger::LeaveFile();
1163 46482 : }
1164 : };
1165 :
1166 : struct NetworkStatisticsAction
1167 : {
1168 47950 : inline explicit NetworkStatisticsAction(const char *pszName)
1169 : {
1170 47950 : NetworkStatisticsLogger::EnterAction(pszName);
1171 47950 : }
1172 :
1173 47950 : inline ~NetworkStatisticsAction()
1174 : {
1175 47950 : NetworkStatisticsLogger::LeaveAction();
1176 47950 : }
1177 : };
1178 :
1179 : } // namespace cpl
1180 :
1181 : int VSICURLGetDownloadChunkSize();
1182 :
1183 : void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1184 : VSICurlReadCbkFunc pfnReadCbk,
1185 : void *pReadCbkUserData);
1186 : size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1187 : void *req);
1188 : void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr,
1189 : std::atomic<bool> *pbInterrupt = nullptr);
1190 : void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1191 :
1192 : int VSICurlParseUnixPermissions(const char *pszPermissions);
1193 :
1194 : // Cache of file properties (size, etc.)
1195 : bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1196 : void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1197 : void VSICURLInvalidateCachedFileProp(const char *pszURL);
1198 : void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1199 : void VSICURLDestroyCacheFileProp();
1200 :
1201 : void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1202 :
1203 : //! @endcond
1204 :
1205 : #endif // HAVE_CURL
1206 :
1207 : #endif // CPL_VSIL_CURL_CLASS_H_INCLUDED
|