Skip to content

Commit 09edad0

Browse files
HJLebbinkTest User
authored andcommitted
added CRC32, CRC32C, SHA1, SHA256 and CRC64NVME
1 parent 2e94a0e commit 09edad0

23 files changed

+1762
-72
lines changed

.github/workflows/rust.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ jobs:
3636
export ACCESS_KEY=minioadmin
3737
export SECRET_KEY=minioadmin
3838
export ENABLE_HTTPS=1
39+
export SERVER_REGION=us-east-1
3940
export MINIO_SSL_CERT_FILE=./tests/public.crt
4041
MINIO_TEST_TOKIO_RUNTIME_FLAVOR="multi_thread" cargo test -- --nocapture
4142
test-current-thread:
@@ -49,6 +50,7 @@ jobs:
4950
export ACCESS_KEY=minioadmin
5051
export SECRET_KEY=minioadmin
5152
export ENABLE_HTTPS=1
53+
export SERVER_REGION=us-east-1
5254
export MINIO_SSL_CERT_FILE=./tests/public.crt
5355
MINIO_TEST_TOKIO_RUNTIME_FLAVOR="current_thread" cargo test -- --nocapture
5456

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ async-trait = "0.1"
4040
base64 = "0.22"
4141
chrono = { version = "0.4", features = ["serde"] }
4242
crc = "3.3"
43+
crc32c = "0.6"
44+
crc32fast = "1.4"
4345
dashmap = "6.1.0"
4446
env_logger = "0.11"
4547
hmac = { version = "0.12", optional = true }
@@ -55,6 +57,7 @@ ring = { version = "0.17", optional = true, default-features = false, features =
5557
serde = { version = "1.0", features = ["derive"] }
5658
serde_json = "1.0"
5759
serde_yaml = "0.9"
60+
sha1 = "0.10"
5861
sha2 = { version = "0.10", optional = true }
5962
urlencoding = "2.1"
6063
xmltree = "0.12"
@@ -95,3 +98,8 @@ name = "load_balancing_with_hooks"
9598
name = "s3-api"
9699
path = "benches/s3/api_benchmarks.rs"
97100
harness = false
101+
102+
[[bench]]
103+
name = "bench_checksums"
104+
path = "benches/bench_checksums.rs"
105+
harness = false

benches/bench_checksums.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
2+
// Copyright 2025 MinIO, Inc.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
17+
use minio::s3::utils::{crc32_checksum, crc32c, crc64nvme_checksum, sha1_hash, sha256_checksum};
18+
19+
fn bench_checksums(c: &mut Criterion) {
20+
let sizes = vec![
21+
("1KB", 1024),
22+
("10KB", 10 * 1024),
23+
("100KB", 100 * 1024),
24+
("1MB", 1024 * 1024),
25+
("10MB", 10 * 1024 * 1024),
26+
];
27+
28+
for (name, size) in sizes {
29+
let data = vec![0u8; size];
30+
31+
let mut group = c.benchmark_group(format!("checksum_{}", name));
32+
group.throughput(Throughput::Bytes(size as u64));
33+
34+
group.bench_function("CRC32", |b| b.iter(|| crc32_checksum(&data)));
35+
36+
group.bench_function("CRC32C", |b| b.iter(|| crc32c(&data)));
37+
38+
group.bench_function("CRC64NVME", |b| b.iter(|| crc64nvme_checksum(&data)));
39+
40+
group.bench_function("SHA1", |b| b.iter(|| sha1_hash(&data)));
41+
42+
group.bench_function("SHA256", |b| b.iter(|| sha256_checksum(&data)));
43+
44+
group.finish();
45+
}
46+
}
47+
48+
criterion_group!(benches, bench_checksums);
49+
criterion_main!(benches);

common/src/example.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn create_bucket_notification_config_example() -> NotificationConfig {
5252
suffix_filter_rule: Some(SuffixFilterRule {
5353
value: String::from("pg"),
5454
}),
55-
queue: String::from("arn:minio:sqs::miniojavatest:webhook"),
55+
queue: String::from("arn:minio:sqs:us-east-1:miniojavatest:webhook"),
5656
}]),
5757
..Default::default()
5858
}

