mirror of
https://github.com/openai/codex.git
synced 2026-11-09 20:50:33 +00:00
exec-server: sandbox filesystem helper ops
This commit is contained in:
@@ -9,10 +9,27 @@ struct ExecServerArgs {
|
||||
default_value = codex_exec_server::DEFAULT_LISTEN_URL
|
||||
)]
|
||||
listen: String,
|
||||
// Hidden entrypoint used by LocalFileSystem to re-run the same fs logic
|
||||
// in a one-shot helper process after sandbox transformation.
|
||||
#[arg(long = "internal-fs-op", hide = true)]
|
||||
internal_fs_op: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let args = ExecServerArgs::parse();
|
||||
codex_exec_server::run_main_with_listen_url(&args.listen).await
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
runtime.block_on(async move {
|
||||
let args = ExecServerArgs::parse();
|
||||
if args.internal_fs_op {
|
||||
// The helper mode reads a single structured fs request from stdin,
|
||||
// executes it, writes the response to stdout, and exits.
|
||||
codex_exec_server::run_internal_fs_op().await?;
|
||||
} else {
|
||||
codex_exec_server::run_main_with_listen_url(&args.listen)
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!(err.to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokio::io;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -18,7 +21,7 @@ pub struct CopyOptions {
|
||||
pub recursive: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FileMetadata {
|
||||
pub is_directory: bool,
|
||||
pub is_file: bool,
|
||||
@@ -26,7 +29,7 @@ pub struct FileMetadata {
|
||||
pub modified_at_ms: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ReadDirectoryEntry {
|
||||
pub file_name: String,
|
||||
pub is_directory: bool,
|
||||
@@ -39,27 +42,88 @@ pub type FileSystemResult<T> = io::Result<T>;
|
||||
pub trait ExecutorFileSystem: Send + Sync {
|
||||
async fn read_file(&self, path: &AbsolutePathBuf) -> FileSystemResult<Vec<u8>>;
|
||||
|
||||
async fn read_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<u8>> {
|
||||
self.read_file(path).await
|
||||
}
|
||||
|
||||
async fn write_file(&self, path: &AbsolutePathBuf, contents: Vec<u8>) -> FileSystemResult<()>;
|
||||
|
||||
async fn write_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
contents: Vec<u8>,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
self.write_file(path, contents).await
|
||||
}
|
||||
|
||||
async fn create_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
) -> FileSystemResult<()>;
|
||||
|
||||
async fn create_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
self.create_directory(path, options).await
|
||||
}
|
||||
|
||||
async fn get_metadata(&self, path: &AbsolutePathBuf) -> FileSystemResult<FileMetadata>;
|
||||
|
||||
async fn get_metadata_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<FileMetadata> {
|
||||
self.get_metadata(path).await
|
||||
}
|
||||
|
||||
async fn read_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>>;
|
||||
|
||||
async fn read_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>> {
|
||||
self.read_directory(path).await
|
||||
}
|
||||
|
||||
async fn remove(&self, path: &AbsolutePathBuf, options: RemoveOptions) -> FileSystemResult<()>;
|
||||
|
||||
async fn remove_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: RemoveOptions,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
self.remove(path, options).await
|
||||
}
|
||||
|
||||
async fn copy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
) -> FileSystemResult<()>;
|
||||
|
||||
async fn copy_with_sandbox_policy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
_sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
self.copy(source_path, destination_path, options).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ mod protocol;
|
||||
mod remote_file_system;
|
||||
mod remote_process;
|
||||
mod rpc;
|
||||
mod sandboxed_file_system;
|
||||
mod server;
|
||||
|
||||
pub use client::ExecServerClient;
|
||||
@@ -61,6 +62,7 @@ pub use protocol::TerminateResponse;
|
||||
pub use protocol::WriteParams;
|
||||
pub use protocol::WriteResponse;
|
||||
pub use protocol::WriteStatus;
|
||||
pub use sandboxed_file_system::run_internal_fs_op;
|
||||
pub use server::DEFAULT_LISTEN_URL;
|
||||
pub use server::ExecServerListenUrlParseError;
|
||||
pub use server::run_main;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::path::Component;
|
||||
use std::path::Path;
|
||||
@@ -14,6 +15,7 @@ use crate::FileMetadata;
|
||||
use crate::FileSystemResult;
|
||||
use crate::ReadDirectoryEntry;
|
||||
use crate::RemoveOptions;
|
||||
use crate::sandboxed_file_system::SandboxedFileSystem;
|
||||
|
||||
const MAX_READ_FILE_BYTES: u64 = 512 * 1024 * 1024;
|
||||
|
||||
@@ -33,10 +35,37 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
tokio::fs::read(path.as_path()).await
|
||||
}
|
||||
|
||||
async fn read_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<u8>> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.read_file(path)
|
||||
.await;
|
||||
}
|
||||
self.read_file(path).await
|
||||
}
|
||||
|
||||
async fn write_file(&self, path: &AbsolutePathBuf, contents: Vec<u8>) -> FileSystemResult<()> {
|
||||
tokio::fs::write(path.as_path(), contents).await
|
||||
}
|
||||
|
||||
async fn write_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
contents: Vec<u8>,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.write_file(path, contents)
|
||||
.await;
|
||||
}
|
||||
self.write_file(path, contents).await
|
||||
}
|
||||
|
||||
async fn create_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
@@ -50,6 +79,20 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.create_directory(path, options)
|
||||
.await;
|
||||
}
|
||||
self.create_directory(path, options).await
|
||||
}
|
||||
|
||||
async fn get_metadata(&self, path: &AbsolutePathBuf) -> FileSystemResult<FileMetadata> {
|
||||
let metadata = tokio::fs::metadata(path.as_path()).await?;
|
||||
Ok(FileMetadata {
|
||||
@@ -60,6 +103,19 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_metadata_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<FileMetadata> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.get_metadata(path)
|
||||
.await;
|
||||
}
|
||||
self.get_metadata(path).await
|
||||
}
|
||||
|
||||
async fn read_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
@@ -77,6 +133,19 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
async fn read_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.read_directory(path)
|
||||
.await;
|
||||
}
|
||||
self.read_directory(path).await
|
||||
}
|
||||
|
||||
async fn remove(&self, path: &AbsolutePathBuf, options: RemoveOptions) -> FileSystemResult<()> {
|
||||
match tokio::fs::symlink_metadata(path.as_path()).await {
|
||||
Ok(metadata) => {
|
||||
@@ -97,6 +166,20 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: RemoveOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.remove(path, options)
|
||||
.await;
|
||||
}
|
||||
self.remove(path, options).await
|
||||
}
|
||||
|
||||
async fn copy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
@@ -147,6 +230,21 @@ impl ExecutorFileSystem for LocalFileSystem {
|
||||
.await
|
||||
.map_err(|err| io::Error::other(format!("filesystem task failed: {err}")))?
|
||||
}
|
||||
|
||||
async fn copy_with_sandbox_policy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
if let Some(sandbox_policy) = sandbox_policy {
|
||||
return SandboxedFileSystem::new(sandbox_policy.clone())
|
||||
.copy(source_path, destination_path, options)
|
||||
.await;
|
||||
}
|
||||
self.copy(source_path, destination_path, options).await
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_dir_recursive(source: &Path, target: &Path) -> io::Result<()> {
|
||||
|
||||
@@ -8,6 +8,7 @@ use codex_app_server_protocol::FsReadDirectoryParams;
|
||||
use codex_app_server_protocol::FsReadFileParams;
|
||||
use codex_app_server_protocol::FsRemoveParams;
|
||||
use codex_app_server_protocol::FsWriteFileParams;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use tokio::io;
|
||||
use tracing::trace;
|
||||
@@ -39,10 +40,22 @@ impl RemoteFileSystem {
|
||||
#[async_trait]
|
||||
impl ExecutorFileSystem for RemoteFileSystem {
|
||||
async fn read_file(&self, path: &AbsolutePathBuf) -> FileSystemResult<Vec<u8>> {
|
||||
self.read_file_with_sandbox_policy(path, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<u8>> {
|
||||
trace!("remote fs read_file");
|
||||
let response = self
|
||||
.client
|
||||
.fs_read_file(FsReadFileParams { path: path.clone() })
|
||||
.fs_read_file(FsReadFileParams {
|
||||
path: path.clone(),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
STANDARD.decode(response.data_base64).map_err(|err| {
|
||||
@@ -54,11 +67,22 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
}
|
||||
|
||||
async fn write_file(&self, path: &AbsolutePathBuf, contents: Vec<u8>) -> FileSystemResult<()> {
|
||||
self.write_file_with_sandbox_policy(path, contents, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn write_file_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
contents: Vec<u8>,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
trace!("remote fs write_file");
|
||||
self.client
|
||||
.fs_write_file(FsWriteFileParams {
|
||||
path: path.clone(),
|
||||
data_base64: STANDARD.encode(contents),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
@@ -69,12 +93,23 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
) -> FileSystemResult<()> {
|
||||
self.create_directory_with_sandbox_policy(path, options, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
trace!("remote fs create_directory");
|
||||
self.client
|
||||
.fs_create_directory(FsCreateDirectoryParams {
|
||||
path: path.clone(),
|
||||
recursive: Some(options.recursive),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
@@ -82,10 +117,22 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
}
|
||||
|
||||
async fn get_metadata(&self, path: &AbsolutePathBuf) -> FileSystemResult<FileMetadata> {
|
||||
self.get_metadata_with_sandbox_policy(path, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_metadata_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<FileMetadata> {
|
||||
trace!("remote fs get_metadata");
|
||||
let response = self
|
||||
.client
|
||||
.fs_get_metadata(FsGetMetadataParams { path: path.clone() })
|
||||
.fs_get_metadata(FsGetMetadataParams {
|
||||
path: path.clone(),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
Ok(FileMetadata {
|
||||
@@ -99,11 +146,23 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
async fn read_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>> {
|
||||
self.read_directory_with_sandbox_policy(path, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_directory_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>> {
|
||||
trace!("remote fs read_directory");
|
||||
let response = self
|
||||
.client
|
||||
.fs_read_directory(FsReadDirectoryParams { path: path.clone() })
|
||||
.fs_read_directory(FsReadDirectoryParams {
|
||||
path: path.clone(),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
Ok(response
|
||||
@@ -118,12 +177,23 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
}
|
||||
|
||||
async fn remove(&self, path: &AbsolutePathBuf, options: RemoveOptions) -> FileSystemResult<()> {
|
||||
self.remove_with_sandbox_policy(path, options, /*sandbox_policy*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn remove_with_sandbox_policy(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: RemoveOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
trace!("remote fs remove");
|
||||
self.client
|
||||
.fs_remove(FsRemoveParams {
|
||||
path: path.clone(),
|
||||
recursive: Some(options.recursive),
|
||||
force: Some(options.force),
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
@@ -135,6 +205,22 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
) -> FileSystemResult<()> {
|
||||
self.copy_with_sandbox_policy(
|
||||
source_path,
|
||||
destination_path,
|
||||
options,
|
||||
/*sandbox_policy*/ None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn copy_with_sandbox_policy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
sandbox_policy: Option<&FileSystemSandboxPolicy>,
|
||||
) -> FileSystemResult<()> {
|
||||
trace!("remote fs copy");
|
||||
self.client
|
||||
@@ -142,6 +228,7 @@ impl ExecutorFileSystem for RemoteFileSystem {
|
||||
source_path: source_path.clone(),
|
||||
destination_path: destination_path.clone(),
|
||||
recursive: options.recursive,
|
||||
sandbox_policy: sandbox_policy.cloned(),
|
||||
})
|
||||
.await
|
||||
.map_err(map_remote_error)?;
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::NetworkAccess;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_sandboxing::SandboxCommand;
|
||||
use codex_sandboxing::SandboxExecRequest;
|
||||
use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxTransformRequest;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::CopyOptions;
|
||||
use crate::CreateDirectoryOptions;
|
||||
use crate::ExecutorFileSystem;
|
||||
use crate::FileMetadata;
|
||||
use crate::FileSystemResult;
|
||||
use crate::ReadDirectoryEntry;
|
||||
use crate::RemoveOptions;
|
||||
use crate::local_file_system::LocalFileSystem;
|
||||
|
||||
const INTERNAL_FS_OP_FLAG: &str = "--internal-fs-op";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
enum SandboxedFileSystemRequest {
|
||||
ReadFile {
|
||||
path: AbsolutePathBuf,
|
||||
},
|
||||
WriteFile {
|
||||
path: AbsolutePathBuf,
|
||||
data_base64: String,
|
||||
},
|
||||
CreateDirectory {
|
||||
path: AbsolutePathBuf,
|
||||
recursive: bool,
|
||||
},
|
||||
GetMetadata {
|
||||
path: AbsolutePathBuf,
|
||||
},
|
||||
ReadDirectory {
|
||||
path: AbsolutePathBuf,
|
||||
},
|
||||
Remove {
|
||||
path: AbsolutePathBuf,
|
||||
recursive: bool,
|
||||
force: bool,
|
||||
},
|
||||
Copy {
|
||||
source_path: AbsolutePathBuf,
|
||||
destination_path: AbsolutePathBuf,
|
||||
recursive: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
enum SandboxedFileSystemResponse {
|
||||
Unit,
|
||||
ReadFile { data_base64: String },
|
||||
GetMetadata { metadata: FileMetadata },
|
||||
ReadDirectory { entries: Vec<ReadDirectoryEntry> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum SandboxedFileSystemEnvelope {
|
||||
Ok(SandboxedFileSystemResponse),
|
||||
Error(SandboxedFileSystemError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SandboxedFileSystemError {
|
||||
kind: SandboxedFileSystemErrorKind,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum SandboxedFileSystemErrorKind {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
AlreadyExists,
|
||||
InvalidInput,
|
||||
Unsupported,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl SandboxedFileSystemErrorKind {
|
||||
fn from_io_kind(kind: io::ErrorKind) -> Self {
|
||||
match kind {
|
||||
io::ErrorKind::NotFound => Self::NotFound,
|
||||
io::ErrorKind::PermissionDenied => Self::PermissionDenied,
|
||||
io::ErrorKind::AlreadyExists => Self::AlreadyExists,
|
||||
io::ErrorKind::InvalidInput => Self::InvalidInput,
|
||||
io::ErrorKind::Unsupported => Self::Unsupported,
|
||||
_ => Self::Other,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_io_kind(self) -> io::ErrorKind {
|
||||
match self {
|
||||
Self::NotFound => io::ErrorKind::NotFound,
|
||||
Self::PermissionDenied => io::ErrorKind::PermissionDenied,
|
||||
Self::AlreadyExists => io::ErrorKind::AlreadyExists,
|
||||
Self::InvalidInput => io::ErrorKind::InvalidInput,
|
||||
Self::Unsupported => io::ErrorKind::Unsupported,
|
||||
Self::Other => io::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SandboxedFileSystem {
|
||||
sandbox_policy: FileSystemSandboxPolicy,
|
||||
}
|
||||
|
||||
impl SandboxedFileSystem {
|
||||
pub(crate) fn new(sandbox_policy: FileSystemSandboxPolicy) -> Self {
|
||||
Self { sandbox_policy }
|
||||
}
|
||||
|
||||
async fn execute_request_via_sandbox(
|
||||
&self,
|
||||
request: SandboxedFileSystemRequest,
|
||||
) -> FileSystemResult<SandboxedFileSystemResponse> {
|
||||
let prepared = prepare_helper_request(&self.sandbox_policy)?;
|
||||
let request_bytes = serde_json::to_vec(&request).map_err(io::Error::other)?;
|
||||
let (program, args) = prepared.command.split_first().ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::InvalidInput, "helper command is empty")
|
||||
})?;
|
||||
|
||||
let mut command = Command::new(program);
|
||||
command.args(args);
|
||||
command.current_dir(&prepared.cwd);
|
||||
command.envs(&prepared.env);
|
||||
command.stdin(Stdio::piped());
|
||||
command.stdout(Stdio::piped());
|
||||
command.stderr(Stdio::piped());
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Some(arg0) = prepared.arg0.as_ref() {
|
||||
command.arg0(arg0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut child = command.spawn()?;
|
||||
let Some(mut stdin) = child.stdin.take() else {
|
||||
return Err(io::Error::other("sandbox helper stdin was not piped"));
|
||||
};
|
||||
stdin.write_all(&request_bytes).await?;
|
||||
drop(stdin);
|
||||
|
||||
let output = child.wait_with_output().await?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let message = if !stderr.trim().is_empty() {
|
||||
stderr.trim().to_string()
|
||||
} else if !stdout.trim().is_empty() {
|
||||
stdout.trim().to_string()
|
||||
} else {
|
||||
"sandbox helper exited without error output".to_string()
|
||||
};
|
||||
return Err(io::Error::other(message));
|
||||
}
|
||||
|
||||
let envelope: SandboxedFileSystemEnvelope =
|
||||
serde_json::from_slice(&output.stdout).map_err(io::Error::other)?;
|
||||
match envelope {
|
||||
SandboxedFileSystemEnvelope::Ok(response) => Ok(response),
|
||||
SandboxedFileSystemEnvelope::Error(error) => {
|
||||
Err(io::Error::new(error.kind.to_io_kind(), error.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ExecutorFileSystem for SandboxedFileSystem {
|
||||
async fn read_file(&self, path: &AbsolutePathBuf) -> FileSystemResult<Vec<u8>> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::ReadFile {
|
||||
path: path.clone(),
|
||||
})
|
||||
.await?;
|
||||
let SandboxedFileSystemResponse::ReadFile { data_base64 } = response else {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for read_file",
|
||||
));
|
||||
};
|
||||
STANDARD.decode(data_base64).map_err(|err| {
|
||||
io::Error::other(format!("sandbox helper returned invalid base64: {err}"))
|
||||
})
|
||||
}
|
||||
|
||||
async fn write_file(&self, path: &AbsolutePathBuf, contents: Vec<u8>) -> FileSystemResult<()> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::WriteFile {
|
||||
path: path.clone(),
|
||||
data_base64: STANDARD.encode(contents),
|
||||
})
|
||||
.await?;
|
||||
if !matches!(response, SandboxedFileSystemResponse::Unit) {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for write_file",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
options: CreateDirectoryOptions,
|
||||
) -> FileSystemResult<()> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::CreateDirectory {
|
||||
path: path.clone(),
|
||||
recursive: options.recursive,
|
||||
})
|
||||
.await?;
|
||||
if !matches!(response, SandboxedFileSystemResponse::Unit) {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for create_directory",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_metadata(&self, path: &AbsolutePathBuf) -> FileSystemResult<FileMetadata> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::GetMetadata {
|
||||
path: path.clone(),
|
||||
})
|
||||
.await?;
|
||||
let SandboxedFileSystemResponse::GetMetadata { metadata } = response else {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for get_metadata",
|
||||
));
|
||||
};
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
async fn read_directory(
|
||||
&self,
|
||||
path: &AbsolutePathBuf,
|
||||
) -> FileSystemResult<Vec<ReadDirectoryEntry>> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::ReadDirectory {
|
||||
path: path.clone(),
|
||||
})
|
||||
.await?;
|
||||
let SandboxedFileSystemResponse::ReadDirectory { entries } = response else {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for read_directory",
|
||||
));
|
||||
};
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
async fn remove(&self, path: &AbsolutePathBuf, options: RemoveOptions) -> FileSystemResult<()> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::Remove {
|
||||
path: path.clone(),
|
||||
recursive: options.recursive,
|
||||
force: options.force,
|
||||
})
|
||||
.await?;
|
||||
if !matches!(response, SandboxedFileSystemResponse::Unit) {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for remove",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn copy(
|
||||
&self,
|
||||
source_path: &AbsolutePathBuf,
|
||||
destination_path: &AbsolutePathBuf,
|
||||
options: CopyOptions,
|
||||
) -> FileSystemResult<()> {
|
||||
let response = self
|
||||
.execute_request_via_sandbox(SandboxedFileSystemRequest::Copy {
|
||||
source_path: source_path.clone(),
|
||||
destination_path: destination_path.clone(),
|
||||
recursive: options.recursive,
|
||||
})
|
||||
.await?;
|
||||
if !matches!(response, SandboxedFileSystemResponse::Unit) {
|
||||
return Err(io::Error::other(
|
||||
"sandbox helper returned unexpected response for copy",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_internal_fs_op() -> io::Result<()> {
|
||||
let mut input = Vec::new();
|
||||
tokio::io::stdin().read_to_end(&mut input).await?;
|
||||
let request: SandboxedFileSystemRequest =
|
||||
serde_json::from_slice(&input).map_err(io::Error::other)?;
|
||||
let response = match execute_internal_request(request).await {
|
||||
Ok(response) => SandboxedFileSystemEnvelope::Ok(response),
|
||||
Err(err) => SandboxedFileSystemEnvelope::Error(SandboxedFileSystemError {
|
||||
kind: SandboxedFileSystemErrorKind::from_io_kind(err.kind()),
|
||||
message: err.to_string(),
|
||||
}),
|
||||
};
|
||||
let bytes = serde_json::to_vec(&response).map_err(io::Error::other)?;
|
||||
tokio::io::stdout().write_all(&bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_internal_request(
|
||||
request: SandboxedFileSystemRequest,
|
||||
) -> FileSystemResult<SandboxedFileSystemResponse> {
|
||||
let file_system = LocalFileSystem;
|
||||
match request {
|
||||
SandboxedFileSystemRequest::ReadFile { path } => {
|
||||
let bytes = file_system.read_file(&path).await?;
|
||||
Ok(SandboxedFileSystemResponse::ReadFile {
|
||||
data_base64: STANDARD.encode(bytes),
|
||||
})
|
||||
}
|
||||
SandboxedFileSystemRequest::WriteFile { path, data_base64 } => {
|
||||
let bytes = STANDARD.decode(data_base64).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("invalid writeFile base64 payload: {err}"),
|
||||
)
|
||||
})?;
|
||||
file_system.write_file(&path, bytes).await?;
|
||||
Ok(SandboxedFileSystemResponse::Unit)
|
||||
}
|
||||
SandboxedFileSystemRequest::CreateDirectory { path, recursive } => {
|
||||
file_system
|
||||
.create_directory(&path, CreateDirectoryOptions { recursive })
|
||||
.await?;
|
||||
Ok(SandboxedFileSystemResponse::Unit)
|
||||
}
|
||||
SandboxedFileSystemRequest::GetMetadata { path } => {
|
||||
let metadata = file_system.get_metadata(&path).await?;
|
||||
Ok(SandboxedFileSystemResponse::GetMetadata { metadata })
|
||||
}
|
||||
SandboxedFileSystemRequest::ReadDirectory { path } => {
|
||||
let entries = file_system.read_directory(&path).await?;
|
||||
Ok(SandboxedFileSystemResponse::ReadDirectory { entries })
|
||||
}
|
||||
SandboxedFileSystemRequest::Remove {
|
||||
path,
|
||||
recursive,
|
||||
force,
|
||||
} => {
|
||||
file_system
|
||||
.remove(&path, RemoveOptions { recursive, force })
|
||||
.await?;
|
||||
Ok(SandboxedFileSystemResponse::Unit)
|
||||
}
|
||||
SandboxedFileSystemRequest::Copy {
|
||||
source_path,
|
||||
destination_path,
|
||||
recursive,
|
||||
} => {
|
||||
file_system
|
||||
.copy(&source_path, &destination_path, CopyOptions { recursive })
|
||||
.await?;
|
||||
Ok(SandboxedFileSystemResponse::Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_helper_request(
|
||||
sandbox_policy: &FileSystemSandboxPolicy,
|
||||
) -> io::Result<SandboxExecRequest> {
|
||||
let helper_exe = helper_executable_path()?;
|
||||
let codex_linux_sandbox_exe = linux_sandbox_executable_path(&helper_exe);
|
||||
let helper_cwd = std::env::current_dir()?;
|
||||
let network_policy = NetworkSandboxPolicy::Enabled;
|
||||
let helper_readable_roots =
|
||||
helper_readable_roots(&helper_exe, codex_linux_sandbox_exe.as_ref());
|
||||
let effective_file_system_policy = sandbox_policy
|
||||
.clone()
|
||||
.with_additional_readable_roots(helper_cwd.as_path(), &helper_readable_roots);
|
||||
let legacy_policy = helper_legacy_policy(
|
||||
&effective_file_system_policy,
|
||||
network_policy,
|
||||
helper_cwd.as_path(),
|
||||
)?;
|
||||
let manager = SandboxManager::new();
|
||||
let sandbox = manager.select_initial(
|
||||
&effective_file_system_policy,
|
||||
network_policy,
|
||||
codex_sandboxing::SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
/*has_managed_network_requirements*/ false,
|
||||
);
|
||||
let command = SandboxCommand {
|
||||
program: helper_exe.clone().into(),
|
||||
args: vec![INTERNAL_FS_OP_FLAG.to_string()],
|
||||
cwd: helper_cwd.clone(),
|
||||
env: HashMap::new(),
|
||||
additional_permissions: None,
|
||||
};
|
||||
manager
|
||||
.transform(SandboxTransformRequest {
|
||||
command,
|
||||
policy: &legacy_policy,
|
||||
file_system_policy: &effective_file_system_policy,
|
||||
network_policy,
|
||||
sandbox,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: helper_cwd.as_path(),
|
||||
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_ref(),
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
})
|
||||
.map_err(io::Error::other)
|
||||
}
|
||||
|
||||
fn helper_readable_roots(
|
||||
helper_exe: &Path,
|
||||
codex_linux_sandbox_exe: Option<&PathBuf>,
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
let mut roots = Vec::new();
|
||||
|
||||
for path in
|
||||
std::iter::once(helper_exe).chain(codex_linux_sandbox_exe.into_iter().map(PathBuf::as_path))
|
||||
{
|
||||
if let Some(parent) = path.parent()
|
||||
&& let Ok(root) = AbsolutePathBuf::from_absolute_path(parent)
|
||||
&& !roots.iter().any(|existing| existing == &root)
|
||||
{
|
||||
roots.push(root);
|
||||
}
|
||||
}
|
||||
|
||||
roots
|
||||
}
|
||||
|
||||
fn helper_legacy_policy(
|
||||
sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_policy: NetworkSandboxPolicy,
|
||||
helper_cwd: &Path,
|
||||
) -> io::Result<SandboxPolicy> {
|
||||
match sandbox_policy.to_legacy_sandbox_policy(network_policy, helper_cwd) {
|
||||
Ok(policy) => Ok(policy),
|
||||
Err(_) if sandbox_policy.needs_direct_runtime_enforcement(network_policy, helper_cwd) => {
|
||||
Ok(SandboxPolicy::ExternalSandbox {
|
||||
network_access: if network_policy.is_enabled() {
|
||||
NetworkAccess::Enabled
|
||||
} else {
|
||||
NetworkAccess::Restricted
|
||||
},
|
||||
})
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn helper_executable_path() -> io::Result<PathBuf> {
|
||||
if let Some(path) = std::env::var_os("CODEX_EXEC_SERVER_SELF_EXE") {
|
||||
return Ok(PathBuf::from(path));
|
||||
}
|
||||
if let Some(path) = std::env::var_os("CARGO_BIN_EXE_codex-exec-server") {
|
||||
return Ok(PathBuf::from(path));
|
||||
}
|
||||
if let Ok(path) = which::which("codex-exec-server") {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
let current_exe = std::env::current_exe()?;
|
||||
if current_exe.file_name().and_then(|value| value.to_str()) == Some("codex-exec-server") {
|
||||
return Ok(current_exe);
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"could not find codex-exec-server executable for sandboxed filesystem helper",
|
||||
))
|
||||
}
|
||||
|
||||
fn linux_sandbox_executable_path(helper_exe: &Path) -> Option<PathBuf> {
|
||||
if !cfg!(target_os = "linux") {
|
||||
return None;
|
||||
}
|
||||
|
||||
std::env::var_os("CODEX_LINUX_SANDBOX_EXE")
|
||||
.map(PathBuf::from)
|
||||
.or_else(|| std::env::var_os("CARGO_BIN_EXE_codex-linux-sandbox").map(PathBuf::from))
|
||||
.or_else(|| which::which("codex-linux-sandbox").ok())
|
||||
.or_else(|| {
|
||||
helper_exe.parent().and_then(|parent| {
|
||||
let sibling = parent.join("codex-linux-sandbox");
|
||||
sibling.exists().then_some(sibling)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::helper_legacy_policy;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::NetworkAccess;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
#[test]
|
||||
fn helper_legacy_policy_falls_back_to_external_sandbox_for_direct_runtime_enforcement() {
|
||||
let outside_workspace =
|
||||
AbsolutePathBuf::try_from(std::path::PathBuf::from("/outside")).expect("absolute path");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
path: outside_workspace,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
|
||||
let resolved = helper_legacy_policy(
|
||||
&policy,
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
std::path::Path::new("/workspace"),
|
||||
)
|
||||
.expect("direct-enforcement policy should resolve");
|
||||
|
||||
assert_eq!(
|
||||
resolved,
|
||||
SandboxPolicy::ExternalSandbox {
|
||||
network_access: NetworkAccess::Enabled,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ impl FileSystemHandler {
|
||||
) -> Result<FsReadFileResponse, JSONRPCErrorError> {
|
||||
let bytes = self
|
||||
.file_system
|
||||
.read_file(¶ms.path)
|
||||
.read_file_with_sandbox_policy(¶ms.path, params.sandbox_policy.as_ref())
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
Ok(FsReadFileResponse {
|
||||
@@ -57,7 +57,7 @@ impl FileSystemHandler {
|
||||
))
|
||||
})?;
|
||||
self.file_system
|
||||
.write_file(¶ms.path, bytes)
|
||||
.write_file_with_sandbox_policy(¶ms.path, bytes, params.sandbox_policy.as_ref())
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
Ok(FsWriteFileResponse {})
|
||||
@@ -68,11 +68,12 @@ impl FileSystemHandler {
|
||||
params: FsCreateDirectoryParams,
|
||||
) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError> {
|
||||
self.file_system
|
||||
.create_directory(
|
||||
.create_directory_with_sandbox_policy(
|
||||
¶ms.path,
|
||||
CreateDirectoryOptions {
|
||||
recursive: params.recursive.unwrap_or(true),
|
||||
},
|
||||
params.sandbox_policy.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
@@ -85,7 +86,7 @@ impl FileSystemHandler {
|
||||
) -> Result<FsGetMetadataResponse, JSONRPCErrorError> {
|
||||
let metadata = self
|
||||
.file_system
|
||||
.get_metadata(¶ms.path)
|
||||
.get_metadata_with_sandbox_policy(¶ms.path, params.sandbox_policy.as_ref())
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
Ok(FsGetMetadataResponse {
|
||||
@@ -102,7 +103,7 @@ impl FileSystemHandler {
|
||||
) -> Result<FsReadDirectoryResponse, JSONRPCErrorError> {
|
||||
let entries = self
|
||||
.file_system
|
||||
.read_directory(¶ms.path)
|
||||
.read_directory_with_sandbox_policy(¶ms.path, params.sandbox_policy.as_ref())
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
Ok(FsReadDirectoryResponse {
|
||||
@@ -122,12 +123,13 @@ impl FileSystemHandler {
|
||||
params: FsRemoveParams,
|
||||
) -> Result<FsRemoveResponse, JSONRPCErrorError> {
|
||||
self.file_system
|
||||
.remove(
|
||||
.remove_with_sandbox_policy(
|
||||
¶ms.path,
|
||||
RemoveOptions {
|
||||
recursive: params.recursive.unwrap_or(true),
|
||||
force: params.force.unwrap_or(true),
|
||||
},
|
||||
params.sandbox_policy.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
@@ -139,12 +141,13 @@ impl FileSystemHandler {
|
||||
params: FsCopyParams,
|
||||
) -> Result<FsCopyResponse, JSONRPCErrorError> {
|
||||
self.file_system
|
||||
.copy(
|
||||
.copy_with_sandbox_policy(
|
||||
¶ms.source_path,
|
||||
¶ms.destination_path,
|
||||
CopyOptions {
|
||||
recursive: params.recursive,
|
||||
},
|
||||
params.sandbox_policy.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
|
||||
Reference in New Issue
Block a user