fix(checksum): align multipart CRC64NVME with full object (#2286)

This commit is contained in:
weisd
2026-03-25 12:44:46 +08:00
committed by GitHub
parent 19b8389dc4
commit 2681731443
4 changed files with 206 additions and 7 deletions
+139 -1
View File
@@ -20,8 +20,9 @@ mod tests {
use crate::common::{RustFSTestEnvironment, init_logging};
use aws_sdk_s3::Client;
use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::types::{CompletedMultipartUpload, CompletedPart};
use aws_sdk_s3::types::{ChecksumAlgorithm, ChecksumMode, CompletedMultipartUpload, CompletedPart};
use base64::Engine;
use rustfs_rio::{Checksum, ChecksumType as RioChecksumType};
use serial_test::serial;
use sha2::{Digest, Sha256};
use tracing::info;
@@ -57,6 +58,12 @@ mod tests {
base64::engine::general_purpose::STANDARD.encode(digest.as_slice())
}
fn checksum_crc64nvme_base64(body: &[u8]) -> String {
Checksum::new_from_data(RioChecksumType::CRC64_NVME, body)
.expect("crc64nvme checksum")
.encoded
}
/// PutObject with Content-MD5: upload succeeds and GetObject returns same content.
#[tokio::test]
#[serial]
@@ -226,4 +233,135 @@ mod tests {
);
info!("PASSED: MultipartUpload with checksum and GetObject content match");
}
/// Regression test for issue #2282:
/// CRC64NVME full-object checksum should match between direct PutObject and multipart upload.
#[tokio::test]
#[serial]
async fn test_crc64nvme_matches_between_put_object_and_multipart_upload() {
init_logging();
info!("TEST: CRC64NVME matches between direct PutObject and multipart upload");
let mut env = RustFSTestEnvironment::new().await.expect("Failed to create test environment");
env.start_rustfs_server(vec![]).await.expect("Failed to start RustFS");
let client = create_s3_client(&env);
let bucket = "test-crc64nvme-multipart-match";
create_bucket(&client, bucket).await.expect("Failed to create bucket");
const PART_SIZE: usize = 6 * 1024 * 1024;
let part1: Vec<u8> = (0..PART_SIZE).map(|i| (i % 251) as u8).collect();
let part2: Vec<u8> = (0..PART_SIZE).map(|i| ((i + 17) % 251) as u8).collect();
let content: Vec<u8> = part1.iter().chain(part2.iter()).copied().collect();
let direct_key = "crc64nvme-direct.bin";
let multipart_key = "crc64nvme-multipart.bin";
let full_checksum = checksum_crc64nvme_base64(&content);
let part1_checksum = checksum_crc64nvme_base64(&part1);
let part2_checksum = checksum_crc64nvme_base64(&part2);
client
.put_object()
.bucket(bucket)
.key(direct_key)
.body(ByteStream::from(content.clone()))
.checksum_algorithm(ChecksumAlgorithm::Crc64Nvme)
.checksum_crc64_nvme(full_checksum.clone())
.send()
.await
.expect("Failed to put direct object with CRC64NVME");
let create_result = client
.create_multipart_upload()
.bucket(bucket)
.key(multipart_key)
.checksum_algorithm(ChecksumAlgorithm::Crc64Nvme)
.send()
.await
.expect("Failed to create multipart upload");
let upload_id = create_result.upload_id().expect("No upload_id").to_string();
let upload1 = client
.upload_part()
.bucket(bucket)
.key(multipart_key)
.upload_id(&upload_id)
.part_number(1)
.body(ByteStream::from(part1.clone()))
.checksum_algorithm(ChecksumAlgorithm::Crc64Nvme)
.checksum_crc64_nvme(part1_checksum)
.send()
.await
.expect("Failed to upload multipart part 1");
let upload2 = client
.upload_part()
.bucket(bucket)
.key(multipart_key)
.upload_id(&upload_id)
.part_number(2)
.body(ByteStream::from(part2.clone()))
.checksum_algorithm(ChecksumAlgorithm::Crc64Nvme)
.checksum_crc64_nvme(part2_checksum)
.send()
.await
.expect("Failed to upload multipart part 2");
let completed_upload = CompletedMultipartUpload::builder()
.parts(
CompletedPart::builder()
.part_number(1)
.e_tag(upload1.e_tag().expect("No etag for part 1"))
.checksum_crc64_nvme(upload1.checksum_crc64_nvme().expect("No CRC64NVME for part 1"))
.build(),
)
.parts(
CompletedPart::builder()
.part_number(2)
.e_tag(upload2.e_tag().expect("No etag for part 2"))
.checksum_crc64_nvme(upload2.checksum_crc64_nvme().expect("No CRC64NVME for part 2"))
.build(),
)
.build();
client
.complete_multipart_upload()
.bucket(bucket)
.key(multipart_key)
.upload_id(&upload_id)
.multipart_upload(completed_upload)
.send()
.await
.expect("Failed to complete multipart upload");
let direct_head = client
.head_object()
.bucket(bucket)
.key(direct_key)
.checksum_mode(ChecksumMode::Enabled)
.send()
.await
.expect("Failed to head direct object");
let multipart_head = client
.head_object()
.bucket(bucket)
.key(multipart_key)
.checksum_mode(ChecksumMode::Enabled)
.send()
.await
.expect("Failed to head multipart object");
assert_eq!(
direct_head.checksum_crc64_nvme(),
Some(full_checksum.as_str()),
"Direct object should report the uploaded full-object CRC64NVME"
);
assert_eq!(
multipart_head.checksum_crc64_nvme(),
Some(full_checksum.as_str()),
"Multipart object should report the same full-object CRC64NVME as direct upload"
);
}
}