Codex 系统手册

Exec-server 文件系统沙箱服务

stage-14.2.56 个文件

这一阶段是 exec-server 的“文件管家”,属于幕后支撑部分。它让执行任务读写文件时,不是随便碰硬盘,而是走统一通道。file_read 负责把大文件一段段读,并用编号管理。fs_helper 规定读、写、建目录、删除这些请求怎么说、怎么做。fs_sandbox 把操作关进沙盒这个隔离房间。sandboxed_file_system 戴着安全手套转交给助手。local_file_system 决定本机操作走直连还是沙盒。remote_file_system 则把同一套操作发到远端服务器执行。

本阶段的文件6

文件读取服务

管理 exec-server RPC 接口,用于打开文件读取句柄,并通过这些句柄提供有界随机访问读取。

exec-server/src/file_read.rs源码 ↗
io_transportrequest handling / teardown

这个文件解决的是远程读文件时很实际的问题:文件可能很大,不能一次全读;同一个连接也可能同时读多个文件,所以需要一本“登记簿”记住哪些文件还开着。FileReadHandleManager 就像前台保管柜,用 handle_id 这个编号保存已打开的文件。open 会登记新文件,并限制最多同时打开 128 个,防止资源被耗光。read_block 会按“从哪里开始、读多少字节”去读一小块,并检查长度不能太大;真正读磁盘的动作会放到后台阻塞任务里做,避免卡住异步服务器。读失败时,它会自动关掉这个编号,避免留下坏状态。close 和 close_all 则负责单个关闭和全部清理。底层还区分 Unix 和 Windows,因为两个系统按位置读取文件的系统接口名字不一样,但对上层来说用法是一样的。

函数细节8
FileReadHandleManager::open23–44 ↗
async fn open(
        &self,
        handle_id: String,
        file: tokio::fs::File,
    ) -> io::Result<String>

作用:把一个已经打开的文件登记到管理器里,并给它绑定一个 handle_id,也就是之后继续读取用的“取件号”。它还会拒绝重复编号和过多同时打开的文件,防止混乱和资源耗尽。

数据流:进去的是一个字符串编号 handle_id 和一个 Tokio 文件对象;函数先把异步文件转成标准文件,再拿到内部那张“编号到文件”的表。它检查编号是否已存在、当前打开数量是否超过 128;通过检查后把文件存进去。出来的是同一个 handle_id,表示登记成功;如果不合规,就返回错误,表不会被改坏。

调用关系:它通常由外层的 open 请求处理流程调用,也就是客户端说“我要开始读这个文件”时用到。它不读文件内容,只负责把文件放进后续 FileReadHandleManager::read_block 能找到的位置。

调用图:被 1 处调用(open);外部调用 4 个(new, into_std, new, format!)。

FileReadHandleManager::read_block46–71 ↗
async fn read_block(
        &self,
        handle_id: &str,
        offset: u64,
        len: usize,
    ) -> io::Result<FileReadBlock>

作用:按指定编号,从文件的指定位置读取一小段内容。它是客户端反复拉取大文件内容时最核心的一步。

数据流:进去的是 handle_id、起始位置 offset、想读的长度 len;它先调用 validate_read_block_len 检查长度是否合法,再从登记表里找对应文件。找到后,它把真正的磁盘读取交给后台任务执行,后台会调用 read_block_at。出来的是 FileReadBlock,里面有读到的字节和是否到文件末尾的标记;如果读取出错,它会顺手调用 close 关掉这个 handle,避免以后继续用坏编号。

调用关系:它由外层 read_block 请求处理流程调用,处在“客户端要下一块文件内容”的位置。它自己负责检查、找文件和安排后台读取;具体怎么按位置读文件交给 read_block_at,出错后的清理由 FileReadHandleManager::close 完成。

调用图:调用 2 个内部函数(close, validate_read_block_len);被 1 处调用(read_block);外部调用 3 个(other, format!, spawn_blocking)。

FileReadHandleManager::close73–75 ↗
async fn close(&self, handle_id: &str)

作用:关闭一个文件读取编号,也就是把这个编号从管理器的登记表里删掉。客户端读完了,或者读文件时出错了,都会需要它。

数据流:进去的是 handle_id;函数锁住内部登记表,然后删除这个编号对应的文件。它没有返回内容;执行后,这个编号就不能再用来读取文件了。

调用关系:它会被外层 close 请求处理流程调用,也会被 FileReadHandleManager::read_block 在读取失败时自动调用。它是单个读取会话的收尾动作。

调用图:被 2 处调用(read_block, close)。

FileReadHandleManager::close_all77–79 ↗
async fn close_all(&self)

作用:一次性关闭当前连接里所有还登记着的文件读取任务。连接结束或服务器清理时,用它防止文件句柄泄漏。

数据流:它不需要具体编号;函数锁住内部登记表,然后把整张表清空。执行后,所有之前登记的文件都不再可读。

调用关系:它由 shutdown 这类关闭流程调用,属于“大扫除”。平时读文件不会用它,只有连接或服务要收尾时才会把所有未关闭的读取任务一起清掉。

调用图:被 1 处调用(shutdown)。

read_block_at82–101 ↗
fn read_block_at(file: &File, offset: u64, len: usize) -> io::Result<FileReadBlock>

作用:从一个文件的指定位置开始,尽量读满指定长度,并告诉调用者是否已经碰到文件末尾。它是真正拼出“一块文件内容”的地方。

数据流:进去的是文件对象、起始 offset 和长度 len;函数先准备一块指定大小的空缓冲区,然后循环读取,直到读够、遇到文件末尾,或发生错误。每次读取前都会检查偏移量加法有没有溢出,防止位置算错。出来的是 FileReadBlock:bytes 是实际读到的内容,eof 表示是不是没读满就到末尾了。

调用关系:它被 FileReadHandleManager::read_block 放到后台任务里调用,因为磁盘读取可能阻塞。它不管 handle_id,也不管登记表,只专心调用 read_file_at 从指定位置读文件。

调用图:调用 1 个内部函数(read_file_at);外部调用 1 个(vec!)。

read_file_at109–111 ↗
fn read_file_at(file: &File, bytes: &mut [u8], offset: u64) -> io::Result<usize>

作用:用当前操作系统提供的办法,从文件的某个固定位置读取字节。它把 Unix 和 Windows 的差异藏起来,让上层不用关心系统不同。

数据流:进去的是文件、要填充的字节缓冲区、读取起点 offset;在 Unix 上调用 read_at,在 Windows 上调用 seek_read。出来的是本次实际读到的字节数,或者一个读取错误。

调用关系:它只被 read_block_at 调用,是最靠近操作系统的一层小适配器。read_block_at 负责循环和判断文件末尾,read_file_at 只负责“这一次从这个位置读一下”。

调用图:被 1 处调用(read_block_at);外部调用 2 个(read_at, seek_read)。

validate_read_block_len113–121 ↗
fn validate_read_block_len(len: usize) -> io::Result<()>

作用:检查一次读取的长度是否合理:不能是 0,也不能超过系统允许的最大块大小。这样可以避免无意义请求,也防止一次读太多拖垮内存或网络。

数据流:进去的是 len;函数判断它是否落在 1 到 FILE_READ_CHUNK_SIZE 之间。合法就返回成功;不合法就返回一个带说明的输入错误,不会继续读文件。

调用关系:它被 FileReadHandleManager::read_block 在真正读取前调用,相当于入口处的尺子。只有通过这道检查,后面的后台读取和 read_block_at 才会发生。

调用图:被 1 处调用(read_block);外部调用 2 个(new, format!)。

unknown_handle_error123–128 ↗
fn unknown_handle_error(handle_id: &str) -> io::Error

作用:生成一个“找不到这个文件读取编号”的错误。这样调用者能清楚知道,不是磁盘坏了,而是给的 handle_id 不存在或已经关闭了。

数据流:进去的是 handle_id;函数把它放进一条错误信息里,并创建一个 NotFound 类型的输入输出错误。出来的是这个错误对象,供上层返回给请求方。

调用关系:它在 FileReadHandleManager::read_block 找不到编号时使用,是错误提示的小工具。它不参与正常读文件流程,只在客户端拿了未知或过期编号来读时出现。

调用图:外部调用 2 个(new, format!)。

文件系统辅助协议

定义辅助进程的请求/响应协议,以及作为沙盒化文件系统工作通用操作层的直接执行器。

exec-server/src/fs_helper.rs源码 ↗
io_transportrequest handling

可以把这个文件想成一个“文件操作柜台”。外面的人递进来一张单子,比如“读这个文件”或“复制这个目录”;这里先看单子是哪种操作,再交给本地文件系统去做,最后把结果装成统一格式还回去。文件内容在传输时会用 base64(一种把二进制内容变成普通文本的编码)包装,避免 JSON 这类文本格式装不下原始字节。它还会把底层磁盘错误翻译成 JSON-RPC 错误(JSON-RPC 是一种用 JSON 发请求和收回应的规约),比如文件不存在就说 not found,权限不对就说 invalid request。另一个重要点是,每种回应都能被“验货”:调用方期待读文件结果,就不能误拿到删文件结果;拿错时会生成清楚的内部错误。

函数细节13
FsHelperPayload::operation94–105 ↗
fn operation(&self) -> &'static str

作用:这个函数把一个具体的文件系统回应,翻译成它对应的操作名字。比如读文件回应会变成“fs/readFile”这个方法名。

数据流:进去的是一个 FsHelperPayload,也就是某次文件操作的回应包装 → 它检查里面到底是哪一种回应 → 出来的是一个固定的操作名称字符串,不改动任何数据。

调用关系:它主要给各个 expect_* 函数当“标签读取器”用。当调用方拿到的回应类型不符合预期时,这个函数帮助错误信息说清楚:原本期待什么,实际拿到了什么。

FsHelperPayload::expect_read_file107–112 ↗
fn expect_read_file(self) -> Result<FsReadFileResponse, JSONRPCErrorError>

作用:这个函数用于确认“我手里的回应确实是读文件的结果”。如果是,就取出读到的文件内容回应;如果不是,就报一个清楚的错误。

数据流:进去的是一个通用回应包装 → 它判断里面是不是 ReadFile → 是的话输出 FsReadFileResponse;不是的话调用 unexpected_response 生成 JSON-RPC 错误。

调用关系:它通常被发起读文件请求的一方在收到回应后使用。它不自己修复错配,而是把错配交给 unexpected_response 统一生成错误说明。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_write_file114–119 ↗
fn expect_write_file(self) -> Result<FsWriteFileResponse, JSONRPCErrorError>

作用:这个函数用于确认回应是不是“写文件成功”的结果。这样调用方不会把别的操作结果误当成写文件结果。

数据流:进去的是一个通用回应包装 → 它检查是否为 WriteFile → 如果匹配就输出 FsWriteFileResponse;如果不匹配,就用 unexpected_response 做成错误返回。

调用关系:它在写文件流程的收尾处使用,负责验收回应类型。遇到类型不对时,它把错误组织工作交给 unexpected_response。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_create_directory121–131 ↗
fn expect_create_directory(
        self,
    ) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“创建目录”的结果。它保证调用方拿到的是自己刚才请求的那类结果。

数据流:进去的是通用回应 → 它检查是否为 CreateDirectory → 匹配则输出 FsCreateDirectoryResponse;不匹配则调用 unexpected_response,告诉调用方回应种类不对。

调用关系:它服务于创建目录请求之后的验收步骤。它依赖 unexpected_response 来统一处理“答非所问”的情况。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_get_metadata133–141 ↗
fn expect_get_metadata(self) -> Result<FsGetMetadataResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“获取文件信息”的结果。文件信息包括是不是目录、是不是文件、大小、时间等。

数据流:进去的是一个通用回应包装 → 它判断里面是否为 GetMetadata → 是就取出 FsGetMetadataResponse;否则生成 unexpected_response 错误。

调用关系:它用在查询文件元数据之后。它本身只负责类型验收,错误文案由 unexpected_response 统一生成。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_canonicalize143–151 ↗
fn expect_canonicalize(self) -> Result<FsCanonicalizeResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“规范化路径”的结果。规范化路径就是把路径整理成系统认可的标准形式。

数据流:进去的是通用回应 → 它检查是否为 Canonicalize → 匹配时输出 FsCanonicalizeResponse;不匹配时调用 unexpected_response 返回错误。

