Request
The payload of the channel request: the tag byte 7 and the request of the channel.
[7][size: u64, big-endian][path …]
- The form. The bytes after the tag are exactly the bytes after
the tag of the FUSE truncate 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 volume. - 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/volumes/fuse_serve/client/channel_request/frame.rs:
//! What a client's channel request frame carries on a FUSE serve scope.
use std::fmt;
use crate::container_proxy::outside::endpoints::fuse::mount::server::channel_request as mount;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// What a client asks the provider for while a volume is served: one
/// of the nine asks, the stop, or the tree.
///
/// A payload leads with one byte saying which. The nine asks carry
/// the tags they carry on a mount's scope — `0` read, `1` write, `2`
/// list, `3` remove, `4` rename, `5` mkdir, `6` stat, `7` truncate,
/// `8` setattr — and are that scope's very frames, so a bridge
/// forwards bytes; `9` is the stop, bare; `10` is the tree, bare.
///
/// | tag | asks for |
/// |-----|----------|
/// | `0`–`8` | [`Ask`](Self::Ask): the mount's ask of that tag |
/// | `9` | [`Stop`](Self::Stop) |
/// | `10` | [`Filetree`](Self::Filetree) |
#[derive(Debug, Clone, PartialEq)]
pub enum Frame<'a> {
/// One of the nine, answered on its channel with that ask's own
/// frame — the shared vocabulary's — and the finish. The provider
/// answers as a caller's server would, from the volume.
Ask(mount::Frame<'a>),
/// Stop serving. Tag `9`.
///
/// Carries nothing, and nothing comes back on this channel: the
/// provider sends no channel response and no channel response
/// finish on it. What comes back is the scope's finish, after
/// every ask still being answered has been: the provider gives
/// the volume back and finishes. A second stop changes nothing.
Stop,
/// The tree of the volume as this FUSE serve sees it, and then every
/// change to it. Tag `10`.
///
/// Carries nothing: the scope is the volume, and the root is the
/// volume's. Answered on its channel with a
/// [`filetree`](crate::shared::filetree) stream — one snapshot of
/// the served view, the FUSE serve's layer included, then one frame
/// per change the FUSE serve's own asks make — for as long as the
/// scope lives; the channel finishes with the scope. A read-only
/// volume's stream is its snapshot and nothing after. Any number
/// of these may be open at once, each a stream of its own
/// beginning whole.
Filetree,
}
/// Tag for [`Frame::Stop`].
const STOP: u8 = 9;
/// Tag for [`Frame::Filetree`].
const FILETREE: u8 = 10;
impl Encode for Frame<'_> {
/// The ask's own failure; the stop and the tree cannot fail.
type Error = mount::FrameEncodeError;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
match self {
Frame::Ask(ask) => ask.encode(out),
Frame::Stop => {
out.extend_from_slice(&[STOP]);
Ok(())
}
Frame::Filetree => {
out.extend_from_slice(&[FILETREE]);
Ok(())
}
}
}
}
impl<'a> Decode<'a> for Frame<'a> {
/// The ask's own failures, and the empty payload.
type Error = FrameError;
fn decode(bytes: &'a [u8]) -> Result<Self, Self::Error> {
match bytes.first() {
None => Err(FrameError::Empty),
Some(&STOP) => Ok(Frame::Stop),
Some(&FILETREE) => Ok(Frame::Filetree),
Some(_) => mount::Frame::decode(bytes).map(Frame::Ask).map_err(FrameError::Ask),
}
}
}
/// A FUSE serve channel request that could not be read.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrameError {
/// No bytes at all, so not even a tag.
Empty,
/// The ask did not decode: an unknown tag, or fewer bytes than it
/// promises.
Ask(mount::FrameError),
}
impl fmt::Display for FrameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FrameError::Empty => f.write_str("volume FUSE serve channel request frame is empty"),
FrameError::Ask(error) => write!(f, "volume FUSE serve ask did not decode: {error}"),
}
}
}
impl std::error::Error for FrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FrameError::Ask(error) => Some(error),
FrameError::Empty => None,
}
}
}
and by diverge-sdk-rs/src/container_proxy/outside/endpoints/fuse/mount/server/channel_request/truncate.rs:
//! Truncate a mounted file.
use std::str;
use super::FrameError;
use crate::wire::encode::{Encode, Writer};
/// Set a file of the mount to `size` bytes, by its path. Answered
/// with one [`Ack`](crate::shared::containers::fuse::ack::Frame).
///
/// ```text
/// [size: u64 BE][path: utf8…]
/// ```
///
/// The size precedes the path, so the path runs to the end and needs
/// no prefix. On a file mount the path is empty.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Truncate<'a> {
/// The file's path inside the mount; empty for a file mount.
pub path: &'a str,
/// The length the file has after, in bytes.
pub size: u64,
}
impl Encode for Truncate<'_> {
/// [`Infallible`](std::convert::Infallible): eight bytes and a
/// path's own.
type Error = std::convert::Infallible;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
out.extend_from_slice(&self.size.to_be_bytes());
out.extend_from_slice(self.path.as_bytes());
Ok(())
}
}
impl<'a> Truncate<'a> {
/// Decode from the bytes after the ask's tag. The path borrows
/// from `bytes`.
pub fn decode(bytes: &'a [u8]) -> Result<Self, FrameError> {
let size: [u8; 8] = bytes.get(..8).and_then(|head| head.try_into().ok()).ok_or(FrameError::Truncated)?;
Ok(Truncate {
path: str::from_utf8(&bytes[8..]).map_err(|_| FrameError::PathUtf8)?,
size: u64::from_be_bytes(size),
})
}
}