diff options
Diffstat (limited to '')
| -rw-r--r-- | src/ops/dir.rs | 106 | ||||
| -rw-r--r-- | src/ops/entry.rs | 15 | ||||
| -rw-r--r-- | src/ops/global.rs | 20 | ||||
| -rw-r--r-- | src/ops/mod.rs | 34 | ||||
| -rw-r--r-- | src/ops/open.rs | 92 | ||||
| -rw-r--r-- | src/ops/rw.rs | 46 | ||||
| -rw-r--r-- | src/ops/traits.rs | 190 | ||||
| -rw-r--r-- | src/ops/xattr.rs | 88 | ||||
| -rw-r--r-- | src/session.rs | 1 |
9 files changed, 396 insertions, 196 deletions
diff --git a/src/ops/dir.rs b/src/ops/dir.rs index cb3a4f7..e1998cd 100644 --- a/src/ops/dir.rs +++ b/src/ops/dir.rs @@ -11,7 +11,12 @@ use crate::{ Done, Operation, Reply, Request, }; -use super::{c_to_os, FromRequest}; +use super::{ + c_to_os, make_entry, + traits::{ReplyBuffered, ReplyKnown, ReplyNotFound}, + FromRequest, +}; + use crate::{proto, Errno, Ino, Ttl}; use bytemuck::{bytes_of, Zeroable}; use bytes::BufMut; @@ -21,6 +26,15 @@ pub enum Lookup {} pub enum Readdir {} pub struct BufferedReaddir<B>(Infallible, PhantomData<B>); +pub trait ReplyFound<'o>: ReplyKnown<'o> { + fn not_found_for(reply: Reply<'o, Self>, ttl: Ttl) -> Done<'o>; +} + +pub trait ReplyEntries<'o>: Operation<'o> { + fn entry(reply: Reply<'o, Self>, entry: Entry<impl Known>) -> Interruptible<'o, Self, ()>; + fn end(reply: Reply<'o, Self>) -> Done<'o>; +} + pub struct ReaddirState<B> { max_read: usize, is_plus: bool, @@ -58,34 +72,21 @@ impl<'o> Request<'o, Lookup> { } } -impl<'o> Reply<'o, Lookup> { - /// The requested entry was found. The FUSE client will become aware of the found inode if - /// it wasn't before. This result may be cached by the client for up to the given TTL. - pub fn found(self, entry: impl Known, ttl: Ttl) -> Done<'o> { - let (attrs, attrs_ttl) = entry.inode().attrs(); - let attrs = attrs.finish(entry.inode()); - - let done = self.single(&make_entry((entry.inode().ino(), ttl), (attrs, attrs_ttl))); - entry.unveil(); - - done +impl<'o> ReplyNotFound<'o> for Lookup { + fn not_found(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::ENOENT) } +} + +impl<'o> ReplyKnown<'o> for Lookup {} - /// The requested entry was not found in this directory. The FUSE clint may include this - /// response in negative cache for up to the given TTL. - pub fn not_found(self, ttl: Ttl) -> Done<'o> { - self.single(&make_entry( +impl<'o> ReplyFound<'o> for Lookup { + fn not_found_for(reply: Reply<'o, Self>, ttl: Ttl) -> Done<'o> { + reply.single(&make_entry( (Ino::NULL, ttl), (Zeroable::zeroed(), Ttl::NULL), )) } - - /// The requested entry was not found in this directory, but unlike [`Reply::not_found()`] - /// this does not report back a TTL to the FUSE client. The client should not cache the - /// response. - pub fn not_found_uncached(self) -> Done<'o> { - self.fail(Errno::ENOENT) - } } impl<'o> Request<'o, Readdir> { @@ -112,22 +113,24 @@ impl<'o> Request<'o, Readdir> { } } -impl<'o> Reply<'o, Readdir> { - pub fn buffered<B>(self, buffer: B) -> Reply<'o, BufferedReaddir<B>> - where - B: BufMut + AsRef<[u8]>, - { +impl<'o, B> ReplyBuffered<'o, B> for Readdir +where + B: BufMut + AsRef<[u8]>, +{ + type Buffered = BufferedReaddir<B>; + + fn buffered(reply: Reply<'o, Self>, buffer: B) -> Reply<'o, Self::Buffered> { assert!(buffer.as_ref().is_empty()); let ReaddirState { max_read, is_plus, buffer: (), - } = self.tail; + } = reply.tail; Reply { - session: self.session, - unique: self.unique, + session: reply.session, + unique: reply.unique, tail: ReaddirState { max_read, is_plus, @@ -137,9 +140,9 @@ impl<'o> Reply<'o, Readdir> { } } -impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { - pub fn entry(mut self, entry: Entry<impl Known>) -> Interruptible<'o, BufferedReaddir<B>, ()> { - let entry_header_len = if self.tail.is_plus { +impl<'o, B: BufMut + AsRef<[u8]>> ReplyEntries<'o> for BufferedReaddir<B> { + fn entry(mut reply: Reply<'o, Self>, entry: Entry<impl Known>) -> Interruptible<'o, Self, ()> { + let entry_header_len = if reply.tail.is_plus { std::mem::size_of::<proto::DirentPlus>() } else { std::mem::size_of::<proto::Dirent>() @@ -148,19 +151,19 @@ impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { let name = entry.name.as_bytes(); let padding_len = dirent_pad_bytes(entry_header_len + name.len()); - let buffer = &mut self.tail.buffer; + let buffer = &mut reply.tail.buffer; let remaining = buffer .remaining_mut() - .min(self.tail.max_read - buffer.as_ref().len()); + .min(reply.tail.max_read - buffer.as_ref().len()); let record_len = entry_header_len + name.len() + padding_len; if remaining < record_len { if buffer.as_ref().is_empty() { - log::error!("Buffer for readdir req #{} is too small", self.unique); - return Interruptible::Interrupted(self.fail(Errno::ENOBUFS)); + log::error!("Buffer for readdir req #{} is too small", reply.unique); + return Interruptible::Interrupted(reply.fail(Errno::ENOBUFS)); } - return Interruptible::Interrupted(self.end()); + return Interruptible::Interrupted(reply.end()); } let inode = entry.inode.inode(); @@ -187,7 +190,7 @@ impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { DirentPlus(proto::DirentPlus), } - let ent = if self.tail.is_plus { + let ent = if reply.tail.is_plus { let (attrs, attrs_ttl) = inode.attrs(); let attrs = attrs.finish(inode); let entry_out = make_entry((ino, entry.ttl), (attrs, attrs_ttl)); @@ -211,14 +214,14 @@ impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { buffer.put_slice(&[0; 7][..padding_len]); if remaining - record_len >= entry_header.len() + (1 << proto::DIRENT_ALIGNMENT_BITS) { - Interruptible::Completed(self, ()) + Interruptible::Completed(reply, ()) } else { - Interruptible::Interrupted(self.end()) + Interruptible::Interrupted(reply.end()) } } - pub fn end(self) -> Done<'o> { - self.inner(|this| this.tail.buffer.as_ref()) + fn end(reply: Reply<'o, Self>) -> Done<'o> { + reply.inner(|reply| reply.tail.buffer.as_ref()) } } @@ -232,21 +235,6 @@ impl<'o> FromRequest<'o, Readdir> for ReaddirState<()> { } } -fn make_entry( - (Ino(ino), entry_ttl): (Ino, Ttl), - (attrs, attr_ttl): (proto::Attrs, Ttl), -) -> proto::EntryOut { - proto::EntryOut { - nodeid: ino, - generation: 0, //TODO - entry_valid: entry_ttl.seconds, - attr_valid: attr_ttl.seconds, - entry_valid_nsec: entry_ttl.nanoseconds, - attr_valid_nsec: attr_ttl.nanoseconds, - attr: attrs, - } -} - fn dirent_pad_bytes(entry_len: usize) -> usize { const ALIGN_MASK: usize = (1 << proto::DIRENT_ALIGNMENT_BITS) - 1; ((entry_len + ALIGN_MASK) & !ALIGN_MASK) - entry_len diff --git a/src/ops/entry.rs b/src/ops/entry.rs index d3e2b17..1bcf71f 100644 --- a/src/ops/entry.rs +++ b/src/ops/entry.rs @@ -1,8 +1,13 @@ +use super::traits::ReplyOk; use crate::{io::Stat, private_trait::Sealed, proto, Done, Ino, Operation, Reply, Request}; pub enum Forget {} pub enum Getattr {} +pub trait ReplyStat<'o>: Operation<'o> { + fn stat(reply: Reply<'o, Self>, inode: &impl Stat) -> Done<'o>; +} + impl Sealed for Forget {} impl Sealed for Getattr {} @@ -51,8 +56,8 @@ impl<'o> Request<'o, Forget> { } } -impl<'o> Reply<'o, Forget> { - pub fn ok(self) -> Done<'o> { +impl<'o> ReplyOk<'o> for Forget { + fn ok(_reply: Reply<'o, Self>) -> Done<'o> { // No reply for forget requests Done::new() } @@ -64,12 +69,12 @@ impl<'o> Request<'o, Getattr> { } } -impl<'o> Reply<'o, Getattr> { - pub fn known(self, inode: &impl Stat) -> Done<'o> { +impl<'o> ReplyStat<'o> for Getattr { + fn stat(reply: Reply<'o, Self>, inode: &impl Stat) -> Done<'o> { let (attrs, ttl) = inode.attrs(); let attrs = attrs.finish(inode); - self.single(&proto::AttrOut { + reply.single(&proto::AttrOut { attr_valid: ttl.seconds, attr_valid_nsec: ttl.nanoseconds, dummy: Default::default(), diff --git a/src/ops/global.rs b/src/ops/global.rs index cd6b260..aa83b1e 100644 --- a/src/ops/global.rs +++ b/src/ops/global.rs @@ -1,3 +1,4 @@ +use super::traits::ReplyOk; use crate::{io::FsInfo, private_trait::Sealed, proto, util::page_size, Done, Operation, Reply}; pub enum Init {} @@ -8,6 +9,10 @@ pub struct InitState { pub(crate) buffer_pages: usize, } +pub trait ReplyFsInfo<'o>: Operation<'o> { + fn info(reply: Reply<'o, Self>, info: &FsInfo) -> Done<'o>; +} + impl Sealed for Init {} impl Sealed for Statfs {} @@ -21,12 +26,12 @@ impl<'o> Operation<'o> for Statfs { type ReplyTail = (); } -impl<'o> Reply<'o, Init> { - pub fn ok(self) -> Done<'o> { +impl<'o> ReplyOk<'o> for Init { + fn ok(reply: Reply<'o, Self>) -> Done<'o> { let InitState { kernel_flags, buffer_pages, - } = self.tail; + } = reply.tail; let flags = { use proto::InitFlags; @@ -63,7 +68,7 @@ impl<'o> Reply<'o, Init> { // See fs/fuse/dev.c in the kernel source tree for details about max_write let max_write = buffer_size - std::mem::size_of::<(proto::InHeader, proto::WriteIn)>(); - self.single(&proto::InitOut { + reply.single(&proto::InitOut { major: proto::MAJOR_VERSION, minor: proto::TARGET_MINOR_VERSION, max_readahead: 0, //TODO @@ -79,9 +84,8 @@ impl<'o> Reply<'o, Init> { } } -impl<'o> Reply<'o, Statfs> { - /// Replies with filesystem statistics. - pub fn info(self, statfs: &FsInfo) -> Done<'o> { - self.single(&proto::StatfsOut::from(*statfs)) +impl<'o> ReplyFsInfo<'o> for Statfs { + fn info(reply: Reply<'o, Self>, fs_info: &FsInfo) -> Done<'o> { + reply.single(&proto::StatfsOut::from(*fs_info)) } } diff --git a/src/ops/mod.rs b/src/ops/mod.rs index 13d146d..3e64b97 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -3,15 +3,13 @@ use std::{ os::unix::ffi::OsStrExt, }; -use crate::{private_trait::Sealed, util::OutputChain, Done, Operation, Reply, Request}; +use crate::{ + private_trait::Sealed, proto, util::OutputChain, Done, Ino, Operation, Reply, Request, Ttl, +}; + use bytemuck::{bytes_of, Pod}; -mod dir; -mod entry; -mod global; -mod open; -mod rw; -mod xattr; +pub mod traits; pub use dir::{BufferedReaddir, Lookup, Readdir}; pub use entry::{Forget, Getattr}; @@ -20,6 +18,13 @@ pub use open::{Access, Open, Opendir, Release, Releasedir}; pub use rw::{Flush, Read, Readlink, Write}; pub use xattr::{Getxattr, Listxattr, Removexattr, Setxattr}; +mod dir; +mod entry; +mod global; +mod open; +mod rw; +mod xattr; + pub(crate) use global::InitState; pub trait FromRequest<'o, O: Operation<'o>> { @@ -62,6 +67,21 @@ impl<'o, O: Operation<'o>> Reply<'o, O> { } } +fn make_entry( + (Ino(ino), entry_ttl): (Ino, Ttl), + (attrs, attr_ttl): (proto::Attrs, Ttl), +) -> proto::EntryOut { + proto::EntryOut { + nodeid: ino, + generation: 0, //TODO + entry_valid: entry_ttl.seconds, + attr_valid: attr_ttl.seconds, + entry_valid_nsec: entry_ttl.nanoseconds, + attr_valid_nsec: attr_ttl.nanoseconds, + attr: attrs, + } +} + fn c_to_os(c_str: &CStr) -> &OsStr { OsStr::from_bytes(c_str.to_bytes()) } diff --git a/src/ops/open.rs b/src/ops/open.rs index 9123421..ab9eea7 100644 --- a/src/ops/open.rs +++ b/src/ops/open.rs @@ -4,7 +4,7 @@ use crate::{ proto, Done, Errno, Operation, Reply, Request, }; -use super::FromRequest; +use super::{traits::ReplyOk, FromRequest}; pub enum Open {} pub enum Release {} @@ -12,6 +12,28 @@ pub enum Opendir {} pub enum Releasedir {} pub enum Access {} +pub trait ReplyOpen<'o>: ReplyOk<'o, ReplyTail = proto::OpenOutFlags> { + fn ok_with_handle(reply: Reply<'o, Self>, handle: u64) -> Done<'o> { + let open_flags = reply.tail.bits(); + + reply.single(&proto::OpenOut { + fh: handle, + open_flags, + padding: Default::default(), + }) + } + + fn force_direct_io(reply: &mut Reply<'o, Self>) { + reply.tail |= proto::OpenOutFlags::DIRECT_IO; + } +} + +pub trait ReplyPermissionDenied<'o>: Operation<'o> { + fn permission_denied(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::EACCES) + } +} + impl Sealed for Open {} impl Sealed for Release {} impl Sealed for Opendir {} @@ -30,7 +52,7 @@ impl<'o> Operation<'o> for Release { impl<'o> Operation<'o> for Opendir { type RequestBody = &'o proto::OpendirIn; - type ReplyTail = (); + type ReplyTail = proto::OpenOutFlags; } impl<'o> Operation<'o> for Releasedir { @@ -49,63 +71,39 @@ impl<'o> Request<'o, Open> { } } -impl<'o> Reply<'o, Open> { - pub fn force_direct_io(&mut self) { - self.tail |= proto::OpenOutFlags::DIRECT_IO; - } - - pub fn ok(self) -> Done<'o> { - self.ok_with_handle(0) - } - - pub fn ok_with_handle(self, handle: u64) -> Done<'o> { - let open_flags = self.tail.bits(); - - self.single(&proto::OpenOut { - fh: handle, - open_flags, - padding: Default::default(), - }) +impl<'o> ReplyOk<'o> for Open { + fn ok(reply: Reply<'o, Self>) -> Done<'o> { + reply.ok_with_handle(0) } } +impl<'o> ReplyOpen<'o> for Open {} +impl<'o> ReplyPermissionDenied<'o> for Open {} + impl<'o> Request<'o, Release> { pub fn handle(&self) -> u64 { self.body.fh } } -impl<'o> Reply<'o, Release> { - pub fn ok(self) -> Done<'o> { - self.empty() - } -} +impl<'o> ReplyOk<'o> for Release {} -impl<'o> Reply<'o, Opendir> { - pub fn ok(self) -> Done<'o> { - self.ok_with_handle(0) - } - - pub fn ok_with_handle(self, handle: u64) -> Done<'o> { - self.single(&proto::OpenOut { - fh: handle, - open_flags: 0, - padding: Default::default(), - }) +impl<'o> ReplyOk<'o> for Opendir { + fn ok(reply: Reply<'o, Self>) -> Done<'o> { + reply.ok_with_handle(0) } } +impl<'o> ReplyPermissionDenied<'o> for Opendir {} +impl<'o> ReplyOpen<'o> for Opendir {} + impl<'o> Request<'o, Releasedir> { pub fn handle(&self) -> u64 { self.body.release_in.fh } } -impl<'o> Reply<'o, Releasedir> { - pub fn ok(self) -> Done<'o> { - self.empty() - } -} +impl<'o> ReplyOk<'o> for Releasedir {} impl<'o> Request<'o, Access> { pub fn mask(&self) -> AccessFlags { @@ -113,18 +111,12 @@ impl<'o> Request<'o, Access> { } } -impl<'o> Reply<'o, Access> { - pub fn ok(self) -> Done<'o> { - self.empty() - } +impl<'o> ReplyOk<'o> for Access {} - pub fn permission_denied(self) -> Done<'o> { - self.fail(Errno::EACCES) - } -} +impl<'o> ReplyPermissionDenied<'o> for Access {} -impl<'o> FromRequest<'o, Open> for proto::OpenOutFlags { - fn from_request(_request: &Request<'o, Open>) -> Self { +impl<'o, O: ReplyOpen<'o>> FromRequest<'o, O> for proto::OpenOutFlags { + fn from_request(_request: &Request<'o, O>) -> Self { proto::OpenOutFlags::empty() } } diff --git a/src/ops/rw.rs b/src/ops/rw.rs index b1c184b..8206c7c 100644 --- a/src/ops/rw.rs +++ b/src/ops/rw.rs @@ -1,6 +1,9 @@ -use super::FromRequest; -use crate::{private_trait::Sealed, proto, util::OutputChain, Done, Operation, Reply, Request}; -use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; +use super::{ + traits::{ReplyGather, ReplyOk}, + FromRequest, +}; + +use crate::{private_trait::Sealed, proto, Done, Operation, Reply, Request}; pub enum Readlink {} pub enum Read {} @@ -11,6 +14,10 @@ pub struct WriteState { size: u32, } +pub trait ReplyAll<'o>: Operation<'o> { + fn all(reply: Reply<'o, Self>) -> Done<'o>; +} + impl Sealed for Readlink {} impl Sealed for Read {} impl Sealed for Write {} @@ -36,18 +43,7 @@ impl<'o> Operation<'o> for Flush { type ReplyTail = (); } -impl<'o> Reply<'o, Readlink> { - /// This inode corresponds to a symbolic link pointing to the given target path. - pub fn target<T: AsRef<OsStr>>(self, target: T) -> Done<'o> { - self.chain(OutputChain::tail(&[target.as_ref().as_bytes()])) - } - - /// Same as [`Reply::target()`], except that the target path is taken from disjoint - /// slices. This involves no additional allocation. - pub fn gather_target(self, target: &[&[u8]]) -> Done<'o> { - self.chain(OutputChain::tail(target)) - } -} +impl<'o> ReplyGather<'o> for Readlink {} impl<'o> Request<'o, Read> { pub fn handle(&self) -> u64 { @@ -63,11 +59,7 @@ impl<'o> Request<'o, Read> { } } -impl<'o> Reply<'o, Read> { - pub fn slice(self, data: &[u8]) -> Done<'o> { - self.chain(OutputChain::tail(&[data])) - } -} +impl<'o> ReplyGather<'o> for Read {} impl<'o> Request<'o, Write> { pub fn handle(&self) -> u64 { @@ -83,10 +75,10 @@ impl<'o> Request<'o, Write> { } } -impl<'o> Reply<'o, Write> { - pub fn all(self) -> Done<'o> { - let size = self.tail.size; - self.single(&proto::WriteOut { +impl<'o> ReplyAll<'o> for Write { + fn all(reply: Reply<'o, Self>) -> Done<'o> { + let size = reply.tail.size; + reply.single(&proto::WriteOut { size, padding: Default::default(), }) @@ -99,11 +91,7 @@ impl<'o> Request<'o, Flush> { } } -impl<'o> Reply<'o, Flush> { - pub fn ok(self) -> Done<'o> { - self.empty() - } -} +impl<'o> ReplyOk<'o> for Flush {} impl<'o> FromRequest<'o, Write> for WriteState { fn from_request(request: &Request<'o, Write>) -> Self { diff --git a/src/ops/traits.rs b/src/ops/traits.rs new file mode 100644 index 0000000..8bb59e4 --- /dev/null +++ b/src/ops/traits.rs @@ -0,0 +1,190 @@ +use crate::{ + io::{Entry, FsInfo, Interruptible, Known, Stat}, + Done, Operation, Reply, Ttl, +}; + +pub use super::{ + dir::{ReplyEntries, ReplyFound}, + entry::ReplyStat, + global::ReplyFsInfo, + open::{ReplyOpen, ReplyPermissionDenied}, + rw::ReplyAll, + xattr::ReplyXattrRead, +}; + +use super::make_entry; +use bytes::BufMut; +use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; + +pub trait ReplyOk<'o>: Operation<'o> { + fn ok(reply: Reply<'o, Self>) -> Done<'o> { + reply.empty() + } +} + +pub trait ReplyKnown<'o>: Operation<'o> { + fn known(reply: Reply<'o, Self>, entry: impl Known, ttl: Ttl) -> Done<'o> { + let (attrs, attrs_ttl) = entry.inode().attrs(); + let attrs = attrs.finish(entry.inode()); + + let done = reply.single(&make_entry((entry.inode().ino(), ttl), (attrs, attrs_ttl))); + entry.unveil(); + + done + } +} + +pub trait ReplyNotFound<'o>: Operation<'o> { + fn not_found(reply: Reply<'o, Self>) -> Done<'o>; +} + +pub trait ReplyBuffered<'o, B>: Operation<'o> +where + B: BufMut + AsRef<[u8]>, +{ + type Buffered: Operation<'o>; + fn buffered(reply: Reply<'o, Self>, buffer: B) -> Reply<'o, Self::Buffered>; +} + +pub trait ReplyGather<'o>: Operation<'o> { + fn blob(reply: Reply<'o, Self>, blob: impl AsRef<OsStr>) -> Done<'o> { + Self::slice(reply, blob.as_ref().as_bytes()) + } + + fn slice(reply: Reply<'o, Self>, slice: impl AsRef<[u8]>) -> Done<'o> { + Self::gather(reply, &[slice.as_ref()]) + } + + fn gather(reply: Reply<'o, Self>, fragments: &[&[u8]]) -> Done<'o> { + reply.chain(crate::util::OutputChain::tail(fragments)) + } +} + +impl<'o, O: Operation<'o>> Reply<'o, O> { + pub fn ok(self) -> Done<'o> + where + O: ReplyOk<'o>, + { + O::ok(self) + } + + pub fn known(self, entry: impl Known, ttl: Ttl) -> Done<'o> + where + O: ReplyKnown<'o>, + { + O::known(self, entry, ttl) + } + + pub fn not_found(self) -> Done<'o> + where + O: ReplyNotFound<'o>, + { + O::not_found(self) + } + + pub fn permission_denied(self) -> Done<'o> + where + O: ReplyPermissionDenied<'o>, + { + O::permission_denied(self) + } + + pub fn stat(self, inode: &impl Stat) -> Done<'o> + where + O: ReplyStat<'o>, + { + O::stat(self, inode) + } + + pub fn ok_with_handle(self, handle: u64) -> Done<'o> + where + O: ReplyOpen<'o>, + { + O::ok_with_handle(self, handle) + } + + pub fn force_direct_io(&mut self) + where + O: ReplyOpen<'o>, + { + O::force_direct_io(self) + } + + pub fn not_found_for(self, ttl: Ttl) -> Done<'o> + where + O: ReplyFound<'o>, + { + O::not_found_for(self, ttl) + } + + pub fn entry(self, entry: Entry<impl Known>) -> Interruptible<'o, O, ()> + where + O: ReplyEntries<'o>, + { + O::entry(self, entry) + } + + pub fn end(self) -> Done<'o> + where + O: ReplyEntries<'o>, + { + O::end(self) + } + + pub fn all(self) -> Done<'o> + where + O: ReplyAll<'o>, + { + O::all(self) + } + + pub fn buffered<B>(self, buffer: B) -> Reply<'o, O::Buffered> + where + O: ReplyBuffered<'o, B>, + B: BufMut + AsRef<[u8]>, + { + O::buffered(self, buffer) + } + + pub fn blob(self, blob: impl AsRef<OsStr>) -> Done<'o> + where + O: ReplyGather<'o>, + { + O::blob(self, blob) + } + + pub fn slice(self, slice: impl AsRef<[u8]>) -> Done<'o> + where + O: ReplyGather<'o>, + { + O::slice(self, slice) + } + + pub fn gather(self, fragments: &[&[u8]]) -> Done<'o> + where + O: ReplyGather<'o>, + { + O::gather(self, fragments) + } + + pub fn info(self, info: &FsInfo) -> Done<'o> + where + O: ReplyFsInfo<'o>, + { + O::info(self, info) + } + + pub fn requires_size(self, size: u32) -> Done<'o> + where + O: ReplyXattrRead<'o>, + { + O::requires_size(self, size) + } + + pub fn buffer_too_small(self) -> Done<'o> + where + O: ReplyXattrRead<'o>, + { + O::buffer_too_small(self) + } +} diff --git a/src/ops/xattr.rs b/src/ops/xattr.rs index 886b290..2de222b 100644 --- a/src/ops/xattr.rs +++ b/src/ops/xattr.rs @@ -2,7 +2,11 @@ use crate::{ private_trait::Sealed, proto, util::OutputChain, Done, Errno, Operation, Reply, Request, }; -use super::c_to_os; +use super::{ + c_to_os, + traits::{ReplyGather, ReplyNotFound, ReplyOk}, +}; + use std::ffi::{CStr, OsStr}; pub enum Setxattr {} @@ -14,6 +18,14 @@ pub struct XattrReadState { size: u32, } +pub trait ReplyXattrRead<'o>: Operation<'o> { + fn requires_size(reply: Reply<'o, Self>, size: u32) -> Done<'o>; + + fn buffer_too_small(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::ERANGE) + } +} + impl Sealed for Setxattr {} impl Sealed for Getxattr {} impl Sealed for Listxattr {} @@ -53,13 +65,11 @@ impl<'o> Request<'o, Setxattr> { } } -impl<'o> Reply<'o, Setxattr> { - pub fn ok(self) -> Done<'o> { - self.empty() - } +impl<'o> ReplyOk<'o> for Setxattr {} - pub fn not_found(self) -> Done<'o> { - self.fail(Errno::ENODATA) +impl<'o> ReplyNotFound<'o> for Setxattr { + fn not_found(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::ENODATA) } } @@ -73,34 +83,40 @@ impl<'o> Request<'o, Getxattr> { } } -impl<'o> Reply<'o, Getxattr> { - pub fn slice(self, value: &[u8]) -> Done<'o> { - let size = value.len().try_into().expect("Extremely large xattr"); - if self.tail.size == 0 { - return self.value_size(size); - } else if self.tail.size < size { - return self.buffer_too_small(); +impl<'o> ReplyNotFound<'o> for Getxattr { + fn not_found(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::ENODATA) + } +} + +impl<'o> ReplyGather<'o> for Getxattr { + fn gather(reply: Reply<'o, Self>, fragments: &[&[u8]]) -> Done<'o> { + let size = fragments + .iter() + .map(|fragment| fragment.len()) + .sum::<usize>() + .try_into() + .expect("Extremely large xattr"); + + if reply.tail.size == 0 { + return reply.requires_size(size); + } else if reply.tail.size < size { + return reply.buffer_too_small(); } - self.chain(OutputChain::tail(&[value])) + reply.chain(OutputChain::tail(fragments)) } +} - pub fn value_size(self, size: u32) -> Done<'o> { - assert_eq!(self.tail.size, 0); +impl<'o> ReplyXattrRead<'o> for Getxattr { + fn requires_size(reply: Reply<'o, Self>, size: u32) -> Done<'o> { + assert_eq!(reply.tail.size, 0); - self.single(&proto::GetxattrOut { + reply.single(&proto::GetxattrOut { size, padding: Default::default(), }) } - - pub fn buffer_too_small(self) -> Done<'o> { - self.fail(Errno::ERANGE) - } - - pub fn not_found(self) -> Done<'o> { - self.fail(Errno::ENODATA) - } } impl<'o> Request<'o, Listxattr> { @@ -109,23 +125,19 @@ impl<'o> Request<'o, Listxattr> { } } -impl<'o> Reply<'o, Listxattr> { +impl<'o> ReplyXattrRead<'o> for Listxattr { //TODO: buffered(), gather() - pub fn value_size(self, size: u32) -> Done<'o> { - assert_eq!(self.tail.size, 0); + fn requires_size(reply: Reply<'o, Self>, size: u32) -> Done<'o> { + assert_eq!(reply.tail.size, 0); - self.single(&proto::ListxattrOut { + reply.single(&proto::ListxattrOut { getxattr_out: proto::GetxattrOut { size, padding: Default::default(), }, }) } - - pub fn buffer_too_small(self) -> Done<'o> { - self.fail(Errno::ERANGE) - } } impl<'o> Request<'o, Removexattr> { @@ -134,8 +146,10 @@ impl<'o> Request<'o, Removexattr> { } } -impl<'o> Reply<'o, Removexattr> { - pub fn ok(self) -> Done<'o> { - self.empty() +impl<'o> ReplyOk<'o> for Removexattr {} + +impl<'o> ReplyNotFound<'o> for Removexattr { + fn not_found(reply: Reply<'o, Self>) -> Done<'o> { + reply.fail(Errno::ENODATA) } } diff --git a/src/session.rs b/src/session.rs index 29b3ab6..0c768b3 100644 --- a/src/session.rs +++ b/src/session.rs @@ -206,7 +206,6 @@ impl Session { unique, }; - //TODO: Full const generics any time now? Fs::EXPECTED_REQUEST_SEGMENTS let header = [bytes_of(&header)]; let output = output.preceded(&header); let buffers: SmallVec<[_; 8]> = output |
