diff options
author | Heyang Zhou <zhy20000919@hotmail.com> | 2024-07-02 06:54:17 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-02 06:54:17 +0800 |
commit | 9c1f741112f87ba97125e19efb3abf918205ad23 (patch) | |
tree | e2993f2f3e6ec8c1cbbeba076a0775c10eb85b8b | |
parent | a555cb4d1d3c04a4bdc0e04d20d23b157cef3b92 (diff) |
fix(compile): atomically write compile output (#24378)
Atomically write `deno compile` output file so we won't get a partially
written ELF/PE file, and prevents corrupting running processes.
-rw-r--r-- | cli/tools/compile.rs | 53 |
1 files changed, 42 insertions, 11 deletions
diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 94bcce7e6..b7aa94691 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -12,6 +12,7 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_graph::GraphKind; use deno_terminal::colors; +use rand::Rng; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -97,8 +98,20 @@ pub async fn compile( ); validate_output_path(&output_path)?; - let mut file = std::fs::File::create(&output_path) - .with_context(|| format!("Opening file '{}'", output_path.display()))?; + let mut temp_filename = output_path.file_name().unwrap().to_owned(); + temp_filename.push(format!( + ".tmp-{}", + faster_hex::hex_encode( + &rand::thread_rng().gen::<[u8; 8]>(), + &mut [0u8; 16] + ) + .unwrap() + )); + let temp_path = output_path.with_file_name(temp_filename); + + let mut file = std::fs::File::create(&temp_path).with_context(|| { + format!("Opening temporary file '{}'", temp_path.display()) + })?; let write_result = binary_writer .write_bin( &mut file, @@ -108,20 +121,38 @@ pub async fn compile( cli_options, ) .await - .with_context(|| format!("Writing {}", output_path.display())); + .with_context(|| { + format!("Writing temporary file '{}'", temp_path.display()) + }); drop(file); - if let Err(err) = write_result { - // errored, so attempt to remove the output path - let _ = std::fs::remove_file(output_path); - return Err(err); - } // set it as executable #[cfg(unix)] - { + let write_result = write_result.and_then(|_| { use std::os::unix::fs::PermissionsExt; - let perms = std::fs::Permissions::from_mode(0o777); - std::fs::set_permissions(output_path, perms)?; + let perms = std::fs::Permissions::from_mode(0o755); + std::fs::set_permissions(&temp_path, perms).with_context(|| { + format!( + "Setting permissions on temporary file '{}'", + temp_path.display() + ) + }) + }); + + let write_result = write_result.and_then(|_| { + std::fs::rename(&temp_path, &output_path).with_context(|| { + format!( + "Renaming temporary file '{}' to '{}'", + temp_path.display(), + output_path.display() + ) + }) + }); + + if let Err(err) = write_result { + // errored, so attempt to remove the temporary file + let _ = std::fs::remove_file(temp_path); + return Err(err); } Ok(()) |