LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 741 887 83.5 %
Date: 2025-05-24 03:54:53 Functions: 56 58 96.6 %

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

Generated by: LCOV version 1.14