s3file.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "jlib.hpp"
  15. #include "jio.hpp"
  16. #include "jmutex.hpp"
  17. #include "jfile.hpp"
  18. #include "jregexp.hpp"
  19. #include "jstring.hpp"
  20. #include "jlog.hpp"
  21. #include "s3file.hpp"
  22. #include <aws/core/Aws.h>
  23. #include <aws/core/auth/AWSCredentialsProvider.h>
  24. #include <aws/s3/S3Client.h>
  25. #include <aws/s3/model/GetObjectRequest.h>
  26. #include <aws/s3/model/HeadObjectRequest.h>
  27. #include <aws/s3/model/PutObjectRequest.h>
  28. #include <aws/s3/model/DeleteObjectRequest.h>
  29. /*
  30. * S3 questions:
  31. *
  32. * Where would we store access id/secrets?
  33. * Current use the default default credential manager which gets them from environment variables, or passed in
  34. * when running on an AWS instance.
  35. * What is the latency on file access? ~200ms.
  36. * What is the cost of accessing the data? $0.4/million GET requests (including requesting meta)
  37. * How to you efficiently page results from a S3 bucket? You can perform a range get, but you'll need to pay for each call.
  38. * You can get the length of an object using HeadObject..getContentLength, but you will get charged - so for small files it is better to just get it.
  39. * Probably best to request the first 10Mb, and if that returns a full 10Mb then request the size information.
  40. * S3 does supports partial writes in chunks of 5Mb or more. It may be simpler to create a file locally and then submit it in a single action.
  41. *
  42. * This is currently a proof of concept implementations. The following changes are required for a production version:
  43. * - Revisit credentials
  44. * - Support chunked writes.
  45. * - Implement directory iteration.
  46. * - Ideally switch engines to use streaming interfaces for reading and writing files.
  47. * - Investigate adding a read-ahead thread to read the next block of the file.
  48. */
  49. //#define TRACE_S3
  50. //#define TEST_S3_PAGING
  51. //#define FIXED_CREDENTIALS
  52. constexpr const char * s3FilePrefix = "s3://";
  53. #ifdef TEST_S3_PAGING
  54. constexpr offset_t awsReadRequestSize = 50;
  55. #else
  56. constexpr offset_t awsReadRequestSize = 0x400000; // Default to requesting 4Mb each time
  57. #endif
  58. static Aws::SDKOptions options;
  59. MODULE_INIT(INIT_PRIORITY_HQLINTERNAL)
  60. {
  61. Aws::InitAPI(options);
  62. return true;
  63. }
  64. MODULE_EXIT()
  65. {
  66. Aws::ShutdownAPI(options);
  67. }
  68. //---------------------------------------------------------------------------------------------------------------------
  69. class S3File;
  70. class S3FileReadIO : implements CInterfaceOf<IFileIO>
  71. {
  72. public:
  73. S3FileReadIO(S3File * _file, Aws::S3::Model::GetObjectOutcome & firstRead, FileIOStats & _stats);
  74. virtual size32_t read(offset_t pos, size32_t len, void * data) override;
  75. virtual offset_t size() override;
  76. virtual void close() override
  77. {
  78. //This could set a flag here to check for reading after close(), but I don't think any file read code
  79. //ever calls close, and it would be harmless (and would complicate the rest of the code).
  80. }
  81. // Write methods not implemented - this is a read-only file
  82. virtual size32_t write(offset_t pos, size32_t len, const void * data) override
  83. {
  84. throwUnexpected();
  85. return 0;
  86. }
  87. virtual offset_t appendFile(IFile *file,offset_t pos=0,offset_t len=(offset_t)-1) override
  88. {
  89. throwUnexpected();
  90. return 0;
  91. }
  92. virtual void setSize(offset_t size) override
  93. {
  94. throwUnexpected();
  95. }
  96. virtual void flush() override
  97. {
  98. //Could implement if we use the async version of the putObject call.
  99. }
  100. unsigned __int64 getStatistic(StatisticKind kind) override;
  101. protected:
  102. size_t extractDataFromResult(size_t offset, size_t length, void * target);
  103. protected:
  104. Linked<S3File> file;
  105. CriticalSection cs;
  106. offset_t startResultOffset = 0;
  107. offset_t endResultOffset = 0;
  108. Aws::S3::Model::GetObjectOutcome readResult;
  109. FileIOStats stats;
  110. };
  111. class S3FileWriteIO : implements CInterfaceOf<IFileIO>
  112. {
  113. public:
  114. S3FileWriteIO(S3File * _file);
  115. virtual void beforeDispose() override;
  116. virtual size32_t read(offset_t pos, size32_t len, void * data) override
  117. {
  118. throwUnexpected();
  119. }
  120. virtual offset_t size() override
  121. {
  122. throwUnexpected();
  123. }
  124. virtual void close() override;
  125. virtual offset_t appendFile(IFile *file,offset_t pos=0,offset_t len=(offset_t)-1) override;
  126. virtual size32_t write(offset_t pos, size32_t len, const void * data) override;
  127. virtual void setSize(offset_t size) override;
  128. virtual void flush() override;
  129. virtual unsigned __int64 getStatistic(StatisticKind kind) override;
  130. protected:
  131. Linked<S3File> file;
  132. CriticalSection cs;
  133. FileIOStats stats;
  134. bool blobWritten = false;
  135. };
  136. class S3File : implements CInterfaceOf<IFile>
  137. {
  138. friend class S3FileReadIO;
  139. friend class S3FileWriteIO;
  140. public:
  141. S3File(const char *_s3FileName);
  142. virtual bool exists() override
  143. {
  144. ensureMetaData();
  145. return fileExists;
  146. }
  147. virtual bool getTime(CDateTime * createTime, CDateTime * modifiedTime, CDateTime * accessedTime) override;
  148. virtual fileBool isDirectory() override
  149. {
  150. ensureMetaData();
  151. if (!fileExists)
  152. return fileBool::notFound;
  153. return isDir ? fileBool::foundYes : fileBool::foundNo;
  154. }
  155. virtual fileBool isFile() override
  156. {
  157. ensureMetaData();
  158. if (!fileExists)
  159. return fileBool::notFound;
  160. return !isDir ? fileBool::foundYes : fileBool::foundNo;
  161. }
  162. virtual fileBool isReadOnly() override
  163. {
  164. ensureMetaData();
  165. if (!fileExists)
  166. return fileBool::notFound;
  167. return fileBool::foundYes;
  168. }
  169. virtual IFileIO * open(IFOmode mode, IFEflags extraFlags=IFEnone) override
  170. {
  171. if (mode == IFOcreate)
  172. return createFileWriteIO();
  173. assertex(mode==IFOread && fileExists);
  174. return createFileReadIO();
  175. }
  176. virtual IFileAsyncIO * openAsync(IFOmode mode) override
  177. {
  178. UNIMPLEMENTED;
  179. }
  180. virtual IFileIO * openShared(IFOmode mode, IFSHmode shmode, IFEflags extraFlags=IFEnone) override
  181. {
  182. if (mode == IFOcreate)
  183. return createFileWriteIO();
  184. assertex(mode==IFOread && fileExists);
  185. return createFileReadIO();
  186. }
  187. virtual const char * queryFilename() override
  188. {
  189. return fullName.str();
  190. }
  191. virtual offset_t size() override
  192. {
  193. ensureMetaData();
  194. return fileSize;
  195. }
  196. // Directory functions
  197. virtual IDirectoryIterator *directoryFiles(const char *mask, bool sub, bool includeDirs) override
  198. {
  199. UNIMPLEMENTED;
  200. }
  201. virtual bool getInfo(bool &isdir,offset_t &size,CDateTime &modtime) override
  202. {
  203. ensureMetaData();
  204. isdir = isDir;
  205. size = fileSize;
  206. modtime.clear();
  207. return true;
  208. }
  209. // Not going to be implemented - this IFile interface is too big..
  210. virtual bool setTime(const CDateTime * createTime, const CDateTime * modifiedTime, const CDateTime * accessedTime) override { UNIMPLEMENTED; }
  211. virtual bool remove() override;
  212. virtual void rename(const char *newTail) override { UNIMPLEMENTED; }
  213. virtual void move(const char *newName) override { UNIMPLEMENTED; }
  214. virtual void setReadOnly(bool ro) override { UNIMPLEMENTED; }
  215. virtual void setFilePermissions(unsigned fPerms) override { UNIMPLEMENTED; }
  216. virtual bool setCompression(bool set) override { UNIMPLEMENTED; }
  217. virtual offset_t compressedSize() override { UNIMPLEMENTED; }
  218. virtual unsigned getCRC() override { UNIMPLEMENTED; }
  219. virtual void setCreateFlags(unsigned short cflags) override { UNIMPLEMENTED; }
  220. virtual void setShareMode(IFSHmode shmode) override { UNIMPLEMENTED; }
  221. virtual bool createDirectory() override { UNIMPLEMENTED; }
  222. virtual IDirectoryDifferenceIterator *monitorDirectory(
  223. IDirectoryIterator *prev=NULL, // in (NULL means use current as baseline)
  224. const char *mask=NULL,
  225. bool sub=false,
  226. bool includedirs=false,
  227. unsigned checkinterval=60*1000,
  228. unsigned timeout=(unsigned)-1,
  229. Semaphore *abortsem=NULL) override { UNIMPLEMENTED; }
  230. virtual void copySection(const RemoteFilename &dest, offset_t toOfs=(offset_t)-1, offset_t fromOfs=0, offset_t size=(offset_t)-1, ICopyFileProgress *progress=NULL, CFflags copyFlags=CFnone) override { UNIMPLEMENTED; }
  231. virtual void copyTo(IFile *dest, size32_t buffersize=DEFAULT_COPY_BLKSIZE, ICopyFileProgress *progress=NULL, bool usetmp=false, CFflags copyFlags=CFnone) override { UNIMPLEMENTED; }
  232. virtual IMemoryMappedFile *openMemoryMapped(offset_t ofs=0, memsize_t len=(memsize_t)-1, bool write=false) override { UNIMPLEMENTED; }
  233. protected:
  234. void readBlob(Aws::S3::Model::GetObjectOutcome & readResult, FileIOStats & stats, offset_t from = 0, offset_t length = unknownFileSize);
  235. size32_t writeBlob(size32_t len, const void * data, FileIOStats & stats);
  236. void ensureMetaData();
  237. void gatherMetaData();
  238. IFileIO * createFileReadIO();
  239. IFileIO * createFileWriteIO();
  240. protected:
  241. StringBuffer fullName;
  242. StringBuffer bucketName;
  243. StringBuffer objectName;
  244. offset_t fileSize = unknownFileSize;
  245. bool haveMeta = false;
  246. bool isDir = false;
  247. bool fileExists = false;
  248. int64_t modifiedMsTime = 0;
  249. CriticalSection cs;
  250. };
  251. //---------------------------------------------------------------------------------------------------------------------
  252. S3FileReadIO::S3FileReadIO(S3File * _file, Aws::S3::Model::GetObjectOutcome & firstRead, FileIOStats & _firstStats)
  253. : file(_file), readResult(std::move(firstRead)), stats(_firstStats)
  254. {
  255. startResultOffset = 0;
  256. endResultOffset = readResult.GetResult().GetContentLength();
  257. }
  258. size32_t S3FileReadIO::read(offset_t pos, size32_t len, void * data)
  259. {
  260. if (pos > file->fileSize)
  261. return 0;
  262. if (pos + len > file->fileSize)
  263. len = file->fileSize - pos;
  264. if (len == 0)
  265. return 0;
  266. size32_t sizeRead = 0;
  267. offset_t lastOffset = pos + len;
  268. // MORE: Do we ever read file IO from more than one thread? I'm not convinced we do, and the critical blocks waste space and slow it down.
  269. //It might be worth revisiting (although I'm not sure what effect stranding has) - by revisiting the primary interface used to read files.
  270. CriticalBlock block(cs);
  271. for(;;)
  272. {
  273. //Check if part of the request can be fulfilled from the current read block
  274. if (pos >= startResultOffset && pos < endResultOffset)
  275. {
  276. size_t copySize = ((lastOffset > endResultOffset) ? endResultOffset : lastOffset) - pos;
  277. size_t extractedSize = extractDataFromResult((pos - startResultOffset), copySize, data);
  278. assertex(copySize == extractedSize);
  279. pos += copySize;
  280. len -= copySize;
  281. data = (byte *)data + copySize;
  282. sizeRead += copySize;
  283. if (len == 0)
  284. return sizeRead;
  285. }
  286. #ifdef TEST_S3_PAGING
  287. offset_t readSize = awsReadRequestSize;
  288. #else
  289. offset_t readSize = (len > awsReadRequestSize) ? len : awsReadRequestSize;
  290. #endif
  291. file->readBlob(readResult, stats, pos, readSize);
  292. if (!readResult.IsSuccess())
  293. return sizeRead;
  294. offset_t contentSize = readResult.GetResult().GetContentLength();
  295. //If the results are inconsistent then do not loop forever
  296. if (contentSize == 0)
  297. return sizeRead;
  298. startResultOffset = pos;
  299. endResultOffset = pos + contentSize;
  300. }
  301. }
  302. offset_t S3FileReadIO::size()
  303. {
  304. return file->fileSize;
  305. }
  306. size_t S3FileReadIO::extractDataFromResult(size_t offset, size_t length, void * target)
  307. {
  308. auto & contents = readResult.GetResultWithOwnership().GetBody();
  309. auto buffer = contents.rdbuf();
  310. buffer->pubseekoff(0, std::ios_base::beg, std::ios_base::in);
  311. return buffer->sgetn((char *)target, length);
  312. }
  313. unsigned __int64 S3FileReadIO::getStatistic(StatisticKind kind)
  314. {
  315. return stats.getStatistic(kind);
  316. }
  317. //---------------------------------------------------------------------------------------------------------------------
  318. S3FileWriteIO::S3FileWriteIO(S3File * _file)
  319. : file(_file)
  320. {
  321. }
  322. void S3FileWriteIO::beforeDispose()
  323. {
  324. try
  325. {
  326. close();
  327. }
  328. catch (...)
  329. {
  330. }
  331. }
  332. void S3FileWriteIO::close()
  333. {
  334. CriticalBlock block(cs);
  335. if (!blobWritten)
  336. file->writeBlob(0, nullptr, stats);
  337. }
  338. offset_t S3FileWriteIO::appendFile(IFile *file, offset_t pos, offset_t len)
  339. {
  340. throwUnexpected();
  341. return 0;
  342. }
  343. size32_t S3FileWriteIO::write(offset_t pos, size32_t len, const void * data)
  344. {
  345. if (len)
  346. {
  347. CriticalBlock block(cs);
  348. //Very strange semantics for a proof of concept - only allow a single write to the file.
  349. //A full implementation will need to either
  350. // write to a temporary file, and then copy to the s3 file when the file is closed.
  351. // use the multi-part upload functionality (has a minimum part size of 5Mb)
  352. assertex(!blobWritten);
  353. file->writeBlob(len, data, stats);
  354. blobWritten = true;
  355. }
  356. return len;
  357. }
  358. void S3FileWriteIO::setSize(offset_t size)
  359. {
  360. UNIMPLEMENTED;
  361. }
  362. void S3FileWriteIO::flush()
  363. {
  364. }
  365. unsigned __int64 S3FileWriteIO::getStatistic(StatisticKind kind)
  366. {
  367. return stats.getStatistic(kind);
  368. }
  369. //---------------------------------------------------------------------------------------------------------------------
  370. static Aws::S3::S3Client createAwsClient()
  371. {
  372. //There should be a default region, (and a default client), but allow the region to be overridden with s3:@region//,
  373. //in which a new client would be created.
  374. // Set up the request
  375. Aws::Client::ClientConfiguration configuration;
  376. configuration.region = "eu-west-2";
  377. #ifdef FIXED_CREDENTIALS
  378. //The following code allows the access id/secret to come from a value that had been saved away in a secrets manager
  379. constexpr const char * myAccessKeyId = "<id>";
  380. constexpr const char * myAccessKeySecret = "<secret>";
  381. auto credentials = std::make_shared<Aws::Auth::SimpleAWSCredentialsProvider>(Aws::String(myAccessKeyId), Aws::String(myAccessKeySecret));
  382. return Aws::S3::S3Client(credentials, configuration);
  383. #else
  384. //Retrieve the details from environment variables/file/current environment
  385. return Aws::S3::S3Client(configuration);
  386. #endif
  387. }
  388. static Aws::S3::S3Client & getAwsClient()
  389. {
  390. static Aws::S3::S3Client client = createAwsClient();
  391. return client;
  392. }
  393. S3File::S3File(const char *_s3FileName) : fullName(_s3FileName)
  394. {
  395. const char * filename = fullName.str() + strlen(s3FilePrefix);
  396. const char * slash = strchr(filename, '/');
  397. assertex(slash);
  398. bucketName.append(slash-filename, filename);
  399. objectName.set(slash+1);
  400. }
  401. bool S3File::getTime(CDateTime * createTime, CDateTime * modifiedTime, CDateTime * accessedTime)
  402. {
  403. ensureMetaData();
  404. if (createTime)
  405. {
  406. createTime->clear();
  407. //Creation date does not seem to be available, so use the last modified date instead.
  408. createTime->set((time_t)(modifiedMsTime / 1000));
  409. }
  410. if (modifiedTime)
  411. {
  412. modifiedTime->clear();
  413. modifiedTime->set((time_t)(modifiedMsTime / 1000));
  414. }
  415. if (accessedTime)
  416. accessedTime->clear();
  417. return false;
  418. }
  419. void S3File::readBlob(Aws::S3::Model::GetObjectOutcome & readResult, FileIOStats & stats, offset_t from, offset_t length)
  420. {
  421. Aws::S3::S3Client & s3_client = getAwsClient();
  422. Aws::S3::Model::GetObjectRequest object_request;
  423. object_request.SetBucket(bucketName);
  424. object_request.SetKey(objectName);
  425. if ((from != 0) || (length != unknownFileSize))
  426. {
  427. StringBuffer range;
  428. range.append("bytes=").append(from).append("-");
  429. if (length != unknownFileSize)
  430. range.append(from + length - 1);
  431. object_request.SetRange(Aws::String(range));
  432. }
  433. // Get the object
  434. CCycleTimer timer;
  435. readResult = s3_client.GetObject(object_request);
  436. stats.ioReads++;
  437. stats.ioReadCycles += timer.elapsedCycles();
  438. stats.ioReadBytes += readResult.GetResult().GetContentLength();
  439. #ifdef TRACE_S3
  440. if (!readResult.IsSuccess())
  441. {
  442. auto error = readResult.GetError();
  443. DBGLOG("ERROR: %s: %s", error.GetExceptionName().c_str(), error.GetMessage().c_str());
  444. }
  445. #endif
  446. }
  447. size32_t S3File::writeBlob(size32_t len, const void * data, FileIOStats & stats)
  448. {
  449. Aws::S3::S3Client & s3_client = getAwsClient();
  450. Aws::S3::Model::PutObjectOutcome writeResult;
  451. Aws::S3::Model::PutObjectRequest writeRequest;
  452. writeRequest.WithBucket(bucketName).WithKey(objectName);
  453. auto body = std::make_shared<std::stringstream>(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
  454. body->write(reinterpret_cast<const char*>(data), len);
  455. writeRequest.SetBody(body);
  456. CCycleTimer timer;
  457. writeResult = s3_client.PutObject(writeRequest);
  458. stats.ioWrites++;
  459. stats.ioWriteCycles += timer.elapsedCycles();
  460. stats.ioWriteBytes += len;
  461. #ifdef TRACE_S3
  462. if (!writeResult.IsSuccess())
  463. {
  464. auto error = writeResult.GetError();
  465. DBGLOG("ERROR: %s: %s", error.GetExceptionName().c_str(), error.GetMessage().c_str());
  466. return 0;
  467. }
  468. #endif
  469. return len;
  470. }
  471. IFileIO * S3File::createFileReadIO()
  472. {
  473. //Read the first chunk of the file. If it is the full file then fill in the meta information, otherwise
  474. //ensure the meta information is calculated before creating the file IO object
  475. Aws::S3::Model::GetObjectOutcome readResult;
  476. FileIOStats readStats;
  477. CriticalBlock block(cs);
  478. readBlob(readResult, readStats, 0, awsReadRequestSize);
  479. if (!readResult.IsSuccess())
  480. return nullptr;
  481. if (!haveMeta)
  482. {
  483. offset_t readSize = readResult.GetResult().GetContentLength();
  484. //If we read the entire file then we don't need to gather the meta to discover the file size.
  485. if (readSize < awsReadRequestSize)
  486. {
  487. haveMeta = true;
  488. fileExists = true;
  489. fileSize = readResult.GetResult().GetContentLength();
  490. modifiedMsTime = readResult.GetResult().GetLastModified().Millis();
  491. }
  492. else
  493. {
  494. gatherMetaData();
  495. if (!fileExists)
  496. {
  497. DBGLOG("Internal consistency - read succeeded but head failed.");
  498. return nullptr;
  499. }
  500. }
  501. }
  502. return new S3FileReadIO(this, readResult, readStats);
  503. }
  504. IFileIO * S3File::createFileWriteIO()
  505. {
  506. return new S3FileWriteIO(this);
  507. }
  508. void S3File::ensureMetaData()
  509. {
  510. CriticalBlock block(cs);
  511. if (haveMeta)
  512. return;
  513. gatherMetaData();
  514. }
  515. void S3File::gatherMetaData()
  516. {
  517. Aws::S3::S3Client & s3_client = getAwsClient();
  518. Aws::S3::Model::HeadObjectRequest request;
  519. request.SetBucket(bucketName);
  520. request.SetKey(objectName);
  521. // Get the object
  522. Aws::S3::Model::HeadObjectOutcome headResult = s3_client.HeadObject(request);
  523. if (headResult.IsSuccess())
  524. {
  525. fileExists = true;
  526. fileSize = headResult.GetResult().GetContentLength();
  527. modifiedMsTime = headResult.GetResult().GetLastModified().Millis();
  528. }
  529. else
  530. {
  531. #ifdef TRACE_S3
  532. auto error = headResult.GetError();
  533. DBGLOG("ERROR: %s: %s", error.GetExceptionName().c_str(), error.GetMessage().c_str());
  534. #endif
  535. }
  536. haveMeta = true;
  537. }
  538. bool S3File::remove()
  539. {
  540. Aws::S3::S3Client & s3_client = getAwsClient();
  541. Aws::S3::Model::DeleteObjectRequest object_request;
  542. object_request.SetBucket(bucketName);
  543. object_request.SetKey(objectName);
  544. // Get the object
  545. Aws::S3::Model::DeleteObjectOutcome result = s3_client.DeleteObject(object_request);
  546. if (result.IsSuccess())
  547. {
  548. CriticalBlock block(cs);
  549. haveMeta = true;
  550. fileExists = false;
  551. fileSize = unknownFileSize;
  552. return true;
  553. }
  554. else
  555. {
  556. #ifdef TRACE_S3
  557. auto error = result.GetError();
  558. DBGLOG("ERROR: S3 Delete Object %s: %s", error.GetExceptionName().c_str(), error.GetMessage().c_str());
  559. #endif
  560. return false;
  561. }
  562. }
  563. //---------------------------------------------------------------------------------------------------------------------
  564. static IFile *createS3File(const char *s3FileName)
  565. {
  566. return new S3File(s3FileName);
  567. }
  568. //---------------------------------------------------------------------------------------------------------------------
  569. class S3FileHook : public CInterfaceOf<IContainedFileHook>
  570. {
  571. public:
  572. virtual IFile * createIFile(const char *fileName)
  573. {
  574. if (isS3FileName(fileName))
  575. return createS3File(fileName);
  576. else
  577. return NULL;
  578. }
  579. protected:
  580. static bool isS3FileName(const char *fileName)
  581. {
  582. if (!startsWith(fileName, s3FilePrefix))
  583. return false;
  584. const char * filename = fileName + strlen(s3FilePrefix);
  585. const char * slash = strchr(filename, '/');
  586. if (!slash)
  587. return false;
  588. return true;
  589. }
  590. } *s3FileHook;
  591. static CriticalSection *cs;
  592. extern S3FILE_API void installFileHook()
  593. {
  594. CriticalBlock b(*cs); // Probably overkill!
  595. if (!s3FileHook)
  596. {
  597. s3FileHook = new S3FileHook;
  598. addContainedFileHook(s3FileHook);
  599. }
  600. }
  601. extern S3FILE_API void removeFileHook()
  602. {
  603. if (cs)
  604. {
  605. CriticalBlock b(*cs); // Probably overkill!
  606. if (s3FileHook)
  607. {
  608. removeContainedFileHook(s3FileHook);
  609. delete s3FileHook;
  610. s3FileHook = NULL;
  611. }
  612. }
  613. }
  614. MODULE_INIT(INIT_PRIORITY_STANDARD)
  615. {
  616. cs = new CriticalSection;
  617. s3FileHook = NULL; // Not really needed, but you have to have a modinit to match a modexit
  618. return true;
  619. }
  620. MODULE_EXIT()
  621. {
  622. if (s3FileHook)
  623. {
  624. removeContainedFileHook(s3FileHook);
  625. s3FileHook = NULL;
  626. }
  627. ::Release(s3FileHook);
  628. delete cs;
  629. cs = NULL;
  630. }