Codex Handbook
windows-sandbox-rs/src/helper_materialization.rs 562 lines
use anyhow::Context;use anyhow::Result;use anyhow::anyhow;use std::collections::HashMap;use std::ffi::OsStr;use std::fs;use std::io::Write;use std::path::Path;use std::path::PathBuf;use std::sync::Mutex;use std::sync::OnceLock;use std::time::UNIX_EPOCH;use tempfile::NamedTempFile;use crate::logging::log_note;use crate::sandbox_bin_dir;const DEV_BUILD_VERSION_SENTINEL: &str = "0.0.0";pub(crate) const BIN_DIRNAME: &str = "bin";pub(crate) const RESOURCES_DIRNAME: &str = "codex-resources";#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub(crate) enum HelperExecutable {    CommandRunner,}impl HelperExecutable {    fn file_name(self) -> &'static str {        match self {            Self::CommandRunner => "codex-command-runner.exe",        }    }    fn label(self) -> &'static str {        match self {            Self::CommandRunner => "command-runner",        }    }}#[derive(Clone, Copy, Debug, Eq, PartialEq)]enum CopyOutcome {    Reused,    ReCopied,}static HELPER_PATH_CACHE: OnceLock<Mutex<HashMap<String, PathBuf>>> = OnceLock::new();pub(crate) fn helper_bin_dir(codex_home: &Path) -> PathBuf {    sandbox_bin_dir(codex_home)}pub(crate) fn legacy_lookup(kind: HelperExecutable) -> PathBuf {    if let Ok(exe) = std::env::current_exe()        && let Some(candidate) = bundled_executable_path_for_exe(&exe, kind.file_name())    {        return candidate;    }    PathBuf::from(kind.file_name())}pub(crate) fn resolve_helper_for_launch(    kind: HelperExecutable,    codex_home: &Path,    log_dir: Option<&Path>,) -> PathBuf {    match copy_helper_if_needed(kind, codex_home, log_dir) {        Ok(path) => {            log_note(                &format!(                    "helper launch resolution: using copied {} path {}",                    kind.label(),                    path.display()                ),                log_dir,            );            path        }        Err(err) => {            let fallback = legacy_lookup(kind);            log_note(                &format!(                    "helper copy failed for {}: {err:#}; falling back to legacy path {}",                    kind.label(),                    fallback.display()                ),                log_dir,            );            fallback        }    }}pub fn resolve_current_exe_for_launch(codex_home: &Path, fallback_executable: &str) -> PathBuf {    let source = match std::env::current_exe() {        Ok(path) => path,        Err(_) => return PathBuf::from(fallback_executable),    };    let Some(file_name) = source.file_name() else {        return source;    };    let destination = helper_bin_dir(codex_home).join(file_name);    match copy_from_source_if_needed(&source, &destination) {        Ok(_) => destination,        Err(err) => {            let sandbox_log_dir = crate::sandbox_dir(codex_home);            log_note(                &format!(                    "helper copy failed for current executable: {err:#}; falling back to legacy path {}",                    source.display()                ),                Some(&sandbox_log_dir),            );            source        }    }}pub(crate) fn copy_helper_if_needed(    kind: HelperExecutable,    codex_home: &Path,    log_dir: Option<&Path>,) -> Result<PathBuf> {    let cache_key = format!("{}|{}", kind.file_name(), codex_home.display());    if let Some(path) = cached_helper_path(&cache_key) {        log_note(            &format!(                "helper copy: using in-memory cache for {} -> {}",                kind.label(),                path.display()            ),            log_dir,        );        return Ok(path);    }    let source = sibling_source_path(kind)?;    let destination = helper_destination_for_source(kind, codex_home, &source)?;    log_note(        &format!(            "helper copy: validating {} source={} destination={}",            kind.label(),            source.display(),            destination.display()        ),        log_dir,    );    let outcome = copy_from_source_if_needed(&source, &destination)?;    let action = match outcome {        CopyOutcome::Reused => "reused",        CopyOutcome::ReCopied => "recopied",    };    log_note(        &format!(            "helper copy: {} {} source={} destination={}",            action,            kind.label(),            source.display(),            destination.display()        ),        log_dir,    );    store_helper_path(cache_key, destination.clone());    Ok(destination)}fn cached_helper_path(cache_key: &str) -> Option<PathBuf> {    let cache = HELPER_PATH_CACHE.get_or_init(|| Mutex::new(HashMap::new()));    let guard = cache.lock().ok()?;    guard.get(cache_key).cloned()}fn store_helper_path(cache_key: String, path: PathBuf) {    let cache = HELPER_PATH_CACHE.get_or_init(|| Mutex::new(HashMap::new()));    if let Ok(mut guard) = cache.lock() {        guard.insert(cache_key, path);    }}fn sibling_source_path(kind: HelperExecutable) -> Result<PathBuf> {    let exe = std::env::current_exe().context("resolve current executable for helper lookup")?;    bundled_executable_path_for_exe(&exe, kind.file_name()).ok_or_else(|| {        anyhow!(            "helper not found next to current executable or under {RESOURCES_DIRNAME}: {}",            exe.display()        )    })}pub(crate) fn bundled_executable_path_for_exe(exe: &Path, file_name: &str) -> Option<PathBuf> {    let dir = exe.parent()?;    let direct_candidate = dir.join(file_name);    if direct_candidate.is_file() {        return Some(direct_candidate);    }    if dir.file_name() == Some(OsStr::new(BIN_DIRNAME))        && let Some(package_dir) = dir.parent()    {        let package_resource_candidate = package_dir.join(RESOURCES_DIRNAME).join(file_name);        if package_resource_candidate.is_file() {            return Some(package_resource_candidate);        }    }    let resource_candidate = dir.join(RESOURCES_DIRNAME).join(file_name);    resource_candidate.is_file().then_some(resource_candidate)}fn helper_destination_for_source(    kind: HelperExecutable,    codex_home: &Path,    source: &Path,) -> Result<PathBuf> {    let suffix = helper_version_suffix(source)?;    let file_name = materialized_file_name(kind, &suffix);    Ok(helper_bin_dir(codex_home).join(file_name))}fn materialized_file_name(kind: HelperExecutable, suffix: &str) -> String {    let source_name = kind.file_name();    let path = Path::new(source_name);    let stem = path        .file_stem()        .and_then(|stem| stem.to_str())        .unwrap_or(source_name);    let extension = path        .extension()        .and_then(|ext| ext.to_str())        .map(|ext| format!(".{ext}"))        .unwrap_or_default();    format!("{stem}-{suffix}{extension}")}fn helper_version_suffix(source: &Path) -> Result<String> {    let version = env!("CARGO_PKG_VERSION");    if version == DEV_BUILD_VERSION_SENTINEL {        dev_build_suffix(source)    } else {        Ok(version.to_string())    }}fn dev_build_suffix(source: &Path) -> Result<String> {    let metadata = fs::metadata(source)        .with_context(|| format!("read helper source metadata {}", source.display()))?;    let modified = metadata        .modified()        .with_context(|| format!("read helper source mtime {}", source.display()))?;    let duration = modified        .duration_since(UNIX_EPOCH)        .with_context(|| format!("convert helper source mtime {}", source.display()))?;    Ok(format!("{}-{:x}", metadata.len(), duration.as_secs(),))}fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result<CopyOutcome> {    if destination_is_fresh(source, destination)? {        return Ok(CopyOutcome::Reused);    }    let destination_dir = destination.parent().ok_or_else(|| {        anyhow!(            "helper destination has no parent: {}",            destination.display()        )    })?;    fs::create_dir_all(destination_dir).with_context(|| {        format!(            "create helper destination directory {}",            destination_dir.display()        )    })?;    let temp_path = NamedTempFile::new_in(destination_dir)        .with_context(|| {            format!(                "create temporary helper file in {}",                destination_dir.display()            )        })?        .into_temp_path();    let temp_path_buf = temp_path.to_path_buf();    let mut source_file = fs::File::open(source)        .with_context(|| format!("open helper source for read {}", source.display()))?;    let mut temp_file = fs::OpenOptions::new()        .write(true)        .truncate(true)        .open(&temp_path_buf)        .with_context(|| format!("open temporary helper file {}", temp_path_buf.display()))?;    // Write into a temp file created inside `.sandbox-bin` so the copied helper keeps the    // destination directory's inherited ACLs instead of reusing the source file's descriptor.    std::io::copy(&mut source_file, &mut temp_file).with_context(|| {        format!(            "copy helper from {} to {}",            source.display(),            temp_path_buf.display()        )    })?;    temp_file        .flush()        .with_context(|| format!("flush temporary helper file {}", temp_path_buf.display()))?;    drop(temp_file);    if destination.exists() {        fs::remove_file(destination).with_context(|| {            format!("remove stale helper destination {}", destination.display())        })?;    }    match fs::rename(&temp_path_buf, destination) {        Ok(()) => Ok(CopyOutcome::ReCopied),        Err(rename_err) => {            if destination_is_fresh(source, destination)? {                Ok(CopyOutcome::Reused)            } else {                Err(rename_err).with_context(|| {                    format!(                        "rename helper temp file {} to {}",                        temp_path_buf.display(),                        destination.display()                    )                })            }        }    }}fn destination_is_fresh(source: &Path, destination: &Path) -> Result<bool> {    let source_meta = fs::metadata(source)        .with_context(|| format!("read helper source metadata {}", source.display()))?;    let destination_meta = match fs::metadata(destination) {        Ok(meta) => meta,        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),        Err(err) => {            return Err(err).with_context(|| {                format!("read helper destination metadata {}", destination.display())            });        }    };    if source_meta.len() != destination_meta.len() {        return Ok(false);    }    let source_modified = source_meta        .modified()        .with_context(|| format!("read helper source mtime {}", source.display()))?;    let destination_modified = destination_meta        .modified()        .with_context(|| format!("read helper destination mtime {}", destination.display()))?;    Ok(destination_modified >= source_modified)}#[cfg(test)]mod tests {    use super::BIN_DIRNAME;    use super::CopyOutcome;    use super::DEV_BUILD_VERSION_SENTINEL;    use super::HelperExecutable;    use super::RESOURCES_DIRNAME;    use super::bundled_executable_path_for_exe;    use super::copy_from_source_if_needed;    use super::destination_is_fresh;    use super::dev_build_suffix;    use super::helper_bin_dir;    use super::helper_version_suffix;    use super::materialized_file_name;    use pretty_assertions::assert_eq;    use std::fs;    use std::path::Path;    use std::path::PathBuf;    use tempfile::TempDir;    #[test]    fn copy_from_source_if_needed_copies_missing_destination() {        let tmp = TempDir::new().expect("tempdir");        let source = tmp.path().join("source.exe");        let destination = tmp.path().join("bin").join("helper.exe");        fs::write(&source, b"runner-v1").expect("write source");        let outcome = copy_from_source_if_needed(&source, &destination).expect("copy helper");        assert_eq!(CopyOutcome::ReCopied, outcome);        assert_eq!(            b"runner-v1".as_slice(),            fs::read(&destination).expect("read destination")        );    }    #[test]    fn destination_is_fresh_uses_size_and_mtime() {        let tmp = TempDir::new().expect("tempdir");        let source = tmp.path().join("source.exe");        let destination = tmp.path().join("destination.exe");        fs::write(&destination, b"same-size").expect("write destination");        std::thread::sleep(std::time::Duration::from_secs(1));        fs::write(&source, b"same-size").expect("write source");        assert!(!destination_is_fresh(&source, &destination).expect("stale metadata"));        fs::write(&destination, b"same-size").expect("rewrite destination");        assert!(destination_is_fresh(&source, &destination).expect("fresh metadata"));    }    #[test]    fn copy_from_source_if_needed_reuses_fresh_destination() {        let tmp = TempDir::new().expect("tempdir");        let source = tmp.path().join("source.exe");        let destination = tmp.path().join("bin").join("helper.exe");        fs::write(&source, b"runner-v1").expect("write source");        copy_from_source_if_needed(&source, &destination).expect("initial copy");        let outcome = copy_from_source_if_needed(&source, &destination).expect("revalidate helper");        assert_eq!(CopyOutcome::Reused, outcome);        assert_eq!(            b"runner-v1".as_slice(),            fs::read(&destination).expect("read destination")        );    }    #[test]    fn helper_bin_dir_is_under_sandbox_bin() {        let codex_home = Path::new(r"C:\Users\example\.codex");        assert_eq!(            PathBuf::from(r"C:\Users\example\.codex\.sandbox-bin"),            helper_bin_dir(codex_home)        );    }    #[test]    fn copy_runner_into_shared_bin_dir() {        let tmp = TempDir::new().expect("tempdir");        let codex_home = tmp.path().join("codex-home");        let source_dir = tmp.path().join("sibling-source");        fs::create_dir_all(&source_dir).expect("create source dir");        let runner_source = source_dir.join("codex-command-runner.exe");        fs::write(&runner_source, b"runner").expect("runner");        let runner_suffix = helper_version_suffix(&runner_source).expect("runner suffix");        let runner_destination = helper_bin_dir(&codex_home).join(materialized_file_name(            HelperExecutable::CommandRunner,            &runner_suffix,        ));        let runner_outcome =            copy_from_source_if_needed(&runner_source, &runner_destination).expect("runner copy");        assert_eq!(CopyOutcome::ReCopied, runner_outcome);        assert_eq!(            b"runner".as_slice(),            fs::read(&runner_destination).expect("read runner")        );    }    #[test]    fn helper_source_lookup_checks_resource_dir() {        let tmp = TempDir::new().expect("tempdir");        let release_dir = tmp.path().join("release");        let resources_dir = release_dir.join(RESOURCES_DIRNAME);        fs::create_dir_all(&resources_dir).expect("create resources dir");        let exe = release_dir.join("codex.exe");        let helper = resources_dir.join("codex-command-runner.exe");        fs::write(&exe, b"codex").expect("write exe");        fs::write(&helper, b"runner").expect("write helper");        let resolved =            bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")                .expect("helper path");        assert_eq!(resolved, helper);    }    #[test]    fn helper_source_lookup_checks_package_resource_dir_for_bin_exe() {        let tmp = TempDir::new().expect("tempdir");        let package_dir = tmp.path().join("package");        let bin_dir = package_dir.join(BIN_DIRNAME);        let resources_dir = package_dir.join(RESOURCES_DIRNAME);        fs::create_dir_all(&bin_dir).expect("create bin dir");        fs::create_dir_all(&resources_dir).expect("create resources dir");        let exe = bin_dir.join("codex.exe");        let helper = resources_dir.join("codex-command-runner.exe");        fs::write(&exe, b"codex").expect("write exe");        fs::write(&helper, b"runner").expect("write helper");        let resolved =            bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")                .expect("helper path");        assert_eq!(resolved, helper);    }    #[test]    fn helper_source_lookup_prefers_package_resource_dir_over_bin_resource_dir() {        let tmp = TempDir::new().expect("tempdir");        let package_dir = tmp.path().join("package");        let bin_dir = package_dir.join(BIN_DIRNAME);        let package_resources_dir = package_dir.join(RESOURCES_DIRNAME);        let bin_resources_dir = bin_dir.join(RESOURCES_DIRNAME);        fs::create_dir_all(&package_resources_dir).expect("create package resources dir");        fs::create_dir_all(&bin_resources_dir).expect("create bin resources dir");        let exe = bin_dir.join("codex.exe");        let package_helper = package_resources_dir.join("codex-command-runner.exe");        let bin_helper = bin_resources_dir.join("codex-command-runner.exe");        fs::write(&exe, b"codex").expect("write exe");        fs::write(&package_helper, b"package runner").expect("write package helper");        fs::write(&bin_helper, b"bin runner").expect("write bin helper");        let resolved =            bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")                .expect("helper path");        assert_eq!(resolved, package_helper);    }    #[test]    fn helper_source_lookup_prefers_direct_sibling_over_resource_dir() {        let tmp = TempDir::new().expect("tempdir");        let release_dir = tmp.path().join("release");        let resources_dir = release_dir.join(RESOURCES_DIRNAME);        fs::create_dir_all(&resources_dir).expect("create resources dir");        let exe = release_dir.join("codex.exe");        let sibling_helper = release_dir.join("codex-command-runner.exe");        let resource_helper = resources_dir.join("codex-command-runner.exe");        fs::write(&exe, b"codex").expect("write exe");        fs::write(&sibling_helper, b"sibling runner").expect("write sibling helper");        fs::write(&resource_helper, b"resource runner").expect("write resource helper");        let resolved =            bundled_executable_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe")                .expect("helper path");        assert_eq!(resolved, sibling_helper);    }    #[test]    fn helper_version_suffix_uses_cli_version_or_dev_build_metadata() {        let tmp = TempDir::new().expect("tempdir");        let source = tmp.path().join("source.exe");        fs::write(&source, b"runner-v1").expect("write source");        let suffix = helper_version_suffix(&source).expect("suffix");        if env!("CARGO_PKG_VERSION") == DEV_BUILD_VERSION_SENTINEL {            assert_eq!(suffix, dev_build_suffix(&source).expect("dev build suffix"));        } else {            assert_eq!(suffix, env!("CARGO_PKG_VERSION"));        }    }    #[test]    fn materialized_file_name_adds_suffix_before_extension() {        let file_name = materialized_file_name(HelperExecutable::CommandRunner, "test-suffix");        assert_eq!(file_name, "codex-command-runner-test-suffix.exe");    }}