|
15 | 15 | #include <aws/core/utils/memory/stl/AWSStreamFwd.h> |
16 | 16 | #include <aws/core/utils/memory/stl/AWSStringStream.h> |
17 | 17 | #include <aws/core/utils/stream/PreallocatedStreamBuf.h> |
| 18 | +#include <aws/common/byte_order.h> |
| 19 | +#include <cstring> |
| 20 | +#include <aws/crt/checksum/CRC.h> |
18 | 21 | #include <aws/s3/S3Client.h> |
19 | 22 | #include <aws/s3/model/AbortMultipartUploadRequest.h> |
20 | 23 | #include <aws/s3/model/CompleteMultipartUploadRequest.h> |
@@ -406,12 +409,9 @@ namespace Aws |
406 | 409 |
|
407 | 410 | const auto fullObjectHashCalculator = [](const std::shared_ptr<TransferHandle>& handle, bool isRetry, S3::Model::ChecksumAlgorithm algorithm) -> std::shared_ptr<Aws::Utils::Crypto::Hash> { |
408 | 411 | if (handle->GetChecksum().empty() && !isRetry) { |
409 | | - if (algorithm == S3::Model::ChecksumAlgorithm::CRC32) { |
| 412 | + if (algorithm == S3::Model::ChecksumAlgorithm::CRC32 || algorithm == S3::Model::ChecksumAlgorithm::CRC32C) { |
410 | 413 | return Aws::MakeShared<Aws::Utils::Crypto::CRC32>("TransferManager"); |
411 | 414 | } |
412 | | - if (algorithm == S3::Model::ChecksumAlgorithm::CRC32C) { |
413 | | - return Aws::MakeShared<Aws::Utils::Crypto::CRC32C>("TransferManager"); |
414 | | - } |
415 | 415 | if (algorithm == S3::Model::ChecksumAlgorithm::SHA1) { |
416 | 416 | return Aws::MakeShared<Aws::Utils::Crypto::Sha1>("TransferManager"); |
417 | 417 | } |
@@ -673,6 +673,10 @@ namespace Aws |
673 | 673 | { |
674 | 674 | return outcome.GetResult().GetChecksumCRC32C(); |
675 | 675 | } |
| 676 | + else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC64NVME) |
| 677 | + { |
| 678 | + return outcome.GetResult().GetChecksumCRC64NVME(); |
| 679 | + } |
676 | 680 | else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::SHA1) |
677 | 681 | { |
678 | 682 | return outcome.GetResult().GetChecksumSHA1(); |
@@ -965,6 +969,7 @@ namespace Aws |
965 | 969 | headObjectRequest.SetCustomizedAccessLogTag(m_transferConfig.customizedAccessLogTag); |
966 | 970 | headObjectRequest.WithBucket(handle->GetBucketName()) |
967 | 971 | .WithKey(handle->GetKey()); |
| 972 | + headObjectRequest.SetChecksumMode(Aws::S3::Model::ChecksumMode::ENABLED); |
968 | 973 |
|
969 | 974 | if(!handle->GetVersionId().empty()) |
970 | 975 | { |
@@ -1000,6 +1005,18 @@ namespace Aws |
1000 | 1005 | handle->SetContentType(headObjectOutcome.GetResult().GetContentType()); |
1001 | 1006 | handle->SetMetadata(headObjectOutcome.GetResult().GetMetadata()); |
1002 | 1007 | handle->SetEtag(headObjectOutcome.GetResult().GetETag()); |
| 1008 | + if (headObjectOutcome.GetResult().GetChecksumType() == Aws::S3::Model::ChecksumType::FULL_OBJECT) { |
| 1009 | + if (!headObjectOutcome.GetResult().GetChecksumCRC32C().empty()) { |
| 1010 | + handle->SetChecksum(headObjectOutcome.GetResult().GetChecksumCRC32C()); |
| 1011 | + handle->SetChecksumAlgorithm(S3::Model::ChecksumAlgorithm::CRC32C); |
| 1012 | + } else if (!headObjectOutcome.GetResult().GetChecksumCRC32().empty()) { |
| 1013 | + handle->SetChecksum(headObjectOutcome.GetResult().GetChecksumCRC32()); |
| 1014 | + handle->SetChecksumAlgorithm(S3::Model::ChecksumAlgorithm::CRC32); |
| 1015 | + } else if (!headObjectOutcome.GetResult().GetChecksumCRC64NVME().empty()) { |
| 1016 | + handle->SetChecksum(headObjectOutcome.GetResult().GetChecksumCRC64NVME()); |
| 1017 | + handle->SetChecksumAlgorithm(S3::Model::ChecksumAlgorithm::CRC64NVME); |
| 1018 | + } |
| 1019 | + } |
1003 | 1020 | /* When bucket versioning is suspended, head object will return "null" for unversioned object. |
1004 | 1021 | * Send following GetObject with "null" as versionId will result in 403 access denied error if your IAM role or policy |
1005 | 1022 | * doesn't have GetObjectVersion permission. |
@@ -1204,6 +1221,7 @@ namespace Aws |
1204 | 1221 |
|
1205 | 1222 | Aws::String errMsg{handle->WritePartToDownloadStream(bufferStream, partState->GetRangeBegin())}; |
1206 | 1223 | if (errMsg.empty()) { |
| 1224 | + if (m_transferConfig.validateChecksums) { handle->AddChecksumForPart(bufferStream, partState); } |
1207 | 1225 | handle->ChangePartToCompleted(partState, outcome.GetResult().GetETag()); |
1208 | 1226 | } else { |
1209 | 1227 | Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE, |
@@ -1239,6 +1257,72 @@ namespace Aws |
1239 | 1257 | { |
1240 | 1258 | if (failedParts.size() == 0 && handle->GetBytesTransferred() == handle->GetBytesTotalSize()) |
1241 | 1259 | { |
| 1260 | + if (m_transferConfig.validateChecksums && !handle->GetChecksum().empty() && |
| 1261 | + (handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC32 || |
| 1262 | + handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC32C || |
| 1263 | + handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC64NVME)) { |
| 1264 | + uint64_t combinedChecksum = 0; |
| 1265 | + bool first = true; |
| 1266 | + for (const auto& part: handle->GetCompletedParts()) { |
| 1267 | + Aws::String checksumStr = part.second->GetChecksum(); |
| 1268 | + uint64_t partSize = part.second->GetSizeInBytes(); |
| 1269 | + if (checksumStr.empty()) { continue; } |
| 1270 | + auto decoded = Aws::Utils::HashingUtils::Base64Decode(checksumStr); |
| 1271 | + const auto* raw = decoded.GetUnderlyingData(); |
| 1272 | + if (first) { |
| 1273 | + if (handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC64NVME) { |
| 1274 | + uint64_t partCrcBE = 0; |
| 1275 | + std::memcpy(&partCrcBE, raw, sizeof(uint64_t)); |
| 1276 | + const uint64_t partCrc = aws_ntoh64(partCrcBE); |
| 1277 | + combinedChecksum = partCrc; |
| 1278 | + } else { |
| 1279 | + uint32_t partCrcBE = 0; |
| 1280 | + std::memcpy(&partCrcBE, raw, sizeof(uint32_t)); |
| 1281 | + const uint32_t partCrc = aws_ntoh32(partCrcBE); |
| 1282 | + combinedChecksum = partCrc; |
| 1283 | + } |
| 1284 | + first = false; |
| 1285 | + } else { |
| 1286 | + if (handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC64NVME) { |
| 1287 | + uint64_t partCrcBE = 0; |
| 1288 | + std::memcpy(&partCrcBE, raw, sizeof(uint64_t)); |
| 1289 | + const uint64_t partCrc = aws_ntoh64(partCrcBE); |
| 1290 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC64NVME(combinedChecksum, partCrc, partSize); |
| 1291 | + } |
| 1292 | + else { |
| 1293 | + uint32_t partCrcBE = 0; |
| 1294 | + std::memcpy(&partCrcBE, raw, sizeof(uint32_t)); |
| 1295 | + const uint32_t partCrc = aws_ntoh32(partCrcBE); |
| 1296 | + if (handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC32) { |
| 1297 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC32(static_cast<uint32_t>(combinedChecksum), partCrc, partSize); |
| 1298 | + } else { |
| 1299 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC32C(static_cast<uint32_t>(combinedChecksum), partCrc, partSize); |
| 1300 | + } |
| 1301 | + } |
| 1302 | + } |
| 1303 | + } |
| 1304 | + Aws::Utils::ByteBuffer checksumBuffer(handle->GetChecksumAlgorithm()== S3::Model::ChecksumAlgorithm::CRC64NVME ? 8 : 4); |
| 1305 | + if (handle->GetChecksumAlgorithm() == S3::Model::ChecksumAlgorithm::CRC64NVME) { |
| 1306 | + const uint64_t be = aws_hton64(combinedChecksum); |
| 1307 | + std::memcpy(checksumBuffer.GetUnderlyingData(), &be, sizeof(uint64_t)); |
| 1308 | + } else { |
| 1309 | + const uint32_t be = aws_hton32(static_cast<uint32_t>(combinedChecksum)); |
| 1310 | + std::memcpy(checksumBuffer.GetUnderlyingData(), &be, sizeof(uint32_t)); |
| 1311 | + } |
| 1312 | + Aws::String combinedChecksumStr = Aws::Utils::HashingUtils::Base64Encode(checksumBuffer); |
| 1313 | + if (combinedChecksumStr != handle->GetChecksum()) { |
| 1314 | + AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId() |
| 1315 | + << "] Full-object checksum mismatch. Expected: " << handle->GetChecksum() |
| 1316 | + << ", Calculated: " << combinedChecksumStr); |
| 1317 | + Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE, |
| 1318 | + "ChecksumMismatch", |
| 1319 | + "Full-object checksum validation failed", |
| 1320 | + false); |
| 1321 | + handle->SetError(error); |
| 1322 | + handle->UpdateStatus(TransferStatus::FAILED); |
| 1323 | + TriggerErrorCallback(handle, error); |
| 1324 | + } |
| 1325 | + } |
1242 | 1326 | outcome.GetResult().GetBody().flush(); |
1243 | 1327 | handle->UpdateStatus(TransferStatus::COMPLETED); |
1244 | 1328 | } |
|
0 commit comments