From 7842b48e60cd54be0e5f248563821a766590b0f0 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 12 Aug 2026 05:45:02 +0000 Subject: Prevent archive overwrite races --- src/archive.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/archive.rs') diff --git a/src/archive.rs b/src/archive.rs index ad424c4..c0a2195 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -27,6 +27,9 @@ impl NativeTarWriter { let output = output.as_ref(); reject_output_inside_rootfs(rootfs, output)?; + if output.exists() { + bail!("archive output already exists; refusing to overwrite it: {}", output.display()); + } let temporary = temporary_output_path(output)?; let result = (|| { let file = OpenOptions::new() @@ -39,13 +42,18 @@ impl NativeTarWriter { archive .finish() .with_context(|| format!("finish archive {}", temporary.display()))?; - fs::rename(&temporary, output).with_context(|| { + // `rename` replaces an existing destination on Unix. Publishing via + // a hard link instead gives this same-directory temporary file an + // atomic no-replace final name. + fs::hard_link(&temporary, output).with_context(|| { format!( "publish completed archive {} as {}", temporary.display(), output.display() ) - }) + })?; + fs::remove_file(&temporary) + .with_context(|| format!("remove temporary archive {}", temporary.display())) })(); if result.is_err() { let _ = fs::remove_file(&temporary); -- cgit v1.2.3