summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeyang Zhou <zhy20000919@hotmail.com>2024-07-02 06:54:17 +0800
committerGitHub <noreply@github.com>2024-07-02 06:54:17 +0800
commit9c1f741112f87ba97125e19efb3abf918205ad23 (patch)
treee2993f2f3e6ec8c1cbbeba076a0775c10eb85b8b
parenta555cb4d1d3c04a4bdc0e04d20d23b157cef3b92 (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.rs53
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(())