Skip to main content

open_confined_write

Function open_confined_write 

Source
pub fn open_confined_write(path: &Path) -> Result<File>
Expand description

Opens path for writing, refusing to follow a pre-existing symlink planted at that exact location.

The same guard write_confined_file applies, factored out as its own blocking, synchronous primitive. write_confined_file stages content in memory and writes it to its final path in one call; a caller that instead needs to stage into a separate .tmp path and rename it into place (e.g. mcp-execution-fileswrite_file_atomic) cannot reuse that function directly, since the symlink race it closes is specific to the exact path passed in — here, the .tmp staging path rather than the final one (issue #504). Exposing this primitive lets both crates share one guard instead of each hand-rolling its own.

On Unix, the open that creates or truncates path carries O_NOFOLLOW, so the kernel rejects a symlinked terminal component (dangling or not) as part of that same syscall. Windows has no equivalent flag usable together with create(true).truncate(true) (see write_confined_file’s doc comment for why), so it relies solely on a std::fs::symlink_metadata pre-check with no yield point before the open — this narrows but does not close the window against a symlink planted by a racing process between the two.

A pre-existing regular file is still opened and truncated normally: this only rejects a symlink at the final path component, not an ordinary overwrite.

§Errors

Returns an error if the symlink check (Windows only) or the open failed — including the platform’s “too many levels of symbolic links” error on Unix when path’s terminal component is a symlink.

§Examples

use mcp_execution_core::open_confined_write;
use std::io::Write;
use tempfile::TempDir;

let dir = TempDir::new().unwrap();
let path = dir.path().join("staged.tmp");
let mut file = open_confined_write(&path).unwrap();
file.write_all(b"content").unwrap();