LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 744 890 83.6 %
Date: 2025-07-05 13:22:42 Functions: 57 59 96.6 %

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

Generated by: LCOV version 1.14