summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2022-01-05 02:52:04 -0600
committerAlejandro Soto <alejandro@34project.org>2022-01-05 02:52:04 -0600
commit75cced9d4c101ec2f9f04ed95621ff3a3f750eae (patch)
treea8ff3fa46a75b18ee3c1133aa6fc0857132d6115
parentff17b04143dde5157808be5bcf1cbf8a942db4c4 (diff)
Refactor impls of Reply as individual traits
-rw-r--r--examples/ext2.rs10
-rw-r--r--examples/passthrough.rs6
-rw-r--r--src/ops/dir.rs106
-rw-r--r--src/ops/entry.rs15
-rw-r--r--src/ops/global.rs20
-rw-r--r--src/ops/mod.rs34
-rw-r--r--src/ops/open.rs92
-rw-r--r--src/ops/rw.rs46
-rw-r--r--src/ops/traits.rs190
-rw-r--r--src/ops/xattr.rs88
-rw-r--r--src/session.rs1
11 files changed, 404 insertions, 204 deletions
diff --git a/examples/ext2.rs b/examples/ext2.rs
index 0a1722e..dd55867 100644
--- a/examples/ext2.rs
+++ b/examples/ext2.rs
@@ -347,7 +347,7 @@ impl Ext2 {
let ino = request.ino();
let (reply, inode) = reply.and_then(self.inode(ino))?;
- reply.known(&Resolved { ino, inode })
+ reply.stat(&Resolved { ino, inode })
}
async fn lookup<'o>(&self, (request, reply): Op<'o, ops::Lookup>) -> Done<'o> {
@@ -370,9 +370,9 @@ impl Ext2 {
};
if let Some(inode) = inode {
- reply.found(inode, Ttl::MAX)
+ reply.known(inode, Ttl::MAX)
} else {
- reply.not_found(Ttl::MAX)
+ reply.not_found_for(Ttl::MAX)
}
}
@@ -387,7 +387,7 @@ impl Ext2 {
let size = inode.i_size as usize;
if size < size_of::<[u32; 15]>() {
- return reply.target(OsStr::from_bytes(&cast_slice(&inode.i_block)[..size]));
+ return reply.slice(&cast_slice(&inode.i_block)[..size]);
}
/* This is unlikely to ever spill, and is guaranteed not to
@@ -407,7 +407,7 @@ impl Ext2 {
offset += segment.len() as u64;
}
- reply.gather_target(&segments)
+ reply.gather(&segments)
}
async fn readdir<'o>(&self, (request, reply): Op<'o, ops::Readdir>) -> Done<'o> {
diff --git a/examples/passthrough.rs b/examples/passthrough.rs
index 5ecb2e8..1607f10 100644
--- a/examples/passthrough.rs
+++ b/examples/passthrough.rs
@@ -102,7 +102,7 @@ impl Passthrough {
let path = parent.path.join(request.name());
let (reply, metadata) = reply.and_then(fs::symlink_metadata(&path).await)?;
- reply.found(New(&mut self.known, Inode::new(path, metadata)), Ttl::MAX)
+ reply.known(New(&mut self.known, Inode::new(path, metadata)), Ttl::MAX)
}
fn forget<'o>(&mut self, (request, reply): Op<'o, ops::Forget>) -> Done<'o> {
@@ -124,14 +124,14 @@ impl Passthrough {
fn getattr<'o>(&mut self, (request, reply): Op<'o, ops::Getattr>) -> Done<'o> {
let (reply, inode) = reply.and_then(self.known(request.ino()))?;
- reply.known(inode)
+ reply.stat(inode)
}
async fn readlink<'o>(&mut self, (request, reply): Op<'o, ops::Readlink>) -> Done<'o> {
let (reply, inode) = reply.and_then(self.known(request.ino()))?;
let (reply, target) = reply.and_then(fs::read_link(&inode.path).await)?;
- reply.target(&target)
+ reply.blob(&target)
}
async fn open<'o>(&mut self, (request, reply): Op<'o, ops::Open>) -> Done<'o> {
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