diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/fuse/ops.rs | 927 |
1 files changed, 460 insertions, 467 deletions
diff --git a/src/fuse/ops.rs b/src/fuse/ops.rs index 1e22717..6f37c3f 100644 --- a/src/fuse/ops.rs +++ b/src/fuse/ops.rs @@ -26,586 +26,593 @@ pub trait FromRequest<'o, O: Operation<'o>> { fn from_request(request: &Request<'o, O>) -> Self; } -macro_rules! op { - { $name:ident $operation:tt } => { - pub struct $name(Infallible); - - impl Sealed for $name {} - impl<'o> Operation<'o> for $name $operation - }; +pub enum Any {} +pub enum Lookup {} +pub enum Forget {} +pub enum Getattr {} +pub enum Readlink {} +pub enum Open {} +pub enum Read {} +pub enum Write {} +pub enum Init {} +pub enum Statfs {} +pub enum Release {} +pub enum Setxattr {} +pub enum Getxattr {} +pub enum Listxattr {} +pub enum Removexattr {} +pub enum Flush {} +pub enum Opendir {} +pub enum Readdir {} +pub struct BufferedReaddir<B>(Infallible, PhantomData<B>); +pub enum Releasedir {} +pub enum Access {} + +impl Sealed for Any {} +impl Sealed for Lookup {} +impl Sealed for Forget {} +impl Sealed for Getattr {} +impl Sealed for Readlink {} +impl Sealed for Open {} +impl Sealed for Read {} +impl Sealed for Write {} +impl Sealed for Init {} +impl Sealed for Statfs {} +impl Sealed for Release {} +impl Sealed for Setxattr {} +impl Sealed for Getxattr {} +impl Sealed for Listxattr {} +impl Sealed for Removexattr {} +impl Sealed for Flush {} +impl Sealed for Opendir {} +impl Sealed for Readdir {} +impl<B> Sealed for BufferedReaddir<B> {} +impl Sealed for Releasedir {} +impl Sealed for Access {} - { $name:ident $operation:tt impl Request $request:tt $($next:tt)* } => { - impl<'o> Request<'o, $name> $request +impl<'o> Operation<'o> for Any { + type RequestBody = (); + type ReplyTail = (); +} - op! { $name $operation $($next)* } - }; +impl<'o> Operation<'o> for Lookup { + type RequestBody = &'o CStr; // name() + type ReplyTail = (); +} - { $name:ident $operation:tt impl Reply $reply:tt $($next:tt)* } => { - impl<'o> Reply<'o, $name> $reply +impl<'o> Operation<'o> for Forget { + type RequestBody = proto::OpcodeSelect< + (&'o proto::BatchForgetIn, &'o [proto::ForgetOne]), + &'o proto::ForgetIn, + { proto::Opcode::BatchForget as u32 }, + >; - op! { $name $operation $($next)* } - }; + type ReplyTail = (); } -op! { - Any { - type RequestBody = (); - type ReplyTail = (); - } +impl<'o> Operation<'o> for Getattr { + type RequestBody = &'o proto::GetattrIn; + type ReplyTail = (); } -op! { - Lookup { - type RequestBody = &'o CStr; // name() - type ReplyTail = (); - } +impl<'o> Operation<'o> for Readlink { + type RequestBody = (); + type ReplyTail = (); +} - impl Request { - /// Returns the name of the entry being looked up in this directory. - pub fn name(&self) -> &OsStr { - c_to_os(self.body) - } - } +impl<'o> Operation<'o> for Open { + type RequestBody = &'o proto::OpenIn; + type ReplyTail = proto::OpenOutFlags; +} - impl Reply { - /// 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()); +impl<'o> Operation<'o> for Read { + type RequestBody = &'o proto::ReadIn; + type ReplyTail = (); +} - let done = self.single(&make_entry((entry.inode().ino(), ttl), (attrs, attrs_ttl))); - entry.unveil(); +impl<'o> Operation<'o> for Write { + type RequestBody = (&'o proto::WriteIn, &'o [u8]); + type ReplyTail = state::Write; +} - done - } +impl<'o> Operation<'o> for Init { + type RequestBody = &'o proto::InitIn; + type ReplyTail = state::Init; +} - /// 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((Ino::NULL, ttl), (Zeroable::zeroed(), Ttl::NULL))) - } +impl<'o> Operation<'o> for Statfs { + type RequestBody = (); + type ReplyTail = (); +} - /// 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> Operation<'o> for Release { + type RequestBody = &'o proto::ReleaseIn; + type ReplyTail = (); } -op! { - Forget { - type RequestBody = proto::OpcodeSelect< - (&'o proto::BatchForgetIn, &'o [proto::ForgetOne]), - &'o proto::ForgetIn, - { proto::Opcode::BatchForget as u32 }, - >; +impl<'o> Operation<'o> for Setxattr { + // header, name, value + type RequestBody = (&'o proto::SetxattrIn, &'o CStr, &'o [u8]); + type ReplyTail = (); +} - type ReplyTail = (); - } +impl<'o> Operation<'o> for Getxattr { + type RequestBody = (&'o proto::GetxattrIn, &'o CStr); + type ReplyTail = state::ReadXattr; +} - impl Request { - pub fn forget_list(&self) -> impl '_ + Iterator<Item = (Ino, u64)> { - use proto::OpcodeSelect::*; +impl<'o> Operation<'o> for Listxattr { + type RequestBody = &'o proto::ListxattrIn; + type ReplyTail = state::ReadXattr; +} - enum List<'a> { - Single(Option<(Ino, u64)>), - Batch(std::slice::Iter<'a, proto::ForgetOne>), - } +impl<'o> Operation<'o> for Removexattr { + type RequestBody = &'o CStr; + type ReplyTail = (); +} - impl Iterator for List<'_> { - type Item = (Ino, u64); +impl<'o> Operation<'o> for Flush { + type RequestBody = &'o proto::FlushIn; + type ReplyTail = (); +} - fn next(&mut self) -> Option<Self::Item> { - match self { - List::Single(single) => single.take(), - List::Batch(batch) => { - let forget = batch.next()?; - Some((Ino(forget.ino), forget.nlookup)) - } - } - } - } +impl<'o> Operation<'o> for Opendir { + type RequestBody = &'o proto::OpendirIn; + type ReplyTail = (); +} - match self.body { - Match((_, slice)) => List::Batch(slice.iter()), - Alt(single) => List::Single(Some((self.ino(), single.nlookup))), - } - } - } +impl<'o> Operation<'o> for Readdir { + type RequestBody = proto::OpcodeSelect< + &'o proto::ReaddirPlusIn, + &'o proto::ReaddirIn, + { proto::Opcode::ReaddirPlus as u32 }, + >; - impl Reply { - pub fn ok(self) -> Done<'o> { - // No reply for forget requests - Done::new() - } - } + type ReplyTail = state::Readdir<()>; } -op! { - Getattr { - type RequestBody = &'o proto::GetattrIn; - type ReplyTail = (); - } - - impl Request { - pub fn handle(&self) -> u64 { - self.body.fh - } - } +impl<'o> Operation<'o> for Releasedir { + type RequestBody = &'o proto::ReleasedirIn; + type ReplyTail = (); +} - impl Reply { - pub fn known(self, inode: &impl Stat) -> Done<'o> { - let (attrs, ttl) = inode.attrs(); - let attrs = attrs.finish(inode); +impl<'o> Operation<'o> for Access { + type RequestBody = &'o proto::AccessIn; + type ReplyTail = (); +} - self.single(&proto::AttrOut { - attr_valid: ttl.seconds, - attr_valid_nsec: ttl.nanoseconds, - dummy: Default::default(), - attr: attrs, - }) - } - } +impl<'o, B> Operation<'o> for BufferedReaddir<B> { + type RequestBody = (); // Never actually created + type ReplyTail = state::Readdir<B>; } -op! { - Readlink { - type RequestBody = (); - type ReplyTail = (); +impl<'o> Request<'o, Lookup> { + /// Returns the name of the entry being looked up in this directory. + pub fn name(&self) -> &OsStr { + c_to_os(self.body) } +} - impl Reply { - /// 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()])) - } +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()); - /// 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)) - } - } -} + let done = self.single(&make_entry((entry.inode().ino(), ttl), (attrs, attrs_ttl))); + entry.unveil(); -op! { - Open { - type RequestBody = &'o proto::OpenIn; - type ReplyTail = proto::OpenOutFlags; + done } - impl Request { - pub fn flags(&self) -> OpenFlags { - OpenFlags::from_bits_truncate(self.body.flags.try_into().unwrap_or_default()) - } + /// 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( + (Ino::NULL, ttl), + (Zeroable::zeroed(), Ttl::NULL), + )) } - impl Reply { - 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(), - }) - } + /// 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) } } -op! { - Read { - type RequestBody = &'o proto::ReadIn; - type ReplyTail = (); - } +impl<'o> Request<'o, Forget> { + pub fn forget_list(&self) -> impl '_ + Iterator<Item = (Ino, u64)> { + use proto::OpcodeSelect::*; - impl Request { - pub fn handle(&self) -> u64 { - self.body.fh + enum List<'a> { + Single(Option<(Ino, u64)>), + Batch(std::slice::Iter<'a, proto::ForgetOne>), } - pub fn offset(&self) -> u64 { - self.body.offset + impl Iterator for List<'_> { + type Item = (Ino, u64); + + fn next(&mut self) -> Option<Self::Item> { + match self { + List::Single(single) => single.take(), + List::Batch(batch) => { + let forget = batch.next()?; + Some((Ino(forget.ino), forget.nlookup)) + } + } + } } - pub fn size(&self) -> u32 { - self.body.size + match self.body { + Match((_, slice)) => List::Batch(slice.iter()), + Alt(single) => List::Single(Some((self.ino(), single.nlookup))), } } +} - impl Reply { - pub fn slice(self, data: &[u8]) -> Done<'o> { - self.chain(OutputChain::tail(&[data])) - } +impl<'o> Reply<'o, Forget> { + pub fn ok(self) -> Done<'o> { + // No reply for forget requests + Done::new() } } -op! { - Write { - type RequestBody = (&'o proto::WriteIn, &'o [u8]); - type ReplyTail = state::Write; +impl<'o> Request<'o, Getattr> { + pub fn handle(&self) -> u64 { + self.body.fh } +} - impl Request { - pub fn handle(&self) -> u64 { - self.body.0.fh - } +impl<'o> Reply<'o, Getattr> { + pub fn known(self, inode: &impl Stat) -> Done<'o> { + let (attrs, ttl) = inode.attrs(); + let attrs = attrs.finish(inode); - pub fn offset(&self) -> u64 { - self.body.0.offset - } + self.single(&proto::AttrOut { + attr_valid: ttl.seconds, + attr_valid_nsec: ttl.nanoseconds, + dummy: Default::default(), + attr: attrs, + }) + } +} - pub fn data(&self) -> &[u8] { - self.body.1 - } +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()])) } - impl Reply { - pub fn all(self) -> Done<'o> { - let size = self.tail.size; - self.single(&proto::WriteOut { - size, - padding: Default::default(), - }) - } + /// 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)) } } -op! { - Init { - type RequestBody = &'o proto::InitIn; - type ReplyTail = state::Init; - } - - impl Reply { - pub fn ok(self) -> Done<'o> { - let state::Init { kernel_flags, buffer_pages } = self.tail; - - let flags = { - use proto::InitFlags; - - //TODO: Conditions for these feature flags - // - Locks - // - ASYNC_DIO - // - WRITEBACK_CACHE - // - NO_OPEN_SUPPORT - // - HANDLE_KILLPRIV - // - POSIX_ACL - // - NO_OPENDIR_SUPPORT - // - EXPLICIT_INVAL_DATA - - let supported = InitFlags::ASYNC_READ - | InitFlags::FILE_OPS - | InitFlags::ATOMIC_O_TRUNC - | InitFlags::EXPORT_SUPPORT - | InitFlags::BIG_WRITES - | InitFlags::HAS_IOCTL_DIR - | InitFlags::AUTO_INVAL_DATA - | InitFlags::DO_READDIRPLUS - | InitFlags::READDIRPLUS_AUTO - | InitFlags::PARALLEL_DIROPS - | InitFlags::ABORT_ERROR - | InitFlags::MAX_PAGES - | InitFlags::CACHE_SYMLINKS; - - kernel_flags & supported - }; - - let buffer_size = page_size() * buffer_pages; - - // 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 { - major: proto::MAJOR_VERSION, - minor: proto::TARGET_MINOR_VERSION, - max_readahead: 0, //TODO - flags: flags.bits(), - max_background: 0, //TODO - congestion_threshold: 0, //TODO - max_write: max_write.try_into().unwrap(), - time_gran: 1, //TODO - max_pages: buffer_pages.try_into().unwrap(), - padding: Default::default(), - unused: Default::default(), - }) - } +impl<'o> Request<'o, Open> { + pub fn flags(&self) -> OpenFlags { + OpenFlags::from_bits_truncate(self.body.flags.try_into().unwrap_or_default()) } } -op! { - Statfs { - type RequestBody = (); - type ReplyTail = (); +impl<'o> Reply<'o, Open> { + pub fn force_direct_io(&mut self) { + self.tail |= proto::OpenOutFlags::DIRECT_IO; } - impl Reply { - /// Replies with filesystem statistics. - pub fn info(self, statfs: &FsInfo) -> Done<'o> { - self.single(&proto::StatfsOut::from(*statfs)) - } + 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(), + }) } } -op! { - Release { - type RequestBody = &'o proto::ReleaseIn; - type ReplyTail = (); +impl<'o> Request<'o, Read> { + pub fn handle(&self) -> u64 { + self.body.fh } - impl Request { - pub fn handle(&self) -> u64 { - self.body.fh - } + pub fn offset(&self) -> u64 { + self.body.offset } - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() - } + pub fn size(&self) -> u32 { + self.body.size } } -op! { - Setxattr { - // header, name, value - type RequestBody = (&'o proto::SetxattrIn, &'o CStr, &'o [u8]); - type ReplyTail = (); +impl<'o> Reply<'o, Read> { + pub fn slice(self, data: &[u8]) -> Done<'o> { + self.chain(OutputChain::tail(&[data])) } +} - //TODO: flags - impl Request { - pub fn name(&self) -> &OsStr { - let (_header, name, _value) = self.body; - c_to_os(name) - } - - pub fn value(&self) -> &[u8] { - let (_header, _name, value) = self.body; - value - } +impl<'o> Request<'o, Write> { + pub fn handle(&self) -> u64 { + self.body.0.fh } - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() - } + pub fn offset(&self) -> u64 { + self.body.0.offset + } - pub fn not_found(self) -> Done<'o> { - self.fail(Errno::ENODATA) - } + pub fn data(&self) -> &[u8] { + self.body.1 } } -op! { - Getxattr { - type RequestBody = (&'o proto::GetxattrIn, &'o CStr); - type ReplyTail = state::ReadXattr; +impl<'o> Reply<'o, Write> { + pub fn all(self) -> Done<'o> { + let size = self.tail.size; + self.single(&proto::WriteOut { + size, + padding: Default::default(), + }) } +} - impl Request { - pub fn size(&self) -> u32 { - self.body.0.size - } +impl<'o> Reply<'o, Init> { + pub fn ok(self) -> Done<'o> { + let state::Init { + kernel_flags, + buffer_pages, + } = self.tail; - pub fn name(&self) -> &OsStr { - c_to_os(self.body.1) - } - } + let flags = { + use proto::InitFlags; - impl Reply { - 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(); - } + //TODO: Conditions for these feature flags + // - Locks + // - ASYNC_DIO + // - WRITEBACK_CACHE + // - NO_OPEN_SUPPORT + // - HANDLE_KILLPRIV + // - POSIX_ACL + // - NO_OPENDIR_SUPPORT + // - EXPLICIT_INVAL_DATA - self.chain(OutputChain::tail(&[value])) - } + let supported = InitFlags::ASYNC_READ + | InitFlags::FILE_OPS + | InitFlags::ATOMIC_O_TRUNC + | InitFlags::EXPORT_SUPPORT + | InitFlags::BIG_WRITES + | InitFlags::HAS_IOCTL_DIR + | InitFlags::AUTO_INVAL_DATA + | InitFlags::DO_READDIRPLUS + | InitFlags::READDIRPLUS_AUTO + | InitFlags::PARALLEL_DIROPS + | InitFlags::ABORT_ERROR + | InitFlags::MAX_PAGES + | InitFlags::CACHE_SYMLINKS; - pub fn value_size(self, size: u32) -> Done<'o> { - assert_eq!(self.tail.size, 0); + kernel_flags & supported + }; - self.single(&proto::GetxattrOut { - size, - padding: Default::default(), - }) - } + let buffer_size = page_size() * buffer_pages; - pub fn buffer_too_small(self) -> Done<'o> { - self.fail(Errno::ERANGE) - } + // 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)>(); - pub fn not_found(self) -> Done<'o> { - self.fail(Errno::ENODATA) - } + self.single(&proto::InitOut { + major: proto::MAJOR_VERSION, + minor: proto::TARGET_MINOR_VERSION, + max_readahead: 0, //TODO + flags: flags.bits(), + max_background: 0, //TODO + congestion_threshold: 0, //TODO + max_write: max_write.try_into().unwrap(), + time_gran: 1, //TODO + max_pages: buffer_pages.try_into().unwrap(), + padding: Default::default(), + unused: Default::default(), + }) + } +} + +impl<'o> Reply<'o, Statfs> { + /// Replies with filesystem statistics. + pub fn info(self, statfs: &FsInfo) -> Done<'o> { + self.single(&proto::StatfsOut::from(*statfs)) } } -op! { - Listxattr { - type RequestBody = &'o proto::ListxattrIn; - type ReplyTail = state::ReadXattr; +impl<'o> Request<'o, Release> { + pub fn handle(&self) -> u64 { + self.body.fh } +} - impl Request { - pub fn size(&self) -> u32 { - self.body.getxattr_in.size - } +impl<'o> Reply<'o, Release> { + pub fn ok(self) -> Done<'o> { + self.empty() } +} - impl Reply { - //TODO: buffered(), gather() +//TODO: flags +impl<'o> Request<'o, Setxattr> { + pub fn name(&self) -> &OsStr { + let (_header, name, _value) = self.body; + c_to_os(name) + } - pub fn value_size(self, size: u32) -> Done<'o> { - assert_eq!(self.tail.size, 0); + pub fn value(&self) -> &[u8] { + let (_header, _name, value) = self.body; + value + } +} - self.single(&proto::ListxattrOut { - getxattr_out: proto::GetxattrOut { - size, - padding: Default::default(), - }, - }) - } +impl<'o> Reply<'o, Setxattr> { + pub fn ok(self) -> Done<'o> { + self.empty() + } - pub fn buffer_too_small(self) -> Done<'o> { - self.fail(Errno::ERANGE) - } + pub fn not_found(self) -> Done<'o> { + self.fail(Errno::ENODATA) } } -op! { - Removexattr { - type RequestBody = &'o CStr; - type ReplyTail = (); +impl<'o> Request<'o, Getxattr> { + pub fn size(&self) -> u32 { + self.body.0.size } - impl Request { - pub fn name(&self) -> &OsStr { - c_to_os(self.body) - } + pub fn name(&self) -> &OsStr { + c_to_os(self.body.1) } +} - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() +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(); } + + self.chain(OutputChain::tail(&[value])) } -} -op! { - Flush { - type RequestBody = &'o proto::FlushIn; - type ReplyTail = (); + pub fn value_size(self, size: u32) -> Done<'o> { + assert_eq!(self.tail.size, 0); + + self.single(&proto::GetxattrOut { + size, + padding: Default::default(), + }) } - impl Request { - pub fn handle(&self) -> u64 { - self.body.fh - } + pub fn buffer_too_small(self) -> Done<'o> { + self.fail(Errno::ERANGE) } - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() - } + pub fn not_found(self) -> Done<'o> { + self.fail(Errno::ENODATA) } } -op! { - Opendir { - type RequestBody = &'o proto::OpendirIn; - type ReplyTail = (); +impl<'o> Request<'o, Listxattr> { + pub fn size(&self) -> u32 { + self.body.getxattr_in.size } +} - impl Reply { - pub fn ok(self) -> Done<'o> { - self.ok_with_handle(0) - } +impl<'o> Reply<'o, Listxattr> { + //TODO: buffered(), gather() + + pub fn value_size(self, size: u32) -> Done<'o> { + assert_eq!(self.tail.size, 0); - pub fn ok_with_handle(self, handle: u64) -> Done<'o> { - self.single(&proto::OpenOut { - fh: handle, - open_flags: 0, + self.single(&proto::ListxattrOut { + getxattr_out: proto::GetxattrOut { + size, padding: Default::default(), - }) - } + }, + }) + } + + pub fn buffer_too_small(self) -> Done<'o> { + self.fail(Errno::ERANGE) } } -op! { - Readdir { - type RequestBody = proto::OpcodeSelect< - &'o proto::ReaddirPlusIn, - &'o proto::ReaddirIn, - { proto::Opcode::ReaddirPlus as u32 }, - >; +impl<'o> Request<'o, Removexattr> { + pub fn name(&self) -> &OsStr { + c_to_os(self.body) + } +} - type ReplyTail = state::Readdir<()>; +impl<'o> Reply<'o, Removexattr> { + pub fn ok(self) -> Done<'o> { + self.empty() } +} - impl Request { - pub fn handle(&self) -> u64 { - self.read_in().fh - } +impl<'o> Request<'o, Flush> { + pub fn handle(&self) -> u64 { + self.body.fh + } +} - /// Returns the base offset in the directory stream to read from. - pub fn offset(&self) -> u64 { - self.read_in().offset - } +impl<'o> Reply<'o, Flush> { + pub fn ok(self) -> Done<'o> { + self.empty() + } +} - pub fn size(&self) -> u32 { - self.read_in().size - } +impl<'o> Reply<'o, Opendir> { + pub fn ok(self) -> Done<'o> { + self.ok_with_handle(0) + } - fn read_in(&self) -> &proto::ReadIn { - use proto::OpcodeSelect::*; + pub fn ok_with_handle(self, handle: u64) -> Done<'o> { + self.single(&proto::OpenOut { + fh: handle, + open_flags: 0, + padding: Default::default(), + }) + } +} - match &self.body { - Match(readdir_plus) => &readdir_plus.read_in, - Alt(readdir) => &readdir.read_in, - } - } +impl<'o> Request<'o, Readdir> { + pub fn handle(&self) -> u64 { + self.read_in().fh } - impl Reply { - pub fn buffered<B>(self, buffer: B) -> Reply<'o, BufferedReaddir<B>> - where - B: BufMut + AsRef<[u8]>, - { - assert!(buffer.as_ref().is_empty()); + /// Returns the base offset in the directory stream to read from. + pub fn offset(&self) -> u64 { + self.read_in().offset + } - let state::Readdir { max_read, is_plus, buffer: (), } = self.tail; + pub fn size(&self) -> u32 { + self.read_in().size + } - Reply { - session: self.session, - unique: self.unique, - tail: state::Readdir { max_read, is_plus, buffer, }, - } + fn read_in(&self) -> &proto::ReadIn { + use proto::OpcodeSelect::*; + + match &self.body { + Match(readdir_plus) => &readdir_plus.read_in, + Alt(readdir) => &readdir.read_in, } } } -pub struct BufferedReaddir<B>(Infallible, PhantomData<B>); +impl<'o> Reply<'o, Readdir> { + pub fn buffered<B>(self, buffer: B) -> Reply<'o, BufferedReaddir<B>> + where + B: BufMut + AsRef<[u8]>, + { + assert!(buffer.as_ref().is_empty()); -impl<B> Sealed for BufferedReaddir<B> {} + let state::Readdir { + max_read, + is_plus, + buffer: (), + } = self.tail; -impl<'o, B> Operation<'o> for BufferedReaddir<B> { - type RequestBody = (); // Never actually created - type ReplyTail = state::Readdir<B>; + Reply { + session: self.session, + unique: self.unique, + tail: state::Readdir { + max_read, + is_plus, + buffer, + }, + } + } } impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { @@ -693,45 +700,31 @@ impl<'o, B: BufMut + AsRef<[u8]>> Reply<'o, BufferedReaddir<B>> { } } -op! { - Releasedir { - type RequestBody = &'o proto::ReleasedirIn; - type ReplyTail = (); - } - - impl Request { - pub fn handle(&self) -> u64 { - self.body.release_in.fh - } +impl<'o> Request<'o, Releasedir> { + pub fn handle(&self) -> u64 { + self.body.release_in.fh } +} - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() - } +impl<'o> Reply<'o, Releasedir> { + pub fn ok(self) -> Done<'o> { + self.empty() } } -op! { - Access { - type RequestBody = &'o proto::AccessIn; - type ReplyTail = (); +impl<'o> Request<'o, Access> { + pub fn mask(&self) -> AccessFlags { + AccessFlags::from_bits_truncate(self.body.mask as i32) } +} - impl Request { - pub fn mask(&self) -> AccessFlags { - AccessFlags::from_bits_truncate(self.body.mask as i32) - } +impl<'o> Reply<'o, Access> { + pub fn ok(self) -> Done<'o> { + self.empty() } - impl Reply { - pub fn ok(self) -> Done<'o> { - self.empty() - } - - pub fn permission_denied(self) -> Done<'o> { - self.fail(Errno::EACCES) - } + pub fn permission_denied(self) -> Done<'o> { + self.fail(Errno::EACCES) } } |
