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 : #include "cpl_multiproc.h"
27 :
28 : #include "cpl_curl_priv.h"
29 :
30 : #include <algorithm>
31 : #include <atomic>
32 : #include <condition_variable>
33 : #include <set>
34 : #include <map>
35 : #include <memory>
36 : #include <mutex>
37 : #include <thread>
38 : #include <utility>
39 :
40 : // To avoid aliasing to CopyFile to CopyFileA on Windows
41 : #ifdef CopyFile
42 : #undef CopyFile
43 : #endif
44 :
45 : //! @cond Doxygen_Suppress
46 :
47 : // Leave it for backward compatibility, but deprecate.
48 : #define HAVE_CURLINFO_REDIRECT_URL
49 :
50 : void VSICurlStreamingClearCache(void); // from cpl_vsil_curl_streaming.cpp
51 :
52 : struct curl_slist *VSICurlSetOptions(CURL *hCurlHandle, const char *pszURL,
53 : const char *const *papszOptions);
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 35 : static size_t ReadCallBackBuffer(char *buffer, size_t size, size_t nitems,
126 : void *instream)
127 : {
128 35 : PutData *poThis = static_cast<PutData *>(instream);
129 35 : const size_t nSizeMax = size * nitems;
130 : const size_t nSizeToWrite =
131 35 : std::min(nSizeMax, poThis->nTotalSize - poThis->nOff);
132 35 : memcpy(buffer, poThis->pabyData + poThis->nOff, nSizeToWrite);
133 35 : poThis->nOff += nSizeToWrite;
134 35 : 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 46931 : FilenameOffsetPair(const std::string &filename, vsi_l_offset offset)
154 46931 : : filename_(filename), offset_(offset)
155 : {
156 46931 : }
157 :
158 45868 : bool operator==(const FilenameOffsetPair &other) const
159 : {
160 45868 : return filename_ == other.filename_ && offset_ == other.offset_;
161 : }
162 : };
163 :
164 : struct FilenameOffsetPairHasher
165 : {
166 47678 : std::size_t operator()(const FilenameOffsetPair &k) const
167 : {
168 47678 : return std::hash<std::string>()(k.filename_) ^
169 47678 : 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 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
246 : const char *pszAccess, bool bSetError,
247 : CSLConstList /* papszOptions */) override;
248 :
249 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
250 : int nFlags) override;
251 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
252 : char **SiblingFiles(const char *pszFilename) override;
253 :
254 18 : int HasOptimizedReadMultiRange(const char * /* pszPath */) override
255 : {
256 18 : return true;
257 : }
258 :
259 : const char *GetActualURL(const char *pszFilename) override;
260 :
261 : const char *GetOptions() override;
262 :
263 : char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
264 : CSLConstList papszOptions) override;
265 :
266 : char **ReadDirInternal(const char *pszDirname, int nMaxFiles,
267 : bool *pbGotFileList);
268 : void InvalidateDirContent(const std::string &osDirname);
269 :
270 : virtual const char *GetDebugKey() const = 0;
271 :
272 : virtual std::string GetFSPrefix() const = 0;
273 : virtual bool AllowCachedDataFor(const char *pszFilename);
274 :
275 6 : bool IsLocal(const char * /* pszPath */) const override
276 : {
277 6 : return false;
278 : }
279 :
280 : virtual bool
281 2 : SupportsSequentialWrite(const char * /* pszPath */,
282 : bool /* bAllowLocalTempFile */) override
283 : {
284 2 : return false;
285 : }
286 :
287 1 : virtual bool SupportsRandomWrite(const char * /* pszPath */,
288 : bool /* bAllowLocalTempFile */) override
289 : {
290 1 : return false;
291 : }
292 :
293 : std::shared_ptr<std::string> GetRegion(const char *pszURL,
294 : vsi_l_offset nFileOffsetStart);
295 :
296 : void AddRegion(const char *pszURL, vsi_l_offset nFileOffsetStart,
297 : size_t nSize, const char *pData);
298 :
299 : std::pair<bool, std::string>
300 : NotifyStartDownloadRegion(const std::string &osURL,
301 : vsi_l_offset startOffset, int nBlocks);
302 : void NotifyStopDownloadRegion(const std::string &osURL,
303 : vsi_l_offset startOffset, int nBlocks,
304 : const std::string &osData);
305 :
306 : bool GetCachedFileProp(const char *pszURL, FileProp &oFileProp);
307 : void SetCachedFileProp(const char *pszURL, FileProp &oFileProp);
308 : void InvalidateCachedData(const char *pszURL);
309 :
310 : CURLM *GetCurlMultiHandleFor(const std::string &osURL);
311 :
312 : virtual void ClearCache();
313 : virtual void PartialClearCache(const char *pszFilename);
314 :
315 : bool GetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
316 : void SetCachedDirList(const char *pszURL, CachedDirList &oCachedDirList);
317 : bool ExistsInCacheDirList(const std::string &osDirname, bool *pbIsDir);
318 :
319 : virtual std::string GetURLFromFilename(const std::string &osFilename) const;
320 :
321 : std::string
322 : GetStreamingFilename(const std::string &osFilename) const override = 0;
323 :
324 : static std::set<std::string> GetS3IgnoredStorageClasses();
325 : };
326 :
327 : class VSICurlFilesystemHandler final : public VSICurlFilesystemHandlerBase
328 : {
329 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandler)
330 :
331 : public:
332 1754 : VSICurlFilesystemHandler() = default;
333 :
334 909 : const char *GetDebugKey() const override
335 : {
336 909 : return "VSICURL";
337 : }
338 :
339 48079 : std::string GetFSPrefix() const override
340 : {
341 48079 : return "/vsicurl/";
342 : }
343 :
344 : std::string
345 : GetStreamingFilename(const std::string &osFilename) const override;
346 : };
347 :
348 : /************************************************************************/
349 : /* VSICurlHandle */
350 : /************************************************************************/
351 :
352 : class VSICurlHandle /* non final*/ : public VSIVirtualHandle
353 : {
354 : CPL_DISALLOW_COPY_ASSIGN(VSICurlHandle)
355 :
356 : protected:
357 : VSICurlFilesystemHandlerBase *poFS = nullptr;
358 :
359 : bool m_bCached = true;
360 :
361 : mutable FileProp oFileProp{};
362 :
363 : mutable std::mutex m_oMutex{};
364 : std::string m_osFilename{}; // e.g "/vsicurl/http://example.com/foo"
365 : char *m_pszURL = nullptr; // e.g "http://example.com/foo"
366 : mutable std::string m_osQueryString{}; // e.g. an Azure SAS
367 :
368 : CPLStringList m_aosHTTPOptions{};
369 : CPLHTTPRetryParameters
370 : m_oRetryParameters; // must be initialized in constructor
371 :
372 : vsi_l_offset lastDownloadedOffset = VSI_L_OFFSET_MAX;
373 : int nBlocksToDownload = 1;
374 :
375 : bool bStopOnInterruptUntilUninstall = false;
376 : bool bInterrupted = false;
377 : VSICurlReadCbkFunc pfnReadCbk = nullptr;
378 : void *pReadCbkUserData = nullptr;
379 :
380 : CPLStringList m_aosHeaders{};
381 :
382 : void DownloadRegionPostProcess(const vsi_l_offset startOffset,
383 : const int nBlocks, const char *pBuffer,
384 : size_t nSize);
385 :
386 : private:
387 : vsi_l_offset curOffset = 0;
388 :
389 : bool bEOF = false;
390 : bool bError = false;
391 :
392 : virtual std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks);
393 :
394 : bool m_bUseHead = false;
395 : bool m_bUseRedirectURLIfNoQueryStringParams = false;
396 :
397 : mutable std::atomic<bool> m_bInterrupt = false;
398 :
399 : // Specific to Planetary Computer signing:
400 : // https://planetarycomputer.microsoft.com/docs/concepts/sas/
401 : mutable bool m_bPlanetaryComputerURLSigning = false;
402 : mutable std::string m_osPlanetaryComputerCollection{};
403 : void ManagePlanetaryComputerSigning() const;
404 :
405 : void UpdateQueryString() const;
406 :
407 : int ReadMultiRangeSingleGet(int nRanges, void **ppData,
408 : const vsi_l_offset *panOffsets,
409 : const size_t *panSizes);
410 : std::string GetRedirectURLIfValid(bool &bHasExpired,
411 : CPLStringList &aosHTTPOptions) const;
412 :
413 : void UpdateRedirectInfo(CURL *hCurlHandle,
414 : const WriteFuncStruct &sWriteFuncHeaderData);
415 :
416 : // Used by AdviseRead()
417 : struct AdviseReadRange
418 : {
419 : bool bDone = false;
420 : bool bToRetry = true;
421 : double dfSleepDelay = 0.0;
422 : std::mutex oMutex{};
423 : std::condition_variable oCV{};
424 : vsi_l_offset nStartOffset = 0;
425 : size_t nSize = 0;
426 : std::vector<GByte> abyData{};
427 : CPLHTTPRetryContext retryContext;
428 :
429 6 : explicit AdviseReadRange(const CPLHTTPRetryParameters &oRetryParameters)
430 6 : : retryContext(oRetryParameters)
431 : {
432 6 : }
433 :
434 : AdviseReadRange(const AdviseReadRange &) = delete;
435 : AdviseReadRange &operator=(const AdviseReadRange &) = delete;
436 : AdviseReadRange(AdviseReadRange &&) = delete;
437 : AdviseReadRange &operator=(AdviseReadRange &&) = delete;
438 : };
439 :
440 : std::vector<std::unique_ptr<AdviseReadRange>> m_aoAdviseReadRanges{};
441 : std::thread m_oThreadAdviseRead{};
442 : CURLM *m_hCurlMultiHandleForAdviseRead = nullptr;
443 :
444 : protected:
445 554 : virtual struct curl_slist *GetCurlHeaders(const std::string & /*osVerb*/,
446 : struct curl_slist *psHeaders)
447 : {
448 554 : return psHeaders;
449 : }
450 :
451 621 : virtual bool AllowAutomaticRedirection()
452 : {
453 621 : return true;
454 : }
455 :
456 127 : virtual bool CanRestartOnError(const char *, const char *, bool)
457 : {
458 127 : return false;
459 : }
460 :
461 737 : virtual bool UseLimitRangeGetInsteadOfHead()
462 : {
463 737 : return false;
464 : }
465 :
466 272 : virtual bool IsDirectoryFromExists(const char * /*pszVerb*/,
467 : int /*response_code*/)
468 : {
469 272 : return false;
470 : }
471 :
472 131 : virtual void ProcessGetFileSizeResult(const char * /* pszContent */)
473 : {
474 131 : }
475 :
476 : void SetURL(const char *pszURL);
477 :
478 0 : virtual bool Authenticate(const char * /* pszFilename */)
479 : {
480 0 : return false;
481 : }
482 :
483 : public:
484 : VSICurlHandle(VSICurlFilesystemHandlerBase *poFS, const char *pszFilename,
485 : const char *pszURLIn = nullptr);
486 : ~VSICurlHandle() override;
487 :
488 : int Seek(vsi_l_offset nOffset, int nWhence) override;
489 : vsi_l_offset Tell() override;
490 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
491 : int ReadMultiRange(int nRanges, void **ppData,
492 : const vsi_l_offset *panOffsets,
493 : const size_t *panSizes) override;
494 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
495 : void ClearErr() override;
496 : int Eof() override;
497 : int Error() override;
498 : int Flush() override;
499 : int Close() override;
500 :
501 0 : void Interrupt() override
502 : {
503 0 : m_bInterrupt = true;
504 0 : }
505 :
506 18 : bool HasPRead() const override
507 : {
508 18 : return true;
509 : }
510 :
511 : size_t PRead(void *pBuffer, size_t nSize,
512 : vsi_l_offset nOffset) const override;
513 :
514 : void AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
515 : const size_t *panSizes) override;
516 :
517 : size_t GetAdviseReadTotalBytesLimit() const override;
518 :
519 591 : bool IsKnownFileSize() const
520 : {
521 591 : return oFileProp.bHasComputedFileSize;
522 : }
523 :
524 : vsi_l_offset GetFileSizeOrHeaders(bool bSetError, bool bGetHeaders);
525 :
526 954 : virtual vsi_l_offset GetFileSize(bool bSetError)
527 : {
528 954 : return GetFileSizeOrHeaders(bSetError, false);
529 : }
530 :
531 : bool Exists(bool bSetError);
532 :
533 721 : bool IsDirectory() const
534 : {
535 721 : return oFileProp.bIsDirectory;
536 : }
537 :
538 591 : int GetMode() const
539 : {
540 591 : return oFileProp.nMode;
541 : }
542 :
543 591 : time_t GetMTime() const
544 : {
545 591 : return oFileProp.mTime;
546 : }
547 :
548 4 : const CPLStringList &GetHeaders()
549 : {
550 4 : return m_aosHeaders;
551 : }
552 :
553 : int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, void *pfnUserData,
554 : int bStopOnInterruptUntilUninstall);
555 : int UninstallReadCbk();
556 :
557 5 : const char *GetURL() const
558 : {
559 5 : return m_pszURL;
560 : }
561 : };
562 :
563 : /************************************************************************/
564 : /* VSICurlFilesystemHandlerBaseWritable */
565 : /************************************************************************/
566 :
567 : class VSICurlFilesystemHandlerBaseWritable /* non final */
568 : : public VSICurlFilesystemHandlerBase
569 : {
570 : CPL_DISALLOW_COPY_ASSIGN(VSICurlFilesystemHandlerBaseWritable)
571 :
572 : protected:
573 12278 : VSICurlFilesystemHandlerBaseWritable() = default;
574 :
575 : virtual VSIVirtualHandleUniquePtr
576 : CreateWriteHandle(const char *pszFilename, CSLConstList papszOptions) = 0;
577 :
578 : public:
579 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
580 : const char *pszAccess, bool bSetError,
581 : 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 /* non final */
598 : : public VSICurlFilesystemHandlerBaseWritable
599 : {
600 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandler)
601 :
602 : virtual int MkdirInternal(const char *pszDirname, long nMode,
603 : bool bDoStatCheck);
604 :
605 : protected:
606 : char **GetFileList(const char *pszFilename, int nMaxFiles,
607 : bool *pbGotFileList) override;
608 :
609 : virtual IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
610 : bool bAllowNoObject) = 0;
611 :
612 : virtual int CopyObject(const char *oldpath, const char *newpath,
613 : CSLConstList papszMetadata);
614 :
615 : int RmdirRecursiveInternal(const char *pszDirname, int nBatchSize);
616 :
617 : virtual bool
618 0 : IsAllowedHeaderForObjectCreation(const char * /* pszHeaderName */)
619 : {
620 0 : return false;
621 : }
622 :
623 10524 : IVSIS3LikeFSHandler() = default;
624 :
625 : public:
626 : int Unlink(const char *pszFilename) override;
627 : int Mkdir(const char *pszDirname, long nMode) override;
628 : int Rmdir(const char *pszDirname) override;
629 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
630 : int nFlags) override;
631 : int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
632 : void *) override;
633 :
634 : virtual int CopyFile(const char *pszSource, const char *pszTarget,
635 : VSILFILE *fpSource, vsi_l_offset nSourceSize,
636 : const char *const *papszOptions,
637 : GDALProgressFunc pProgressFunc,
638 : void *pProgressData) override;
639 :
640 : virtual int DeleteObject(const char *pszFilename);
641 :
642 : virtual int *DeleteObjectBatch(CSLConstList papszFilesOrDirs);
643 :
644 : bool Sync(const char *pszSource, const char *pszTarget,
645 : const char *const *papszOptions, GDALProgressFunc pProgressFunc,
646 : void *pProgressData, char ***ppapszOutputs) override;
647 :
648 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
649 : const char *const *papszOptions) override;
650 : };
651 :
652 : /************************************************************************/
653 : /* IVSIS3LikeFSHandlerWithMultipartUpload */
654 : /************************************************************************/
655 :
656 : class IVSIS3LikeFSHandlerWithMultipartUpload /* non final */
657 : : public IVSIS3LikeFSHandler
658 : {
659 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeFSHandlerWithMultipartUpload)
660 :
661 : protected:
662 8770 : IVSIS3LikeFSHandlerWithMultipartUpload() = default;
663 :
664 : public:
665 5 : virtual bool SupportsNonSequentialMultipartUpload() const
666 : {
667 5 : return true;
668 : }
669 :
670 36 : virtual bool SupportsParallelMultipartUpload() const
671 : {
672 36 : return true;
673 : }
674 :
675 : virtual bool SupportsMultipartAbort() const = 0;
676 :
677 : size_t GetUploadChunkSizeInBytes(const char *pszFilename,
678 : const char *pszSpecifiedValInBytes);
679 :
680 : virtual int CopyFileRestartable(const char *pszSource,
681 : const char *pszTarget,
682 : const char *pszInputPayload,
683 : char **ppszOutputPayload,
684 : CSLConstList papszOptions,
685 : GDALProgressFunc pProgressFunc,
686 : void *pProgressData) override;
687 :
688 : //! Maximum number of parts for multipart upload
689 : // Limit currently used by S3 and GS.
690 : // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
691 : // and https://cloud.google.com/storage/quotas#requests
692 11 : virtual int GetMaximumPartCount()
693 : {
694 11 : return 10000;
695 : }
696 :
697 : //! Minimum size of a part for multipart upload (except last one), in MiB.
698 : // Limit currently used by S3 and GS.
699 : // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
700 : // and https://cloud.google.com/storage/quotas#requests
701 5 : virtual int GetMinimumPartSizeInMiB()
702 : {
703 5 : return 5;
704 : }
705 :
706 : //! Maximum size of a part for multipart upload, in MiB.
707 : // Limit currently used by S3 and GS.
708 : // Cf https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
709 : // and https://cloud.google.com/storage/quotas#requests
710 50 : virtual int GetMaximumPartSizeInMiB()
711 : {
712 : #if SIZEOF_VOIDP == 8
713 50 : return 5 * 1024;
714 : #else
715 : // Cannot be larger than 4, otherwise integer overflow would occur
716 : // 1 GiB is the maximum reasonable value on a 32-bit machine
717 : return 1 * 1024;
718 : #endif
719 : }
720 :
721 : //! Default size of a part for multipart upload, in MiB.
722 47 : virtual int GetDefaultPartSizeInMiB()
723 : {
724 47 : return 50;
725 : }
726 :
727 : virtual std::string
728 : InitiateMultipartUpload(const std::string &osFilename,
729 : IVSIS3LikeHandleHelper *poS3HandleHelper,
730 : const CPLHTTPRetryParameters &oRetryParameters,
731 : CSLConstList papszOptions);
732 :
733 : virtual std::string
734 : UploadPart(const std::string &osFilename, int nPartNumber,
735 : const std::string &osUploadID, vsi_l_offset nPosition,
736 : const void *pabyBuffer, size_t nBufferSize,
737 : IVSIS3LikeHandleHelper *poS3HandleHelper,
738 : const CPLHTTPRetryParameters &oRetryParameters,
739 : CSLConstList papszOptions);
740 :
741 : virtual bool CompleteMultipart(
742 : const std::string &osFilename, const std::string &osUploadID,
743 : const std::vector<std::string> &aosEtags, vsi_l_offset nTotalSize,
744 : IVSIS3LikeHandleHelper *poS3HandleHelper,
745 : const CPLHTTPRetryParameters &oRetryParameters);
746 :
747 : virtual bool AbortMultipart(const std::string &osFilename,
748 : const std::string &osUploadID,
749 : IVSIS3LikeHandleHelper *poS3HandleHelper,
750 : const CPLHTTPRetryParameters &oRetryParameters);
751 :
752 : bool AbortPendingUploads(const char *pszFilename) override;
753 :
754 : bool MultipartUploadGetCapabilities(int *pbNonSequentialUploadSupported,
755 : int *pbParallelUploadSupported,
756 : int *pbAbortSupported,
757 : size_t *pnMinPartSize,
758 : size_t *pnMaxPartSize,
759 : int *pnMaxPartCount) override;
760 :
761 : char *MultipartUploadStart(const char *pszFilename,
762 : CSLConstList papszOptions) override;
763 :
764 : char *MultipartUploadAddPart(const char *pszFilename,
765 : const char *pszUploadId, int nPartNumber,
766 : vsi_l_offset nFileOffset, const void *pData,
767 : size_t nDataLength,
768 : CSLConstList papszOptions) override;
769 :
770 : bool MultipartUploadEnd(const char *pszFilename, const char *pszUploadId,
771 : size_t nPartIdsCount,
772 : const char *const *apszPartIds,
773 : vsi_l_offset nTotalSize,
774 : CSLConstList papszOptions) override;
775 :
776 : bool MultipartUploadAbort(const char *pszFilename, const char *pszUploadId,
777 : CSLConstList papszOptions) override;
778 : };
779 :
780 : /************************************************************************/
781 : /* IVSIS3LikeHandle */
782 : /************************************************************************/
783 :
784 261 : class IVSIS3LikeHandle /* non final */ : public VSICurlHandle
785 : {
786 : CPL_DISALLOW_COPY_ASSIGN(IVSIS3LikeHandle)
787 :
788 : protected:
789 269 : bool UseLimitRangeGetInsteadOfHead() override
790 : {
791 269 : return true;
792 : }
793 :
794 111 : bool IsDirectoryFromExists(const char *pszVerb, int response_code) override
795 : {
796 : // A bit dirty, but on S3, a GET on a existing directory returns a 416
797 115 : return response_code == 416 && EQUAL(pszVerb, "GET") &&
798 115 : std::string(m_pszURL).back() == '/';
799 : }
800 :
801 55 : void ProcessGetFileSizeResult(const char *pszContent) override
802 : {
803 55 : oFileProp.bIsDirectory =
804 55 : strstr(pszContent, "ListBucketResult") != nullptr;
805 55 : }
806 :
807 : public:
808 261 : IVSIS3LikeHandle(VSICurlFilesystemHandlerBase *poFSIn,
809 : const char *pszFilename, const char *pszURLIn)
810 261 : : VSICurlHandle(poFSIn, pszFilename, pszURLIn)
811 : {
812 261 : }
813 :
814 : ~IVSIS3LikeHandle() override;
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 : ~VSIChunkedWriteHandle() override;
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 : ~VSIAppendWriteHandle() override;
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 128 : struct VSIDIRWithMissingDirSynthesis /* non final */ : public VSIDIR
1016 : {
1017 : std::vector<std::unique_ptr<VSIDIREntry>> aoEntries{};
1018 :
1019 : protected:
1020 : ~VSIDIRWithMissingDirSynthesis() override;
1021 :
1022 : std::vector<std::string> m_aosSubpathsStack{};
1023 :
1024 : void SynthetizeMissingDirectories(const std::string &osCurSubdir,
1025 : bool bAddEntryForThisSubdir);
1026 : };
1027 :
1028 : /************************************************************************/
1029 : /* VSIDIRS3Like */
1030 : /************************************************************************/
1031 :
1032 : struct VSIDIRS3Like /* non final */ : public VSIDIRWithMissingDirSynthesis
1033 : {
1034 : const std::string m_osDirName;
1035 :
1036 : int nRecurseDepth = 0;
1037 :
1038 : std::string osNextMarker{};
1039 : int nPos = 0;
1040 :
1041 : std::string osBucket{};
1042 : std::string osObjectKey{};
1043 : VSICurlFilesystemHandlerBase *poFS = nullptr;
1044 : IVSIS3LikeFSHandler *poS3FS = nullptr;
1045 : std::unique_ptr<IVSIS3LikeHandleHelper> poHandleHelper{};
1046 : int nMaxFiles = 0;
1047 : bool bCacheEntries = true;
1048 : bool m_bSynthetizeMissingDirectories = false;
1049 : std::string m_osFilterPrefix{};
1050 :
1051 : // used when listing only the file system prefix
1052 : std::unique_ptr<VSIDIR, decltype(&VSICloseDir)> m_subdir{nullptr,
1053 128 : VSICloseDir};
1054 :
1055 128 : VSIDIRS3Like(const std::string &osDirName, IVSIS3LikeFSHandler *poFSIn)
1056 128 : : m_osDirName(osDirName), poFS(poFSIn), poS3FS(poFSIn)
1057 : {
1058 128 : }
1059 :
1060 0 : VSIDIRS3Like(const std::string &osDirName,
1061 : VSICurlFilesystemHandlerBase *poFSIn)
1062 0 : : m_osDirName(osDirName), poFS(poFSIn)
1063 : {
1064 0 : }
1065 :
1066 : VSIDIRS3Like(const VSIDIRS3Like &) = delete;
1067 : VSIDIRS3Like &operator=(const VSIDIRS3Like &) = delete;
1068 :
1069 : const VSIDIREntry *NextDirEntry() override;
1070 :
1071 : virtual bool IssueListDir() = 0;
1072 : void clear();
1073 : };
1074 :
1075 : /************************************************************************/
1076 : /* CurlRequestHelper */
1077 : /************************************************************************/
1078 :
1079 : struct CurlRequestHelper
1080 : {
1081 : WriteFuncStruct sWriteFuncData{};
1082 : WriteFuncStruct sWriteFuncHeaderData{};
1083 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
1084 :
1085 : CurlRequestHelper();
1086 : ~CurlRequestHelper();
1087 : long perform(CURL *hCurlHandle,
1088 : struct curl_slist *headers, // ownership transferred
1089 : VSICurlFilesystemHandlerBase *poFS,
1090 : IVSIS3LikeHandleHelper *poS3HandleHelper);
1091 : };
1092 :
1093 : /************************************************************************/
1094 : /* NetworkStatisticsLogger */
1095 : /************************************************************************/
1096 :
1097 : class NetworkStatisticsLogger
1098 : {
1099 : static int gnEnabled;
1100 : static NetworkStatisticsLogger gInstance;
1101 :
1102 1577 : NetworkStatisticsLogger() = default;
1103 :
1104 : std::mutex m_mutex{};
1105 :
1106 : struct Counters
1107 : {
1108 : GIntBig nHEAD = 0;
1109 : GIntBig nGET = 0;
1110 : GIntBig nPUT = 0;
1111 : GIntBig nPOST = 0;
1112 : GIntBig nDELETE = 0;
1113 : GIntBig nGETDownloadedBytes = 0;
1114 : GIntBig nPUTUploadedBytes = 0;
1115 : GIntBig nPOSTDownloadedBytes = 0;
1116 : GIntBig nPOSTUploadedBytes = 0;
1117 : };
1118 :
1119 : enum class ContextPathType
1120 : {
1121 : FILESYSTEM,
1122 : FILE,
1123 : ACTION,
1124 : };
1125 :
1126 : struct ContextPathItem
1127 : {
1128 : ContextPathType eType;
1129 : std::string osName;
1130 :
1131 3 : ContextPathItem(ContextPathType eTypeIn, const std::string &osNameIn)
1132 3 : : eType(eTypeIn), osName(osNameIn)
1133 : {
1134 3 : }
1135 :
1136 0 : bool operator<(const ContextPathItem &other) const
1137 : {
1138 0 : if (static_cast<int>(eType) < static_cast<int>(other.eType))
1139 0 : return true;
1140 0 : if (static_cast<int>(eType) > static_cast<int>(other.eType))
1141 0 : return false;
1142 0 : return osName < other.osName;
1143 : }
1144 : };
1145 :
1146 : struct Stats
1147 : {
1148 : Counters counters{};
1149 : std::map<ContextPathItem, Stats> children{};
1150 :
1151 : void AsJSON(CPLJSONObject &oJSON) const;
1152 : };
1153 :
1154 : // Workaround bug in Coverity Scan
1155 : // coverity[generated_default_constructor_used_in_field_initializer]
1156 : Stats m_stats{};
1157 : std::map<GIntBig, std::vector<ContextPathItem>>
1158 : m_mapThreadIdToContextPath{};
1159 :
1160 : static void ReadEnabled();
1161 :
1162 : std::vector<Counters *> GetCountersForContext();
1163 :
1164 : public:
1165 283195 : static inline bool IsEnabled()
1166 : {
1167 283195 : if (gnEnabled < 0)
1168 : {
1169 8 : ReadEnabled();
1170 : }
1171 283195 : return gnEnabled == TRUE;
1172 : }
1173 :
1174 : static void EnterFileSystem(const char *pszName);
1175 :
1176 : static void LeaveFileSystem();
1177 :
1178 : static void EnterFile(const char *pszName);
1179 :
1180 : static void LeaveFile();
1181 :
1182 : static void EnterAction(const char *pszName);
1183 :
1184 : static void LeaveAction();
1185 :
1186 : static void LogHEAD();
1187 :
1188 : static void LogGET(size_t nDownloadedBytes);
1189 :
1190 : static void LogPUT(size_t nUploadedBytes);
1191 :
1192 : static void LogPOST(size_t nUploadedBytes, size_t nDownloadedBytes);
1193 :
1194 : static void LogDELETE();
1195 :
1196 : static void Reset();
1197 :
1198 : static std::string GetReportAsSerializedJSON();
1199 : };
1200 :
1201 : struct NetworkStatisticsFileSystem
1202 : {
1203 47505 : inline explicit NetworkStatisticsFileSystem(const char *pszName)
1204 : {
1205 47505 : NetworkStatisticsLogger::EnterFileSystem(pszName);
1206 47505 : }
1207 :
1208 47505 : inline ~NetworkStatisticsFileSystem()
1209 : {
1210 47505 : NetworkStatisticsLogger::LeaveFileSystem();
1211 47505 : }
1212 : };
1213 :
1214 : struct NetworkStatisticsFile
1215 : {
1216 45947 : inline explicit NetworkStatisticsFile(const char *pszName)
1217 : {
1218 45947 : NetworkStatisticsLogger::EnterFile(pszName);
1219 45947 : }
1220 :
1221 45947 : inline ~NetworkStatisticsFile()
1222 : {
1223 45947 : NetworkStatisticsLogger::LeaveFile();
1224 45947 : }
1225 : };
1226 :
1227 : struct NetworkStatisticsAction
1228 : {
1229 47505 : inline explicit NetworkStatisticsAction(const char *pszName)
1230 : {
1231 47505 : NetworkStatisticsLogger::EnterAction(pszName);
1232 47505 : }
1233 :
1234 47505 : inline ~NetworkStatisticsAction()
1235 : {
1236 47505 : NetworkStatisticsLogger::LeaveAction();
1237 47505 : }
1238 : };
1239 :
1240 : } // namespace cpl
1241 :
1242 : int VSICURLGetDownloadChunkSize();
1243 :
1244 : void VSICURLInitWriteFuncStruct(cpl::WriteFuncStruct *psStruct, VSILFILE *fp,
1245 : VSICurlReadCbkFunc pfnReadCbk,
1246 : void *pReadCbkUserData);
1247 : size_t VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb,
1248 : void *req);
1249 : void VSICURLMultiPerform(CURLM *hCurlMultiHandle, CURL *hEasyHandle = nullptr,
1250 : std::atomic<bool> *pbInterrupt = nullptr);
1251 : void VSICURLResetHeaderAndWriterFunctions(CURL *hCurlHandle);
1252 :
1253 : int VSICurlParseUnixPermissions(const char *pszPermissions);
1254 :
1255 : // Cache of file properties (size, etc.)
1256 : bool VSICURLGetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1257 : void VSICURLSetCachedFileProp(const char *pszURL, cpl::FileProp &oFileProp);
1258 : void VSICURLInvalidateCachedFileProp(const char *pszURL);
1259 : void VSICURLInvalidateCachedFilePropPrefix(const char *pszURL);
1260 : void VSICURLDestroyCacheFileProp();
1261 :
1262 : void VSICURLMultiCleanup(CURLM *hCurlMultiHandle);
1263 :
1264 : //! @endcond
1265 :
1266 : #endif // HAVE_CURL
1267 :
1268 : #endif // CPL_VSIL_CURL_CLASS_H_INCLUDED
|