LCOV - code coverage report
Current view: top level - port - cpl_vsil_adls.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 741 887 83.5 %
Date: 2025-01-18 12:42:00 Functions: 56 58 96.6 %

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

Generated by: LCOV version 1.14