Skip to main content

capsec_std/
fs.rs

1//! Capability-gated filesystem operations.
2//!
3//! Drop-in replacements for `std::fs` functions that require a capability token
4//! proving the caller has filesystem permission.
5//!
6//! # Example
7//!
8//! ```no_run
9//! # use capsec_core::root::test_root;
10//! # use capsec_core::permission::FsRead;
11//! # use capsec_core::cap_provider::CapProvider;
12//! let root = test_root();
13//! let cap = root.grant::<FsRead>();
14//! let data = capsec_std::fs::read("/tmp/data.bin", &cap).unwrap();
15//! ```
16
17use crate::file::{ReadFile, WriteFile};
18use capsec_core::cap::Cap;
19use capsec_core::cap_provider::CapProvider;
20use capsec_core::error::CapSecError;
21use capsec_core::permission::{FsRead, FsWrite};
22use std::path::Path;
23
24/// Reads the entire contents of a file into a byte vector.
25/// Requires [`FsRead`] permission.
26pub fn read(
27    path: impl AsRef<Path>,
28    cap: &impl CapProvider<FsRead>,
29) -> Result<Vec<u8>, CapSecError> {
30    let _proof: Cap<FsRead> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
31    Ok(std::fs::read(path)?)
32}
33
34/// Reads the entire contents of a file into a string.
35/// Requires [`FsRead`] permission.
36pub fn read_to_string(
37    path: impl AsRef<Path>,
38    cap: &impl CapProvider<FsRead>,
39) -> Result<String, CapSecError> {
40    let _proof: Cap<FsRead> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
41    Ok(std::fs::read_to_string(path)?)
42}
43
44/// Returns an iterator over the entries within a directory.
45/// Requires [`FsRead`] permission.
46pub fn read_dir(
47    path: impl AsRef<Path>,
48    cap: &impl CapProvider<FsRead>,
49) -> Result<std::fs::ReadDir, CapSecError> {
50    let _proof: Cap<FsRead> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
51    Ok(std::fs::read_dir(path)?)
52}
53
54/// Returns metadata about a file or directory.
55/// Requires [`FsRead`] permission.
56pub fn metadata(
57    path: impl AsRef<Path>,
58    cap: &impl CapProvider<FsRead>,
59) -> Result<std::fs::Metadata, CapSecError> {
60    let _proof: Cap<FsRead> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
61    Ok(std::fs::metadata(path)?)
62}
63
64/// Writes bytes to a file, creating it if it doesn't exist, truncating if it does.
65/// Requires [`FsWrite`] permission.
66pub fn write(
67    path: impl AsRef<Path>,
68    contents: impl AsRef<[u8]>,
69    cap: &impl CapProvider<FsWrite>,
70) -> Result<(), CapSecError> {
71    let _proof: Cap<FsWrite> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
72    Ok(std::fs::write(path, contents)?)
73}
74
75/// Creates all directories in the given path if they don't exist.
76/// Requires [`FsWrite`] permission.
77pub fn create_dir_all(
78    path: impl AsRef<Path>,
79    cap: &impl CapProvider<FsWrite>,
80) -> Result<(), CapSecError> {
81    let _proof: Cap<FsWrite> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
82    Ok(std::fs::create_dir_all(path)?)
83}
84
85/// Deletes a file.
86/// Requires [`FsWrite`] permission.
87pub fn remove_file(
88    path: impl AsRef<Path>,
89    cap: &impl CapProvider<FsWrite>,
90) -> Result<(), CapSecError> {
91    let _proof: Cap<FsWrite> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
92    Ok(std::fs::remove_file(path)?)
93}
94
95/// Recursively deletes a directory and all its contents.
96/// Requires [`FsWrite`] permission.
97pub fn remove_dir_all(
98    path: impl AsRef<Path>,
99    cap: &impl CapProvider<FsWrite>,
100) -> Result<(), CapSecError> {
101    let _proof: Cap<FsWrite> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
102    Ok(std::fs::remove_dir_all(path)?)
103}
104
105/// Renames a file or directory.
106/// Requires [`FsWrite`] permission.
107///
108/// Both source and destination paths are checked against the capability's scope.
109pub fn rename(
110    from: impl AsRef<Path>,
111    to: impl AsRef<Path>,
112    cap: &impl CapProvider<FsWrite>,
113) -> Result<(), CapSecError> {
114    let _proof: Cap<FsWrite> = cap.provide_cap(&from.as_ref().to_string_lossy())?;
115    let _proof2: Cap<FsWrite> = cap.provide_cap(&to.as_ref().to_string_lossy())?;
116    Ok(std::fs::rename(from, to)?)
117}
118
119/// Copies a file. Requires both [`FsRead`] and [`FsWrite`] permissions
120/// (passed as separate capability tokens).
121///
122/// The read capability is checked against the source path, and the write
123/// capability is checked against the destination path.
124pub fn copy(
125    from: impl AsRef<Path>,
126    to: impl AsRef<Path>,
127    read_cap: &impl CapProvider<FsRead>,
128    write_cap: &impl CapProvider<FsWrite>,
129) -> Result<u64, CapSecError> {
130    let _read_proof: Cap<FsRead> = read_cap.provide_cap(&from.as_ref().to_string_lossy())?;
131    let _write_proof: Cap<FsWrite> = write_cap.provide_cap(&to.as_ref().to_string_lossy())?;
132    Ok(std::fs::copy(from, to)?)
133}
134
135/// Opens a file for reading. Returns a [`ReadFile`] that implements `Read` + `Seek`
136/// but NOT `Write`, enforcing the capability boundary beyond the function call.
137/// Requires [`FsRead`] permission.
138pub fn open(
139    path: impl AsRef<Path>,
140    cap: &impl CapProvider<FsRead>,
141) -> Result<ReadFile, CapSecError> {
142    let _proof: Cap<FsRead> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
143    Ok(ReadFile::new(std::fs::File::open(path)?))
144}
145
146/// Creates or truncates a file for writing. Returns a [`WriteFile`] that implements
147/// `Write` + `Seek` but NOT `Read`, enforcing the capability boundary beyond the function call.
148/// Requires [`FsWrite`] permission.
149pub fn create(
150    path: impl AsRef<Path>,
151    cap: &impl CapProvider<FsWrite>,
152) -> Result<WriteFile, CapSecError> {
153    let _proof: Cap<FsWrite> = cap.provide_cap(&path.as_ref().to_string_lossy())?;
154    Ok(WriteFile::new(std::fs::File::create(path)?))
155}