mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Respect the Windows current-directory limit
This commit is contained in:
@@ -58,6 +58,29 @@ fn requested_cwd_precedes_child_path() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_long_and_lexical_paths_unchanged() {
|
||||
let temp = TempDir::new().expect("tempdir");
|
||||
let mut cwd = temp.path().to_path_buf();
|
||||
while cwd.to_string_lossy().len() <= 270 {
|
||||
cwd.push("long-working-directory-segment");
|
||||
}
|
||||
fs::create_dir_all(cwd.join("nested")).expect("create long cwd");
|
||||
let executable = cwd.join("tool.EXE");
|
||||
fs::write(&executable, b"fixture").expect("write executable fixture");
|
||||
let lexical = cwd.join("nested").join("..").join("tool.EXE");
|
||||
|
||||
assert_eq!(
|
||||
resolve_windows_executable(
|
||||
&[lexical.to_string_lossy().into_owned()],
|
||||
&cwd,
|
||||
&HashMap::new(),
|
||||
)
|
||||
.expect("resolve long lexical executable"),
|
||||
lexical
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_extended_length_paths_unchanged() {
|
||||
let executable = dunce::canonicalize(std::env::current_exe().expect("current executable"))
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::process::WindowsProcessLaunch;
|
||||
use crate::winutil::format_last_error;
|
||||
use crate::winutil::quote_windows_arg;
|
||||
use crate::winutil::to_wide;
|
||||
use crate::winutil::to_win32_path_wide;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_utils_pty::PsuedoCon;
|
||||
@@ -144,7 +143,7 @@ pub fn spawn_conpty_process_as_user(
|
||||
0,
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
|
||||
env_block.as_ptr() as *mut c_void,
|
||||
to_win32_path_wide(cwd).as_ptr(),
|
||||
to_wide(cwd).as_ptr(),
|
||||
&si.StartupInfo,
|
||||
&mut pi,
|
||||
)
|
||||
|
||||
@@ -16,7 +16,6 @@ use crate::runner_pipe::find_runner_exe;
|
||||
use crate::runner_pipe::pipe_pair;
|
||||
use crate::winutil::quote_windows_arg;
|
||||
use crate::winutil::to_wide;
|
||||
use crate::winutil::to_win32_path_wide;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use std::ffi::c_void;
|
||||
@@ -343,7 +342,7 @@ pub(crate) fn spawn_runner_transport(
|
||||
);
|
||||
let mut cmdline_vec = to_wide(&runner_full_cmd);
|
||||
let exe_w = to_wide(&runner_cmdline);
|
||||
let cwd_w = to_win32_path_wide(cwd);
|
||||
let cwd_w = to_wide(cwd);
|
||||
let user_w = to_wide(&sandbox_creds.username);
|
||||
let domain_w = to_wide(".");
|
||||
let password_w = to_wide(&sandbox_creds.password);
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::proc_thread_attr::ProcThreadAttributeList;
|
||||
use crate::winutil::argv_to_command_line;
|
||||
use crate::winutil::format_last_error;
|
||||
use crate::winutil::to_wide;
|
||||
use crate::winutil::to_win32_path_wide;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
@@ -118,7 +117,7 @@ pub(crate) unsafe fn create_process_as_user(
|
||||
let env_block = make_env_block(env_map);
|
||||
let desktop = LaunchDesktop::prepare(use_private_desktop, logs_base_dir)?;
|
||||
let mut pi: PROCESS_INFORMATION = std::mem::zeroed();
|
||||
let cwd_wide = to_win32_path_wide(cwd);
|
||||
let cwd_wide = to_wide(cwd);
|
||||
let env_block_len = env_block.len();
|
||||
match stdio {
|
||||
Some((stdin_h, stdout_h, stderr_h)) => {
|
||||
|
||||
@@ -250,7 +250,7 @@ fn legacy_non_tty_uses_explicit_application_path() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_resolves_request_pathext_in_a_long_working_directory() {
|
||||
fn legacy_resolves_request_pathext() {
|
||||
let _guard = legacy_process_test_guard();
|
||||
let runtime = current_thread_runtime();
|
||||
runtime.block_on(async move {
|
||||
@@ -259,12 +259,9 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() {
|
||||
fs::create_dir_all(&tools).expect("create tools directory");
|
||||
let executable = tools.join("path-only-tool.BIN");
|
||||
fs::copy(r"C:\Windows\System32\cmd.exe", &executable).expect("copy executable fixture");
|
||||
let mut cwd = fixture.path().join("workspace");
|
||||
while cwd.to_string_lossy().len() <= 270 {
|
||||
cwd.push("long-working-directory-segment");
|
||||
}
|
||||
fs::create_dir_all(&cwd).expect("create long working directory");
|
||||
let codex_home = sandbox_home("legacy-long-cwd-resolution");
|
||||
let cwd = fixture.path().join("workspace");
|
||||
fs::create_dir_all(&cwd).expect("create working directory");
|
||||
let codex_home = sandbox_home("legacy-request-pathext-resolution");
|
||||
let permission_profile = PermissionProfile::workspace_write();
|
||||
let env_map = HashMap::from([
|
||||
("Path".to_string(), tools.to_string_lossy().into_owned()),
|
||||
@@ -278,7 +275,7 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() {
|
||||
vec![
|
||||
"path-only-tool".to_string(),
|
||||
"/c".to_string(),
|
||||
"echo REQUEST-PATHEXT-LONG-CWD".to_string(),
|
||||
"echo REQUEST-PATHEXT".to_string(),
|
||||
],
|
||||
cwd.as_path(),
|
||||
env_map,
|
||||
@@ -296,10 +293,7 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() {
|
||||
collect_stdout_and_exit(spawned, codex_home.path(), Duration::from_secs(10)).await;
|
||||
let stdout = String::from_utf8_lossy(&stdout);
|
||||
assert_eq!(exit_code, 0, "stdout={stdout:?}");
|
||||
assert!(
|
||||
stdout.contains("REQUEST-PATHEXT-LONG-CWD"),
|
||||
"stdout={stdout:?}"
|
||||
);
|
||||
assert!(stdout.contains("REQUEST-PATHEXT"), "stdout={stdout:?}");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use anyhow::Result;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
use windows_sys::Win32::Foundation::HLOCAL;
|
||||
@@ -23,32 +22,6 @@ pub fn to_wide<S: AsRef<OsStr>>(s: S) -> Vec<u16> {
|
||||
v
|
||||
}
|
||||
|
||||
const MAX_PATH_UTF16_UNITS: usize = 260;
|
||||
const EXTENDED_PATH_PREFIX: &[u16] = &[b'\\' as u16, b'\\' as u16, b'?' as u16, b'\\' as u16];
|
||||
const DEVICE_PATH_PREFIX: &[u16] = &[b'\\' as u16, b'\\' as u16, b'.' as u16, b'\\' as u16];
|
||||
|
||||
/// Encodes a CreateProcess path, adding an extended-length prefix only when needed.
|
||||
pub(crate) fn to_win32_path_wide(path: &Path) -> Vec<u16> {
|
||||
let wide = path.as_os_str().encode_wide().collect::<Vec<_>>();
|
||||
if !path.is_absolute()
|
||||
|| wide.len() < MAX_PATH_UTF16_UNITS
|
||||
|| wide.starts_with(EXTENDED_PATH_PREFIX)
|
||||
|| wide.starts_with(DEVICE_PATH_PREFIX)
|
||||
{
|
||||
return wide.into_iter().chain([0]).collect();
|
||||
}
|
||||
|
||||
let mut extended = EXTENDED_PATH_PREFIX.to_vec();
|
||||
if wide.starts_with(&[b'\\' as u16, b'\\' as u16]) {
|
||||
extended.extend("UNC\\".encode_utf16());
|
||||
extended.extend_from_slice(&wide[2..]);
|
||||
} else {
|
||||
extended.extend(wide);
|
||||
}
|
||||
extended.push(0);
|
||||
extended
|
||||
}
|
||||
|
||||
/// Quote a single Windows command-line argument following the rules used by
|
||||
/// CommandLineToArgvW/CRT so that spaces, quotes, and backslashes are preserved.
|
||||
/// Reference behavior matches Rust std::process::Command on Windows.
|
||||
@@ -235,10 +208,7 @@ fn sid_bytes_from_string(sid_str: &str) -> Result<Vec<u8>> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::argv_to_command_line;
|
||||
use super::to_wide;
|
||||
use super::to_win32_path_wide;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn argv_to_command_line_quotes_each_argument_independently() {
|
||||
@@ -268,23 +238,4 @@ mod tests {
|
||||
"pwsh.exe -Command \"Write-Output \\\"hello world\\\"\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_process_paths_add_extended_prefixes_only_for_long_paths() {
|
||||
let long_tail = ["long-working-directory-segment"; 10].join(r"\");
|
||||
let long_drive = format!(r"C:\{long_tail}");
|
||||
let long_unc = format!(r"\\localhost\C$\{long_tail}");
|
||||
|
||||
assert_eq!(
|
||||
to_win32_path_wide(Path::new(&long_drive)),
|
||||
to_wide(format!(r"\\?\{long_drive}"))
|
||||
);
|
||||
assert_eq!(
|
||||
to_win32_path_wide(Path::new(&long_unc)),
|
||||
to_wide(format!(r"\\?\UNC\{}", &long_unc[2..]))
|
||||
);
|
||||
for path in [r"C:\short", r"\\localhost\C$\short", r"\\?\C:\already"] {
|
||||
assert_eq!(to_win32_path_wide(Path::new(path)), to_wide(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user