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