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