Skip to main content

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}