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