diff options
-rw-r--r-- | cli/tools/upgrade.rs | 48 |
1 files changed, 35 insertions, 13 deletions
diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index 0861d1f49..8c987e82c 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -8,6 +8,7 @@ use crate::util::progress_bar::ProgressBar; use crate::version; use deno_core::anyhow::bail; +use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures::future::BoxFuture; use deno_core::futures::FutureExt; @@ -234,14 +235,14 @@ async fn fetch_and_store_latest_version< } pub async fn upgrade(upgrade_flags: UpgradeFlags) -> Result<(), AnyError> { - let old_exe_path = std::env::current_exe()?; - let metadata = fs::metadata(&old_exe_path)?; + let current_exe_path = std::env::current_exe()?; + let metadata = fs::metadata(¤t_exe_path)?; let permissions = metadata.permissions(); if permissions.readonly() { bail!( "You do not have write permission to {}", - old_exe_path.display() + current_exe_path.display() ); } #[cfg(unix)] @@ -252,7 +253,7 @@ pub async fn upgrade(upgrade_flags: UpgradeFlags) -> Result<(), AnyError> { "You don't have write permission to {} because it's owned by root.\n", "Consider updating deno through your package manager if its installed from it.\n", "Otherwise run `deno upgrade` as root.", - ), old_exe_path.display()); + ), current_exe_path.display()); } let client = build_http_client(upgrade_flags.ca_file)?; @@ -348,12 +349,33 @@ pub async fn upgrade(upgrade_flags: UpgradeFlags) -> Result<(), AnyError> { check_exe(&new_exe_path)?; if !upgrade_flags.dry_run { - match upgrade_flags.output { - Some(path) => { - fs::rename(&new_exe_path, &path) - .or_else(|_| fs::copy(&new_exe_path, &path).map(|_| ()))?; + let output_exe_path = + upgrade_flags.output.as_ref().unwrap_or(¤t_exe_path); + let output_result = if *output_exe_path == current_exe_path { + replace_exe(&new_exe_path, output_exe_path) + } else { + fs::rename(&new_exe_path, output_exe_path) + .or_else(|_| fs::copy(&new_exe_path, output_exe_path).map(|_| ())) + }; + if let Err(err) = output_result { + const WIN_ERROR_ACCESS_DENIED: i32 = 5; + if cfg!(windows) && err.raw_os_error() == Some(WIN_ERROR_ACCESS_DENIED) { + return Err(err).with_context(|| { + format!( + concat!( + "Access denied: Could not replace the deno executable. This may be ", + "because an existing deno process is running. Please ensure there ", + "are no running deno processes (ex. Stop-Process -Name deno ; deno {}), ", + "close any editors before upgrading, and ensure you have sufficient ", + "permission to '{}'." + ), + std::env::args().collect::<Vec<_>>().join(" "), + output_exe_path.display(), + ) + }); + } else { + return Err(err.into()); } - None => replace_exe(&new_exe_path, &old_exe_path)?, } } @@ -519,17 +541,17 @@ pub fn unpack( Ok(exe_path) } -fn replace_exe(new: &Path, old: &Path) -> Result<(), std::io::Error> { +fn replace_exe(from: &Path, to: &Path) -> Result<(), std::io::Error> { if cfg!(windows) { // On windows you cannot replace the currently running executable. // so first we rename it to deno.old.exe - fs::rename(old, old.with_extension("old.exe"))?; + fs::rename(to, to.with_extension("old.exe"))?; } else { - fs::remove_file(old)?; + fs::remove_file(to)?; } // Windows cannot rename files across device boundaries, so if rename fails, // we try again with copy. - fs::rename(new, old).or_else(|_| fs::copy(new, old).map(|_| ()))?; + fs::rename(from, to).or_else(|_| fs::copy(from, to).map(|_| ()))?; Ok(()) } |