src/s3/builders/append_object.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use crate::s3::response_traits::HasObjectSize;
2626
use crate::s3::segmented_bytes::SegmentedBytes;
2727
use crate::s3::sse::Sse;
2828
use crate::s3::types::{S3Api, S3Request, ToS3Request};
29-
use crate::s3::utils::{check_bucket_name, check_object_name, check_sse};
29+
use crate::s3::utils::{
30+
ChecksumAlgorithm, check_bucket_name, check_object_name, check_sse, compute_checksum_sb,
31+
};
3032
use http::Method;
3133
use std::sync::Arc;
3234
use typed_builder::TypedBuilder;
@@ -64,6 +66,14 @@ pub struct AppendObject {
6466
#[builder(!default)] // force required
6567
/// value of x-amz-write-offset-bytes
6668
offset_bytes: u64,
69+
70+
/// Optional checksum algorithm for data integrity verification during append.
71+
///
72+
/// When specified, computes a checksum of the appended data using the selected algorithm
73+
/// (CRC32, CRC32C, SHA1, SHA256, or CRC64NVME). The checksum is sent with the append
74+
/// operation and verified by the server.
75+
#[builder(default, setter(into))]
76+
checksum_algorithm: Option<ChecksumAlgorithm>,
6777
}
6878

6979
impl S3Api for AppendObject {
@@ -83,6 +93,7 @@ pub type AppendObjectBldr = AppendObjectBuilder<(
8393
(),
8494
(Arc<SegmentedBytes>,),
8595
(u64,),
96+
(),
8697
)>;
8798

8899
impl ToS3Request for AppendObject {
@@ -94,6 +105,21 @@ impl ToS3Request for AppendObject {
94105
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
95106
headers.add(X_AMZ_WRITE_OFFSET_BYTES, self.offset_bytes.to_string());
96107

108+
if let Some(algorithm) = self.checksum_algorithm {
109+
let checksum_value = compute_checksum_sb(algorithm, &self.data);
110+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
111+
112+
match algorithm {
113+
ChecksumAlgorithm::CRC32 => headers.add(X_AMZ_CHECKSUM_CRC32, checksum_value),
114+
ChecksumAlgorithm::CRC32C => headers.add(X_AMZ_CHECKSUM_CRC32C, checksum_value),
115+
ChecksumAlgorithm::SHA1 => headers.add(X_AMZ_CHECKSUM_SHA1, checksum_value),
116+
ChecksumAlgorithm::SHA256 => headers.add(X_AMZ_CHECKSUM_SHA256, checksum_value),
117+
ChecksumAlgorithm::CRC64NVME => {
118+
headers.add(X_AMZ_CHECKSUM_CRC64NVME, checksum_value)
119+
}
120+
}
121+
}
122+
97123
Ok(S3Request::builder()
98124
.client(self.client)
99125
.method(Method::PUT)
@@ -144,6 +170,13 @@ pub struct AppendObjectContent {
144170
/// Value of x-amz-write-offset-bytes
145171
#[builder(default)]
146172
offset_bytes: u64,
173+
/// Optional checksum algorithm for data integrity verification during append.
174+
///
175+
/// When specified, computes checksums for appended data using the selected algorithm
176+
/// (CRC32, CRC32C, SHA1, SHA256, or CRC64NVME). The checksum is computed for each
177+
/// chunk and sent with the append operation.
178+
#[builder(default, setter(into))]
179+
checksum_algorithm: Option<ChecksumAlgorithm>,
147180
}
148181

149182
/// Builder type for [`AppendObjectContent`] that is returned by [`MinioClient::append_object_content`](crate::s3::client::MinioClient::append_object_content).
@@ -162,6 +195,7 @@ pub type AppendObjectContentBldr = AppendObjectContentBuilder<(
162195
(),
163196
(),
164197
(),
198+
(),
165199
)>;
166200

167201
impl AppendObjectContent {
@@ -229,6 +263,7 @@ impl AppendObjectContent {
229263
offset_bytes: current_file_size,
230264
sse: self.sse,
231265
data: Arc::new(seg_bytes),
266+
checksum_algorithm: self.checksum_algorithm,
232267
};
233268
ao.send().await
234269
} else if let Some(expected) = object_size.value()
@@ -296,6 +331,7 @@ impl AppendObjectContent {
296331
sse: self.sse.clone(),
297332
data: Arc::new(part_content),
298333
offset_bytes: next_offset_bytes,
334+
checksum_algorithm: self.checksum_algorithm,
299335
};
300336
let resp: AppendObjectResponse = append_object.send().await?;
301337
//println!("AppendObjectResponse: object_size={:?}", resp.object_size);

src/s3/builders/copy_object.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ use crate::s3::response::{
2323
CopyObjectInternalResponse, CopyObjectResponse, CreateMultipartUploadResponse,
2424
StatObjectResponse, UploadPartCopyResponse,
2525
};
26+
use crate::s3::response_traits::HasChecksumHeaders;
2627
use crate::s3::response_traits::HasEtagFromBody;
2728
use crate::s3::sse::{Sse, SseCustomerKey};
2829
use crate::s3::types::{Directive, PartInfo, Retention, S3Api, S3Request, ToS3Request};
2930
use crate::s3::utils::{
30-
UtcTime, check_bucket_name, check_object_name, check_sse, check_ssec, to_http_header_value,
31-
to_iso8601utc, url_encode,
31+
ChecksumAlgorithm, UtcTime, check_bucket_name, check_object_name, check_sse, check_ssec,
32+
to_http_header_value, to_iso8601utc, url_encode,
3233
};
3334
use async_recursion::async_recursion;
3435
use http::Method;
@@ -59,6 +60,13 @@ pub struct UploadPartCopy {
5960
part_number: u16,
6061
#[builder(default)]
6162
headers: Multimap,
63+
/// Optional checksum algorithm for data integrity verification during part copy.
64+
///
65+
/// When specified, the server computes a checksum of the copied part data using
66+
/// this algorithm. Use the same algorithm for all parts in a multipart upload.
67+
/// Supported algorithms: CRC32, CRC32C, SHA1, SHA256, CRC64NVME.
68+
#[builder(default, setter(into))]
69+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
6270
}
6371

6472
impl S3Api for UploadPartCopy {
@@ -78,6 +86,7 @@ pub type UploadPartCopyBldr = UploadPartCopyBuilder<(
7886
(String,),
7987
(),
8088
(),
89+
(),
8190
)>;
8291

8392
impl ToS3Request for UploadPartCopy {
@@ -100,6 +109,10 @@ impl ToS3Request for UploadPartCopy {
100109
let mut headers: Multimap = self.extra_headers.unwrap_or_default();
101110
headers.add_multimap(self.headers);
102111

112+
if let Some(algorithm) = self.checksum_algorithm {
113+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
114+
}
115+
103116
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
104117
{
105118
query_params.add("partNumber", self.part_number.to_string());
@@ -150,6 +163,8 @@ pub struct CopyObjectInternal {
150163
metadata_directive: Option<Directive>,
151164
#[builder(default, setter(into))]
152165
tagging_directive: Option<Directive>,
166+
#[builder(default, setter(into))]
167+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
153168
}
154169

155170
impl S3Api for CopyObjectInternal {
@@ -175,6 +190,7 @@ pub type CopyObjectInternalBldr = CopyObjectInternalBuilder<(
175190
(),
176191
(),
177192
(),
193+
(),
178194
)>;
179195

180196
impl ToS3Request for CopyObjectInternal {
@@ -261,6 +277,10 @@ impl ToS3Request for CopyObjectInternal {
261277
if let Some(v) = self.source.ssec {
262278
headers.add_multimap(v.copy_headers());
263279
}
280+
281+
if let Some(algorithm) = self.checksum_algorithm {
282+
headers.add(X_AMZ_CHECKSUM_ALGORITHM, algorithm.as_str().to_string());
283+
}
264284
};
265285

266286
Ok(S3Request::builder()
@@ -310,6 +330,13 @@ pub struct CopyObject {
310330
metadata_directive: Option<Directive>,
311331
#[builder(default, setter(into))]
312332
tagging_directive: Option<Directive>,
333+
/// Optional checksum algorithm for data integrity verification during copy.
334+
///
335+
/// When specified, the server computes a checksum of the destination object using
336+
/// this algorithm during the copy operation. Supported algorithms: CRC32, CRC32C,
337+
/// SHA1, SHA256, CRC64NVME. The checksum value is included in response headers for verification.
338+
#[builder(default, setter(into))]
339+
checksum_algorithm: Option<crate::s3::utils::ChecksumAlgorithm>,
313340
}
314341

315342
/// Builder type for [`CopyObject`] that is returned by [`MinioClient::copy_object`](crate::s3::client::MinioClient::copy_object).
@@ -331,6 +358,7 @@ pub type CopyObjectBldr = CopyObjectBuilder<(
331358
(),
332359
(),
333360
(),
361+
(),
334362
)>;
335363

336364
impl CopyObject {
@@ -434,6 +462,7 @@ impl CopyObject {
434462
.source(self.source)
435463
.metadata_directive(self.metadata_directive)
436464
.tagging_directive(self.tagging_directive)
465+
.checksum_algorithm(self.checksum_algorithm)
437466
.build()
438467
.send()
439468
.await?;
@@ -472,6 +501,8 @@ pub struct ComposeObjectInternal {
472501
legal_hold: bool,
473502
#[builder(default)]
474503
sources: Vec<ComposeSource>,
504+
#[builder(default, setter(into))]
505+
checksum_algorithm: Option<ChecksumAlgorithm>,
475506
}
476507

477508
/// Builder type for [`ComposeObjectInternal`] that is returned by `compose_object_internal` method.
@@ -491,6 +522,7 @@ pub type ComposeObjectInternalBldr = ComposeObjectInternalBuilder<(
491522
(),
492523
(),
493524
(),
525+
(),
494526
)>;
495527

496528
impl ComposeObjectInternal {
@@ -554,6 +586,7 @@ impl ComposeObjectInternal {
554586
.extra_query_params(self.extra_query_params.clone())
555587
.region(self.region.clone())
556588
.extra_headers(Some(headers))
589+
.checksum_algorithm(self.checksum_algorithm)
557590
.build()
558591
.send()
559592
.await
@@ -625,11 +658,10 @@ impl ComposeObjectInternal {
625658
Err(e) => return (Err(e.into()), upload_id),
626659
};
627660

628-
parts.push(PartInfo {
629-
number: part_number,
630-
etag,
631-
size,
632-
});
661+
let checksum = self
662+
.checksum_algorithm
663+
.and_then(|alg| resp.get_checksum(alg).map(|v| (alg, v)));
664+
parts.push(PartInfo::new(part_number, etag, size, checksum));
633665
} else {
634666
while size > 0 {
635667
part_number += 1;
@@ -665,11 +697,10 @@ impl ComposeObjectInternal {
665697
Err(e) => return (Err(e.into()), upload_id),
666698
};
667699

668-
parts.push(PartInfo {
669-
number: part_number,
670-
etag,
671-
size,
672-
});
700+
let checksum = self
701+
.checksum_algorithm
702+
.and_then(|alg| resp.get_checksum(alg).map(|v| (alg, v)));
703+
parts.push(PartInfo::new(part_number, etag, size, checksum));
673704

674705
offset += length;
675706
size -= length;
@@ -732,6 +763,8 @@ pub struct ComposeObject {
732763
legal_hold: bool,
733764
#[builder(default)]
734765
sources: Vec<ComposeSource>,
766+
#[builder(default, setter(into))]
767+
checksum_algorithm: Option<ChecksumAlgorithm>,
735768
}
736769

737770
/// Builder type for [`ComposeObject`] that is returned by [`MinioClient::compose_object`](crate::s3::client::MinioClient::compose_object).
@@ -751,6 +784,7 @@ pub type ComposeObjectBldr = ComposeObjectBuilder<(
751784
(),
752785
(),
753786
(Vec<ComposeSource>,),
787+
(),
754788
)>;
755789

756790
impl ComposeObject {
@@ -773,6 +807,7 @@ impl ComposeObject {
773807
.retention(self.retention)
774808
.legal_hold(self.legal_hold)
775809
.sources(self.sources)
810+
.checksum_algorithm(self.checksum_algorithm)
776811
.build()
777812
.send()
778813
.await;

0 commit comments

Comments
 (0)