LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 735 881 83.4 %
Date: 2025-08-01 10:10:57 Functions: 57 59 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        1691 :     VSIADLSFSHandler() = default;
     177        2244 :     ~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 = poHandleHelper->GetCurlHeaders("GET", headers);
     620          17 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     621             : 
     622          17 :     CurlRequestHelper requestHelper;
     623          17 :     const long response_code = requestHelper.perform(
     624          17 :         hCurlHandle, headers, m_poFS, poHandleHelper.get());
     625             : 
     626          17 :     NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
     627             : 
     628          17 :     bool ret = false;
     629          17 :     if (response_code != 200)
     630             :     {
     631           1 :         CPLDebug(m_poFS->GetDebugKey(), "%s",
     632           1 :                  requestHelper.sWriteFuncData.pBuffer
     633             :                      ? requestHelper.sWriteFuncData.pBuffer
     634             :                      : "(null)");
     635             :     }
     636             :     else
     637             :     {
     638          16 :         if (!m_osFilesystem.empty())
     639             :         {
     640             :             // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/list
     641          11 :             ret = AnalysePathList(osBaseURL,
     642          11 :                                   requestHelper.sWriteFuncData.pBuffer);
     643             :         }
     644             :         else
     645             :         {
     646             :             // https://docs.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/filesystem/list
     647           5 :             ret = AnalyseFilesystemList(osBaseURL,
     648           5 :                                         requestHelper.sWriteFuncData.pBuffer);
     649             :         }
     650             : 
     651             :         // Get continuation token for response headers
     652             :         oIter.m_osNextMarker =
     653          16 :             GetContinuationToken(requestHelper.sWriteFuncHeaderData.pBuffer);
     654             :     }
     655             : 
     656          17 :     curl_easy_cleanup(hCurlHandle);
     657          17 :     return ret;
     658             : }
     659             : 
     660             : /************************************************************************/
     661             : /*                           NextDirEntry()                             */
     662             : /************************************************************************/
     663             : 
     664          33 : const VSIDIREntry *VSIDIRADLS::NextDirEntry()
     665             : {
     666             :     while (true)
     667             :     {
     668             :         auto &oIter =
     669          33 :             !m_osFilesystem.empty() ? m_oIterWithinFilesystem : m_oIterFromRoot;
     670          33 :         if (oIter.m_nPos < static_cast<int>(oIter.m_aoEntries.size()))
     671             :         {
     672          17 :             auto &entry = oIter.m_aoEntries[oIter.m_nPos];
     673          17 :             oIter.m_nPos++;
     674          17 :             if (m_bRecursiveRequestFromAccountRoot)
     675             :             {
     676             :                 // If we just read an entry from the account root, it is a
     677             :                 // filesystem name, and we want the next iteration to read
     678             :                 // into it.
     679           6 :                 if (m_osFilesystem.empty())
     680             :                 {
     681           3 :                     m_osFilesystem = entry->pszName;
     682           3 :                     if (!IssueListDir())
     683             :                     {
     684           0 :                         return nullptr;
     685             :                     }
     686             :                 }
     687             :             }
     688          19 :             if (!m_osFilterPrefix.empty() &&
     689           2 :                 !STARTS_WITH(entry->pszName, m_osFilterPrefix.c_str()))
     690             :             {
     691           1 :                 continue;
     692             :             }
     693          16 :             return entry.get();
     694             :         }
     695          16 :         if (oIter.m_osNextMarker.empty())
     696             :         {
     697          12 :             if (m_bRecursiveRequestFromAccountRoot)
     698             :             {
     699             :                 // If we have no more entries at the filesystem level, go back
     700             :                 // to the root level.
     701           4 :                 if (!m_osFilesystem.empty())
     702             :                 {
     703           3 :                     m_osFilesystem.clear();
     704           3 :                     continue;
     705             :                 }
     706             :             }
     707           9 :             return nullptr;
     708             :         }
     709           4 :         if (!IssueListDir())
     710             :         {
     711           0 :             return nullptr;
     712             :         }
     713           8 :     }
     714             : }
     715             : 
     716             : /************************************************************************/
     717             : /*                          VSIADLSHandle                              */
     718             : /************************************************************************/
     719             : 
     720             : class VSIADLSHandle final : public VSICurlHandle
     721             : {
     722             :     CPL_DISALLOW_COPY_ASSIGN(VSIADLSHandle)
     723             : 
     724             :     std::unique_ptr<VSIAzureBlobHandleHelper> m_poHandleHelper{};
     725             : 
     726             :   protected:
     727             :     virtual struct curl_slist *
     728             :     GetCurlHeaders(const std::string &osVerb,
     729             :                    struct curl_slist *psHeaders) override;
     730             :     bool CanRestartOnError(const char *, const char *, bool) 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 = poHandleHelper->GetCurlHeaders("HEAD", headers);
     836           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     837             : 
     838           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 1);
     839             : 
     840           6 :         CurlRequestHelper requestHelper;
     841           3 :         const long response_code = requestHelper.perform(
     842             :             hCurlHandle, headers, this, poHandleHelper.get());
     843             : 
     844           3 :         NetworkStatisticsLogger::LogHEAD();
     845             : 
     846           3 :         if (response_code != 200 ||
     847           3 :             requestHelper.sWriteFuncHeaderData.pBuffer == nullptr)
     848             :         {
     849           0 :             curl_easy_cleanup(hCurlHandle);
     850           0 :             return -1;
     851             :         }
     852             : 
     853           3 :         memset(pStatBuf, 0, sizeof(VSIStatBufL));
     854           3 :         pStatBuf->st_mode = S_IFDIR;
     855             : 
     856           3 :         const char *pszLastModified = strstr(
     857             :             requestHelper.sWriteFuncHeaderData.pBuffer, "Last-Modified: ");
     858           3 :         if (pszLastModified)
     859             :         {
     860           0 :             pszLastModified += strlen("Last-Modified: ");
     861           0 :             const char *pszEOL = strstr(pszLastModified, "\r\n");
     862           0 :             if (pszEOL)
     863             :             {
     864           0 :                 std::string osLastModified;
     865             :                 osLastModified.assign(pszLastModified,
     866           0 :                                       pszEOL - pszLastModified);
     867             : 
     868             :                 const GIntBig nMTime =
     869           0 :                     GetUnixTimeFromRFC822(osLastModified.c_str());
     870           0 :                 if (nMTime != GINTBIG_MIN)
     871             :                 {
     872           0 :                     pStatBuf->st_mtime = static_cast<time_t>(nMTime);
     873             :                 }
     874             :             }
     875             :         }
     876             : 
     877           3 :         curl_easy_cleanup(hCurlHandle);
     878             : 
     879           3 :         return 0;
     880             :     }
     881             : 
     882          22 :     return VSICurlFilesystemHandlerBase::Stat(osFilenameWithoutSlash.c_str(),
     883          22 :                                               pStatBuf, nFlags);
     884             : }
     885             : 
     886             : /************************************************************************/
     887             : /*                          GetFileMetadata()                           */
     888             : /************************************************************************/
     889             : 
     890           4 : char **VSIADLSFSHandler::GetFileMetadata(const char *pszFilename,
     891             :                                          const char *pszDomain,
     892             :                                          CSLConstList papszOptions)
     893             : {
     894           4 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     895           0 :         return nullptr;
     896             : 
     897           4 :     if (pszDomain == nullptr ||
     898           4 :         (!EQUAL(pszDomain, "STATUS") && !EQUAL(pszDomain, "ACL")))
     899             :     {
     900           1 :         return VSICurlFilesystemHandlerBase::GetFileMetadata(
     901           1 :             pszFilename, pszDomain, papszOptions);
     902             :     }
     903             : 
     904             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
     905           6 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), false));
     906           3 :     if (poHandleHelper == nullptr)
     907             :     {
     908           0 :         return nullptr;
     909             :     }
     910             : 
     911           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     912           6 :     NetworkStatisticsAction oContextAction("GetFileMetadata");
     913             : 
     914             :     bool bRetry;
     915           3 :     bool bError = true;
     916             : 
     917           6 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
     918           6 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
     919           6 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
     920             : 
     921           3 :     CPLStringList aosMetadata;
     922           3 :     do
     923             :     {
     924           3 :         bRetry = false;
     925           3 :         CURL *hCurlHandle = curl_easy_init();
     926           3 :         poHandleHelper->AddQueryParameter("action", EQUAL(pszDomain, "STATUS")
     927             :                                                         ? "getStatus"
     928             :                                                         : "getAccessControl");
     929             : 
     930             :         struct curl_slist *headers =
     931           3 :             VSICurlSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
     932             :                               aosHTTPOptions.List());
     933             : 
     934           3 :         headers = poHandleHelper->GetCurlHeaders("HEAD", headers);
     935           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     936             : 
     937           3 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 1);
     938             : 
     939           6 :         CurlRequestHelper requestHelper;
     940           3 :         const long response_code = requestHelper.perform(
     941             :             hCurlHandle, headers, this, poHandleHelper.get());
     942             : 
     943           3 :         NetworkStatisticsLogger::LogHEAD();
     944             : 
     945           3 :         if (response_code != 200 ||
     946           2 :             requestHelper.sWriteFuncHeaderData.pBuffer == nullptr)
     947             :         {
     948             :             // Look if we should attempt a retry
     949           1 :             if (oRetryContext.CanRetry(
     950             :                     static_cast<int>(response_code),
     951           1 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
     952             :                     requestHelper.szCurlErrBuf))
     953             :             {
     954           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     955             :                          "HTTP error code: %d - %s. "
     956             :                          "Retrying again in %.1f secs",
     957             :                          static_cast<int>(response_code),
     958           0 :                          poHandleHelper->GetURL().c_str(),
     959             :                          oRetryContext.GetCurrentDelay());
     960           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
     961           0 :                 bRetry = true;
     962             :             }
     963             :             else
     964             :             {
     965           1 :                 CPLDebug(GetDebugKey(), "GetFileMetadata failed on %s: %s",
     966             :                          pszFilename,
     967           1 :                          requestHelper.sWriteFuncData.pBuffer
     968             :                              ? requestHelper.sWriteFuncData.pBuffer
     969             :                              : "(null)");
     970             :             }
     971             :         }
     972             :         else
     973             :         {
     974           4 :             char **papszHeaders = CSLTokenizeString2(
     975           2 :                 requestHelper.sWriteFuncHeaderData.pBuffer, "\r\n", 0);
     976          12 :             for (int i = 0; papszHeaders[i]; ++i)
     977             :             {
     978          10 :                 char *pszKey = nullptr;
     979             :                 const char *pszValue =
     980          10 :                     CPLParseNameValue(papszHeaders[i], &pszKey);
     981          10 :                 if (pszKey && pszValue && !EQUAL(pszKey, "Server") &&
     982           6 :                     !EQUAL(pszKey, "Date"))
     983             :                 {
     984           4 :                     aosMetadata.SetNameValue(pszKey, pszValue);
     985             :                 }
     986          10 :                 CPLFree(pszKey);
     987             :             }
     988           2 :             CSLDestroy(papszHeaders);
     989           2 :             bError = false;
     990             :         }
     991             : 
     992           3 :         curl_easy_cleanup(hCurlHandle);
     993             :     } while (bRetry);
     994           3 :     return bError ? nullptr : CSLDuplicate(aosMetadata.List());
     995             : }
     996             : 
     997             : /************************************************************************/
     998             : /*                          SetFileMetadata()                           */
     999             : /************************************************************************/
    1000             : 
    1001           4 : bool VSIADLSFSHandler::SetFileMetadata(const char *pszFilename,
    1002             :                                        CSLConstList papszMetadata,
    1003             :                                        const char *pszDomain,
    1004             :                                        CSLConstList papszOptions)
    1005             : {
    1006           4 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
    1007           0 :         return false;
    1008             : 
    1009           4 :     if (pszDomain == nullptr ||
    1010           4 :         !(EQUAL(pszDomain, "PROPERTIES") || EQUAL(pszDomain, "ACL")))
    1011             :     {
    1012           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1013             :                  "Only PROPERTIES and ACL domain are supported");
    1014           0 :         return false;
    1015             :     }
    1016             : 
    1017             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1018           8 :         CreateHandleHelper(pszFilename + GetFSPrefix().size(), false));
    1019           4 :     if (poHandleHelper == nullptr)
    1020             :     {
    1021           0 :         return false;
    1022             :     }
    1023             : 
    1024             :     const bool bRecursive =
    1025           4 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "RECURSIVE", "FALSE"));
    1026           4 :     const char *pszMode = CSLFetchNameValue(papszOptions, "MODE");
    1027           4 :     if (!EQUAL(pszDomain, "PROPERTIES") && bRecursive && pszMode == nullptr)
    1028             :     {
    1029           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1030             :                  "For setAccessControlRecursive, the MODE option should be set "
    1031             :                  "to: 'set', 'modify' or 'remove'");
    1032           0 :         return false;
    1033             :     }
    1034             : 
    1035           8 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1036           8 :     NetworkStatisticsAction oContextAction("SetFileMetadata");
    1037             : 
    1038             :     bool bRetry;
    1039           4 :     bool bRet = false;
    1040             : 
    1041           8 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
    1042           8 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
    1043           4 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1044             : 
    1045           4 :     do
    1046             :     {
    1047           4 :         bRetry = false;
    1048           4 :         CURL *hCurlHandle = curl_easy_init();
    1049           8 :         poHandleHelper->AddQueryParameter(
    1050           4 :             "action", EQUAL(pszDomain, "PROPERTIES") ? "setProperties"
    1051           2 :                       : bRecursive ? "setAccessControlRecursive"
    1052             :                                    : "setAccessControl");
    1053           4 :         if (pszMode)
    1054             :         {
    1055           2 :             poHandleHelper->AddQueryParameter("mode",
    1056           2 :                                               CPLString(pszMode).tolower());
    1057             :         }
    1058           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PATCH");
    1059             : 
    1060             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1061           4 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1062             :                               aosHTTPOptions.List()));
    1063             : 
    1064           8 :         CPLStringList aosList;
    1065           8 :         for (CSLConstList papszIter = papszMetadata; papszIter && *papszIter;
    1066             :              ++papszIter)
    1067             :         {
    1068           4 :             char *pszKey = nullptr;
    1069           4 :             const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    1070           4 :             if (pszKey && pszValue)
    1071             :             {
    1072           4 :                 if ((EQUAL(pszDomain, "PROPERTIES") &&
    1073           2 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1074           2 :                       EQUAL(pszKey, "x-ms-cache-control") ||
    1075           2 :                       EQUAL(pszKey, "x-ms-content-type") ||
    1076           2 :                       EQUAL(pszKey, "x-ms-content-disposition") ||
    1077           2 :                       EQUAL(pszKey, "x-ms-content-encoding") ||
    1078           2 :                       EQUAL(pszKey, "x-ms-content-language") ||
    1079           2 :                       EQUAL(pszKey, "x-ms-content-md5") ||
    1080           2 :                       EQUAL(pszKey, "x-ms-properties") ||
    1081           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1082           0 :                       STARTS_WITH_CI(pszKey, "If-"))) ||
    1083           2 :                     (!EQUAL(pszDomain, "PROPERTIES") && !bRecursive &&
    1084           1 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1085           1 :                       EQUAL(pszKey, "x-ms-owner") ||
    1086           1 :                       EQUAL(pszKey, "x-ms-group") ||
    1087           1 :                       EQUAL(pszKey, "x-ms-permissions") ||
    1088           1 :                       EQUAL(pszKey, "x-ms-acl") ||
    1089           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1090           0 :                       STARTS_WITH_CI(pszKey, "If-"))) ||
    1091           1 :                     (!EQUAL(pszDomain, "PROPERTIES") && bRecursive &&
    1092           1 :                      (EQUAL(pszKey, "x-ms-lease-id") ||
    1093           1 :                       EQUAL(pszKey, "x-ms-acl") ||
    1094           0 :                       EQUAL(pszKey, "x-ms-client-request-id") ||
    1095           0 :                       STARTS_WITH_CI(pszKey, "If-"))))
    1096             :                 {
    1097             :                     const char *pszHeader =
    1098           4 :                         CPLSPrintf("%s: %s", pszKey, pszValue);
    1099           4 :                     aosList.AddString(pszHeader);
    1100           4 :                     headers = curl_slist_append(headers, pszHeader);
    1101             :                 }
    1102             :                 else
    1103             :                 {
    1104           0 :                     CPLDebug(GetDebugKey(), "Ignorizing metadata item %s",
    1105             :                              *papszIter);
    1106             :                 }
    1107             :             }
    1108           4 :             CPLFree(pszKey);
    1109             :         }
    1110             : 
    1111           4 :         headers = poHandleHelper->GetCurlHeaders("PATCH", headers);
    1112           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1113             : 
    1114           4 :         NetworkStatisticsLogger::LogPUT(0);
    1115             : 
    1116           8 :         CurlRequestHelper requestHelper;
    1117           4 :         const long response_code = requestHelper.perform(
    1118             :             hCurlHandle, headers, this, poHandleHelper.get());
    1119             : 
    1120           4 :         if (response_code != 200 && response_code != 202)
    1121             :         {
    1122             :             // Look if we should attempt a retry
    1123           1 :             if (oRetryContext.CanRetry(
    1124             :                     static_cast<int>(response_code),
    1125           1 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1126             :                     requestHelper.szCurlErrBuf))
    1127             :             {
    1128           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1129             :                          "HTTP error code: %d - %s. "
    1130             :                          "Retrying again in %.1f secs",
    1131             :                          static_cast<int>(response_code),
    1132           0 :                          poHandleHelper->GetURL().c_str(),
    1133             :                          oRetryContext.GetCurrentDelay());
    1134           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1135           0 :                 bRetry = true;
    1136             :             }
    1137             :             else
    1138             :             {
    1139           1 :                 CPLDebug(GetDebugKey(), "SetFileMetadata on %s failed: %s",
    1140             :                          pszFilename,
    1141           1 :                          requestHelper.sWriteFuncData.pBuffer
    1142             :                              ? requestHelper.sWriteFuncData.pBuffer
    1143             :                              : "(null)");
    1144             :             }
    1145             :         }
    1146             :         else
    1147             :         {
    1148           3 :             bRet = true;
    1149             :         }
    1150             : 
    1151           4 :         curl_easy_cleanup(hCurlHandle);
    1152             :     } while (bRetry);
    1153           4 :     return bRet;
    1154             : }
    1155             : 
    1156             : /************************************************************************/
    1157             : /*                       VSIADLSWriteHandle()                          */
    1158             : /************************************************************************/
    1159             : 
    1160           7 : VSIADLSWriteHandle::VSIADLSWriteHandle(VSIADLSFSHandler *poFS,
    1161             :                                        const char *pszFilename,
    1162           7 :                                        VSIAzureBlobHandleHelper *poHandleHelper)
    1163           7 :     : VSIAppendWriteHandle(poFS, poFS->GetFSPrefix().c_str(), pszFilename,
    1164             :                            GetAzureAppendBufferSize()),
    1165          14 :       m_poHandleHelper(poHandleHelper)
    1166             : {
    1167           7 : }
    1168             : 
    1169             : /************************************************************************/
    1170             : /*                      ~VSIADLSWriteHandle()                          */
    1171             : /************************************************************************/
    1172             : 
    1173          14 : VSIADLSWriteHandle::~VSIADLSWriteHandle()
    1174             : {
    1175           7 :     Close();
    1176          14 : }
    1177             : 
    1178             : /************************************************************************/
    1179             : /*                    InvalidateParentDirectory()                       */
    1180             : /************************************************************************/
    1181             : 
    1182           6 : void VSIADLSWriteHandle::InvalidateParentDirectory()
    1183             : {
    1184           6 :     m_poFS->InvalidateCachedData(m_poHandleHelper->GetURLNoKVP().c_str());
    1185             : 
    1186           6 :     const std::string osFilenameWithoutSlash(RemoveTrailingSlash(m_osFilename));
    1187           6 :     m_poFS->InvalidateDirContent(
    1188          12 :         CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
    1189           6 : }
    1190             : 
    1191             : /************************************************************************/
    1192             : /*                          CreateFile()                                */
    1193             : /************************************************************************/
    1194             : 
    1195           7 : bool VSIADLSWriteHandle::CreateFile(CSLConstList papszOptions)
    1196             : {
    1197           7 :     m_bCreated =
    1198           7 :         SendInternal(VSIADLSFSHandler::Event::CREATE_FILE, papszOptions);
    1199           7 :     return m_bCreated;
    1200             : }
    1201             : 
    1202             : /************************************************************************/
    1203             : /*                             Send()                                   */
    1204             : /************************************************************************/
    1205             : 
    1206           9 : bool VSIADLSWriteHandle::Send(bool bIsLastBlock)
    1207             : {
    1208           9 :     if (!m_bCreated)
    1209           1 :         return false;
    1210             :     // If we have a non-empty buffer, append it
    1211          15 :     if (m_nBufferOff != 0 &&
    1212           7 :         !SendInternal(VSIADLSFSHandler::Event::APPEND_DATA, nullptr))
    1213           1 :         return false;
    1214             :     // If we are the last block, send the flush event
    1215           7 :     if (bIsLastBlock && !SendInternal(VSIADLSFSHandler::Event::FLUSH, nullptr))
    1216           1 :         return false;
    1217             : 
    1218           6 :     InvalidateParentDirectory();
    1219             : 
    1220           6 :     return true;
    1221             : }
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                          SendInternal()                              */
    1225             : /************************************************************************/
    1226             : 
    1227          19 : bool VSIADLSWriteHandle::SendInternal(VSIADLSFSHandler::Event event,
    1228             :                                       CSLConstList papszOptions)
    1229             : {
    1230          38 :     return cpl::down_cast<VSIADLSFSHandler *>(m_poFS)->UploadFile(
    1231          19 :         m_osFilename, event,
    1232             :         event == VSIADLSFSHandler::Event::CREATE_FILE ? 0
    1233             :         : event == VSIADLSFSHandler::Event::APPEND_DATA
    1234          12 :             ? m_nCurOffset - m_nBufferOff
    1235             :             : m_nCurOffset,
    1236          19 :         m_pabyBuffer, m_nBufferOff, m_poHandleHelper.get(), m_oRetryParameters,
    1237          19 :         papszOptions);
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                            ClearCache()                              */
    1242             : /************************************************************************/
    1243             : 
    1244         331 : void VSIADLSFSHandler::ClearCache()
    1245             : {
    1246         331 :     IVSIS3LikeFSHandler::ClearCache();
    1247             : 
    1248         331 :     VSIAzureBlobHandleHelper::ClearCache();
    1249         331 : }
    1250             : 
    1251             : /************************************************************************/
    1252             : /*                          GetURLFromFilename()                        */
    1253             : /************************************************************************/
    1254             : 
    1255             : std::string
    1256          17 : VSIADLSFSHandler::GetURLFromFilename(const std::string &osFilename) const
    1257             : {
    1258             :     const std::string osFilenameWithoutPrefix =
    1259          34 :         osFilename.substr(GetFSPrefix().size());
    1260             :     auto poHandleHelper = std::unique_ptr<VSIAzureBlobHandleHelper>(
    1261             :         VSIAzureBlobHandleHelper::BuildFromURI(osFilenameWithoutPrefix.c_str(),
    1262          34 :                                                GetFSPrefix().c_str()));
    1263          17 :     if (!poHandleHelper)
    1264           0 :         return std::string();
    1265          17 :     return poHandleHelper->GetURLNoKVP();
    1266             : }
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                          CreateHandleHelper()                        */
    1270             : /************************************************************************/
    1271             : 
    1272          39 : IVSIS3LikeHandleHelper *VSIADLSFSHandler::CreateHandleHelper(const char *pszURI,
    1273             :                                                              bool)
    1274             : {
    1275          39 :     return VSIAzureBlobHandleHelper::BuildFromURI(pszURI,
    1276          78 :                                                   GetFSPrefix().c_str());
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                               Rename()                               */
    1281             : /************************************************************************/
    1282             : 
    1283           1 : int VSIADLSFSHandler::Rename(const char *oldpath, const char *newpath,
    1284             :                              GDALProgressFunc, void *)
    1285             : {
    1286           1 :     if (!STARTS_WITH_CI(oldpath, GetFSPrefix().c_str()))
    1287           0 :         return -1;
    1288           1 :     if (!STARTS_WITH_CI(newpath, GetFSPrefix().c_str()))
    1289           0 :         return -1;
    1290             : 
    1291           2 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1292           2 :     NetworkStatisticsAction oContextAction("Rename");
    1293             : 
    1294             :     VSIStatBufL sStat;
    1295           1 :     if (VSIStatL(oldpath, &sStat) != 0)
    1296             :     {
    1297           0 :         CPLDebug(GetDebugKey(), "%s is not a object", oldpath);
    1298           0 :         errno = ENOENT;
    1299           0 :         return -1;
    1300             :     }
    1301             : 
    1302             :     // POSIX says renaming on the same file is OK
    1303           1 :     if (strcmp(oldpath, newpath) == 0)
    1304           0 :         return 0;
    1305             : 
    1306             :     auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1307           2 :         CreateHandleHelper(newpath + GetFSPrefix().size(), false));
    1308           1 :     if (poHandleHelper == nullptr)
    1309             :     {
    1310           0 :         return -1;
    1311             :     }
    1312             : 
    1313           2 :     std::string osContinuation;
    1314           1 :     int nRet = 0;
    1315             :     bool bRetry;
    1316             : 
    1317           1 :     InvalidateCachedData(GetURLFromFilename(oldpath).c_str());
    1318           1 :     InvalidateCachedData(GetURLFromFilename(newpath).c_str());
    1319           1 :     InvalidateDirContent(CPLGetDirnameSafe(oldpath));
    1320             : 
    1321           2 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(oldpath));
    1322           2 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
    1323           1 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1324             : 
    1325           1 :     do
    1326             :     {
    1327           1 :         bRetry = false;
    1328             : 
    1329           1 :         CURL *hCurlHandle = curl_easy_init();
    1330           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1331             : 
    1332           1 :         poHandleHelper->ResetQueryParameters();
    1333           1 :         if (!osContinuation.empty())
    1334           0 :             poHandleHelper->AddQueryParameter("continuation", osContinuation);
    1335             : 
    1336             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1337           1 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1338             :                               aosHTTPOptions.List()));
    1339           1 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1340           2 :         std::string osRenameSource("x-ms-rename-source: /");
    1341             :         osRenameSource +=
    1342           1 :             CPLAWSURLEncode(oldpath + GetFSPrefix().size(), false);
    1343           1 :         headers = curl_slist_append(headers, osRenameSource.c_str());
    1344           1 :         headers = poHandleHelper->GetCurlHeaders("PUT", headers);
    1345           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1346             : 
    1347           2 :         CurlRequestHelper requestHelper;
    1348           1 :         const long response_code = requestHelper.perform(
    1349             :             hCurlHandle, headers, this, poHandleHelper.get());
    1350             : 
    1351           1 :         NetworkStatisticsLogger::LogPUT(0);
    1352             : 
    1353           1 :         if (response_code != 201)
    1354             :         {
    1355             :             // Look if we should attempt a retry
    1356           0 :             if (oRetryContext.CanRetry(
    1357             :                     static_cast<int>(response_code),
    1358           0 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1359             :                     requestHelper.szCurlErrBuf))
    1360             :             {
    1361           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1362             :                          "HTTP error code: %d - %s. "
    1363             :                          "Retrying again in %.1f secs",
    1364             :                          static_cast<int>(response_code),
    1365           0 :                          poHandleHelper->GetURL().c_str(),
    1366             :                          oRetryContext.GetCurrentDelay());
    1367           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1368           0 :                 bRetry = true;
    1369             :             }
    1370             :             else
    1371             :             {
    1372           0 :                 CPLDebug(GetDebugKey(), "Renaming of %s failed: %s", oldpath,
    1373           0 :                          requestHelper.sWriteFuncData.pBuffer
    1374             :                              ? requestHelper.sWriteFuncData.pBuffer
    1375             :                              : "(null)");
    1376           0 :                 nRet = -1;
    1377             :             }
    1378             :         }
    1379             :         else
    1380             :         {
    1381             :             // Get continuation token for response headers
    1382           1 :             osContinuation = GetContinuationToken(
    1383           1 :                 requestHelper.sWriteFuncHeaderData.pBuffer);
    1384           1 :             if (!osContinuation.empty())
    1385             :             {
    1386           0 :                 oRetryContext.ResetCounter();
    1387           0 :                 bRetry = true;
    1388             :             }
    1389             :         }
    1390             : 
    1391           1 :         curl_easy_cleanup(hCurlHandle);
    1392             :     } while (bRetry);
    1393             : 
    1394           1 :     return nRet;
    1395             : }
    1396             : 
    1397             : /************************************************************************/
    1398             : /*                               Unlink()                               */
    1399             : /************************************************************************/
    1400             : 
    1401           2 : int VSIADLSFSHandler::Unlink(const char *pszFilename)
    1402             : {
    1403           2 :     return IVSIS3LikeFSHandler::Unlink(pszFilename);
    1404             : }
    1405             : 
    1406             : /************************************************************************/
    1407             : /*                               Mkdir()                                */
    1408             : /************************************************************************/
    1409             : 
    1410           3 : int VSIADLSFSHandler::MkdirInternal(const char *pszDirname, long nMode,
    1411             :                                     bool bDoStatCheck)
    1412             : {
    1413           3 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1414           1 :         return -1;
    1415             : 
    1416           4 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1417           4 :     NetworkStatisticsAction oContextAction("Mkdir");
    1418             : 
    1419           4 :     const std::string osDirname(pszDirname);
    1420             : 
    1421           2 :     if (bDoStatCheck)
    1422             :     {
    1423             :         VSIStatBufL sStat;
    1424           2 :         if (VSIStatL(osDirname.c_str(), &sStat) == 0)
    1425             :         {
    1426           1 :             CPLDebug(GetDebugKey(), "Directory or file %s already exists",
    1427             :                      osDirname.c_str());
    1428           1 :             errno = EEXIST;
    1429           1 :             return -1;
    1430             :         }
    1431             :     }
    1432             : 
    1433           2 :     const std::string osDirnameWithoutEndSlash(RemoveTrailingSlash(osDirname));
    1434             :     auto poHandleHelper =
    1435             :         std::unique_ptr<IVSIS3LikeHandleHelper>(CreateHandleHelper(
    1436           2 :             osDirnameWithoutEndSlash.c_str() + GetFSPrefix().size(), false));
    1437           1 :     if (poHandleHelper == nullptr)
    1438             :     {
    1439           0 :         return -1;
    1440             :     }
    1441             : 
    1442           1 :     InvalidateCachedData(GetURLFromFilename(osDirname.c_str()).c_str());
    1443           1 :     InvalidateCachedData(
    1444           2 :         GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str());
    1445           1 :     InvalidateDirContent(CPLGetDirnameSafe(osDirnameWithoutEndSlash.c_str()));
    1446             : 
    1447           1 :     int nRet = 0;
    1448             : 
    1449             :     bool bRetry;
    1450             : 
    1451           2 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
    1452           2 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
    1453           1 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1454             : 
    1455           1 :     do
    1456             :     {
    1457           1 :         bRetry = false;
    1458           1 :         CURL *hCurlHandle = curl_easy_init();
    1459           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1460             : 
    1461           1 :         poHandleHelper->ResetQueryParameters();
    1462           2 :         poHandleHelper->AddQueryParameter(
    1463           1 :             "resource", osDirnameWithoutEndSlash.find(
    1464           2 :                             '/', GetFSPrefix().size()) == std::string::npos
    1465             :                             ? "filesystem"
    1466             :                             : "directory");
    1467             : 
    1468             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1469           1 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1470             :                               aosHTTPOptions.List()));
    1471           1 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1472           2 :         CPLString osPermissions;  // keep in this scope
    1473           1 :         if ((nMode & 0777) != 0)
    1474             :         {
    1475             :             osPermissions.Printf("x-ms-permissions: 0%03o",
    1476           0 :                                  static_cast<int>(nMode));
    1477           0 :             headers = curl_slist_append(headers, osPermissions.c_str());
    1478             :         }
    1479           1 :         if (bDoStatCheck)
    1480             :         {
    1481           1 :             headers = curl_slist_append(headers, "If-None-Match: \"*\"");
    1482             :         }
    1483             : 
    1484           1 :         headers = poHandleHelper->GetCurlHeaders("PUT", headers);
    1485           1 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1486             : 
    1487           2 :         CurlRequestHelper requestHelper;
    1488           1 :         const long response_code = requestHelper.perform(
    1489             :             hCurlHandle, headers, this, poHandleHelper.get());
    1490             : 
    1491           1 :         NetworkStatisticsLogger::LogPUT(0);
    1492             : 
    1493           1 :         if (response_code != 201)
    1494             :         {
    1495             :             // Look if we should attempt a retry
    1496           0 :             if (oRetryContext.CanRetry(
    1497             :                     static_cast<int>(response_code),
    1498           0 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1499             :                     requestHelper.szCurlErrBuf))
    1500             :             {
    1501           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1502             :                          "HTTP error code: %d - %s. "
    1503             :                          "Retrying again in %.1f secs",
    1504             :                          static_cast<int>(response_code),
    1505           0 :                          poHandleHelper->GetURL().c_str(),
    1506             :                          oRetryContext.GetCurrentDelay());
    1507           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1508           0 :                 bRetry = true;
    1509             :             }
    1510             :             else
    1511             :             {
    1512           0 :                 CPLDebug(GetDebugKey(), "Creation of %s failed: %s",
    1513             :                          osDirname.c_str(),
    1514           0 :                          requestHelper.sWriteFuncData.pBuffer
    1515             :                              ? requestHelper.sWriteFuncData.pBuffer
    1516             :                              : "(null)");
    1517           0 :                 nRet = -1;
    1518             :             }
    1519             :         }
    1520             : 
    1521           1 :         curl_easy_cleanup(hCurlHandle);
    1522             :     } while (bRetry);
    1523             : 
    1524           1 :     return nRet;
    1525             : }
    1526             : 
    1527           3 : int VSIADLSFSHandler::Mkdir(const char *pszDirname, long nMode)
    1528             : {
    1529           3 :     return MkdirInternal(pszDirname, nMode, true);
    1530             : }
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                          RmdirInternal()                             */
    1534             : /************************************************************************/
    1535             : 
    1536           4 : int VSIADLSFSHandler::RmdirInternal(const char *pszDirname, bool bRecursive)
    1537             : {
    1538           8 :     const std::string osDirname(pszDirname);
    1539             :     const std::string osDirnameWithoutEndSlash(
    1540          12 :         RemoveTrailingSlash(osDirname.c_str()));
    1541             : 
    1542             :     const bool bIsFileSystem =
    1543           4 :         osDirnameWithoutEndSlash.find('/', GetFSPrefix().size()) ==
    1544           4 :         std::string::npos;
    1545             : 
    1546           4 :     if (!bRecursive && bIsFileSystem)
    1547             :     {
    1548             :         // List content, to confirm it is empty first, as filesystem deletion
    1549             :         // is recursive by default.
    1550           0 :         bool bGotFileList = false;
    1551           0 :         CSLDestroy(GetFileList(osDirnameWithoutEndSlash.c_str(), 1, false,
    1552             :                                &bGotFileList));
    1553           0 :         if (bGotFileList)
    1554             :         {
    1555           0 :             CPLDebug(GetDebugKey(), "Cannot delete filesystem with "
    1556             :                                     "non-recursive method as it is not empty");
    1557           0 :             errno = ENOTEMPTY;
    1558           0 :             return -1;
    1559             :         }
    1560             :     }
    1561             : 
    1562           4 :     if (!bIsFileSystem)
    1563             :     {
    1564             :         VSIStatBufL sStat;
    1565           4 :         if (VSIStatL(osDirname.c_str(), &sStat) != 0)
    1566             :         {
    1567           1 :             CPLDebug(GetDebugKey(), "Object %s does not exist",
    1568             :                      osDirname.c_str());
    1569           1 :             errno = ENOENT;
    1570           2 :             return -1;
    1571             :         }
    1572           3 :         if (!VSI_ISDIR(sStat.st_mode))
    1573             :         {
    1574           1 :             CPLDebug(GetDebugKey(), "Object %s is not a directory",
    1575             :                      osDirname.c_str());
    1576           1 :             errno = ENOTDIR;
    1577           1 :             return -1;
    1578             :         }
    1579             :     }
    1580             : 
    1581             :     auto poHandleHelper =
    1582             :         std::unique_ptr<IVSIS3LikeHandleHelper>(CreateHandleHelper(
    1583           4 :             osDirnameWithoutEndSlash.c_str() + GetFSPrefix().size(), false));
    1584           2 :     if (poHandleHelper == nullptr)
    1585             :     {
    1586           0 :         return -1;
    1587             :     }
    1588             : 
    1589           2 :     InvalidateCachedData(GetURLFromFilename(osDirname.c_str()).c_str());
    1590           2 :     InvalidateCachedData(
    1591           4 :         GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str());
    1592           2 :     InvalidateDirContent(CPLGetDirnameSafe(osDirnameWithoutEndSlash.c_str()));
    1593           2 :     if (bRecursive)
    1594             :     {
    1595           1 :         PartialClearCache(osDirnameWithoutEndSlash.c_str());
    1596             :     }
    1597             : 
    1598           4 :     std::string osContinuation;
    1599           2 :     int nRet = 0;
    1600             :     bool bRetry;
    1601             : 
    1602           4 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
    1603           4 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
    1604           2 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1605             : 
    1606           2 :     do
    1607             :     {
    1608           2 :         bRetry = false;
    1609           2 :         CURL *hCurlHandle = curl_easy_init();
    1610           2 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST,
    1611             :                                    "DELETE");
    1612             : 
    1613           2 :         poHandleHelper->ResetQueryParameters();
    1614           2 :         if (bIsFileSystem)
    1615             :         {
    1616           0 :             poHandleHelper->AddQueryParameter("resource", "filesystem");
    1617             :         }
    1618             :         else
    1619             :         {
    1620           2 :             poHandleHelper->AddQueryParameter("recursive",
    1621             :                                               bRecursive ? "true" : "false");
    1622           2 :             if (!osContinuation.empty())
    1623           0 :                 poHandleHelper->AddQueryParameter("continuation",
    1624             :                                                   osContinuation);
    1625             :         }
    1626             : 
    1627             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1628           2 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1629             :                               aosHTTPOptions.List()));
    1630           2 :         headers = poHandleHelper->GetCurlHeaders("DELETE", headers);
    1631             : 
    1632           4 :         CurlRequestHelper requestHelper;
    1633           2 :         const long response_code = requestHelper.perform(
    1634             :             hCurlHandle, headers, this, poHandleHelper.get());
    1635             : 
    1636           2 :         NetworkStatisticsLogger::LogDELETE();
    1637             : 
    1638             :         // 200 for path deletion
    1639             :         // 202 for filesystem deletion
    1640           2 :         if (response_code != 200 && response_code != 202)
    1641             :         {
    1642             :             // Look if we should attempt a retry
    1643           0 :             if (oRetryContext.CanRetry(
    1644             :                     static_cast<int>(response_code),
    1645           0 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1646             :                     requestHelper.szCurlErrBuf))
    1647             :             {
    1648           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1649             :                          "HTTP error code: %d - %s. "
    1650             :                          "Retrying again in %.1f secs",
    1651             :                          static_cast<int>(response_code),
    1652           0 :                          poHandleHelper->GetURL().c_str(),
    1653             :                          oRetryContext.GetCurrentDelay());
    1654           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1655           0 :                 bRetry = true;
    1656             :             }
    1657             :             else
    1658             :             {
    1659           0 :                 CPLDebug(GetDebugKey(), "Delete of %s failed: %s",
    1660             :                          osDirname.c_str(),
    1661           0 :                          requestHelper.sWriteFuncData.pBuffer
    1662             :                              ? requestHelper.sWriteFuncData.pBuffer
    1663             :                              : "(null)");
    1664           0 :                 if (requestHelper.sWriteFuncData.pBuffer != nullptr)
    1665             :                 {
    1666           0 :                     VSIError(VSIE_ObjectStorageGenericError, "%s",
    1667             :                              requestHelper.sWriteFuncData.pBuffer);
    1668           0 :                     if (strstr(requestHelper.sWriteFuncData.pBuffer,
    1669             :                                "PathNotFound"))
    1670             :                     {
    1671           0 :                         errno = ENOENT;
    1672             :                     }
    1673           0 :                     else if (strstr(requestHelper.sWriteFuncData.pBuffer,
    1674             :                                     "DirectoryNotEmpty"))
    1675             :                     {
    1676           0 :                         errno = ENOTEMPTY;
    1677             :                     }
    1678             :                 }
    1679           0 :                 nRet = -1;
    1680             :             }
    1681             :         }
    1682             :         else
    1683             :         {
    1684             :             // Get continuation token for response headers
    1685           2 :             osContinuation = GetContinuationToken(
    1686           2 :                 requestHelper.sWriteFuncHeaderData.pBuffer);
    1687           2 :             if (!osContinuation.empty())
    1688             :             {
    1689           0 :                 oRetryContext.ResetCounter();
    1690           0 :                 bRetry = true;
    1691             :             }
    1692             :         }
    1693             : 
    1694           2 :         curl_easy_cleanup(hCurlHandle);
    1695             :     } while (bRetry);
    1696             : 
    1697           2 :     return nRet;
    1698             : }
    1699             : 
    1700             : /************************************************************************/
    1701             : /*                               Rmdir()                                */
    1702             : /************************************************************************/
    1703             : 
    1704           4 : int VSIADLSFSHandler::Rmdir(const char *pszDirname)
    1705             : {
    1706           4 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1707           1 :         return -1;
    1708             : 
    1709           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1710           6 :     NetworkStatisticsAction oContextAction("Rmdir");
    1711             : 
    1712           3 :     return RmdirInternal(pszDirname, false);
    1713             : }
    1714             : 
    1715             : /************************************************************************/
    1716             : /*                          RmdirRecursive()                            */
    1717             : /************************************************************************/
    1718             : 
    1719           1 : int VSIADLSFSHandler::RmdirRecursive(const char *pszDirname)
    1720             : {
    1721           1 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
    1722           0 :         return -1;
    1723             : 
    1724           2 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1725           2 :     NetworkStatisticsAction oContextAction("RmdirRecursive");
    1726             : 
    1727           1 :     return RmdirInternal(pszDirname, true);
    1728             : }
    1729             : 
    1730             : /************************************************************************/
    1731             : /*                            CopyObject()                              */
    1732             : /************************************************************************/
    1733             : 
    1734           4 : int VSIADLSFSHandler::CopyObject(const char *oldpath, const char *newpath,
    1735             :                                  CSLConstList /* papszMetadata */)
    1736             : {
    1737             :     // There is no CopyObject in ADLS... So use the base Azure blob one...
    1738             : 
    1739           8 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1740           8 :     NetworkStatisticsAction oContextAction("CopyObject");
    1741             : 
    1742          12 :     std::string osTargetNameWithoutPrefix = newpath + GetFSPrefix().size();
    1743             :     auto poAzHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1744           4 :         VSIAzureBlobHandleHelper::BuildFromURI(
    1745           8 :             osTargetNameWithoutPrefix.c_str(), "/vsiaz/"));
    1746           4 :     if (poAzHandleHelper == nullptr)
    1747             :     {
    1748           0 :         return -1;
    1749             :     }
    1750             : 
    1751          12 :     std::string osSourceNameWithoutPrefix = oldpath + GetFSPrefix().size();
    1752             :     auto poAzHandleHelperSource = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1753           4 :         VSIAzureBlobHandleHelper::BuildFromURI(
    1754           8 :             osSourceNameWithoutPrefix.c_str(), "/vsiaz/"));
    1755           4 :     if (poAzHandleHelperSource == nullptr)
    1756             :     {
    1757           0 :         return -1;
    1758             :     }
    1759             : 
    1760           8 :     std::string osSourceHeader("x-ms-copy-source: ");
    1761           4 :     osSourceHeader += poAzHandleHelperSource->GetURLNoKVP();
    1762             : 
    1763           4 :     int nRet = 0;
    1764             : 
    1765             :     bool bRetry;
    1766             : 
    1767           8 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(oldpath));
    1768           8 :     const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
    1769           4 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1770             : 
    1771           4 :     do
    1772             :     {
    1773           4 :         bRetry = false;
    1774           4 :         CURL *hCurlHandle = curl_easy_init();
    1775           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
    1776             : 
    1777             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1778           4 :             CPLHTTPSetOptions(hCurlHandle, poAzHandleHelper->GetURL().c_str(),
    1779             :                               aosHTTPOptions.List()));
    1780           4 :         headers = curl_slist_append(headers, osSourceHeader.c_str());
    1781           4 :         headers = curl_slist_append(headers, "Content-Length: 0");
    1782           4 :         headers = VSICurlSetContentTypeFromExt(headers, newpath);
    1783           4 :         headers = poAzHandleHelper->GetCurlHeaders("PUT", headers);
    1784           4 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1785             : 
    1786           8 :         CurlRequestHelper requestHelper;
    1787           4 :         const long response_code = requestHelper.perform(
    1788             :             hCurlHandle, headers, this, poAzHandleHelper.get());
    1789             : 
    1790           4 :         NetworkStatisticsLogger::LogPUT(0);
    1791             : 
    1792           4 :         if (response_code != 202)
    1793             :         {
    1794             :             // Look if we should attempt a retry
    1795           1 :             if (oRetryContext.CanRetry(
    1796             :                     static_cast<int>(response_code),
    1797           1 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1798             :                     requestHelper.szCurlErrBuf))
    1799             :             {
    1800           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1801             :                          "HTTP error code: %d - %s. "
    1802             :                          "Retrying again in %.1f secs",
    1803             :                          static_cast<int>(response_code),
    1804           0 :                          poAzHandleHelper->GetURL().c_str(),
    1805             :                          oRetryContext.GetCurrentDelay());
    1806           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1807           0 :                 bRetry = true;
    1808             :             }
    1809             :             else
    1810             :             {
    1811           1 :                 CPLDebug(GetDebugKey(), "%s",
    1812           1 :                          requestHelper.sWriteFuncData.pBuffer
    1813             :                              ? requestHelper.sWriteFuncData.pBuffer
    1814             :                              : "(null)");
    1815           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Copy of %s to %s failed",
    1816             :                          oldpath, newpath);
    1817           1 :                 nRet = -1;
    1818             :             }
    1819             :         }
    1820             :         else
    1821             :         {
    1822             :             auto poADLSHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
    1823           3 :                 VSIAzureBlobHandleHelper::BuildFromURI(
    1824           9 :                     osTargetNameWithoutPrefix.c_str(), GetFSPrefix().c_str()));
    1825           3 :             if (poADLSHandleHelper != nullptr)
    1826           3 :                 InvalidateCachedData(poADLSHandleHelper->GetURLNoKVP().c_str());
    1827             : 
    1828             :             const std::string osFilenameWithoutSlash(
    1829           6 :                 RemoveTrailingSlash(newpath));
    1830           3 :             InvalidateDirContent(
    1831           6 :                 CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
    1832             :         }
    1833             : 
    1834           4 :         curl_easy_cleanup(hCurlHandle);
    1835             :     } while (bRetry);
    1836             : 
    1837           4 :     return nRet;
    1838             : }
    1839             : 
    1840             : /************************************************************************/
    1841             : /*                          UploadFile()                                */
    1842             : /************************************************************************/
    1843             : 
    1844          25 : bool VSIADLSFSHandler::UploadFile(
    1845             :     const std::string &osFilename, Event event, vsi_l_offset nPosition,
    1846             :     const void *pabyBuffer, size_t nBufferSize,
    1847             :     IVSIS3LikeHandleHelper *poHandleHelper,
    1848             :     const CPLHTTPRetryParameters &oRetryParameters, CSLConstList papszOptions)
    1849             : {
    1850          50 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    1851          50 :     NetworkStatisticsFile oContextFile(osFilename.c_str());
    1852          50 :     NetworkStatisticsAction oContextAction("UploadFile");
    1853             : 
    1854          25 :     if (event == Event::CREATE_FILE)
    1855             :     {
    1856           8 :         InvalidateCachedData(poHandleHelper->GetURLNoKVP().c_str());
    1857           8 :         InvalidateDirContent(CPLGetDirnameSafe(osFilename.c_str()));
    1858             :     }
    1859             : 
    1860             :     const CPLStringList aosHTTPOptions(
    1861          50 :         CPLHTTPGetOptionsFromEnv(osFilename.c_str()));
    1862             : 
    1863          25 :     bool bSuccess = true;
    1864          25 :     CPLHTTPRetryContext oRetryContext(oRetryParameters);
    1865             :     bool bRetry;
    1866          25 :     do
    1867             :     {
    1868          25 :         bRetry = false;
    1869             : 
    1870          25 :         CURL *hCurlHandle = curl_easy_init();
    1871             : 
    1872          25 :         poHandleHelper->ResetQueryParameters();
    1873          25 :         if (event == Event::CREATE_FILE)
    1874             :         {
    1875             :             // Cf https://learn.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/create?view=rest-storageservices-datalakestoragegen2-2019-12-12
    1876           8 :             poHandleHelper->AddQueryParameter("resource", "file");
    1877             :         }
    1878          17 :         else if (event == Event::APPEND_DATA)
    1879             :         {
    1880             :             // Cf https://learn.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/update?view=rest-storageservices-datalakestoragegen2-2019-12-12
    1881          10 :             poHandleHelper->AddQueryParameter("action", "append");
    1882          10 :             poHandleHelper->AddQueryParameter(
    1883             :                 "position",
    1884             :                 CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosition)));
    1885             :         }
    1886             :         else
    1887             :         {
    1888             :             // Cf https://learn.microsoft.com/en-us/rest/api/storageservices/datalakestoragegen2/path/update?view=rest-storageservices-datalakestoragegen2-2019-12-12
    1889           7 :             poHandleHelper->AddQueryParameter("action", "flush");
    1890           7 :             poHandleHelper->AddQueryParameter("close", "true");
    1891           7 :             poHandleHelper->AddQueryParameter(
    1892             :                 "position",
    1893             :                 CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nPosition)));
    1894             :         }
    1895             : 
    1896          25 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_UPLOAD, 1L);
    1897          25 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_READFUNCTION,
    1898             :                                    PutData::ReadCallBackBuffer);
    1899          25 :         PutData putData;
    1900          25 :         putData.pabyData = static_cast<const GByte *>(pabyBuffer);
    1901          25 :         putData.nOff = 0;
    1902          25 :         putData.nTotalSize = nBufferSize;
    1903          25 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_READDATA, &putData);
    1904             : 
    1905             :         struct curl_slist *headers = static_cast<struct curl_slist *>(
    1906          25 :             CPLHTTPSetOptions(hCurlHandle, poHandleHelper->GetURL().c_str(),
    1907             :                               aosHTTPOptions.List()));
    1908          25 :         headers = VSICurlSetCreationHeadersFromOptions(headers, papszOptions,
    1909             :                                                        osFilename.c_str());
    1910             : 
    1911          50 :         CPLString osContentLength;  // leave it in this scope
    1912             : 
    1913          25 :         if (event == Event::APPEND_DATA)
    1914             :         {
    1915          10 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE,
    1916             :                                        static_cast<int>(nBufferSize));
    1917             :             // Disable "Expect: 100-continue" which doesn't hurt, but is not
    1918             :             // needed
    1919          10 :             headers = curl_slist_append(headers, "Expect:");
    1920             :             osContentLength.Printf("Content-Length: %d",
    1921          10 :                                    static_cast<int>(nBufferSize));
    1922          10 :             headers = curl_slist_append(headers, osContentLength.c_str());
    1923             :         }
    1924             :         else
    1925             :         {
    1926          15 :             unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE, 0);
    1927          15 :             headers = curl_slist_append(headers, "Content-Length: 0");
    1928             :         }
    1929             : 
    1930          25 :         const char *pszVerb = (event == Event::CREATE_FILE) ? "PUT" : "PATCH";
    1931          25 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, pszVerb);
    1932          25 :         headers = poHandleHelper->GetCurlHeaders(pszVerb, headers);
    1933          25 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1934             : 
    1935          50 :         CurlRequestHelper requestHelper;
    1936             :         const long response_code =
    1937          25 :             requestHelper.perform(hCurlHandle, headers, this, poHandleHelper);
    1938             : 
    1939          25 :         NetworkStatisticsLogger::LogPUT(
    1940             :             event == Event::APPEND_DATA ? nBufferSize : 0);
    1941             : 
    1942             :         // 200 for PATCH flush
    1943             :         // 201 for PUT create
    1944             :         // 202 for PATCH append
    1945          25 :         if (response_code != 200 && response_code != 201 &&
    1946             :             response_code != 202)
    1947             :         {
    1948             :             // Look if we should attempt a retry
    1949           3 :             if (oRetryContext.CanRetry(
    1950             :                     static_cast<int>(response_code),
    1951           3 :                     requestHelper.sWriteFuncHeaderData.pBuffer,
    1952             :                     requestHelper.szCurlErrBuf))
    1953             :             {
    1954           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1955             :                          "HTTP error code: %d - %s. "
    1956             :                          "Retrying again in %.1f secs",
    1957             :                          static_cast<int>(response_code),
    1958           0 :                          poHandleHelper->GetURL().c_str(),
    1959             :                          oRetryContext.GetCurrentDelay());
    1960           0 :                 CPLSleep(oRetryContext.GetCurrentDelay());
    1961           0 :                 bRetry = true;
    1962             :             }
    1963             :             else
    1964             :             {
    1965           3 :                 CPLDebug(GetDebugKey(), "%s of %s failed: %s", pszVerb,
    1966             :                          osFilename.c_str(),
    1967           3 :                          requestHelper.sWriteFuncData.pBuffer
    1968             :                              ? requestHelper.sWriteFuncData.pBuffer
    1969             :                              : "(null)");
    1970           3 :                 bSuccess = false;
    1971             :             }
    1972             :         }
    1973             : 
    1974          25 :         curl_easy_cleanup(hCurlHandle);
    1975             :     } while (bRetry);
    1976             : 
    1977          50 :     return bSuccess;
    1978             : }
    1979             : 
    1980             : /************************************************************************/
    1981             : /*                           GetFileList()                              */
    1982             : /************************************************************************/
    1983             : 
    1984           4 : char **VSIADLSFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
    1985             :                                      bool *pbGotFileList)
    1986             : {
    1987           4 :     return GetFileList(pszDirname, nMaxFiles, true, pbGotFileList);
    1988             : }
    1989             : 
    1990           4 : char **VSIADLSFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
    1991             :                                      bool bCacheEntries, bool *pbGotFileList)
    1992             : {
    1993             :     if (ENABLE_DEBUG)
    1994             :         CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
    1995             : 
    1996           4 :     *pbGotFileList = false;
    1997             : 
    1998             :     char **papszOptions =
    1999           4 :         CSLSetNameValue(nullptr, "MAXFILES", CPLSPrintf("%d", nMaxFiles));
    2000           4 :     papszOptions = CSLSetNameValue(papszOptions, "CACHE_ENTRIES",
    2001             :                                    bCacheEntries ? "YES" : "NO");
    2002           4 :     auto dir = OpenDir(pszDirname, 0, papszOptions);
    2003           4 :     CSLDestroy(papszOptions);
    2004           4 :     if (!dir)
    2005             :     {
    2006           1 :         return nullptr;
    2007             :     }
    2008           6 :     CPLStringList aosFileList;
    2009             :     while (true)
    2010             :     {
    2011           8 :         auto entry = dir->NextDirEntry();
    2012           8 :         if (!entry)
    2013             :         {
    2014           3 :             break;
    2015             :         }
    2016           5 :         aosFileList.AddString(entry->pszName);
    2017             : 
    2018           5 :         if (nMaxFiles > 0 && aosFileList.size() >= nMaxFiles)
    2019           0 :             break;
    2020           5 :     }
    2021           3 :     delete dir;
    2022           3 :     *pbGotFileList = true;
    2023           3 :     return aosFileList.StealList();
    2024             : }
    2025             : 
    2026             : /************************************************************************/
    2027             : /*                           GetOptions()                               */
    2028             : /************************************************************************/
    2029             : 
    2030           1 : const char *VSIADLSFSHandler::GetOptions()
    2031             : {
    2032             :     static std::string osOptions(
    2033           2 :         std::string("<Options>") +
    2034             :         "  <Option name='AZURE_STORAGE_CONNECTION_STRING' type='string' "
    2035             :         "description='Connection string that contains account name and "
    2036             :         "secret key'/>"
    2037             :         "  <Option name='AZURE_STORAGE_ACCOUNT' type='string' "
    2038             :         "description='Storage account. To use with AZURE_STORAGE_ACCESS_KEY'/>"
    2039             :         "  <Option name='AZURE_STORAGE_ACCESS_KEY' type='string' "
    2040             :         "description='Secret key'/>"
    2041             :         "  <Option name='VSIAZ_CHUNK_SIZE' type='int' "
    2042             :         "description='Size in MB for chunks of files that are uploaded' "
    2043           3 :         "default='4' min='1' max='4'/>" +
    2044           2 :         VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
    2045           1 :     return osOptions.c_str();
    2046             : }
    2047             : 
    2048             : /************************************************************************/
    2049             : /*                           GetSignedURL()                             */
    2050             : /************************************************************************/
    2051             : 
    2052           2 : char *VSIADLSFSHandler::GetSignedURL(const char *pszFilename,
    2053             :                                      CSLConstList papszOptions)
    2054             : {
    2055           2 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
    2056           0 :         return nullptr;
    2057             : 
    2058             :     auto poHandleHelper = std::unique_ptr<VSIAzureBlobHandleHelper>(
    2059             :         VSIAzureBlobHandleHelper::BuildFromURI(pszFilename +
    2060           2 :                                                    GetFSPrefix().size(),
    2061             :                                                "/vsiaz/",  // use Azure blob
    2062           4 :                                                nullptr, papszOptions));
    2063           2 :     if (poHandleHelper == nullptr)
    2064             :     {
    2065           1 :         return nullptr;
    2066             :     }
    2067             : 
    2068           2 :     std::string osRet(poHandleHelper->GetSignedURL(papszOptions));
    2069             : 
    2070           1 :     return CPLStrdup(osRet.c_str());
    2071             : }
    2072             : 
    2073             : /************************************************************************/
    2074             : /*                            OpenDir()                                 */
    2075             : /************************************************************************/
    2076             : 
    2077          10 : VSIDIR *VSIADLSFSHandler::OpenDir(const char *pszPath, int nRecurseDepth,
    2078             :                                   const char *const *papszOptions)
    2079             : {
    2080          10 :     if (nRecurseDepth > 0)
    2081             :     {
    2082           0 :         return VSIFilesystemHandler::OpenDir(pszPath, nRecurseDepth,
    2083           0 :                                              papszOptions);
    2084             :     }
    2085             : 
    2086          10 :     if (!STARTS_WITH_CI(pszPath, GetFSPrefix().c_str()))
    2087           0 :         return nullptr;
    2088             : 
    2089          20 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
    2090          20 :     NetworkStatisticsAction oContextAction("OpenDir");
    2091             : 
    2092             :     const std::string osDirnameWithoutPrefix =
    2093          30 :         RemoveTrailingSlash(pszPath + GetFSPrefix().size());
    2094          20 :     std::string osFilesystem(osDirnameWithoutPrefix);
    2095          20 :     std::string osObjectKey;
    2096          10 :     size_t nSlashPos = osDirnameWithoutPrefix.find('/');
    2097          10 :     if (nSlashPos != std::string::npos)
    2098             :     {
    2099           3 :         osFilesystem = osDirnameWithoutPrefix.substr(0, nSlashPos);
    2100           3 :         osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
    2101             :     }
    2102             : 
    2103          10 :     VSIDIRADLS *dir = new VSIDIRADLS(this);
    2104          10 :     dir->m_nRecurseDepth = nRecurseDepth;
    2105          10 :     dir->m_poFS = this;
    2106          10 :     dir->m_bRecursiveRequestFromAccountRoot =
    2107          10 :         osFilesystem.empty() && nRecurseDepth < 0;
    2108          10 :     dir->m_osFilesystem = std::move(osFilesystem);
    2109          10 :     dir->m_osObjectKey = std::move(osObjectKey);
    2110          10 :     dir->m_nMaxFiles =
    2111          10 :         atoi(CSLFetchNameValueDef(papszOptions, "MAXFILES", "0"));
    2112          10 :     dir->m_bCacheEntries =
    2113          10 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CACHE_ENTRIES", "YES"));
    2114          10 :     dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
    2115          10 :     if (!dir->IssueListDir())
    2116             :     {
    2117           1 :         delete dir;
    2118           1 :         return nullptr;
    2119             :     }
    2120             : 
    2121           9 :     return dir;
    2122             : }
    2123             : 
    2124             : /************************************************************************/
    2125             : /*                      GetStreamingFilename()                          */
    2126             : /************************************************************************/
    2127             : 
    2128             : std::string
    2129           0 : VSIADLSFSHandler::GetStreamingFilename(const std::string &osFilename) const
    2130             : {
    2131           0 :     if (STARTS_WITH(osFilename.c_str(), GetFSPrefix().c_str()))
    2132           0 :         return "/vsiaz_streaming/" + osFilename.substr(GetFSPrefix().size());
    2133           0 :     return osFilename;
    2134             : }
    2135             : 
    2136             : /************************************************************************/
    2137             : /*                           VSIADLSHandle()                           */
    2138             : /************************************************************************/
    2139             : 
    2140          25 : VSIADLSHandle::VSIADLSHandle(VSIADLSFSHandler *poFSIn, const char *pszFilename,
    2141          25 :                              VSIAzureBlobHandleHelper *poHandleHelper)
    2142          25 :     : VSICurlHandle(poFSIn, pszFilename, poHandleHelper->GetURLNoKVP().c_str()),
    2143          50 :       m_poHandleHelper(poHandleHelper)
    2144             : {
    2145          25 :     m_osQueryString = poHandleHelper->GetSASQueryString();
    2146          25 : }
    2147             : 
    2148             : /************************************************************************/
    2149             : /*                          GetCurlHeaders()                            */
    2150             : /************************************************************************/
    2151             : 
    2152          21 : struct curl_slist *VSIADLSHandle::GetCurlHeaders(const std::string &osVerb,
    2153             :                                                  struct curl_slist *psHeaders)
    2154             : {
    2155          21 :     return m_poHandleHelper->GetCurlHeaders(osVerb, psHeaders);
    2156             : }
    2157             : 
    2158             : /************************************************************************/
    2159             : /*                          CanRestartOnError()                         */
    2160             : /************************************************************************/
    2161             : 
    2162           6 : bool VSIADLSHandle::CanRestartOnError(const char *pszErrorMsg,
    2163             :                                       const char *pszHeaders, bool bSetError)
    2164             : {
    2165           6 :     return m_poHandleHelper->CanRestartOnError(pszErrorMsg, pszHeaders,
    2166           6 :                                                bSetError);
    2167             : }
    2168             : 
    2169             : } /* end of namespace cpl */
    2170             : 
    2171             : #endif  // DOXYGEN_SKIP
    2172             : //! @endcond
    2173             : 
    2174             : /************************************************************************/
    2175             : /*                      VSIInstallADLSFileHandler()                    */
    2176             : /************************************************************************/
    2177             : 
    2178             : /*!
    2179             :  \brief Install /vsiaz/ Microsoft Azure Data Lake Storage Gen2 file system
    2180             :  handler (requires libcurl)
    2181             : 
    2182             :  \verbatim embed:rst
    2183             :  See :ref:`/vsiadls/ documentation <vsiadls>`
    2184             :  \endverbatim
    2185             : 
    2186             :  @since GDAL 3.3
    2187             :  */
    2188             : 
    2189        1691 : void VSIInstallADLSFileHandler(void)
    2190             : {
    2191        1691 :     VSIFileManager::InstallHandler("/vsiadls/", new cpl::VSIADLSFSHandler);
    2192        1691 : }
    2193             : 
    2194             : #endif /* HAVE_CURL */

Generated by: LCOV version 1.14