// SPDX-License-Identifier: AGPL-3.0-or-later use super::{Handle, Inode}; use btlib::{ bterr, crypto::ConcretePub, BlockMetaSecrets, DirEntry, DirEntryKind, Epoch, IssuedProcRec, }; use btmsg::CallMsg; use core::time::Duration; use paste::paste; use serde::{Deserialize, Serialize}; use std::{ fmt::Display, io, ops::{BitOr, BitOrAssign}, }; #[derive(Serialize, Deserialize)] pub enum FsMsg<'a> { #[serde(borrow)] Lookup(Lookup<'a>), #[serde(borrow)] Create(Create<'a>), Open(Open), Read(Read), #[serde(borrow)] Write(Write<&'a [u8]>), Flush(Flush), ReadDir(ReadDir), #[serde(borrow)] Link(Link<'a>), #[serde(borrow)] Unlink(Unlink<'a>), ReadMeta(ReadMeta), WriteMeta(WriteMeta), Allocate(Allocate), Close(Close), Forget(Forget), Lock(Lock), Unlock(Unlock), AddReadcap(AddReadcap), GrantAccess(GrantAccess), } #[derive(Serialize, Deserialize)] pub enum FsReply<'a> { Ack(()), Lookup(LookupReply), Create(CreateReply), Open(OpenReply), #[serde(borrow)] Read(ReadReply<'a>), Write(WriteReply), ReadDir(ReadDirReply), Link(LinkReply), ReadMeta(ReadMetaReply), WriteMeta(WriteMetaReply), } impl<'a> CallMsg<'a> for FsMsg<'a> { type Reply<'b> = FsReply<'b>; } #[derive(Debug, Serialize, Deserialize)] pub enum FsError { Other, } impl Display for FsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Other => write!(f, "uncategorized error"), } } } impl std::error::Error for FsError {} #[repr(u64)] /// An enumeration of special Inodes. pub enum SpecInodes { RootDir = 1, Sb = 2, FirstFree = 11, } impl SpecInodes { pub fn value(self) -> Inode { self as Inode } } impl From for Inode { fn from(special: SpecInodes) -> Self { special.value() } } #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[repr(u32)] // This type needs to match `libc::mode_t`. /// The type of a file (regular, directory, etc). pub enum FileType { /// Directory. Dir = libc::S_IFDIR, /// Regular file. Reg = libc::S_IFREG, } impl FileType { /// Returns the underlying mode bits for this file type. pub fn value(self) -> libc::mode_t { self as libc::mode_t } /// Attempts to convert the given mode bits into a `FileType` enum value. pub fn from_value(value: libc::mode_t) -> btlib::Result { if (value & libc::S_IFDIR) != 0 { return Ok(FileType::Dir); } if (value & libc::S_IFREG) != 0 { return Ok(FileType::Reg); } Err(bterr!("unknown file type: 0o{value:0o}")) } pub fn dir_entry_kind(self) -> DirEntryKind { match self { Self::Dir => DirEntryKind::Directory, Self::Reg => DirEntryKind::File, } } } impl From for libc::mode_t { fn from(file_type: FileType) -> Self { file_type.value() } } impl TryFrom for FileType { type Error = btlib::Error; fn try_from(value: libc::mode_t) -> btlib::Result { Self::from_value(value) } } impl From for DirEntryKind { fn from(value: FileType) -> Self { value.dir_entry_kind() } } impl BitOr for FileType { type Output = libc::mode_t; fn bitor(self, rhs: libc::mode_t) -> Self::Output { self.value() | rhs } } #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[repr(i32)] /// The generators for the group of [Flags]. These are mostly copied from [libc], save for /// several custom values. Note that the presence of a flag in this enum does not guarantee /// it's supported. pub enum FlagValue { // Standard flags. ReadOnly = libc::O_RDONLY, WriteOnly = libc::O_WRONLY, ReadWrite = libc::O_RDWR, AccMode = libc::O_ACCMODE, Create = libc::O_CREAT, Exclusive = libc::O_EXCL, NoCtty = libc::O_NOCTTY, Truncate = libc::O_TRUNC, Append = libc::O_APPEND, NonBlock = libc::O_NONBLOCK, Dsync = libc::O_DSYNC, Async = libc::O_ASYNC, Direct = libc::O_DIRECT, Directory = libc::O_DIRECTORY, NoFollow = libc::O_NOFOLLOW, NoAtime = libc::O_NOATIME, CloseExec = libc::O_CLOEXEC, Rsync = libc::O_RSYNC, Path = libc::O_PATH, TmpFile = libc::O_TMPFILE, // Custom flags. /// Indicates that a process block should be created. Process = 0x01000000, /// Indicates that a server block be created. Server = 0x02000000, } impl FlagValue { pub const fn value(self) -> i32 { self as i32 } } impl Copy for FlagValue {} impl From for i32 { fn from(flag_value: FlagValue) -> Self { flag_value.value() } } impl BitOr for FlagValue { type Output = Flags; fn bitor(self, rhs: Self) -> Self::Output { Flags::new(self.value() | rhs.value()) } } #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] /// A wrapper type around [i32] with convenience methods for checking if `libc::O_*` /// flags have been set. pub struct Flags(i32); impl Copy for Flags {} impl Flags { pub const fn new(value: i32) -> Self { Self(value) } pub const fn value(self) -> i32 { self.0 } /// Returns true if and only if the given open flags allow the file to be written to. pub const fn writeable(self) -> bool { const MASK: i32 = FlagValue::ReadWrite.value() | FlagValue::WriteOnly.value(); self.0 & MASK != 0 } pub const fn readable(self) -> bool { !self.write_only() } pub const fn read_only(self) -> bool { self.0 == FlagValue::ReadOnly.value() } pub const fn write_only(self) -> bool { self.0 & FlagValue::WriteOnly.value() != 0 } pub const fn directory(self) -> bool { self.0 & FlagValue::Directory.value() != 0 } pub const fn process(self) -> bool { self.0 & FlagValue::Process.value() != 0 } pub const fn server(self) -> bool { self.0 & FlagValue::Server.value() != 0 } pub fn assert_readable(self) -> Result<(), io::Error> { if !self.readable() { Err(io::Error::from_raw_os_error(libc::EACCES)) } else { Ok(()) } } pub fn assert_writeable(self) -> Result<(), io::Error> { if !self.writeable() { Err(io::Error::from_raw_os_error(libc::EACCES)) } else { Ok(()) } } } impl From for Flags { fn from(value: i32) -> Self { Self::new(value) } } impl From for i32 { fn from(flags: Flags) -> Self { flags.value() } } impl From for Flags { fn from(flag_value: FlagValue) -> Self { Self::new(flag_value.value()) } } impl BitOr for Flags { type Output = Flags; fn bitor(self, rhs: Flags) -> Self::Output { Self::new(self.value() | rhs.value()) } } impl BitOr for Flags { type Output = Flags; fn bitor(self, rhs: FlagValue) -> Self::Output { Self::new(self.value() | rhs.value()) } } impl Display for Flags { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.value().fmt(f) } } impl Default for Flags { fn default() -> Self { Self::new(0) } } #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Hash, Default)] pub struct Attrs { pub mode: u32, pub uid: u32, pub gid: u32, pub atime: Epoch, pub mtime: Epoch, pub ctime: Epoch, pub tags: Vec<(String, Vec)>, } #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Hash)] /// A type for indicating which fields in [Attrs] have been set and which should be ignored. This /// method was chosen over using [Option] for greater efficiency on the wire. pub struct AttrsSet(u16); macro_rules! field { ($index:expr, $name:ident) => { pub const $name: AttrsSet = AttrsSet::new(1 << $index); paste! { pub fn [<$name:lower>](self) -> bool { const MASK: u16 = 1 << $index; self.0 & MASK != 0 } } }; } impl AttrsSet { field!(0, MODE); field!(1, UID); field!(2, GID); field!(3, ATIME); field!(4, MTIME); field!(5, CTIME); pub const ALL: Self = Self::new( Self::MODE.0 | Self::UID.0 | Self::GID.0 | Self::ATIME.0 | Self::MTIME.0 | Self::CTIME.0, ); pub const fn new(value: u16) -> Self { Self(value) } pub const fn none() -> Self { Self(0) } pub const fn value(self) -> u16 { self.0 } } impl Copy for AttrsSet {} impl From for AttrsSet { fn from(value: u16) -> Self { Self::new(value) } } impl From for u16 { fn from(attr: AttrsSet) -> Self { attr.value() } } impl BitOr for AttrsSet { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { AttrsSet::new(self.value() | rhs.value()) } } impl BitOrAssign for AttrsSet { fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0 } } #[derive(Debug, Serialize, Deserialize)] pub struct Entry { pub attr: BlockMetaSecrets, pub attr_timeout: Duration, pub entry_timeout: Duration, } #[derive(Serialize, Deserialize)] pub struct Lookup<'a> { pub parent: Inode, pub name: &'a str, } #[derive(Debug, Serialize, Deserialize)] pub struct LookupReply { pub inode: Inode, pub generation: u64, pub entry: Entry, } #[derive(Serialize, Deserialize)] pub struct Create<'a> { pub parent: Inode, pub name: &'a str, pub flags: Flags, pub mode: u32, pub umask: u32, } #[derive(Serialize, Deserialize)] pub struct CreateReply { pub inode: Inode, pub handle: Handle, pub entry: Entry, } #[derive(Serialize, Deserialize)] pub struct Open { pub inode: Inode, pub flags: Flags, } #[derive(Serialize, Deserialize)] pub struct OpenReply { pub handle: Handle, } #[derive(Serialize, Deserialize)] pub struct Read { pub inode: Inode, pub handle: Handle, pub offset: u64, pub size: u64, } #[derive(Serialize, Deserialize)] pub struct ReadReply<'a> { pub data: &'a [u8], } #[derive(Serialize, Deserialize)] pub struct Write { pub inode: Inode, pub handle: Handle, pub offset: u64, pub data: R, } #[derive(Serialize, Deserialize)] pub struct WriteReply { pub written: u64, } #[derive(Serialize, Deserialize)] pub struct Flush { pub inode: Inode, pub handle: Handle, } #[derive(Serialize, Deserialize)] pub struct ReadDir { pub inode: Inode, pub handle: Handle, /// The maximum number of directory entries to return in a single response. A value of 0 /// indicates there is no limit. Note that the server may impose it's own limit. pub limit: u32, /// An opaque value which the server uses to keep track of the client's position in reading /// the directory. A value of 0 indicates the directory is to be iterated from the beginning. pub state: u64, } #[derive(Serialize, Deserialize)] pub struct ReadDirReply { pub entries: Vec<(String, DirEntry)>, /// This is the value to pass in a subsequent [ReadDir] message to continue reading this /// directory. A value of 0 indicates that all entries have been returned. pub new_state: u64, } #[derive(Serialize, Deserialize)] pub struct Link<'a> { pub inode: Inode, pub new_parent: Inode, pub name: &'a str, } #[derive(Serialize, Deserialize)] pub struct LinkReply { pub entry: Entry, } #[derive(Serialize, Deserialize)] pub struct Unlink<'a> { pub parent: Inode, pub name: &'a str, } #[derive(Serialize, Deserialize)] pub struct ReadMeta { pub inode: Inode, pub handle: Option, } #[derive(Serialize, Deserialize)] pub struct ReadMetaReply { pub attrs: BlockMetaSecrets, pub valid_for: Duration, } #[derive(Serialize, Deserialize)] pub struct WriteMeta { pub inode: Inode, pub handle: Option, pub attrs: Attrs, /// The bits in this value indicate which fields in `attrs` have been initialized. pub attrs_set: AttrsSet, } #[derive(Serialize, Deserialize)] pub struct WriteMetaReply { pub attrs: BlockMetaSecrets, pub valid_for: Duration, } #[derive(Serialize, Deserialize)] pub struct Allocate { pub inode: Inode, pub handle: Handle, pub offset: Option, pub size: u64, } #[derive(Serialize, Deserialize)] pub struct Close { pub inode: Inode, pub handle: Handle, } #[derive(Serialize, Deserialize)] pub struct Forget { pub inode: Inode, pub count: u64, } #[derive(Serialize, Deserialize)] pub struct LockDesc { pub offset: u64, pub size: u64, pub exclusive: bool, } #[derive(Serialize, Deserialize)] pub struct Lock { pub inode: Inode, pub handle: Handle, pub desc: LockDesc, } #[derive(Serialize, Deserialize)] pub struct Unlock { pub inode: Inode, pub handle: Handle, } #[derive(Serialize, Deserialize)] pub struct AddReadcap { pub inode: Inode, pub handle: Handle, pub pub_creds: ConcretePub, } #[derive(Serialize, Deserialize)] pub struct GrantAccess { pub inode: Inode, pub record: IssuedProcRec, }