use bytemuck::Zeroable; use nix::sys::stat::SFlag; use std::{ convert::Infallible, ffi::OsStr, future::Future, ops::{ControlFlow, FromResidual, Try}, time::{SystemTime, UNIX_EPOCH}, }; use super::{Done, Operation, Reply, Request}; use crate::{proto, Errno, FuseResult}; #[doc(no_inline)] pub use nix::{ dir::Type as EntryType, fcntl::OFlag as OpenFlags, sys::stat::Mode, unistd::{AccessFlags, Gid, Pid, Uid}, }; pub use proto::FsyncFlags; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct Ino(pub u64); #[derive(Copy, Clone, Eq, PartialEq)] pub struct Ttl { seconds: u64, nanoseconds: u32, } #[derive(Copy, Clone, Default, Eq, PartialEq)] pub struct Timestamp { seconds: i64, nanoseconds: u32, } pub enum Interruptible<'o, O: Operation<'o>, T> { Completed(Reply<'o, O>, T), Interrupted(Done<'o>), } pub trait Stat { fn ino(&self) -> Ino; fn inode_type(&self) -> EntryType; fn attrs(&self) -> (Attrs, Ttl); } pub trait Known { type Inode: Stat; fn inode(&self) -> &Self::Inode; fn unveil(self); } pub struct Failed<'o, E>(pub Done<'o>, pub E); pub trait Finish<'o, O: Operation<'o>> { fn finish(&self, reply: Reply<'o, O>) -> Done<'o>; } #[derive(Clone)] pub struct Attrs(proto::Attrs); pub struct Entry<'a, K> { pub offset: u64, pub name: &'a OsStr, pub inode: K, pub ttl: Ttl, } #[derive(Copy, Clone)] pub struct FsInfo(proto::StatfsOut); impl Ino { pub const NULL: Self = Ino(0); pub const ROOT: Self = Ino(proto::ROOT_ID); pub fn as_raw(self) -> u64 { self.0 } } impl std::fmt::Display for Ino { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "{}", self.0) } } impl Ttl { pub const NULL: Self = Ttl { seconds: 0, nanoseconds: 0, }; pub const MAX: Self = Ttl { seconds: u64::MAX, nanoseconds: u32::MAX, }; pub fn new(seconds: u64, nanoseconds: u32) -> Ttl { assert!(nanoseconds < 1_000_000_000); Ttl { seconds, nanoseconds, } } pub fn seconds(self) -> u64 { self.seconds } pub fn nanoseconds(self) -> u32 { self.nanoseconds } } impl Timestamp { pub fn new(seconds: i64, nanoseconds: u32) -> Self { Timestamp { seconds, nanoseconds, } } } impl From for Timestamp { fn from(time: SystemTime) -> Self { let (seconds, nanoseconds) = match time.duration_since(UNIX_EPOCH) { Ok(duration) => { let secs = duration.as_secs().try_into().unwrap(); (secs, duration.subsec_nanos()) } Err(before_epoch) => { let duration = before_epoch.duration(); let secs = -i64::try_from(duration.as_secs()).unwrap(); (secs, duration.subsec_nanos()) } }; Timestamp { seconds, nanoseconds, } } } impl<'o, E> From> for Done<'o> { fn from(failed: Failed<'o, E>) -> Done<'o> { failed.0 } } impl<'o, O: Operation<'o>> Finish<'o, O> for Errno { fn finish(&self, reply: Reply<'o, O>) -> Done<'o> { reply.fail(*self) } } impl<'o, O: Operation<'o>> Finish<'o, O> for std::io::Error { fn finish(&self, reply: Reply<'o, O>) -> Done<'o> { reply.fail( self.raw_os_error() .map(Errno::from_i32) .unwrap_or(Errno::EIO), ) } } impl<'o, O: Operation<'o>> Request<'o, O> { pub fn ino(&self) -> Ino { Ino(self.header.ino) } pub fn generation(&self) -> u64 { 0 } pub fn uid(&self) -> Uid { Uid::from_raw(self.header.uid) } pub fn gid(&self) -> Gid { Gid::from_raw(self.header.gid) } pub fn pid(&self) -> Pid { Pid::from_raw(self.header.pid as i32) } } impl<'o, O: Operation<'o>> Reply<'o, O> { pub async fn interruptible(self, f: F) -> Interruptible<'o, O, T> where F: Future, { tokio::pin!(f); let mut rx = self.session.interrupt_rx(); use Interruptible::*; loop { tokio::select! { output = &mut f => break Completed(self, output), result = rx.recv() => match result { Ok(unique) if unique == self.unique => { break Interrupted(self.interrupted()); } _ => continue, } } } } pub fn and_then(self, result: Result) -> Result<(Self, T), Failed<'o, E>> where E: Finish<'o, O>, { match result { Ok(t) => Ok((self, t)), Err(error) => { let done = error.finish(self); Err(Failed(done, error)) } } } pub fn fail(self, errno: Errno) -> Done<'o> { let result = self.session.fail(self.unique, errno as i32); self.finish(result) } pub fn not_implemented(self) -> Done<'o> { self.fail(Errno::ENOSYS) } pub fn not_permitted(self) -> Done<'o> { self.fail(Errno::EPERM) } pub fn io_error(self) -> Done<'o> { self.fail(Errno::EIO) } pub fn invalid_argument(self) -> Done<'o> { self.fail(Errno::EINVAL) } pub fn interrupted(self) -> Done<'o> { self.fail(Errno::EINTR) } pub(crate) fn finish(self, result: FuseResult<()>) -> Done<'o> { if let Err(error) = result { log::error!("Replying to request {}: {}", self.unique, error); } Done::new() } } impl<'o, O: Operation<'o>> From<(Reply<'o, O>, Errno)> for Done<'o> { fn from((reply, errno): (Reply<'o, O>, Errno)) -> Done<'o> { reply.fail(errno) } } impl<'o> FromResidual> for Done<'o> { fn from_residual(residual: Done<'o>) -> Self { residual } } impl<'o, T: Into>> FromResidual> for Done<'o> { fn from_residual(residual: Result) -> Self { match residual { Ok(_) => unreachable!(), Err(t) => t.into(), } } } impl<'o, O: Operation<'o>> FromResidual> for Done<'o> { fn from_residual(residual: Interruptible<'o, O, Infallible>) -> Self { match residual { Interruptible::Completed(_, _) => unreachable!(), Interruptible::Interrupted(done) => done, } } } impl Try for Done<'_> { type Output = Self; type Residual = Self; fn from_output(output: Self::Output) -> Self { output } fn branch(self) -> ControlFlow { ControlFlow::Break(self) } } impl<'o, O: Operation<'o>, T> FromResidual> for Interruptible<'o, O, T> { fn from_residual(residual: Interruptible<'o, O, Infallible>) -> Self { use Interruptible::*; match residual { Completed(_, _) => unreachable!(), Interrupted(done) => Interrupted(done), } } } impl<'o, O: Operation<'o>, T> Try for Interruptible<'o, O, T> { type Output = (Reply<'o, O>, T); type Residual = Interruptible<'o, O, Infallible>; fn from_output((reply, t): Self::Output) -> Self { Self::Completed(reply, t) } fn branch(self) -> ControlFlow { use Interruptible::*; match self { Completed(reply, t) => ControlFlow::Continue((reply, t)), Interrupted(done) => ControlFlow::Break(Interrupted(done)), } } } impl Attrs { #[must_use] pub fn size(self, size: u64) -> Self { Attrs(proto::Attrs { size, ..self.0 }) } #[must_use] pub fn owner(self, uid: Uid, gid: Gid) -> Self { Attrs(proto::Attrs { uid: uid.as_raw(), gid: gid.as_raw(), ..self.0 }) } #[must_use] pub fn mode(self, mode: Mode) -> Self { Attrs(proto::Attrs { mode: mode.bits(), ..self.0 }) } #[must_use] pub fn blocks(self, blocks: u64) -> Self { Attrs(proto::Attrs { blocks, ..self.0 }) } #[must_use] pub fn block_size(self, block_size: u32) -> Self { Attrs(proto::Attrs { blksize: block_size, ..self.0 }) } #[must_use] pub fn device(self, device: u32) -> Self { Attrs(proto::Attrs { rdev: device, ..self.0 }) } #[must_use] pub fn times(self, access: Timestamp, modify: Timestamp, change: Timestamp) -> Self { Attrs(proto::Attrs { atime: access.seconds as _, mtime: modify.seconds as _, ctime: change.seconds as _, atimensec: access.nanoseconds, mtimensec: modify.nanoseconds, ctimensec: change.nanoseconds, ..self.0 }) } #[must_use] pub fn links(self, links: u32) -> Self { Attrs(proto::Attrs { nlink: links, ..self.0 }) } pub(crate) fn finish(self, inode: &impl Stat) -> proto::Attrs { let Ino(ino) = inode.ino(); let inode_type = match inode.inode_type() { EntryType::Fifo => SFlag::S_IFIFO, EntryType::CharacterDevice => SFlag::S_IFCHR, EntryType::Directory => SFlag::S_IFDIR, EntryType::BlockDevice => SFlag::S_IFBLK, EntryType::File => SFlag::S_IFREG, EntryType::Symlink => SFlag::S_IFLNK, EntryType::Socket => SFlag::S_IFSOCK, }; proto::Attrs { ino, mode: self.0.mode | inode_type.bits(), ..self.0 } } } impl Default for Attrs { fn default() -> Self { Attrs(Zeroable::zeroed()).links(1) } } impl FsInfo { #[must_use] pub fn blocks(self, size: u32, total: u64, free: u64, available: u64) -> Self { FsInfo(proto::StatfsOut { bsize: size, blocks: total, bfree: free, bavail: available, ..self.0 }) } #[must_use] pub fn inodes(self, total: u64, free: u64) -> Self { FsInfo(proto::StatfsOut { files: total, ffree: free, ..self.0 }) } #[must_use] pub fn max_filename(self, max: u32) -> Self { FsInfo(proto::StatfsOut { namelen: max, ..self.0 }) } } impl Default for FsInfo { fn default() -> Self { FsInfo(Zeroable::zeroed()) } } impl From for proto::StatfsOut { fn from(FsInfo(statfs): FsInfo) -> proto::StatfsOut { statfs } }