LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 739 885 83.5 %
Date: 2024-11-21 22:18:42 Functions: 56 58 96.6 %

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

Generated by: LCOV version 1.14