1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

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.
This commit is contained in:
Heyang Zhou 2024-07-02 06:54:17 +08:00 committed by GitHub
parent a555cb4d1d
commit 9c1f741112
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -12,6 +12,7 @@ use deno_core::error::AnyError;
use deno_core::resolve_url_or_path; use deno_core::resolve_url_or_path;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_terminal::colors; use deno_terminal::colors;
use rand::Rng;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -97,8 +98,20 @@ pub async fn compile(
); );
validate_output_path(&output_path)?; validate_output_path(&output_path)?;
let mut file = std::fs::File::create(&output_path) let mut temp_filename = output_path.file_name().unwrap().to_owned();
.with_context(|| format!("Opening file '{}'", output_path.display()))?; 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 let write_result = binary_writer
.write_bin( .write_bin(
&mut file, &mut file,
@ -108,20 +121,38 @@ pub async fn compile(
cli_options, cli_options,
) )
.await .await
.with_context(|| format!("Writing {}", output_path.display())); .with_context(|| {
format!("Writing temporary file '{}'", temp_path.display())
});
drop(file); 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 // set it as executable
#[cfg(unix)] #[cfg(unix)]
{ let write_result = write_result.and_then(|_| {
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
let perms = std::fs::Permissions::from_mode(0o777); let perms = std::fs::Permissions::from_mode(0o755);
std::fs::set_permissions(output_path, perms)?; 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(()) Ok(())