Skip to main content

strate_net_silo/
ipc.rs

1//! Typed helpers to build and parse the VFS scheme wire protocol.
2//!
3//! Every function here delegates to the typed ABI layer
4//! (`strat9_abi::ipc_payload`) instead of hand-rolled payload offsets,
5//! so the wire contract has a single source of truth.
6
7use strat9_abi::ipc_payload::{
8    OpenReply, OpenRequest, ReadReply, ReadRequest, WriteReply, WriteRequest,
9};
10use strat9_syscall::data::{
11    IpcMessage, IPC_FILE_FLAG_CHUNK_READ, IPC_FILE_FLAG_CHUNK_WRITE, IPC_FILE_FLAG_PIPE,
12};
13
14pub(crate) const HANDLE_FLAG_INFO: u32 = IPC_FILE_FLAG_CHUNK_READ;
15pub(crate) const HANDLE_FLAG_CONTROL: u32 = 0;
16pub(crate) const HANDLE_FLAG_STREAM: u32 =
17    IPC_FILE_FLAG_PIPE | IPC_FILE_FLAG_CHUNK_READ | IPC_FILE_FLAG_CHUNK_WRITE;
18pub(crate) const HANDLE_FLAG_DATAGRAM: u32 = IPC_FILE_FLAG_CHUNK_READ;
19
20/// Errno value returned for malformed requests (`EINVAL`).
21const EINVAL: i32 = -22;
22
23pub(crate) fn reply_open(sender: u64, file_id: u64, size: u64, flags: u32) -> IpcMessage {
24    let mut msg = IpcMessage::new(IpcMessage::REPLY_MSG_TYPE);
25    msg.sender = sender;
26    OpenReply {
27        status: 0,
28        file_id,
29        size,
30        file_flags: flags,
31    }
32    .encode_into(&mut msg.payload);
33    msg
34}
35
36pub(crate) fn reply_read(sender: u64, data: &[u8]) -> IpcMessage {
37    let (msg, _n) = ReadReply::encode_ok(sender, data);
38    msg
39}
40
41pub(crate) fn reply_write(sender: u64, n: usize) -> IpcMessage {
42    WriteReply::encode_ok(sender, n)
43}
44
45pub(crate) fn reply_write_data_len(sender: u64, msg: &IpcMessage) -> IpcMessage {
46    match WriteRequest::parse_prefix(&msg.payload) {
47        Some(req) => reply_write(sender, req.data_len as usize),
48        None => reply_write(sender, 0),
49    }
50}
51
52pub(crate) fn reply_ok(sender: u64) -> IpcMessage {
53    IpcMessage::status_reply(sender, 0)
54}
55
56/// Extract the path from an OPEN request, with any leading `/` trimmed.
57pub(crate) fn open_path(msg: &IpcMessage) -> Result<&str, i32> {
58    let (_flags, path) = OpenRequest::parse(&msg.payload).ok_or(EINVAL)?;
59    if path.len() > IpcMessage::OPEN_INLINE_CAPACITY {
60        return Err(EINVAL);
61    }
62    Ok(path.trim_start_matches('/'))
63}
64
65pub(crate) fn message_file_id(msg: &IpcMessage) -> u64 {
66    // Used for CLOSE-style requests whose payload is a single `ino: u64`.
67    u64::from_le_bytes(msg.payload[0..8].try_into().unwrap_or([0u8; 8]))
68}
69
70/// Parse a READ request payload into `(file_id, offset, requested_count)`.
71pub(crate) fn read_request(msg: &IpcMessage) -> (u64, u64, usize) {
72    match ReadRequest::parse(&msg.payload) {
73        Some(req) => (req.ino, req.offset, req.count_usize()),
74        None => (0, 0, 0),
75    }
76}
77
78pub(crate) fn write_data(msg: &IpcMessage) -> &[u8] {
79    WriteRequest::parse_prefix(&msg.payload)
80        .and_then(|req| req.data(&msg.payload))
81        .unwrap_or(&[])
82}