From 30fdbaf47dc312d0487c650d7c3a828ce2fe18c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 07:18:00 +0000 Subject: [PATCH] Fix ETXTBSY flake in vlt shim self-test vlt_e2e_harness_npx_shim_forwards_the_args_after_the_package wrote two shell scripts with fs::write and exec'd them right away. When a sibling test thread forks while that write fd is open, its child keeps the fd until it execs, and Linux refuses the exec with ETXTBSY ("Text file busy"). This hit both exec sites: the test's own spawn of the npx shim and the shim's `exec` of the fake socket binary. The latter can't be fixed by retrying the spawn. Add write_executable, which stages the bytes next to the target and has a child `cp` create the executable, so the test process never holds a writable fd to it. Use it for the npx/vlt shims (also exec'd by the real-vlt legs) and the fake binary. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_013p5RirmgQs47AecZUsbSmd --- .../tests/vlt_e2e_common/mod.rs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/crates/socket-patch-cli/tests/vlt_e2e_common/mod.rs b/crates/socket-patch-cli/tests/vlt_e2e_common/mod.rs index 4730ee81b..6a4d88fb2 100644 --- a/crates/socket-patch-cli/tests/vlt_e2e_common/mod.rs +++ b/crates/socket-patch-cli/tests/vlt_e2e_common/mod.rs @@ -809,15 +809,8 @@ pub fn write_shims_for(bin: &Path, js: &Path, log: &Path, socket: &Path) { "@echo off\r\nnode --no-warnings \"{}\" %*\r\n", js.display() ); - for (name, body) in [("npx", sh_npx), ("vlt", sh_vlt)] { - let p = bin.join(name); - std::fs::write(&p, body).unwrap(); - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt as _; - std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).unwrap(); - } - } + write_executable(&bin.join("npx"), sh_npx); + write_executable(&bin.join("vlt"), sh_vlt); std::fs::write(bin.join("npx.cmd"), cmd_npx).unwrap(); std::fs::write(bin.join("vlt.cmd"), cmd_vlt).unwrap(); } @@ -2175,6 +2168,33 @@ pub fn write(path: &Path, body: impl AsRef<[u8]>) { std::fs::write(path, body).unwrap(); } +/// Write `body` to `path` as an executable without this process ever +/// holding a writable fd to it. +/// +/// Linux refuses to exec a file while any process holds it open for +/// writing (`ETXTBSY`). Tests run on parallel threads, and a sibling +/// thread that forks while an `fs::write` handle is open passes that fd +/// to its child until the child execs, so exec'ing a just-written script +/// fails intermittently, both when the test spawns it and when a shim +/// `exec`s it. A child `cp` creates the file instead: the only write fd +/// lives in a process no test thread forks from. +pub fn write_executable(path: &Path, body: impl AsRef<[u8]>) { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + let mut name = path.file_name().unwrap().to_os_string(); + name.push(".staged"); + let staged = path.with_file_name(name); + write(&staged, body); + let out = Command::new("cp").arg(&staged).arg(path).output().unwrap(); + assert_ok(&out, "cp of a staged executable"); + std::fs::remove_file(&staged).unwrap(); + std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755)).unwrap(); + } + #[cfg(not(unix))] + write(path, body); +} + pub fn package_json(name: &str, deps: &[(&str, &str)]) -> String { package_json_fields(name, &[("dependencies", deps)]) } @@ -2511,15 +2531,10 @@ fn vlt_e2e_harness_npx_shim_forwards_the_args_after_the_package() { fake } else { let fake = dir.join("socket"); - write( + write_executable( &fake, format!("#!/bin/sh\nprintf '%s\\n' \"$*\" > '{}'\n", argv.display()), ); - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt as _; - std::fs::set_permissions(&fake, std::fs::Permissions::from_mode(0o755)).unwrap(); - } fake }; let bin = dir.join("bin");