From cb53ee13cddc749300734c72b7657b94a298ec0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Wed, 14 Jan 2026 21:09:13 +0800 Subject: [PATCH] fix: handle copy_source_if_match in copy_object for S3 compatibility (#1408) Co-authored-by: loverustfs Co-authored-by: houseme --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- rustfs/src/storage/ecfs.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 238c6b6f..25d4df93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8544,8 +8544,9 @@ checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "s3s" -version = "0.13.0-alpha" -source = "git+https://github.com/s3s-project/s3s.git?branch=main#18c168ae21bf1176555f8f529686ecdc2ebd6db7" +version = "0.13.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ea18014c794952beba5e5faf4663be467b591d45b4834916aad4bbcd2b5c27" dependencies = [ "arrayvec", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 836c3996..b354e705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -225,7 +225,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.1", features = ["minio"] } 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 59df88de..8f21ff57 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -873,6 +873,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 { @@ -948,6 +950,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; }