Skip to main content

microsandbox_protocol/
fs.rs

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