LCOV - code coverage report
Current view: top level - port - cpl_vsil_swift.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 252 287 87.8 %
Date: 2025-09-10 17:48:50 Functions: 19 23 82.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for OpenStack Swift
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017-2018, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_json.h"
      14             : #include "cpl_port.h"
      15             : #include "cpl_http.h"
      16             : #include "cpl_time.h"
      17             : #include "cpl_vsil_curl_priv.h"
      18             : #include "cpl_vsil_curl_class.h"
      19             : 
      20             : #include <errno.h>
      21             : 
      22             : #include <algorithm>
      23             : #include <set>
      24             : #include <map>
      25             : #include <memory>
      26             : 
      27             : #include "cpl_swift.h"
      28             : 
      29             : #ifndef HAVE_CURL
      30             : 
      31             : void VSIInstallSwiftFileHandler(void)
      32             : {
      33             :     // Not supported
      34             : }
      35             : 
      36             : #else
      37             : 
      38             : //! @cond Doxygen_Suppress
      39             : #ifndef DOXYGEN_SKIP
      40             : 
      41             : #define ENABLE_DEBUG 0
      42             : 
      43             : #define unchecked_curl_easy_setopt(handle, opt, param)                         \
      44             :     CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
      45             : 
      46             : namespace cpl
      47             : {
      48             : 
      49             : /************************************************************************/
      50             : /*                       AnalyseSwiftFileList()                         */
      51             : /************************************************************************/
      52             : 
      53          13 : void VSICurlFilesystemHandlerBase::AnalyseSwiftFileList(
      54             :     const std::string &osBaseURL, const std::string &osPrefix,
      55             :     const char *pszJson, CPLStringList &osFileList, int nMaxFilesThisQuery,
      56             :     int nMaxFiles, bool &bIsTruncated, std::string &osNextMarker)
      57             : {
      58             : #if DEBUG_VERBOSE
      59             :     CPLDebug("SWIFT", "%s", pszJson);
      60             : #endif
      61          13 :     osNextMarker = "";
      62          13 :     bIsTruncated = false;
      63             : 
      64          13 :     CPLJSONDocument oDoc;
      65          13 :     if (!oDoc.LoadMemory(reinterpret_cast<const GByte *>(pszJson)))
      66           0 :         return;
      67             : 
      68          26 :     std::vector<std::pair<std::string, FileProp>> aoProps;
      69             :     // Count the number of occurrences of a path. Can be 1 or 2. 2 in the case
      70             :     // that both a filename and directory exist
      71          26 :     std::map<std::string, int> aoNameCount;
      72             : 
      73          26 :     CPLJSONArray oArray = oDoc.GetRoot().ToArray();
      74          23 :     for (int i = 0; i < oArray.Size(); i++)
      75             :     {
      76          10 :         CPLJSONObject oItem = oArray[i];
      77          20 :         std::string osName = oItem.GetString("name");
      78          10 :         GInt64 nSize = oItem.GetLong("bytes");
      79          20 :         std::string osLastModified = oItem.GetString("last_modified");
      80          20 :         std::string osSubdir = oItem.GetString("subdir");
      81          10 :         bool bHasCount = oItem.GetLong("count", -1) >= 0;
      82          10 :         if (!osName.empty())
      83             :         {
      84           6 :             osNextMarker = osName;
      85          12 :             if (osName.size() > osPrefix.size() &&
      86          12 :                 osName.substr(0, osPrefix.size()) == osPrefix)
      87             :             {
      88           6 :                 if (CPLHasUnbalancedPathTraversal(osName.c_str()))
      89             :                 {
      90           0 :                     CPLError(
      91             :                         CE_Warning, CPLE_AppDefined,
      92             :                         "Ignoring name '%s' that has a path traversal pattern",
      93             :                         osName.c_str());
      94           0 :                     continue;
      95             :                 }
      96             : 
      97           6 :                 if (bHasCount)
      98             :                 {
      99             :                     // Case when listing /vsiswift/
     100           2 :                     FileProp prop;
     101           2 :                     prop.eExists = EXIST_YES;
     102           2 :                     prop.bIsDirectory = true;
     103           2 :                     prop.bHasComputedFileSize = true;
     104           2 :                     prop.fileSize = 0;
     105           2 :                     prop.mTime = 0;
     106             : 
     107           2 :                     aoProps.push_back(
     108           4 :                         std::pair<std::string, FileProp>(osName, prop));
     109           2 :                     aoNameCount[osName]++;
     110             :                 }
     111             :                 else
     112             :                 {
     113           4 :                     FileProp prop;
     114           4 :                     prop.eExists = EXIST_YES;
     115           4 :                     prop.bHasComputedFileSize = true;
     116           4 :                     prop.fileSize = static_cast<GUIntBig>(nSize);
     117           4 :                     prop.bIsDirectory = false;
     118           4 :                     prop.mTime = 0;
     119             :                     int nYear, nMonth, nDay, nHour, nMin, nSec;
     120           4 :                     if (sscanf(osLastModified.c_str(),
     121             :                                "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
     122           4 :                                &nDay, &nHour, &nMin, &nSec) == 6)
     123             :                     {
     124             :                         struct tm brokendowntime;
     125           4 :                         brokendowntime.tm_year = nYear - 1900;
     126           4 :                         brokendowntime.tm_mon = nMonth - 1;
     127           4 :                         brokendowntime.tm_mday = nDay;
     128           4 :                         brokendowntime.tm_hour = nHour;
     129           4 :                         brokendowntime.tm_min = nMin;
     130           4 :                         brokendowntime.tm_sec = nSec;
     131           4 :                         prop.mTime = static_cast<time_t>(
     132           4 :                             CPLYMDHMSToUnixTime(&brokendowntime));
     133             :                     }
     134             : 
     135           4 :                     aoProps.push_back(std::pair<std::string, FileProp>(
     136           8 :                         osName.substr(osPrefix.size()), prop));
     137           4 :                     aoNameCount[osName.substr(osPrefix.size())]++;
     138             :                 }
     139             :             }
     140             :         }
     141           4 :         else if (!osSubdir.empty())
     142             :         {
     143           4 :             osNextMarker = osSubdir;
     144           4 :             if (osSubdir.back() == '/')
     145           4 :                 osSubdir.pop_back();
     146           4 :             if (STARTS_WITH(osSubdir.c_str(), osPrefix.c_str()))
     147             :             {
     148           4 :                 if (CPLHasUnbalancedPathTraversal(osSubdir.c_str()))
     149             :                 {
     150           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     151             :                              "Ignoring subdir '%s' that has a path traversal "
     152             :                              "pattern",
     153             :                              osSubdir.c_str());
     154           0 :                     continue;
     155             :                 }
     156             : 
     157           4 :                 FileProp prop;
     158           4 :                 prop.eExists = EXIST_YES;
     159           4 :                 prop.bIsDirectory = true;
     160           4 :                 prop.bHasComputedFileSize = true;
     161           4 :                 prop.fileSize = 0;
     162           4 :                 prop.mTime = 0;
     163             : 
     164           4 :                 aoProps.push_back(std::pair<std::string, FileProp>(
     165           8 :                     osSubdir.substr(osPrefix.size()), prop));
     166           4 :                 aoNameCount[osSubdir.substr(osPrefix.size())]++;
     167             :             }
     168             :         }
     169             : 
     170          10 :         if (nMaxFiles > 0 && aoProps.size() > static_cast<unsigned>(nMaxFiles))
     171           0 :             break;
     172             :     }
     173             : 
     174          13 :     bIsTruncated = aoProps.size() >= static_cast<unsigned>(nMaxFilesThisQuery);
     175          13 :     if (!bIsTruncated)
     176             :     {
     177          11 :         osNextMarker.clear();
     178             :     }
     179             : 
     180          23 :     for (size_t i = 0; i < aoProps.size(); i++)
     181             :     {
     182          10 :         std::string osSuffix;
     183          12 :         if (aoNameCount[aoProps[i].first] == 2 &&
     184           2 :             aoProps[i].second.bIsDirectory)
     185             :         {
     186             :             // Add a / suffix to disambiguish the situation
     187             :             // Normally we don't suffix directories with /, but we have
     188             :             // no alternative here
     189           1 :             osSuffix = "/";
     190             :         }
     191          10 :         if (nMaxFiles != 1)
     192             :         {
     193             :             std::string osCachedFilename =
     194          20 :                 osBaseURL + "/" + CPLAWSURLEncode(osPrefix, false) +
     195          30 :                 CPLAWSURLEncode(aoProps[i].first, false) + osSuffix;
     196             : #if DEBUG_VERBOSE
     197             :             CPLDebug("SWIFT", "Cache %s", osCachedFilename.c_str());
     198             : #endif
     199          10 :             SetCachedFileProp(osCachedFilename.c_str(), aoProps[i].second);
     200             :         }
     201          10 :         osFileList.AddString((aoProps[i].first + osSuffix).c_str());
     202             :     }
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*                         VSISwiftFSHandler                            */
     207             : /************************************************************************/
     208             : 
     209             : class VSISwiftFSHandler final : public IVSIS3LikeFSHandler
     210             : {
     211             :     const std::string m_osPrefix;
     212             :     CPL_DISALLOW_COPY_ASSIGN(VSISwiftFSHandler)
     213             : 
     214             :   protected:
     215             :     VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
     216             :     std::string
     217             :     GetURLFromFilename(const std::string &osFilename) const override;
     218             : 
     219          28 :     const char *GetDebugKey() const override
     220             :     {
     221          28 :         return "SWIFT";
     222             :     }
     223             : 
     224             :     IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
     225             :                                                bool bAllowNoObject) override;
     226             : 
     227         407 :     std::string GetFSPrefix() const override
     228             :     {
     229         407 :         return m_osPrefix;
     230             :     }
     231             : 
     232             :     char **GetFileList(const char *pszFilename, int nMaxFiles,
     233             :                        bool *pbGotFileList) override;
     234             : 
     235             :     void ClearCache() override;
     236             : 
     237             :     VSIVirtualHandleUniquePtr
     238             :     CreateWriteHandle(const char *pszFilename,
     239             :                       CSLConstList papszOptions) override;
     240             : 
     241             :   public:
     242        1754 :     explicit VSISwiftFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
     243             :     {
     244        1754 :     }
     245             : 
     246             :     ~VSISwiftFSHandler() override;
     247             : 
     248             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     249             :              int nFlags) override;
     250             : 
     251           0 :     VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
     252             :                     const char *const *papszOptions) override
     253             :     {
     254           0 :         return VSICurlFilesystemHandlerBase::OpenDir(pszPath, nRecurseDepth,
     255           0 :                                                      papszOptions);
     256             :     }
     257             : 
     258             :     const char *GetOptions() override;
     259             : 
     260             :     std::string
     261           0 :     GetStreamingFilename(const std::string &osFilename) const override
     262             :     {
     263           0 :         return osFilename;
     264             :     }
     265             : 
     266           0 :     VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
     267             :     {
     268           0 :         return new VSISwiftFSHandler(pszPrefix);
     269             :     }
     270             : };
     271             : 
     272             : /************************************************************************/
     273             : /*                            VSISwiftHandle                              */
     274             : /************************************************************************/
     275             : 
     276             : class VSISwiftHandle final : public IVSIS3LikeHandle
     277             : {
     278             :     CPL_DISALLOW_COPY_ASSIGN(VSISwiftHandle)
     279             : 
     280             :     VSISwiftHandleHelper *m_poHandleHelper = nullptr;
     281             : 
     282             :   protected:
     283             :     struct curl_slist *GetCurlHeaders(const std::string &osVerb,
     284             :                                       struct curl_slist *psHeaders) override;
     285             :     bool Authenticate(const char *pszFilename) override;
     286             : 
     287             :   public:
     288             :     VSISwiftHandle(VSISwiftFSHandler *poFS, const char *pszFilename,
     289             :                    VSISwiftHandleHelper *poHandleHelper);
     290             :     ~VSISwiftHandle() override;
     291             : };
     292             : 
     293             : /************************************************************************/
     294             : /*                          CreateWriteHandle()                         */
     295             : /************************************************************************/
     296             : 
     297             : VSIVirtualHandleUniquePtr
     298           3 : VSISwiftFSHandler::CreateWriteHandle(const char *pszFilename,
     299             :                                      CSLConstList papszOptions)
     300             : {
     301             :     auto poHandleHelper =
     302           3 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), false);
     303           3 :     if (poHandleHelper == nullptr)
     304           0 :         return nullptr;
     305             :     auto poHandle = std::make_unique<VSIChunkedWriteHandle>(
     306           6 :         this, pszFilename, poHandleHelper, papszOptions);
     307           3 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                       ~VSISwiftFSHandler()                           */
     312             : /************************************************************************/
     313             : 
     314        2242 : VSISwiftFSHandler::~VSISwiftFSHandler()
     315             : {
     316        1121 :     VSISwiftFSHandler::ClearCache();
     317        1121 :     VSISwiftHandleHelper::CleanMutex();
     318        2242 : }
     319             : 
     320             : /************************************************************************/
     321             : /*                            ClearCache()                              */
     322             : /************************************************************************/
     323             : 
     324        1458 : void VSISwiftFSHandler::ClearCache()
     325             : {
     326        1458 :     VSICurlFilesystemHandlerBase::ClearCache();
     327             : 
     328        1458 :     VSISwiftHandleHelper::ClearCache();
     329        1458 : }
     330             : 
     331             : /************************************************************************/
     332             : /*                           GetOptions()                               */
     333             : /************************************************************************/
     334             : 
     335           2 : const char *VSISwiftFSHandler::GetOptions()
     336             : {
     337             :     static std::string osOptions(
     338           2 :         std::string("<Options>") +
     339             :         "  <Option name='SWIFT_STORAGE_URL' type='string' "
     340             :         "description='Storage URL. To use with SWIFT_AUTH_TOKEN'/>"
     341             :         "  <Option name='SWIFT_AUTH_TOKEN' type='string' "
     342             :         "description='Authorization token'/>"
     343             :         "  <Option name='SWIFT_AUTH_V1_URL' type='string' "
     344             :         "description='Authentication V1 URL. To use with SWIFT_USER and "
     345             :         "SWIFT_KEY'/>"
     346             :         "  <Option name='SWIFT_USER' type='string' "
     347             :         "description='User name to use with authentication V1'/>"
     348             :         "  <Option name='SWIFT_KEY' type='string' "
     349             :         "description='Key/password to use with authentication V1'/>"
     350             :         "  <Option name='OS_IDENTITY_API_VERSION' type='string' "
     351             :         "description='OpenStack identity API version'/>"
     352             :         "  <Option name='OS_AUTH_TYPE' type='string' "
     353             :         "description='Authentication URL'/>"
     354             :         "  <Option name='OS_USERNAME' type='string' "
     355             :         "description='User name'/>"
     356             :         "  <Option name='OS_PASSWORD' type='string' "
     357             :         "description='Password'/>"
     358             :         "  <Option name='OS_USER_DOMAIN_NAME' type='string' "
     359             :         "description='User domain name'/>"
     360             :         "  <Option name='OS_PROJECT_NAME' type='string' "
     361             :         "description='Project name'/>"
     362             :         "  <Option name='OS_PROJECT_DOMAIN_NAME' type='string' "
     363             :         "description='Project domain name'/>"
     364             :         "  <Option name='OS_REGION_NAME' type='string' "
     365           3 :         "description='Region name'/>" +
     366           3 :         VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
     367           2 :     return osOptions.c_str();
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                          CreateFileHandle()                          */
     372             : /************************************************************************/
     373             : 
     374          21 : VSICurlHandle *VSISwiftFSHandler::CreateFileHandle(const char *pszFilename)
     375             : {
     376          42 :     VSISwiftHandleHelper *poHandleHelper = VSISwiftHandleHelper::BuildFromURI(
     377          63 :         pszFilename + GetFSPrefix().size(), GetFSPrefix().c_str());
     378          21 :     if (poHandleHelper)
     379             :     {
     380          19 :         return new VSISwiftHandle(this, pszFilename, poHandleHelper);
     381             :     }
     382           2 :     return nullptr;
     383             : }
     384             : 
     385             : /************************************************************************/
     386             : /*                         GetURLFromFilename()                         */
     387             : /************************************************************************/
     388             : 
     389             : std::string
     390          21 : VSISwiftFSHandler::GetURLFromFilename(const std::string &osFilename) const
     391             : {
     392             :     const std::string osFilenameWithoutPrefix =
     393          42 :         osFilename.substr(GetFSPrefix().size());
     394             : 
     395             :     auto poHandleHelper = std::unique_ptr<VSISwiftHandleHelper>(
     396             :         VSISwiftHandleHelper::BuildFromURI(osFilenameWithoutPrefix.c_str(),
     397          42 :                                            GetFSPrefix().c_str()));
     398          21 :     if (!poHandleHelper)
     399             :     {
     400           0 :         return std::string();
     401             :     }
     402          42 :     std::string osBaseURL(poHandleHelper->GetURL());
     403          21 :     if (!osBaseURL.empty() && osBaseURL.back() == '/')
     404           2 :         osBaseURL.pop_back();
     405          21 :     return osBaseURL;
     406             : }
     407             : 
     408             : /************************************************************************/
     409             : /*                          CreateHandleHelper()                        */
     410             : /************************************************************************/
     411             : 
     412             : IVSIS3LikeHandleHelper *
     413          26 : VSISwiftFSHandler::CreateHandleHelper(const char *pszURI, bool)
     414             : {
     415          26 :     return VSISwiftHandleHelper::BuildFromURI(pszURI, GetFSPrefix().c_str());
     416             : }
     417             : 
     418             : /************************************************************************/
     419             : /*                                Stat()                                */
     420             : /************************************************************************/
     421             : 
     422          13 : int VSISwiftFSHandler::Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     423             :                             int nFlags)
     424             : {
     425          13 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     426           0 :         return -1;
     427             : 
     428          13 :     if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
     429           2 :         return VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf,
     430           2 :                                                   nFlags);
     431             : 
     432          22 :     std::string osFilename(pszFilename);
     433          11 :     if (osFilename.back() == '/')
     434           6 :         osFilename.pop_back();
     435             : 
     436          11 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     437             : 
     438          11 :     if (VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf, nFlags) == 0)
     439             :     {
     440             :         // if querying /vsiswift/container_name, the GET will succeed and
     441             :         // we would consider this as a file whereas it should be exposed as
     442             :         // a directory
     443           6 :         if (std::count(osFilename.begin(), osFilename.end(), '/') <= 2)
     444             :         {
     445             : 
     446             :             auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     447           1 :                 CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
     448           1 :             if (poHandleHelper)
     449             :             {
     450           2 :                 FileProp cachedFileProp;
     451           1 :                 cachedFileProp.eExists = EXIST_YES;
     452           1 :                 cachedFileProp.bHasComputedFileSize = false;
     453           1 :                 cachedFileProp.fileSize = 0;
     454           1 :                 cachedFileProp.bIsDirectory = true;
     455           1 :                 cachedFileProp.mTime = 0;
     456           1 :                 cachedFileProp.nMode = S_IFDIR;
     457           1 :                 SetCachedFileProp(poHandleHelper->GetURL().c_str(),
     458             :                                   cachedFileProp);
     459             :             }
     460             : 
     461           1 :             pStatBuf->st_size = 0;
     462           1 :             pStatBuf->st_mode = S_IFDIR;
     463             :         }
     464           6 :         return 0;
     465             :     }
     466             : 
     467             :     // In the case of a directory, a GET on it will not work, so we have to
     468             :     // query the upper directory contents
     469           5 :     if (std::count(osFilename.begin(), osFilename.end(), '/') < 2)
     470           0 :         return -1;
     471             : 
     472             :     char **papszContents =
     473           5 :         VSIReadDir(CPLGetPathSafe(osFilename.c_str()).c_str());
     474           5 :     int nRet = CSLFindStringCaseSensitive(
     475             :                    papszContents, CPLGetFilename(osFilename.c_str())) >= 0
     476           5 :                    ? 0
     477           5 :                    : -1;
     478           5 :     CSLDestroy(papszContents);
     479             : 
     480          10 :     FileProp cachedFileProp;
     481           5 :     if (nRet == 0)
     482             :     {
     483           2 :         pStatBuf->st_mode = S_IFDIR;
     484             : 
     485           2 :         cachedFileProp.eExists = EXIST_YES;
     486           2 :         cachedFileProp.bHasComputedFileSize = false;
     487           2 :         cachedFileProp.fileSize = 0;
     488           2 :         cachedFileProp.bIsDirectory = true;
     489           2 :         cachedFileProp.mTime = 0;
     490           2 :         cachedFileProp.nMode = S_IFDIR;
     491             :     }
     492             :     else
     493             :     {
     494           3 :         cachedFileProp.eExists = EXIST_NO;
     495             :     }
     496             : 
     497             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     498           5 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
     499           5 :     if (poHandleHelper)
     500             :     {
     501           5 :         SetCachedFileProp(poHandleHelper->GetURL().c_str(), cachedFileProp);
     502             :     }
     503             : 
     504           5 :     return nRet;
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                           GetFileList()                              */
     509             : /************************************************************************/
     510             : 
     511          14 : char **VSISwiftFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
     512             :                                       bool *pbGotFileList)
     513             : {
     514             :     if (ENABLE_DEBUG)
     515             :         CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
     516          14 :     *pbGotFileList = false;
     517          14 :     CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
     518          42 :     std::string osDirnameWithoutPrefix = pszDirname + GetFSPrefix().size();
     519          14 :     if (!osDirnameWithoutPrefix.empty() && osDirnameWithoutPrefix.back() == '/')
     520             :     {
     521           0 :         osDirnameWithoutPrefix.pop_back();
     522             :     }
     523             : 
     524          28 :     std::string osBucket(osDirnameWithoutPrefix);
     525          28 :     std::string osObjectKey;
     526          14 :     size_t nSlashPos = osDirnameWithoutPrefix.find('/');
     527          14 :     if (nSlashPos != std::string::npos)
     528             :     {
     529           3 :         osBucket = osDirnameWithoutPrefix.substr(0, nSlashPos);
     530           3 :         osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
     531             :     }
     532             : 
     533             :     IVSIS3LikeHandleHelper *poS3HandleHelper =
     534          14 :         CreateHandleHelper(osBucket.c_str(), true);
     535          14 :     if (poS3HandleHelper == nullptr)
     536             :     {
     537           0 :         return nullptr;
     538             :     }
     539             : 
     540          14 :     WriteFuncStruct sWriteFuncData;
     541             : 
     542          28 :     CPLStringList osFileList;  // must be left in this scope !
     543          28 :     std::string osNextMarker;  // must be left in this scope !
     544             : 
     545          28 :     std::string osMaxKeys = CPLGetConfigOption("SWIFT_MAX_KEYS", "10000");
     546          14 :     int nMaxFilesThisQuery = atoi(osMaxKeys.c_str());
     547          14 :     if (nMaxFiles > 0 && nMaxFiles <= 100 && nMaxFiles < nMaxFilesThisQuery)
     548             :     {
     549           2 :         nMaxFilesThisQuery = nMaxFiles + 1;
     550             :     }
     551          14 :     const std::string osPrefix(osObjectKey.empty() ? std::string()
     552          28 :                                                    : osObjectKey + "/");
     553             : 
     554          28 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
     555          28 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
     556             : 
     557             :     while (true)
     558             :     {
     559          16 :         CPLHTTPRetryContext oRetryContext(oRetryParameters);
     560             :         bool bRetry;
     561           2 :         do
     562             :         {
     563          16 :             bRetry = false;
     564          16 :             poS3HandleHelper->ResetQueryParameters();
     565          16 :             std::string osBaseURL(poS3HandleHelper->GetURL());
     566             : 
     567          16 :             CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
     568          16 :             CURL *hCurlHandle = curl_easy_init();
     569             : 
     570          16 :             if (!osBucket.empty())
     571             :             {
     572          13 :                 poS3HandleHelper->AddQueryParameter("delimiter", "/");
     573          13 :                 if (!osNextMarker.empty())
     574           2 :                     poS3HandleHelper->AddQueryParameter("marker", osNextMarker);
     575          13 :                 poS3HandleHelper->AddQueryParameter(
     576             :                     "limit", CPLSPrintf("%d", nMaxFilesThisQuery));
     577          13 :                 if (!osPrefix.empty())
     578           3 :                     poS3HandleHelper->AddQueryParameter("prefix", osPrefix);
     579             :             }
     580             : 
     581          32 :             struct curl_slist *headers = VSICurlSetOptions(
     582          16 :                 hCurlHandle, poS3HandleHelper->GetURL().c_str(),
     583             :                 aosHTTPOptions.List());
     584             :             // Disable automatic redirection
     585          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
     586             : 
     587          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, nullptr);
     588             : 
     589          16 :             VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr,
     590             :                                        nullptr);
     591          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA,
     592             :                                        &sWriteFuncData);
     593          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     594             :                                        VSICurlHandleWriteFunc);
     595             : 
     596          16 :             WriteFuncStruct sWriteFuncHeaderData;
     597          16 :             VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, nullptr, nullptr,
     598             :                                        nullptr);
     599          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA,
     600             :                                        &sWriteFuncHeaderData);
     601          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
     602             :                                        VSICurlHandleWriteFunc);
     603             : 
     604          16 :             char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
     605          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
     606             :                                        szCurlErrBuf);
     607             : 
     608          16 :             headers = poS3HandleHelper->GetCurlHeaders("GET", headers);
     609          16 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER,
     610             :                                        headers);
     611             : 
     612          16 :             VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
     613             : 
     614          16 :             VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
     615             : 
     616          16 :             if (headers != nullptr)
     617          16 :                 curl_slist_free_all(headers);
     618             : 
     619          16 :             if (sWriteFuncData.pBuffer == nullptr)
     620             :             {
     621           3 :                 delete poS3HandleHelper;
     622           3 :                 curl_easy_cleanup(hCurlHandle);
     623           3 :                 CPLFree(sWriteFuncHeaderData.pBuffer);
     624           3 :                 return nullptr;
     625             :             }
     626             : 
     627          13 :             long response_code = 0;
     628          13 :             curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     629          13 :             if (response_code != 200)
     630             :             {
     631             :                 // Look if we should attempt a retry
     632           0 :                 if (oRetryContext.CanRetry(static_cast<int>(response_code),
     633           0 :                                            sWriteFuncHeaderData.pBuffer,
     634             :                                            szCurlErrBuf))
     635             :                 {
     636           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     637             :                              "HTTP error code: %d - %s. "
     638             :                              "Retrying again in %.1f secs",
     639             :                              static_cast<int>(response_code),
     640           0 :                              poS3HandleHelper->GetURL().c_str(),
     641             :                              oRetryContext.GetCurrentDelay());
     642           0 :                     CPLSleep(oRetryContext.GetCurrentDelay());
     643           0 :                     bRetry = true;
     644           0 :                     CPLFree(sWriteFuncData.pBuffer);
     645           0 :                     CPLFree(sWriteFuncHeaderData.pBuffer);
     646             :                 }
     647             :                 else
     648             :                 {
     649           0 :                     CPLDebug(GetDebugKey(), "%s", sWriteFuncData.pBuffer);
     650           0 :                     CPLFree(sWriteFuncData.pBuffer);
     651           0 :                     CPLFree(sWriteFuncHeaderData.pBuffer);
     652           0 :                     delete poS3HandleHelper;
     653           0 :                     curl_easy_cleanup(hCurlHandle);
     654           0 :                     return nullptr;
     655             :                 }
     656             :             }
     657             :             else
     658             :             {
     659          13 :                 *pbGotFileList = true;
     660             :                 bool bIsTruncated;
     661          13 :                 AnalyseSwiftFileList(
     662          13 :                     osBaseURL, osPrefix, sWriteFuncData.pBuffer, osFileList,
     663             :                     nMaxFilesThisQuery, nMaxFiles, bIsTruncated, osNextMarker);
     664             : 
     665          13 :                 CPLFree(sWriteFuncData.pBuffer);
     666          13 :                 CPLFree(sWriteFuncHeaderData.pBuffer);
     667             : 
     668          13 :                 if (osNextMarker.empty())
     669             :                 {
     670          11 :                     delete poS3HandleHelper;
     671          11 :                     curl_easy_cleanup(hCurlHandle);
     672          11 :                     return osFileList.StealList();
     673             :                 }
     674             :             }
     675             : 
     676           2 :             curl_easy_cleanup(hCurlHandle);
     677             :         } while (bRetry);
     678           2 :     }
     679             : }
     680             : 
     681             : /************************************************************************/
     682             : /*                            VSISwiftHandle()                            */
     683             : /************************************************************************/
     684             : 
     685          19 : VSISwiftHandle::VSISwiftHandle(VSISwiftFSHandler *poFSIn,
     686             :                                const char *pszFilename,
     687          19 :                                VSISwiftHandleHelper *poHandleHelper)
     688          19 :     : IVSIS3LikeHandle(poFSIn, pszFilename, poHandleHelper->GetURL().c_str()),
     689          19 :       m_poHandleHelper(poHandleHelper)
     690             : {
     691          19 : }
     692             : 
     693             : /************************************************************************/
     694             : /*                            ~VSISwiftHandle()                           */
     695             : /************************************************************************/
     696             : 
     697          38 : VSISwiftHandle::~VSISwiftHandle()
     698             : {
     699          19 :     delete m_poHandleHelper;
     700          38 : }
     701             : 
     702             : /************************************************************************/
     703             : /*                           GetCurlHeaders()                           */
     704             : /************************************************************************/
     705             : 
     706          17 : struct curl_slist *VSISwiftHandle::GetCurlHeaders(const std::string &osVerb,
     707             :                                                   struct curl_slist *psHeaders)
     708             : {
     709          17 :     return m_poHandleHelper->GetCurlHeaders(osVerb, psHeaders);
     710             : }
     711             : 
     712             : /************************************************************************/
     713             : /*                           Authenticate()                             */
     714             : /************************************************************************/
     715             : 
     716           0 : bool VSISwiftHandle::Authenticate(const char *pszFilename)
     717             : {
     718           0 :     return m_poHandleHelper->Authenticate(pszFilename);
     719             : }
     720             : 
     721             : } /* end of namespace cpl */
     722             : 
     723             : #endif  // DOXYGEN_SKIP
     724             : //! @endcond
     725             : 
     726             : /************************************************************************/
     727             : /*                     VSIInstallSwiftFileHandler()                     */
     728             : /************************************************************************/
     729             : 
     730             : /*!
     731             :  \brief Install /vsiswift/ OpenStack Swif Object Storage (Swift) file
     732             :  system handler (requires libcurl)
     733             : 
     734             :  \verbatim embed:rst
     735             :  See :ref:`/vsiswift/ documentation <vsiswift>`
     736             :  \endverbatim
     737             : 
     738             :  @since GDAL 2.3
     739             :  */
     740        1754 : void VSIInstallSwiftFileHandler(void)
     741             : {
     742        1754 :     VSIFileManager::InstallHandler("/vsiswift/",
     743        1754 :                                    new cpl::VSISwiftFSHandler("/vsiswift/"));
     744        1754 : }
     745             : 
     746             : #endif /* HAVE_CURL */

Generated by: LCOV version 1.14