调用关系:它在路径规范化请求完成后使用。若回应不是路径规范化结果,它会通过 unexpected_response 把错误讲明白。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_read_directory153–163 ↗
fn expect_read_directory(
        self,
    ) -> Result<FsReadDirectoryResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“读取目录列表”的结果。它让调用方安全地拿到目录里的文件和子目录清单。

数据流:进去的是通用回应包装 → 它判断是否为 ReadDirectory → 是则输出 FsReadDirectoryResponse;否则调用 unexpected_response 生成错误。

调用关系:它用于读取目录流程的回应检查。它把所有类型不匹配的错误都交给 unexpected_response,保持错误格式一致。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_remove165–170 ↗
fn expect_remove(self) -> Result<FsRemoveResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“删除文件或目录”的结果。它避免调用方把别的操作成功误认为删除成功。

数据流:进去的是一个通用回应 → 它检查是否为 Remove → 如果是,输出 FsRemoveResponse;如果不是,调用 unexpected_response 返回错误。

调用关系:它位于删除操作完成后的验收环节。类型不对时,它会把实际操作名交给 unexpected_response 做错误说明。

调用图:调用 1 个内部函数(unexpected_response)。

FsHelperPayload::expect_copy172–177 ↗
fn expect_copy(self) -> Result<FsCopyResponse, JSONRPCErrorError>

作用:这个函数确认回应是不是“复制文件或目录”的结果。它让复制流程的调用方只接收正确类型的回应。

数据流:进去的是通用回应包装 → 它检查是否为 Copy → 匹配则输出 FsCopyResponse;不匹配则调用 unexpected_response 生成错误。

调用关系:它在复制请求之后被使用。它和其他 expect_* 函数一样,把“回应类型不对”的处理交给 unexpected_response。

调用图:调用 1 个内部函数(unexpected_response)。

unexpected_response180–184 ↗
fn unexpected_response(expected: &str, actual: &str) -> JSONRPCErrorError

作用:这个函数专门生成“收到的回应类型不对”的错误信息。它让所有类似错误的说法保持一致,方便排查问题。

数据流:进去的是期待的操作名和实际收到的操作名 → 它把二者拼成一句错误说明 → 出来的是一个 JSONRPCErrorError,类型是内部错误。

调用关系:它被所有 expect_* 函数调用。也就是说,只要某个地方期待一种文件系统回应却拿到了另一种,就会集中走到这里生成错误。

调用图:调用 1 个内部函数(internal_error);被 8 处调用(expect_canonicalize, expect_copy, expect_create_directory, expect_get_metadata, expect_read_directory, expect_read_file, expect_remove, expect_write_file);外部调用 1 个(format!)。

run_direct_request186–295 ↗
async fn run_direct_request(
    request: FsHelperRequest,
) -> Result<FsHelperPayload, JSONRPCErrorError>

作用:这个函数是真正执行文件系统请求的地方。它收到“读、写、建目录、查信息、规范路径、列目录、删除、复制”等请求后,直接调用本地文件系统完成操作,再包装成统一回应。

数据流:进去的是一个 FsHelperRequest,也就是带参数的文件操作单子 → 它创建 DirectFileSystem,按请求种类调用对应的磁盘操作;读文件会把字节编码成 base64,写文件会先把 base64 解码成字节;遇到磁盘错误会交给 map_fs_error 翻译 → 出来的是 FsHelperPayload,或者一个 JSON-RPC 错误;同时真实磁盘可能被读取、写入、创建、删除或复制。

调用关系:它由 run_main 调用,是这个 helper 处理单次请求的核心入口。它把高层协议请求分发给底层 DirectFileSystem,并把底层结果重新装回协议回应。

调用图:被 1 处调用(run_main);外部调用 8 个(Canonicalize, Copy, CreateDirectory, GetMetadata, ReadDirectory, ReadFile, Remove, WriteFile)。

map_fs_error297–305 ↗
fn map_fs_error(err: io::Error) -> JSONRPCErrorError

作用:这个函数把操作系统给出的文件错误,翻译成外层协议能理解的错误。这样调用方不用直接理解各种底层 io::Error。

数据流:进去的是一个 io::Error,也就是系统文件操作失败的原因 → 它查看错误种类:找不到文件变成 not_found,输入不合法或权限不足变成 invalid_request,其他情况变成 internal_error → 出来的是 JSONRPCErrorError。

调用关系:run_direct_request 在每次调用真实文件系统失败时都会用它。它连接了底层磁盘错误和上层 JSON-RPC 错误格式。

调用图:调用 3 个内部函数(internal_error, invalid_request, not_found);外部调用 2 个(kind, to_string)。

tests::helper_protocol_uses_path_uris316–372 ↗
fn helper_protocol_uses_path_uris() -> serde_json::Result<()>

作用:这个测试确认文件系统 helper 在 JSON 里使用的是 file: 开头的路径 URI,而不是随便的本地路径字符串。路径 URI 可以理解为一种更明确的路径写法,方便跨平台和远程路径表达。

数据流:进去的是测试里构造的本地路径和类似 file://server/share/file 的路径 → 它把写文件请求和规范化路径回应序列化成 JSON,再检查字段名、操作名、路径字符串是否符合预期 → 测试通过则说明协议格式没有被意外改坏。

调用关系:它只在测试时运行,不参与正常请求处理。它会构造 WriteFile 和 Canonicalize 相关数据,验证这个文件定义的请求、回应序列化规则是否稳定。

调用图:调用 2 个内部函数(from_path, parse);外部调用 8 个(new, assert!, assert_eq!, Canonicalize, WriteFile, Ok, to_value, current_dir)。

沙盒化文件系统执行

通过验证可沙盒化请求、调用辅助子进程,并将其结果转换回文件系统操作,构建沙盒化文件系统后端。

exec-server/src/fs_sandbox.rs源码 ↗
orchestrationrequest handling

这个文件的核心是 FileSystemSandboxRunner。它接到一个文件系统请求后,先确定这次操作应该在哪个目录里运行,再把用户给的权限规则转成系统能执行的沙盒规则。然后它会补上一些“助手程序自己启动必须能读到的东西”,比如当前可执行文件所在目录和最小系统读取权限,否则助手可能还没干活就启动失败。接着它会统一路径别名,减少同一个目录因为符号链接或系统别名导致权限判断不一致的问题。最后,它启动一个受限的子进程,把请求用 JSON(文本格式的数据包装)写进子进程的标准输入,再读取子进程输出的 JSON 结果。文件里还很小心地过滤环境变量,只传 PATH、临时目录等少量必要变量,避免把 API key、代理地址这类秘密带进沙盒。

函数细节35
FileSystemSandboxRunner::new56–61 ↗
fn new(runtime_paths: ExecServerRuntimePaths) -> Self

作用:创建一个文件系统沙盒运行器。它保存运行时需要的程序路径,并提前准备一份安全的环境变量清单。

数据流:输入是运行时路径,比如当前程序和 Linux 沙盒程序的位置;函数调用 helper_env 过滤当前环境变量;输出是一个 FileSystemSandboxRunner,之后可以用它来启动沙盒助手。

调用关系:这是运行器的入口构造步骤。测试 sandbox_exec_request_carries_helper_env 会用它检查环境变量是否被带入;真正执行请求前,也需要先有这个运行器实例。

调用图:调用 1 个内部函数(helper_env);被 2 处调用(sandbox_exec_request_carries_helper_env, new)。

FileSystemSandboxRunner::run63–94 ↗
async fn run(
        &self,
        sandbox: &FileSystemSandboxContext,
        request: FsHelperRequest,
    ) -> Result<FsHelperPayload, JSONRPCErrorError>

作用:执行一次真正的沙盒文件系统请求。它把权限、工作目录、助手程序和请求内容全部准备好,然后交给子进程去做。

数据流:输入是沙盒上下文和一个助手请求;它先算出工作目录,转换并补强文件权限,限制网络,再生成沙盒命令,把请求编码成 JSON;输出是助手返回的有效数据,或者一个 JSON-RPC 错误。

调用关系:它是本文件最主要的流程函数,由上层 run_sandboxed 调用。它自己串起 sandbox_cwd、helper_read_roots、add_helper_runtime_permissions、normalize_file_system_policy_root_aliases、sandbox_exec_request 和 run_command。

调用图:调用 7 个内部函数(sandbox_exec_request, add_helper_runtime_permissions, helper_read_roots, normalize_file_system_policy_root_aliases, run_command, sandbox_cwd, from_runtime_permissions_with_enforcement);被 1 处调用(run_sandboxed);外部调用 2 个(new, to_vec)。

FileSystemSandboxRunner::sandbox_exec_request96–133 ↗
fn sandbox_exec_request(
        &self,
        permission_profile: &PermissionProfile,
        cwd: &PathUri,
        sandbox_context: &FileSystemSandboxContext,
    ) -> Result<SandboxExecRequest, J

作用:把“我要运行助手程序”这件事包装成一份可执行的沙盒启动请求。它决定用哪种沙盒机制,并把程序、参数、目录、环境变量都放进去。

数据流:输入是最终权限配置、工作目录 URI 和沙盒上下文;它向 SandboxManager 询问该用哪种隔离方式,并把 codex 自身作为助手程序启动;输出是一份 SandboxExecRequest,里面已经是可以 spawn 的命令形式。

调用关系:它被 run 调用,是从“权限规则”走向“实际命令”的桥。后续 run_command 会拿它启动进程。

调用图:调用 2 个内部函数(to_runtime_permissions, new);被 1 处调用(run);外部调用 2 个(clone, vec!)。

sandbox_cwd136–154 ↗
fn sandbox_cwd(sandbox: &FileSystemSandboxContext) -> Result<SandboxCwd, JSONRPCErrorError>

作用:确定沙盒里的当前工作目录。很多权限规则会说“项目目录下可以访问”,这时必须知道项目目录到底是哪儿。

数据流:输入是沙盒上下文;如果上下文里明确给了 cwd,就转成本机绝对路径;如果没给但权限依赖 cwd,就报错;如果不依赖 cwd,就用当前进程目录;输出同时包含 URI 形式和本机路径形式的 SandboxCwd。

调用关系:run 在准备权限时先调用它。相关测试会检查:有 cwd 时使用它,非本机 cwd 要拒绝,依赖 cwd 却没给时也要拒绝。

调用图:调用 6 个内部函数(native_sandbox_cwd, current_sandbox_cwd, invalid_request, has_cwd_dependent_permissions, from_absolute_path, from_abs_path);被 3 处调用(run, sandbox_cwd_rejects_cwd_dependent_profile_without_context_cwd, sandbox_cwd_rejects_non_native_context_cwd_without_fallback)。

native_sandbox_cwd156–159 ↗
fn native_sandbox_cwd(cwd: &PathUri) -> Result<AbsolutePathBuf, JSONRPCErrorError>

作用:把工作目录的 URI 转成本机能理解的绝对路径。URI 是统一写法,本机路径才是操作系统真正能进入的目录。

数据流:输入是 PathUri;它尝试转换成当前操作系统上的绝对路径;输出是 AbsolutePathBuf,转换失败就返回“请求无效”的错误。

调用关系:它只由 sandbox_cwd 调用,用来处理上下文里已经明确提供的 cwd。

调用图:调用 1 个内部函数(to_abs_path);被 1 处调用(sandbox_cwd)。

helper_read_roots161–174 ↗
fn helper_read_roots(runtime_paths: &ExecServerRuntimePaths) -> Vec<AbsolutePathBuf>

作用:找出助手程序启动时必须能读取的目录。没有这些读取权限,沙盒太严时助手可能连自己的程序文件都读不到。

数据流:输入是运行时路径;它查看 codex 自身程序和可选的 Linux 沙盒程序所在父目录,去重后收集起来;输出是一组绝对路径。

调用关系:run 会用它给权限策略补洞。多个测试也会用它确认 codex 程序目录和 Linux 沙盒别名目录都会被加入读取范围。

调用图:调用 1 个内部函数(from_absolute_path);被 4 处调用(run, helper_permissions_include_helper_read_root_without_additional_permissions, helper_permissions_include_linux_sandbox_alias_parent, helper_permissions_preserve_existing_writes);外部调用 2 个(new, once)。

add_helper_runtime_permissions176–205 ↗
fn add_helper_runtime_permissions(
    file_system_policy: &mut FileSystemSandboxPolicy,
    helper_read_roots: &[AbsolutePathBuf],
    cwd: &std::path::Path,
)

