From 4147d5ad8c1898c5502b5d6b88deecf1ec41445a Mon Sep 17 00:00:00 2001 From: overtrue Date: Tue, 6 Jan 2026 19:00:12 +0800 Subject: [PATCH] fix: handle copy_source_if_match in copy_object for S3 compatibility - Update s3s to PR #449 (ac13a56) which accepts unquoted ETag values - Add copy_source_if_match and copy_source_if_none_match handling in copy_object - Return PreconditionFailed when ETag conditions are not met Fixes #1400 --- Cargo.lock | 19 ++++++++++--------- Cargo.toml | 2 +- rustfs/src/storage/ecfs.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 875747ac..0af004f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3524,7 +3524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4620,7 +4620,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.61.2", ] [[package]] @@ -4912,7 +4912,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7078,7 +7078,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8485,7 +8485,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8498,7 +8498,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8586,7 +8586,7 @@ checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "s3s" version = "0.13.0-alpha" -source = "git+https://github.com/s3s-project/s3s.git?branch=main#18c168ae21bf1176555f8f529686ecdc2ebd6db7" +source = "git+https://github.com/s3s-project/s3s.git?rev=ac13a56#ac13a568c44f2da640d0292269a0ee02ec8da433" dependencies = [ "arrayvec", "async-trait", @@ -9408,6 +9408,7 @@ dependencies = [ "cfg-if", "libc", "psm", + "windows-sys 0.52.0", "windows-sys 0.59.0", ] @@ -9729,7 +9730,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -10770,7 +10771,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 083860d2..aeddcc0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -224,7 +224,7 @@ regex = { version = "1.12.2" } rumqttc = { version = "0.25.1" } rust-embed = { version = "8.9.0" } rustc-hash = { version = "2.1.1" } -s3s = { version = "0.13.0-alpha", features = ["minio"], git = "https://github.com/s3s-project/s3s.git", branch = "main" } +s3s = { version = "0.13.0-alpha", features = ["minio"], git = "https://github.com/s3s-project/s3s.git", rev = "ac13a56" } serial_test = "3.3.1" shadow-rs = { version = "1.5.0", default-features = false } siphasher = "1.0.1" diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index fe6c779d..d5412988 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -852,6 +852,8 @@ impl S3 for FS { sse_customer_key_md5, metadata_directive, metadata, + copy_source_if_match, + copy_source_if_none_match, .. } = req.input.clone(); let (src_bucket, src_key, version_id) = match copy_source { @@ -927,6 +929,30 @@ impl S3 for FS { let mut src_info = gr.object_info.clone(); + // Validate copy source conditions + if let Some(if_match) = copy_source_if_match { + if let Some(ref etag) = src_info.etag { + if let Some(strong_etag) = if_match.into_etag() { + if ETag::Strong(etag.clone()) != strong_etag { + return Err(s3_error!(PreconditionFailed)); + } + } else { + // Weak ETag or Any (*) in If-Match should fail per RFC 9110 + return Err(s3_error!(PreconditionFailed)); + } + } else { + return Err(s3_error!(PreconditionFailed)); + } + } + + if let Some(if_none_match) = copy_source_if_none_match + && let Some(ref etag) = src_info.etag + && let Some(strong_etag) = if_none_match.into_etag() + && ETag::Strong(etag.clone()) == strong_etag + { + return Err(s3_error!(PreconditionFailed)); + } + if cp_src_dst_same { src_info.metadata_only = true; }