Enable writable file streaming in exec-server (#50177)

## What changed

Advertise `fileWriteStreaming` and support `fs/open` with `mode: "replace"` to create or truncate a file for writing. Implement `fs/writeBlock` with explicit offsets and nonempty chunks of up to 1 MiB, rejecting ranges beyond the signed 64-bit file offset limit.

Apply write sandbox permissions when opening writable handles, including when full-disk reads are allowed. Keep `read` as the default open mode.

## Testing

Add integration coverage for positional writes, chunk and offset bounds, replacement truncation, read/write handle permissions, and sandbox write restrictions. Verify that duplicate handle IDs and exhausted handle capacity reject replacement opens without creating or truncating files.

GitOrigin-RevId: 696ee6437dd5f5b69d5a7667dddea0ff44472094
This commit is contained in:
Adam Perry @ OpenAI
2026-10-02 04:25:52 +00:00
committed by copyberry
parent 7135b303d9
commit c39bfa4c8f
14 changed files with 425 additions and 50 deletions
@@ -28,7 +28,6 @@ use crate::protocol::FsCreateDirectoryParams;
use crate::protocol::FsCreateDirectoryResponse;
use crate::protocol::FsGetMetadataParams;
use crate::protocol::FsGetMetadataResponse;
use crate::protocol::FsOpenMode;
use crate::protocol::FsOpenParams;
use crate::protocol::FsOpenResponse;
use crate::protocol::FsReadBlockParams;
@@ -125,18 +124,12 @@ impl FileSystemHandler {
params: FsOpenParams,
) -> Result<FsOpenResponse, JSONRPCErrorError> {
validate_file_handle_id(&params.handle_id)?;
// TODO(anp): Enable replacement opens when writable file streams are implemented.
if params.mode == FsOpenMode::Replace {
return Err(invalid_request(
"exec-server does not support writable file streams".to_string(),
));
}
let handle_id = self
.file_handles
.open(
params.handle_id,
self.file_system
.open_file(&params.path, params.sandbox.as_ref()),
.open_file(&params.path, params.mode, params.sandbox.as_ref()),
)
.await
.map_err(map_fs_error)?;
@@ -164,10 +157,11 @@ impl FileSystemHandler {
params: FsWriteBlockParams,
) -> Result<FsWriteBlockResponse, JSONRPCErrorError> {
validate_file_handle_id(&params.handle_id)?;
// TODO(anp): Implement positional writes before advertising writable file streams.
Err(invalid_request(
"exec-server does not support writable file streams".to_string(),
))
self.file_handles
.write_block(&params.handle_id, params.offset, params.chunk.into_inner())
.await
.map_err(map_fs_error)?;
Ok(FsWriteBlockResponse {})
}
pub(crate) async fn close(
@@ -357,7 +351,7 @@ impl FileSystemHandler {
fn validate_file_handle_id(handle_id: &str) -> Result<(), JSONRPCErrorError> {
if handle_id.len() > MAX_FILE_HANDLE_ID_BYTES {
return Err(invalid_request(format!(
"file read handle ID must not exceed {MAX_FILE_HANDLE_ID_BYTES} bytes"
"file handle ID must not exceed {MAX_FILE_HANDLE_ID_BYTES} bytes"
)));
}
Ok(())
@@ -383,7 +377,6 @@ mod tests {
use super::*;
use crate::FileSystemSandboxContext;
use crate::protocol::FsReadFileParams;
use crate::protocol::FsWriteFileParams;
#[tokio::test]