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

Generated by: LCOV version 1.14