@@ -36,6 +36,21 @@ namespace Aws
3636 return (path.find_last_of (' /' ) == path.size () - 1 || path.find_last_of (' \\ ' ) == path.size () - 1 );
3737 }
3838
39+ static Aws::String GenerateTempFilePath (const Aws::String& targetPath)
40+ {
41+ static const char * HEX_CHARS = " 0123456789abcdef" ;
42+ char uniqueId[9 ] = {0 };
43+ for (int i = 0 ; i < 8 ; ++i) {
44+ uniqueId[i] = HEX_CHARS[rand () % 16 ];
45+ }
46+
47+ size_t lastSlash = targetPath.find_last_of (" /\\ " );
48+ if (lastSlash != Aws::String::npos) {
49+ return targetPath.substr (0 , lastSlash + 1 ) + " .s3tmp." + uniqueId;
50+ }
51+ return " .s3tmp." + Aws::String (uniqueId);
52+ }
53+
3954 template <typename RequestT>
4055 static void SetChecksumOnRequest (RequestT& request, S3::Model::ChecksumAlgorithm checksumAlgorithm, const Aws::String& checksum) {
4156 if (checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC64NVME) {
@@ -231,15 +246,19 @@ namespace Aws
231246 const DownloadConfiguration& downloadConfig,
232247 const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context)
233248 {
249+ Aws::String tempFilePath = GenerateTempFilePath (writeToFile);
250+
234251#ifdef _MSC_VER
235- auto createFileFn = [=]() { return Aws::New<Aws::FStream>(CLASS_TAG, Aws::Utils::StringUtils::ToWString (writeToFile .c_str ()).c_str (),
252+ auto createFileFn = [=]() { return Aws::New<Aws::FStream>(CLASS_TAG, Aws::Utils::StringUtils::ToWString (tempFilePath .c_str ()).c_str (),
236253 std::ios_base::out | std::ios_base::in | std::ios_base::binary | std::ios_base::trunc);};
237254#else
238- auto createFileFn = [=]() { return Aws::New<Aws::FStream>(CLASS_TAG, writeToFile .c_str (),
255+ auto createFileFn = [=]() { return Aws::New<Aws::FStream>(CLASS_TAG, tempFilePath .c_str (),
239256 std::ios_base::out | std::ios_base::in | std::ios_base::binary | std::ios_base::trunc);};
240257#endif
241258
242- return DownloadFile (bucketName, keyName, createFileFn, downloadConfig, writeToFile, context);
259+ auto handle = DownloadFile (bucketName, keyName, createFileFn, downloadConfig, writeToFile, context);
260+ handle->SetTempFilePath (tempFilePath);
261+ return handle;
243262 }
244263
245264 std::shared_ptr<TransferHandle> TransferManager::RetryUpload (const Aws::String& fileName, const std::shared_ptr<TransferHandle>& retryHandle)
@@ -939,16 +958,12 @@ namespace Aws
939958 handle->ChangePartToCompleted (partState, getObjectOutcome.GetResult ().GetETag ());
940959 getObjectOutcome.GetResult ().GetBody ().flush ();
941960
942- if (!ValidateDownloadChecksum (handle)) {
943- Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
944- Aws::S3::S3Errors::INTERNAL_FAILURE,
945- " ChecksumMismatch" ,
946- " Downloaded file checksum does not match expected checksum" ,
947- false );
961+ auto checksumOutcome = ValidateDownloadChecksum (handle);
962+ if (!checksumOutcome.IsSuccess ()) {
948963 handle->ChangePartToFailed (partState);
949964 handle->UpdateStatus (TransferStatus::FAILED);
950- handle->SetError (checksumError );
951- TriggerErrorCallback (handle, checksumError );
965+ handle->SetError (checksumOutcome. GetError () );
966+ TriggerErrorCallback (handle, checksumOutcome. GetError () );
952967 TriggerTransferStatusUpdatedCallback (handle);
953968 return ;
954969 }
@@ -961,7 +976,12 @@ namespace Aws
961976 << " ] Failed to download object to Bucket: [" << handle->GetBucketName () << " ] with Key: ["
962977 << handle->GetKey () << " ] " << getObjectOutcome.GetError ());
963978 handle->ChangePartToFailed (partState);
964- handle->UpdateStatus (DetermineIfFailedOrCanceled (*handle));
979+ auto finalStatus = DetermineIfFailedOrCanceled (*handle);
980+ handle->UpdateStatus (finalStatus);
981+
982+ if (finalStatus == TransferStatus::FAILED && !handle->GetTempFilePath ().empty ()) {
983+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
984+ }
965985 handle->SetError (getObjectOutcome.GetError ());
966986
967987 TriggerErrorCallback (handle, getObjectOutcome.GetError ());
@@ -1271,16 +1291,11 @@ namespace Aws
12711291 {
12721292 outcome.GetResult ().GetBody ().flush ();
12731293
1274- // Validate checksum if available
1275- if (!ValidateDownloadChecksum (handle)) {
1276- Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
1277- Aws::S3::S3Errors::INTERNAL_FAILURE,
1278- " ChecksumMismatch" ,
1279- " Downloaded file checksum does not match expected checksum" ,
1280- false );
1294+ auto checksumOutcome = ValidateDownloadChecksum (handle);
1295+ if (!checksumOutcome.IsSuccess ()) {
12811296 handle->UpdateStatus (TransferStatus::FAILED);
1282- handle->SetError (checksumError );
1283- TriggerErrorCallback (handle, checksumError );
1297+ handle->SetError (checksumOutcome. GetError () );
1298+ TriggerErrorCallback (handle, checksumOutcome. GetError () );
12841299 TriggerTransferStatusUpdatedCallback (handle);
12851300 partState->SetDownloadPartStream (nullptr );
12861301 return ;
@@ -1290,7 +1305,12 @@ namespace Aws
12901305 }
12911306 else
12921307 {
1293- handle->UpdateStatus (DetermineIfFailedOrCanceled (*handle));
1308+ auto finalStatus = DetermineIfFailedOrCanceled (*handle);
1309+ handle->UpdateStatus (finalStatus);
1310+
1311+ if (finalStatus == TransferStatus::FAILED && !handle->GetTempFilePath ().empty ()) {
1312+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1313+ }
12941314 }
12951315 TriggerTransferStatusUpdatedCallback (handle);
12961316 }
@@ -1641,9 +1661,21 @@ namespace Aws
16411661 }
16421662 }
16431663
1644- bool TransferManager::ValidateDownloadChecksum (const std::shared_ptr<TransferHandle>& handle) {
1645- if (handle->GetChecksum ().empty () || handle->GetTargetFilePath ().empty ()) {
1646- return true ;
1664+ Aws::Utils::Outcome<Aws::NoResult, Aws::Client::AWSError<Aws::S3::S3Errors>> TransferManager::ValidateDownloadChecksum (const std::shared_ptr<TransferHandle>& handle) {
1665+ if (handle->GetChecksum ().empty ()) {
1666+ if (!handle->GetTempFilePath ().empty () && !handle->GetTargetFilePath ().empty ()) {
1667+ if (!Aws::FileSystem::RelocateFileOrDirectory (handle->GetTempFilePath ().c_str (), handle->GetTargetFilePath ().c_str ())) {
1668+ AWS_LOGSTREAM_ERROR (CLASS_TAG, " Failed to rename temp file from " << handle->GetTempFilePath () << " to " << handle->GetTargetFilePath ());
1669+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1670+ return Aws::Client::AWSError<Aws::S3::S3Errors>(Aws::S3::S3Errors::INTERNAL_FAILURE, " FileRenameError" , " Failed to rename temporary file to target" , false );
1671+ }
1672+ }
1673+ return Aws::NoResult ();
1674+ }
1675+
1676+ Aws::String fileToValidate = handle->GetTempFilePath ().empty () ? handle->GetTargetFilePath () : handle->GetTempFilePath ();
1677+ if (fileToValidate.empty ()) {
1678+ return Aws::NoResult ();
16471679 }
16481680
16491681 std::shared_ptr<Aws::Utils::Crypto::Hash> hashCalculator;
@@ -1660,13 +1692,16 @@ namespace Aws
16601692 }
16611693
16621694 auto fileStream = Aws::MakeShared<Aws::FStream>(" TransferManager" ,
1663- handle-> GetTargetFilePath () .c_str (),
1695+ fileToValidate .c_str (),
16641696 std::ios_base::in | std::ios_base::binary);
16651697
16661698 if (!fileStream->good ()) {
16671699 AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
1668- << " ] Failed to open downloaded file for checksum validation: " << handle->GetTargetFilePath ());
1669- return false ;
1700+ << " ] Failed to open downloaded file for checksum validation: " << fileToValidate);
1701+ if (!handle->GetTempFilePath ().empty ()) {
1702+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1703+ }
1704+ return Aws::Client::AWSError<Aws::S3::S3Errors>(Aws::S3::S3Errors::INTERNAL_FAILURE, " FileOpenError" , " Failed to open downloaded file for checksum validation" , false );
16701705 }
16711706
16721707 unsigned char buffer[8192 ];
@@ -1678,18 +1713,33 @@ namespace Aws
16781713 if (!hashResult.IsSuccess ()) {
16791714 AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
16801715 << " ] Failed to calculate checksum for downloaded file" );
1681- return false ;
1716+ if (!handle->GetTempFilePath ().empty ()) {
1717+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1718+ }
1719+ return Aws::Client::AWSError<Aws::S3::S3Errors>(Aws::S3::S3Errors::INTERNAL_FAILURE, " ChecksumCalculationError" , " Failed to calculate checksum for downloaded file" , false );
16821720 }
16831721
16841722 Aws::String calculatedChecksum = Aws::Utils::HashingUtils::Base64Encode (hashResult.GetResult ());
16851723 if (calculatedChecksum != handle->GetChecksum ()) {
16861724 AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
16871725 << " ] Checksum validation failed. Expected: " << handle->GetChecksum ()
16881726 << " , Calculated: " << calculatedChecksum);
1689- return false ;
1727+ if (!handle->GetTempFilePath ().empty ()) {
1728+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1729+ }
1730+ return Aws::Client::AWSError<Aws::S3::S3Errors>(Aws::S3::S3Errors::INTERNAL_FAILURE, " ChecksumMismatch" , " Downloaded file checksum does not match expected checksum" , false );
16901731 }
16911732
1692- return true ;
1733+ // Validation succeeded, rename temp to target
1734+ if (!handle->GetTempFilePath ().empty () && !handle->GetTargetFilePath ().empty ()) {
1735+ if (!Aws::FileSystem::RelocateFileOrDirectory (handle->GetTempFilePath ().c_str (), handle->GetTargetFilePath ().c_str ())) {
1736+ AWS_LOGSTREAM_ERROR (CLASS_TAG, " Failed to rename temp file from " << handle->GetTempFilePath () << " to " << handle->GetTargetFilePath ());
1737+ Aws::FileSystem::RemoveFileIfExists (handle->GetTempFilePath ().c_str ());
1738+ return Aws::Client::AWSError<Aws::S3::S3Errors>(Aws::S3::S3Errors::INTERNAL_FAILURE, " FileRenameError" , " Failed to rename temporary file to target" , false );
1739+ }
1740+ }
1741+
1742+ return Aws::NoResult ();
16931743 }
16941744 }
16951745}
0 commit comments