LCOV - code coverage report
Current view: top level - port - cpl_vsil_webhdfs.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 476 526 90.5 %
Date: 2026-08-21 03:01:58 Functions: 28 33 84.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for WebHDFS REST API
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, 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_vsil_curl_priv.h"
      17             : #include "cpl_vsil_curl_class.h"
      18             : 
      19             : #include <errno.h>
      20             : 
      21             : #include <algorithm>
      22             : #include <set>
      23             : #include <map>
      24             : #include <memory>
      25             : 
      26             : #include "cpl_alibaba_oss.h"
      27             : 
      28             : #ifndef HAVE_CURL
      29             : 
      30             : void VSIInstallWebHdfsHandler(void)
      31             : {
      32             :     // Not supported
      33             : }
      34             : 
      35             : #else
      36             : 
      37             : //! @cond Doxygen_Suppress
      38             : #ifndef DOXYGEN_SKIP
      39             : 
      40             : #define ENABLE_DEBUG 0
      41             : 
      42             : #define unchecked_curl_easy_setopt(handle, opt, param)                         \
      43             :     CPL_IGNORE_RET_VAL(curl_easy_setopt(handle, opt, param))
      44             : 
      45             : namespace cpl
      46             : {
      47             : 
      48             : /************************************************************************/
      49             : /*                         VSIWebHDFSFSHandler                          */
      50             : /************************************************************************/
      51             : 
      52             : class VSIWebHDFSFSHandler final : public VSICurlFilesystemHandlerBaseWritable
      53             : {
      54             :     const std::string m_osPrefix;
      55             :     CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSFSHandler)
      56             : 
      57             :   protected:
      58             :     VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
      59             : 
      60           0 :     int HasOptimizedReadMultiRange(const char * /* pszPath */) override
      61             :     {
      62           0 :         return false;
      63             :     }
      64             : 
      65             :     char **GetFileList(const char *pszFilename, int nMaxFiles,
      66             :                        bool *pbGotFileList) override;
      67             : 
      68             :     std::string
      69             :     GetURLFromFilename(const std::string &osFilename) const override;
      70             : 
      71             :     VSIVirtualHandleUniquePtr
      72             :     CreateWriteHandle(const char *pszFilename,
      73             :                       CSLConstList papszOptions) override;
      74             : 
      75             :   public:
      76        2101 :     explicit VSIWebHDFSFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
      77             :     {
      78        2101 :     }
      79             : 
      80        1303 :     ~VSIWebHDFSFSHandler() override = default;
      81             : 
      82             :     int Unlink(const char *pszFilename) override;
      83             :     int Rmdir(const char *pszFilename) override;
      84             :     int Mkdir(const char *pszDirname, long nMode) override;
      85             : 
      86           1 :     const char *GetDebugKey() const override
      87             :     {
      88           1 :         return "VSIWEBHDFS";
      89             :     }
      90             : 
      91         162 :     std::string GetFSPrefix() const override
      92             :     {
      93         162 :         return m_osPrefix;
      94             :     }
      95             : 
      96             :     const char *GetOptions() override;
      97             : 
      98             :     std::string
      99           0 :     GetStreamingFilename(const std::string &osFilename) const override
     100             :     {
     101           0 :         return osFilename;
     102             :     }
     103             : 
     104           0 :     VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
     105             :     {
     106           0 :         return new VSIWebHDFSFSHandler(pszPrefix);
     107             :     }
     108             : };
     109             : 
     110             : /************************************************************************/
     111             : /*                           VSIWebHDFSHandle                           */
     112             : /************************************************************************/
     113             : 
     114             : class VSIWebHDFSHandle final : public VSICurlHandle
     115             : {
     116             :     CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSHandle)
     117             : 
     118             :     std::string m_osDataNodeHost{};
     119             :     std::string m_osUsernameParam{};
     120             :     std::string m_osDelegationParam{};
     121             : 
     122             :     std::string DownloadRegion(vsi_l_offset startOffset, int nBlocks) override;
     123             : 
     124             :   public:
     125             :     VSIWebHDFSHandle(VSIWebHDFSFSHandler *poFS, const char *pszFilename,
     126             :                      const char *pszURL);
     127          14 :     ~VSIWebHDFSHandle() override = default;
     128             : 
     129           0 :     int ReadMultiRange(int nRanges, void **ppData,
     130             :                        const vsi_l_offset *panOffsets,
     131             :                        const size_t *panSizes) override
     132             :     {
     133           0 :         return VSIVirtualHandle::ReadMultiRange(nRanges, ppData, panOffsets,
     134           0 :                                                 panSizes);
     135             :     }
     136             : 
     137             :     vsi_l_offset GetFileSize(bool bSetError) override;
     138             : };
     139             : 
     140             : /************************************************************************/
     141             : /*                          PatchWebHDFSUrl()                           */
     142             : /************************************************************************/
     143             : 
     144           6 : static std::string PatchWebHDFSUrl(const std::string &osURLIn,
     145             :                                    const std::string &osNewHost)
     146             : {
     147           6 :     std::string osURL(osURLIn);
     148           6 :     size_t nStart = 0;
     149           6 :     if (STARTS_WITH(osURL.c_str(), "http://"))
     150           6 :         nStart = strlen("http://");
     151           0 :     else if (STARTS_WITH(osURL.c_str(), "https://"))
     152           0 :         nStart = strlen("https://");
     153           6 :     if (nStart)
     154             :     {
     155           6 :         size_t nHostEnd = osURL.find(':', nStart);
     156           6 :         if (nHostEnd != std::string::npos)
     157             :         {
     158             :             osURL =
     159           6 :                 osURL.substr(0, nStart) + osNewHost + osURL.substr(nHostEnd);
     160             :         }
     161             :     }
     162           6 :     return osURL;
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                       GetWebHDFSDataNodeHost()                       */
     167             : /************************************************************************/
     168             : 
     169          13 : static std::string GetWebHDFSDataNodeHost(const char *pszFilename)
     170             : {
     171             :     return std::string(
     172          13 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DATANODE_HOST", ""));
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                        VSIWebHDFSWriteHandle                         */
     177             : /************************************************************************/
     178             : 
     179             : class VSIWebHDFSWriteHandle final : public VSIAppendWriteHandle
     180             : {
     181             :     CPL_DISALLOW_COPY_ASSIGN(VSIWebHDFSWriteHandle)
     182             : 
     183             :     std::string m_osURL{};
     184             :     std::string m_osDataNodeHost{};
     185             :     std::string m_osUsernameParam{};
     186             :     std::string m_osDelegationParam{};
     187             :     CPLStringList m_aosHTTPOptions{};
     188             :     RunThreadUser m_threadUser;
     189             : 
     190             :     bool Send(bool bIsLastBlock) override;
     191             :     bool CreateFile();
     192             :     bool Append();
     193             : 
     194             :     void InvalidateParentDirectory();
     195             : 
     196             :   public:
     197             :     VSIWebHDFSWriteHandle(VSIWebHDFSFSHandler *poFS, const char *pszFilename);
     198             :     ~VSIWebHDFSWriteHandle() override;
     199             : };
     200             : 
     201             : /************************************************************************/
     202             : /*                        GetWebHDFSBufferSize()                        */
     203             : /************************************************************************/
     204             : 
     205           6 : static int GetWebHDFSBufferSize()
     206             : {
     207             :     int nBufferSize;
     208           6 :     int nChunkSizeMB = atoi(CPLGetConfigOption("VSIWEBHDFS_SIZE", "4"));
     209           6 :     if (nChunkSizeMB <= 0 || nChunkSizeMB > 1000)
     210           0 :         nBufferSize = 4 * 1024 * 1024;
     211             :     else
     212           6 :         nBufferSize = nChunkSizeMB * 1024 * 1024;
     213             : 
     214             :     // For testing only !
     215             :     const char *pszChunkSizeBytes =
     216           6 :         CPLGetConfigOption("VSIWEBHDFS_SIZE_BYTES", nullptr);
     217           6 :     if (pszChunkSizeBytes)
     218           0 :         nBufferSize = atoi(pszChunkSizeBytes);
     219           6 :     if (nBufferSize <= 0 || nBufferSize > 1000 * 1024 * 1024)
     220           0 :         nBufferSize = 4 * 1024 * 1024;
     221           6 :     return nBufferSize;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                       VSIWebHDFSWriteHandle()                        */
     226             : /************************************************************************/
     227             : 
     228           6 : VSIWebHDFSWriteHandle::VSIWebHDFSWriteHandle(VSIWebHDFSFSHandler *poFS,
     229           6 :                                              const char *pszFilename)
     230           6 :     : VSIAppendWriteHandle(poFS, poFS->GetFSPrefix().c_str(), pszFilename,
     231             :                            GetWebHDFSBufferSize()),
     232          12 :       m_osURL(pszFilename + poFS->GetFSPrefix().size()),
     233             :       m_osDataNodeHost(GetWebHDFSDataNodeHost(pszFilename)),
     234             :       m_aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename)),
     235          24 :       m_threadUser(*poFS)
     236             : {
     237             :     // cppcheck-suppress useInitializationList
     238             :     m_osUsernameParam =
     239           6 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
     240           6 :     if (!m_osUsernameParam.empty())
     241           5 :         m_osUsernameParam = "&user.name=" + m_osUsernameParam;
     242             :     m_osDelegationParam =
     243           6 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
     244           6 :     if (!m_osDelegationParam.empty())
     245           0 :         m_osDelegationParam = "&delegation=" + m_osDelegationParam;
     246             : 
     247           6 :     if (m_pabyBuffer != nullptr && !CreateFile())
     248             :     {
     249           3 :         CPLFree(m_pabyBuffer);
     250           3 :         m_pabyBuffer = nullptr;
     251             :     }
     252           6 : }
     253             : 
     254             : /************************************************************************/
     255             : /*                       ~VSIWebHDFSWriteHandle()                       */
     256             : /************************************************************************/
     257             : 
     258          12 : VSIWebHDFSWriteHandle::~VSIWebHDFSWriteHandle()
     259             : {
     260           6 :     Close();
     261          12 : }
     262             : 
     263             : /************************************************************************/
     264             : /*                     InvalidateParentDirectory()                      */
     265             : /************************************************************************/
     266             : 
     267           3 : void VSIWebHDFSWriteHandle::InvalidateParentDirectory()
     268             : {
     269           3 :     m_poFS->InvalidateCachedData(m_osURL.c_str());
     270             : 
     271           3 :     std::string osFilenameWithoutSlash(m_osFilename);
     272           3 :     if (!osFilenameWithoutSlash.empty() && osFilenameWithoutSlash.back() == '/')
     273           0 :         osFilenameWithoutSlash.pop_back();
     274           3 :     m_poFS->InvalidateDirContent(
     275           6 :         CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
     276           3 : }
     277             : 
     278             : /************************************************************************/
     279             : /*                                Send()                                */
     280             : /************************************************************************/
     281             : 
     282           6 : bool VSIWebHDFSWriteHandle::Send(bool /* bIsLastBlock */)
     283             : {
     284           6 :     if (m_nCurOffset > 0)
     285           2 :         return Append();
     286           4 :     return true;
     287             : }
     288             : 
     289             : /************************************************************************/
     290             : /*                             CreateFile()                             */
     291             : /************************************************************************/
     292             : 
     293           6 : bool VSIWebHDFSWriteHandle::CreateFile()
     294             : {
     295           6 :     if (m_osUsernameParam.empty() && m_osDelegationParam.empty())
     296             :     {
     297           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     298             :                  "Configuration option WEBHDFS_USERNAME or WEBHDFS_DELEGATION "
     299             :                  "should be defined");
     300           1 :         return false;
     301             :     }
     302             : 
     303          10 :     NetworkStatisticsFileSystem oContextFS(m_poFS->GetFSPrefix().c_str());
     304          10 :     NetworkStatisticsFile oContextFile(m_osFilename.c_str());
     305          10 :     NetworkStatisticsAction oContextAction("Write");
     306             : 
     307          10 :     std::string osURL = m_osURL + "?op=CREATE&overwrite=true" +
     308          15 :                         m_osUsernameParam + m_osDelegationParam;
     309             : 
     310             :     std::string osPermission = VSIGetPathSpecificOption(
     311          10 :         m_osFilename.c_str(), "WEBHDFS_PERMISSION", "");
     312           5 :     if (!osPermission.empty())
     313           0 :         osURL += "&permission=" + osPermission;
     314             : 
     315             :     std::string osReplication = VSIGetPathSpecificOption(
     316          10 :         m_osFilename.c_str(), "WEBHDFS_REPLICATION", "");
     317           5 :     if (!osReplication.empty())
     318           0 :         osURL += "&replication=" + osReplication;
     319             : 
     320           5 :     bool bInRedirect = false;
     321             : 
     322           5 :     RunThreadUser runThreadUser(*m_poFS);
     323           8 : retry:
     324           8 :     CURL *hCurlHandle = curl_easy_init();
     325             : 
     326             :     struct curl_slist *headers = static_cast<struct curl_slist *>(
     327           8 :         CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
     328             : 
     329           8 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
     330           8 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_INFILESIZE, 0);
     331             : 
     332           8 :     if (!m_osDataNodeHost.empty())
     333             :     {
     334           7 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
     335             :     }
     336             : 
     337           8 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     338             : 
     339           8 :     WriteFuncStruct sWriteFuncData;
     340           8 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     341           8 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     342           8 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     343             :                                VSICurlHandleWriteFunc);
     344             : 
     345           8 :     m_poFS->Perform(hCurlHandle);
     346             : 
     347           8 :     curl_slist_free_all(headers);
     348             : 
     349           8 :     NetworkStatisticsLogger::LogPUT(0);
     350             : 
     351           8 :     long response_code = 0;
     352           8 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     353             : 
     354           8 :     if (!bInRedirect)
     355             :     {
     356           5 :         char *pszRedirectURL = nullptr;
     357           5 :         curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
     358           5 :         if (pszRedirectURL && strstr(pszRedirectURL, osURL.c_str()) == nullptr)
     359             :         {
     360           3 :             CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
     361             : 
     362           3 :             bInRedirect = true;
     363           3 :             osURL = pszRedirectURL;
     364           3 :             if (!m_osDataNodeHost.empty())
     365             :             {
     366           3 :                 osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
     367             :             }
     368             : 
     369           3 :             curl_easy_cleanup(hCurlHandle);
     370           3 :             CPLFree(sWriteFuncData.pBuffer);
     371             : 
     372           3 :             goto retry;
     373             :         }
     374             :     }
     375             : 
     376           5 :     curl_easy_cleanup(hCurlHandle);
     377             : 
     378           5 :     if (response_code == 201)
     379             :     {
     380           3 :         InvalidateParentDirectory();
     381             :     }
     382             :     else
     383             :     {
     384           2 :         CPLDebug("WEBHDFS", "%s",
     385           2 :                  sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
     386           2 :         CPLError(CE_Failure, CPLE_AppDefined, "PUT of %s failed",
     387             :                  m_osURL.c_str());
     388             :     }
     389           5 :     CPLFree(sWriteFuncData.pBuffer);
     390             : 
     391           5 :     return response_code == 201;
     392             : }
     393             : 
     394             : /************************************************************************/
     395             : /*                               Append()                               */
     396             : /************************************************************************/
     397             : 
     398           2 : bool VSIWebHDFSWriteHandle::Append()
     399             : {
     400           4 :     RunThreadUser threadUser(*m_poFS);
     401             : 
     402           4 :     NetworkStatisticsFileSystem oContextFS(m_poFS->GetFSPrefix().c_str());
     403           4 :     NetworkStatisticsFile oContextFile(m_osFilename.c_str());
     404           4 :     NetworkStatisticsAction oContextAction("Write");
     405             : 
     406             :     std::string osURL =
     407           6 :         m_osURL + "?op=APPEND" + m_osUsernameParam + m_osDelegationParam;
     408             : 
     409           2 :     CURL *hCurlHandle = curl_easy_init();
     410             : 
     411             :     struct curl_slist *headers = static_cast<struct curl_slist *>(
     412           2 :         CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
     413             : 
     414           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "POST");
     415           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
     416           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     417             : 
     418           2 :     WriteFuncStruct sWriteFuncData;
     419           2 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     420           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     421           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     422             :                                VSICurlHandleWriteFunc);
     423             : 
     424           2 :     m_poFS->Perform(hCurlHandle);
     425             : 
     426           2 :     curl_slist_free_all(headers);
     427             : 
     428           2 :     NetworkStatisticsLogger::LogPOST(0, 0);
     429             : 
     430           2 :     long response_code = 0;
     431           2 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     432             : 
     433           2 :     if (response_code != 307)
     434             :     {
     435           0 :         CPLDebug("WEBHDFS", "%s",
     436           0 :                  sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
     437           0 :         CPLError(CE_Failure, CPLE_AppDefined, "POST of %s failed",
     438             :                  m_osURL.c_str());
     439           0 :         curl_easy_cleanup(hCurlHandle);
     440           0 :         CPLFree(sWriteFuncData.pBuffer);
     441           0 :         return false;
     442             :     }
     443             : 
     444           2 :     char *pszRedirectURL = nullptr;
     445           2 :     curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
     446           2 :     if (pszRedirectURL == nullptr)
     447             :     {
     448           0 :         curl_easy_cleanup(hCurlHandle);
     449           0 :         CPLFree(sWriteFuncData.pBuffer);
     450           0 :         return false;
     451             :     }
     452           2 :     CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
     453             : 
     454           2 :     osURL = pszRedirectURL;
     455           2 :     if (!m_osDataNodeHost.empty())
     456             :     {
     457           2 :         osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
     458             :     }
     459             : 
     460           2 :     curl_easy_cleanup(hCurlHandle);
     461           2 :     CPLFree(sWriteFuncData.pBuffer);
     462             : 
     463             :     // After redirection
     464             : 
     465           2 :     hCurlHandle = curl_easy_init();
     466             : 
     467             :     headers = static_cast<struct curl_slist *>(
     468           2 :         CPLHTTPSetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List()));
     469             :     headers =
     470           2 :         curl_slist_append(headers, "Content-Type: application/octet-stream");
     471             : 
     472           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_POSTFIELDS, m_pabyBuffer);
     473           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_POSTFIELDSIZE,
     474             :                                m_nBufferOff);
     475           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
     476           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     477             : 
     478           2 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     479           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     480           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     481             :                                VSICurlHandleWriteFunc);
     482             : 
     483           2 :     m_poFS->Perform(hCurlHandle);
     484             : 
     485           2 :     curl_slist_free_all(headers);
     486             : 
     487           2 :     NetworkStatisticsLogger::LogPOST(m_nBufferOff, 0);
     488             : 
     489           2 :     response_code = 0;
     490           2 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     491             : 
     492           2 :     curl_easy_cleanup(hCurlHandle);
     493             : 
     494           2 :     if (response_code != 200)
     495             :     {
     496           1 :         CPLDebug("WEBHDFS", "%s",
     497           1 :                  sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
     498           1 :         CPLError(CE_Failure, CPLE_AppDefined, "POST of %s failed",
     499             :                  m_osURL.c_str());
     500             :     }
     501           2 :     CPLFree(sWriteFuncData.pBuffer);
     502             : 
     503           2 :     return response_code == 200;
     504             : }
     505             : 
     506             : /************************************************************************/
     507             : /*                         CreateWriteHandle()                          */
     508             : /************************************************************************/
     509             : 
     510             : VSIVirtualHandleUniquePtr
     511           6 : VSIWebHDFSFSHandler::CreateWriteHandle(const char *pszFilename,
     512             :                                        CSLConstList /*papszOptions*/)
     513             : {
     514          12 :     auto poHandle = std::make_unique<VSIWebHDFSWriteHandle>(this, pszFilename);
     515           6 :     if (!poHandle->IsOK())
     516             :     {
     517           3 :         return nullptr;
     518             :     }
     519           3 :     return VSIVirtualHandleUniquePtr(poHandle.release());
     520             : }
     521             : 
     522             : /************************************************************************/
     523             : /*                             GetOptions()                             */
     524             : /************************************************************************/
     525             : 
     526           1 : const char *VSIWebHDFSFSHandler::GetOptions()
     527             : {
     528             :     static std::string osOptions(
     529           2 :         std::string("<Options>") +
     530             :         "  <Option name='WEBHDFS_USERNAME' type='string' "
     531             :         "description='username (when security is off)'/>"
     532             :         "  <Option name='WEBHDFS_DELEGATION' type='string' "
     533             :         "description='Hadoop delegation token (when security is on)'/>"
     534             :         "  <Option name='WEBHDFS_DATANODE_HOST' type='string' "
     535             :         "description='For APIs using redirect, substitute the redirection "
     536             :         "hostname with the one provided by this option (normally resolvable "
     537             :         "hostname should be rewritten by a proxy)'/>"
     538             :         "  <Option name='WEBHDFS_REPLICATION' type='integer' "
     539             :         "description='Replication value used when creating a file'/>"
     540             :         "  <Option name='WEBHDFS_PERMISSION' type='integer' "
     541             :         "description='Permission mask (to provide as decimal number) when "
     542           3 :         "creating a file or directory'/>" +
     543           2 :         VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
     544           1 :     return osOptions.c_str();
     545             : }
     546             : 
     547             : /************************************************************************/
     548             : /*                          CreateFileHandle()                          */
     549             : /************************************************************************/
     550             : 
     551           7 : VSICurlHandle *VSIWebHDFSFSHandler::CreateFileHandle(const char *pszFilename)
     552             : {
     553             :     return new VSIWebHDFSHandle(this, pszFilename,
     554           7 :                                 pszFilename + GetFSPrefix().size());
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                         GetURLFromFilename()                         */
     559             : /************************************************************************/
     560             : 
     561             : std::string
     562          20 : VSIWebHDFSFSHandler::GetURLFromFilename(const std::string &osFilename) const
     563             : {
     564          40 :     return osFilename.substr(GetFSPrefix().size());
     565             : }
     566             : 
     567             : /************************************************************************/
     568             : /*                            GetFileList()                             */
     569             : /************************************************************************/
     570             : 
     571           2 : char **VSIWebHDFSFSHandler::GetFileList(const char *pszDirname,
     572             :                                         int /*nMaxFiles*/, bool *pbGotFileList)
     573             : {
     574             :     if (ENABLE_DEBUG)
     575             :         CPLDebug("WEBHDFS", "GetFileList(%s)", pszDirname);
     576           2 :     *pbGotFileList = false;
     577             : 
     578           4 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     579           4 :     NetworkStatisticsAction oContextAction("ListBucket");
     580             : 
     581           2 :     CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
     582             : 
     583           6 :     std::string osBaseURL = pszDirname + GetFSPrefix().size();
     584           2 :     if (!osBaseURL.empty() && osBaseURL.back() != '/')
     585           2 :         osBaseURL += '/';
     586             : 
     587             :     //RAII for CURL run thread.
     588           4 :     RunThreadUser threadUser(*this);
     589             : 
     590             :     std::string osUsernameParam =
     591           4 :         VSIGetPathSpecificOption(pszDirname, "WEBHDFS_USERNAME", "");
     592           2 :     if (!osUsernameParam.empty())
     593           0 :         osUsernameParam = "&user.name=" + osUsernameParam;
     594             :     std::string osDelegationParam =
     595           4 :         VSIGetPathSpecificOption(pszDirname, "WEBHDFS_DELEGATION", "");
     596           2 :     if (!osDelegationParam.empty())
     597           0 :         osDelegationParam = "&delegation=" + osDelegationParam;
     598             :     std::string osURL =
     599           6 :         osBaseURL + "?op=LISTSTATUS" + osUsernameParam + osDelegationParam;
     600           4 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
     601             : 
     602           2 :     CURL *hCurlHandle = curl_easy_init();
     603             : 
     604             :     struct curl_slist *headers =
     605           2 :         SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
     606             : 
     607           2 :     WriteFuncStruct sWriteFuncData;
     608           2 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     609           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     610           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     611             :                                VSICurlHandleWriteFunc);
     612             : 
     613           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     614             : 
     615           2 :     Perform(hCurlHandle);
     616             : 
     617           2 :     VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
     618             : 
     619           2 :     curl_slist_free_all(headers);
     620             : 
     621           2 :     NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
     622             : 
     623           2 :     long response_code = 0;
     624           2 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     625             : 
     626           4 :     CPLStringList aosList;
     627           2 :     bool bOK = false;
     628           2 :     if (response_code == 200 && sWriteFuncData.pBuffer)
     629             :     {
     630           2 :         CPLJSONDocument oDoc;
     631           1 :         if (oDoc.LoadMemory(
     632           1 :                 reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
     633             :         {
     634             :             CPLJSONArray oFileStatus =
     635           3 :                 oDoc.GetRoot().GetArray("FileStatuses/FileStatus");
     636           1 :             bOK = oFileStatus.IsValid();
     637           3 :             for (int i = 0; i < oFileStatus.Size(); i++)
     638             :             {
     639           2 :                 CPLJSONObject oItem = oFileStatus[i];
     640           2 :                 vsi_l_offset fileSize = oItem.GetLong("length");
     641             :                 size_t mTime = static_cast<size_t>(
     642           2 :                     oItem.GetLong("modificationTime") / 1000);
     643           2 :                 bool bIsDirectory = oItem.GetString("type") == "DIRECTORY";
     644           4 :                 std::string osName = oItem.GetString("pathSuffix");
     645             :                 // can be empty if we for example ask to list a file: in that
     646             :                 // case the file entry is reported but with an empty pathSuffix
     647           2 :                 if (!osName.empty())
     648             :                 {
     649           2 :                     if (CPLHasUnbalancedPathTraversal(osName.c_str()))
     650             :                     {
     651           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     652             :                                  "Ignoring pathSuffix '%s' that has a path "
     653             :                                  "traversal pattern",
     654             :                                  osName.c_str());
     655           0 :                         continue;
     656             :                     }
     657           2 :                     aosList.AddString(osName.c_str());
     658             : 
     659           4 :                     FileProp prop;
     660           2 :                     prop.eExists = EXIST_YES;
     661           2 :                     prop.bIsDirectory = bIsDirectory;
     662           2 :                     prop.bHasComputedFileSize = true;
     663           2 :                     prop.fileSize = fileSize;
     664           2 :                     prop.mTime = mTime;
     665           4 :                     std::string osCachedFilename(osBaseURL + osName);
     666             : #if DEBUG_VERBOSE
     667             :                     CPLDebug("WEBHDFS", "Cache %s", osCachedFilename.c_str());
     668             : #endif
     669           2 :                     SetCachedFileProp(osCachedFilename.c_str(), prop);
     670             :                 }
     671             :             }
     672             :         }
     673             :     }
     674             : 
     675           2 :     *pbGotFileList = bOK;
     676             : 
     677           2 :     CPLFree(sWriteFuncData.pBuffer);
     678           2 :     curl_easy_cleanup(hCurlHandle);
     679             : 
     680           2 :     if (bOK)
     681           1 :         return aosList.StealList();
     682             :     else
     683           1 :         return nullptr;
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                               Unlink()                               */
     688             : /************************************************************************/
     689             : 
     690           7 : int VSIWebHDFSFSHandler::Unlink(const char *pszFilename)
     691             : {
     692           7 :     if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
     693           1 :         return -1;
     694             : 
     695          12 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     696          12 :     NetworkStatisticsAction oContextAction("Unlink");
     697             : 
     698          18 :     std::string osBaseURL = GetURLFromFilename(pszFilename);
     699             : 
     700          12 :     RunThreadUser threadUser(*this);
     701             : 
     702             :     std::string osUsernameParam =
     703          12 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
     704           6 :     if (!osUsernameParam.empty())
     705           1 :         osUsernameParam = "&user.name=" + osUsernameParam;
     706             :     std::string osDelegationParam =
     707          12 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
     708           6 :     if (!osDelegationParam.empty())
     709           1 :         osDelegationParam = "&delegation=" + osDelegationParam;
     710             :     std::string osURL =
     711          18 :         osBaseURL + "?op=DELETE" + osUsernameParam + osDelegationParam;
     712          12 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszFilename));
     713             : 
     714           6 :     CURL *hCurlHandle = curl_easy_init();
     715             : 
     716           6 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
     717             : 
     718             :     struct curl_slist *headers =
     719           6 :         SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
     720             : 
     721           6 :     WriteFuncStruct sWriteFuncData;
     722           6 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     723           6 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     724           6 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     725             :                                VSICurlHandleWriteFunc);
     726             : 
     727           6 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     728             : 
     729           6 :     Perform(hCurlHandle);
     730             : 
     731           6 :     VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
     732             : 
     733           6 :     curl_slist_free_all(headers);
     734             : 
     735           6 :     NetworkStatisticsLogger::LogDELETE();
     736             : 
     737           6 :     long response_code = 0;
     738           6 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     739             : 
     740           6 :     CPLStringList aosList;
     741           6 :     bool bOK = false;
     742           6 :     if (response_code == 200 && sWriteFuncData.pBuffer)
     743             :     {
     744           8 :         CPLJSONDocument oDoc;
     745           4 :         if (oDoc.LoadMemory(
     746           4 :                 reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
     747             :         {
     748           4 :             bOK = oDoc.GetRoot().GetBool("boolean");
     749             :         }
     750             :     }
     751           6 :     if (bOK)
     752             :     {
     753           3 :         InvalidateCachedData(osBaseURL.c_str());
     754             : 
     755           3 :         std::string osFilenameWithoutSlash(pszFilename);
     756           6 :         if (!osFilenameWithoutSlash.empty() &&
     757           3 :             osFilenameWithoutSlash.back() == '/')
     758           0 :             osFilenameWithoutSlash.pop_back();
     759             : 
     760           3 :         InvalidateDirContent(CPLGetDirnameSafe(osFilenameWithoutSlash.c_str()));
     761             :     }
     762             :     else
     763             :     {
     764           3 :         CPLDebug("WEBHDFS", "%s",
     765           3 :                  sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
     766             :     }
     767             : 
     768           6 :     CPLFree(sWriteFuncData.pBuffer);
     769           6 :     curl_easy_cleanup(hCurlHandle);
     770             : 
     771           6 :     return bOK ? 0 : -1;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                               Rmdir()                                */
     776             : /************************************************************************/
     777             : 
     778           3 : int VSIWebHDFSFSHandler::Rmdir(const char *pszFilename)
     779             : {
     780           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     781           6 :     NetworkStatisticsAction oContextAction("Rmdir");
     782             : 
     783           6 :     return Unlink(pszFilename);
     784             : }
     785             : 
     786             : /************************************************************************/
     787             : /*                               Mkdir()                                */
     788             : /************************************************************************/
     789             : 
     790           5 : int VSIWebHDFSFSHandler::Mkdir(const char *pszDirname, long nMode)
     791             : {
     792           5 :     if (!STARTS_WITH_CI(pszDirname, GetFSPrefix().c_str()))
     793           1 :         return -1;
     794             : 
     795           8 :     std::string osDirnameWithoutEndSlash(pszDirname);
     796           8 :     if (!osDirnameWithoutEndSlash.empty() &&
     797           4 :         osDirnameWithoutEndSlash.back() == '/')
     798             :     {
     799           2 :         osDirnameWithoutEndSlash.pop_back();
     800             :     }
     801             : 
     802           4 :     if (osDirnameWithoutEndSlash.find("/webhdfs/v1") ==
     803           5 :             osDirnameWithoutEndSlash.size() - strlen("/webhdfs/v1") &&
     804           1 :         std::count(osDirnameWithoutEndSlash.begin(),
     805           5 :                    osDirnameWithoutEndSlash.end(), '/') == 6)
     806             :     {
     807             :         // The server does weird things (creating a webhdfs/v1 subfolder)
     808             :         // if we provide the root directory like
     809             :         // /vsiwebhdfs/http://localhost:50070/webhdfs/v1
     810           1 :         return -1;
     811             :     }
     812             : 
     813           6 :     NetworkStatisticsFileSystem oContextFS(GetFSPrefix().c_str());
     814           6 :     NetworkStatisticsAction oContextAction("Mkdir");
     815             : 
     816             :     std::string osBaseURL =
     817           9 :         GetURLFromFilename(osDirnameWithoutEndSlash.c_str());
     818             : 
     819             :     // RAII for CURL perform thread.
     820           6 :     RunThreadUser threadUser(*this);
     821             : 
     822             :     std::string osUsernameParam =
     823           6 :         VSIGetPathSpecificOption(pszDirname, "WEBHDFS_USERNAME", "");
     824           3 :     if (!osUsernameParam.empty())
     825           1 :         osUsernameParam = "&user.name=" + osUsernameParam;
     826             :     std::string osDelegationParam =
     827           6 :         VSIGetPathSpecificOption(pszDirname, "WEBHDFS_DELEGATION", "");
     828           3 :     if (!osDelegationParam.empty())
     829           1 :         osDelegationParam = "&delegation=" + osDelegationParam;
     830             :     std::string osURL =
     831           9 :         osBaseURL + "?op=MKDIRS" + osUsernameParam + osDelegationParam;
     832           3 :     if (nMode)
     833             :     {
     834           1 :         osURL += "&permission=";
     835           1 :         osURL += CPLSPrintf("%o", static_cast<int>(nMode));
     836             :     }
     837           6 :     const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
     838             : 
     839           3 :     CURL *hCurlHandle = curl_easy_init();
     840             : 
     841           3 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
     842             : 
     843             :     struct curl_slist *headers =
     844           3 :         SetOptions(hCurlHandle, osURL.c_str(), aosHTTPOptions.List());
     845             : 
     846           3 :     WriteFuncStruct sWriteFuncData;
     847           3 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     848           3 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     849           3 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     850             :                                VSICurlHandleWriteFunc);
     851             : 
     852           3 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     853             : 
     854           3 :     Perform(hCurlHandle);
     855             : 
     856           3 :     VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
     857             : 
     858           3 :     curl_slist_free_all(headers);
     859             : 
     860           3 :     NetworkStatisticsLogger::LogPUT(0);
     861             : 
     862           3 :     long response_code = 0;
     863           3 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     864             : 
     865           3 :     CPLStringList aosList;
     866           3 :     bool bOK = false;
     867           3 :     if (response_code == 200 && sWriteFuncData.pBuffer)
     868             :     {
     869           4 :         CPLJSONDocument oDoc;
     870           2 :         if (oDoc.LoadMemory(
     871           2 :                 reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
     872             :         {
     873           2 :             bOK = oDoc.GetRoot().GetBool("boolean");
     874             :         }
     875             :     }
     876           3 :     if (bOK)
     877             :     {
     878           2 :         InvalidateDirContent(
     879           4 :             CPLGetDirnameSafe(osDirnameWithoutEndSlash.c_str()));
     880             : 
     881           4 :         FileProp cachedFileProp;
     882           2 :         cachedFileProp.eExists = EXIST_YES;
     883           2 :         cachedFileProp.bIsDirectory = true;
     884           2 :         cachedFileProp.bHasComputedFileSize = true;
     885           2 :         SetCachedFileProp(
     886           4 :             GetURLFromFilename(osDirnameWithoutEndSlash.c_str()).c_str(),
     887             :             cachedFileProp);
     888             : 
     889           2 :         RegisterEmptyDir(osDirnameWithoutEndSlash);
     890             :     }
     891             :     else
     892             :     {
     893           1 :         CPLDebug("WEBHDFS", "%s",
     894           1 :                  sWriteFuncData.pBuffer ? sWriteFuncData.pBuffer : "(null)");
     895             :     }
     896             : 
     897           3 :     CPLFree(sWriteFuncData.pBuffer);
     898           3 :     curl_easy_cleanup(hCurlHandle);
     899             : 
     900           3 :     return bOK ? 0 : -1;
     901             : }
     902             : 
     903             : /************************************************************************/
     904             : /*                          VSIWebHDFSHandle()                          */
     905             : /************************************************************************/
     906             : 
     907           7 : VSIWebHDFSHandle::VSIWebHDFSHandle(VSIWebHDFSFSHandler *poFSIn,
     908           7 :                                    const char *pszFilename, const char *pszURL)
     909             :     : VSICurlHandle(poFSIn, pszFilename, pszURL),
     910           7 :       m_osDataNodeHost(GetWebHDFSDataNodeHost(pszFilename))
     911             : {
     912             :     // cppcheck-suppress useInitializationList
     913             :     m_osUsernameParam =
     914           7 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_USERNAME", "");
     915           7 :     if (!m_osUsernameParam.empty())
     916           1 :         m_osUsernameParam = "&user.name=" + m_osUsernameParam;
     917             :     m_osDelegationParam =
     918           7 :         VSIGetPathSpecificOption(pszFilename, "WEBHDFS_DELEGATION", "");
     919           7 :     if (!m_osDelegationParam.empty())
     920           1 :         m_osDelegationParam = "&delegation=" + m_osDelegationParam;
     921           7 : }
     922             : 
     923             : /************************************************************************/
     924             : /*                            GetFileSize()                             */
     925             : /************************************************************************/
     926             : 
     927           4 : vsi_l_offset VSIWebHDFSHandle::GetFileSize(bool bSetError)
     928             : {
     929           4 :     if (oFileProp.bHasComputedFileSize)
     930           2 :         return oFileProp.fileSize;
     931             : 
     932           4 :     NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
     933           4 :     NetworkStatisticsFile oContextFile(m_osFilename.c_str());
     934           4 :     NetworkStatisticsAction oContextAction("GetFileSize");
     935             : 
     936           2 :     oFileProp.bHasComputedFileSize = true;
     937             : 
     938           2 :     std::string osURL(m_pszURL);
     939             : 
     940           2 :     if (osURL.size() > strlen("/webhdfs/v1") &&
     941           2 :         osURL.find("/webhdfs/v1") == osURL.size() - strlen("/webhdfs/v1") &&
     942           2 :         std::count(osURL.begin(), osURL.end(), '/') == 4)
     943             :     {
     944             :         // If this is the root directory, add a trailing slash
     945           0 :         osURL += "/";
     946             :     }
     947             : 
     948           2 :     osURL += "?op=GETFILESTATUS" + m_osUsernameParam + m_osDelegationParam;
     949             : 
     950           2 :     CURL *hCurlHandle = curl_easy_init();
     951             : 
     952             :     struct curl_slist *headers =
     953           2 :         poFS->SetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
     954             : 
     955           2 :     WriteFuncStruct sWriteFuncData;
     956           2 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr, nullptr);
     957           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
     958           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
     959             :                                VSICurlHandleWriteFunc);
     960             : 
     961           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
     962             : 
     963           2 :     char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
     964           2 :     szCurlErrBuf[0] = '\0';
     965           2 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
     966             : 
     967           2 :     poFS->Perform(hCurlHandle);
     968             : 
     969           2 :     VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
     970             : 
     971           2 :     curl_slist_free_all(headers);
     972             : 
     973           2 :     NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
     974             : 
     975           2 :     long response_code = 0;
     976           2 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
     977             : 
     978           2 :     oFileProp.eExists = EXIST_NO;
     979           2 :     if (response_code == 200 && sWriteFuncData.pBuffer)
     980             :     {
     981           2 :         CPLJSONDocument oDoc;
     982           1 :         if (oDoc.LoadMemory(
     983           1 :                 reinterpret_cast<const GByte *>(sWriteFuncData.pBuffer)))
     984             :         {
     985           2 :             CPLJSONObject oFileStatus = oDoc.GetRoot().GetObj("FileStatus");
     986           1 :             oFileProp.fileSize = oFileStatus.GetLong("length");
     987           1 :             oFileProp.mTime = static_cast<size_t>(
     988           1 :                 oFileStatus.GetLong("modificationTime") / 1000);
     989           1 :             oFileProp.bIsDirectory =
     990           1 :                 oFileStatus.GetString("type") == "DIRECTORY";
     991           1 :             oFileProp.eExists = EXIST_YES;
     992             :         }
     993             :     }
     994             : 
     995             :     // If there was no VSI error thrown in the process,
     996             :     // fail by reporting the HTTP response code.
     997           2 :     if (response_code != 200 && bSetError && VSIGetLastErrorNo() == 0)
     998             :     {
     999           0 :         if (strlen(szCurlErrBuf) > 0)
    1000             :         {
    1001           0 :             if (response_code == 0)
    1002             :             {
    1003           0 :                 VSIError(VSIE_HttpError, "CURL error: %s", szCurlErrBuf);
    1004             :             }
    1005             :             else
    1006             :             {
    1007           0 :                 VSIError(VSIE_HttpError, "HTTP response code: %d - %s",
    1008             :                          static_cast<int>(response_code), szCurlErrBuf);
    1009             :             }
    1010             :         }
    1011             :         else
    1012             :         {
    1013           0 :             VSIError(VSIE_HttpError, "HTTP response code: %d",
    1014             :                      static_cast<int>(response_code));
    1015             :         }
    1016             :     }
    1017             : 
    1018             :     if (ENABLE_DEBUG)
    1019             :         CPLDebug(
    1020             :             "WEBHDFS", "GetFileSize(%s)=" CPL_FRMT_GUIB "  response_code=%d",
    1021             :             osURL.c_str(), oFileProp.fileSize, static_cast<int>(response_code));
    1022             : 
    1023           2 :     CPLFree(sWriteFuncData.pBuffer);
    1024           2 :     curl_easy_cleanup(hCurlHandle);
    1025             : 
    1026           2 :     oFileProp.bHasComputedFileSize = true;
    1027           2 :     poFS->SetCachedFileProp(m_pszURL, oFileProp);
    1028             : 
    1029           2 :     return oFileProp.fileSize;
    1030             : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                           DownloadRegion()                           */
    1034             : /************************************************************************/
    1035             : 
    1036           3 : std::string VSIWebHDFSHandle::DownloadRegion(const vsi_l_offset startOffset,
    1037             :                                              const int nBlocks)
    1038             : {
    1039           3 :     if (bInterrupted && bStopOnInterruptUntilUninstall)
    1040           0 :         return std::string();
    1041             : 
    1042           3 :     poFS->GetCachedFileProp(m_pszURL, oFileProp);
    1043           3 :     if (oFileProp.eExists == EXIST_NO)
    1044           0 :         return std::string();
    1045             : 
    1046           6 :     NetworkStatisticsFileSystem oContextFS(poFS->GetFSPrefix().c_str());
    1047           6 :     NetworkStatisticsFile oContextFile(m_osFilename.c_str());
    1048           6 :     NetworkStatisticsAction oContextAction("Read");
    1049             : 
    1050           6 :     std::string osURL(m_pszURL);
    1051             : 
    1052           3 :     WriteFuncStruct sWriteFuncData;
    1053           6 :     CPLHTTPRetryContext oRetryContext(m_oRetryParameters);
    1054           3 :     bool bInRedirect = false;
    1055             :     const vsi_l_offset nEndOffset =
    1056           3 :         startOffset +
    1057           3 :         static_cast<vsi_l_offset>(nBlocks) * VSICURLGetDownloadChunkSize() - 1;
    1058             : 
    1059           4 : retry:
    1060           4 :     CURL *hCurlHandle = curl_easy_init();
    1061             : 
    1062           4 :     VSICURLInitWriteFuncStruct(&sWriteFuncData, this, pfnReadCbk,
    1063             :                                pReadCbkUserData);
    1064           4 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData);
    1065           4 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
    1066             :                                VSICurlHandleWriteFunc);
    1067             : 
    1068           4 :     if (!bInRedirect)
    1069             :     {
    1070           3 :         osURL += "?op=OPEN&offset=";
    1071           3 :         osURL += CPLSPrintf(CPL_FRMT_GUIB, startOffset);
    1072           3 :         osURL += "&length=";
    1073           3 :         osURL += CPLSPrintf(CPL_FRMT_GUIB, nEndOffset - startOffset + 1);
    1074           3 :         osURL += m_osUsernameParam + m_osDelegationParam;
    1075             :     }
    1076             : 
    1077             :     struct curl_slist *headers =
    1078           4 :         poFS->SetOptions(hCurlHandle, osURL.c_str(), m_aosHTTPOptions.List());
    1079             : 
    1080           4 :     if (!m_osDataNodeHost.empty())
    1081             :     {
    1082           2 :         unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
    1083             :     }
    1084             : 
    1085             :     if (ENABLE_DEBUG)
    1086             :         CPLDebug("WEBHDFS", "Downloading %s...", osURL.c_str());
    1087             : 
    1088           4 :     char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
    1089           4 :     szCurlErrBuf[0] = '\0';
    1090           4 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);
    1091             : 
    1092           4 :     unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER, headers);
    1093             : 
    1094           4 :     poFS->Perform(hCurlHandle);
    1095             : 
    1096           4 :     VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
    1097             : 
    1098           4 :     curl_slist_free_all(headers);
    1099             : 
    1100           4 :     NetworkStatisticsLogger::LogGET(sWriteFuncData.nSize);
    1101             : 
    1102           4 :     if (sWriteFuncData.bInterrupted)
    1103             :     {
    1104           0 :         bInterrupted = true;
    1105             : 
    1106           0 :         CPLFree(sWriteFuncData.pBuffer);
    1107           0 :         curl_easy_cleanup(hCurlHandle);
    1108             : 
    1109           0 :         return std::string();
    1110             :     }
    1111             : 
    1112           4 :     long response_code = 0;
    1113           4 :     curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
    1114             : 
    1115             :     if (ENABLE_DEBUG)
    1116             :         CPLDebug("WEBHDFS", "Got response_code=%ld", response_code);
    1117             : 
    1118           4 :     if (!bInRedirect)
    1119             :     {
    1120           3 :         char *pszRedirectURL = nullptr;
    1121           3 :         curl_easy_getinfo(hCurlHandle, CURLINFO_REDIRECT_URL, &pszRedirectURL);
    1122           3 :         if (pszRedirectURL && strstr(pszRedirectURL, m_pszURL) == nullptr)
    1123             :         {
    1124           1 :             CPLDebug("WEBHDFS", "Redirect URL: %s", pszRedirectURL);
    1125             : 
    1126           1 :             bInRedirect = true;
    1127           1 :             osURL = pszRedirectURL;
    1128           1 :             if (!m_osDataNodeHost.empty())
    1129             :             {
    1130           1 :                 osURL = PatchWebHDFSUrl(osURL, m_osDataNodeHost);
    1131             :             }
    1132             : 
    1133           1 :             CPLFree(sWriteFuncData.pBuffer);
    1134           1 :             curl_easy_cleanup(hCurlHandle);
    1135             : 
    1136           1 :             goto retry;
    1137             :         }
    1138             :     }
    1139             : 
    1140           3 :     if (response_code != 200)
    1141             :     {
    1142           1 :         if (oRetryContext.CanRetry(static_cast<int>(response_code), nullptr,
    1143             :                                    szCurlErrBuf))
    1144             :         {
    1145           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1146             :                      "HTTP error code: %d - %s. "
    1147             :                      "Retrying again in %.1f secs",
    1148             :                      static_cast<int>(response_code), m_pszURL,
    1149             :                      oRetryContext.GetCurrentDelay());
    1150           0 :             CPLSleep(oRetryContext.GetCurrentDelay());
    1151           0 :             CPLFree(sWriteFuncData.pBuffer);
    1152           0 :             curl_easy_cleanup(hCurlHandle);
    1153           0 :             goto retry;
    1154             :         }
    1155             : 
    1156           1 :         if (response_code >= 400 && szCurlErrBuf[0] != '\0')
    1157             :         {
    1158           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%d: %s",
    1159             :                      static_cast<int>(response_code), szCurlErrBuf);
    1160             :         }
    1161           1 :         if (!oFileProp.bHasComputedFileSize && startOffset == 0)
    1162             :         {
    1163           1 :             oFileProp.bHasComputedFileSize = true;
    1164           1 :             oFileProp.fileSize = 0;
    1165           1 :             oFileProp.eExists = EXIST_NO;
    1166           1 :             poFS->SetCachedFileProp(m_pszURL, oFileProp);
    1167             :         }
    1168           1 :         CPLFree(sWriteFuncData.pBuffer);
    1169           1 :         curl_easy_cleanup(hCurlHandle);
    1170           1 :         return std::string();
    1171             :     }
    1172             : 
    1173           2 :     oFileProp.eExists = EXIST_YES;
    1174           2 :     poFS->SetCachedFileProp(m_pszURL, oFileProp);
    1175             : 
    1176           2 :     DownloadRegionPostProcess(startOffset, nBlocks, sWriteFuncData.pBuffer,
    1177             :                               sWriteFuncData.nSize);
    1178             : 
    1179           4 :     std::string osRet;
    1180           2 :     osRet.assign(sWriteFuncData.pBuffer, sWriteFuncData.nSize);
    1181             : 
    1182           2 :     CPLFree(sWriteFuncData.pBuffer);
    1183           2 :     curl_easy_cleanup(hCurlHandle);
    1184             : 
    1185           2 :     return osRet;
    1186             : }
    1187             : 
    1188             : } /* end of namespace cpl */
    1189             : 
    1190             : #endif  // DOXYGEN_SKIP
    1191             : //! @endcond
    1192             : 
    1193             : /************************************************************************/
    1194             : /*                      VSIInstallWebHdfsHandler()                      */
    1195             : /************************************************************************/
    1196             : 
    1197             : /*!
    1198             :  \brief Install /vsiwebhdfs/ WebHDFS (Hadoop File System) REST API file
    1199             :  system handler (requires libcurl)
    1200             : 
    1201             :  \verbatim embed:rst
    1202             :  See :ref:`/vsiwebhdfs/ documentation <vsiwebhdfs>`
    1203             :  \endverbatim
    1204             : 
    1205             :  */
    1206        2101 : void VSIInstallWebHdfsHandler(void)
    1207             : {
    1208        2101 :     VSIFileManager::InstallHandler(
    1209             :         "/vsiwebhdfs/",
    1210        4202 :         std::make_shared<cpl::VSIWebHDFSFSHandler>("/vsiwebhdfs/"));
    1211        2101 : }
    1212             : 
    1213             : #endif /* HAVE_CURL */

Generated by: LCOV version 1.14