mirror of
https://github.com/openai/codex.git
synced 2026-05-22 03:54:18 +00:00
Compare commits
4 Commits
abhinav/sp
...
automation
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e0931e079 | ||
|
|
080a390eb1 | ||
|
|
c92ca4d0c7 | ||
|
|
2ba1fedded |
@@ -10,6 +10,7 @@ use codex_login::default_client::originator;
|
||||
use codex_otel::sanitize_metric_tag_value;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_utils_path::env::is_headless_environment;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
@@ -85,7 +86,21 @@ pub fn resolve_windows_sandbox_private_desktop(cfg: &ConfigToml, profile: &Confi
|
||||
.as_ref()
|
||||
.and_then(|windows| windows.sandbox_private_desktop)
|
||||
})
|
||||
.unwrap_or(true)
|
||||
.unwrap_or_else(default_windows_sandbox_private_desktop)
|
||||
}
|
||||
|
||||
fn default_windows_sandbox_private_desktop() -> bool {
|
||||
default_windows_sandbox_private_desktop_for_environment(
|
||||
cfg!(target_os = "windows"),
|
||||
is_headless_environment(),
|
||||
)
|
||||
}
|
||||
|
||||
fn default_windows_sandbox_private_desktop_for_environment(
|
||||
is_windows: bool,
|
||||
is_headless: bool,
|
||||
) -> bool {
|
||||
!is_windows || !is_headless
|
||||
}
|
||||
|
||||
fn legacy_windows_sandbox_keys_present(features: Option<&FeaturesToml>) -> bool {
|
||||
|
||||
@@ -166,9 +166,9 @@ fn resolve_windows_sandbox_private_desktop_prefers_profile_windows() {
|
||||
|
||||
#[test]
|
||||
fn resolve_windows_sandbox_private_desktop_defaults_to_true() {
|
||||
assert!(resolve_windows_sandbox_private_desktop(
|
||||
&ConfigToml::default(),
|
||||
&ConfigProfile::default()
|
||||
assert!(default_windows_sandbox_private_desktop_for_environment(
|
||||
/*is_windows*/ true,
|
||||
/*is_headless*/ false
|
||||
));
|
||||
}
|
||||
|
||||
@@ -187,3 +187,19 @@ fn resolve_windows_sandbox_private_desktop_respects_explicit_cfg_value() {
|
||||
&ConfigProfile::default()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_windows_sandbox_private_desktop_defaults_to_false_for_headless_windows() {
|
||||
assert!(!default_windows_sandbox_private_desktop_for_environment(
|
||||
/*is_windows*/ true,
|
||||
/*is_headless*/ true
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_windows_sandbox_private_desktop_non_windows_default_stays_true() {
|
||||
assert!(default_windows_sandbox_private_desktop_for_environment(
|
||||
/*is_windows*/ false,
|
||||
/*is_headless*/ true
|
||||
));
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ features = [
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_System_Registry",
|
||||
"Win32_NetworkManagement_WNet",
|
||||
]
|
||||
version = "0.52"
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@ use codex_windows_sandbox::encode_bytes;
|
||||
use codex_windows_sandbox::get_current_token_for_restriction;
|
||||
use codex_windows_sandbox::hide_current_user_profile_dir;
|
||||
use codex_windows_sandbox::log_note;
|
||||
use codex_windows_sandbox::normalize_spawn_cwd;
|
||||
use codex_windows_sandbox::parse_policy;
|
||||
use codex_windows_sandbox::path_uses_unc_prefix;
|
||||
use codex_windows_sandbox::read_frame;
|
||||
use codex_windows_sandbox::read_handle_loop;
|
||||
use codex_windows_sandbox::spawn_process_with_pipes;
|
||||
@@ -200,6 +202,21 @@ fn read_spawn_request(reader: &mut File) -> Result<SpawnRequest> {
|
||||
|
||||
/// Pick an effective CWD, using a junction if the ACL helper is active.
|
||||
fn effective_cwd(req_cwd: &Path, log_dir: Option<&Path>) -> PathBuf {
|
||||
let normalized_cwd = normalize_spawn_cwd(req_cwd);
|
||||
if path_uses_unc_prefix(&normalized_cwd) {
|
||||
if normalized_cwd != req_cwd {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: using UNC cwd {} resolved from {}",
|
||||
normalized_cwd.display(),
|
||||
req_cwd.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
}
|
||||
return normalized_cwd;
|
||||
}
|
||||
|
||||
let use_junction = match read_acl_mutex::read_acl_mutex_exists() {
|
||||
Ok(exists) => exists,
|
||||
Err(err) => {
|
||||
@@ -213,9 +230,10 @@ fn effective_cwd(req_cwd: &Path, log_dir: Option<&Path>) -> PathBuf {
|
||||
}
|
||||
};
|
||||
if use_junction {
|
||||
cwd_junction::create_cwd_junction(req_cwd, log_dir).unwrap_or_else(|| req_cwd.to_path_buf())
|
||||
cwd_junction::create_cwd_junction(req_cwd, log_dir)
|
||||
.unwrap_or_else(|| normalized_cwd.clone())
|
||||
} else {
|
||||
req_cwd.to_path_buf()
|
||||
normalized_cwd
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::ipc_framed::Message;
|
||||
use crate::ipc_framed::SpawnRequest;
|
||||
use crate::ipc_framed::read_frame;
|
||||
use crate::ipc_framed::write_frame;
|
||||
use crate::path_normalization::resolve_sandbox_path;
|
||||
use crate::runner_pipe::PIPE_ACCESS_INBOUND;
|
||||
use crate::runner_pipe::PIPE_ACCESS_OUTBOUND;
|
||||
use crate::runner_pipe::connect_pipe;
|
||||
@@ -241,7 +242,8 @@ 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_wide(cwd);
|
||||
let resolved_cwd = resolve_sandbox_path(cwd);
|
||||
let cwd_w = to_wide(&resolved_cwd);
|
||||
let user_w = to_wide(&sandbox_creds.username);
|
||||
let domain_w = to_wide(".");
|
||||
let password_w = to_wide(&sandbox_creds.password);
|
||||
|
||||
@@ -34,6 +34,7 @@ mod windows_impl {
|
||||
use crate::logging::log_failure;
|
||||
use crate::logging::log_start;
|
||||
use crate::logging::log_success;
|
||||
use crate::path_normalization::resolve_sandbox_path;
|
||||
use crate::policy::SandboxPolicy;
|
||||
use crate::policy::parse_policy;
|
||||
use crate::runner_client::spawn_runner_transport;
|
||||
@@ -184,12 +185,14 @@ mod windows_impl {
|
||||
}
|
||||
|
||||
(|| -> Result<CaptureResult> {
|
||||
let resolved_cwd = resolve_sandbox_path(cwd);
|
||||
let resolved_policy_cwd = resolve_sandbox_path(sandbox_policy_cwd);
|
||||
let spawn_request = SpawnRequest {
|
||||
command: command.clone(),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: resolved_cwd,
|
||||
env: env_map.clone(),
|
||||
policy_json_or_preset: policy_json_or_preset.to_string(),
|
||||
sandbox_policy_cwd: sandbox_policy_cwd.to_path_buf(),
|
||||
sandbox_policy_cwd: resolved_policy_cwd,
|
||||
codex_home: sandbox_base.clone(),
|
||||
real_codex_home: codex_home.to_path_buf(),
|
||||
cap_sids,
|
||||
|
||||
209
codex-rs/windows-sandbox-rs/src/legacy_cwd.rs
Normal file
209
codex-rs/windows-sandbox-rs/src/legacy_cwd.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use crate::log_note;
|
||||
use crate::path_normalization::normalize_spawn_cwd;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
use std::os::windows::fs::MetadataExt as _;
|
||||
use std::os::windows::process::CommandExt as _;
|
||||
use std::path::Component;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::Prefix;
|
||||
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
|
||||
fn junction_name_for_path(path: &Path) -> String {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
path.to_string_lossy().hash(&mut hasher);
|
||||
format!("{:x}", hasher.finish())
|
||||
}
|
||||
|
||||
fn junction_root_for_userprofile(userprofile: &str) -> PathBuf {
|
||||
PathBuf::from(userprofile)
|
||||
.join(".codex")
|
||||
.join(".sandbox")
|
||||
.join("cwd")
|
||||
}
|
||||
|
||||
fn drive_letter(path: &Path) -> Option<char> {
|
||||
match path.components().next()? {
|
||||
Component::Prefix(prefix) => match prefix.kind() {
|
||||
Prefix::Disk(drive) | Prefix::VerbatimDisk(drive) => {
|
||||
Some((drive as char).to_ascii_uppercase())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn system_drive_letter(system_drive: Option<&str>) -> Option<char> {
|
||||
drive_letter(Path::new(system_drive?))
|
||||
}
|
||||
|
||||
fn should_materialize_junction(requested_cwd: &Path, system_drive: Option<&str>) -> bool {
|
||||
let Some(requested_drive) = drive_letter(requested_cwd) else {
|
||||
return false;
|
||||
};
|
||||
let Some(system_drive) = system_drive_letter(system_drive) else {
|
||||
return false;
|
||||
};
|
||||
requested_drive != system_drive
|
||||
}
|
||||
|
||||
fn create_cwd_junction(requested_cwd: &Path, log_dir: Option<&Path>) -> Option<PathBuf> {
|
||||
let userprofile = std::env::var("USERPROFILE").ok()?;
|
||||
let junction_root = junction_root_for_userprofile(&userprofile);
|
||||
if let Err(err) = std::fs::create_dir_all(&junction_root) {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: failed to create {}: {err}",
|
||||
junction_root.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
let junction_path = junction_root.join(junction_name_for_path(requested_cwd));
|
||||
if junction_path.exists() {
|
||||
match std::fs::symlink_metadata(&junction_path) {
|
||||
Ok(md) if (md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT) != 0 => {
|
||||
log_note(
|
||||
&format!("junction: reusing existing {}", junction_path.display()),
|
||||
log_dir,
|
||||
);
|
||||
return Some(junction_path);
|
||||
}
|
||||
Ok(_) => {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: existing path is not a reparse point, recreating {}",
|
||||
junction_path.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: failed to stat existing {}: {err}",
|
||||
junction_path.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = std::fs::remove_dir(&junction_path) {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: failed to remove existing {}: {err}",
|
||||
junction_path.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let link = junction_path.to_string_lossy().to_string();
|
||||
let target = requested_cwd.to_string_lossy().to_string();
|
||||
let link_quoted = format!("\"{link}\"");
|
||||
let target_quoted = format!("\"{target}\"");
|
||||
log_note(
|
||||
&format!("junction: creating via cmd /c mklink /J {link_quoted} {target_quoted}"),
|
||||
log_dir,
|
||||
);
|
||||
let output = match std::process::Command::new("cmd")
|
||||
.raw_arg("/c")
|
||||
.raw_arg("mklink")
|
||||
.raw_arg("/J")
|
||||
.raw_arg(&link_quoted)
|
||||
.raw_arg(&target_quoted)
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
log_note(&format!("junction: mklink failed to run: {err}"), log_dir);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if output.status.success() && junction_path.exists() {
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: created {} -> {}",
|
||||
junction_path.display(),
|
||||
requested_cwd.display()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
return Some(junction_path);
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
log_note(
|
||||
&format!(
|
||||
"junction: mklink failed status={:?} stdout={} stderr={}",
|
||||
output.status,
|
||||
stdout.trim(),
|
||||
stderr.trim()
|
||||
),
|
||||
log_dir,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn effective_legacy_spawn_cwd(cwd: &Path, log_dir: Option<&Path>) -> PathBuf {
|
||||
let normalized_cwd = normalize_spawn_cwd(cwd);
|
||||
if should_materialize_junction(
|
||||
&normalized_cwd,
|
||||
std::env::var("SystemDrive").ok().as_deref(),
|
||||
) {
|
||||
create_cwd_junction(&normalized_cwd, log_dir).unwrap_or_else(|| normalized_cwd.clone())
|
||||
} else {
|
||||
normalized_cwd
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::effective_legacy_spawn_cwd;
|
||||
use super::should_materialize_junction;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn skips_system_drive_workspaces() {
|
||||
assert!(!should_materialize_junction(
|
||||
Path::new(r"C:\repo"),
|
||||
Some("C:"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_junction_for_non_system_drive_workspaces() {
|
||||
assert!(should_materialize_junction(
|
||||
Path::new(r"F:\repo"),
|
||||
Some("C:"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skips_unc_paths() {
|
||||
assert!(!should_materialize_junction(
|
||||
Path::new(r"\\server\share\repo"),
|
||||
Some("C:"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaves_system_drive_paths_unchanged() {
|
||||
let cwd = PathBuf::from(r"C:\repo");
|
||||
assert_eq!(effective_legacy_spawn_cwd(&cwd, None), cwd);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ windows_modules!(
|
||||
helper_materialization,
|
||||
hide_users,
|
||||
identity,
|
||||
legacy_cwd,
|
||||
logging,
|
||||
path_normalization,
|
||||
policy,
|
||||
@@ -149,6 +150,10 @@ pub use logging::log_note;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use path_normalization::canonicalize_path;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use path_normalization::normalize_spawn_cwd;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use path_normalization::path_uses_unc_prefix;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use policy::SandboxPolicy;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use policy::parse_policy;
|
||||
@@ -249,6 +254,7 @@ mod windows_impl {
|
||||
use super::allow::compute_allow_paths;
|
||||
use super::cap::load_or_create_cap_sids;
|
||||
use super::cap::workspace_cap_sid_for_cwd;
|
||||
use super::legacy_cwd::effective_legacy_spawn_cwd;
|
||||
use super::logging::log_failure;
|
||||
use super::logging::log_success;
|
||||
use super::path_normalization::canonicalize_path;
|
||||
@@ -457,11 +463,12 @@ mod windows_impl {
|
||||
}
|
||||
let (stdin_pair, stdout_pair, stderr_pair) = unsafe { setup_stdio_pipes()? };
|
||||
let ((in_r, in_w), (out_r, out_w), (err_r, err_w)) = (stdin_pair, stdout_pair, stderr_pair);
|
||||
let effective_cwd = effective_legacy_spawn_cwd(cwd, logs_base_dir);
|
||||
let spawn_res = unsafe {
|
||||
create_process_as_user(
|
||||
h_token,
|
||||
&command,
|
||||
cwd,
|
||||
&effective_cwd,
|
||||
&env_map,
|
||||
logs_base_dir,
|
||||
Some((in_r, out_w, err_w)),
|
||||
|
||||
@@ -1,8 +1,35 @@
|
||||
use crate::winutil::to_wide;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::path::Prefix;
|
||||
use windows_sys::Win32::Foundation::ERROR_MORE_DATA;
|
||||
use windows_sys::Win32::Foundation::NO_ERROR;
|
||||
use windows_sys::Win32::NetworkManagement::WNet::WNetGetConnectionW;
|
||||
|
||||
pub fn canonicalize_path(path: &Path) -> PathBuf {
|
||||
dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
|
||||
let mapped_path = resolve_sandbox_path(path);
|
||||
dunce::canonicalize(&mapped_path).unwrap_or(mapped_path)
|
||||
}
|
||||
|
||||
pub fn normalize_spawn_cwd(path: &Path) -> PathBuf {
|
||||
let simplified = dunce::simplified(path).to_path_buf();
|
||||
if path_uses_unc_prefix(&simplified) {
|
||||
return simplified;
|
||||
}
|
||||
|
||||
let canonical = dunce::canonicalize(path).ok();
|
||||
let canonical = canonical
|
||||
.as_deref()
|
||||
.map(dunce::simplified)
|
||||
.map(Path::to_path_buf);
|
||||
if let Some(canonical) = canonical
|
||||
&& path_uses_unc_prefix(&canonical)
|
||||
{
|
||||
return canonical;
|
||||
}
|
||||
|
||||
simplified
|
||||
}
|
||||
|
||||
pub fn canonical_path_key(path: &Path) -> String {
|
||||
@@ -12,17 +39,136 @@ pub fn canonical_path_key(path: &Path) -> String {
|
||||
.to_ascii_lowercase()
|
||||
}
|
||||
|
||||
pub fn path_uses_unc_prefix(path: &Path) -> bool {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
matches!(
|
||||
path.components().next(),
|
||||
Some(std::path::Component::Prefix(prefix))
|
||||
if matches!(prefix.kind(), Prefix::UNC(..) | Prefix::VerbatimUNC(..))
|
||||
)
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = path;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_sandbox_path(path: &Path) -> PathBuf {
|
||||
resolve_mapped_drive_path(path).unwrap_or_else(|| path.to_path_buf())
|
||||
}
|
||||
|
||||
fn resolve_mapped_drive_path(path: &Path) -> Option<PathBuf> {
|
||||
let (drive, suffix) = split_mapped_drive_path(path)?;
|
||||
let drive_w = to_wide(drive);
|
||||
let mut remote_len = 0u32;
|
||||
let mut status =
|
||||
unsafe { WNetGetConnectionW(drive_w.as_ptr(), std::ptr::null_mut(), &mut remote_len) };
|
||||
if status != ERROR_MORE_DATA && status != NO_ERROR {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut remote_buf = vec![0u16; remote_len as usize + 1];
|
||||
status =
|
||||
unsafe { WNetGetConnectionW(drive_w.as_ptr(), remote_buf.as_mut_ptr(), &mut remote_len) };
|
||||
if status != NO_ERROR {
|
||||
return None;
|
||||
}
|
||||
|
||||
let remote_end = remote_buf
|
||||
.iter()
|
||||
.position(|ch| *ch == 0)
|
||||
.unwrap_or(remote_buf.len());
|
||||
let remote = String::from_utf16_lossy(&remote_buf[..remote_end]);
|
||||
if remote.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut resolved = PathBuf::from(remote);
|
||||
if let Some(suffix) = suffix {
|
||||
resolved.push(suffix);
|
||||
}
|
||||
Some(resolved)
|
||||
}
|
||||
|
||||
fn split_mapped_drive_path(path: &Path) -> Option<(&str, Option<&str>)> {
|
||||
let raw = path.to_str()?;
|
||||
let bytes = raw.as_bytes();
|
||||
if bytes.len() < 2 || !bytes[0].is_ascii_alphabetic() || bytes[1] != b':' {
|
||||
return None;
|
||||
}
|
||||
if bytes.len() > 2 && bytes[2] != b'\\' && bytes[2] != b'/' {
|
||||
return None;
|
||||
}
|
||||
|
||||
let suffix = if bytes.len() > 3 {
|
||||
Some(&raw[3..])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some((&raw[..2], suffix))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::canonical_path_key;
|
||||
use super::normalize_spawn_cwd;
|
||||
use super::path_uses_unc_prefix;
|
||||
use super::split_mapped_drive_path;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn canonical_path_key_normalizes_case_and_separators() {
|
||||
let windows_style = Path::new(r"C:\Users\Dev\Repo");
|
||||
let slash_style = Path::new("c:/users/dev/repo");
|
||||
|
||||
assert_eq!(canonical_path_key(windows_style), canonical_path_key(slash_style));
|
||||
assert_eq!(
|
||||
canonical_path_key(windows_style),
|
||||
canonical_path_key(slash_style)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn path_uses_unc_prefix_matches_standard_and_verbatim_unc_paths() {
|
||||
assert!(path_uses_unc_prefix(Path::new(r"\\server\share\repo")));
|
||||
assert!(path_uses_unc_prefix(Path::new(
|
||||
r"\\?\UNC\server\share\repo"
|
||||
)));
|
||||
assert!(!path_uses_unc_prefix(Path::new(r"C:\repo")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_spawn_cwd_preserves_regular_local_paths() {
|
||||
let path = PathBuf::from(r"C:\repo");
|
||||
|
||||
assert_eq!(normalize_spawn_cwd(&path), path);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn normalize_spawn_cwd_simplifies_verbatim_unc_paths() {
|
||||
let path = PathBuf::from(r"\\?\UNC\server\share\repo");
|
||||
|
||||
assert_eq!(
|
||||
normalize_spawn_cwd(&path),
|
||||
PathBuf::from(r"\\server\share\repo")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_mapped_drive_path_keeps_drive_relative_paths_unchanged() {
|
||||
assert_eq!(split_mapped_drive_path(Path::new(r"L:repo")), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_mapped_drive_path_extracts_drive_root_suffix() {
|
||||
assert_eq!(
|
||||
split_mapped_drive_path(Path::new(r"L:\cs-web\context")),
|
||||
Some(("L:", Some("cs-web\\context")))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
use crate::desktop::LaunchDesktop;
|
||||
use crate::logging;
|
||||
use crate::path_normalization::normalize_spawn_cwd;
|
||||
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 anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
use windows_sys::Win32::Foundation::SetHandleInformation;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT;
|
||||
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
|
||||
use windows_sys::Win32::Foundation::SetHandleInformation;
|
||||
use windows_sys::Win32::Storage::FileSystem::ReadFile;
|
||||
use windows_sys::Win32::System::Console::GetStdHandle;
|
||||
use windows_sys::Win32::System::Console::STD_ERROR_HANDLE;
|
||||
use windows_sys::Win32::System::Console::STD_INPUT_HANDLE;
|
||||
use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE;
|
||||
use windows_sys::Win32::System::Pipes::CreatePipe;
|
||||
use windows_sys::Win32::System::Threading::CreateProcessAsUserW;
|
||||
use windows_sys::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT;
|
||||
use windows_sys::Win32::System::Threading::CreateProcessAsUserW;
|
||||
use windows_sys::Win32::System::Threading::EXTENDED_STARTUPINFO_PRESENT;
|
||||
use windows_sys::Win32::System::Threading::PROCESS_INFORMATION;
|
||||
use windows_sys::Win32::System::Threading::STARTF_USESTDHANDLES;
|
||||
@@ -89,7 +90,8 @@ pub 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_wide(cwd);
|
||||
let spawn_cwd = normalize_spawn_cwd(cwd);
|
||||
let cwd_wide = to_wide(&spawn_cwd);
|
||||
let env_block_len = env_block.len();
|
||||
match stdio {
|
||||
Some((stdin_h, stdout_h, stderr_h)) => {
|
||||
@@ -139,7 +141,7 @@ pub unsafe fn create_process_as_user(
|
||||
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
err,
|
||||
format_last_error(err),
|
||||
cwd.display(),
|
||||
spawn_cwd.display(),
|
||||
cmdline_str,
|
||||
env_block_len,
|
||||
si.StartupInfo.dwFlags,
|
||||
@@ -180,7 +182,7 @@ pub unsafe fn create_process_as_user(
|
||||
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
err,
|
||||
format_last_error(err),
|
||||
cwd.display(),
|
||||
spawn_cwd.display(),
|
||||
cmdline_str,
|
||||
env_block_len,
|
||||
si.dwFlags,
|
||||
|
||||
@@ -15,6 +15,8 @@ use crate::allow::compute_allow_paths;
|
||||
use crate::helper_materialization::helper_bin_dir;
|
||||
use crate::logging::log_note;
|
||||
use crate::path_normalization::canonical_path_key;
|
||||
use crate::path_normalization::canonicalize_path;
|
||||
use crate::path_normalization::resolve_sandbox_path;
|
||||
use crate::policy::SandboxPolicy;
|
||||
use crate::setup_error::SetupErrorCode;
|
||||
use crate::setup_error::SetupFailure;
|
||||
@@ -177,7 +179,7 @@ fn run_setup_refresh_inner(
|
||||
offline_username: OFFLINE_USERNAME.to_string(),
|
||||
online_username: ONLINE_USERNAME.to_string(),
|
||||
codex_home: request.codex_home.to_path_buf(),
|
||||
command_cwd: request.command_cwd.to_path_buf(),
|
||||
command_cwd: resolve_sandbox_path(request.command_cwd),
|
||||
read_roots,
|
||||
write_roots,
|
||||
deny_write_paths,
|
||||
@@ -322,7 +324,7 @@ fn canonical_existing(paths: &[PathBuf]) -> Vec<PathBuf> {
|
||||
if !p.exists() {
|
||||
return None;
|
||||
}
|
||||
Some(dunce::canonicalize(p).unwrap_or_else(|_| p.clone()))
|
||||
Some(canonicalize_path(p))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -727,7 +729,7 @@ pub fn run_elevated_setup(
|
||||
offline_username: OFFLINE_USERNAME.to_string(),
|
||||
online_username: ONLINE_USERNAME.to_string(),
|
||||
codex_home: request.codex_home.to_path_buf(),
|
||||
command_cwd: request.command_cwd.to_path_buf(),
|
||||
command_cwd: resolve_sandbox_path(request.command_cwd),
|
||||
read_roots,
|
||||
write_roots,
|
||||
deny_write_paths,
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::windows_common::normalize_windows_tty_input;
|
||||
use crate::acl::revoke_ace;
|
||||
use crate::conpty::spawn_conpty_process_as_user;
|
||||
use crate::desktop::LaunchDesktop;
|
||||
use crate::legacy_cwd::effective_legacy_spawn_cwd;
|
||||
use crate::logging::log_failure;
|
||||
use crate::logging::log_success;
|
||||
use crate::process::StderrMode;
|
||||
@@ -66,11 +67,12 @@ fn spawn_legacy_process(
|
||||
writer_rx: mpsc::Receiver<Vec<u8>>,
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<LegacyProcessHandles> {
|
||||
let effective_cwd = effective_legacy_spawn_cwd(cwd, logs_base_dir);
|
||||
let (pi, output_join, writer_handle, hpc, desktop) = if tty {
|
||||
let (pi, conpty) = spawn_conpty_process_as_user(
|
||||
h_token,
|
||||
command,
|
||||
cwd,
|
||||
&effective_cwd,
|
||||
env_map,
|
||||
use_private_desktop,
|
||||
logs_base_dir,
|
||||
@@ -87,7 +89,7 @@ fn spawn_legacy_process(
|
||||
let pipe_handles = spawn_process_with_pipes(
|
||||
h_token,
|
||||
command,
|
||||
cwd,
|
||||
&effective_cwd,
|
||||
env_map,
|
||||
if stdin_open {
|
||||
StdinMode::Open
|
||||
|
||||
Reference in New Issue
Block a user