Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for OpenStack Swift
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2017-2018, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_json.h"
14 : #include "cpl_port.h"
15 : #include "cpl_http.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_swift.h"
28 :
29 : #ifndef HAVE_CURL
30 :
31 : void VSIInstallSwiftFileHandler(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 : /* AnalyseSwiftFileList() */
51 : /************************************************************************/
52 :
53 13 : void VSICurlFilesystemHandlerBase::AnalyseSwiftFileList(
54 : const std::string &osBaseURL, const std::string &osPrefix,
55 : const char *pszJson, CPLStringList &osFileList, int nMaxFilesThisQuery,
56 : int nMaxFiles, bool &bIsTruncated, std::string &osNextMarker)
57 : {
58 : #if DEBUG_VERBOSE
59 : CPLDebug("SWIFT", "%s", pszJson);
60 : #endif
61 13 : osNextMarker = "";
62 13 : bIsTruncated = false;
63 :
64 13 : CPLJSONDocument oDoc;
65 13 : if (!oDoc.LoadMemory(reinterpret_cast<const GByte *>(pszJson)))
66 0 : return;
67 :
68 26 : std::vector<std::pair<std::string, FileProp>> aoProps;
69 : // Count the number of occurrences of a path. Can be 1 or 2. 2 in the case
70 : // that both a filename and directory exist
71 26 : std::map<std::string, int> aoNameCount;
72 :
73 26 : CPLJSONArray oArray = oDoc.GetRoot().ToArray();
74 23 : for (int i = 0; i < oArray.Size(); i++)
75 : {
76 10 : CPLJSONObject oItem = oArray[i];
77 20 : std::string osName = oItem.GetString("name");
78 10 : GInt64 nSize = oItem.GetLong("bytes");
79 20 : std::string osLastModified = oItem.GetString("last_modified");
80 20 : std::string osSubdir = oItem.GetString("subdir");
81 10 : bool bHasCount = oItem.GetLong("count", -1) >= 0;
82 10 : if (!osName.empty())
83 : {
84 6 : osNextMarker = osName;
85 12 : if (osName.size() > osPrefix.size() &&
86 12 : osName.substr(0, osPrefix.size()) == osPrefix)
87 : {
88 6 : if (CPLHasUnbalancedPathTraversal(osName.c_str()))
89 : {
90 0 : CPLError(
91 : CE_Warning, CPLE_AppDefined,
92 : "Ignoring name '%s' that has a path traversal pattern",
93 : osName.c_str());
94 0 : continue;
95 : }
96 :
97 6 : if (bHasCount)
98 : {
99 : // Case when listing /vsiswift/
100 2 : FileProp prop;
101 2 : prop.eExists = EXIST_YES;
102 2 : prop.bIsDirectory = true;
103 2 : prop.bHasComputedFileSize = true;
104 2 : prop.fileSize = 0;
105 2 : prop.mTime = 0;
106 :
107 2 : aoProps.push_back(
108 4 : std::pair<std::string, FileProp>(osName, prop));
109 2 : aoNameCount[osName]++;
110 : }
111 : else
112 : {
113 4 : FileProp prop;
114 4 : prop.eExists = EXIST_YES;
115 4 : prop.bHasComputedFileSize = true;
116 4 : prop.fileSize = static_cast<GUIntBig>(nSize);
117 4 : prop.bIsDirectory = false;
118 4 : prop.mTime = 0;
119 : int nYear, nMonth, nDay, nHour, nMin, nSec;
120 4 : if (sscanf(osLastModified.c_str(),
121 : "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
122 4 : &nDay, &nHour, &nMin, &nSec) == 6)
123 : {
124 : struct tm brokendowntime;
125 4 : brokendowntime.tm_year = nYear - 1900;
126 4 : brokendowntime.tm_mon = nMonth - 1;
127 4 : brokendowntime.tm_mday = nDay;
128 4 : brokendowntime.tm_hour = nHour;
129 4 : brokendowntime.tm_min = nMin;
130 4 : brokendowntime.tm_sec = nSec;
131 4 : prop.mTime = static_cast<time_t>(
132 4 : CPLYMDHMSToUnixTime(&brokendowntime));
133 : }
134 :
135 4 : aoProps.push_back(std::pair<std::string, FileProp>(
136 8 : osName.substr(osPrefix.size()), prop));
137 4 : aoNameCount[osName.substr(osPrefix.size())]++;
138 : }
139 : }
140 : }
141 4 : else if (!osSubdir.empty())
142 : {
143 4 : osNextMarker = osSubdir;
144 4 : if (osSubdir.back() == '/')
145 4 : osSubdir.pop_back();
146 4 : if (STARTS_WITH(osSubdir.c_str(), osPrefix.c_str()))
147 : {
148 4 : if (CPLHasUnbalancedPathTraversal(osSubdir.c_str()))
149 : {
150 0 : CPLError(CE_Warning, CPLE_AppDefined,
151 : "Ignoring subdir '%s' that has a path traversal "
152 : "pattern",
153 : osSubdir.c_str());
154 0 : continue;
155 : }
156 :
157 4 : FileProp prop;
158 4 : prop.eExists = EXIST_YES;
159 4 : prop.bIsDirectory = true;
160 4 : prop.bHasComputedFileSize = true;
161 4 : prop.fileSize = 0;
162 4 : prop.mTime = 0;
163 :
164 4 : aoProps.push_back(std::pair<std::string, FileProp>(
165 8 : osSubdir.substr(osPrefix.size()), prop));
166 4 : aoNameCount[osSubdir.substr(osPrefix.size())]++;
167 : }
168 : }
169 :
170 10 : if (nMaxFiles > 0 && aoProps.size() > static_cast<unsigned>(nMaxFiles))
171 0 : break;
172 : }
173 :
174 13 : bIsTruncated = aoProps.size() >= static_cast<unsigned>(nMaxFilesThisQuery);
175 13 : if (!bIsTruncated)
176 : {
177 11 : osNextMarker.clear();
178 : }
179 :
180 23 : for (size_t i = 0; i < aoProps.size(); i++)
181 : {
182 10 : std::string osSuffix;
183 12 : if (aoNameCount[aoProps[i].first] == 2 &&
184 2 : aoProps[i].second.bIsDirectory)
185 : {
186 : // Add a / suffix to disambiguish the situation
187 : // Normally we don't suffix directories with /, but we have
188 : // no alternative here
189 1 : osSuffix = "/";
190 : }
191 10 : if (nMaxFiles != 1)
192 : {
193 : std::string osCachedFilename =
194 20 : osBaseURL + "/" + CPLAWSURLEncode(osPrefix, false) +
195 30 : CPLAWSURLEncode(aoProps[i].first, false) + osSuffix;
196 : #if DEBUG_VERBOSE
197 : CPLDebug("SWIFT", "Cache %s", osCachedFilename.c_str());
198 : #endif
199 10 : SetCachedFileProp(osCachedFilename.c_str(), aoProps[i].second);
200 : }
201 10 : osFileList.AddString((aoProps[i].first + osSuffix).c_str());
202 : }
203 : }
204 :
205 : /************************************************************************/
206 : /* VSISwiftFSHandler */
207 : /************************************************************************/
208 :
209 : class VSISwiftFSHandler final : public IVSIS3LikeFSHandler
210 : {
211 : const std::string m_osPrefix;
212 : CPL_DISALLOW_COPY_ASSIGN(VSISwiftFSHandler)
213 :
214 : protected:
215 : VSICurlHandle *CreateFileHandle(const char *pszFilename) override;
216 : std::string
217 : GetURLFromFilename(const std::string &osFilename) const override;
218 :
219 28 : const char *GetDebugKey() const override
220 : {
221 28 : return "SWIFT";
222 : }
223 :
224 : IVSIS3LikeHandleHelper *CreateHandleHelper(const char *pszURI,
225 : bool bAllowNoObject) override;
226 :
227 407 : std::string GetFSPrefix() const override
228 : {
229 407 : return m_osPrefix;
230 : }
231 :
232 : char **GetFileList(const char *pszFilename, int nMaxFiles,
233 : bool *pbGotFileList) override;
234 :
235 : void ClearCache() override;
236 :
237 : VSIVirtualHandleUniquePtr
238 : CreateWriteHandle(const char *pszFilename,
239 : CSLConstList papszOptions) override;
240 :
241 : public:
242 1754 : explicit VSISwiftFSHandler(const char *pszPrefix) : m_osPrefix(pszPrefix)
243 : {
244 1754 : }
245 :
246 : ~VSISwiftFSHandler() override;
247 :
248 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
249 : int nFlags) override;
250 :
251 0 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
252 : const char *const *papszOptions) override
253 : {
254 0 : return VSICurlFilesystemHandlerBase::OpenDir(pszPath, nRecurseDepth,
255 0 : papszOptions);
256 : }
257 :
258 : const char *GetOptions() override;
259 :
260 : std::string
261 0 : GetStreamingFilename(const std::string &osFilename) const override
262 : {
263 0 : return osFilename;
264 : }
265 :
266 0 : VSIFilesystemHandler *Duplicate(const char *pszPrefix) override
267 : {
268 0 : return new VSISwiftFSHandler(pszPrefix);
269 : }
270 : };
271 :
272 : /************************************************************************/
273 : /* VSISwiftHandle */
274 : /************************************************************************/
275 :
276 : class VSISwiftHandle final : public IVSIS3LikeHandle
277 : {
278 : CPL_DISALLOW_COPY_ASSIGN(VSISwiftHandle)
279 :
280 : VSISwiftHandleHelper *m_poHandleHelper = nullptr;
281 :
282 : protected:
283 : struct curl_slist *GetCurlHeaders(const std::string &osVerb,
284 : struct curl_slist *psHeaders) override;
285 : bool Authenticate(const char *pszFilename) override;
286 :
287 : public:
288 : VSISwiftHandle(VSISwiftFSHandler *poFS, const char *pszFilename,
289 : VSISwiftHandleHelper *poHandleHelper);
290 : ~VSISwiftHandle() override;
291 : };
292 :
293 : /************************************************************************/
294 : /* CreateWriteHandle() */
295 : /************************************************************************/
296 :
297 : VSIVirtualHandleUniquePtr
298 3 : VSISwiftFSHandler::CreateWriteHandle(const char *pszFilename,
299 : CSLConstList papszOptions)
300 : {
301 : auto poHandleHelper =
302 3 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), false);
303 3 : if (poHandleHelper == nullptr)
304 0 : return nullptr;
305 : auto poHandle = std::make_unique<VSIChunkedWriteHandle>(
306 6 : this, pszFilename, poHandleHelper, papszOptions);
307 3 : return VSIVirtualHandleUniquePtr(poHandle.release());
308 : }
309 :
310 : /************************************************************************/
311 : /* ~VSISwiftFSHandler() */
312 : /************************************************************************/
313 :
314 2242 : VSISwiftFSHandler::~VSISwiftFSHandler()
315 : {
316 1121 : VSISwiftFSHandler::ClearCache();
317 1121 : VSISwiftHandleHelper::CleanMutex();
318 2242 : }
319 :
320 : /************************************************************************/
321 : /* ClearCache() */
322 : /************************************************************************/
323 :
324 1458 : void VSISwiftFSHandler::ClearCache()
325 : {
326 1458 : VSICurlFilesystemHandlerBase::ClearCache();
327 :
328 1458 : VSISwiftHandleHelper::ClearCache();
329 1458 : }
330 :
331 : /************************************************************************/
332 : /* GetOptions() */
333 : /************************************************************************/
334 :
335 2 : const char *VSISwiftFSHandler::GetOptions()
336 : {
337 : static std::string osOptions(
338 2 : std::string("<Options>") +
339 : " <Option name='SWIFT_STORAGE_URL' type='string' "
340 : "description='Storage URL. To use with SWIFT_AUTH_TOKEN'/>"
341 : " <Option name='SWIFT_AUTH_TOKEN' type='string' "
342 : "description='Authorization token'/>"
343 : " <Option name='SWIFT_AUTH_V1_URL' type='string' "
344 : "description='Authentication V1 URL. To use with SWIFT_USER and "
345 : "SWIFT_KEY'/>"
346 : " <Option name='SWIFT_USER' type='string' "
347 : "description='User name to use with authentication V1'/>"
348 : " <Option name='SWIFT_KEY' type='string' "
349 : "description='Key/password to use with authentication V1'/>"
350 : " <Option name='OS_IDENTITY_API_VERSION' type='string' "
351 : "description='OpenStack identity API version'/>"
352 : " <Option name='OS_AUTH_TYPE' type='string' "
353 : "description='Authentication URL'/>"
354 : " <Option name='OS_USERNAME' type='string' "
355 : "description='User name'/>"
356 : " <Option name='OS_PASSWORD' type='string' "
357 : "description='Password'/>"
358 : " <Option name='OS_USER_DOMAIN_NAME' type='string' "
359 : "description='User domain name'/>"
360 : " <Option name='OS_PROJECT_NAME' type='string' "
361 : "description='Project name'/>"
362 : " <Option name='OS_PROJECT_DOMAIN_NAME' type='string' "
363 : "description='Project domain name'/>"
364 : " <Option name='OS_REGION_NAME' type='string' "
365 3 : "description='Region name'/>" +
366 3 : VSICurlFilesystemHandlerBase::GetOptionsStatic() + "</Options>");
367 2 : return osOptions.c_str();
368 : }
369 :
370 : /************************************************************************/
371 : /* CreateFileHandle() */
372 : /************************************************************************/
373 :
374 21 : VSICurlHandle *VSISwiftFSHandler::CreateFileHandle(const char *pszFilename)
375 : {
376 42 : VSISwiftHandleHelper *poHandleHelper = VSISwiftHandleHelper::BuildFromURI(
377 63 : pszFilename + GetFSPrefix().size(), GetFSPrefix().c_str());
378 21 : if (poHandleHelper)
379 : {
380 19 : return new VSISwiftHandle(this, pszFilename, poHandleHelper);
381 : }
382 2 : return nullptr;
383 : }
384 :
385 : /************************************************************************/
386 : /* GetURLFromFilename() */
387 : /************************************************************************/
388 :
389 : std::string
390 21 : VSISwiftFSHandler::GetURLFromFilename(const std::string &osFilename) const
391 : {
392 : const std::string osFilenameWithoutPrefix =
393 42 : osFilename.substr(GetFSPrefix().size());
394 :
395 : auto poHandleHelper = std::unique_ptr<VSISwiftHandleHelper>(
396 : VSISwiftHandleHelper::BuildFromURI(osFilenameWithoutPrefix.c_str(),
397 42 : GetFSPrefix().c_str()));
398 21 : if (!poHandleHelper)
399 : {
400 0 : return std::string();
401 : }
402 42 : std::string osBaseURL(poHandleHelper->GetURL());
403 21 : if (!osBaseURL.empty() && osBaseURL.back() == '/')
404 2 : osBaseURL.pop_back();
405 21 : return osBaseURL;
406 : }
407 :
408 : /************************************************************************/
409 : /* CreateHandleHelper() */
410 : /************************************************************************/
411 :
412 : IVSIS3LikeHandleHelper *
413 26 : VSISwiftFSHandler::CreateHandleHelper(const char *pszURI, bool)
414 : {
415 26 : return VSISwiftHandleHelper::BuildFromURI(pszURI, GetFSPrefix().c_str());
416 : }
417 :
418 : /************************************************************************/
419 : /* Stat() */
420 : /************************************************************************/
421 :
422 13 : int VSISwiftFSHandler::Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
423 : int nFlags)
424 : {
425 13 : if (!STARTS_WITH_CI(pszFilename, GetFSPrefix().c_str()))
426 0 : return -1;
427 :
428 13 : if ((nFlags & VSI_STAT_CACHE_ONLY) != 0)
429 2 : return VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf,
430 2 : nFlags);
431 :
432 22 : std::string osFilename(pszFilename);
433 11 : if (osFilename.back() == '/')
434 6 : osFilename.pop_back();
435 :
436 11 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
437 :
438 11 : if (VSICurlFilesystemHandlerBase::Stat(pszFilename, pStatBuf, nFlags) == 0)
439 : {
440 : // if querying /vsiswift/container_name, the GET will succeed and
441 : // we would consider this as a file whereas it should be exposed as
442 : // a directory
443 6 : if (std::count(osFilename.begin(), osFilename.end(), '/') <= 2)
444 : {
445 :
446 : auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
447 1 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
448 1 : if (poHandleHelper)
449 : {
450 2 : FileProp cachedFileProp;
451 1 : cachedFileProp.eExists = EXIST_YES;
452 1 : cachedFileProp.bHasComputedFileSize = false;
453 1 : cachedFileProp.fileSize = 0;
454 1 : cachedFileProp.bIsDirectory = true;
455 1 : cachedFileProp.mTime = 0;
456 1 : cachedFileProp.nMode = S_IFDIR;
457 1 : SetCachedFileProp(poHandleHelper->GetURL().c_str(),
458 : cachedFileProp);
459 : }
460 :
461 1 : pStatBuf->st_size = 0;
462 1 : pStatBuf->st_mode = S_IFDIR;
463 : }
464 6 : return 0;
465 : }
466 :
467 : // In the case of a directory, a GET on it will not work, so we have to
468 : // query the upper directory contents
469 5 : if (std::count(osFilename.begin(), osFilename.end(), '/') < 2)
470 0 : return -1;
471 :
472 : char **papszContents =
473 5 : VSIReadDir(CPLGetPathSafe(osFilename.c_str()).c_str());
474 5 : int nRet = CSLFindStringCaseSensitive(
475 : papszContents, CPLGetFilename(osFilename.c_str())) >= 0
476 5 : ? 0
477 5 : : -1;
478 5 : CSLDestroy(papszContents);
479 :
480 10 : FileProp cachedFileProp;
481 5 : if (nRet == 0)
482 : {
483 2 : pStatBuf->st_mode = S_IFDIR;
484 :
485 2 : cachedFileProp.eExists = EXIST_YES;
486 2 : cachedFileProp.bHasComputedFileSize = false;
487 2 : cachedFileProp.fileSize = 0;
488 2 : cachedFileProp.bIsDirectory = true;
489 2 : cachedFileProp.mTime = 0;
490 2 : cachedFileProp.nMode = S_IFDIR;
491 : }
492 : else
493 : {
494 3 : cachedFileProp.eExists = EXIST_NO;
495 : }
496 :
497 : auto poHandleHelper = std::unique_ptr<IVSIS3LikeHandleHelper>(
498 5 : CreateHandleHelper(pszFilename + GetFSPrefix().size(), true));
499 5 : if (poHandleHelper)
500 : {
501 5 : SetCachedFileProp(poHandleHelper->GetURL().c_str(), cachedFileProp);
502 : }
503 :
504 5 : return nRet;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetFileList() */
509 : /************************************************************************/
510 :
511 14 : char **VSISwiftFSHandler::GetFileList(const char *pszDirname, int nMaxFiles,
512 : bool *pbGotFileList)
513 : {
514 : if (ENABLE_DEBUG)
515 : CPLDebug(GetDebugKey(), "GetFileList(%s)", pszDirname);
516 14 : *pbGotFileList = false;
517 14 : CPLAssert(strlen(pszDirname) >= GetFSPrefix().size());
518 42 : std::string osDirnameWithoutPrefix = pszDirname + GetFSPrefix().size();
519 14 : if (!osDirnameWithoutPrefix.empty() && osDirnameWithoutPrefix.back() == '/')
520 : {
521 0 : osDirnameWithoutPrefix.pop_back();
522 : }
523 :
524 28 : std::string osBucket(osDirnameWithoutPrefix);
525 28 : std::string osObjectKey;
526 14 : size_t nSlashPos = osDirnameWithoutPrefix.find('/');
527 14 : if (nSlashPos != std::string::npos)
528 : {
529 3 : osBucket = osDirnameWithoutPrefix.substr(0, nSlashPos);
530 3 : osObjectKey = osDirnameWithoutPrefix.substr(nSlashPos + 1);
531 : }
532 :
533 : IVSIS3LikeHandleHelper *poS3HandleHelper =
534 14 : CreateHandleHelper(osBucket.c_str(), true);
535 14 : if (poS3HandleHelper == nullptr)
536 : {
537 0 : return nullptr;
538 : }
539 :
540 14 : WriteFuncStruct sWriteFuncData;
541 :
542 28 : CPLStringList osFileList; // must be left in this scope !
543 28 : std::string osNextMarker; // must be left in this scope !
544 :
545 28 : std::string osMaxKeys = CPLGetConfigOption("SWIFT_MAX_KEYS", "10000");
546 14 : int nMaxFilesThisQuery = atoi(osMaxKeys.c_str());
547 14 : if (nMaxFiles > 0 && nMaxFiles <= 100 && nMaxFiles < nMaxFilesThisQuery)
548 : {
549 2 : nMaxFilesThisQuery = nMaxFiles + 1;
550 : }
551 14 : const std::string osPrefix(osObjectKey.empty() ? std::string()
552 28 : : osObjectKey + "/");
553 :
554 28 : const CPLStringList aosHTTPOptions(CPLHTTPGetOptionsFromEnv(pszDirname));
555 28 : const CPLHTTPRetryParameters oRetryParameters(aosHTTPOptions);
556 :
557 : while (true)
558 : {
559 16 : CPLHTTPRetryContext oRetryContext(oRetryParameters);
560 : bool bRetry;
561 2 : do
562 : {
563 16 : bRetry = false;
564 16 : poS3HandleHelper->ResetQueryParameters();
565 16 : std::string osBaseURL(poS3HandleHelper->GetURL());
566 :
567 16 : CURLM *hCurlMultiHandle = GetCurlMultiHandleFor(osBaseURL);
568 16 : CURL *hCurlHandle = curl_easy_init();
569 :
570 16 : if (!osBucket.empty())
571 : {
572 13 : poS3HandleHelper->AddQueryParameter("delimiter", "/");
573 13 : if (!osNextMarker.empty())
574 2 : poS3HandleHelper->AddQueryParameter("marker", osNextMarker);
575 13 : poS3HandleHelper->AddQueryParameter(
576 : "limit", CPLSPrintf("%d", nMaxFilesThisQuery));
577 13 : if (!osPrefix.empty())
578 3 : poS3HandleHelper->AddQueryParameter("prefix", osPrefix);
579 : }
580 :
581 32 : struct curl_slist *headers = VSICurlSetOptions(
582 16 : hCurlHandle, poS3HandleHelper->GetURL().c_str(),
583 : aosHTTPOptions.List());
584 : // Disable automatic redirection
585 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_FOLLOWLOCATION, 0);
586 :
587 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, nullptr);
588 :
589 16 : VSICURLInitWriteFuncStruct(&sWriteFuncData, nullptr, nullptr,
590 : nullptr);
591 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA,
592 : &sWriteFuncData);
593 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION,
594 : VSICurlHandleWriteFunc);
595 :
596 16 : WriteFuncStruct sWriteFuncHeaderData;
597 16 : VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, nullptr, nullptr,
598 : nullptr);
599 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA,
600 : &sWriteFuncHeaderData);
601 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION,
602 : VSICurlHandleWriteFunc);
603 :
604 16 : char szCurlErrBuf[CURL_ERROR_SIZE + 1] = {};
605 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER,
606 : szCurlErrBuf);
607 :
608 16 : headers = poS3HandleHelper->GetCurlHeaders("GET", headers);
609 16 : unchecked_curl_easy_setopt(hCurlHandle, CURLOPT_HTTPHEADER,
610 : headers);
611 :
612 16 : VSICURLMultiPerform(hCurlMultiHandle, hCurlHandle);
613 :
614 16 : VSICURLResetHeaderAndWriterFunctions(hCurlHandle);
615 :
616 16 : if (headers != nullptr)
617 16 : curl_slist_free_all(headers);
618 :
619 16 : if (sWriteFuncData.pBuffer == nullptr)
620 : {
621 3 : delete poS3HandleHelper;
622 3 : curl_easy_cleanup(hCurlHandle);
623 3 : CPLFree(sWriteFuncHeaderData.pBuffer);
624 3 : return nullptr;
625 : }
626 :
627 13 : long response_code = 0;
628 13 : curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
629 13 : if (response_code != 200)
630 : {
631 : // Look if we should attempt a retry
632 0 : if (oRetryContext.CanRetry(static_cast<int>(response_code),
633 0 : sWriteFuncHeaderData.pBuffer,
634 : szCurlErrBuf))
635 : {
636 0 : CPLError(CE_Warning, CPLE_AppDefined,
637 : "HTTP error code: %d - %s. "
638 : "Retrying again in %.1f secs",
639 : static_cast<int>(response_code),
640 0 : poS3HandleHelper->GetURL().c_str(),
641 : oRetryContext.GetCurrentDelay());
642 0 : CPLSleep(oRetryContext.GetCurrentDelay());
643 0 : bRetry = true;
644 0 : CPLFree(sWriteFuncData.pBuffer);
645 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
646 : }
647 : else
648 : {
649 0 : CPLDebug(GetDebugKey(), "%s", sWriteFuncData.pBuffer);
650 0 : CPLFree(sWriteFuncData.pBuffer);
651 0 : CPLFree(sWriteFuncHeaderData.pBuffer);
652 0 : delete poS3HandleHelper;
653 0 : curl_easy_cleanup(hCurlHandle);
654 0 : return nullptr;
655 : }
656 : }
657 : else
658 : {
659 13 : *pbGotFileList = true;
660 : bool bIsTruncated;
661 13 : AnalyseSwiftFileList(
662 13 : osBaseURL, osPrefix, sWriteFuncData.pBuffer, osFileList,
663 : nMaxFilesThisQuery, nMaxFiles, bIsTruncated, osNextMarker);
664 :
665 13 : CPLFree(sWriteFuncData.pBuffer);
666 13 : CPLFree(sWriteFuncHeaderData.pBuffer);
667 :
668 13 : if (osNextMarker.empty())
669 : {
670 11 : delete poS3HandleHelper;
671 11 : curl_easy_cleanup(hCurlHandle);
672 11 : return osFileList.StealList();
673 : }
674 : }
675 :
676 2 : curl_easy_cleanup(hCurlHandle);
677 : } while (bRetry);
678 2 : }
679 : }
680 :
681 : /************************************************************************/
682 : /* VSISwiftHandle() */
683 : /************************************************************************/
684 :
685 19 : VSISwiftHandle::VSISwiftHandle(VSISwiftFSHandler *poFSIn,
686 : const char *pszFilename,
687 19 : VSISwiftHandleHelper *poHandleHelper)
688 19 : : IVSIS3LikeHandle(poFSIn, pszFilename, poHandleHelper->GetURL().c_str()),
689 19 : m_poHandleHelper(poHandleHelper)
690 : {
691 19 : }
692 :
693 : /************************************************************************/
694 : /* ~VSISwiftHandle() */
695 : /************************************************************************/
696 :
697 38 : VSISwiftHandle::~VSISwiftHandle()
698 : {
699 19 : delete m_poHandleHelper;
700 38 : }
701 :
702 : /************************************************************************/
703 : /* GetCurlHeaders() */
704 : /************************************************************************/
705 :
706 17 : struct curl_slist *VSISwiftHandle::GetCurlHeaders(const std::string &osVerb,
707 : struct curl_slist *psHeaders)
708 : {
709 17 : return m_poHandleHelper->GetCurlHeaders(osVerb, psHeaders);
710 : }
711 :
712 : /************************************************************************/
713 : /* Authenticate() */
714 : /************************************************************************/
715 :
716 0 : bool VSISwiftHandle::Authenticate(const char *pszFilename)
717 : {
718 0 : return m_poHandleHelper->Authenticate(pszFilename);
719 : }
720 :
721 : } /* end of namespace cpl */
722 :
723 : #endif // DOXYGEN_SKIP
724 : //! @endcond
725 :
726 : /************************************************************************/
727 : /* VSIInstallSwiftFileHandler() */
728 : /************************************************************************/
729 :
730 : /*!
731 : \brief Install /vsiswift/ OpenStack Swif Object Storage (Swift) file
732 : system handler (requires libcurl)
733 :
734 : \verbatim embed:rst
735 : See :ref:`/vsiswift/ documentation <vsiswift>`
736 : \endverbatim
737 :
738 : @since GDAL 2.3
739 : */
740 1754 : void VSIInstallSwiftFileHandler(void)
741 : {
742 1754 : VSIFileManager::InstallHandler("/vsiswift/",
743 1754 : new cpl::VSISwiftFSHandler("/vsiswift/"));
744 1754 : }
745 :
746 : #endif /* HAVE_CURL */
|