作用:给文件权限策略补上助手程序运行所需的最低读取权限,同时不破坏原来允许写入的规则。

数据流:输入是可修改的文件系统策略、助手需要读取的目录列表和当前目录;它先确保没有全盘读取时仍有平台最小读取权限,再把助手目录加入只读权限;结果是原策略被原地更新。

调用关系:run 在正式启动沙盒前调用它。测试重点检查它会补最小读取权限、保留已有写权限、并加入助手程序所在目录。

调用图:调用 2 个内部函数(can_read_path_with_cwd, has_full_disk_read_access);被 6 处调用(run, helper_permissions_enable_minimal_reads_for_restricted_profile, helper_permissions_enable_minimal_reads_for_restricted_profile_with_writes, helper_permissions_include_helper_read_root_without_additional_permissions, helper_permissions_include_linux_sandbox_alias_parent, helper_permissions_preserve_existing_writes)。

normalize_file_system_policy_root_aliases207–213 ↗
fn normalize_file_system_policy_root_aliases(file_system_policy: &mut FileSystemSandboxPolicy)

作用:把权限策略里的路径别名尽量换成更标准的路径。这样同一个地方不会因为写法不同而绕过或错过权限判断。

数据流:输入是可修改的文件系统策略;它逐条查看路径型规则,把每个路径交给 normalize_top_level_alias;输出是被原地规范化后的策略。

调用关系:run 在补完助手权限后调用它。它把具体的路径规范化工作交给 normalize_top_level_alias。

调用图:调用 1 个内部函数(normalize_top_level_alias);被 1 处调用(run)。

normalize_top_level_alias215–237 ↗
fn normalize_top_level_alias(path: AbsolutePathBuf) -> AbsolutePathBuf

作用:规范化一个路径最上层可识别的别名。比如某些系统目录可能通过别名或符号链接指向真实位置,这里尽量统一它。

数据流:输入是一个绝对路径;它从路径的各级祖先目录往上检查,找到能被规范化且结果不同的祖先后,把剩余后缀接回去;成功则输出新绝对路径,否则原样返回。

调用关系:它由 normalize_file_system_policy_root_aliases 调用。它使用保留符号链接语义的规范化方法,避免粗暴解析导致路径含义变化太大。

调用图:调用 2 个内部函数(from_absolute_path, to_path_buf);被 1 处调用(normalize_file_system_policy_root_aliases);外部调用 2 个(canonicalize_preserving_symlinks, symlink_metadata)。

helper_env239–241 ↗
fn helper_env() -> HashMap<String, String>

作用:生成传给沙盒助手的环境变量。它不是照搬全部环境,而是只留下启动必须的少数变量。

数据流:输入来自当前进程的系统环境变量;它把这些变量交给 helper_env_from_vars 过滤;输出是一个字符串键值表,只包含允许传递的变量。

调用关系:FileSystemSandboxRunner::new 会调用它并保存结果。测试 helper_env_carries_only_allowlisted_runtime_vars 会验证它和白名单规则一致。

调用图:调用 1 个内部函数(helper_env_from_vars);被 2 处调用(new, helper_env_carries_only_allowlisted_runtime_vars);外部调用 1 个(vars_os)。

helper_env_from_vars243–253 ↗
fn helper_env_from_vars(
    vars: impl IntoIterator<Item = (std::ffi::OsString, std::ffi::OsString)>,
) -> HashMap<String, String>

作用:从给定环境变量列表里筛出安全可传的变量。这样测试可以喂入假环境,生产代码也可以喂入真实环境。

数据流:输入是一批环境变量键值;它把键和值转成字符串,并用 helper_env_key_is_allowed 判断键是否允许;输出是过滤后的 HashMap。

调用关系:helper_env 用它处理真实环境。多个测试用它验证 PATH、临时目录、macOS 特殊变量、Windows Path 的保留规则,以及秘密变量不会泄露。

调用图:被 4 处调用(helper_env, helper_env_preserves_corefoundation_text_encoding, helper_env_preserves_path_for_system_bwrap_discovery_without_leaking_secrets, helper_env_preserves_windows_path_key_for_system_bwrap_discovery);外部调用 1 个(into_iter)。

helper_env_key_is_allowed255–261 ↗
fn helper_env_key_is_allowed(key: &str) -> bool

作用:判断某个环境变量名能不能传给助手程序。规则很保守,只允许少数运行所需变量。

数据流:输入是环境变量名;它检查固定白名单、macOS 的文本编码变量、调试时 Bazel 运行需要的变量,以及 Windows 下大小写不同的 PATH;输出是真或假。

调用关系:它被 helper_env_from_vars 间接使用,也会把 Bazel 相关判断交给 bazel_bwrap_env_key_is_allowed。

调用图:调用 1 个内部函数(bazel_bwrap_env_key_is_allowed);外部调用 1 个(cfg!)。

bazel_bwrap_env_key_is_allowed269–271 ↗
fn bazel_bwrap_env_key_is_allowed(_key: &str) -> bool

作用:在调试构建中,判断 Bazel 测试环境里和 bwrap 沙盒工具有关的变量能不能保留。bwrap 是 Linux 上常用的隔离工具。

数据流:输入是环境变量名;调试构建时,它先确认当前像是在 Bazel 包里运行,再查专门的 Bazel 白名单;非调试构建则永远返回 false。

调用关系:它只服务于 helper_env_key_is_allowed。这样普通运行不会额外泄露 Bazel 变量,但测试和调试环境还能找到需要的沙盒工具。

调用图:被 1 处调用(helper_env_key_is_allowed);外部调用 1 个(option_env!)。

run_command273–299 ↗
async fn run_command(
    command: SandboxExecRequest,
    request_json: Vec<u8>,
) -> Result<FsHelperPayload, JSONRPCErrorError>

作用:真正启动沙盒助手子进程,并和它通过输入输出交换 JSON 消息。它像把任务纸条塞给隔离房间里的人,再等他把结果递出来。

数据流:输入是沙盒命令和已经编码好的请求 JSON;它启动子进程,把 JSON 写入 stdin,关闭输入,等待输出;如果进程失败就报错,如果成功就解析 stdout 里的 FsHelperResponse,输出有效载荷或助手返回的错误。

调用关系:run 在准备好命令和请求后调用它。它把启动进程的细节交给 spawn_command,并把 I/O、JSON、进程状态错误统一转换成 JSON-RPC 错误。

调用图:调用 2 个内部函数(spawn_command, internal_error);被 1 处调用(run);外部调用 2 个(format!, from_slice)。

spawn_command301–329 ↗
fn spawn_command(
    SandboxExecRequest {
        command: argv,
        cwd,
        env,
        arg0,
        ..
    }: SandboxExecRequest,
) -> Result<tokio::process::Child, JSONRPCErrorError>

作用:按 SandboxExecRequest 创建一个异步子进程。它负责把程序名、参数、目录、环境变量和管道都设置好。

数据流:输入是一份 SandboxExecRequest;它取出命令第一项作为程序,其余作为参数,设置当前目录,清空旧环境后放入指定环境,并打开 stdin/stdout/stderr 管道;输出是已启动的 Child,命令为空则返回请求无效错误。

调用关系:它由 run_command 调用,是实际碰操作系统启动进程的地方。run_command 随后会向这个子进程写请求并读取结果。

调用图:调用 1 个内部函数(invalid_request);被 1 处调用(run_command);外部调用 2 个(new, piped)。

io_error331–333 ↗
fn io_error(err: std::io::Error) -> JSONRPCErrorError

作用:把普通输入输出错误包装成 JSON-RPC 内部错误。这样上层看到的是统一格式的错误。

数据流:输入是 std::io::Error;它取出错误文字并包装成 internal_error;输出是 JSONRPCErrorError。

调用关系:文件中的异步写入、关闭 stdin、等待子进程等 I/O 操作失败时会用它做错误转换。

调用图:调用 1 个内部函数(internal_error);外部调用 1 个(to_string)。

json_error335–339 ↗
fn json_error(err: serde_json::Error) -> JSONRPCErrorError

作用:把 JSON 编码或解码失败包装成 JSON-RPC 内部错误。JSON 是这里父进程和助手进程传话的格式。

数据流:输入是 serde_json::Error;它加上一句说明“助手消息编码或解码失败”,再包装成 internal_error;输出是 JSONRPCErrorError。

调用关系:run 编码请求失败、run_command 解析响应失败时会用它,让错误信息保持统一且可读。

调用图:调用 1 个内部函数(internal_error);外部调用 1 个(format!)。

tests::helper_permissions_enable_minimal_reads_for_restricted_profile369–377 ↗
fn helper_permissions_enable_minimal_reads_for_restricted_profile()

作用:测试受限权限下也会自动加入最小读取权限。否则助手可能在严格沙盒里无法正常启动。

数据流:输入是一个空的受限策略和临时目录 cwd;测试调用 add_helper_runtime_permissions;之后断言策略包含平台默认的最小读取能力。

调用关系:它直接验证 add_helper_runtime_permissions 的基础行为,保证 run 构造出来的沙盒不会把助手自己困死。

调用图:调用 2 个内部函数(add_helper_runtime_permissions, from_absolute_path);外部调用 4 个(new, assert!, restricted_policy, temp_dir)。

tests::helper_permissions_enable_minimal_reads_for_restricted_profile_with_writes380–391 ↗
fn helper_permissions_enable_minimal_reads_for_restricted_profile_with_writes()

作用:测试即使策略里已有写权限,也仍会补上最小读取权限。写权限不等于助手能读取启动所需文件。

数据流:输入是带一个可写目录的受限策略;调用 add_helper_runtime_permissions 后,检查平台默认读取权限被包含;原策略因此多了启动所需读取能力。

调用关系:它补充验证 add_helper_runtime_permissions 在已有写规则时不会忘记最小读取规则。

调用图:调用 2 个内部函数(add_helper_runtime_permissions, from_absolute_path);外部调用 4 个(assert!, restricted_policy, temp_dir, vec!)。

tests::helper_permissions_preserve_existing_writes394–422 ↗
fn helper_permissions_preserve_existing_writes()

作用:测试给助手补读取权限时,不会把用户原本允许写的目录弄丢。权限补丁不能误伤原来的工作权限。

数据流:输入是一个带可写目录的策略和运行时程序路径;测试计算助手读取根目录并调用 add_helper_runtime_permissions;之后检查助手目录可读,原来的目录仍可写。

调用关系:它同时覆盖 helper_read_roots 和 add_helper_runtime_permissions,证明“补权限”是追加而不是重写。

调用图:调用 4 个内部函数(add_helper_runtime_permissions, helper_read_roots, new, from_absolute_path);外部调用 5 个(assert!, restricted_policy, current_exe, temp_dir, vec!)。

tests::helper_env_carries_only_allowlisted_runtime_vars425–437 ↗
fn helper_env_carries_only_allowlisted_runtime_vars()

作用:测试 helper_env 只携带白名单环境变量。这样可以防止秘密信息被无意传进沙盒。

数据流:输入是真实系统环境;测试用 helper_env 得到实际结果,再用同一允许规则手工计算期望结果;最后比较两者完全一致。

调用关系:它验证 helper_env 和 helper_env_key_is_allowed 配合正确,是环境变量安全边界的总检查。

调用图:调用 1 个内部函数(helper_env);外部调用 2 个(assert_eq!, vars_os)。

tests::helper_env_preserves_path_for_system_bwrap_discovery_without_leaking_secrets440–463 ↗
fn helper_env_preserves_path_for_system_bwrap_discovery_without_leaking_secrets()

作用:测试 PATH 和临时目录会保留,但 HOME、API key、代理地址等不会泄露。PATH 常用于找到系统工具,秘密变量则不该带入。

数据流:输入是一组人工构造的环境变量;调用 helper_env_from_vars 后,输出应只剩 PATH、TMPDIR、TMP、TEMP;测试用断言确认结果。

调用关系:它专门检查 helper_env_from_vars 的白名单过滤效果,尤其是避免敏感变量进入沙盒助手。

调用图:调用 1 个内部函数(helper_env_from_vars);外部调用 1 个(assert_eq!)。

tests::helper_env_preserves_corefoundation_text_encoding467–483 ↗
fn helper_env_preserves_corefoundation_text_encoding()

作用:在 macOS 上测试保留 __CF_USER_TEXT_ENCODING。这个变量会影响 CoreFoundation 启动时的用户文本编码判断。

