|
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/crt/checksum/CRC.h> |
18 | 19 | #include <aws/s3/S3Client.h> |
19 | 20 | #include <aws/s3/model/AbortMultipartUploadRequest.h> |
20 | 21 | #include <aws/s3/model/CompleteMultipartUploadRequest.h> |
@@ -1280,45 +1281,13 @@ namespace Aws |
1280 | 1281 | Aws::IOStream* bufferStream = partState->GetDownloadPartStream(); |
1281 | 1282 | assert(bufferStream); |
1282 | 1283 |
|
1283 | | - // Calculate and validate checksum for this part if validation is enabled |
| 1284 | + // checksum for this part if validation is enabled |
1284 | 1285 | if (m_transferConfig.validateChecksums) |
1285 | 1286 | { |
1286 | 1287 | auto hash = handle->GetPartChecksum(partState->GetPartId()); |
1287 | 1288 | if (hash && partState->GetDownloadBuffer()) |
1288 | 1289 | { |
1289 | 1290 | hash->Update(partState->GetDownloadBuffer(), static_cast<size_t>(partState->GetSizeInBytes())); |
1290 | | - |
1291 | | - // Get expected checksum from response |
1292 | | - Aws::String expectedChecksum = GetChecksumFromResult(outcome.GetResult(), m_transferConfig.checksumAlgorithm); |
1293 | | - |
1294 | | - // Validate part checksum |
1295 | | - if (!expectedChecksum.empty()) |
1296 | | - { |
1297 | | - auto calculatedResult = hash->GetHash(); |
1298 | | - if (calculatedResult.IsSuccess()) |
1299 | | - { |
1300 | | - Aws::String calculatedChecksum = Utils::HashingUtils::Base64Encode(calculatedResult.GetResult()); |
1301 | | - if (calculatedChecksum != expectedChecksum) |
1302 | | - { |
1303 | | - AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId() |
1304 | | - << "] Checksum mismatch for part " << partState->GetPartId() |
1305 | | - << ". Expected: " << expectedChecksum << ", Calculated: " << calculatedChecksum); |
1306 | | - Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE, |
1307 | | - "ChecksumMismatch", |
1308 | | - "Part checksum validation failed", |
1309 | | - false); |
1310 | | - handle->ChangePartToFailed(partState); |
1311 | | - handle->SetError(error); |
1312 | | - TriggerErrorCallback(handle, error); |
1313 | | - if(partState->GetDownloadBuffer()) |
1314 | | - { |
1315 | | - m_bufferManager.Release(partState->GetDownloadBuffer()); |
1316 | | - partState->SetDownloadBuffer(nullptr); |
1317 | | - } |
1318 | | - return; |
1319 | | - } |
1320 | | - } |
1321 | | - } |
1322 | 1291 | } |
1323 | 1292 | } |
1324 | 1293 |
|
@@ -1359,7 +1328,67 @@ namespace Aws |
1359 | 1328 | { |
1360 | 1329 | if (failedParts.size() == 0 && handle->GetBytesTransferred() == handle->GetBytesTotalSize()) |
1361 | 1330 | { |
1362 | | - // TODO: Combine part checksums and validate full-object checksum when CRT provides combining utility |
| 1331 | + // Combine part checksums and validate full-object checksum |
| 1332 | + if (m_transferConfig.validateChecksums) |
| 1333 | + { |
| 1334 | + Aws::String expectedChecksum = GetChecksumFromResult(outcome.GetResult(), m_transferConfig.checksumAlgorithm); |
| 1335 | + if (!expectedChecksum.empty()) |
| 1336 | + { |
| 1337 | + auto combinedChecksum = 0ULL; |
| 1338 | + bool isCRC64 = (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC64NVME); |
| 1339 | + |
| 1340 | + for (auto& partChecksum : handle->GetPartChecksums()) |
| 1341 | + { |
| 1342 | + int partNumber = partChecksum.first; |
| 1343 | + auto hash = partChecksum.second; |
| 1344 | + |
| 1345 | + // Get part size from completed parts |
| 1346 | + auto partSize = handle->GetCompletedParts()[partNumber]->GetSizeInBytes(); |
| 1347 | + |
| 1348 | + auto partResult = hash->GetHash(); |
| 1349 | + auto partData = partResult.GetResult(); |
| 1350 | + |
| 1351 | + auto partCrc = isCRC64 ? |
| 1352 | + *reinterpret_cast<const unsigned long long*>(partData.GetUnderlyingData()) : |
| 1353 | + *reinterpret_cast<const unsigned int*>(partData.GetUnderlyingData()); |
| 1354 | + |
| 1355 | + if (combinedChecksum == 0) { |
| 1356 | + combinedChecksum = partCrc; |
| 1357 | + } else { |
| 1358 | + if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC32) { |
| 1359 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC32(combinedChecksum, partCrc, partSize); |
| 1360 | + } else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC32C) { |
| 1361 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC32C(combinedChecksum, partCrc, partSize); |
| 1362 | + } else if (isCRC64) { |
| 1363 | + combinedChecksum = Aws::Crt::Checksum::CombineCRC64NVME(combinedChecksum, partCrc, partSize); |
| 1364 | + } |
| 1365 | + } |
| 1366 | + } |
| 1367 | + |
| 1368 | + // Compare with expected checksum |
| 1369 | + Aws::Utils::ByteBuffer checksumBuffer(isCRC64 ? 8 : 4); |
| 1370 | + if (isCRC64) { |
| 1371 | + *reinterpret_cast<unsigned long long*>(checksumBuffer.GetUnderlyingData()) = combinedChecksum; |
| 1372 | + } else { |
| 1373 | + *reinterpret_cast<unsigned int*>(checksumBuffer.GetUnderlyingData()) = static_cast<unsigned int>(combinedChecksum); |
| 1374 | + } |
| 1375 | + Aws::String calculatedChecksum = Utils::HashingUtils::Base64Encode(checksumBuffer); |
| 1376 | + |
| 1377 | + if (calculatedChecksum != expectedChecksum) { |
| 1378 | + AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId() |
| 1379 | + << "] Full-object checksum mismatch. Expected: " << expectedChecksum |
| 1380 | + << ", Calculated: " << calculatedChecksum); |
| 1381 | + Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE, |
| 1382 | + "ChecksumMismatch", |
| 1383 | + "Full-object checksum validation failed", |
| 1384 | + false); |
| 1385 | + handle->SetError(error); |
| 1386 | + handle->UpdateStatus(TransferStatus::FAILED); |
| 1387 | + TriggerErrorCallback(handle, error); |
| 1388 | + return; |
| 1389 | + } |
| 1390 | + } |
| 1391 | + } |
1363 | 1392 | outcome.GetResult().GetBody().flush(); |
1364 | 1393 | handle->UpdateStatus(TransferStatus::COMPLETED); |
1365 | 1394 | } |
|
0 commit comments