Request

The payload of the channel request: the tag byte 1 and the request of the channel.

[1][path_len: u16, big-endian][path …][offset: u64, big-endian][bytes …]
  • The form. The bytes after the tag are exactly the bytes after the tag of the FUSE write ask on a mount scope, and mean the same; that page states every field. The path is UTF-8, /-separated, with no leading /; empty is the root of the served subtree.
  • Malformed. A channel request whose payload does not decode as stated is answered by a channel response finish that no channel response precedes, and the server does nothing else for it.

The channel request is defined by diverge-sdk-rs/src/provider/endpoints/containers/fuse_serve/client/channel_request/frame.rs:

//! What a client's channel request frame carries on a container
//! FUSE serve scope.

/// One of the nine asks, the stop, or the tree: the
/// [`volumes::fuse_serve`](crate::provider::endpoints::volumes::fuse_serve) scope's
/// channel request, byte for byte — `0`–`8` the mount's asks, `9`
/// the stop, `10` the tree — answered here from a container rather
/// than a volume. See [`Frame`](crate::provider::endpoints::volumes::fuse_serve::client::channel_request::Frame)
/// for every tag.
///
/// An alias rather than a re-export because this module is real: the
/// path says this scope's channel request lives here, and it does,
/// rather than naming somewhere else and hoping a reader follows.
/// What the alias points at is right there in the signature. One
/// type rather than two because the whole point is that a bridge
/// forwards bytes: an ask a mount makes goes to a volume or to a
/// container in the same frame, and nothing is re-encoded on the way.
pub type Frame<'a> = crate::provider::endpoints::volumes::fuse_serve::client::channel_request::Frame<'a>;

/// A container FUSE serve channel request that could not be read: the
/// volume FUSE serve's own failures. See
/// [`FrameError`](crate::provider::endpoints::volumes::fuse_serve::client::channel_request::FrameError).
pub type FrameError = crate::provider::endpoints::volumes::fuse_serve::client::channel_request::FrameError;

and by diverge-sdk-rs/src/container_proxy/outside/endpoints/fuse/mount/server/channel_request/write.rs:

//! Write a piece of a mounted file.

use std::str;

use super::{FrameEncodeError, FrameError, prefixed};
use crate::wire::encode::{Encode, Writer};

/// Write a piece of a file of the mount in place at `offset`, by its
/// path. Answered with one
/// [`Ack`](crate::shared::containers::fuse::ack::Frame).
///
/// ```text
/// [path_len: u16 BE][path: utf8…][offset: u64 BE][bytes…]
/// ```
///
/// The bytes follow the path, so the path carries a length prefix
/// here. On a file mount the path is empty. One `write(2)`, one ask:
/// a file that does not exist yet is made by this, one shorter than
/// the offset is extended to it, and the bytes at and after the
/// offset are replaced.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Write<'a> {
    /// The file's path inside the mount; empty for a file mount.
    pub path: &'a str,
    /// Where the piece lands, in bytes from the file's start.
    pub offset: u64,
    /// The piece, verbatim. Empty writes nothing and makes the file.
    pub bytes: &'a [u8],
}

impl Encode for Write<'_> {
    /// One way to fail: a path longer than its prefix holds.
    type Error = FrameEncodeError;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), FrameEncodeError> {
        prefixed::put(out, self.path.as_bytes())?;
        out.extend_from_slice(&self.offset.to_be_bytes());
        out.extend_from_slice(self.bytes);
        Ok(())
    }
}

impl<'a> Write<'a> {
    /// Decode from the bytes after the ask's tag. The path and the
    /// bytes borrow from `bytes`.
    pub fn decode(bytes: &'a [u8]) -> Result<Self, FrameError> {
        let (path, rest) = prefixed::take(bytes)?;
        let offset: [u8; 8] = rest.get(..8).and_then(|head| head.try_into().ok()).ok_or(FrameError::Truncated)?;
        Ok(Write {
            path: str::from_utf8(path).map_err(|_| FrameError::PathUtf8)?,
            offset: u64::from_be_bytes(offset),
            bytes: &rest[8..],
        })
    }
}