fix(upgrade/windows): show informative message on access denied error (#16887)

Closes #16886
This commit is contained in:
David Sherret 2022-12-02 14:53:10 -05:00 committed by GitHub
parent c3b75c692c
commit 83bc8c473d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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(&current_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(&current_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(())
}