Files
codex/codex-rs/cli/tests/support/executable.rs
T
jif 58670eeac4 Isolate executable fixture copies in CLI tests on Linux (#48213)
## Why

Sibling tests can spawn children while an executable fixture is open for
writing, allowing inherited writable descriptors to cause `ETXTBSY` when
launching the fixture.

## What changed

Add a shared `copy_executable` helper for the daemon, doctor path safety, and
exec-server tests. On Linux, run `/bin/cp` in a separate process and wait for
it to exit before launching the fixture, then restore the source permissions
to account for restrictive umasks. Keep `std::fs::copy` on other platforms.

GitOrigin-RevId: 88f36826bbc6ab4e16e28ff02d5e7d6bd8dbf793
2026-09-25 20:54:31 +00:00

40 lines
1.4 KiB
Rust

//! Copies executable fixtures without exposing writable descriptors to sibling test spawns.
//! Linux copies finish in a separate process before the fixture can be launched.
use std::path::Path;
#[cfg(target_os = "linux")]
use std::process::Command;
#[cfg(target_os = "linux")]
use anyhow::Context;
use anyhow::Result;
#[cfg(target_os = "linux")]
use anyhow::ensure;
pub(super) fn copy_executable(source: &Path, destination: &Path) -> Result<()> {
#[cfg(target_os = "linux")]
{
// Sibling tests can spawn children while a copy holds the executable open
// for writing. Keep that descriptor in a separate process and wait for it
// to exit, so inherited descriptors cannot cause ETXTBSY during launch.
let output = Command::new("/bin/cp")
.arg("--")
.arg(source)
.arg(destination)
.output()
.context("failed to start fixture executable copy")?;
ensure!(
output.status.success(),
"failed to copy fixture executable {} to {}: {}",
source.display(),
destination.display(),
String::from_utf8_lossy(&output.stderr)
);
// Match fs::copy even when the test process has a restrictive umask.
std::fs::set_permissions(destination, std::fs::metadata(source)?.permissions())?;
}
#[cfg(not(target_os = "linux"))]
std::fs::copy(source, destination)?;
Ok(())
}