@@ -878,6 +878,7 @@ namespace Aws
878878 getObjectOutcome.GetResult ().GetBody ().flush ();
879879
880880 // Validate full object checksum if available
881+ bool checksumValid = true ;
881882 if (!handle->GetChecksum ().empty ()) {
882883 const auto & getResult = getObjectOutcome.GetResult ();
883884 Aws::String actualChecksum;
@@ -896,6 +897,7 @@ namespace Aws
896897 }
897898
898899 if (!actualChecksum.empty () && actualChecksum != handle->GetChecksum ()) {
900+ checksumValid = false ;
899901 Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
900902 Aws::S3::S3Errors::INTERNAL_FAILURE,
901903 " ChecksumMismatch" ,
@@ -913,6 +915,32 @@ namespace Aws
913915 }
914916 }
915917
918+ // Rename temp file to final file if using temporary file pattern
919+ if (!handle->GetTargetFilePath ().empty () && checksumValid) {
920+ Aws::String tempFile = GenerateTempFileName (handle->GetTargetFilePath ());
921+ if (handle->GetTargetFilePath ().find (" .s3tmp." ) != Aws::String::npos) {
922+ // This is a temp file, rename to final
923+ Aws::String finalFile = handle->GetTargetFilePath ();
924+ size_t pos = finalFile.find (" .s3tmp." );
925+ if (pos != Aws::String::npos) {
926+ finalFile = finalFile.substr (0 , pos);
927+ if (!RenameTempFileToFinal (handle->GetTargetFilePath (), finalFile)) {
928+ Aws::Client::AWSError<Aws::S3::S3Errors> renameError (
929+ Aws::S3::S3Errors::INTERNAL_FAILURE,
930+ " FileRenameFailure" ,
931+ " Failed to rename temporary file to final destination" ,
932+ false );
933+ handle->ChangePartToFailed (partState);
934+ handle->UpdateStatus (TransferStatus::FAILED);
935+ handle->SetError (renameError);
936+ TriggerErrorCallback (handle, renameError);
937+ TriggerTransferStatusUpdatedCallback (handle);
938+ return ;
939+ }
940+ }
941+ }
942+ }
943+
916944 handle->UpdateStatus (TransferStatus::COMPLETED);
917945 }
918946 else
@@ -1224,11 +1252,13 @@ namespace Aws
12241252 outcome.GetResult ().GetBody ().flush ();
12251253
12261254 // Validate full object checksum if available
1255+ bool checksumValid = true ;
12271256 if (!handle->GetChecksum ().empty ()) {
12281257 // Combine part checksums to calculate full object checksum
12291258 Aws::String calculatedChecksum = CombinePartChecksums (handle);
12301259
12311260 if (!calculatedChecksum.empty () && calculatedChecksum != handle->GetChecksum ()) {
1261+ checksumValid = false ;
12321262 Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
12331263 Aws::S3::S3Errors::INTERNAL_FAILURE,
12341264 " ChecksumMismatch" ,
@@ -1245,6 +1275,30 @@ namespace Aws
12451275 }
12461276 }
12471277
1278+ // Rename temp file to final file if using temporary file pattern
1279+ if (!handle->GetTargetFilePath ().empty () && checksumValid) {
1280+ if (handle->GetTargetFilePath ().find (" .s3tmp." ) != Aws::String::npos) {
1281+ // This is a temp file, rename to final
1282+ Aws::String finalFile = handle->GetTargetFilePath ();
1283+ size_t pos = finalFile.find (" .s3tmp." );
1284+ if (pos != Aws::String::npos) {
1285+ finalFile = finalFile.substr (0 , pos);
1286+ if (!RenameTempFileToFinal (handle->GetTargetFilePath (), finalFile)) {
1287+ Aws::Client::AWSError<Aws::S3::S3Errors> renameError (
1288+ Aws::S3::S3Errors::INTERNAL_FAILURE,
1289+ " FileRenameFailure" ,
1290+ " Failed to rename temporary file to final destination" ,
1291+ false );
1292+ handle->UpdateStatus (TransferStatus::FAILED);
1293+ handle->SetError (renameError);
1294+ TriggerErrorCallback (handle, renameError);
1295+ TriggerTransferStatusUpdatedCallback (handle);
1296+ return ;
1297+ }
1298+ }
1299+ }
1300+ }
1301+
12481302 handle->UpdateStatus (TransferStatus::COMPLETED);
12491303 }
12501304 else
@@ -1640,5 +1694,30 @@ namespace Aws
16401694 ss << std::hex << combinedCrc;
16411695 return ss.str ();
16421696 }
1697+
1698+ Aws::String TransferManager::GenerateTempFileName (const Aws::String& finalFileName) {
1699+ // Generate unique identifier (max 8 chars as per SEP)
1700+ Aws::String uuid = Aws::Utils::UUID::RandomUUID ();
1701+ Aws::String uniqueId = uuid.length () > 8 ? uuid.substr (0 , 8 ) : uuid;
1702+ return finalFileName + " .s3tmp." + uniqueId;
1703+ }
1704+
1705+ bool TransferManager::RenameTempFileToFinal (const Aws::String& tempFile, const Aws::String& finalFile) {
1706+ // Use atomic rename if supported by platform
1707+ if (std::rename (tempFile.c_str (), finalFile.c_str ()) == 0 ) {
1708+ return true ;
1709+ }
1710+
1711+ // Fallback strategy for platforms without atomic rename
1712+ if (Aws::FileSystem::RemoveFileIfExists (finalFile.c_str ())) {
1713+ if (std::rename (tempFile.c_str (), finalFile.c_str ()) == 0 ) {
1714+ return true ;
1715+ }
1716+ }
1717+
1718+ // Clean up temp file on failure
1719+ Aws::FileSystem::RemoveFileIfExists (tempFile.c_str ());
1720+ return false ;
1721+ }
16431722 }
16441723}
0 commit comments