magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Windows relocation without self-replace's executable-search cleanup helper.
use anyhow::{Context, Result, ensure};
use std::{
    ffi::OsString,
    fs,
    os::windows::{ffi::OsStringExt, process::CommandExt},
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

// Only the uniquely named relocated image is removed. A mapped image cannot be
// deleted on Windows, so retry until its last user exits. Bound the helper's life;
// a long-lived second instance can leave recovery files for manual removal.
const CLEANUP_SCRIPT: &str = r#"
$ErrorActionPreference = 'Stop'
$path = $env:MAGI_UPDATE_RELOCATED_IMAGE
$deadline = [DateTime]::UtcNow.AddHours(2)
do {
    try {
        [IO.File]::Delete($path)
        [IO.Directory]::Delete([IO.Path]::GetDirectoryName($path))
        exit 0
    } catch {
        [Threading.Thread]::Sleep(1000)
    }
} while ([DateTime]::UtcNow -lt $deadline)
exit 1
"#;

pub(super) fn system_directory() -> Result<PathBuf> {
    let mut buffer = vec![0u16; 32768];
    // SAFETY: the writable buffer contains the advertised number of UTF-16 units.
    let length = unsafe {
        windows_sys::Win32::System::SystemInformation::GetSystemDirectoryW(
            buffer.as_mut_ptr(),
            buffer.len() as u32,
        )
    } as usize;
    ensure!(
        length > 0 && length < buffer.len(),
        "cannot resolve Windows system directory"
    );
    let directory = PathBuf::from(OsString::from_wide(&buffer[..length]));
    ensure!(
        directory.is_absolute(),
        "Windows system directory is not absolute"
    );
    Ok(directory)
}

fn cleanup_command(system: &Path, relocated: &Path) -> Command {
    let mut command = Command::new(system.join("WindowsPowerShell/v1.0/powershell.exe"));
    command
        .args([
            "-NoLogo",
            "-NoProfile",
            "-NonInteractive",
            "-Command",
            CLEANUP_SCRIPT,
        ])
        // The filename is data, never interpolated into shell source. Neither
        // PATH, SystemRoot, repository cwd nor PowerShell profiles select code.
        .env("MAGI_UPDATE_RELOCATED_IMAGE", relocated)
        .current_dir(system)
        .creation_flags(0x08000000) // CREATE_NO_WINDOW
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null());
    command
}

pub(super) fn release_running_executable(executable: &Path, copy: &Path) -> Result<()> {
    let system = system_directory()?;
    ensure!(
        system
            .join("WindowsPowerShell/v1.0/powershell.exe")
            .is_file(),
        "Windows PowerShell is unavailable; update this installation manually"
    );
    // Stay on the installation filesystem so both renames are atomic. Keep the
    // directory private and retain it on failure rather than lose a mapped image.
    let directory = tempfile::Builder::new()
        .prefix(".magi-code-relocated-")
        .tempdir_in(executable.parent().context("executable has no parent")?)?;
    let replacement = directory.path().join("replacement.exe");
    fs::copy(copy, &replacement).context("cannot stage Windows replacement")?;
    let directory = directory.keep();
    let relocated = directory.join("previous.exe");
    fs::rename(executable, &relocated).context("cannot relocate running Windows image")?;
    if let Err(error) = fs::rename(&replacement, executable) {
        fs::rename(&relocated, executable).with_context(|| {
            format!(
                "cannot restore Windows image; recovery files: {}",
                directory.display()
            )
        })?;
        return Err(error).context("cannot restore the Windows install path");
    }
    // Intentionally outlives this updater: it removes only the old mapped image,
    // never Cargo's install destination or records. No copied helper executable.
    cleanup_command(&system, &relocated)
        .spawn()
        .with_context(|| {
            format!(
                "cannot start Windows cleanup; recovery files: {}",
                directory.display()
            )
        })?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn cleanup_uses_absolute_system_program_and_keeps_filename_out_of_script() {
        let system = system_directory().unwrap();
        let path = Path::new(r"C:\untrusted & ' directory\previous.exe");
        let command = cleanup_command(&system, path);
        assert_eq!(
            command.get_program(),
            system.join("WindowsPowerShell/v1.0/powershell.exe")
        );
        assert_eq!(command.get_current_dir(), Some(system.as_path()));
        assert_eq!(
            command.get_args().last(),
            Some(std::ffi::OsStr::new(CLEANUP_SCRIPT))
        );
        assert!(command.get_envs().any(|(key, value)| {
            key == "MAGI_UPDATE_RELOCATED_IMAGE" && value == Some(path.as_os_str())
        }));
    }

    #[test]
    fn cleanup_removes_only_relocated_file_and_its_empty_directory() {
        let root = tempfile::tempdir().unwrap();
        let old_directory = root.path().join("old & ' image");
        fs::create_dir(&old_directory).unwrap();
        let old = old_directory.join("previous.exe");
        fs::write(&old, b"old").unwrap();
        let installed = root.path().join("magi-code.exe");
        fs::write(&installed, b"new").unwrap();
        let mut command = cleanup_command(&system_directory().unwrap(), &old);
        let status =
            crate::updates::process::run_cargo(&mut command, std::time::Duration::from_secs(15))
                .unwrap();
        assert!(status.success());
        assert!(!old_directory.exists());
        assert_eq!(fs::read(installed).unwrap(), b"new");
    }
}