Codex Handbook
windows-sandbox-rs/src/deny_read_state.rs 87 lines
use crate::acl::revoke_ace;use crate::deny_read_acl::apply_deny_read_acls;use crate::deny_read_acl::lexical_path_key;use crate::setup::sandbox_dir;use anyhow::Context;use anyhow::Result;use serde::Deserialize;use serde::Serialize;use std::collections::BTreeMap;use std::collections::HashSet;use std::ffi::c_void;use std::path::Path;use std::path::PathBuf;const DENY_READ_ACL_STATE_FILE: &str = "deny_read_acl_state.json";#[derive(Default, Deserialize, Serialize)]struct PersistentDenyReadAclState {    principals: BTreeMap<String, Vec<PathBuf>>,}/// Reconciles the persistent deny-read ACEs owned by one sandbox principal.////// Workspace-write and elevated sandbox sessions intentionally leave ACLs in/// place after a command exits, because descendants may outlive the launcher./// That makes the ACL set stateful across runs. Persist the paths applied for/// each SID, apply the new desired set first, and only then revoke stale paths/// from the same SID so profile changes do not leave old deny-read ACEs behind.////// # Safety/// Caller must pass a valid SID pointer matching `principal_sid`.pub unsafe fn sync_persistent_deny_read_acls(    codex_home: &Path,    principal_sid: &str,    desired_paths: &[PathBuf],    psid: *mut c_void,) -> Result<Vec<PathBuf>> {    let state_path = sandbox_dir(codex_home).join(DENY_READ_ACL_STATE_FILE);    let mut state = load_state(&state_path)?;    let previous_paths = state        .principals        .get(principal_sid)        .cloned()        .unwrap_or_default();    let applied_paths = unsafe { apply_deny_read_acls(desired_paths, psid) }?;    let desired_keys = applied_paths        .iter()        .map(|path| lexical_path_key(path))        .collect::<HashSet<_>>();    for path in previous_paths {        if !desired_keys.contains(&lexical_path_key(&path)) {            revoke_ace(&path, psid);        }    }    if applied_paths.is_empty() {        state.principals.remove(principal_sid);    } else {        state            .principals            .insert(principal_sid.to_string(), applied_paths.clone());    }    store_state(&state_path, &state)?;    Ok(applied_paths)}fn load_state(path: &Path) -> Result<PersistentDenyReadAclState> {    match std::fs::read(path) {        Ok(bytes) => serde_json::from_slice(&bytes)            .with_context(|| format!("parse deny-read ACL state {}", path.display())),        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {            Ok(PersistentDenyReadAclState::default())        }        Err(err) => {            Err(err).with_context(|| format!("read deny-read ACL state {}", path.display()))        }    }}fn store_state(path: &Path, state: &PersistentDenyReadAclState) -> Result<()> {    let bytes = serde_json::to_vec_pretty(state).context("serialize deny-read ACL state")?;    std::fs::write(path, bytes)        .with_context(|| format!("write deny-read ACL state {}", path.display()))}