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_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 35 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
127 : void *instream)
128 : {
129 35 : PutData *poThis = static_cast<PutData *>(instream);
130 35 : const size_t nSizeMax = size * nitems;
131 : const size_t nSizeToWrite =
132 35 : std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
133 35 : memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
134 35 : poThis->nOff += nSizeToWrite;
135 35 : return nSizeToWrite;
136 : }
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 46924 : FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
155 46924 : : filename_(filename), offset_(offset)
156 : {
157 46924 : }
158 :
159 45867 : bool operator==(const FilenameOffsetPair &other) const
160 : {
161 45867 : return filename_ == other.filename_ && offset_ == other.offset_;
162 : }
163 : };
164 :
165 : struct FilenameOffsetPairHasher
166 : {
167 47671 : std::size_t operator()(const FilenameOffsetPair &k) const
168 : {
169 47671 : return std::hash<std::string>()(k.filename_) ^
170 47671 : std::hash<vsi_l_offset>()(k.offset_);
171 : }
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 18 : int HasOptimizedReadMultiRange(const char * /* pszPath */) override
256 : {
257 18 : return true;
258 : }
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 5 : virtual bool IsLocal(const char * /* pszPath */) override
277 : {
278 5 : return false;
279 : }
280 :
281 : virtual bool
282 2 : SupportsSequentialWrite(const char * /* pszPath */,
283 : bool /* bAllowLocalTempFile */) override
284 : {
285 2 : return false;
286 : }
287 :
288 1 : virtual bool SupportsRandomWrite(const char * /* pszPath */,
289 : bool /* bAllowLocalTempFile */) override
290 : {
291 1 : return false;
292 : }
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 1616 : VSICurlFilesystemHandler() = default;
334 :
335 903 : const char *GetDebugKey() const override
336 : {
337 903 : return "VSICURL";
338 : }
339 :
340 48057 : std::string GetFSPrefix() const override
341 : {
342 48057 : return "/vsicurl/";
343 : }
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 6 : explicit AdviseReadRange(const CPLHTTPRetryParameters &oRetryParameters)
431 6 : : retryContext(oRetryParameters)
432 : {
433 6 : }
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 550 : GetCurlHeaders(const std::string & /*osVerb*/,
448 : const struct curl_slist * /* psExistingHeaders */)
449 : {
450 550 : return nullptr;
451 : }
452 :
453 616 : virtual bool AllowAutomaticRedirection()
454 : {
455 616 : return true;
456 : }
457 :
458 1 : virtual bool CanRestartOnError(const char *, const char *, bool)
459 : {
460 1 : return false;
461 : }
462 :
463 735 : virtual bool UseLimitRangeGetInsteadOfHead()
464 : {
465 735 : return false;
466 : }
467 :
468 269 : virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
469 : int /*response_code*/)
470 : {
471 269 : return false;
472 : }
473 :
474 130 : virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
475 : {
476 130 : }
477 :
478 : void SetURL(const char *pszURL);
479 :
480 0 : virtual bool Authenticate(const char * /* pszFilename */)
481 : {
482 0 : return false;
483 : }
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 0 : void Interrupt() override
504 : {
505 0 : m_bInterrupt = true;
506 0 : }
507 :
508 18 : bool HasPRead() const override
509 : {
510 18 : return true;
511 : }
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 585 : bool IsKnownFileSize() const
522 : {
523 585 : return oFileProp.bHasComputedFileSize;
524 : }
525 :
526 : vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
527 :
528 947 : virtual vsi_l_offset GetFileSize(bool bSetError)
529 : {
530 947 : return GetFileSizeOrHeaders(bSetError, false);
531 : }
532 :
533 : bool Exists(bool bSetError);
534 :
535 713 : bool IsDirectory() const
536 : {
537 713 : return oFileProp.bIsDirectory;
538 : }
539 :
540 585 : int GetMode() const
541 : {
542 585 : return oFileProp.nMode;
543 : }
544 :
545 585 : time_t GetMTime() const
546 : {
547 585 : return oFileProp.mTime;
548 : }
549 :
550 4 : const CPLStringList &GetHeaders()
551 : {
552 4 : return m_aosHeaders;
553 : }
554 :
555 : int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
556 : int bStopOnInterruptUntilUninstall);
557 : int UninstallReadCbk();
558 :
559 5 : const char *GetURL() const
560 : {
561 5 : return m_pszURL;
562 : }
563 : };
564 :
565 : /************************************************************************/
566 : /* VSICurlFilesystemHandlerBaseWritable */
567 : /************************************************************************/
568 :
569 : class VSICurlFilesystemHandlerBaseWritable : public VSICurlFilesystemHandlerBase
570 : {
571 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
572 :
573 : protected:
574 11312 : 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 1 : bool SupportsSequentialWrite(const char * /* pszPath */,
584 : bool /* bAllowLocalTempFile */) override
585 : {
586 1 : return true;
587 : }
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 0 : IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
618 : {
619 0 : return false;
620 : }
621 :
622 9696 : 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 8080 : IVSIS3LikeFSHandlerWithMultipartUpload() = default;
661 :
662 : public:
663 5 : virtual bool SupportsNonSequentialMultipartUpload() const
664 : {
665 5 : return true;
666 : }
667 :
668 36 : virtual bool SupportsParallelMultipartUpload() const
669 : {
670 36 : return true;
671 : }
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 11 : virtual int GetMaximumPartCount()
691 : {
692 11 : return 10000;
693 : }
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 5 : virtual int GetMinimumPartSizeInMiB()
700 : {
701 5 : return 5;
702 : }
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 50 : virtual int GetMaximumPartSizeInMiB()
709 : {
710 : #if SIZEOF_VOIDP == 8
711 50 : 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 : }
718 :
719 : //! Default size of a part for multipart upload, in MiB.
720 47 : virtual int GetDefaultPartSizeInMiB()
721 : {
722 47 : return 50;
723 : }
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 294 : bool UseLimitRangeGetInsteadOfHead() override
788 : {
789 294 : return true;
790 : }
791 :
792 108 : bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
793 : {
794 : // A bit dirty, but on S3, a GET on a existing directory returns a 416
795 112 : return response_code == 416 && EQUAL(pszVerb, "GET") &&
796 112 : std::string(m_pszURL).back() == '/';
797 : }
798 :
799 52 : void ProcessGetFileSizeResult(const char *pszContent) override
800 : {
801 52 : oFileProp.bIsDirectory =
802 52 : strstr(pszContent, "ListBucketResult") != nullptr;
803 52 : }
804 :
805 : public:
806 257 : IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
807 : const char *pszFilename, const char *pszURLIn)
808 257 : : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
809 : {
810 257 : }
811 :
812 257 : ~IVSIS3LikeHandle() override
813 257 : {
814 257 : }
815 : };
816 :
817 : /************************************************************************/
818 : /* VSIMultipartWriteHandle */
819 : /************************************************************************/
820 :
821 : class VSIMultipartWriteHandle final : public VSIVirtualHandle
822 : {
823 : CPL_DISALLOW_COPY_ASSIGN(VSIMultipartWriteHandle)
824 :
825 : IVSIS3LikeFSHandlerWithMultipartUpload *m_poFS = nullptr;
826 : std::string m_osFilename{};
827 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
828 : CPLStringList m_aosOptions{};
829 : CPLStringList m_aosHTTPOptions{};
830 : CPLHTTPRetryParameters m_oRetryParameters;
831 :
832 : vsi_l_offset m_nCurOffset = 0;
833 : size_t m_nBufferOff = 0;
834 : size_t m_nBufferSize = 0;
835 : bool m_bClosed = false;
836 : GByte *m_pabyBuffer = nullptr;
837 : std::string m_osUploadID{};
838 : int m_nPartNumber = 0;
839 : std::vector<std::string> m_aosEtags{};
840 : bool m_bError = false;
841 :
842 : WriteFuncStruct m_sWriteFuncHeaderData{};
843 :
844 : bool UploadPart();
845 : bool DoSinglePartPUT();
846 :
847 : void InvalidateParentDirectory();
848 :
849 : public:
850 : VSIMultipartWriteHandle(IVSIS3LikeFSHandlerWithMultipartUpload *poFS,
851 : const char *pszFilename,
852 : IVSIS3LikeHandleHelper *poS3HandleHelper,
853 : CSLConstList papszOptions);
854 : ~VSIMultipartWriteHandle() override;
855 :
856 : int Seek(vsi_l_offset nOffset, int nWhence) override;
857 : vsi_l_offset Tell() override;
858 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
859 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
860 :
861 0 : void ClearErr() override
862 : {
863 0 : }
864 :
865 0 : int Error() override
866 : {
867 0 : return FALSE;
868 : }
869 :
870 0 : int Eof() override
871 : {
872 0 : return FALSE;
873 : }
874 :
875 : int Close() override;
876 :
877 31 : bool IsOK()
878 : {
879 31 : return m_pabyBuffer != nullptr;
880 : }
881 : };
882 :
883 : /************************************************************************/
884 : /* VSIChunkedWriteHandle() */
885 : /************************************************************************/
886 :
887 : /** Class with Write() append-only implementation using
888 : * "Transfer-Encoding: chunked" writing
889 : */
890 : class VSIChunkedWriteHandle final : public VSIVirtualHandle
891 : {
892 : CPL_DISALLOW_COPY_ASSIGN(VSIChunkedWriteHandle)
893 :
894 : IVSIS3LikeFSHandler *m_poFS = nullptr;
895 : std::string m_osFilename{};
896 : IVSIS3LikeHandleHelper *m_poS3HandleHelper = nullptr;
897 : CPLStringList m_aosOptions{};
898 : CPLStringList m_aosHTTPOptions{};
899 : CPLHTTPRetryParameters m_oRetryParameters;
900 :
901 : vsi_l_offset m_nCurOffset = 0;
902 : size_t m_nBufferOff = 0;
903 : bool m_bError = false;
904 : bool m_bClosed = false;
905 :
906 : CURLM *m_hCurlMulti = nullptr;
907 : CURL *m_hCurl = nullptr;
908 : const void *m_pBuffer = nullptr;
909 : std::string m_osCurlErrBuf{};
910 : size_t m_nChunkedBufferOff = 0;
911 : size_t m_nChunkedBufferSize = 0;
912 : size_t m_nWrittenInPUT = 0;
913 :
914 : WriteFuncStruct m_sWriteFuncHeaderData{};
915 :
916 : static size_t ReadCallBackBufferChunked(char *buffer, size_t size,
917 : size_t nitems, void *instream);
918 : int FinishChunkedTransfer();
919 :
920 : bool DoEmptyPUT();
921 :
922 : void InvalidateParentDirectory();
923 :
924 : public:
925 : VSIChunkedWriteHandle(IVSIS3LikeFSHandler *poFS, const char *pszFilename,
926 : IVSIS3LikeHandleHelper *poS3HandleHelper,
927 : CSLConstList papszOptions);
928 : virtual ~VSIChunkedWriteHandle();
929 :
930 : int Seek(vsi_l_offset nOffset, int nWhence) override;
931 : vsi_l_offset Tell() override;
932 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
933 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
934 :
935 0 : void ClearErr() override
936 : {
937 0 : }
938 :
939 0 : int Error() override
940 : {
941 0 : return FALSE;
942 : }
943 :
944 0 : int Eof() override
945 : {
946 0 : return FALSE;
947 : }
948 :
949 : int Close() override;
950 : };
951 :
952 : /************************************************************************/
953 : /* VSIAppendWriteHandle */
954 : /************************************************************************/
955 :
956 : class VSIAppendWriteHandle CPL_NON_FINAL : public VSIVirtualHandle
957 : {
958 : CPL_DISALLOW_COPY_ASSIGN(VSIAppendWriteHandle)
959 :
960 : protected:
961 : VSICurlFilesystemHandlerBase *m_poFS = nullptr;
962 : std::string m_osFSPrefix{};
963 : std::string m_osFilename{};
964 : CPLHTTPRetryParameters m_oRetryParameters{};
965 :
966 : vsi_l_offset m_nCurOffset = 0;
967 : int m_nBufferOff = 0;
968 : int m_nBufferSize = 0;
969 : int m_nBufferOffReadCallback = 0;
970 : bool m_bClosed = false;
971 : GByte *m_pabyBuffer = nullptr;
972 : bool m_bError = false;
973 :
974 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
975 : void *instream);
976 : virtual bool Send(bool bIsLastBlock) = 0;
977 :
978 : public:
979 : VSIAppendWriteHandle(VSICurlFilesystemHandlerBase *poFS,
980 : const char *pszFSPrefix, const char *pszFilename,
981 : int nChunkSize);
982 : virtual ~VSIAppendWriteHandle();
983 :
984 : int Seek(vsi_l_offset nOffset, int nWhence) override;
985 : vsi_l_offset Tell() override;
986 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
987 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
988 :
989 0 : void ClearErr() override
990 : {
991 0 : }
992 :
993 0 : int Error() override
994 : {
995 0 : return FALSE;
996 : }
997 :
998 0 : int Eof() override
999 : {
1000 0 : return FALSE;
1001 : }
1002 :
1003 : int Close() override;
1004 :
1005 10 : bool IsOK()
1006 : {
1007 10 : return m_pabyBuffer != nullptr;
1008 : }
1009 : };
1010 :
1011 : /************************************************************************/
1012 : /* VSIDIRWithMissingDirSynthesis */
1013 : /************************************************************************/
1014 :
1015 : struct VSIDIRWithMissingDirSynthesis : public VSIDIR
1016 : {
1017 : std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
1018 :
1019 : protected:
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 124 : VSICloseDir};
1050 :
1051 124 : explicit VSIDIRS3Like(IVSIS3LikeFSHandler *poFSIn)
1052 124 : : poFS(poFSIn), poS3FS(poFSIn)
1053 : {
1054 124 : }
1055 :
1056 0 : explicit VSIDIRS3Like(VSICurlFilesystemHandlerBase *poFSIn) : poFS(poFSIn)
1057 : {
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 1438 : 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 3 : ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
1126 3 : : eType(eTypeIn), osName(osNameIn)
1127 : {
1128 3 : }
1129 :
1130 0 : bool operator<(const ContextPathItem &other) const
1131 : {
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 : }
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 282845 : static inline bool IsEnabled()
1160 : {
1161 282845 : if (gnEnabled < 0)
1162 : {
1163 7 : ReadEnabled();
1164 : }
1165 282845 : return gnEnabled == TRUE;
1166 : }
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 47428 : inline explicit NetworkStatisticsFileSystem(const char *pszName)
1198 : {
1199 47428 : NetworkStatisticsLogger::EnterFileSystem(pszName);
1200 47428 : }
1201 :
1202 47428 : inline ~NetworkStatisticsFileSystem()
1203 : {
1204 47428 : NetworkStatisticsLogger::LeaveFileSystem();
1205 47427 : }
1206 : };
1207 :
1208 : struct NetworkStatisticsFile
1209 : {
1210 45936 : inline explicit NetworkStatisticsFile(const char *pszName)
1211 : {
1212 45936 : NetworkStatisticsLogger::EnterFile(pszName);
1213 45936 : }
1214 :
1215 45935 : inline ~NetworkStatisticsFile()
1216 : {
1217 45935 : NetworkStatisticsLogger::LeaveFile();
1218 45936 : }
1219 : };
1220 :
1221 : struct NetworkStatisticsAction
1222 : {
1223 47428 : inline explicit NetworkStatisticsAction(const char *pszName)
1224 : {
1225 47428 : NetworkStatisticsLogger::EnterAction(pszName);
1226 47428 : }
1227 :
1228 47428 : inline ~NetworkStatisticsAction()
1229 : {
1230 47428 : NetworkStatisticsLogger::LeaveAction();
1231 47428 : }
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
|