数据流:输入包含 macOS 文本编码变量和 HOME;调用 helper_env_from_vars 后,应只保留文本编码变量;测试比较输出。

调用关系:它只在 macOS 编译运行时生效,验证 helper_env_key_is_allowed 里的 macOS 特例。

调用图:调用 1 个内部函数(helper_env_from_vars);外部调用 1 个(assert_eq!)。

tests::helper_env_preserves_windows_path_key_for_system_bwrap_discovery487–501 ↗
fn helper_env_preserves_windows_path_key_for_system_bwrap_discovery()

作用:在 Windows 上测试大小写不同的 Path 也会被当作 PATH 保留。Windows 环境变量名通常不区分大小写。

数据流:输入包含 Path、一个伪装的 PATH_INJECTION 和 API key;调用 helper_env_from_vars 后,只应留下真正的 Path;测试比较输出。

调用关系:它只在 Windows 上运行,验证 helper_env_key_is_allowed 的 Windows PATH 特例不会被 PATH_INJECTION 这种名字骗过。

调用图:调用 1 个内部函数(helper_env_from_vars);外部调用 1 个(assert_eq!)。

tests::sandbox_exec_request_carries_helper_env504–532 ↗
fn sandbox_exec_request_carries_helper_env()

作用:测试生成沙盒启动请求时,会把过滤后的助手环境变量带进去。没有 PATH 时,助手可能找不到必要系统工具。

数据流:输入是真实 PATH、运行时路径、cwd 和受限策略;测试创建 FileSystemSandboxRunner 并调用 sandbox_exec_request;最后检查请求里的环境包含原来的 PATH 值。

调用关系:它验证 FileSystemSandboxRunner::new 保存的 helper_env 会流入 sandbox_exec_request,确保真正启动助手时环境没有丢。

调用图:调用 5 个内部函数(new, new, from_runtime_permissions, current_dir, from_abs_path);外部调用 6 个(assert_eq!, restricted_policy, sandbox_context_with_cwd, current_exe, vars_os, vec!)。

tests::sandbox_cwd_uses_context_cwd535–552 ↗
fn sandbox_cwd_uses_context_cwd()

作用:测试上下文明确给了工作目录时,sandbox_cwd 会使用它。这样依赖项目目录的权限才能按正确位置解释。

数据流:输入是临时目录 URI 和带项目根特殊权限的策略上下文;调用 sandbox_cwd 后,输出应同时包含同一个 URI 和对应本机绝对路径。

调用关系:它验证 sandbox_cwd 的正常路径,也是动态权限能工作的前提。

调用图:调用 2 个内部函数(from_absolute_path, from_abs_path);外部调用 5 个(assert_eq!, restricted_policy, sandbox_context_with_cwd, temp_dir, vec!)。

tests::sandbox_cwd_rejects_non_native_context_cwd_without_fallback555–572 ↗
fn sandbox_cwd_rejects_non_native_context_cwd_without_fallback()

作用:测试非本机格式的 cwd 会被拒绝,而不是偷偷退回当前目录。偷偷退回会让权限判断跑到错误地方。

数据流:输入是一个当前系统无法当作本机路径使用的 URI;调用 sandbox_cwd 后应得到错误;测试比较错误内容是否明确指出该 URI 在当前系统无效。

调用关系:它直接检查 sandbox_cwd 和 native_sandbox_cwd 的错误处理,防止跨平台路径被误用。

调用图:调用 1 个内部函数(sandbox_cwd);外部调用 5 个(assert_eq!, non_native_cwd, restricted_policy, sandbox_context_with_cwd, vec!)。

tests::sandbox_cwd_rejects_cwd_dependent_profile_without_context_cwd575–592 ↗
fn sandbox_cwd_rejects_cwd_dependent_profile_without_context_cwd()

作用:测试权限依赖 cwd 但上下文没给 cwd 时会报错。没有 cwd,就无法知道“项目根目录”指哪里。

数据流:输入是包含 project_roots 这类动态特殊路径的权限策略,但不提供 cwd;调用 sandbox_cwd 后应返回错误;测试检查错误消息。

调用关系:它验证 sandbox_cwd 不会在关键信息缺失时猜测,保护 run 后续权限计算的准确性。

调用图:调用 4 个内部函数(sandbox_cwd, from_permission_profile, from_runtime_permissions, restricted);外部调用 2 个(assert_eq!, vec!)。

tests::helper_permissions_include_helper_read_root_without_additional_permissions595–618 ↗
fn helper_permissions_include_helper_read_root_without_additional_permissions()

作用:测试即使用户没有额外权限,助手程序所在目录也会被加入可读范围。否则严格沙盒下助手可能读不到自己的文件。

数据流:输入是当前可执行文件路径生成的运行时路径和空受限策略;测试调用 helper_read_roots 再调用 add_helper_runtime_permissions;最后确认 codex 程序父目录可读。

调用关系:它覆盖 helper_read_roots 与 add_helper_runtime_permissions 的组合效果,保证 run 里的自动补权限足够启动助手。

调用图:调用 4 个内部函数(add_helper_runtime_permissions, helper_read_roots, new, from_absolute_path);外部调用 5 个(new, assert!, restricted_policy, current_exe, temp_dir)。

tests::helper_permissions_include_linux_sandbox_alias_parent621–644 ↗
fn helper_permissions_include_linux_sandbox_alias_parent()

作用:测试 Linux 沙盒程序如果在另一个别名目录里,这个目录也会被加为可读。这样通过别名路径启动沙盒工具时不会失败。

数据流:输入是临时构造的 codex 程序目录和 codex-linux-sandbox 别名目录;调用 helper_read_roots 和 add_helper_runtime_permissions 后,检查两个父目录都可读。

调用关系:它验证 helper_read_roots 不只考虑 codex 自身,也考虑可选的 Linux 沙盒可执行文件路径。

调用图:调用 4 个内部函数(add_helper_runtime_permissions, helper_read_roots, new, from_absolute_path);外部调用 5 个(new, assert!, restricted_policy, temp_dir, tempdir)。

tests::restricted_policy646–648 ↗
fn restricted_policy(entries: Vec<FileSystemSandboxEntry>) -> FileSystemSandboxPolicy

作用:测试辅助函数,用来快速创建受限文件系统策略。它让测试代码少写重复样板。

数据流:输入是一组文件系统权限条目;它调用 FileSystemSandboxPolicy::restricted 包起来;输出是受限策略。

调用关系:多个测试用它准备输入策略,再交给 add_helper_runtime_permissions 或 sandbox_cwd 相关流程。

调用图:调用 1 个内部函数(restricted)。

tests::sandbox_context_with_cwd650–658 ↗
fn sandbox_context_with_cwd(
        policy: &FileSystemSandboxPolicy,
        cwd: PathUri,
    ) -> crate::FileSystemSandboxContext

作用:测试辅助函数,用权限策略和 cwd 组装一个沙盒上下文。它模拟真实请求里“权限加工作目录”的情况。

数据流:输入是文件系统策略和 cwd URI;它先构造 PermissionProfile,再调用 from_permission_profile_with_cwd;输出是 FileSystemSandboxContext。

调用关系:sandbox_cwd 相关测试和 sandbox_exec_request_carries_helper_env 使用它来准备接近真实运行的上下文。

调用图:调用 2 个内部函数(from_permission_profile_with_cwd, from_runtime_permissions)。

tests::non_native_cwd660–667 ↗
fn non_native_cwd() -> PathUri

作用:测试辅助函数,生成一个当前操作系统不该接受的 cwd URI。它专门用来测试路径校验失败。

数据流:输入没有外部参数;在 Unix 上生成类似网络共享的 file URI,在 Windows 上生成类 Unix 路径 URI;解析后输出 PathUri。

调用关系:tests::sandbox_cwd_rejects_non_native_context_cwd_without_fallback 调用它,确保 sandbox_cwd 会拒绝非本机目录。

调用图:调用 1 个内部函数(parse)。

tests::path_entry669–674 ↗
fn path_entry(path: AbsolutePathBuf, access: FileSystemAccessMode) -> FileSystemSandboxEntry

作用:测试辅助函数,创建一个普通路径权限条目。它把“某个路径可以读或写”包装成策略条目。

数据流:输入是绝对路径和访问模式;它把路径放进 FileSystemPath::Path,并附上访问模式;输出是 FileSystemSandboxEntry。

调用关系:多个权限测试用它构造可写或可读路径,再交给 restricted_policy 生成完整策略。

tests::special_entry676–684 ↗
fn special_entry(
        value: FileSystemSpecialPath,
        access: FileSystemAccessMode,
    ) -> FileSystemSandboxEntry

作用:测试辅助函数,创建一个特殊路径权限条目。特殊路径指“项目根目录”或“最小系统读取”这类不是固定普通路径的规则。

数据流:输入是特殊路径值和访问模式;它把特殊路径放进 FileSystemPath::Special,并附上访问模式;输出是 FileSystemSandboxEntry。

调用关系:sandbox_cwd 相关测试用它构造依赖 cwd 的权限,从而检查 sandbox_cwd 对动态权限的处理。

exec-server/src/sandboxed_file_system.rs源码 ↗
io_transportrequest handling

这个文件可以理解成文件系统的“安检门”。外面的代码想读写文件时,不是直接打开文件,而是先来到 SandboxedFileSystem。它会先确认两件事:第一,传进来的路径必须是本机文件路径,不能是奇怪的远程地址;第二,必须真的带了平台沙箱策略,沙箱就是一块受限制的运行区域,像给程序画了活动范围。通过检查后,它把“读文件”“写文件”“复制”等请求包装成 FsHelperRequest,交给 FileSystemSandboxRunner 在沙箱里执行。文件内容因为要通过协议传递,会用 base64 编码,简单说就是把二进制数据变成适合放进文本消息里的字符串。最后它再把沙箱返回的结果翻译成普通的文件结果,也会把协议错误转换成常见的文件错误,比如“找不到文件”或“输入无效”。有个重要限制:流式读取文件在这里不支持,因为这种持续打开通道的读法还没有接入平台沙箱。

函数细节14
SandboxedFileSystem::new36–40 ↗
fn new(runtime_paths: ExecServerRuntimePaths) -> Self

作用:创建一个新的沙箱文件系统对象。调用者给它运行时路径后,它就准备好用这些路径去启动或找到沙箱辅助程序。

数据流:进去的是 ExecServerRuntimePaths,也就是执行服务器运行时需要用到的一组路径 → 函数用这些路径创建 FileSystemSandboxRunner,这个 runner 是真正负责把请求送进沙箱的人 → 出来的是 SandboxedFileSystem,里面保存了这个 runner,之后所有文件操作都会靠它转发。

调用关系:它是这个文件的入口式构造函数。with_runtime_paths 会用它搭建带沙箱的文件系统,测试 sandboxed_file_system_rejects_non_native_uri_as_invalid_input 也会用它创建对象来检查路径拦截是否生效。它内部把初始化工作交给 FileSystemSandboxRunner::new。

调用图:调用 1 个内部函数(new);被 2 处调用(with_runtime_paths, sandboxed_file_system_rejects_non_native_uri_as_invalid_input)。

SandboxedFileSystem::run_sandboxed42–51 ↗
async fn run_sandboxed(
        &self,
        sandbox: &FileSystemSandboxContext,
        request: FsHelperRequest,
    ) -> FileSystemResult<FsHelperPayload>

作用:把一个具体文件请求送进沙箱执行,并把沙箱协议里的错误翻译成普通文件错误。这样上层代码不用关心沙箱通信细节。

数据流:进去的是沙箱上下文和一个 FsHelperRequest,比如读文件或删文件请求 → 它调用 sandbox_runner.run,让沙箱辅助程序执行这件事 → 出来的是 FsHelperPayload,也就是沙箱返回的数据;如果失败,就用 map_sandbox_error 把错误改成 io::Error。

调用关系:它是所有具体文件操作共用的“派件员”。canonicalize、read_file、write_file、create_directory、get_metadata、read_directory、remove、copy 都会先准备好请求,再交给它送进沙箱。它自己再调用底层 runner 的 run。

调用图:调用 1 个内部函数(run);被 8 处调用(canonicalize, copy, create_directory, get_metadata, read_directory, read_file, remove, write_file)。

SandboxedFileSystem::canonicalize253–259 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:把一个路径整理成系统认可的标准绝对路径,同时保证这个动作是在沙箱保护下完成的。比如把带有“..”的路径清理成最终位置。

