LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 747 911 82.0 %
Date: 2024-04-28 23:18:46 Functions: 52 54 96.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for Microsoft Azure Data Lake Storage
       5             :  *Gen2 Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, 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             : #include "cpl_port.h"
      30             : #include "cpl_http.h"
      31             : #include "cpl_json.h"
      32             : #include "cpl_time.h"
      33             : #include "cpl_vsil_curl_priv.h"
      34             : #include "cpl_vsil_curl_class.h"
      35             : 
      36             : #include <errno.h>
      37             : 
      38             : #include <algorithm>
      39             : #include <set>
      40             : #include <map>
      41             : #include <memory>
      42             : 
      43             : #include "cpl_azure.h"
      44             : 
      45             : #ifndef HAVE_CURL
      46             : 
      47             : void VSIInstallADLSFileHandler(void)
      48             : {
      49             :     // Not supported
      50             : }
      51             : 
      52             : #else
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : #ifndef DOXYGEN_SKIP
      56             : 
      57             : #define ENABLE_DEBUG 0
      58             : 
      59             : #define unchecked_curl_easy_setopt(handle, opt, param)                         \
      60             :     CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
      61             : 
      62             : namespace cpl
      63             : {
      64             : 
      65             : /************************************************************************/
      66             : /*                         GetContinuationToken()                       */
      67             : /************************************************************************/
      68             : 
      69          19 : static std::string GetContinuationToken(const char *pszHeaders)
      70             : {
      71          19 :     std::string osContinuation;
      72          19 :     if (pszHeaders)
      73             :     {
      74          19 :         const char *pszContinuation = strstr(pszHeaders, "x-ms-continuation: ");
      75          19 :         if (pszContinuation)
      76             :         {
      77          11 :             pszContinuation += strlen("x-ms-continuation: ");
      78          11 :             const char *pszEOL = strstr(pszContinuation, "\r\n");
      79          11 :             if (pszEOL)
      80             :             {
      81             :                 osContinuation.assign(pszContinuation,
      82          11 :                                       pszEOL - pszContinuation);
      83             :             }
      84             :         }
      85             :     }
      86          19 :     return osContinuation;
      87             : }
      88             : 
      89             : /************************************************************************/
      90             : /*                        RemoveTrailingSlash()                        */
      91             : /************************************************************************/
      92             : 
      93          49 : static std::string RemoveTrailingSlash(const std::string &osFilename)
      94             : {
      95          49 :     std::string osWithoutSlash(osFilename);
      96          49 :     if (!osWithoutSlash.empty() && osWithoutSlash.back() == '/')
      97           2 :         osWithoutSlash.resize(osWithoutSlash.size() - 1);
      98          49 :     return osWithoutSlash;
      99             : }
     100             : 
     101             : /************************************************************************/
     102             : /*                             VSIDIRADLS                               */
     103             : /************************************************************************/
     104             : 
     105             : class VSIADLSFSHandler;
     106             : 
     107             : struct VSIDIRADLS : public VSIDIR
     108             : {
     109             :     int m_nRecurseDepth = 0;
     110             : 
     111             :     struct Iterator
     112             :     {
     113             :         std::string m_osNextMarker{};
     114             :         std::vector<std::unique_ptr<VSIDIREntry>> m_aoEntries{};
     115             :         int m_nPos = 0;
     116             : 
     117          17 :         void clear()
     118             :         {
     119          17 :             m_osNextMarker.clear();
     120          17 :             m_nPos = 0;
     121          17 :             m_aoEntries.clear();
     122          17 :         }
     123             :     };
     124             : 
     125             :     Iterator m_oIterWithinFilesystem{};
     126             :     Iterator m_oIterFromRoot{};
     127             : 
     128             :     // Backup file system listing when doing a recursive OpenDir() from
     129             :     // the account root
     130             :     bool m_bRecursiveRequestFromAccountRoot = false;
     131             : 
     132             :     std::string m_osFilesystem{};
     133             :     std::string m_osObjectKey{};
     134             :     VSIADLSFSHandler *m_poFS = nullptr;
     135             :     int m_nMaxFiles = 0;
     136             :     bool m_bCacheEntries = true;
     137             :     std::string
     138             :         m_osFilterPrefix{};  // client-side only. No server-side option in
     139             : 
     140             :     // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/list
     141             : 
     142          10 :     explicit VSIDIRADLS(VSIADLSFSHandler *poFSIn) : m_poFS(poFSIn)
     143             :     {
     144          10 :     }
     145             : 
     146             :     VSIDIRADLS(const VSIDIRADLS &) = delete;
     147             :     VSIDIRADLS &operator=(const VSIDIRADLS &) = delete;
     148             : 
     149             :     const VSIDIREntry *NextDirEntry() override;
     150             : 
     151             :     bool IssueListDir();
     152             :     bool AnalysePathList(const std::string &osBaseURL, const char *pszJSON);
     153             :     bool AnalyseFilesystemList(const std::string &osBaseURL,
     154             :                                const char *pszJSON);
     155             :     void clear();
     156             : };
     157             : 
     158             : /************************************************************************/
     159             : /*                       VSIADLSFSHandler                              */
     160             : /************************************************************************/
     161             : 
     162             : class VSIADLSFSHandler final : public IVSIS3LikeFSHandler
     163             : {
     164             :     CPL_DISALLOW_COPY_ASSIGN(VSIADLSFSHandler)
     165             : 
     166             :   protected:
     167             :     VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
     168             :     std::string GetURLFromFilename(const std::string &osFilename) override;
     169             : 
     170             :     char **GetFileList(const char *pszFilename, int nMaxFiles,
     171             :                        bool *pbGotFileList) override;
     172             : 
     173             :     int CopyObject(const char *oldpath, const char *newpath,
     174             :                    CSLConstList papszMetadata) override;
     175             :     int MkdirInternal(const char *pszDirname, long nMode,
     176             :                       bool bDoStatCheck) override;
     177             :     int RmdirInternal(const char *pszDirname, bool bRecursive);
     178             : 
     179             :     void ClearCache() override;
     180             : 
     181           2 :     bool IsAllowedHeaderForObjectCreation(const char *pszHeaderName) override
     182             :     {
     183           2 :         return STARTS_WITH(pszHeaderName, "x-ms-");
     184             :     }
     185             : 
     186             :     VSIVirtualHandleUniquePtr
     187             :     CreateWriteHandle(const char *pszFilename,
     188             :                       CSLConstList papszOptions) override;
     189             : 
     190             :   public:
     191        1227 :     VSIADLSFSHandler() = default;
     192        1702 :     ~VSIADLSFSHandler() override = default;
     193             : 
     194         648 :     std::string GetFSPrefix() const override
     195             :     {
     196         648 :         return "/vsiadls/";
     197             :     }
     198             : 
     199          35 :     const char *GetDebugKey() const override
     200             :     {
     201          35 :         return "ADLS";
     202             :     }
     203             : 
     204             :     int Rename(const char *oldpath, const char *newpath) override;
     205             :     int Unlink(const char *pszFilename) override;
     206             :     int Mkdir(const char *, long) override;
     207             :     int Rmdir(const char *) override;
     208             :     int RmdirRecursive(const char *pszDirname) override;
     209             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     210             :              int nFlags) override;
     211             : 
     212             :     char **GetFileMetadata(const char *pszFilename, const char *pszDomain,
     213             :                            CSLConstList papszOptions) override;
     214             : 
     215             :     bool SetFileMetadata(const char *pszFilename, CSLConstList papszMetadata,
     216             :                          const char *pszDomain,
     217             :                          CSLConstList papszOptions) override;
     218             : 
     219             :     const char *GetOptions() override;
     220             : 
     221             :     char *GetSignedURL(const char *pszFilename,
     222             :                        CSLConstList papszOptions) override;
     223             : 
     224             :     char **GetFileList(const char *pszFilename, int nMaxFiles,
     225             :                        bool bCacheEntries, bool *pbGotFileList);
     226             : 
     227             :     VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
     228             :                     const char *const *papszOptions) override;
     229             : 
     230             :     enum class Event
     231             :     {
     232             :         CREATE_FILE,
     233             :         APPEND_DATA,
     234             :         FLUSH
     235             :     };
     236             : 
     237             :     // Block list upload
     238             :     bool UploadFile(const std::string &osFilename, Event event,
     239             :                     vsi_l_offset nPosition, const void *pabyBuffer,
     240             :                     size_t nBufferSize,
     241             :                     IVSIS3LikeHandleHelper *poS3HandleHelper, int nMaxRetry,
     242             :                     double dfRetryDelay, CSLConstList papszOptions);
     243             : 
     244             :     // Multipart upload (mapping of S3 interface)
     245           1 :     bool SupportsParallelMultipartUpload() const override
     246             :     {
     247           1 :         return true;
     248             :     }
     249             : 
     250           1 :     std::string InitiateMultipartUpload(
     251             :         const std::string &osFilename, IVSIS3LikeHandleHelper *poS3HandleHelper,
     252             :         int nMaxRetry, double dfRetryDelay, CSLConstList papszOptions) override
     253             :     {
     254           1 :         return UploadFile(osFilename, Event::CREATE_FILE, 0, nullptr, 0,
     255             :                           poS3HandleHelper, nMaxRetry, dfRetryDelay,
     256             :                           papszOptions)
     257             :                    ? std::string("dummy")
     258           1 :                    : std::string();
     259             :     }
     260             : 
     261           2 :     std::string UploadPart(const std::string &osFilename, int /* nPartNumber */,
     262             :                            const std::string & /* osUploadID */,
     263             :                            vsi_l_offset nPosition, const void *pabyBuffer,
     264             :                            size_t nBufferSize,
     265             :                            IVSIS3LikeHandleHelper *poS3HandleHelper,
     266             :                            int nMaxRetry, double dfRetryDelay,
     267             :                            CSLConstList /* papszOptions */) override
     268             :     {
     269           2 :         return UploadFile(osFilename, Event::APPEND_DATA, nPosition, pabyBuffer,
     270             :                           nBufferSize, poS3HandleHelper, nMaxRetry,
     271             :                           dfRetryDelay, nullptr)
     272             :                    ? std::string("dummy")
     273           2 :                    : std::string();
     274             :     }
     275             : 
     276           1 :     bool CompleteMultipart(const std::string &osFilename,
     277             :                            const std::string & /* osUploadID */,
     278             :                            const std::vector<std::string> & /* aosEtags */,
     279             :                            vsi_l_offset nTotalSize,
     280             :                            IVSIS3LikeHandleHelper *poS3HandleHelper,
     281             :                            int nMaxRetry, double dfRetryDelay) override
     282             :     {
     283           1 :         return UploadFile(osFilename, Event::FLUSH, nTotalSize, nullptr, 0,
     284           1 :                           poS3HandleHelper, nMaxRetry, dfRetryDelay, nullptr);
     285             :     }
     286             : 
     287           0 :     bool AbortMultipart(const std::string & /* osFilename */,
     288             :                         const std::string & /* osUploadID */,
     289             :                         IVSIS3LikeHandleHelper * /*poS3HandleHelper */,
     290             :                         int /* nMaxRetry */, double /* dfRetryDelay */) override
     291             :     {
     292           0 :         return true;
     293             :     }
     294             : 
     295             :     IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
     296             :                                                bool bAllowNoObject) override;
     297             : 
     298             :     std::string
     299             :     GetStreamingFilename(const std::string &osFilename) const override;
     300             : };
     301             : 
     302             : /************************************************************************/
     303             : /*                          VSIADLSWriteHandle                         */
     304             : /************************************************************************/
     305             : 
     306             : class VSIADLSWriteHandle final : public VSIAppendWriteHandle
     307             : {
     308             :     CPL_DISALLOW_COPY_ASSIGN(VSIADLSWriteHandle)
     309             : 
     310             :     std::unique_ptr<VSIAzureBlobHandleHelper> m_poHandleHelper{};
     311             :     bool m_bCreated = false;
     312             : 
     313             :     bool Send(bool bIsLastBlock) override;
     314             : 
     315             :     bool SendInternal(VSIADLSFSHandler::Event event, CSLConstList papszOptions);
     316             : 
     317             :     void InvalidateParentDirectory();
     318             : 
     319             :   public:
     320             :     VSIADLSWriteHandle(VSIADLSFSHandler *poFS, const char *pszFilename,
     321             :                        VSIAzureBlobHandleHelper *poHandleHelper);
     322             :     virtual ~VSIADLSWriteHandle();
     323             : 
     324             :     bool CreateFile(CSLConstList papszOptions);
     325             : };
     326             : 
     327             : /************************************************************************/
     328             : /*                                clear()                               */
     329             : /************************************************************************/
     330             : 
     331          17 : void VSIDIRADLS::clear()
     332             : {
     333          17 :     if (!m_osFilesystem.empty())
     334          12 :         m_oIterWithinFilesystem.clear();
     335             :     else
     336           5 :         m_oIterFromRoot.clear();
     337          17 : }
     338             : 
     339             : /************************************************************************/
     340             : /*                        GetUnixTimeFromRFC822()                       */
     341             : /************************************************************************/
     342             : 
     343          17 : static GIntBig GetUnixTimeFromRFC822(const char *pszRFC822DateTime)
     344             : {
     345             :     int nYear, nMonth, nDay, nHour, nMinute, nSecond;
     346          17 :     if (CPLParseRFC822DateTime(pszRFC822DateTime, &nYear, &nMonth, &nDay,
     347          17 :                                &nHour, &nMinute, &nSecond, nullptr, nullptr))
     348             :     {
     349             :         struct tm brokendowntime;
     350          11 :         brokendowntime.tm_year = nYear - 1900;
     351          11 :         brokendowntime.tm_mon = nMonth - 1;
     352          11 :         brokendowntime.tm_mday = nDay;
     353          11 :         brokendowntime.tm_hour = nHour;
     354          11 :         brokendowntime.tm_min = nMinute;
     355          11 :         brokendowntime.tm_sec = nSecond < 0 ? 0 : nSecond;
     356          11 :         return CPLYMDHMSToUnixTime(&brokendowntime);
     357             :     }
     358           6 :     return GINTBIG_MIN;
     359             : }
     360             : 
     361             : /************************************************************************/
     362             : /*                           AnalysePathList()                          */
     363             : /************************************************************************/
     364             : 
     365          11 : bool VSIDIRADLS::AnalysePathList(const std::string &osBaseURL,
     366             :                                  const char *pszJSON)
     367             : {
     368             : #if DEBUG_VERBOSE
     369             :     CPLDebug(m_poFS->GetDebugKey(), "%s", pszJSON);
     370             : #endif
     371             : 
     372          22 :     CPLJSONDocument oDoc;
     373          11 :     if (!oDoc.LoadMemory(pszJSON))
     374           0 :         return false;
     375             : 
     376          33 :     auto oPaths = oDoc.GetRoot().GetArray("paths");
     377          11 :     if (!oPaths.IsValid())
     378             :     {
     379           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find paths[]");
     380           0 :         return false;
     381             :     }
     382             : 
     383          23 :     for (const auto &oPath : oPaths)
     384             :     {
     385          12 :         m_oIterWithinFilesystem.m_aoEntries.push_back(
     386          24 :             std::unique_ptr<VSIDIREntry>(new VSIDIREntry()));
     387          12 :         auto &entry = m_oIterWithinFilesystem.m_aoEntries.back();
     388             : 
     389             :         // Returns relative path to the filesystem, so for example
     390             :         // "mydir/foo.bin" for
     391             :         // https://{account}.dfs.core.windows.net/{filesystem}/mydir/foo.bin
     392          24 :         const std::string osName(oPath.GetString("name"));
     393          17 :         if (!m_osObjectKey.empty() &&
     394          17 :             STARTS_WITH(osName.c_str(), (m_osObjectKey + "/").c_str()))
     395          10 :             entry->pszName =
     396           5 :                 CPLStrdup(osName.substr(m_osObjectKey.size() + 1).c_str());
     397           7 :         else if (m_bRecursiveRequestFromAccountRoot && !m_osFilesystem.empty())
     398           3 :             entry->pszName = CPLStrdup((m_osFilesystem + '/' + osName).c_str());
     399             :         else
     400           4 :             entry->pszName = CPLStrdup(osName.c_str());
     401          12 :         entry->nSize = static_cast<GUIntBig>(oPath.GetLong("contentLength"));
     402          12 :         entry->bSizeKnown = true;
     403          12 :         entry->nMode =
     404          24 :             oPath.GetString("isDirectory") == "true" ? S_IFDIR : S_IFREG;
     405          24 :         entry->nMode |=
     406          12 :             VSICurlParseUnixPermissions(oPath.GetString("permissions").c_str());
     407          12 :         entry->bModeKnown = true;
     408             : 
     409          24 :         std::string ETag = oPath.GetString("etag");
     410          12 :         if (!ETag.empty())
     411             :         {
     412           0 :             entry->papszExtra =
     413           0 :                 CSLSetNameValue(entry->papszExtra, "ETag", ETag.c_str());
     414             :         }
     415             : 
     416             :         const GIntBig nMTime =
     417          12 :             GetUnixTimeFromRFC822(oPath.GetString("lastModified").c_str());
     418          12 :         if (nMTime != GINTBIG_MIN)
     419             :         {
     420          11 :             entry->nMTime = nMTime;
     421          11 :             entry->bMTimeKnown = true;
     422             :         }
     423             : 
     424          12 :         if (m_bCacheEntries)
     425             :         {
     426          24 :             FileProp prop;
     427          12 :             prop.eExists = EXIST_YES;
     428          12 :             prop.bHasComputedFileSize = true;
     429          12 :             prop.fileSize = entry->nSize;
     430          12 :             prop.bIsDirectory = CPL_TO_BOOL(VSI_ISDIR(entry->nMode));
     431          12 :             prop.nMode = entry->nMode;
     432          12 :             prop.mTime = static_cast<time_t>(entry->nMTime);
     433          12 :             prop.ETag = std::move(ETag);
     434             : 
     435             :             std::string osCachedFilename =
     436          36 :                 osBaseURL + "/" + CPLAWSURLEncode(osName, false);
     437             : #if DEBUG_VERBOSE
     438             :             CPLDebug(m_poFS->GetDebugKey(), "Cache %s",
     439             :                      osCachedFilename.c_str());
     440             : #endif
     441          12 :             m_poFS->SetCachedFileProp(osCachedFilename.c_str(), prop);
     442             :         }
     443             : 
     444          12 :         if (m_nMaxFiles > 0 && m_oIterWithinFilesystem.m_aoEntries.size() >
     445           0 :                                    static_cast<unsigned>(m_nMaxFiles))
     446             :         {
     447           0 :             break;
     448             :         }
     449             :     }
     450             : 
     451          11 :     return true;
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                         AnalysePathList()                            */
     456             : /************************************************************************/
     457             : 
     458           5 : bool VSIDIRADLS::AnalyseFilesystemList(const std::string &osBaseURL,
     459             :                                        const char *pszJSON)
     460             : {
     461             : #if DEBUG_VERBOSE
     462             :     CPLDebug(m_poFS->GetDebugKey(), "%s", pszJSON);
     463             : #endif
     464             : 
     465          10 :     CPLJSONDocument oDoc;
     466           5 :     if (!oDoc.LoadMemory(pszJSON))
     467           0 :         return false;
     468             : 
     469          15 :     auto oPaths = oDoc.GetRoot().GetArray("filesystems");
     470           5 :     if (!oPaths.IsValid())
     471             :     {
     472           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find filesystems[]");
     473           0 :         return false;
     474             :     }
     475             : 
     476          10 :     for (const auto &oPath : oPaths)
     477             :     {
     478           5 :         m_oIterFromRoot.m_aoEntries.push_back(
     479          10 :             std::unique_ptr<VSIDIREntry>(new VSIDIREntry()));
     480           5 :         auto &entry = m_oIterFromRoot.m_aoEntries.back();
     481             : 
     482          10 :         const std::string osName(oPath.GetString("name"));
     483           5 :         entry->pszName = CPLStrdup(osName.c_str());
     484           5 :         entry->nSize = 0;
     485           5 :         entry->bSizeKnown = true;
     486           5 :         entry->nMode = S_IFDIR;
     487           5 :         entry->bModeKnown = true;
     488             : 
     489          10 :         std::string ETag = oPath.GetString("etag");
     490           5 :         if (!ETag.empty())
     491             :         {
     492           0 :             entry->papszExtra =
     493           0 :                 CSLSetNameValue(entry->papszExtra, "ETag", ETag.c_str());
     494             :         }
     495             : 
     496             :         const GIntBig nMTime =
     497           5 :             GetUnixTimeFromRFC822(oPath.GetString("lastModified").c_str());
     498           5 :         if (nMTime != GINTBIG_MIN)
     499             :         {
     500           0 :             entry->nMTime = nMTime;
     501           0 :             entry->bMTimeKnown = true;
     502             :         }
     503             : 
     504           5 :         if (m_bCacheEntries)
     505             :         {
     506          10 :             FileProp prop;
     507           5 :             prop.eExists = EXIST_YES;
     508           5 :             prop.bHasComputedFileSize = true;
     509           5 :             prop.fileSize = 0;
     510           5 :             prop.bIsDirectory = true;
     511           5 :             prop.mTime = static_cast<time_t>(entry->nMTime);
     512           5 :             prop.ETag = std::move(ETag);
     513             : 
     514             :             std::string osCachedFilename =
     515          10 :                 osBaseURL + CPLAWSURLEncode(osName, false);
     516             : #if DEBUG_VERBOSE
     517             :             CPLDebug(m_poFS->GetDebugKey(), "Cache %s",
     518             :                      osCachedFilename.c_str());
     519             : #endif
     520           5 :             m_poFS->SetCachedFileProp(osCachedFilename.c_str(), prop);
     521             :         }
     522             : 
     523           5 :         if (m_nMaxFiles > 0 && m_oIterFromRoot.m_aoEntries.size() >
     524           0 :                                    static_cast<unsigned>(m_nMaxFiles))
     525             :         {
     526           0 :             break;
     527             :         }
     528             :     }
     529             : 
     530           5 :     return true;
     531             : }
     532             : 
     533             : /************************************************************************/
     534             : /*                          IssueListDir()                              */
     535             : /************************************************************************/
     536             : 
     537          17 : bool VSIDIRADLS::IssueListDir()
     538             : {
     539          17 :     WriteFuncStruct sWriteFuncData;
     540             : 
     541             :     auto &oIter =
     542          17 :         !m_osFilesystem.empty() ? m_oIterWithinFilesystem : m_oIterFromRoot;
     543          34 :     const std::string l_osNextMarker(oIter.m_osNextMarker);
     544          17 :     clear();
     545             : 
     546          34 :     NetworkStatisticsFileSystem oContextFS(m_poFS->GetFSPrefix().c_str());
     547          34 :     NetworkStatisticsAction oContextAction("ListBucket");
     548             : 
     549          34 :     CPLString osMaxKeys = CPLGetConfigOption("AZURE_MAX_RESULTS", "");
     550          17 :     const int AZURE_SERVER_LIMIT_SINGLE_REQUEST = 5000;
     551          17 :     if (m_nMaxFiles > 0 && m_nMaxFiles < AZURE_SERVER_LIMIT_SINGLE_REQUEST &&
     552           0 :         (osMaxKeys.empty() || m_nMaxFiles < atoi(osMaxKeys)))
     553             :     {
     554           0 :         osMaxKeys.Printf("%d", m_nMaxFiles);
     555             :     }
     556             : 
     557             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     558          34 :         m_poFS->CreateHandleHelper(m_osFilesystem.c_str(), true));
     559          17 :     if (poHandleHelper == nullptr)
     560             :     {
     561           0 :         return false;
     562             :     }
     563             : 
     564          34 :     const std::string osBaseURL(poHandleHelper->GetURLNoKVP());
     565             : 
     566          17 :     CURL *hCurlHandle = curl_easy_init();
     567             : 
     568          17 :     if (!l_osNextMarker.empty())
     569           4 :         poHandleHelper->AddQueryParameter("continuation", l_osNextMarker);
     570          17 :     if (!osMaxKeys.empty())
     571           0 :         poHandleHelper->AddQueryParameter("maxresults", osMaxKeys);
     572          17 :     if (!m_osFilesystem.empty())
     573             :     {
     574          12 :         poHandleHelper->AddQueryParameter("resource", "filesystem");
     575          24 :         poHandleHelper->AddQueryParameter(
     576          12 :             "recursive", m_nRecurseDepth == 0 ? "false" : "true");
     577          12 :         if (!m_osObjectKey.empty())
     578           4 :             poHandleHelper->AddQueryParameter("directory", m_osObjectKey);
     579             :     }
     580             :     else
     581             :     {
     582           5 :         poHandleHelper->AddQueryParameter("resource", "account");
     583             :     }
     584             : 
     585          34 :     std::string osFilename("/vsiadls/");
     586          17 :     if (!m_osFilesystem.empty())
     587             :     {
     588          12 :         osFilename += m_osFilesystem;
     589          12 :         if (!m_osObjectKey.empty())
     590           4 :             osFilename += m_osObjectKey;
     591             :     }
     592             :     const CPLStringList aosHTTPOptions(
     593          34 :         CPLHTTPGetOptionsFromEnv(osFilename.c_str()));
     594             : 
     595          34 :     struct curl_slist *headers = VSICurlSetOptions(
     596          17 :         hCurlHandle, poHandleHelper->GetURL().c_str(), aosHTTPOptions.List());
     597          17 :     headers = VSICurlMergeHeaders(
     598          17 :         headers, poHandleHelper->GetCurlHeaders("GET", headers));
     599          17 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     600             : 
     601          17 :     CurlRequestHelper requestHelper;
     602          17 :     const long response_code = requestHelper.perform(
     603          17 :         hCurlHandle, headers, m_poFS, poHandleHelper.get());
     604             : 
     605          17 :     NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
     606             : 
     607          17 :     bool ret = false;
     608          17 :     if (response_code != 200)
     609             :     {
     610           1 :         CPLDebug(m_poFS->GetDebugKey(), "%s",
     611           1 :                  requestHelper.sWriteFuncData.pBuffer
     612             :                      ? requestHelper.sWriteFuncData.pBuffer
     613             :                      : "(null)");
     614             :     }
     615             :     else
     616             :     {
     617          16 :         if (!m_osFilesystem.empty())
     618             :         {
     619             :             // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/list
     620          11 :             ret = AnalysePathList(osBaseURL,
     621          11 :                                   requestHelper.sWriteFuncData.pBuffer);
     622             :         }
     623             :         else
     624             :         {
     625             :             // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/filesystem/list
     626           5 :             ret = AnalyseFilesystemList(osBaseURL,
     627           5 :                                         requestHelper.sWriteFuncData.pBuffer);
     628             :         }
     629             : 
     630             :         // Get continuation token for response headers
     631             :         oIter.m_osNextMarker =
     632          16 :             GetContinuationToken(requestHelper.sWriteFuncHeaderData.pBuffer);
     633             :     }
     634             : 
     635          17 :     curl_easy_cleanup(hCurlHandle);
     636          17 :     return ret;
     637             : }
     638             : 
     639             : /************************************************************************/
     640             : /*                           NextDirEntry()                             */
     641             : /************************************************************************/
     642             : 
     643          33 : const VSIDIREntry *VSIDIRADLS::NextDirEntry()
     644             : {
     645             :     while (true)
     646             :     {
     647             :         auto &oIter =
     648          33 :             !m_osFilesystem.empty() ? m_oIterWithinFilesystem : m_oIterFromRoot;
     649          33 :         if (oIter.m_nPos < static_cast<int>(oIter.m_aoEntries.size()))
     650             :         {
     651          17 :             auto &entry = oIter.m_aoEntries[oIter.m_nPos];
     652          17 :             oIter.m_nPos++;
     653          17 :             if (m_bRecursiveRequestFromAccountRoot)
     654             :             {
     655             :                 // If we just read an entry from the account root, it is a
     656             :                 // filesystem name, and we want the next iteration to read
     657             :                 // into it.
     658           6 :                 if (m_osFilesystem.empty())
     659             :                 {
     660           3 :                     m_osFilesystem = entry->pszName;
     661           3 :                     if (!IssueListDir())
     662             :                     {
     663           0 :                         return nullptr;
     664             :                     }
     665             :                 }
     666             :             }
     667          19 :             if (!m_osFilterPrefix.empty() &&
     668           2 :                 !STARTS_WITH(entry->pszName, m_osFilterPrefix.c_str()))
     669             :             {
     670           1 :                 continue;
     671             :             }
     672          16 :             return entry.get();
     673             :         }
     674          16 :         if (oIter.m_osNextMarker.empty())
     675             :         {
     676          12 :             if (m_bRecursiveRequestFromAccountRoot)
     677             :             {
     678             :                 // If we have no more entries at the filesystem level, go back
     679             :                 // to the root level.
     680           4 :                 if (!m_osFilesystem.empty())
     681             :                 {
     682           3 :                     m_osFilesystem.clear();
     683           3 :                     continue;
     684             :                 }
     685             :             }
     686           9 :             return nullptr;
     687             :         }
     688           4 :         if (!IssueListDir())
     689             :         {
     690           0 :             return nullptr;
     691             :         }
     692           8 :     }
     693             : }
     694             : 
     695             : /************************************************************************/
     696             : /*                          VSIADLSHandle                              */
     697             : /************************************************************************/
     698             : 
     699             : class VSIADLSHandle final : public VSICurlHandle
     700             : {
     701             :     CPL_DISALLOW_COPY_ASSIGN(VSIADLSHandle)
     702             : 
     703             :     std::unique_ptr<VSIAzureBlobHandleHelper> m_poHandleHelper{};
     704             : 
     705             :   protected:
     706             :     virtual struct curl_slist *
     707             :     GetCurlHeaders(const std::string &osVerb,
     708             :                    const struct curl_slist *psExistingHeaders) override;
     709             : 
     710             :   public:
     711             :     VSIADLSHandle(VSIADLSFSHandler *poFS, const char *pszFilename,
     712             :                   VSIAzureBlobHandleHelper *poHandleHelper);
     713             : };
     714             : 
     715             : /************************************************************************/
     716             : /*                          CreateFileHandle()                          */
     717             : /************************************************************************/
     718             : 
     719          25 : VSICurlHandle *VSIADLSFSHandler::CreateFileHandle(const char *pszFilename)
     720             : {
     721             :     VSIAzureBlobHandleHelper *poHandleHelper =
     722          50 :         VSIAzureBlobHandleHelper::BuildFromURI(
     723          75 :             pszFilename + GetFSPrefix().size(), GetFSPrefix().c_str());
     724          25 :     if (poHandleHelper == nullptr)
     725           0 :         return nullptr;
     726          25 :     return new VSIADLSHandle(this, pszFilename, poHandleHelper);
     727             : }
     728             : 
     729             : /************************************************************************/
     730             : /*                          CreateWriteHandle()                         */
     731             : /************************************************************************/
     732             : 
     733             : VSIVirtualHandleUniquePtr
     734           7 : VSIADLSFSHandler::CreateWriteHandle(const char *pszFilename,
     735             :                                     CSLConstList papszOptions)
     736             : {
     737             :     VSIAzureBlobHandleHelper *poHandleHelper =
     738          14 :         VSIAzureBlobHandleHelper::BuildFromURI(
     739          21 :             pszFilename + GetFSPrefix().size(), GetFSPrefix().c_str());
     740           7 :     if (poHandleHelper == nullptr)
     741           0 :         return nullptr;
     742             :     auto poHandle =
     743          14 :         std::make_unique<VSIADLSWriteHandle>(this, pszFilename, poHandleHelper);
     744           7 :     if (!poHandle->CreateFile(papszOptions))
     745             :     {
     746           1 :         return nullptr;
     747             :     }
     748           6 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     749             : }
     750             : 
     751             : /************************************************************************/
     752             : /*                                Stat()                                */
     753             : /************************************************************************/
     754             : 
     755          27 : int VSIADLSFSHandler::Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     756             :                            int nFlags)
     757             : {
     758          27 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     759           0 :         return -1;
     760             : 
     761          27 :     if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
     762           2 :         return VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf,
     763           2 :                                                   nFlags);
     764             : 
     765          75 :     const std::string osFilenameWithoutSlash(RemoveTrailingSlash(pszFilename));
     766             : 
     767             :     // Stat("/vsiadls/") ?
     768          25 :     if (osFilenameWithoutSlash + "/" == GetFSPrefix())
     769             :     {
     770             :         // List file systems (stop at the first one), to confirm that the
     771             :         // account is correct
     772           0 :         bool bGotFileList = false;
     773           0 :         CSLDestroy(GetFileList(GetFSPrefix().c_str(), 1, false, &bGotFileList));
     774           0 :         if (bGotFileList)
     775             :         {
     776           0 :             memset(pStatBuf, 0, sizeof(VSIStatBufL));
     777           0 :             pStatBuf->st_mode = S_IFDIR;
     778           0 :             return 0;
     779             :         }
     780           0 :         return -1;
     781             :     }
     782             : 
     783          50 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
     784             : 
     785             :     // Stat("/vsiadls/filesystem") ?
     786          75 :     if (osFilenameWithoutSlash.size() > GetFSPrefix().size() &&
     787          50 :         osFilenameWithoutSlash.substr(GetFSPrefix().size()).find('/') ==
     788             :             std::string::npos)
     789             :     {
     790             :         // Use
     791             :         // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/filesystem/getproperties
     792             : 
     793           6 :         NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     794           6 :         NetworkStatisticsAction oContextAction("GetProperties");
     795             : 
     796             :         const std::string osFilesystem(
     797           6 :             osFilenameWithoutSlash.substr(GetFSPrefix().size()));
     798             :         auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     799           6 :             CreateHandleHelper(osFilesystem.c_str(), true));
     800           3 :         if (poHandleHelper == nullptr)
     801             :         {
     802           0 :             return -1;
     803             :         }
     804             : 
     805           3 :         CURL *hCurlHandle = curl_easy_init();
     806             : 
     807           3 :         poHandleHelper->AddQueryParameter("resource", "filesystem");
     808             : 
     809             :         struct curl_slist *headers =
     810           3 :             VSICurlSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
     811             :                               aosHTTPOptions.List());
     812             : 
     813           3 :         headers = VSICurlMergeHeaders(
     814           3 :             headers, poHandleHelper->GetCurlHeaders("HEAD", headers));
     815           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     816             : 
     817           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 1);
     818             : 
     819           6 :         CurlRequestHelper requestHelper;
     820           3 :         const long response_code = requestHelper.perform(
     821             :             hCurlHandle, headers, this, poHandleHelper.get());
     822             : 
     823           3 :         NetworkStatisticsLogger::LogHEAD();
     824             : 
     825           3 :         if (response_code != 200 ||
     826           3 :             requestHelper.sWriteFuncHeaderData.pBuffer == nullptr)
     827             :         {
     828           0 :             curl_easy_cleanup(hCurlHandle);
     829           0 :             return -1;
     830             :         }
     831             : 
     832           3 :         memset(pStatBuf, 0, sizeof(VSIStatBufL));
     833           3 :         pStatBuf->st_mode = S_IFDIR;
     834             : 
     835           3 :         const char *pszLastModified = strstr(
     836             :             requestHelper.sWriteFuncHeaderData.pBuffer, "Last-Modified: ");
     837           3 :         if (pszLastModified)
     838             :         {
     839           0 :             pszLastModified += strlen("Last-Modified: ");
     840           0 :             const char *pszEOL = strstr(pszLastModified, "\r\n");
     841           0 :             if (pszEOL)
     842             :             {
     843           0 :                 std::string osLastModified;
     844             :                 osLastModified.assign(pszLastModified,
     845           0 :                                       pszEOL - pszLastModified);
     846             : 
     847             :                 const GIntBig nMTime =
     848           0 :                     GetUnixTimeFromRFC822(osLastModified.c_str());
     849           0 :                 if (nMTime != GINTBIG_MIN)
     850             :                 {
     851           0 :                     pStatBuf->st_mtime = static_cast<time_t>(nMTime);
     852             :                 }
     853             :             }
     854             :         }
     855             : 
     856           3 :         curl_easy_cleanup(hCurlHandle);
     857             : 
     858           3 :         return 0;
     859             :     }
     860             : 
     861          22 :     return VSICurlFilesystemHandlerBase::Stat(osFilenameWithoutSlash.c_str(),
     862          22 :                                               pStatBuf, nFlags);
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                          GetFileMetadata()                           */
     867             : /************************************************************************/
     868             : 
     869           4 : char **VSIADLSFSHandler::GetFileMetadata(const char *pszFilename,
     870             :                                          const char *pszDomain,
     871             :                                          CSLConstList papszOptions)
     872             : {
     873           4 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     874           0 :         return nullptr;
     875             : 
     876           4 :     if (pszDomain == nullptr ||
     877           4 :         (!EQUAL(pszDomain, "STATUS") && !EQUAL(pszDomain, "ACL")))
     878             :     {
     879           1 :         return VSICurlFilesystemHandlerBase::GetFileMetadata(
     880           1 :             pszFilename, pszDomain, papszOptions);
     881             :     }
     882             : 
     883             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     884           6 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), false));
     885           3 :     if (poHandleHelper == nullptr)
     886             :     {
     887           0 :         return nullptr;
     888             :     }
     889             : 
     890           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     891           6 :     NetworkStatisticsAction oContextAction("GetFileMetadata");
     892             : 
     893             :     bool bRetry;
     894             :     // coverity[tainted_data]
     895           3 :     double dfRetryDelay = CPLAtof(
     896             :         VSIGetPathSpecificOption(pszFilename, "GDAL_HTTP_RETRY_DELAY",
     897             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
     898             :     const int nMaxRetry =
     899           3 :         atoi(VSIGetPathSpecificOption(pszFilename, "GDAL_HTTP_MAX_RETRY",
     900             :                                       CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
     901           3 :     int nRetryCount = 0;
     902           3 :     bool bError = true;
     903             : 
     904           6 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
     905             : 
     906           3 :     CPLStringList aosMetadata;
     907           3 :     do
     908             :     {
     909           3 :         bRetry = false;
     910           3 :         CURL *hCurlHandle = curl_easy_init();
     911           3 :         poHandleHelper->AddQueryParameter("action", EQUAL(pszDomain, "STATUS")
     912             :                                                         ? "getStatus"
     913             :                                                         : "getAccessControl");
     914             : 
     915             :         struct curl_slist *headers =
     916           3 :             VSICurlSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
     917             :                               aosHTTPOptions.List());
     918             : 
     919           3 :         headers = VSICurlMergeHeaders(
     920           3 :             headers, poHandleHelper->GetCurlHeaders("HEAD", headers));
     921           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     922             : 
     923           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 1);
     924             : 
     925           6 :         CurlRequestHelper requestHelper;
     926           3 :         const long response_code = requestHelper.perform(
     927             :             hCurlHandle, headers, this, poHandleHelper.get());
     928             : 
     929           3 :         NetworkStatisticsLogger::LogHEAD();
     930             : 
     931           3 :         if (response_code != 200 ||
     932           2 :             requestHelper.sWriteFuncHeaderData.pBuffer == nullptr)
     933             :         {
     934             :             // Look if we should attempt a retry
     935           2 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
     936             :                 static_cast<int>(response_code), dfRetryDelay,
     937           1 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
     938             :                 requestHelper.szCurlErrBuf);
     939           1 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
     940             :             {
     941           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     942             :                          "HTTP error code: %d - %s. "
     943             :                          "Retrying again in %.1f secs",
     944             :                          static_cast<int>(response_code),
     945           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
     946           0 :                 CPLSleep(dfRetryDelay);
     947           0 :                 dfRetryDelay = dfNewRetryDelay;
     948           0 :                 nRetryCount++;
     949           0 :                 bRetry = true;
     950             :             }
     951             :             else
     952             :             {
     953           1 :                 CPLDebug(GetDebugKey(), "GetFileMetadata failed on %s: %s",
     954             :                          pszFilename,
     955           1 :                          requestHelper.sWriteFuncData.pBuffer
     956             :                              ? requestHelper.sWriteFuncData.pBuffer
     957             :                              : "(null)");
     958           1 :             }
     959             :         }
     960             :         else
     961             :         {
     962           4 :             char **papszHeaders = CSLTokenizeString2(
     963           2 :                 requestHelper.sWriteFuncHeaderData.pBuffer, "\r\n", 0);
     964          12 :             for (int i = 0; papszHeaders[i]; ++i)
     965             :             {
     966          10 :                 char *pszKey = nullptr;
     967             :                 const char *pszValue =
     968          10 :                     CPLParseNameValue(papszHeaders[i], &pszKey);
     969          10 :                 if (pszKey && pszValue && !EQUAL(pszKey, "Server") &&
     970           6 :                     !EQUAL(pszKey, "Date"))
     971             :                 {
     972           4 :                     aosMetadata.SetNameValue(pszKey, pszValue);
     973             :                 }
     974          10 :                 CPLFree(pszKey);
     975             :             }
     976           2 :             CSLDestroy(papszHeaders);
     977           2 :             bError = false;
     978             :         }
     979             : 
     980           3 :         curl_easy_cleanup(hCurlHandle);
     981             :     } while (bRetry);
     982           3 :     return bError ? nullptr : CSLDuplicate(aosMetadata.List());
     983             : }
     984             : 
     985             : /************************************************************************/
     986             : /*                          SetFileMetadata()                           */
     987             : /************************************************************************/
     988             : 
     989           4 : bool VSIADLSFSHandler::SetFileMetadata(const char *pszFilename,
     990             :                                        CSLConstList papszMetadata,
     991             :                                        const char *pszDomain,
     992             :                                        CSLConstList papszOptions)
     993             : {
     994           4 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     995           0 :         return false;
     996             : 
     997           4 :     if (pszDomain == nullptr ||
     998           4 :         !(EQUAL(pszDomain, "PROPERTIES") || EQUAL(pszDomain, "ACL")))
     999             :     {
    1000           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1001             :                  "Only PROPERTIES and ACL domain are supported");
    1002           0 :         return false;
    1003             :     }
    1004             : 
    1005             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1006           8 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), false));
    1007           4 :     if (poHandleHelper == nullptr)
    1008             :     {
    1009           0 :         return false;
    1010             :     }
    1011             : 
    1012             :     const bool bRecursive =
    1013           4 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "RECURSIVE", "FALSE"));
    1014           4 :     const char *pszMode = CSLFetchNameValue(papszOptions, "MODE");
    1015           4 :     if (!EQUAL(pszDomain, "PROPERTIES") && bRecursive && pszMode == nullptr)
    1016             :     {
    1017           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1018             :                  "For setAccessControlRecursive, the MODE option should be set "
    1019             :                  "to: 'set', 'modify' or 'remove'");
    1020           0 :         return false;
    1021             :     }
    1022             : 
    1023           8 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1024           8 :     NetworkStatisticsAction oContextAction("SetFileMetadata");
    1025             : 
    1026             :     bool bRetry;
    1027             :     // coverity[tainted_data]
    1028           4 :     double dfRetryDelay = CPLAtof(
    1029             :         VSIGetPathSpecificOption(pszFilename, "GDAL_HTTP_RETRY_DELAY",
    1030             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1031             :     const int nMaxRetry =
    1032           4 :         atoi(VSIGetPathSpecificOption(pszFilename, "GDAL_HTTP_MAX_RETRY",
    1033             :                                       CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1034           4 :     int nRetryCount = 0;
    1035             : 
    1036           4 :     bool bRet = false;
    1037             : 
    1038           4 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
    1039             : 
    1040           4 :     do
    1041             :     {
    1042           4 :         bRetry = false;
    1043           4 :         CURL *hCurlHandle = curl_easy_init();
    1044           8 :         poHandleHelper->AddQueryParameter(
    1045           4 :             "action", EQUAL(pszDomain, "PROPERTIES") ? "setProperties"
    1046           2 :                       : bRecursive ? "setAccessControlRecursive"
    1047             :                                    : "setAccessControl");
    1048           4 :         if (pszMode)
    1049             :         {
    1050           2 :             poHandleHelper->AddQueryParameter("mode",
    1051           2 :                                               CPLString(pszMode).tolower());
    1052             :         }
    1053           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PATCH");
    1054             : 
    1055             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1056           4 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1057             :                               aosHTTPOptions.List()));
    1058             : 
    1059           8 :         CPLStringList aosList;
    1060           8 :         for (CSLConstList papszIter = papszMetadata; papszIter && *papszIter;
    1061             :              ++papszIter)
    1062             :         {
    1063           4 :             char *pszKey = nullptr;
    1064           4 :             const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    1065           4 :             if (pszKey && pszValue)
    1066             :             {
    1067           4 :                 if ((EQUAL(pszDomain, "PROPERTIES") &&
    1068           2 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1069           2 :                       EQUAL(pszKey, "x-ms-cache-control") ||
    1070           2 :                       EQUAL(pszKey, "x-ms-content-type") ||
    1071           2 :                       EQUAL(pszKey, "x-ms-content-disposition") ||
    1072           2 :                       EQUAL(pszKey, "x-ms-content-encoding") ||
    1073           2 :                       EQUAL(pszKey, "x-ms-content-language") ||
    1074           2 :                       EQUAL(pszKey, "x-ms-content-md5") ||
    1075           2 :                       EQUAL(pszKey, "x-ms-properties") ||
    1076           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1077           0 :                       STARTS_WITH_CI(pszKey, "If-"))) ||
    1078           2 :                     (!EQUAL(pszDomain, "PROPERTIES") && !bRecursive &&
    1079           1 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1080           1 :                       EQUAL(pszKey, "x-ms-owner") ||
    1081           1 :                       EQUAL(pszKey, "x-ms-group") ||
    1082           1 :                       EQUAL(pszKey, "x-ms-permissions") ||
    1083           1 :                       EQUAL(pszKey, "x-ms-acl") ||
    1084           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1085           0 :                       STARTS_WITH_CI(pszKey, "If-"))) ||
    1086           1 :                     (!EQUAL(pszDomain, "PROPERTIES") && bRecursive &&
    1087           1 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1088           1 :                       EQUAL(pszKey, "x-ms-acl") ||
    1089           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1090           0 :                       STARTS_WITH_CI(pszKey, "If-"))))
    1091             :                 {
    1092             :                     const char *pszHeader =
    1093           4 :                         CPLSPrintf("%s: %s", pszKey, pszValue);
    1094           4 :                     aosList.AddString(pszHeader);
    1095           4 :                     headers = curl_slist_append(headers, pszHeader);
    1096             :                 }
    1097             :                 else
    1098             :                 {
    1099           0 :                     CPLDebug(GetDebugKey(), "Ignorizing metadata item %s",
    1100             :                              *papszIter);
    1101             :                 }
    1102             :             }
    1103           4 :             CPLFree(pszKey);
    1104             :         }
    1105             : 
    1106           4 :         headers = VSICurlMergeHeaders(
    1107           4 :             headers, poHandleHelper->GetCurlHeaders("PATCH", headers));
    1108           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1109             : 
    1110           4 :         NetworkStatisticsLogger::LogPUT(0);
    1111             : 
    1112           8 :         CurlRequestHelper requestHelper;
    1113           4 :         const long response_code = requestHelper.perform(
    1114             :             hCurlHandle, headers, this, poHandleHelper.get());
    1115             : 
    1116           4 :         if (response_code != 200 && response_code != 202)
    1117             :         {
    1118             :             // Look if we should attempt a retry
    1119           2 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1120             :                 static_cast<int>(response_code), dfRetryDelay,
    1121           1 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1122             :                 requestHelper.szCurlErrBuf);
    1123           1 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1124             :             {
    1125           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1126             :                          "HTTP error code: %d - %s. "
    1127             :                          "Retrying again in %.1f secs",
    1128             :                          static_cast<int>(response_code),
    1129           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
    1130           0 :                 CPLSleep(dfRetryDelay);
    1131           0 :                 dfRetryDelay = dfNewRetryDelay;
    1132           0 :                 nRetryCount++;
    1133           0 :                 bRetry = true;
    1134             :             }
    1135             :             else
    1136             :             {
    1137           1 :                 CPLDebug(GetDebugKey(), "SetFileMetadata on %s failed: %s",
    1138             :                          pszFilename,
    1139           1 :                          requestHelper.sWriteFuncData.pBuffer
    1140             :                              ? requestHelper.sWriteFuncData.pBuffer
    1141             :                              : "(null)");
    1142           1 :             }
    1143             :         }
    1144             :         else
    1145             :         {
    1146           3 :             bRet = true;
    1147             :         }
    1148             : 
    1149           4 :         curl_easy_cleanup(hCurlHandle);
    1150             :     } while (bRetry);
    1151           4 :     return bRet;
    1152             : }
    1153             : 
    1154             : /************************************************************************/
    1155             : /*                       VSIADLSWriteHandle()                          */
    1156             : /************************************************************************/
    1157             : 
    1158           7 : VSIADLSWriteHandle::VSIADLSWriteHandle(VSIADLSFSHandler *poFS,
    1159             :                                        const char *pszFilename,
    1160           7 :                                        VSIAzureBlobHandleHelper *poHandleHelper)
    1161           7 :     : VSIAppendWriteHandle(poFS, poFS->GetFSPrefix().c_str(), pszFilename,
    1162             :                            GetAzureBufferSize()),
    1163          14 :       m_poHandleHelper(poHandleHelper)
    1164             : {
    1165           7 : }
    1166             : 
    1167             : /************************************************************************/
    1168             : /*                      ~VSIADLSWriteHandle()                          */
    1169             : /************************************************************************/
    1170             : 
    1171          14 : VSIADLSWriteHandle::~VSIADLSWriteHandle()
    1172             : {
    1173           7 :     Close();
    1174          14 : }
    1175             : 
    1176             : /************************************************************************/
    1177             : /*                    InvalidateParentDirectory()                       */
    1178             : /************************************************************************/
    1179             : 
    1180           6 : void VSIADLSWriteHandle::InvalidateParentDirectory()
    1181             : {
    1182           6 :     m_poFS->InvalidateCachedData(m_poHandleHelper->GetURLNoKVP().c_str());
    1183             : 
    1184          12 :     const std::string osFilenameWithoutSlash(RemoveTrailingSlash(m_osFilename));
    1185           6 :     m_poFS->InvalidateDirContent(CPLGetDirname(osFilenameWithoutSlash.c_str()));
    1186           6 : }
    1187             : 
    1188             : /************************************************************************/
    1189             : /*                          CreateFile()                                */
    1190             : /************************************************************************/
    1191             : 
    1192           7 : bool VSIADLSWriteHandle::CreateFile(CSLConstList papszOptions)
    1193             : {
    1194           7 :     m_bCreated =
    1195           7 :         SendInternal(VSIADLSFSHandler::Event::CREATE_FILE, papszOptions);
    1196           7 :     return m_bCreated;
    1197             : }
    1198             : 
    1199             : /************************************************************************/
    1200             : /*                             Send()                                   */
    1201             : /************************************************************************/
    1202             : 
    1203           9 : bool VSIADLSWriteHandle::Send(bool bIsLastBlock)
    1204             : {
    1205           9 :     if (!m_bCreated)
    1206           1 :         return false;
    1207             :     // If we have a non-empty buffer, append it
    1208          15 :     if (m_nBufferOff != 0 &&
    1209           7 :         !SendInternal(VSIADLSFSHandler::Event::APPEND_DATA, nullptr))
    1210           1 :         return false;
    1211             :     // If we are the last block, send the flush event
    1212           7 :     if (bIsLastBlock && !SendInternal(VSIADLSFSHandler::Event::FLUSH, nullptr))
    1213           1 :         return false;
    1214             : 
    1215           6 :     InvalidateParentDirectory();
    1216             : 
    1217           6 :     return true;
    1218             : }
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                          SendInternal()                              */
    1222             : /************************************************************************/
    1223             : 
    1224          19 : bool VSIADLSWriteHandle::SendInternal(VSIADLSFSHandler::Event event,
    1225             :                                       CSLConstList papszOptions)
    1226             : {
    1227             :     // coverity[tainted_data]
    1228          19 :     double dfRetryDelay = CPLAtof(
    1229             :         VSIGetPathSpecificOption(m_osFilename.c_str(), "GDAL_HTTP_RETRY_DELAY",
    1230             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1231          19 :     const int nMaxRetry = atoi(
    1232             :         VSIGetPathSpecificOption(m_osFilename.c_str(), "GDAL_HTTP_MAX_RETRY",
    1233             :                                  CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1234             : 
    1235          38 :     return cpl::down_cast<VSIADLSFSHandler *>(m_poFS)->UploadFile(
    1236          19 :         m_osFilename, event,
    1237             :         event == VSIADLSFSHandler::Event::CREATE_FILE ? 0
    1238             :         : event == VSIADLSFSHandler::Event::APPEND_DATA
    1239          12 :             ? m_nCurOffset - m_nBufferOff
    1240             :             : m_nCurOffset,
    1241          19 :         m_pabyBuffer, m_nBufferOff, m_poHandleHelper.get(), nMaxRetry,
    1242          19 :         dfRetryDelay, papszOptions);
    1243             : }
    1244             : 
    1245             : /************************************************************************/
    1246             : /*                            ClearCache()                              */
    1247             : /************************************************************************/
    1248             : 
    1249         246 : void VSIADLSFSHandler::ClearCache()
    1250             : {
    1251         246 :     IVSIS3LikeFSHandler::ClearCache();
    1252             : 
    1253         246 :     VSIAzureBlobHandleHelper::ClearCache();
    1254         246 : }
    1255             : 
    1256             : /************************************************************************/
    1257             : /*                          GetURLFromFilename()                        */
    1258             : /************************************************************************/
    1259             : 
    1260          17 : std::string VSIADLSFSHandler::GetURLFromFilename(const std::string &osFilename)
    1261             : {
    1262             :     std::string osFilenameWithoutPrefix =
    1263          34 :         osFilename.substr(GetFSPrefix().size());
    1264             :     VSIAzureBlobHandleHelper *poHandleHelper =
    1265          17 :         VSIAzureBlobHandleHelper::BuildFromURI(osFilenameWithoutPrefix.c_str(),
    1266          34 :                                                GetFSPrefix().c_str());
    1267          17 :     if (poHandleHelper == nullptr)
    1268           0 :         return std::string();
    1269          34 :     std::string osURL(poHandleHelper->GetURLNoKVP());
    1270          17 :     delete poHandleHelper;
    1271          17 :     return osURL;
    1272             : }
    1273             : 
    1274             : /************************************************************************/
    1275             : /*                          CreateHandleHelper()                        */
    1276             : /************************************************************************/
    1277             : 
    1278          37 : IVSIS3LikeHandleHelper *VSIADLSFSHandler::CreateHandleHelper(const char *pszURI,
    1279             :                                                              bool)
    1280             : {
    1281          37 :     return VSIAzureBlobHandleHelper::BuildFromURI(pszURI,
    1282          74 :                                                   GetFSPrefix().c_str());
    1283             : }
    1284             : 
    1285             : /************************************************************************/
    1286             : /*                               Rename()                               */
    1287             : /************************************************************************/
    1288             : 
    1289           1 : int VSIADLSFSHandler::Rename(const char *oldpath, const char *newpath)
    1290             : {
    1291           1 :     if (!STARTS_WITH_CI(oldpath, GetFSPrefix().c_str()))
    1292           0 :         return -1;
    1293           1 :     if (!STARTS_WITH_CI(newpath, GetFSPrefix().c_str()))
    1294           0 :         return -1;
    1295             : 
    1296           2 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1297           2 :     NetworkStatisticsAction oContextAction("Rename");
    1298             : 
    1299             :     VSIStatBufL sStat;
    1300           1 :     if (VSIStatL(oldpath, &sStat) != 0)
    1301             :     {
    1302           0 :         CPLDebug(GetDebugKey(), "%s is not a object", oldpath);
    1303           0 :         errno = ENOENT;
    1304           0 :         return -1;
    1305             :     }
    1306             : 
    1307             :     // POSIX says renaming on the same file is OK
    1308           1 :     if (strcmp(oldpath, newpath) == 0)
    1309           0 :         return 0;
    1310             : 
    1311             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1312           2 :         CreateHandleHelper(newpath + GetFSPrefix().size(), false));
    1313           1 :     if (poHandleHelper == nullptr)
    1314             :     {
    1315           0 :         return -1;
    1316             :     }
    1317             : 
    1318           2 :     std::string osContinuation;
    1319           1 :     int nRet = 0;
    1320             :     bool bRetry;
    1321             : 
    1322             :     // coverity[tainted_data]
    1323           1 :     double dfRetryDelay = CPLAtof(
    1324             :         VSIGetPathSpecificOption(oldpath, "GDAL_HTTP_RETRY_DELAY",
    1325             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1326           1 :     const int nMaxRetry = atoi(VSIGetPathSpecificOption(
    1327             :         oldpath, "GDAL_HTTP_MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1328           1 :     int nRetryCount = 0;
    1329             : 
    1330           1 :     InvalidateCachedData(GetURLFromFilename(oldpath).c_str());
    1331           1 :     InvalidateCachedData(GetURLFromFilename(newpath).c_str());
    1332           1 :     InvalidateDirContent(CPLGetDirname(oldpath));
    1333             : 
    1334           1 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(oldpath));
    1335             : 
    1336           1 :     do
    1337             :     {
    1338           1 :         bRetry = false;
    1339             : 
    1340           1 :         CURL *hCurlHandle = curl_easy_init();
    1341           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1342             : 
    1343           1 :         poHandleHelper->ResetQueryParameters();
    1344           1 :         if (!osContinuation.empty())
    1345           0 :             poHandleHelper->AddQueryParameter("continuation", osContinuation);
    1346             : 
    1347             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1348           1 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1349             :                               aosHTTPOptions.List()));
    1350           1 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1351           2 :         std::string osRenameSource("x-ms-rename-source: /");
    1352             :         osRenameSource +=
    1353           1 :             CPLAWSURLEncode(oldpath + GetFSPrefix().size(), false);
    1354           1 :         headers = curl_slist_append(headers, osRenameSource.c_str());
    1355           1 :         headers = VSICurlMergeHeaders(
    1356           1 :             headers, poHandleHelper->GetCurlHeaders("PUT", headers));
    1357           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1358             : 
    1359           2 :         CurlRequestHelper requestHelper;
    1360           1 :         const long response_code = requestHelper.perform(
    1361             :             hCurlHandle, headers, this, poHandleHelper.get());
    1362             : 
    1363           1 :         NetworkStatisticsLogger::LogPUT(0);
    1364             : 
    1365           1 :         if (response_code != 201)
    1366             :         {
    1367             :             // Look if we should attempt a retry
    1368           0 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1369             :                 static_cast<int>(response_code), dfRetryDelay,
    1370           0 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1371             :                 requestHelper.szCurlErrBuf);
    1372           0 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1373             :             {
    1374           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1375             :                          "HTTP error code: %d - %s. "
    1376             :                          "Retrying again in %.1f secs",
    1377             :                          static_cast<int>(response_code),
    1378           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
    1379           0 :                 CPLSleep(dfRetryDelay);
    1380           0 :                 dfRetryDelay = dfNewRetryDelay;
    1381           0 :                 nRetryCount++;
    1382           0 :                 bRetry = true;
    1383             :             }
    1384             :             else
    1385             :             {
    1386           0 :                 CPLDebug(GetDebugKey(), "Renaming of %s failed: %s", oldpath,
    1387           0 :                          requestHelper.sWriteFuncData.pBuffer
    1388             :                              ? requestHelper.sWriteFuncData.pBuffer
    1389             :                              : "(null)");
    1390           0 :                 nRet = -1;
    1391             :             }
    1392             :         }
    1393             :         else
    1394             :         {
    1395             :             // Get continuation token for response headers
    1396           1 :             osContinuation = GetContinuationToken(
    1397           1 :                 requestHelper.sWriteFuncHeaderData.pBuffer);
    1398           1 :             if (!osContinuation.empty())
    1399             :             {
    1400           0 :                 nRetryCount = 0;
    1401           0 :                 bRetry = true;
    1402             :             }
    1403             :         }
    1404             : 
    1405           1 :         curl_easy_cleanup(hCurlHandle);
    1406             :     } while (bRetry);
    1407             : 
    1408           1 :     return nRet;
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                               Unlink()                               */
    1413             : /************************************************************************/
    1414             : 
    1415           2 : int VSIADLSFSHandler::Unlink(const char *pszFilename)
    1416             : {
    1417           2 :     return IVSIS3LikeFSHandler::Unlink(pszFilename);
    1418             : }
    1419             : 
    1420             : /************************************************************************/
    1421             : /*                               Mkdir()                                */
    1422             : /************************************************************************/
    1423             : 
    1424           3 : int VSIADLSFSHandler::MkdirInternal(const char *pszDirname, long nMode,
    1425             :                                     bool bDoStatCheck)
    1426             : {
    1427           3 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1428           1 :         return -1;
    1429             : 
    1430           4 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1431           4 :     NetworkStatisticsAction oContextAction("Mkdir");
    1432             : 
    1433           4 :     const std::string osDirname(pszDirname);
    1434             : 
    1435           2 :     if (bDoStatCheck)
    1436             :     {
    1437             :         VSIStatBufL sStat;
    1438           2 :         if (VSIStatL(osDirname.c_str(), &sStat) == 0)
    1439             :         {
    1440           1 :             CPLDebug(GetDebugKey(), "Directory or file %s already exists",
    1441             :                      osDirname.c_str());
    1442           1 :             errno = EEXIST;
    1443           1 :             return -1;
    1444             :         }
    1445             :     }
    1446             : 
    1447           2 :     const std::string osDirnameWithoutEndSlash(RemoveTrailingSlash(osDirname));
    1448             :     auto poHandleHelper =
    1449             :         std::unique_ptr<IVSIS3LikeHandleHelper>(CreateHandleHelper(
    1450           2 :             osDirnameWithoutEndSlash.c_str() + GetFSPrefix().size(), false));
    1451           1 :     if (poHandleHelper == nullptr)
    1452             :     {
    1453           0 :         return -1;
    1454             :     }
    1455             : 
    1456           1 :     InvalidateCachedData(GetURLFromFilename(osDirname.c_str()).c_str());
    1457           1 :     InvalidateCachedData(
    1458           2 :         GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str());
    1459           1 :     InvalidateDirContent(CPLGetDirname(osDirnameWithoutEndSlash.c_str()));
    1460             : 
    1461           1 :     int nRet = 0;
    1462             : 
    1463             :     bool bRetry;
    1464             : 
    1465             :     // coverity[tainted_data]
    1466           1 :     double dfRetryDelay = CPLAtof(
    1467             :         VSIGetPathSpecificOption(pszDirname, "GDAL_HTTP_RETRY_DELAY",
    1468             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1469             :     const int nMaxRetry =
    1470           1 :         atoi(VSIGetPathSpecificOption(pszDirname, "GDAL_HTTP_MAX_RETRY",
    1471             :                                       CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1472           1 :     int nRetryCount = 0;
    1473             : 
    1474           1 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
    1475             : 
    1476           1 :     do
    1477             :     {
    1478           1 :         bRetry = false;
    1479           1 :         CURL *hCurlHandle = curl_easy_init();
    1480           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1481             : 
    1482           1 :         poHandleHelper->ResetQueryParameters();
    1483           2 :         poHandleHelper->AddQueryParameter(
    1484           1 :             "resource", osDirnameWithoutEndSlash.find(
    1485           2 :                             '/', GetFSPrefix().size()) == std::string::npos
    1486             :                             ? "filesystem"
    1487             :                             : "directory");
    1488             : 
    1489             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1490           1 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1491             :                               aosHTTPOptions.List()));
    1492           1 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1493           2 :         CPLString osPermissions;  // keep in this scope
    1494           1 :         if ((nMode & 0777) != 0)
    1495             :         {
    1496             :             osPermissions.Printf("x-ms-permissions: 0%03o",
    1497           0 :                                  static_cast<int>(nMode));
    1498           0 :             headers = curl_slist_append(headers, osPermissions.c_str());
    1499             :         }
    1500           1 :         if (bDoStatCheck)
    1501             :         {
    1502           1 :             headers = curl_slist_append(headers, "If-None-Match: \"*\"");
    1503             :         }
    1504             : 
    1505           1 :         headers = VSICurlMergeHeaders(
    1506           1 :             headers, poHandleHelper->GetCurlHeaders("PUT", headers));
    1507           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1508             : 
    1509           2 :         CurlRequestHelper requestHelper;
    1510           1 :         const long response_code = requestHelper.perform(
    1511             :             hCurlHandle, headers, this, poHandleHelper.get());
    1512             : 
    1513           1 :         NetworkStatisticsLogger::LogPUT(0);
    1514             : 
    1515           1 :         if (response_code != 201)
    1516             :         {
    1517             :             // Look if we should attempt a retry
    1518           0 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1519             :                 static_cast<int>(response_code), dfRetryDelay,
    1520           0 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1521             :                 requestHelper.szCurlErrBuf);
    1522           0 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1523             :             {
    1524           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1525             :                          "HTTP error code: %d - %s. "
    1526             :                          "Retrying again in %.1f secs",
    1527             :                          static_cast<int>(response_code),
    1528           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
    1529           0 :                 CPLSleep(dfRetryDelay);
    1530           0 :                 dfRetryDelay = dfNewRetryDelay;
    1531           0 :                 nRetryCount++;
    1532           0 :                 bRetry = true;
    1533             :             }
    1534             :             else
    1535             :             {
    1536           0 :                 CPLDebug(GetDebugKey(), "Creation of %s failed: %s",
    1537             :                          osDirname.c_str(),
    1538           0 :                          requestHelper.sWriteFuncData.pBuffer
    1539             :                              ? requestHelper.sWriteFuncData.pBuffer
    1540             :                              : "(null)");
    1541           0 :                 nRet = -1;
    1542             :             }
    1543             :         }
    1544             : 
    1545           1 :         curl_easy_cleanup(hCurlHandle);
    1546             :     } while (bRetry);
    1547             : 
    1548           1 :     return nRet;
    1549             : }
    1550             : 
    1551           3 : int VSIADLSFSHandler::Mkdir(const char *pszDirname, long nMode)
    1552             : {
    1553           3 :     return MkdirInternal(pszDirname, nMode, true);
    1554             : }
    1555             : 
    1556             : /************************************************************************/
    1557             : /*                          RmdirInternal()                             */
    1558             : /************************************************************************/
    1559             : 
    1560           4 : int VSIADLSFSHandler::RmdirInternal(const char *pszDirname, bool bRecursive)
    1561             : {
    1562           8 :     const std::string osDirname(pszDirname);
    1563             :     const std::string osDirnameWithoutEndSlash(
    1564          12 :         RemoveTrailingSlash(osDirname.c_str()));
    1565             : 
    1566             :     const bool bIsFileSystem =
    1567           4 :         osDirnameWithoutEndSlash.find('/', GetFSPrefix().size()) ==
    1568           4 :         std::string::npos;
    1569             : 
    1570           4 :     if (!bRecursive && bIsFileSystem)
    1571             :     {
    1572             :         // List content, to confirm it is empty first, as filesystem deletion
    1573             :         // is recursive by default.
    1574           0 :         bool bGotFileList = false;
    1575           0 :         CSLDestroy(GetFileList(osDirnameWithoutEndSlash.c_str(), 1, false,
    1576             :                                &bGotFileList));
    1577           0 :         if (bGotFileList)
    1578             :         {
    1579           0 :             CPLDebug(GetDebugKey(), "Cannot delete filesystem with "
    1580             :                                     "non-recursive method as it is not empty");
    1581           0 :             errno = ENOTEMPTY;
    1582           0 :             return -1;
    1583             :         }
    1584             :     }
    1585             : 
    1586           4 :     if (!bIsFileSystem)
    1587             :     {
    1588             :         VSIStatBufL sStat;
    1589           4 :         if (VSIStatL(osDirname.c_str(), &sStat) != 0)
    1590             :         {
    1591           1 :             CPLDebug(GetDebugKey(), "Object %s does not exist",
    1592             :                      osDirname.c_str());
    1593           1 :             errno = ENOENT;
    1594           2 :             return -1;
    1595             :         }
    1596           3 :         if (!VSI_ISDIR(sStat.st_mode))
    1597             :         {
    1598           1 :             CPLDebug(GetDebugKey(), "Object %s is not a directory",
    1599             :                      osDirname.c_str());
    1600           1 :             errno = ENOTDIR;
    1601           1 :             return -1;
    1602             :         }
    1603             :     }
    1604             : 
    1605             :     auto poHandleHelper =
    1606             :         std::unique_ptr<IVSIS3LikeHandleHelper>(CreateHandleHelper(
    1607           4 :             osDirnameWithoutEndSlash.c_str() + GetFSPrefix().size(), false));
    1608           2 :     if (poHandleHelper == nullptr)
    1609             :     {
    1610           0 :         return -1;
    1611             :     }
    1612             : 
    1613           2 :     InvalidateCachedData(GetURLFromFilename(osDirname.c_str()).c_str());
    1614           2 :     InvalidateCachedData(
    1615           4 :         GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str());
    1616           2 :     InvalidateDirContent(CPLGetDirname(osDirnameWithoutEndSlash.c_str()));
    1617           2 :     if (bRecursive)
    1618             :     {
    1619           1 :         PartialClearCache(osDirnameWithoutEndSlash.c_str());
    1620             :     }
    1621             : 
    1622           4 :     std::string osContinuation;
    1623           2 :     int nRet = 0;
    1624             :     bool bRetry;
    1625             : 
    1626             :     // coverity[tainted_data]
    1627           2 :     double dfRetryDelay = CPLAtof(
    1628             :         VSIGetPathSpecificOption(pszDirname, "GDAL_HTTP_RETRY_DELAY",
    1629             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1630             :     const int nMaxRetry =
    1631           2 :         atoi(VSIGetPathSpecificOption(pszDirname, "GDAL_HTTP_MAX_RETRY",
    1632             :                                       CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1633           2 :     int nRetryCount = 0;
    1634             : 
    1635           2 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
    1636             : 
    1637           2 :     do
    1638             :     {
    1639           2 :         bRetry = false;
    1640           2 :         CURL *hCurlHandle = curl_easy_init();
    1641           2 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST,
    1642             :                                    "DELETE");
    1643             : 
    1644           2 :         poHandleHelper->ResetQueryParameters();
    1645           2 :         if (bIsFileSystem)
    1646             :         {
    1647           0 :             poHandleHelper->AddQueryParameter("resource", "filesystem");
    1648             :         }
    1649             :         else
    1650             :         {
    1651           2 :             poHandleHelper->AddQueryParameter("recursive",
    1652             :                                               bRecursive ? "true" : "false");
    1653           2 :             if (!osContinuation.empty())
    1654           0 :                 poHandleHelper->AddQueryParameter("continuation",
    1655             :                                                   osContinuation);
    1656             :         }
    1657             : 
    1658             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1659           2 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1660             :                               aosHTTPOptions.List()));
    1661           2 :         headers = VSICurlMergeHeaders(
    1662           2 :             headers, poHandleHelper->GetCurlHeaders("DELETE", headers));
    1663             : 
    1664           4 :         CurlRequestHelper requestHelper;
    1665           2 :         const long response_code = requestHelper.perform(
    1666             :             hCurlHandle, headers, this, poHandleHelper.get());
    1667             : 
    1668           2 :         NetworkStatisticsLogger::LogDELETE();
    1669             : 
    1670             :         // 200 for path deletion
    1671             :         // 202 for filesystem deletion
    1672           2 :         if (response_code != 200 && response_code != 202)
    1673             :         {
    1674             :             // Look if we should attempt a retry
    1675           0 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1676             :                 static_cast<int>(response_code), dfRetryDelay,
    1677           0 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1678             :                 requestHelper.szCurlErrBuf);
    1679           0 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1680             :             {
    1681           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1682             :                          "HTTP error code: %d - %s. "
    1683             :                          "Retrying again in %.1f secs",
    1684             :                          static_cast<int>(response_code),
    1685           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
    1686           0 :                 CPLSleep(dfRetryDelay);
    1687           0 :                 dfRetryDelay = dfNewRetryDelay;
    1688           0 :                 nRetryCount++;
    1689           0 :                 bRetry = true;
    1690             :             }
    1691             :             else
    1692             :             {
    1693           0 :                 CPLDebug(GetDebugKey(), "Delete of %s failed: %s",
    1694             :                          osDirname.c_str(),
    1695           0 :                          requestHelper.sWriteFuncData.pBuffer
    1696             :                              ? requestHelper.sWriteFuncData.pBuffer
    1697             :                              : "(null)");
    1698           0 :                 if (requestHelper.sWriteFuncData.pBuffer != nullptr)
    1699             :                 {
    1700           0 :                     VSIError(VSIE_AWSError, "%s",
    1701             :                              requestHelper.sWriteFuncData.pBuffer);
    1702           0 :                     if (strstr(requestHelper.sWriteFuncData.pBuffer,
    1703             :                                "PathNotFound"))
    1704             :                     {
    1705           0 :                         errno = ENOENT;
    1706             :                     }
    1707           0 :                     else if (strstr(requestHelper.sWriteFuncData.pBuffer,
    1708             :                                     "DirectoryNotEmpty"))
    1709             :                     {
    1710           0 :                         errno = ENOTEMPTY;
    1711             :                     }
    1712             :                 }
    1713           0 :                 nRet = -1;
    1714           0 :             }
    1715             :         }
    1716             :         else
    1717             :         {
    1718             :             // Get continuation token for response headers
    1719           2 :             osContinuation = GetContinuationToken(
    1720           2 :                 requestHelper.sWriteFuncHeaderData.pBuffer);
    1721           2 :             if (!osContinuation.empty())
    1722             :             {
    1723           0 :                 nRetryCount = 0;
    1724           0 :                 bRetry = true;
    1725             :             }
    1726             :         }
    1727             : 
    1728           2 :         curl_easy_cleanup(hCurlHandle);
    1729             :     } while (bRetry);
    1730             : 
    1731           2 :     return nRet;
    1732             : }
    1733             : 
    1734             : /************************************************************************/
    1735             : /*                               Rmdir()                                */
    1736             : /************************************************************************/
    1737             : 
    1738           4 : int VSIADLSFSHandler::Rmdir(const char *pszDirname)
    1739             : {
    1740           4 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1741           1 :         return -1;
    1742             : 
    1743           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1744           6 :     NetworkStatisticsAction oContextAction("Rmdir");
    1745             : 
    1746           3 :     return RmdirInternal(pszDirname, false);
    1747             : }
    1748             : 
    1749             : /************************************************************************/
    1750             : /*                          RmdirRecursive()                            */
    1751             : /************************************************************************/
    1752             : 
    1753           1 : int VSIADLSFSHandler::RmdirRecursive(const char *pszDirname)
    1754             : {
    1755           1 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1756           0 :         return -1;
    1757             : 
    1758           2 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1759           2 :     NetworkStatisticsAction oContextAction("RmdirRecursive");
    1760             : 
    1761           1 :     return RmdirInternal(pszDirname, true);
    1762             : }
    1763             : 
    1764             : /************************************************************************/
    1765             : /*                            CopyObject()                              */
    1766             : /************************************************************************/
    1767             : 
    1768           4 : int VSIADLSFSHandler::CopyObject(const char *oldpath, const char *newpath,
    1769             :                                  CSLConstList /* papszMetadata */)
    1770             : {
    1771             :     // There is no CopyObject in ADLS... So use the base Azure blob one...
    1772             : 
    1773           8 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1774           8 :     NetworkStatisticsAction oContextAction("CopyObject");
    1775             : 
    1776          12 :     std::string osTargetNameWithoutPrefix = newpath + GetFSPrefix().size();
    1777             :     auto poAzHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1778           4 :         VSIAzureBlobHandleHelper::BuildFromURI(
    1779           8 :             osTargetNameWithoutPrefix.c_str(), "/vsiaz/"));
    1780           4 :     if (poAzHandleHelper == nullptr)
    1781             :     {
    1782           0 :         return -1;
    1783             :     }
    1784             : 
    1785          12 :     std::string osSourceNameWithoutPrefix = oldpath + GetFSPrefix().size();
    1786             :     auto poAzHandleHelperSource = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1787           4 :         VSIAzureBlobHandleHelper::BuildFromURI(
    1788           8 :             osSourceNameWithoutPrefix.c_str(), "/vsiaz/"));
    1789           4 :     if (poAzHandleHelperSource == nullptr)
    1790             :     {
    1791           0 :         return -1;
    1792             :     }
    1793             : 
    1794           8 :     std::string osSourceHeader("x-ms-copy-source: ");
    1795           4 :     osSourceHeader += poAzHandleHelperSource->GetURLNoKVP();
    1796             : 
    1797           4 :     int nRet = 0;
    1798             : 
    1799             :     bool bRetry;
    1800             : 
    1801             :     // coverity[tainted_data]
    1802           4 :     double dfRetryDelay = CPLAtof(
    1803             :         VSIGetPathSpecificOption(oldpath, "GDAL_HTTP_RETRY_DELAY",
    1804             :                                  CPLSPrintf("%f", CPL_HTTP_RETRY_DELAY)));
    1805           4 :     const int nMaxRetry = atoi(VSIGetPathSpecificOption(
    1806             :         oldpath, "GDAL_HTTP_MAX_RETRY", CPLSPrintf("%d", CPL_HTTP_MAX_RETRY)));
    1807           4 :     int nRetryCount = 0;
    1808             : 
    1809           4 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(oldpath));
    1810             : 
    1811           4 :     do
    1812             :     {
    1813           4 :         bRetry = false;
    1814           4 :         CURL *hCurlHandle = curl_easy_init();
    1815           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1816             : 
    1817             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1818           4 :             CPLHTTPSetOptions(hCurlHandle, poAzHandleHelper->GetURL().c_str(),
    1819             :                               aosHTTPOptions.List()));
    1820           4 :         headers = curl_slist_append(headers, osSourceHeader.c_str());
    1821           4 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1822           4 :         headers = VSICurlSetContentTypeFromExt(headers, newpath);
    1823           4 :         headers = VSICurlMergeHeaders(
    1824           4 :             headers, poAzHandleHelper->GetCurlHeaders("PUT", headers));
    1825           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1826             : 
    1827           8 :         CurlRequestHelper requestHelper;
    1828           4 :         const long response_code = requestHelper.perform(
    1829             :             hCurlHandle, headers, this, poAzHandleHelper.get());
    1830             : 
    1831           4 :         NetworkStatisticsLogger::LogPUT(0);
    1832             : 
    1833           4 :         if (response_code != 202)
    1834             :         {
    1835             :             // Look if we should attempt a retry
    1836           2 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1837             :                 static_cast<int>(response_code), dfRetryDelay,
    1838           1 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1839             :                 requestHelper.szCurlErrBuf);
    1840           1 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1841             :             {
    1842           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1843             :                          "HTTP error code: %d - %s. "
    1844             :                          "Retrying again in %.1f secs",
    1845             :                          static_cast<int>(response_code),
    1846           0 :                          poAzHandleHelper->GetURL().c_str(), dfRetryDelay);
    1847           0 :                 CPLSleep(dfRetryDelay);
    1848           0 :                 dfRetryDelay = dfNewRetryDelay;
    1849           0 :                 nRetryCount++;
    1850           0 :                 bRetry = true;
    1851             :             }
    1852             :             else
    1853             :             {
    1854           1 :                 CPLDebug(GetDebugKey(), "%s",
    1855           1 :                          requestHelper.sWriteFuncData.pBuffer
    1856             :                              ? requestHelper.sWriteFuncData.pBuffer
    1857             :                              : "(null)");
    1858           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Copy of %s to %s failed",
    1859             :                          oldpath, newpath);
    1860           1 :                 nRet = -1;
    1861             :             }
    1862             :         }
    1863             :         else
    1864             :         {
    1865             :             auto poADLSHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1866           3 :                 VSIAzureBlobHandleHelper::BuildFromURI(
    1867           9 :                     osTargetNameWithoutPrefix.c_str(), GetFSPrefix().c_str()));
    1868           3 :             if (poADLSHandleHelper != nullptr)
    1869           3 :                 InvalidateCachedData(poADLSHandleHelper->GetURLNoKVP().c_str());
    1870             : 
    1871             :             const std::string osFilenameWithoutSlash(
    1872           9 :                 RemoveTrailingSlash(newpath));
    1873           3 :             InvalidateDirContent(CPLGetDirname(osFilenameWithoutSlash.c_str()));
    1874             :         }
    1875             : 
    1876           4 :         curl_easy_cleanup(hCurlHandle);
    1877             :     } while (bRetry);
    1878             : 
    1879           4 :     return nRet;
    1880             : }
    1881             : 
    1882             : /************************************************************************/
    1883             : /*                          UploadFile()                                */
    1884             : /************************************************************************/
    1885             : 
    1886          23 : bool VSIADLSFSHandler::UploadFile(const std::string &osFilename, Event event,
    1887             :                                   vsi_l_offset nPosition,
    1888             :                                   const void *pabyBuffer, size_t nBufferSize,
    1889             :                                   IVSIS3LikeHandleHelper *poHandleHelper,
    1890             :                                   int nMaxRetry, double dfRetryDelay,
    1891             :                                   CSLConstList papszOptions)
    1892             : {
    1893          46 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1894          46 :     NetworkStatisticsFile oContextFile(osFilename.c_str());
    1895          46 :     NetworkStatisticsAction oContextAction("UploadFile");
    1896             : 
    1897          23 :     if (event == Event::CREATE_FILE)
    1898             :     {
    1899           8 :         InvalidateCachedData(poHandleHelper->GetURLNoKVP().c_str());
    1900           8 :         InvalidateDirContent(CPLGetDirname(osFilename.c_str()));
    1901             :     }
    1902             : 
    1903             :     const CPLStringList aosHTTPOptions(
    1904          23 :         CPLHTTPGetOptionsFromEnv(osFilename.c_str()));
    1905             : 
    1906          23 :     bool bSuccess = true;
    1907          23 :     int nRetryCount = 0;
    1908             :     bool bRetry;
    1909          23 :     do
    1910             :     {
    1911          23 :         bRetry = false;
    1912             : 
    1913          23 :         CURL *hCurlHandle = curl_easy_init();
    1914             : 
    1915          23 :         poHandleHelper->ResetQueryParameters();
    1916          23 :         if (event == Event::CREATE_FILE)
    1917             :         {
    1918           8 :             poHandleHelper->AddQueryParameter("resource", "file");
    1919             :         }
    1920          15 :         else if (event == Event::APPEND_DATA)
    1921             :         {
    1922           9 :             poHandleHelper->AddQueryParameter("action", "append");
    1923           9 :             poHandleHelper->AddQueryParameter(
    1924             :                 "position",
    1925             :                 CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosition)));
    1926             :         }
    1927             :         else
    1928             :         {
    1929           6 :             poHandleHelper->AddQueryParameter("action", "flush");
    1930           6 :             poHandleHelper->AddQueryParameter("close", "true");
    1931           6 :             poHandleHelper->AddQueryParameter(
    1932             :                 "position",
    1933             :                 CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosition)));
    1934             :         }
    1935             : 
    1936          23 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_UPLOAD, 1L);
    1937          23 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_READFUNCTION,
    1938             :                                    PutData::ReadCallBackBuffer);
    1939          23 :         PutData putData;
    1940          23 :         putData.pabyData = static_cast<const GByte *>(pabyBuffer);
    1941          23 :         putData.nOff = 0;
    1942          23 :         putData.nTotalSize = nBufferSize;
    1943          23 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_READDATA, &putData);
    1944             : 
    1945             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1946          23 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1947             :                               aosHTTPOptions.List()));
    1948          23 :         headers = VSICurlSetCreationHeadersFromOptions(headers, papszOptions,
    1949             :                                                        osFilename.c_str());
    1950             : 
    1951          46 :         CPLString osContentLength;  // leave it in this scope
    1952             : 
    1953          23 :         if (event == Event::APPEND_DATA)
    1954             :         {
    1955           9 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE,
    1956             :                                        static_cast<int>(nBufferSize));
    1957             :             // Disable "Expect: 100-continue" which doesn't hurt, but is not
    1958             :             // needed
    1959           9 :             headers = curl_slist_append(headers, "Expect:");
    1960             :             osContentLength.Printf("Content-Length: %d",
    1961           9 :                                    static_cast<int>(nBufferSize));
    1962           9 :             headers = curl_slist_append(headers, osContentLength.c_str());
    1963             :         }
    1964             :         else
    1965             :         {
    1966          14 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE, 0);
    1967          14 :             headers = curl_slist_append(headers, "Content-Length: 0");
    1968             :         }
    1969             : 
    1970          23 :         const char *pszVerb = (event == Event::CREATE_FILE) ? "PUT" : "PATCH";
    1971          23 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, pszVerb);
    1972          23 :         headers = VSICurlMergeHeaders(
    1973          23 :             headers, poHandleHelper->GetCurlHeaders(pszVerb, headers));
    1974          23 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1975             : 
    1976          46 :         CurlRequestHelper requestHelper;
    1977             :         const long response_code =
    1978          23 :             requestHelper.perform(hCurlHandle, headers, this, poHandleHelper);
    1979             : 
    1980          23 :         NetworkStatisticsLogger::LogPUT(
    1981             :             event == Event::APPEND_DATA ? nBufferSize : 0);
    1982             : 
    1983             :         // 200 for PATCH flush
    1984             :         // 201 for PUT create
    1985             :         // 202 for PATCH append
    1986          23 :         if (response_code != 200 && response_code != 201 &&
    1987             :             response_code != 202)
    1988             :         {
    1989             :             // Look if we should attempt a retry
    1990           6 :             const double dfNewRetryDelay = CPLHTTPGetNewRetryDelay(
    1991             :                 static_cast<int>(response_code), dfRetryDelay,
    1992           3 :                 requestHelper.sWriteFuncHeaderData.pBuffer,
    1993             :                 requestHelper.szCurlErrBuf);
    1994           3 :             if (dfNewRetryDelay > 0 && nRetryCount < nMaxRetry)
    1995             :             {
    1996           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1997             :                          "HTTP error code: %d - %s. "
    1998             :                          "Retrying again in %.1f secs",
    1999             :                          static_cast<int>(response_code),
    2000           0 :                          poHandleHelper->GetURL().c_str(), dfRetryDelay);
    2001           0 :                 CPLSleep(dfRetryDelay);
    2002           0 :                 dfRetryDelay = dfNewRetryDelay;
    2003           0 :                 nRetryCount++;
    2004           0 :                 bRetry = true;
    2005             :             }
    2006             :             else
    2007             :             {
    2008           3 :                 CPLDebug(GetDebugKey(), "%s of %s failed: %s", pszVerb,
    2009             :                          osFilename.c_str(),
    2010           3 :                          requestHelper.sWriteFuncData.pBuffer
    2011             :                              ? requestHelper.sWriteFuncData.pBuffer
    2012             :                              : "(null)");
    2013           3 :                 bSuccess = false;
    2014             :             }
    2015             :         }
    2016             : 
    2017          23 :         curl_easy_cleanup(hCurlHandle);
    2018             :     } while (bRetry);
    2019             : 
    2020          46 :     return bSuccess;
    2021             : }
    2022             : 
    2023             : /************************************************************************/
    2024             : /*                           GetFileList()                              */
    2025             : /************************************************************************/
    2026             : 
    2027           4 : char **VSIADLSFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
    2028             :                                      bool *pbGotFileList)
    2029             : {
    2030           4 :     return GetFileList(pszDirname, nMaxFiles, true, pbGotFileList);
    2031             : }
    2032             : 
    2033           4 : char **VSIADLSFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
    2034             :                                      bool bCacheEntries, bool *pbGotFileList)
    2035             : {
    2036             :     if (ENABLE_DEBUG)
    2037             :         CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
    2038             : 
    2039           4 :     *pbGotFileList = false;
    2040             : 
    2041             :     char **papszOptions =
    2042           4 :         CSLSetNameValue(nullptr, "MAXFILES", CPLSPrintf("%d", nMaxFiles));
    2043           4 :     papszOptions = CSLSetNameValue(papszOptions, "CACHE_ENTRIES",
    2044             :                                    bCacheEntries ? "YES" : "NO");
    2045           4 :     auto dir = OpenDir(pszDirname, 0, papszOptions);
    2046           4 :     CSLDestroy(papszOptions);
    2047           4 :     if (!dir)
    2048             :     {
    2049           1 :         return nullptr;
    2050             :     }
    2051           6 :     CPLStringList aosFileList;
    2052             :     while (true)
    2053             :     {
    2054           8 :         auto entry = dir->NextDirEntry();
    2055           8 :         if (!entry)
    2056             :         {
    2057           3 :             break;
    2058             :         }
    2059           5 :         aosFileList.AddString(entry->pszName);
    2060             : 
    2061           5 :         if (nMaxFiles > 0 && aosFileList.size() >= nMaxFiles)
    2062           0 :             break;
    2063           5 :     }
    2064           3 :     delete dir;
    2065           3 :     *pbGotFileList = true;
    2066           3 :     return aosFileList.StealList();
    2067             : }
    2068             : 
    2069             : /************************************************************************/
    2070             : /*                           GetOptions()                               */
    2071             : /************************************************************************/
    2072             : 
    2073           1 : const char *VSIADLSFSHandler::GetOptions()
    2074             : {
    2075             :     static std::string osOptions(
    2076           2 :         std::string("<Options>") +
    2077             :         "  <Option name='AZURE_STORAGE_CONNECTION_STRING' type='string' "
    2078             :         "description='Connection string that contains account name and "
    2079             :         "secret key'/>"
    2080             :         "  <Option name='AZURE_STORAGE_ACCOUNT' type='string' "
    2081             :         "description='Storage account. To use with AZURE_STORAGE_ACCESS_KEY'/>"
    2082             :         "  <Option name='AZURE_STORAGE_ACCESS_KEY' type='string' "
    2083             :         "description='Secret key'/>"
    2084             :         "  <Option name='VSIAZ_CHUNK_SIZE' type='int' "
    2085             :         "description='Size in MB for chunks of files that are uploaded' "
    2086           3 :         "default='4' min='1' max='4'/>" +
    2087           2 :         VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
    2088           1 :     return osOptions.c_str();
    2089             : }
    2090             : 
    2091             : /************************************************************************/
    2092             : /*                           GetSignedURL()                             */
    2093             : /************************************************************************/
    2094             : 
    2095           2 : char *VSIADLSFSHandler::GetSignedURL(const char *pszFilename,
    2096             :                                      CSLConstList papszOptions)
    2097             : {
    2098           2 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
    2099           0 :         return nullptr;
    2100             : 
    2101             :     auto poHandleHelper = std::unique_ptr<VSIAzureBlobHandleHelper>(
    2102             :         VSIAzureBlobHandleHelper::BuildFromURI(pszFilename +
    2103           2 :                                                    GetFSPrefix().size(),
    2104             :                                                "/vsiaz/",  // use Azure blob
    2105           4 :                                                papszOptions));
    2106           2 :     if (poHandleHelper == nullptr)
    2107             :     {
    2108           1 :         return nullptr;
    2109             :     }
    2110             : 
    2111           2 :     std::string osRet(poHandleHelper->GetSignedURL(papszOptions));
    2112             : 
    2113           1 :     return CPLStrdup(osRet.c_str());
    2114             : }
    2115             : 
    2116             : /************************************************************************/
    2117             : /*                            OpenDir()                                 */
    2118             : /************************************************************************/
    2119             : 
    2120          10 : VSIDIR *VSIADLSFSHandler::OpenDir(const char *pszPath, int nRecurseDepth,
    2121             :                                   const char *const *papszOptions)
    2122             : {
    2123          10 :     if (nRecurseDepth > 0)
    2124             :     {
    2125           0 :         return VSIFilesystemHandler::OpenDir(pszPath, nRecurseDepth,
    2126           0 :                                              papszOptions);
    2127             :     }
    2128             : 
    2129          10 :     if (!STARTS_WITH_CI(pszPath, GetFSPrefix().c_str()))
    2130           0 :         return nullptr;
    2131             : 
    2132          20 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    2133          20 :     NetworkStatisticsAction oContextAction("OpenDir");
    2134             : 
    2135             :     const std::string osDirnameWithoutPrefix =
    2136          30 :         RemoveTrailingSlash(pszPath + GetFSPrefix().size());
    2137          20 :     std::string osFilesystem(osDirnameWithoutPrefix);
    2138          20 :     std::string osObjectKey;
    2139          10 :     size_t nSlashPos = osDirnameWithoutPrefix.find('/');
    2140          10 :     if (nSlashPos != std::string::npos)
    2141             :     {
    2142           3 :         osFilesystem = osDirnameWithoutPrefix.substr(0, nSlashPos);
    2143           3 :         osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
    2144             :     }
    2145             : 
    2146          10 :     VSIDIRADLS *dir = new VSIDIRADLS(this);
    2147          10 :     dir->m_nRecurseDepth = nRecurseDepth;
    2148          10 :     dir->m_poFS = this;
    2149          10 :     dir->m_bRecursiveRequestFromAccountRoot =
    2150          10 :         osFilesystem.empty() && nRecurseDepth < 0;
    2151          10 :     dir->m_osFilesystem = std::move(osFilesystem);
    2152          10 :     dir->m_osObjectKey = std::move(osObjectKey);
    2153          10 :     dir->m_nMaxFiles =
    2154          10 :         atoi(CSLFetchNameValueDef(papszOptions, "MAXFILES", "0"));
    2155          10 :     dir->m_bCacheEntries =
    2156          10 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CACHE_ENTRIES", "YES"));
    2157          10 :     dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
    2158          10 :     if (!dir->IssueListDir())
    2159             :     {
    2160           1 :         delete dir;
    2161           1 :         return nullptr;
    2162             :     }
    2163             : 
    2164           9 :     return dir;
    2165             : }
    2166             : 
    2167             : /************************************************************************/
    2168             : /*                      GetStreamingFilename()                          */
    2169             : /************************************************************************/
    2170             : 
    2171             : std::string
    2172           0 : VSIADLSFSHandler::GetStreamingFilename(const std::string &osFilename) const
    2173             : {
    2174           0 :     if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
    2175           0 :         return "/vsiaz_streaming/" + osFilename.substr(GetFSPrefix().size());
    2176           0 :     return osFilename;
    2177             : }
    2178             : 
    2179             : /************************************************************************/
    2180             : /*                           VSIADLSHandle()                           */
    2181             : /************************************************************************/
    2182             : 
    2183          25 : VSIADLSHandle::VSIADLSHandle(VSIADLSFSHandler *poFSIn, const char *pszFilename,
    2184          25 :                              VSIAzureBlobHandleHelper *poHandleHelper)
    2185          25 :     : VSICurlHandle(poFSIn, pszFilename, poHandleHelper->GetURLNoKVP().c_str()),
    2186          50 :       m_poHandleHelper(poHandleHelper)
    2187             : {
    2188          25 :     m_osQueryString = poHandleHelper->GetSASQueryString();
    2189          25 : }
    2190             : 
    2191             : /************************************************************************/
    2192             : /*                          GetCurlHeaders()                            */
    2193             : /************************************************************************/
    2194             : 
    2195             : struct curl_slist *
    2196          21 : VSIADLSHandle::GetCurlHeaders(const std::string &osVerb,
    2197             :                               const struct curl_slist *psExistingHeaders)
    2198             : {
    2199          21 :     return m_poHandleHelper->GetCurlHeaders(osVerb, psExistingHeaders);
    2200             : }
    2201             : 
    2202             : } /* end of namespace cpl */
    2203             : 
    2204             : #endif  // DOXYGEN_SKIP
    2205             : //! @endcond
    2206             : 
    2207             : /************************************************************************/
    2208             : /*                      VSIInstallADLSFileHandler()                    */
    2209             : /************************************************************************/
    2210             : 
    2211             : /*!
    2212             :  \brief Install /vsiaz/ Microsoft Azure Data Lake Storage Gen2 file system
    2213             :  handler (requires libcurl)
    2214             : 
    2215             :  \verbatim embed:rst
    2216             :  See :ref:`/vsiadls/ documentation <vsiadls>`
    2217             :  \endverbatim
    2218             : 
    2219             :  @since GDAL 3.3
    2220             :  */
    2221             : 
    2222        1227 : void VSIInstallADLSFileHandler(void)
    2223             : {
    2224        1227 :     VSIFileManager::InstallHandler("/vsiadls/", new cpl::VSIADLSFSHandler);
    2225        1227 : }
    2226             : 
    2227             : #endif /* HAVE_CURL */

Generated by: LCOV version 1.14