diff --git a/src/self_renamer.rs b/src/self_renamer.rs index 96669870..b368ab14 100644 --- a/src/self_renamer.rs +++ b/src/self_renamer.rs @@ -1,6 +1,6 @@ use color_eyre::eyre::Result; use std::{env::current_exe, fs, path::PathBuf}; -use tracing::{debug, error}; +use tracing::{debug, error, warn}; pub struct SelfRenamer { exe_path: PathBuf, @@ -10,12 +10,40 @@ pub struct SelfRenamer { impl SelfRenamer { pub fn create() -> Result { let tempdir = tempfile::tempdir()?; - let temp_path = tempdir.path().join("topgrade.exe"); + let mut temp_path = tempdir.path().join("topgrade.exe"); let exe_path = current_exe()?; - debug!("Current exe in {:?}. Moving it to {:?}", exe_path, temp_path); + debug!( + "Current exe in {:?}. Attempting to move it to {:?}", + exe_path, temp_path + ); - fs::rename(&exe_path, &temp_path)?; + match fs::rename(&exe_path, &temp_path) { + // cross-device error + Err(e) if e.raw_os_error() == Some(17) => { + debug!("Temporary directory is on a different device. Using the binary parent directory instead"); + + let Some(parent_dir) = exe_path.parent() else { + return Err(color_eyre::eyre::Report::msg( + "Could not get parent directory of the current binary", + )); + }; + + let mut builder = tempfile::Builder::new(); + builder.prefix("topgrade").suffix(".exe"); + let temp_file = builder.tempfile_in(parent_dir)?; + temp_path = temp_file.path().to_path_buf(); + + // Delete the temporary file immediately to free up the name + if let Err(e) = temp_file.close() { + warn!("Could not close temporary file: {}", e); + } + + debug!("Moving current exe in {:?} to {:?}", exe_path, temp_path); + fs::rename(&exe_path, &temp_path) + } + other => other, + }?; Ok(SelfRenamer { exe_path, temp_path }) }