数据流:进去的是一个 PathUri 路径和可选沙箱信息 → 它先要求必须有有效沙箱,再检查路径是不是本机路径,然后组装 Canonicalize 请求交给 run_sandboxed → 出来的是标准化后的 PathUri;如果路径不合法或沙箱拒绝,就返回错误。

调用关系:这是 ExecutorFileSystem 接口里“标准化路径”的实现。它先请 require_platform_sandbox 把关沙箱,再请 validate_native_path 把关路径,最后把真正的标准化工作交给 run_sandboxed 和沙箱辅助程序。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, Canonicalize, clone)。

SandboxedFileSystem::read_file261–267 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:在沙箱限制内读取整个文件内容。它适合一次性拿到文件的全部字节数据。

数据流:进去的是文件路径和沙箱信息 → 它检查沙箱和路径,然后发送 ReadFile 请求;沙箱返回的是 base64 字符串 → 它把 base64 解回原始字节 Vec<u8>,作为文件内容返回;如果 base64 格式坏了,会返回“数据无效”的错误。

调用关系:这是上层读取文件时走的普通读入口。它和其他文件操作一样先经过 require_platform_sandbox、validate_native_path,再通过 run_sandboxed 让沙箱辅助程序读文件。它不负责流式读取,流式读取由 read_file_stream 单独拒绝。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, ReadFile, clone)。

