microsandbox_protocol/fs.rs
1//! Filesystem-related protocol message payloads.
2
3use serde::{Deserialize, Serialize};
4
5use crate::bulk::BulkOffer;
6
7//--------------------------------------------------------------------------------------------------
8// Constants
9//--------------------------------------------------------------------------------------------------
10
11/// Maximum chunk size for streaming file data (3 MiB).
12///
13/// This stays safely under the 4 MiB frame limit after CBOR envelope overhead.
14pub const FS_CHUNK_SIZE: usize = 3 * 1024 * 1024;
15
16//--------------------------------------------------------------------------------------------------
17// Types
18//--------------------------------------------------------------------------------------------------
19
20/// A filesystem operation requested by the host.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub enum FsOp {
23 /// Resolve a path to its canonical absolute form.
24 RealPath {
25 /// Guest path to resolve.
26 path: String,
27 },
28
29 /// Get metadata for a path.
30 Stat {
31 /// Guest path to stat.
32 path: String,
33
34 /// Whether to follow symlinks.
35 follow_symlink: bool,
36 },
37
38 /// Update metadata for a path.
39 SetStat {
40 /// Guest path to update.
41 path: String,
42
43 /// Whether to follow symlinks.
44 follow_symlink: bool,
45
46 /// Attributes to update.
47 attrs: FsSetAttrs,
48 },
49
50 /// List directory contents.
51 List {
52 /// Guest directory path to list.
53 path: String,
54 },
55
56 /// Read a symlink target.
57 ReadLink {
58 /// Guest symlink path to read.
59 path: String,
60 },
61
62 /// Create a symlink.
63 Symlink {
64 /// Symlink target.
65 target: String,
66
67 /// Symlink path to create.
68 link_path: String,
69 },
70
71 /// Create a directory (and parents).
72 Mkdir {
73 /// Guest directory path to create.
74 path: String,
75
76 /// Permission bits to set on creation (e.g. 0o755).
77 #[serde(default)]
78 mode: Option<u32>,
79 },
80
81 /// Remove a file.
82 Remove {
83 /// Guest file path to remove.
84 path: String,
85 },
86
87 /// Remove a directory.
88 RemoveDir {
89 /// Guest directory path to remove.
90 path: String,
91
92 /// Whether to remove recursively.
93 recursive: bool,
94 },
95
96 /// Copy a file or directory within the guest.
97 Copy {
98 /// Source path in guest.
99 src: String,
100 /// Destination path in guest.
101 dst: String,
102 },
103
104 /// Rename/move a file or directory.
105 Rename {
106 /// Source path in guest.
107 src: String,
108 /// Destination path in guest.
109 dst: String,
110 },
111
112 /// Open a file and allocate an agentd-side handle.
113 OpenFile {
114 /// Guest file path to open.
115 path: String,
116
117 /// File open options.
118 options: FsOpenOptions,
119 },
120
121 /// Open a directory and allocate an agentd-side handle.
122 OpenDir {
123 /// Guest directory path to open.
124 path: String,
125 },
126
127 /// Close a file or directory handle.
128 CloseHandle {
129 /// Agentd-side handle.
130 handle: u64,
131 },
132
133 /// Read from an open file handle.
134 Read {
135 /// Agentd-side file handle.
136 handle: u64,
137
138 /// Byte offset to read from.
139 offset: u64,
140
141 /// Maximum bytes to read. `None` means read to EOF.
142 len: Option<u64>,
143 },
144
145 /// Write to an open file handle.
146 Write {
147 /// Agentd-side file handle.
148 handle: u64,
149
150 /// Byte offset to write at.
151 offset: u64,
152
153 /// Expected byte count. `None` disables count validation.
154 len: Option<u64>,
155 },
156
157 /// Read the next batch of entries from an open directory handle.
158 ReadDir {
159 /// Agentd-side directory handle.
160 handle: u64,
161
162 /// Maximum entries to return. `None` uses the agent default.
163 limit: Option<u32>,
164 },
165
166 /// Get metadata for an open file or directory handle.
167 FStat {
168 /// Agentd-side handle.
169 handle: u64,
170 },
171
172 /// Update metadata for an open file handle.
173 FSetStat {
174 /// Agentd-side handle.
175 handle: u64,
176
177 /// Attributes to update.
178 attrs: FsSetAttrs,
179 },
180}
181
182/// Attributes accepted by setstat-style filesystem operations.
183#[derive(Debug, Clone, Default, Serialize, Deserialize)]
184pub struct FsSetAttrs {
185 /// Unix permission bits.
186 pub mode: Option<u32>,
187
188 /// Owner user ID.
189 pub uid: Option<u32>,
190
191 /// Owner group ID.
192 pub gid: Option<u32>,
193
194 /// File size.
195 pub size: Option<u64>,
196
197 /// Access time as Unix timestamp seconds.
198 pub atime: Option<i64>,
199
200 /// Modification time as Unix timestamp seconds.
201 pub mtime: Option<i64>,
202}
203
204/// Options used when opening a file handle.
205#[derive(Debug, Clone, Default, Serialize, Deserialize)]
206pub struct FsOpenOptions {
207 /// Open for reading.
208 pub read: bool,
209
210 /// Open for writing.
211 pub write: bool,
212
213 /// Append writes to the end.
214 pub append: bool,
215
216 /// Create the file if it is missing.
217 pub create: bool,
218
219 /// Truncate the file after opening.
220 pub truncate: bool,
221
222 /// Create a new file and fail if it already exists.
223 pub create_new: bool,
224
225 /// Permission bits to set on creation.
226 pub mode: Option<u32>,
227}
228
229/// Request to perform a filesystem operation in the guest.
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct FsRequest {
232 /// The operation to perform.
233 pub op: FsOp,
234
235 /// Generation-8 raw-bulk offer for streaming reads and writes.
236 #[serde(default, skip_serializing_if = "Option::is_none")]
237 pub bulk: Option<BulkOffer>,
238}
239
240/// Metadata about a filesystem entry (wire format).
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct FsEntryInfo {
243 /// Path of the entry.
244 pub path: String,
245
246 /// Kind of entry: `"file"`, `"dir"`, `"symlink"`, or `"other"`.
247 pub kind: String,
248
249 /// Size in bytes.
250 pub size: u64,
251
252 /// Unix permission bits.
253 pub mode: u32,
254
255 /// Last modification time as Unix timestamp (seconds since epoch).
256 pub modified: Option<i64>,
257
258 /// Owner user ID.
259 pub uid: u32,
260
261 /// Owner group ID.
262 pub gid: u32,
263
264 /// Last access time as Unix timestamp (seconds since epoch).
265 pub atime: Option<i64>,
266
267 /// Last modification time as Unix timestamp (seconds since epoch).
268 pub mtime: Option<i64>,
269}
270
271/// Data variants that can be included in a filesystem response.
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub enum FsResponseData {
274 /// Stat result.
275 Stat(FsEntryInfo),
276
277 /// Directory listing result.
278 List(Vec<FsEntryInfo>),
279
280 /// Open handle.
281 Handle(u64),
282
283 /// Resolved path or symlink target.
284 Path(String),
285}
286
287/// Terminal response for a filesystem operation.
288///
289/// This is always the last message sent for a given correlation ID.
290/// For streaming reads, it follows the `FsData` chunks.
291/// For simple operations, it carries the result directly.
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct FsResponse {
294 /// Whether the operation succeeded.
295 pub ok: bool,
296
297 /// Error message if `ok` is false.
298 #[serde(default)]
299 pub error: Option<String>,
300
301 /// Optional result data (for stat/list operations).
302 #[serde(default)]
303 pub data: Option<FsResponseData>,
304}
305
306/// A chunk of file data for streaming read/write operations.
307///
308/// An empty `data` field signals EOF (like `ExecStdin` with empty data).
309#[derive(Debug, Serialize, Deserialize)]
310pub struct FsData {
311 /// The raw file data.
312 #[serde(with = "serde_bytes")]
313 pub data: Vec<u8>,
314}