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