SandboxedFileSystem::read_file_stream269–280 ↗
fn read_file_stream(
        &'a self,
        _path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:明确告诉调用者:沙箱模式下不支持流式读取文件。流式读取就是边读边传,像水管一样持续输出内容。

数据流:进去的路径和沙箱参数会被忽略 → 它直接创建一个 Unsupported 类型的错误 → 出来的是失败结果,错误消息说明“平台沙箱不支持流式文件读取”。

调用关系:它是 ExecutorFileSystem 接口要求必须提供的方法,但这个沙箱实现目前做不了这件事,所以在被调用时立刻失败。这样比偷偷绕过沙箱更安全,也能让调用者及时改用 read_file。

调用图:外部调用 2 个(pin, new)。

SandboxedFileSystem::write_file282–291 ↗
fn write_file(
        &'a self,
        path: &'a PathUri,
        contents: Vec<u8>,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:在沙箱限制内写入一个文件。它会把要写的原始字节先转成适合协议传输的 base64 文本。

数据流:进去的是目标路径、文件内容字节和沙箱信息 → 它检查沙箱和路径,把内容 base64 编码,打包成 WriteFile 请求并送进沙箱 → 成功时没有额外数据返回,但目标文件会被沙箱辅助程序写入或覆盖;失败时返回文件错误。

调用关系:这是上层写文件时走的入口。它先用 require_platform_sandbox 和 validate_native_path 做安全检查,再把真正写盘动作交给 run_sandboxed。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, WriteFile, clone)。

SandboxedFileSystem::create_directory293–302 ↗
fn create_directory(
        &'a self,
        path: &'a PathUri,
        options: CreateDirectoryOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a,

作用:在沙箱限制内创建目录。它支持按选项决定是否递归创建,也就是父目录不存在时要不要一起建出来。

数据流:进去的是目录路径、创建选项和沙箱信息 → 它确认沙箱有效、路径合法,然后把路径和 recursive 选项装进 CreateDirectory 请求 → 沙箱执行创建;成功时返回空结果,失败时返回对应错误。

调用关系:这是上层需要新建文件夹时用的方法。它把安全检查放在前面,把实际创建目录的动作通过 run_sandboxed 交给沙箱辅助程序。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, CreateDirectory, clone)。

SandboxedFileSystem::get_metadata304–310 ↗
fn get_metadata(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:读取文件或目录的基本信息,比如它是不是文件、是不是目录、大小是多少、创建和修改时间是什么。

数据流:进去的是路径和沙箱信息 → 它检查沙箱和路径,发送 GetMetadata 请求 → 沙箱返回原始元数据后,它转换成项目内部使用的 FileMetadata 返回。

调用关系:这是上层判断路径状态时会用的方法。它仍然遵守同一套安全流程:require_platform_sandbox 检查沙箱,validate_native_path 检查路径,run_sandboxed 获取真实信息。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, GetMetadata, clone)。

SandboxedFileSystem::read_directory312–318 ↗
fn read_directory(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:列出一个目录下面有哪些条目,并告诉每个条目大概是文件还是目录。

数据流:进去的是目录路径和沙箱信息 → 它检查沙箱和路径,发送 ReadDirectory 请求 → 沙箱返回目录项列表后,它把每一项转换成 ReadDirectoryEntry,最后返回一个列表。

调用关系:这是上层浏览目录内容时用的方法。它不直接读取硬盘目录,而是把请求交给 run_sandboxed,让沙箱辅助程序在受限范围内完成。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, ReadDirectory, clone)。

SandboxedFileSystem::remove320–332 ↗
fn remove(
        &'a self,
        path: &'a PathUri,
        remove_options: RemoveOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:在沙箱限制内删除文件或目录。它会带上是否递归删除、是否强制忽略不存在等选项。

数据流:进去的是要删除的路径、删除选项和沙箱信息 → 它先确认沙箱和路径,再把 recursive 与 force 选项放进 Remove 请求 → 沙箱执行删除;成功时返回空结果,文件系统里的目标会被删除或按选项处理。

调用关系:这是上层删除东西时的安全入口。因为删除是危险操作,它和其他操作一样必须先过 require_platform_sandbox 和 validate_native_path,再通过 run_sandboxed 执行。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, Remove, clone)。

SandboxedFileSystem::copy334–348 ↗
fn copy(
        &'a self,
        source_path: &'a PathUri,
        destination_path: &'a PathUri,
        options: CopyOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> Execut

作用:在沙箱限制内复制文件或目录。它会同时检查来源路径和目标路径,避免其中任何一边绕过沙箱规则。

数据流:进去的是源路径、目标路径、复制选项和沙箱信息 → 它要求有效沙箱,分别验证两个路径都是本机路径,然后把这些信息打包成 Copy 请求 → 沙箱完成复制;成功时返回空结果,目标位置会出现副本。

调用关系:这是上层复制文件或目录时走的方法。它比单路径操作多一步:源和目标都要经过 validate_native_path。真正复制动作仍由 run_sandboxed 交给沙箱辅助程序。

调用图:调用 3 个内部函数(run_sandboxed, require_platform_sandbox, validate_native_path);外部调用 3 个(pin, Copy, clone)。

validate_native_path351–353 ↗
fn validate_native_path(path: &PathUri) -> FileSystemResult<()>

作用:检查一个 PathUri 能不能变成本机绝对路径。简单说,它用来拦住不属于本机文件系统的地址。

数据流:进去的是 PathUri → 它调用 to_abs_path 尝试把它转换成本机绝对路径,并丢掉转换结果,只保留成功或失败 → 成功时返回空结果,失败时把错误传回去。

调用关系:它是所有沙箱文件操作前的统一路径门卫。canonicalize、read_file、write_file、create_directory、get_metadata、read_directory、remove、copy 都会调用它,确保不会把非本机 URI 送进沙箱辅助程序。

调用图:调用 1 个内部函数(to_abs_path);被 8 处调用(canonicalize, copy, create_directory, get_metadata, read_directory, read_file, remove, write_file)。

require_platform_sandbox355–366 ↗
fn require_platform_sandbox(
    sandbox: Option<&FileSystemSandboxContext>,
) -> FileSystemResult<&FileSystemSandboxContext>

作用:确认调用者真的提供了可用的平台沙箱。没有沙箱,或者沙箱策略不要求进入沙箱,就直接拒绝操作。

数据流:进去的是一个可能为空的沙箱上下文 → 它检查上下文是否存在,并调用 should_run_in_sandbox 判断是否应该在沙箱里跑 → 成功时返回这个沙箱上下文;失败时返回 InvalidInput 错误,说明沙箱文件操作必须使用只读或工作区可写策略。

调用关系:它是所有实际文件操作的第一道安全门。canonicalize、read_file、write_file、create_directory、get_metadata、read_directory、remove、copy 都依赖它,防止这些方法在无保护状态下直接操作文件。

调用图:被 8 处调用(canonicalize, copy, create_directory, get_metadata, read_directory, read_file, remove, write_file)。

map_sandbox_error368–374 ↗
fn map_sandbox_error(error: JSONRPCErrorError) -> io::Error

作用:把沙箱辅助程序返回的 JSON-RPC 错误翻译成 Rust 常见的文件错误。JSON-RPC 是一种用 JSON 消息远程调用功能的协议。

数据流:进去的是 JSONRPCErrorError,里面有错误码和错误消息 → 它根据错误码判断含义:-32004 变成“找不到”,-32600 变成“输入无效”,其他都变成普通错误 → 出来的是 io::Error,方便文件系统接口统一处理。

调用关系:它是沙箱通信和普通文件接口之间的翻译员。run_sandboxed 会用它处理 runner 返回的错误,各个具体操作在解析沙箱返回内容时也会用同样的映射方式,让上层看到的是熟悉的文件错误,而不是协议细节。

调用图:外部调用 2 个(new, other)。

文件系统后端

提供具体的本地文件系统实现,以及通过 RPC 转发的远程实现,向调用方暴露执行器文件系统接口。

exec-server/src/local_file_system.rs源码 ↗
io_transportcross-cutting

执行服务器经常要替用户或模型读写文件,但这件事很危险:路径可能指到不该碰的地方,文件可能大到把内存撑爆,复制目录还可能把目录复制进自己里面造成死循环。这个文件把本机文件系统包装成三层:LocalFileSystem 像前台接待,先看有没有沙箱要求;UnsandboxedFileSystem 表示不进平台沙箱的普通访问,会拒绝误传来的沙箱请求;DirectFileSystem 才是真正调用操作系统读写磁盘的那一层。它还做了一些保护,比如一次性读文件最多 512MB,复制目录前检查目标是不是源目录自己或子目录,复制符号链接(类似快捷方式)时按系统差异处理。整体上,它让上层代码只调用统一接口,不必到处关心路径、安全边界和各种操作系统细节。

函数细节46
file_too_large_error30–35 ↗
fn file_too_large_error() -> io::Error

作用:生成一个“文件太大,不能一次读完”的错误。这样读文件时超过限制,就能给调用者一个清楚的失败原因。

数据流:进去没有业务输入,只读取固定的 512MB 上限 → 把这个上限写进错误消息 → 出来一个输入无效类型的 IO 错误,不改动文件。

调用关系:DirectFileSystem::read_file 在发现文件大小超过限制时会调用它,用统一说法拒绝过大的文件。

调用图:被 1 处调用(read_file);外部调用 2 个(new, format!)。

LocalFileSystem::unsandboxed55–60 ↗
fn unsandboxed() -> Self

作用:创建一个不带沙箱配置的本地文件系统。适合测试或明确只需要直接访问本机磁盘的场景。

数据流:进去没有参数 → 新建默认的 UnsandboxedFileSystem,并把沙箱部分设为空 → 出来一个 LocalFileSystem。

调用关系:它是构造 LocalFileSystem 的入口之一,调用方如 default_for_tests 会用它得到一个简单版本。

调用图:被 1 处调用(default_for_tests);外部调用 1 个(default)。

LocalFileSystem::with_runtime_paths62–67 ↗
fn with_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self

作用:创建一个带沙箱运行路径的本地文件系统。需要安全隔离时,上层会用它让文件操作可以进入沙箱。

数据流:进去一组运行时路径配置 → 创建普通文件系统,再用这些路径创建 SandboxedFileSystem → 出来一个同时支持普通和沙箱访问的 LocalFileSystem。

调用关系:local、new、create_file_system_context 等初始化流程会调用它,之后具体文件操作再由 LocalFileSystem::file_system_for 决定走哪一边。

调用图:调用 1 个内部函数(new);被 3 处调用(local, new, create_file_system_context);外部调用 1 个(default)。

LocalFileSystem::sandboxed69–76 ↗
fn sandboxed(&self) -> io::Result<&SandboxedFileSystem>

作用:取出已经配置好的沙箱文件系统。如果没有配置却要求沙箱操作,它会明确报错。

数据流:进去是当前 LocalFileSystem 自己 → 查看内部有没有 SandboxedFileSystem → 有就返回引用,没有就返回“需要配置运行路径”的错误。

调用关系:LocalFileSystem::file_system_for 在判断本次操作应该进沙箱时会调用它,确保不会在缺少沙箱配置时假装安全。

调用图:被 1 处调用(file_system_for)。

LocalFileSystem::file_system_for78–90 ↗
fn file_system_for(
        &'a self,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> io::Result<(
        &'a dyn ExecutorFileSystem,
        Option<&'a FileSystemSandboxContext>,

作用:根据本次请求的沙箱信息,选择真正干活的文件系统。它像分诊台:该进沙箱就送去沙箱,否则走普通磁盘访问。

数据流:进去一个可选的沙箱上下文 → 判断它是否要求平台沙箱 → 出来一对结果:要调用的文件系统,以及原样传下去的沙箱上下文;如果缺沙箱配置则返回错误。

调用关系:LocalFileSystem 的读、写、删、复制、取元数据等方法都会先调用它,再把实际工作交给选中的文件系统。

调用图:调用 1 个内部函数(sandboxed);被 9 处调用(canonicalize, copy, create_directory, get_metadata, read_directory, read_file, read_file_stream, remove, write_file)。

LocalFileSystem::open_file_for_read94–106 ↗
async fn open_file_for_read(
        &self,
        path: &PathUri,
        sandbox: Option<&FileSystemSandboxContext>,
    ) -> FileSystemResult<tokio::fs::File>

作用:打开一个文件准备用流式读取。它特意不支持平台沙箱里的流式读取,避免能力不一致带来的安全或实现问题。

数据流:进去文件路径和可选沙箱上下文 → 如果要求平台沙箱就直接报错,否则交给 unsandboxed 打开文件 → 出来一个可异步读取的文件句柄。

调用关系:上层 open 流程会调用它;它再调用 UnsandboxedFileSystem::open_file_for_read,最终由 DirectFileSystem 真正打开文件。

调用图:调用 1 个内部函数(open_file_for_read);被 1 处调用(open);外部调用 1 个(new)。

LocalFileSystem::canonicalize198–204 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:把路径整理成操作系统认可的真实绝对路径。这样可以消掉符号链接、点号等容易混淆的路径写法。

数据流:进去路径和沙箱上下文 → 先选出普通或沙箱文件系统 → 交给被选中的文件系统做路径规范化 → 出来一个规范后的 PathUri。

调用关系:统一接口里的 canonicalize 会进入这里;它通过 LocalFileSystem::file_system_for 分派给真正的实现。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(canonicalize);外部调用 1 个(pin)。

LocalFileSystem::read_file206–212 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:读取整个文件内容。它本身不直接碰磁盘,而是先判断是否需要沙箱,再转交给合适的一层。

数据流:进去路径和沙箱上下文 → 选择文件系统 → 读取文件全部字节 → 出来一段 Vec<u8> 字节数据,文件本身不被修改。

调用关系:统一接口里的 read_file 会调用它;它把工作交给沙箱文件系统或 UnsandboxedFileSystem。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(read_file);外部调用 1 个(pin)。

LocalFileSystem::read_file_stream214–220 ↗
fn read_file_stream(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:以一块一块的方式读取文件,适合大文件或不想一次放进内存的情况。

数据流:进去路径和沙箱上下文 → 选择文件系统 → 打开读取流 → 出来一个 FileSystemReadStream,后续调用者可以持续取数据块。

调用关系:上层需要流式读文件时走这里;它先由 LocalFileSystem::file_system_for 分派,再交给具体文件系统。

调用图:调用 1 个内部函数(file_system_for);外部调用 1 个(pin)。

LocalFileSystem::write_file222–229 ↗
fn write_file(
        &'a self,
        path: &'a PathUri,
        contents: Vec<u8>,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:把一段字节写入指定文件。它统一处理是否走沙箱的问题,让上层不用分两套代码。

数据流:进去路径、要写入的字节、沙箱上下文 → 选择文件系统 → 把字节写到目标路径 → 成功时只返回空结果,磁盘上的文件内容被更新。

调用关系:统一接口里的 write_file 会调用它;它再把写入动作交给普通或沙箱实现。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(write_file);外部调用 1 个(pin)。

LocalFileSystem::create_directory231–240 ↗
fn create_directory(
        &'a self,
        path: &'a PathUri,
        options: CreateDirectoryOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a,

作用:创建文件夹,并支持是否递归创建父文件夹。比如要建 a/b/c,递归模式会把缺的 a 和 b 也建好。

数据流:进去目录路径、创建选项、沙箱上下文 → 选择文件系统 → 按选项创建目录 → 成功返回空结果,磁盘目录结构可能增加。

调用关系:统一接口里的 create_directory 会调用它;它负责分派,真正创建由下层完成。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(create_directory);外部调用 1 个(pin)。

LocalFileSystem::get_metadata242–248 ↗
fn get_metadata(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:读取文件或目录的基本信息,比如是不是目录、大小、创建和修改时间。

数据流:进去路径和沙箱上下文 → 选择文件系统 → 查询目标的属性 → 出来 FileMetadata,不改动文件。

调用关系:统一接口里的 get_metadata 会调用它;它通过 LocalFileSystem::file_system_for 找到该用的实现。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(get_metadata);外部调用 1 个(pin)。

LocalFileSystem::read_directory250–256 ↗
fn read_directory(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:列出一个目录下面的条目。调用者可以拿到每个名字,以及它大致是文件还是文件夹。

数据流:进去目录路径和沙箱上下文 → 选择文件系统 → 读取目录内容 → 出来一组 ReadDirectoryEntry,不改动磁盘。

调用关系:统一接口里的 read_directory 会调用它;具体列目录由普通或沙箱文件系统完成。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(read_directory);外部调用 1 个(pin)。

LocalFileSystem::remove258–265 ↗
fn remove(
        &'a self,
        path: &'a PathUri,
        options: RemoveOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:删除文件或目录。它支持递归删除目录,也支持 force 这种“找不到也算成功”的模式。

数据流:进去路径、删除选项、沙箱上下文 → 选择文件系统 → 按目标类型和选项删除 → 成功返回空结果,磁盘上的目标可能消失。

调用关系:统一接口里的 remove 会调用它;它只做路线选择,细节交给下层。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(remove);外部调用 1 个(pin)。

LocalFileSystem::copy267–281 ↗
fn copy(
        &'a self,
        source_path: &'a PathUri,
        destination_path: &'a PathUri,
        options: CopyOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> Execut

作用:复制文件、目录或符号链接。它统一决定是否在沙箱中复制。

数据流:进去源路径、目标路径、复制选项、沙箱上下文 → 选择文件系统 → 执行复制 → 成功返回空结果,目标位置会出现新内容。

调用关系:统一接口里的 copy 会调用它;它通过 LocalFileSystem::file_system_for 把任务转给沙箱或非沙箱实现。

调用图:调用 1 个内部函数(file_system_for);被 1 处调用(copy);外部调用 1 个(pin)。

UnsandboxedFileSystem::open_file_for_read285–294 ↗
async fn open_file_for_read(
        &self,
        path: &PathUri,
        sandbox: Option<&FileSystemSandboxContext>,
    ) -> FileSystemResult<tokio::fs::File>

作用:在非沙箱模式下打开文件读取,并拒绝真正要求平台沙箱的请求。它防止“说要沙箱,结果却直接读磁盘”的错误。

数据流:进去路径和沙箱上下文 → 先检查上下文是否要求平台沙箱 → 通过后去掉沙箱信息,交给 DirectFileSystem 打开 → 出来文件句柄。

调用关系:LocalFileSystem::open_file_for_read 会调用它;它再调用 DirectFileSystem::open_file_for_read。

调用图:调用 2 个内部函数(open_file_for_read, reject_platform_sandbox_context);被 1 处调用(open_file_for_read)。

UnsandboxedFileSystem::canonicalize401–407 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:在非沙箱模式下规范化路径,同时确认没有误用平台沙箱请求。

数据流:进去路径和沙箱上下文 → 检查不能是要求平台沙箱的上下文 → 交给 DirectFileSystem 处理真实路径 → 出来规范后的 PathUri。

调用关系:作为 ExecutorFileSystem 的非沙箱实现被上层分派调用;它是 LocalFileSystem 和 DirectFileSystem 之间的安全检查层。

调用图:调用 2 个内部函数(canonicalize, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::read_file409–415 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:在非沙箱模式下读取整个文件,并挡住不该走这里的沙箱请求。

数据流:进去路径和沙箱上下文 → 检查沙箱要求 → 交给 DirectFileSystem 读取字节 → 出来文件内容。

调用关系:LocalFileSystem 选择非沙箱路线时会走到它;它继续调用 DirectFileSystem::read_file。

调用图:调用 2 个内部函数(read_file, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::read_file_stream417–423 ↗
fn read_file_stream(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:在非沙箱模式下创建文件读取流。它用于一块一块地把文件内容送出去。

数据流:进去路径和沙箱上下文 → 拒绝平台沙箱请求 → 交给 DirectFileSystem 建立读取流 → 出来 FileSystemReadStream。

调用关系:LocalFileSystem 分派到非沙箱读取流时会用它;它再转给 DirectFileSystem。

调用图:调用 2 个内部函数(read_file_stream, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::write_file425–434 ↗
fn write_file(
        &'a self,
        path: &'a PathUri,
        contents: Vec<u8>,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:在非沙箱模式下写文件,并确保平台沙箱请求不会绕过沙箱直接写磁盘。

数据流:进去路径、内容、沙箱上下文 → 检查上下文 → 去掉沙箱信息后交给 DirectFileSystem 写入 → 成功返回空结果,文件内容被写入。

调用关系:LocalFileSystem 选择普通路线时会调用它;实际写磁盘由 DirectFileSystem::write_file 完成。

调用图:调用 2 个内部函数(write_file, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::create_directory436–445 ↗
fn create_directory(
        &'a self,
        path: &'a PathUri,
        options: CreateDirectoryOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a,

作用:在非沙箱模式下创建目录,同时做一层沙箱误用检查。

数据流:进去路径、创建选项、沙箱上下文 → 检查不能要求平台沙箱 → 交给 DirectFileSystem 建目录 → 成功返回空结果。

调用关系:LocalFileSystem 分派普通目录创建时会走这里;它再调用 DirectFileSystem::create_directory。

调用图:调用 2 个内部函数(create_directory, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::get_metadata447–453 ↗
fn get_metadata(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:在非沙箱模式下读取文件属性,并拒绝平台沙箱请求。

数据流:进去路径和沙箱上下文 → 检查上下文 → 交给 DirectFileSystem 查询元数据 → 出来 FileMetadata。

调用关系:LocalFileSystem 选择非沙箱实现时会用它;它把查询交给 DirectFileSystem。

调用图:调用 2 个内部函数(get_metadata, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::read_directory455–461 ↗
fn read_directory(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:在非沙箱模式下列目录,同时避免沙箱请求走错路。

数据流:进去目录路径和沙箱上下文 → 检查上下文 → 交给 DirectFileSystem 读取目录 → 出来目录条目列表。

调用关系:LocalFileSystem 分派普通列目录时会调用它;它再调用 DirectFileSystem::read_directory。

调用图:调用 2 个内部函数(read_directory, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::remove463–470 ↗
fn remove(
        &'a self,
        path: &'a PathUri,
        options: RemoveOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:在非沙箱模式下删除文件或目录,并防止平台沙箱操作被错误地直接执行。

数据流:进去路径、删除选项、沙箱上下文 → 检查上下文 → 交给 DirectFileSystem 删除 → 成功返回空结果,目标可能被移除。

调用关系:LocalFileSystem 选择普通删除路线时会调用它;它再转给 DirectFileSystem::remove。

调用图:调用 2 个内部函数(remove, reject_platform_sandbox_context);外部调用 1 个(pin)。

UnsandboxedFileSystem::copy472–486 ↗
fn copy(
        &'a self,
        source_path: &'a PathUri,
        destination_path: &'a PathUri,
        options: CopyOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> Execut

作用:在非沙箱模式下复制文件、目录或符号链接,并先确认这不是应该进沙箱的操作。

数据流:进去源路径、目标路径、复制选项、沙箱上下文 → 检查沙箱要求 → 交给 DirectFileSystem 复制 → 成功返回空结果,目标路径出现复制结果。

调用关系:LocalFileSystem 分派普通复制时会走这里;它调用 DirectFileSystem::copy 完成真正工作。

调用图:调用 2 个内部函数(copy, reject_platform_sandbox_context);外部调用 1 个(pin)。

DirectFileSystem::open_file_for_read490–498 ↗
async fn open_file_for_read(
        &self,
        path: &PathUri,
        sandbox: Option<&FileSystemSandboxContext>,
    ) -> FileSystemResult<tokio::fs::File>

作用:真正向操作系统打开一个普通文件用于读取。它只接受没有沙箱上下文的直接操作。

数据流:进去 PathUri 和可选沙箱上下文 → 拒绝任何沙箱上下文,把 URI 转成绝对路径 → 用 regular_file 打开文件 → 出来 tokio 文件句柄。

调用关系:UnsandboxedFileSystem 和 DirectFileSystem 自己的读文件、读流都会调用它;它是读文件落到磁盘前的最后一步。

调用图:调用 3 个内部函数(reject_sandbox_context, open, to_abs_path);被 3 处调用(read_file, read_file_stream, open_file_for_read);外部调用 1 个(as_path)。

DirectFileSystem::canonicalize694–700 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:调用操作系统把路径解析成真实绝对路径。比如处理 ..、. 和符号链接。

数据流:进去路径和沙箱上下文 → 拒绝沙箱上下文,把路径转为绝对路径 → 调用系统 canonicalize,再包装回 PathUri → 出来规范路径。

调用关系:非沙箱路径规范化最终会走到这里;它直接使用操作系统提供的路径解析能力。

调用图:调用 4 个内部函数(reject_sandbox_context, from_absolute_path, from_abs_path, to_abs_path);被 1 处调用(canonicalize);外部调用 3 个(pin, canonicalize, as_path)。

DirectFileSystem::read_file702–708 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:真正读取整个文件内容,并保护内存不被超大文件拖垮。

数据流:进去路径和沙箱上下文 → 打开文件,查看大小 → 超过 512MB 就报错,否则最多读取限制多一点点来二次确认 → 出来文件字节。

调用关系:UnsandboxedFileSystem::read_file 会调用它;它内部用 DirectFileSystem::open_file_for_read 打开文件,用 file_too_large_error 生成超限错误。

调用图:调用 2 个内部函数(open_file_for_read, file_too_large_error);被 1 处调用(read_file);外部调用 2 个(pin, with_capacity)。

DirectFileSystem::read_file_stream710–716 ↗
fn read_file_stream(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:真正创建文件读取流,让调用者分块读取内容,而不是一次全塞进内存。

数据流:进去路径和沙箱上下文 → 打开文件 → 用固定块大小包装成 ReaderStream → 出来 FileSystemReadStream。

调用关系:UnsandboxedFileSystem::read_file_stream 会调用它;它依赖 DirectFileSystem::open_file_for_read 获得文件句柄。

调用图:调用 2 个内部函数(open_file_for_read, new);被 1 处调用(read_file_stream);外部调用 2 个(pin, with_capacity)。

DirectFileSystem::write_file718–725 ↗
fn write_file(
        &'a self,
        path: &'a PathUri,
        contents: Vec<u8>,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:真正把字节写到本机磁盘上的文件里。

数据流:进去路径、内容、沙箱上下文 → 拒绝沙箱上下文,把路径转成绝对路径 → 调用 tokio 异步写文件 → 成功返回空结果,目标文件内容被替换或创建。

调用关系:UnsandboxedFileSystem::write_file 会调用它;它是写文件操作的落地层。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(write_file);外部调用 3 个(pin, write, as_path)。

DirectFileSystem::create_directory727–736 ↗
fn create_directory(
        &'a self,
        path: &'a PathUri,
        options: CreateDirectoryOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a,

作用:真正创建目录。它根据选项决定只建最后一级,还是把缺失的父目录一起建好。

数据流:进去路径、创建选项、沙箱上下文 → 拒绝沙箱上下文,把路径转绝对 → recursive 为真就 create_dir_all,否则 create_dir → 成功返回空结果。

调用关系:UnsandboxedFileSystem::create_directory 会调用它;它直接调用操作系统的建目录能力。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(create_directory);外部调用 4 个(pin, create_dir, create_dir_all, as_path)。

DirectFileSystem::get_metadata738–744 ↗
fn get_metadata(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:真正读取文件或目录的属性信息,包括是否是符号链接、大小和时间。

数据流:进去路径和沙箱上下文 → 拒绝沙箱上下文,把路径转绝对 → 分别读取普通元数据和符号链接元数据 → 出来 FileMetadata。

调用关系:UnsandboxedFileSystem::get_metadata 会调用它;时间字段会使用 system_time_to_unix_ms 转成毫秒数字。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(get_metadata);外部调用 4 个(pin, metadata, symlink_metadata, as_path)。

DirectFileSystem::read_directory746–752 ↗
fn read_directory(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:真正读取目录里的项目列表。遇到某个条目元数据读不到时,会跳过它而不是让整个目录读取失败。

数据流:进去目录路径和沙箱上下文 → 拒绝沙箱上下文,把路径转绝对 → 遍历目录项并读取每项元数据 → 出来条目列表。

调用关系:UnsandboxedFileSystem::read_directory 会调用它;它直接使用 tokio 的异步目录读取。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(read_directory);外部调用 5 个(pin, new, metadata, read_dir, as_path)。

DirectFileSystem::remove754–761 ↗
fn remove(
        &'a self,
        path: &'a PathUri,
        options: RemoveOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:真正删除文件、符号链接或目录。它会按目标类型和选项选择合适的删除方式。

数据流:进去路径、删除选项、沙箱上下文 → 拒绝沙箱上下文,把路径转绝对 → 查目标类型;目录按 recursive 决定删一层还是整棵树,其他则删文件;找不到且 force 为真就算成功 → 出来空结果或错误。

调用关系:UnsandboxedFileSystem::remove 会调用它;它是删除操作最终接触磁盘的地方。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(remove);外部调用 6 个(pin, remove_dir, remove_dir_all, remove_file, symlink_metadata, as_path)。

DirectFileSystem::copy763–777 ↗
fn copy(
        &'a self,
        source_path: &'a PathUri,
        destination_path: &'a PathUri,
        options: CopyOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> Execut

作用:真正复制文件、目录或符号链接。因为复制目录可能比较慢,它会放到阻塞任务里执行,避免卡住异步运行器。

数据流:进去源路径、目标路径、复制选项、沙箱上下文 → 拒绝沙箱上下文并转成绝对路径 → 在后台阻塞任务中判断源类型:目录就递归复制,符号链接就复制链接,普通文件就复制文件 → 成功返回空结果。

调用关系:UnsandboxedFileSystem::copy 会调用它;目录复制会用 copy_dir_recursive,并用 destination_is_same_or_descendant_of_source 防止复制到自己里面。

调用图:调用 2 个内部函数(reject_sandbox_context, to_abs_path);被 1 处调用(copy);外部调用 2 个(pin, spawn_blocking)。

reject_sandbox_context780–788 ↗
fn reject_sandbox_context(sandbox: Option<&FileSystemSandboxContext>) -> io::Result<()>

作用:检查直接文件系统操作没有携带任何沙箱上下文。它是防呆措施,防止底层 DirectFileSystem 被错误地拿来处理沙箱请求。

数据流:进去一个可选沙箱上下文 → 如果有值就生成错误,没有就通过 → 出来空成功或 IO 错误,不改动文件。

调用关系:DirectFileSystem 的打开、规范化、写入、建目录、查元数据、列目录、删除、复制等方法都会先调用它。

调用图:被 8 处调用(canonicalize, copy, create_directory, get_metadata, open_file_for_read, read_directory, remove, write_file);外部调用 1 个(new)。

reject_platform_sandbox_context790–798 ↗
fn reject_platform_sandbox_context(sandbox: Option<&FileSystemSandboxContext>) -> io::Result<()>

作用:检查非沙箱文件系统没有收到“必须进平台沙箱”的请求。它允许普通上下文存在,但不允许真正要求隔离的操作绕过沙箱。

数据流:进去一个可选沙箱上下文 → 如果它要求平台沙箱就返回错误,否则通过 → 出来空成功或 IO 错误。

调用关系:UnsandboxedFileSystem 的各种操作都会先调用它,然后才交给 DirectFileSystem。

调用图:被 10 处调用(canonicalize, copy, create_directory, get_metadata, open_file_for_read, read_directory, read_file, read_file_stream, remove, write_file);外部调用 1 个(new)。

copy_dir_recursive800–817 ↗
fn copy_dir_recursive(source: &Path, target: &Path) -> io::Result<()>

作用:递归复制整个目录。它会把目录里的子目录、普通文件和符号链接都按原样搬到目标位置。

数据流:进去源目录和目标目录 → 先创建目标目录,再逐个遍历源目录条目 → 子目录递归复制,文件直接复制,符号链接调用 copy_symlink → 成功返回空结果,目标目录树被创建。

调用关系:DirectFileSystem::copy 在发现源是目录且允许递归时会用它;它又会在遇到链接时交给 copy_symlink。

调用图:调用 1 个内部函数(copy_symlink);外部调用 4 个(join, copy, create_dir_all, read_dir)。

destination_is_same_or_descendant_of_source819–826 ↗
fn destination_is_same_or_descendant_of_source(
    source: &Path,
    destination: &Path,
) -> io::Result<bool>

作用:判断复制目录的目标是不是源目录自己,或者在源目录里面。这样可以避免把一个目录复制进自己,越复制越多。

数据流:进去源路径和目标路径 → 把源路径规范化,把目标路径中已存在的部分解析出来 → 检查目标是否以源路径开头 → 出来 true 或 false。

调用关系:DirectFileSystem::copy 在复制目录前会调用它;它内部使用 resolve_existing_path 处理目标可能还不存在的情况。

调用图:调用 1 个内部函数(resolve_existing_path);外部调用 2 个(starts_with, canonicalize)。

resolve_existing_path828–847 ↗
fn resolve_existing_path(path: &Path) -> io::Result<PathBuf>

作用:尽量把一个路径解析成真实路径,即使路径最后几级还不存在也能处理。它会先找到最近存在的父路径,再把不存在的尾巴接回去。

数据流:进去一个路径 → 从末尾往前找已经存在的部分,把不存在的文件名暂存起来 → 规范化已存在部分,再按原顺序接回暂存尾巴 → 出来解析后的 PathBuf。

调用关系:current_sandbox_cwd 和 destination_is_same_or_descendant_of_source 会调用它;测试也专门验证它处理符号链接加 .. 时不会误判。

调用图:被 3 处调用(current_sandbox_cwd, destination_is_same_or_descendant_of_source, resolve_existing_path_handles_symlink_parent_dotdot_escape);外部调用 2 个(new, canonicalize)。

current_sandbox_cwd849–853 ↗
fn current_sandbox_cwd() -> io::Result<PathBuf>

作用:获取当前工作目录,并把它解析成更可靠的真实路径。沙箱需要知道当前目录时会用到它。

数据流:进去没有参数 → 读取进程当前目录 → 用 resolve_existing_path 解析它 → 出来 PathBuf;如果读取失败,会包装成更清楚的错误。

调用关系:sandbox_cwd 会调用它;它把“当前目录”这个操作系统状态转换成沙箱可用的路径。

调用图:调用 1 个内部函数(resolve_existing_path);被 1 处调用(sandbox_cwd);外部调用 1 个(current_dir)。

system_time_to_unix_ms889–894 ↗
fn system_time_to_unix_ms(time: SystemTime) -> i64

作用:把操作系统给的时间转换成 Unix 毫秒时间戳。Unix 时间戳就是从 1970 年 1 月 1 日开始算的时间数字。

数据流:进去一个 SystemTime → 计算它距离 UNIX_EPOCH 的时长 → 转成毫秒并尝试放进 i64 → 出来毫秒数;失败或越界时返回 0。

调用关系:DirectFileSystem::get_metadata 用它把创建时间和修改时间变成 FileMetadata 里的数字字段。

调用图:外部调用 1 个(duration_since)。

exec-server/src/remote_file_system.rs源码 ↗
io_transportrequest handling / cross-cutting

这个文件像一个“远程文件管家”。别人要读文件、写文件、建目录、删文件、看目录内容时,不需要知道远端通信细节,只要调用统一的 ExecutorFileSystem 接口。RemoteFileSystem 会先拿到远程客户端,再把路径、沙箱限制和操作参数打包成协议请求发出去。沙箱就是一组安全规则,用来限制能访问哪些文件;这里还会把没必要传给远端的当前目录去掉,避免路径在不同机器上被误解。文件内容通过 base64(一种把二进制数据变成普通文本的编码)传输,防止协议传输时弄坏原始字节。它还把远端错误翻译成本地常见的 io::Error,比如“找不到文件”“输入不合法”“连接断了”,这样上层代码不用关心错误来自本机还是远端。

函数细节17
RemoteFileSystem::new39–42 ↗
fn new(client: LazyRemoteExecServerClient) -> Self

作用:创建一个远程文件系统对象,把之后所有文件操作都绑定到同一个远程 exec-server 客户端上。有人想通过远端执行环境访问文件时,会先用它搭好这个入口。

数据流:进去的是一个 LazyRemoteExecServerClient,也就是“需要时才真正连接”的远程客户端;函数只是把它放进 RemoteFileSystem 里,并记录一条跟踪日志;出来的是一个可用于后续文件操作的 RemoteFileSystem。

调用关系:它是整个远程文件系统的起点。remote_with_transport 等搭建远程执行环境的代码会调用它;之后读、写、删、复制等方法都会通过它保存的 client 去联系远端。

调用图:被 2 处调用(remote_with_transport, remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conversion);外部调用 1 个(trace!)。

RemoteFileSystem::canonicalize229–235 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:把一个路径交给远端确认并整理成标准路径。比如路径里有“..”或符号链接时,需要让真正持有文件的那台机器来判断最终位置。

数据流:进去的是 PathUri(带 URI 形式的路径)和可选沙箱规则;它先取得远程客户端,把路径和整理后的沙箱信息发给远端的 fs_canonicalize;远端返回标准路径后,这里把它交回给调用者。如果远端报错,会先转成本地 io 错误。

调用关系:它实现统一文件系统接口里的“规范化路径”能力。上层只是在问“这个路径最终在哪里”,实际判断被交给远端;过程中会调用 remote_sandbox_context 准备沙箱信息,并依赖 map_remote_error 统一错误。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::read_file237–243 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:从远端读取整个文件内容。它适合一次性把文件全部拿回来,而不是一段一段流式读取。

数据流:进去的是文件路径和可选沙箱;它拿到远程客户端后发送 fs_read_file 请求;远端把文件内容用 base64 文本传回;这里再解码成原始字节 Vec<u8>。如果 base64 内容坏了,会返回“数据无效”的错误。

调用关系:它是上层读取远端文件的常用通道。它先用 remote_sandbox_context 处理访问限制,再把读取工作交给远端;远端错误通过 map_remote_error 变成上层能理解的文件错误。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::read_file_stream245–251 ↗
fn read_file_stream(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:以“边读边传”的方式读取远端文件,适合大文件,避免一次性把全部内容塞进内存。

数据流:进去的是路径和可选沙箱;如果沙箱要求使用平台级隔离,它会直接拒绝,因为这种流式读取暂不支持那种沙箱;否则它拿到远程客户端,调用 file_stream::open 打开一个读取流;出来的是 FileSystemReadStream,调用者可以继续分块读取。

调用关系:它是大文件读取的通道,不直接把所有字节返回,而是把后续读取交给 remote_file_stream 模块。它仍然会准备远端沙箱信息,但在不支持的平台沙箱场景下会提前返回 Unsupported 错误。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 5 个(pin, new, open, trace!, clone)。

RemoteFileSystem::write_file253–260 ↗
fn write_file(
        &'a self,
        path: &'a PathUri,
        contents: Vec<u8>,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:把一段内容写到远端文件里。上层可以像写本地文件一样使用它,但真正落盘发生在远端。

数据流:进去的是目标路径、要写入的字节内容和可选沙箱;它把字节内容编码成 base64 文本,连同路径和沙箱一起发给远端 fs_write_file;远端写完后这里返回空结果,失败则返回转换后的 io 错误。

调用关系:它实现统一文件系统接口里的写文件能力。上层不需要知道网络协议和编码细节;这个函数负责把本地字节包装成远端协议能传输的格式。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::create_directory262–271 ↗
fn create_directory(
        &'a self,
        path: &'a PathUri,
        options: CreateDirectoryOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a,

作用:在远端创建目录。它还能按选项决定是否递归创建,也就是父目录不存在时一起建好。

数据流:进去的是目录路径、CreateDirectoryOptions 和可选沙箱;它取出 recursive 选项,连同路径和沙箱发给远端 fs_create_directory;远端完成后返回空结果,出错时转成本地文件错误。

调用关系:它是远端建目录的接口实现。上层发出“我要这个目录存在”的请求,它把具体动作交给远端 exec-server,并用 remote_sandbox_context 确保权限规则一起传过去。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::get_metadata273–279 ↗
fn get_metadata(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:查询远端某个路径的基本信息,比如它是不是文件、是不是目录、大小和时间戳。

数据流:进去的是路径和可选沙箱;它发送 fs_get_metadata 给远端;远端返回协议里的元数据字段;这里把这些字段装成本地通用的 FileMetadata 后返回。

调用关系:它让上层可以在不读取文件内容的情况下了解文件状态。远端负责实际查看磁盘,本函数负责把远端协议结果翻译成项目内部统一的数据结构。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::read_directory281–287 ↗
fn read_directory(
        &'a self,
        path: &'a PathUri,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:列出远端目录里的条目。调用者可以知道目录下有哪些名字,以及每个名字大致是文件还是目录。

数据流:进去的是目录路径和可选沙箱;它向远端发送 fs_read_directory;远端返回 entries 列表;这里逐个转换成 ReadDirectoryEntry,再把整个列表交回调用者。

调用关系:它实现“看目录内容”的远端版本。上层只处理统一的 ReadDirectoryEntry,不需要关心远端协议返回的原始格式。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::remove289–296 ↗
fn remove(
        &'a self,
        path: &'a PathUri,
        options: RemoveOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:删除远端文件或目录。它支持递归删除和强制删除这类常见选项。

数据流:进去的是目标路径、RemoveOptions 和可选沙箱;它把 recursive、force、路径和沙箱打包成 fs_remove 请求;远端删除完成后返回空结果,失败则把错误翻译成本地 io 错误。

调用关系:它是远端删除操作的接口实现。上层决定要删什么以及怎么删,本函数负责把这些意图准确传给远端,并保持错误表现和本地文件系统一致。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

RemoteFileSystem::copy298–312 ↗
fn copy(
        &'a self,
        source_path: &'a PathUri,
        destination_path: &'a PathUri,
        options: CopyOptions,
        sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> Execut

作用:让远端把一个路径复制到另一个路径。这样复制可以直接发生在远端,不必把文件先拉回本地再传回去。

数据流:进去的是源路径、目标路径、CopyOptions 和可选沙箱;它把源、目标、是否递归和沙箱发给远端 fs_copy;远端复制完成后返回空结果。

调用关系:它适合远端内部复制文件或目录。上层发出复制意图,这个函数只做参数转换和远端调用,真正搬数据的工作留在远端执行,效率更高。

调用图:调用 2 个内部函数(get, remote_sandbox_context);外部调用 3 个(pin, trace!, clone)。

remote_sandbox_context315–321 ↗
fn remote_sandbox_context(
    sandbox: Option<&FileSystemSandboxContext>,
) -> Option<FileSystemSandboxContext>

作用:整理要发给远端的沙箱信息。它会去掉远端不需要的当前工作目录,避免把本机路径误带到远端。

数据流:进去的是可选的 FileSystemSandboxContext;如果没有沙箱,就返回 None;如果有,就先复制一份,再调用 drop_cwd_if_unused,把没被规则需要的 cwd 去掉;出来的是适合远端使用的沙箱上下文。

调用关系:几乎所有远端文件操作在发请求前都会用它。它是一个小但重要的安全和兼容性过滤器,确保权限规则能传过去,同时减少本地路径污染远端请求的机会。

调用图:被 11 处调用(canonicalize, copy, create_directory, get_metadata, read_directory, read_file, read_file_stream, remove, write_file, remote_sandbox_context_drops_unused_cwd (+1 more))。

map_remote_error323–337 ↗
fn map_remote_error(error: ExecServerError) -> io::Error

作用:把 exec-server 的错误翻译成普通文件操作错误。这样上层代码不用懂远端协议的错误码,也能按“文件不存在”“连接断了”等常识处理。

数据流:进去的是 ExecServerError;它检查错误类型和远端错误码:找不到文件变成 NotFound,非法请求变成 InvalidInput,连接关闭或断开变成 BrokenPipe,其他错误变成普通 io 错误;出来的是 io::Error。

调用关系:所有远端调用失败时都会间接依赖它。它夹在远端协议和本地文件系统接口之间,负责把“远端语言”翻译成“本地文件错误语言”。

调用图:外部调用 3 个(new, other, to_string)。

tests::remote_sandbox_context_drops_unused_cwd359–377 ↗
fn remote_sandbox_context_drops_unused_cwd()

作用:测试当沙箱规则不需要当前工作目录时,remote_sandbox_context 会把 cwd 删除。这样可以防止无关的本机目录被发到远端。

数据流:进去的是测试里构造的受限文件权限和一个 cwd;它生成沙箱上下文后调用 remote_sandbox_context;最后检查输出里的 cwd 是 None,说明确实被去掉了。

调用关系:它专门验证 remote_sandbox_context 的一个关键行为。这个行为会影响所有远端文件请求,所以测试能防止以后改代码时不小心又把无用 cwd 传给远端。

调用图:调用 4 个内部函数(remote_sandbox_context, from_permission_profile_with_cwd, from_runtime_permissions, restricted);外部调用 3 个(assert_eq!, path_uri, vec!)。

tests::remote_sandbox_context_preserves_required_cwd380–397 ↗
fn remote_sandbox_context_preserves_required_cwd()

作用:测试当沙箱规则确实需要当前工作目录时,remote_sandbox_context 不会把 cwd 删掉。

数据流:进去的是一个使用“项目根目录”这种特殊路径的权限规则和 cwd;它构造沙箱上下文并调用 remote_sandbox_context;最后确认输出仍然保留原来的 cwd。

调用关系:它和前一个测试配成一组:一个证明该删时会删,一个证明不该删时不会删。这样能保证远端沙箱既不多传无关路径,也不丢掉必要信息。

调用图:调用 4 个内部函数(remote_sandbox_context, from_permission_profile_with_cwd, from_runtime_permissions, restricted);外部调用 3 个(assert_eq!, path_uri, vec!)。

tests::transport_errors_map_to_broken_pipe400–427 ↗
fn transport_errors_map_to_broken_pipe()

作用:测试远端连接关闭或断开时,会被统一看成本地的 BrokenPipe 错误。BrokenPipe 可以理解为“管道断了,消息送不过去了”。

数据流:进去的是两个传输层错误:Closed 和 Disconnected;测试把它们分别交给 map_remote_error;出来后检查错误种类都是 BrokenPipe,错误文字也一致。

调用关系:它验证 map_remote_error 对连接故障的翻译。这样上层在远端进程没了、连接断了时,可以用同一种方式处理,而不用区分很多底层原因。

调用图:外部调用 2 个(assert_eq!, Disconnected)。

tests::absolute_test_path429–432 ↗
fn absolute_test_path(name: &str) -> AbsolutePathBuf

作用:给测试生成一个临时目录下的绝对路径。测试需要合法的绝对路径来构造 PathUri 和沙箱规则。

数据流:进去的是一个名字;它把这个名字接到系统临时目录后面,再转换成 AbsolutePathBuf;出来的是测试可用的绝对路径。

调用关系:它是测试辅助函数,被 path_uri 和测试里的权限规则构造过程使用。它不参与真实运行,只是让测试代码少重复写路径准备步骤。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 1 个(temp_dir)。

tests::path_uri434–436 ↗
fn path_uri(name: &str) -> PathUri

作用:给测试生成一个 PathUri。PathUri 可以理解为项目里用来表达路径的统一格式,不直接裸用系统路径字符串。

数据流:进去的是一个名字;它先调用 absolute_test_path 得到绝对路径,再把绝对路径转换成 PathUri;出来的是测试可传给沙箱或远程文件系统逻辑的路径 URI。

调用关系:它是测试里的小工具,帮助 remote_sandbox_context 相关测试快速准备 cwd。它依赖 absolute_test_path,把普通名字一步变成项目内部需要的路径表示。

调用图:调用 1 个内部函数(from_abs_path);外部调用 1 个(absolute_test_path)。