From 12d8d4aac43dea992066cf94307a6adeb939f998 Mon Sep 17 00:00:00 2001 From: Aysha Afrah Ziya Date: Sun, 9 Aug 2026 22:21:41 +0530 Subject: [PATCH] honor if_not_exists on cos multipart uploads --- core/services/cos/src/core.rs | 24 +++++++++++++++++++++--- core/services/cos/src/writer.rs | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core/services/cos/src/core.rs b/core/services/cos/src/core.rs index 20dcb6af3ca7..ec07da45bf3a 100644 --- a/core/services/cos/src/core.rs +++ b/core/services/cos/src/core.rs @@ -448,6 +448,16 @@ impl CosCore { req = req.header(CACHE_CONTROL, cache_control) } + // COS evaluates x-cos-forbid-overwrite on both InitiateMultipartUpload and + // CompleteMultipartUpload, so a multipart if_not_exists write has to carry it + // on both requests. Setting it only on the simple PutObject path lets large + // uploads silently overwrite an existing object. + // + // ref: https://www.tencentcloud.com/document/product/436/7746 + if args.if_not_exists() { + req = req.header("x-cos-forbid-overwrite", "true"); + } + // Set user metadata headers. if let Some(user_metadata) = args.user_metadata() { for (key, value) in user_metadata { @@ -504,6 +514,7 @@ impl CosCore { path: &str, upload_id: &str, parts: Vec, + args: &OpWrite, ) -> Result> { let p = build_abs_path(&self.root, path); @@ -514,14 +525,21 @@ impl CosCore { percent_encode_path(upload_id) ); - let req = Request::post(&url); + let mut req = Request::post(&url); let content = quick_xml::se::to_string(&CompleteMultipartUploadRequest { part: parts }) .map_err(new_xml_serialize_error)?; // Make sure content length has been set to avoid post with chunked encoding. - let req = req.header(CONTENT_LENGTH, content.len()); + req = req.header(CONTENT_LENGTH, content.len()); // Set content-type to `application/xml` to avoid mixed with form post. - let req = req.header(CONTENT_TYPE, "application/xml"); + req = req.header(CONTENT_TYPE, "application/xml"); + // CompleteMultipartUpload is the request that commits the object, so the + // if_not_exists guard has to be repeated here alongside InitiateMultipartUpload. + // + // ref: https://www.tencentcloud.com/document/product/436/7742 + if args.if_not_exists() { + req = req.header("x-cos-forbid-overwrite", "true"); + } let req = req .extension(Operation::Write) diff --git a/core/services/cos/src/writer.rs b/core/services/cos/src/writer.rs index 8c5dd2cfdf3f..4cc122ac24f3 100644 --- a/core/services/cos/src/writer.rs +++ b/core/services/cos/src/writer.rs @@ -166,7 +166,7 @@ impl oio::MultipartWrite for CosWriter { let mut resp = self .core - .cos_complete_multipart_upload(&self.ctx, &self.path, upload_id, parts) + .cos_complete_multipart_upload(&self.ctx, &self.path, upload_id, parts, &self.op) .await?; let mut meta = Self::parse_metadata(resp.headers())?;