use super::fuse_abi::{fuse_in_header, fuse_opcode, InvalidOpcodeError};
use super::{fuse_abi as abi, Errno, Response};
#[cfg(feature = "serializable")]
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt::Display, path::Path};
use std::{error, fmt, mem};
use super::argument::ArgumentIterator;
#[derive(Debug)]
pub enum RequestError {
ShortReadHeader(usize),
UnknownOperation(u32),
ShortRead(usize, usize),
InsufficientData,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
pub struct RequestId(pub u64);
impl From<RequestId> for u64 {
fn from(fh: RequestId) -> Self {
fh.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
pub struct INodeNo(pub u64);
impl From<INodeNo> for u64 {
fn from(fh: INodeNo) -> Self {
fh.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
pub struct FileHandle(pub u64);
impl From<FileHandle> for u64 {
fn from(fh: FileHandle) -> Self {
fh.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
pub struct LockOwner(pub u64);
impl From<LockOwner> for u64 {
fn from(fh: LockOwner) -> Self {
fh.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Lock {
pub range: (u64, u64),
pub typ: i32,
pub pid: u32,
}
impl Lock {
fn from_abi(x: &abi::fuse_file_lock) -> Lock {
Lock {
range: (x.start, x.end),
typ: x.typ,
pid: x.pid,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
pub struct Version(pub u32, pub u32);
impl Version {
pub fn major(&self) -> u32 {
self.0
}
pub fn minor(&self) -> u32 {
self.1
}
}
impl Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.0, self.1)
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct FilenameInDir<'a> {
pub dir: INodeNo,
pub name: &'a Path,
}
impl fmt::Display for RequestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RequestError::ShortReadHeader(len) => write!(
f,
"Short read of FUSE request header ({} < {})",
len,
mem::size_of::<fuse_in_header>()
),
RequestError::UnknownOperation(opcode) => write!(f, "Unknown FUSE opcode ({})", opcode),
RequestError::ShortRead(len, total) => {
write!(f, "Short read of FUSE request ({} < {})", len, total)
}
RequestError::InsufficientData => write!(f, "Insufficient argument data"),
}
}
}
impl error::Error for RequestError {}
pub trait Request: Sized {
fn unique(&self) -> RequestId;
fn nodeid(&self) -> INodeNo;
fn uid(&self) -> u32;
fn gid(&self) -> u32;
fn pid(&self) -> u32;
fn reply_err(&self, errno: Errno) -> Response {
Response::new_error(errno)
}
}
macro_rules! impl_request {
($structname: ty) => {
impl<'a> super::Request for $structname {
#[inline]
fn unique(&self) -> RequestId {
RequestId(self.header.unique)
}
#[inline]
fn nodeid(&self) -> INodeNo {
INodeNo(self.header.nodeid)
}
#[inline]
fn uid(&self) -> u32 {
self.header.uid
}
#[inline]
fn gid(&self) -> u32 {
self.header.gid
}
#[inline]
fn pid(&self) -> u32 {
self.header.pid
}
}
};
}
mod op {
use crate::ll::Response;
use super::{
super::{argument::ArgumentIterator, TimeOrNow},
FilenameInDir, Request,
};
use super::{
abi::consts::*, abi::*, FileHandle, INodeNo, Lock, LockOwner, Operation, RequestId,
};
use std::{
convert::TryInto,
ffi::OsStr,
fmt::Display,
num::NonZeroU32,
path::Path,
time::{Duration, SystemTime},
};
use zerocopy::AsBytes;
#[derive(Debug)]
pub struct Lookup<'a> {
header: &'a fuse_in_header,
name: &'a OsStr,
}
impl_request!(Lookup<'_>);
impl<'a> Lookup<'a> {
pub fn name(&self) -> &'a Path {
self.name.as_ref()
}
}
#[derive(Debug)]
pub struct Forget<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_forget_in,
}
impl_request!(Forget<'_>);
impl<'a> Forget<'a> {
pub fn nlookup(&self) -> u64 {
self.arg.nlookup
}
}
#[derive(Debug)]
pub struct GetAttr<'a> {
header: &'a fuse_in_header,
}
impl_request!(GetAttr<'_>);
#[derive(Debug)]
pub struct SetAttr<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_setattr_in,
}
impl_request!(SetAttr<'_>);
impl<'a> SetAttr<'a> {
pub fn mode(&self) -> Option<u32> {
match self.arg.valid & FATTR_MODE {
0 => None,
_ => Some(self.arg.mode),
}
}
pub fn uid(&self) -> Option<u32> {
match self.arg.valid & FATTR_UID {
0 => None,
_ => Some(self.arg.uid),
}
}
pub fn gid(&self) -> Option<u32> {
match self.arg.valid & FATTR_GID {
0 => None,
_ => Some(self.arg.gid),
}
}
pub fn size(&self) -> Option<u64> {
match self.arg.valid & FATTR_SIZE {
0 => None,
_ => Some(self.arg.size),
}
}
pub fn atime(&self) -> Option<TimeOrNow> {
match self.arg.valid & FATTR_ATIME {
0 => None,
_ => Some(if self.arg.atime_now() {
TimeOrNow::Now
} else {
TimeOrNow::SpecificTime(system_time_from_time(
self.arg.atime,
self.arg.atimensec,
))
}),
}
}
pub fn mtime(&self) -> Option<TimeOrNow> {
match self.arg.valid & FATTR_MTIME {
0 => None,
_ => Some(if self.arg.mtime_now() {
TimeOrNow::Now
} else {
TimeOrNow::SpecificTime(system_time_from_time(
self.arg.mtime,
self.arg.mtimensec,
))
}),
}
}
pub fn ctime(&self) -> Option<SystemTime> {
#[cfg(feature = "abi-7-23")]
match self.arg.valid & FATTR_CTIME {
0 => None,
_ => Some(system_time_from_time(self.arg.ctime, self.arg.ctimensec)),
}
#[cfg(not(feature = "abi-7-23"))]
None
}
pub fn file_handle(&self) -> Option<FileHandle> {
match self.arg.valid & FATTR_FH {
0 => None,
_ => Some(FileHandle(self.arg.fh)),
}
}
pub fn crtime(&self) -> Option<SystemTime> {
#[cfg(target_os = "macos")]
match self.arg.valid & FATTR_CRTIME {
0 => None,
_ => Some(
SystemTime::UNIX_EPOCH + Duration::new(self.arg.crtime, self.arg.crtimensec),
),
}
#[cfg(not(target_os = "macos"))]
None
}
pub fn chgtime(&self) -> Option<SystemTime> {
#[cfg(target_os = "macos")]
match self.arg.valid & FATTR_CHGTIME {
0 => None,
_ => Some(
SystemTime::UNIX_EPOCH + Duration::new(self.arg.chgtime, self.arg.chgtimensec),
),
}
#[cfg(not(target_os = "macos"))]
None
}
pub fn bkuptime(&self) -> Option<SystemTime> {
#[cfg(target_os = "macos")]
match self.arg.valid & FATTR_BKUPTIME {
0 => None,
_ => Some(
SystemTime::UNIX_EPOCH
+ Duration::new(self.arg.bkuptime, self.arg.bkuptimensec),
),
}
#[cfg(not(target_os = "macos"))]
None
}
pub fn flags(&self) -> Option<u32> {
#[cfg(target_os = "macos")]
match self.arg.valid & FATTR_FLAGS {
0 => None,
_ => Some(self.arg.flags),
}
#[cfg(not(target_os = "macos"))]
None
}
}
impl<'a> Display for SetAttr<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SETATTR mode: {:?}, uid: {:?}, gid: {:?}, size: {:?}, atime: {:?}, \
mtime: {:?}, ctime: {:?}, file_handle: {:?}, crtime: {:?}, chgtime: {:?}, \
bkuptime: {:?}, flags: {:?}",
self.mode(),
self.uid(),
self.gid(),
self.size(),
self.atime(),
self.mtime(),
self.ctime(),
self.file_handle(),
self.crtime(),
self.chgtime(),
self.bkuptime(),
self.flags()
)
}
}
#[derive(Debug)]
pub struct ReadLink<'a> {
header: &'a fuse_in_header,
}
impl_request!(ReadLink<'_>);
#[derive(Debug)]
pub struct SymLink<'a> {
header: &'a fuse_in_header,
target: &'a Path,
link: &'a Path,
}
impl_request!(SymLink<'_>);
impl<'a> SymLink<'a> {
pub fn target(&self) -> &'a Path {
self.target
}
pub fn link(&self) -> &'a Path {
self.link
}
}
#[derive(Debug)]
pub struct MkNod<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_mknod_in,
name: &'a Path,
}
impl_request!(MkNod<'_>);
impl<'a> MkNod<'a> {
pub fn name(&self) -> &'a Path {
self.name
}
pub fn mode(&self) -> u32 {
self.arg.mode
}
pub fn umask(&self) -> u32 {
#[cfg(not(feature = "abi-7-12"))]
return 0;
#[cfg(feature = "abi-7-12")]
self.arg.umask
}
pub fn rdev(&self) -> u32 {
self.arg.rdev
}
}
#[derive(Debug)]
pub struct MkDir<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_mkdir_in,
name: &'a Path,
}
impl_request!(MkDir<'_>);
impl<'a> MkDir<'a> {
pub fn name(&self) -> &'a Path {
self.name
}
pub fn mode(&self) -> u32 {
self.arg.mode
}
pub fn umask(&self) -> u32 {
#[cfg(not(feature = "abi-7-12"))]
return 0;
#[cfg(feature = "abi-7-12")]
self.arg.umask
}
}
#[derive(Debug)]
pub struct Unlink<'a> {
header: &'a fuse_in_header,
name: &'a Path,
}
impl_request!(Unlink<'_>);
impl<'a> Unlink<'a> {
pub fn name(&self) -> &'a Path {
self.name
}
}
#[derive(Debug)]
pub struct RmDir<'a> {
header: &'a fuse_in_header,
pub name: &'a Path,
}
impl_request!(RmDir<'_>);
impl<'a> RmDir<'a> {
pub fn name(&self) -> &'a Path {
self.name
}
}
#[derive(Debug)]
pub struct Rename<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_rename_in,
name: &'a Path,
newname: &'a Path,
}
impl_request!(Rename<'_>);
impl<'a> Rename<'a> {
pub fn src(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: self.nodeid(),
name: self.name,
}
}
pub fn dest(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: INodeNo(self.arg.newdir),
name: self.newname,
}
}
}
#[derive(Debug)]
pub struct Link<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_link_in,
name: &'a Path,
}
impl_request!(Link<'_>);
impl<'a> Link<'a> {
pub fn inode_no(&self) -> INodeNo {
INodeNo(self.arg.oldnodeid)
}
pub fn dest(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: self.nodeid(),
name: self.name,
}
}
}
#[derive(Debug)]
pub struct Open<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_open_in,
}
impl_request!(Open<'_>);
impl<'a> Open<'a> {
pub fn flags(&self) -> i32 {
self.arg.flags
}
}
#[derive(Debug)]
pub struct Read<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_read_in,
}
impl_request!(Read<'_>);
impl<'a> Read<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn size(&self) -> u32 {
self.arg.size
}
pub fn lock_owner(&self) -> Option<LockOwner> {
#[cfg(not(feature = "abi-7-9"))]
return None;
#[cfg(feature = "abi-7-9")]
if self.arg.read_flags & FUSE_READ_LOCKOWNER != 0 {
Some(LockOwner(self.arg.lock_owner))
} else {
None
}
}
pub fn flags(&self) -> i32 {
#[cfg(not(feature = "abi-7-9"))]
return 0;
#[cfg(feature = "abi-7-9")]
self.arg.flags
}
}
#[derive(Debug)]
pub struct Write<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_write_in,
data: &'a [u8],
}
impl_request!(Write<'_>);
impl<'a> Write<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn data(&self) -> &'a [u8] {
self.data
}
pub fn write_flags(&self) -> u32 {
self.arg.write_flags
}
pub fn lock_owner(&self) -> Option<LockOwner> {
#[cfg(feature = "abi-7-9")]
if self.arg.write_flags & FUSE_WRITE_LOCKOWNER != 0 {
Some(LockOwner(self.arg.lock_owner))
} else {
None
}
#[cfg(not(feature = "abi-7-9"))]
None
}
pub fn flags(&self) -> i32 {
#[cfg(feature = "abi-7-9")]
return self.arg.flags;
#[cfg(not(feature = "abi-7-9"))]
0
}
}
#[derive(Debug)]
pub struct StatFs<'a> {
header: &'a fuse_in_header,
}
impl_request!(StatFs<'_>);
#[derive(Debug)]
pub struct Release<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_release_in,
}
impl_request!(Release<'_>);
impl<'a> Release<'a> {
pub fn flush(&self) -> bool {
self.arg.release_flags & FUSE_RELEASE_FLUSH != 0
}
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn flags(&self) -> i32 {
self.arg.flags
}
pub fn lock_owner(&self) -> Option<LockOwner> {
#[cfg(not(feature = "abi-7-17"))]
return Some(LockOwner(self.arg.lock_owner));
#[cfg(feature = "abi-7-17")]
if self.arg.release_flags & FUSE_RELEASE_FLOCK_UNLOCK != 0 {
Some(LockOwner(self.arg.lock_owner))
} else {
None
}
}
}
#[derive(Debug)]
pub struct FSync<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_fsync_in,
}
impl_request!(FSync<'a>);
impl<'a> FSync<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn fdatasync(&self) -> bool {
self.arg.fsync_flags & consts::FUSE_FSYNC_FDATASYNC != 0
}
}
#[derive(Debug)]
pub struct SetXAttr<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_setxattr_in,
name: &'a OsStr,
value: &'a [u8],
}
impl_request!(SetXAttr<'a>);
impl<'a> SetXAttr<'a> {
pub fn name(&self) -> &'a OsStr {
self.name
}
pub fn value(&self) -> &'a [u8] {
self.value
}
pub fn flags(&self) -> i32 {
self.arg.flags
}
pub fn position(&self) -> u32 {
#[cfg(target_os = "macos")]
return self.arg.position;
#[cfg(not(target_os = "macos"))]
0
}
}
#[derive(Debug)]
pub struct GetXAttr<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_getxattr_in,
name: &'a OsStr,
}
impl_request!(GetXAttr<'a>);
#[derive(Debug)]
pub struct GetXAttrSize();
#[derive(Debug)]
pub enum GetXAttrSizeEnum {
GetSize(GetXAttrSize),
Size(NonZeroU32),
}
impl<'a> GetXAttr<'a> {
pub fn name(&self) -> &'a OsStr {
self.name
}
pub fn size(&self) -> GetXAttrSizeEnum {
let s: Result<NonZeroU32, _> = self.arg.size.try_into();
match s {
Ok(s) => GetXAttrSizeEnum::Size(s),
Err(_) => GetXAttrSizeEnum::GetSize(GetXAttrSize()),
}
}
pub(crate) fn size_u32(&self) -> u32 {
self.arg.size
}
}
#[derive(Debug)]
pub struct ListXAttr<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_getxattr_in,
}
impl_request!(ListXAttr<'a>);
impl<'a> ListXAttr<'a> {
pub fn size(&self) -> u32 {
self.arg.size
}
}
#[derive(Debug)]
pub struct RemoveXAttr<'a> {
header: &'a fuse_in_header,
name: &'a OsStr,
}
impl_request!(RemoveXAttr<'a>);
impl<'a> RemoveXAttr<'a> {
pub fn name(&self) -> &'a OsStr {
self.name
}
}
#[derive(Debug)]
pub struct Flush<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_flush_in,
}
impl_request!(Flush<'a>);
impl<'a> Flush<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn lock_owner(&self) -> LockOwner {
LockOwner(self.arg.lock_owner)
}
}
#[derive(Debug)]
pub struct Init<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_init_in,
}
impl_request!(Init<'a>);
impl<'a> Init<'a> {
pub fn capabilities(&self) -> u32 {
self.arg.flags
}
pub fn max_readahead(&self) -> u32 {
self.arg.max_readahead
}
pub fn version(&self) -> super::Version {
super::Version(self.arg.major, self.arg.minor)
}
pub fn reply(&self, config: &crate::KernelConfig) -> Response {
let init = fuse_init_out {
major: FUSE_KERNEL_VERSION,
minor: FUSE_KERNEL_MINOR_VERSION,
max_readahead: config.max_readahead,
flags: self.capabilities() & config.requested, #[cfg(not(feature = "abi-7-13"))]
unused: 0,
#[cfg(feature = "abi-7-13")]
max_background: config.max_background,
#[cfg(feature = "abi-7-13")]
congestion_threshold: config.congestion_threshold(),
max_write: config.max_write,
#[cfg(feature = "abi-7-23")]
time_gran: config.time_gran.as_nanos() as u32,
#[cfg(all(feature = "abi-7-23", not(feature = "abi-7-28")))]
reserved: [0; 9],
#[cfg(feature = "abi-7-28")]
max_pages: config.max_pages(),
#[cfg(feature = "abi-7-28")]
unused2: 0,
#[cfg(feature = "abi-7-28")]
reserved: [0; 8],
};
Response::new_data(init.as_bytes())
}
}
#[derive(Debug)]
pub struct OpenDir<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_open_in,
}
impl_request!(OpenDir<'a>);
impl<'a> OpenDir<'a> {
pub fn flags(&self) -> i32 {
self.arg.flags
}
}
#[derive(Debug)]
pub struct ReadDir<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_read_in,
}
impl_request!(ReadDir<'a>);
impl<'a> ReadDir<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn size(&self) -> u32 {
self.arg.size
}
}
#[derive(Debug)]
pub struct ReleaseDir<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_release_in,
}
impl_request!(ReleaseDir<'a>);
impl<'a> ReleaseDir<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn flush(&self) -> bool {
self.arg.release_flags & consts::FUSE_RELEASE_FLUSH != 0
}
pub fn lock_owner(&self) -> Option<LockOwner> {
#[cfg(not(feature = "abi-7-17"))]
return Some(LockOwner(self.arg.lock_owner));
#[cfg(feature = "abi-7-17")]
if self.arg.release_flags & FUSE_RELEASE_FLOCK_UNLOCK != 0 {
Some(LockOwner(self.arg.lock_owner))
} else {
None
}
}
pub fn flags(&self) -> i32 {
self.arg.flags
}
}
#[derive(Debug)]
pub struct FSyncDir<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_fsync_in,
}
impl_request!(FSyncDir<'a>);
impl<'a> FSyncDir<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn fdatasync(&self) -> bool {
self.arg.fsync_flags & consts::FUSE_FSYNC_FDATASYNC != 0
}
}
#[derive(Debug)]
pub struct GetLk<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_lk_in,
}
impl_request!(GetLk<'a>);
impl<'a> GetLk<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn lock(&self) -> Lock {
Lock::from_abi(&self.arg.lk)
}
pub fn lock_owner(&self) -> LockOwner {
LockOwner(self.arg.owner)
}
}
#[derive(Debug)]
pub struct SetLk<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_lk_in,
}
impl_request!(SetLk<'a>);
impl<'a> SetLk<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn lock(&self) -> Lock {
Lock::from_abi(&self.arg.lk)
}
pub fn lock_owner(&self) -> LockOwner {
LockOwner(self.arg.owner)
}
}
#[derive(Debug)]
pub struct SetLkW<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_lk_in,
}
impl_request!(SetLkW<'a>);
impl<'a> SetLkW<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn lock(&self) -> Lock {
Lock::from_abi(&self.arg.lk)
}
pub fn lock_owner(&self) -> LockOwner {
LockOwner(self.arg.owner)
}
}
#[derive(Debug)]
pub struct Access<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_access_in,
}
impl_request!(Access<'a>);
impl<'a> Access<'a> {
pub fn mask(&self) -> i32 {
self.arg.mask
}
}
#[derive(Debug)]
pub struct Create<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_create_in,
name: &'a Path,
}
impl_request!(Create<'a>);
impl<'a> Create<'a> {
pub fn name(&self) -> &'a Path {
self.name
}
pub fn mode(&self) -> u32 {
self.arg.mode
}
pub fn flags(&self) -> i32 {
self.arg.flags
}
pub fn umask(&self) -> u32 {
#[cfg(not(feature = "abi-7-12"))]
return 0;
#[cfg(feature = "abi-7-12")]
self.arg.umask
}
}
#[derive(Debug)]
pub struct Interrupt<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_interrupt_in,
}
impl_request!(Interrupt<'a>);
impl<'a> Interrupt<'a> {
pub fn unique(&self) -> RequestId {
RequestId(self.arg.unique)
}
}
#[derive(Debug)]
pub struct BMap<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_bmap_in,
}
impl_request!(BMap<'a>);
impl<'a> BMap<'a> {
pub fn block_size(&self) -> u32 {
self.arg.blocksize
}
pub fn block(&self) -> u64 {
self.arg.block
}
}
#[derive(Debug)]
pub struct Destroy<'a> {
header: &'a fuse_in_header,
}
impl_request!(Destroy<'a>);
impl<'a> Destroy<'a> {
pub fn reply(&self) -> Response {
Response::new_empty()
}
}
#[cfg(feature = "abi-7-11")]
#[derive(Debug)]
pub struct IoCtl<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_ioctl_in,
data: &'a [u8],
}
#[cfg(feature = "abi-7-11")]
impl_request!(IoCtl<'a>);
#[cfg(feature = "abi-7-11")]
impl<'a> IoCtl<'a> {
pub fn in_data(&self) -> &[u8] {
&self.data[..self.arg.in_size as usize]
}
pub fn unrestricted(&self) -> bool {
self.arg.flags & consts::FUSE_IOCTL_UNRESTRICTED != 0
}
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn flags(&self) -> u32 {
self.arg.flags
}
pub fn command(&self) -> u32 {
self.arg.cmd
}
pub fn out_size(&self) -> u32 {
self.arg.out_size
}
}
#[cfg(feature = "abi-7-11")]
#[derive(Debug)]
pub struct Poll<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_poll_in,
}
#[cfg(feature = "abi-7-11")]
impl_request!(Poll<'a>);
#[cfg(feature = "abi-7-11")]
impl<'a> Poll<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
}
#[cfg(feature = "abi-7-15")]
#[derive(Debug)]
pub struct NotifyReply<'a> {
header: &'a fuse_in_header,
arg: &'a [u8],
}
#[cfg(feature = "abi-7-15")]
impl_request!(NotifyReply<'a>);
#[cfg(feature = "abi-7-16")]
#[derive(Debug)]
pub struct BatchForget<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_batch_forget_in,
nodes: &'a [fuse_forget_one],
}
#[cfg(feature = "abi-7-16")]
impl_request!(BatchForget<'a>);
#[cfg(feature = "abi-7-16")]
impl<'a> BatchForget<'a> {
pub fn nodes(&self) -> &'a [fuse_forget_one] {
self.nodes
}
}
#[cfg(feature = "abi-7-19")]
#[derive(Debug)]
pub struct FAllocate<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_fallocate_in,
}
#[cfg(feature = "abi-7-19")]
impl_request!(FAllocate<'a>);
#[cfg(feature = "abi-7-19")]
impl<'a> FAllocate<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn len(&self) -> i64 {
self.arg.length
}
pub fn mode(&self) -> i32 {
self.arg.mode
}
}
#[cfg(feature = "abi-7-21")]
#[derive(Debug)]
pub struct ReadDirPlus<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_read_in,
}
#[cfg(feature = "abi-7-21")]
impl_request!(ReadDirPlus<'a>);
#[cfg(feature = "abi-7-21")]
impl<'a> ReadDirPlus<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn size(&self) -> u32 {
self.arg.size
}
}
#[cfg(feature = "abi-7-23")]
#[derive(Debug)]
pub struct Rename2<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_rename2_in,
name: &'a Path,
newname: &'a Path,
old_parent: INodeNo,
}
#[cfg(feature = "abi-7-23")]
impl_request!(Rename2<'a>);
#[cfg(feature = "abi-7-23")]
impl<'a> Rename2<'a> {
pub fn from(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: self.old_parent,
name: self.name,
}
}
pub fn to(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: INodeNo(self.arg.newdir),
name: self.newname,
}
}
pub fn flags(&self) -> u32 {
self.arg.flags
}
}
#[cfg(feature = "abi-7-24")]
#[derive(Debug)]
pub struct Lseek<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_lseek_in,
}
#[cfg(feature = "abi-7-24")]
impl_request!(Lseek<'a>);
#[cfg(feature = "abi-7-24")]
impl<'a> Lseek<'a> {
pub fn file_handle(&self) -> FileHandle {
FileHandle(self.arg.fh)
}
pub fn offset(&self) -> i64 {
self.arg.offset
}
pub fn whence(&self) -> i32 {
self.arg.whence
}
}
#[derive(Debug, Clone, Copy)]
pub struct CopyFileRangeFile {
pub inode: INodeNo,
pub file_handle: FileHandle,
pub offset: i64,
}
#[cfg(feature = "abi-7-28")]
#[derive(Debug)]
pub struct CopyFileRange<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_copy_file_range_in,
}
#[cfg(feature = "abi-7-28")]
impl_request!(CopyFileRange<'a>);
#[cfg(feature = "abi-7-28")]
impl<'a> CopyFileRange<'a> {
pub fn src(&self) -> CopyFileRangeFile {
CopyFileRangeFile {
inode: self.nodeid(),
file_handle: FileHandle(self.arg.fh_in),
offset: self.arg.off_in,
}
}
pub fn dest(&self) -> CopyFileRangeFile {
CopyFileRangeFile {
inode: INodeNo(self.arg.nodeid_out),
file_handle: FileHandle(self.arg.fh_out),
offset: self.arg.off_out,
}
}
pub fn len(&self) -> u64 {
self.arg.len
}
pub fn flags(&self) -> u64 {
self.arg.flags
}
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
pub struct SetVolName<'a> {
header: &'a fuse_in_header,
name: &'a OsStr,
}
#[cfg(target_os = "macos")]
impl_request!(SetVolName<'a>);
#[cfg(target_os = "macos")]
impl<'a> SetVolName<'a> {
pub fn name(&self) -> &'a OsStr {
self.name
}
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
pub struct GetXTimes<'a> {
header: &'a fuse_in_header,
}
#[cfg(target_os = "macos")]
impl_request!(GetXTimes<'a>);
#[cfg(target_os = "macos")]
#[derive(Debug)]
pub struct Exchange<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_exchange_in,
oldname: &'a Path,
newname: &'a Path,
}
#[cfg(target_os = "macos")]
impl_request!(Exchange<'a>);
#[cfg(target_os = "macos")]
impl<'a> Exchange<'a> {
pub fn from(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: INodeNo(self.arg.olddir),
name: self.oldname,
}
}
pub fn to(&self) -> FilenameInDir<'a> {
FilenameInDir::<'a> {
dir: INodeNo(self.arg.newdir),
name: self.newname,
}
}
pub fn options(&self) -> u64 {
self.arg.options
}
}
#[cfg(feature = "abi-7-12")]
#[derive(Debug)]
pub struct CuseInit<'a> {
header: &'a fuse_in_header,
arg: &'a fuse_init_in,
}
#[cfg(feature = "abi-7-12")]
impl_request!(CuseInit<'a>);
fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime {
if secs >= 0 {
SystemTime::UNIX_EPOCH + Duration::new(secs as u64, nsecs)
} else {
SystemTime::UNIX_EPOCH - Duration::new((-secs) as u64, nsecs)
}
}
pub(crate) fn parse<'a>(
header: &'a fuse_in_header,
opcode: &fuse_opcode,
data: &'a [u8],
) -> Option<Operation<'a>> {
let mut data = ArgumentIterator::new(data);
Some(match opcode {
fuse_opcode::FUSE_LOOKUP => Operation::Lookup(Lookup {
header,
name: data.fetch_str()?,
}),
fuse_opcode::FUSE_FORGET => Operation::Forget(Forget {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_GETATTR => Operation::GetAttr(GetAttr { header }),
fuse_opcode::FUSE_SETATTR => Operation::SetAttr(SetAttr {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_READLINK => Operation::ReadLink(ReadLink { header }),
fuse_opcode::FUSE_SYMLINK => Operation::SymLink(SymLink {
header,
target: data.fetch_str()?.as_ref(),
link: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_MKNOD => Operation::MkNod(MkNod {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_MKDIR => Operation::MkDir(MkDir {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_UNLINK => Operation::Unlink(Unlink {
header,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_RMDIR => Operation::RmDir(RmDir {
header,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_RENAME => Operation::Rename(Rename {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
newname: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_LINK => Operation::Link(Link {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_OPEN => Operation::Open(Open {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_READ => Operation::Read(Read {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_WRITE => Operation::Write({
let out = Write {
header,
arg: data.fetch()?,
data: data.fetch_all(),
};
assert!(out.data().len() == out.arg.size as usize);
out
}),
fuse_opcode::FUSE_STATFS => Operation::StatFs(StatFs { header }),
fuse_opcode::FUSE_RELEASE => Operation::Release(Release {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_FSYNC => Operation::FSync(FSync {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_SETXATTR => Operation::SetXAttr({
let out = SetXAttr {
header,
arg: data.fetch()?,
name: data.fetch_str()?,
value: data.fetch_all(),
};
assert!(out.value.len() == out.arg.size as usize);
out
}),
fuse_opcode::FUSE_GETXATTR => Operation::GetXAttr(GetXAttr {
header,
arg: data.fetch()?,
name: data.fetch_str()?,
}),
fuse_opcode::FUSE_LISTXATTR => Operation::ListXAttr(ListXAttr {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_REMOVEXATTR => Operation::RemoveXAttr(RemoveXAttr {
header,
name: data.fetch_str()?,
}),
fuse_opcode::FUSE_FLUSH => Operation::Flush(Flush {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_INIT => Operation::Init(Init {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_OPENDIR => Operation::OpenDir(OpenDir {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_READDIR => Operation::ReadDir(ReadDir {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_RELEASEDIR => Operation::ReleaseDir(ReleaseDir {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_FSYNCDIR => Operation::FSyncDir(FSyncDir {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_GETLK => Operation::GetLk(GetLk {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_SETLK => Operation::SetLk(SetLk {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_SETLKW => Operation::SetLkW(SetLkW {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_ACCESS => Operation::Access(Access {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_CREATE => Operation::Create(Create {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
}),
fuse_opcode::FUSE_INTERRUPT => Operation::Interrupt(Interrupt {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_BMAP => Operation::BMap(BMap {
header,
arg: data.fetch()?,
}),
fuse_opcode::FUSE_DESTROY => Operation::Destroy(Destroy { header }),
#[cfg(feature = "abi-7-11")]
fuse_opcode::FUSE_IOCTL => Operation::IoCtl(IoCtl {
header,
arg: data.fetch()?,
data: data.fetch_all(),
}),
#[cfg(feature = "abi-7-11")]
fuse_opcode::FUSE_POLL => Operation::Poll(Poll {
header,
arg: data.fetch()?,
}),
#[cfg(feature = "abi-7-15")]
fuse_opcode::FUSE_NOTIFY_REPLY => Operation::NotifyReply(NotifyReply {
header,
arg: data.fetch_all(),
}),
#[cfg(feature = "abi-7-16")]
fuse_opcode::FUSE_BATCH_FORGET => {
let arg = data.fetch()?;
Operation::BatchForget(BatchForget {
header,
arg,
nodes: data.fetch_slice(arg.count as usize)?,
})
}
#[cfg(feature = "abi-7-19")]
fuse_opcode::FUSE_FALLOCATE => Operation::FAllocate(FAllocate {
header,
arg: data.fetch()?,
}),
#[cfg(feature = "abi-7-21")]
fuse_opcode::FUSE_READDIRPLUS => Operation::ReadDirPlus(ReadDirPlus {
header,
arg: data.fetch()?,
}),
#[cfg(feature = "abi-7-23")]
fuse_opcode::FUSE_RENAME2 => Operation::Rename2(Rename2 {
header,
arg: data.fetch()?,
name: data.fetch_str()?.as_ref(),
newname: data.fetch_str()?.as_ref(),
old_parent: INodeNo(header.nodeid),
}),
#[cfg(feature = "abi-7-24")]
fuse_opcode::FUSE_LSEEK => Operation::Lseek(Lseek {
header,
arg: data.fetch()?,
}),
#[cfg(feature = "abi-7-28")]
fuse_opcode::FUSE_COPY_FILE_RANGE => Operation::CopyFileRange(CopyFileRange {
header,
arg: data.fetch()?,
}),
#[cfg(target_os = "macos")]
fuse_opcode::FUSE_SETVOLNAME => Operation::SetVolName(SetVolName {
header,
name: data.fetch_str()?,
}),
#[cfg(target_os = "macos")]
fuse_opcode::FUSE_GETXTIMES => Operation::GetXTimes(GetXTimes { header }),
#[cfg(target_os = "macos")]
fuse_opcode::FUSE_EXCHANGE => Operation::Exchange(Exchange {
header,
arg: data.fetch()?,
oldname: data.fetch_str()?.as_ref(),
newname: data.fetch_str()?.as_ref(),
}),
#[cfg(feature = "abi-7-12")]
fuse_opcode::CUSE_INIT => Operation::CuseInit(CuseInit {
header,
arg: data.fetch()?,
}),
})
}
}
use op::*;
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Operation<'a> {
Lookup(Lookup<'a>),
Forget(Forget<'a>),
GetAttr(GetAttr<'a>),
SetAttr(SetAttr<'a>),
ReadLink(ReadLink<'a>),
SymLink(SymLink<'a>),
MkNod(MkNod<'a>),
MkDir(MkDir<'a>),
Unlink(Unlink<'a>),
RmDir(RmDir<'a>),
Rename(Rename<'a>),
Link(Link<'a>),
Open(Open<'a>),
Read(Read<'a>),
Write(Write<'a>),
StatFs(StatFs<'a>),
Release(Release<'a>),
FSync(FSync<'a>),
SetXAttr(SetXAttr<'a>),
GetXAttr(GetXAttr<'a>),
ListXAttr(ListXAttr<'a>),
RemoveXAttr(RemoveXAttr<'a>),
Flush(Flush<'a>),
Init(Init<'a>),
OpenDir(OpenDir<'a>),
ReadDir(ReadDir<'a>),
ReleaseDir(ReleaseDir<'a>),
FSyncDir(FSyncDir<'a>),
GetLk(GetLk<'a>),
SetLk(SetLk<'a>),
SetLkW(SetLkW<'a>),
Access(Access<'a>),
Create(Create<'a>),
Interrupt(Interrupt<'a>),
BMap(BMap<'a>),
Destroy(Destroy<'a>),
#[cfg(feature = "abi-7-11")]
IoCtl(IoCtl<'a>),
#[cfg(feature = "abi-7-11")]
Poll(Poll<'a>),
#[cfg(feature = "abi-7-15")]
NotifyReply(NotifyReply<'a>),
#[cfg(feature = "abi-7-16")]
BatchForget(BatchForget<'a>),
#[cfg(feature = "abi-7-19")]
FAllocate(FAllocate<'a>),
#[cfg(feature = "abi-7-21")]
ReadDirPlus(ReadDirPlus<'a>),
#[cfg(feature = "abi-7-23")]
Rename2(Rename2<'a>),
#[cfg(feature = "abi-7-24")]
Lseek(Lseek<'a>),
#[cfg(feature = "abi-7-28")]
CopyFileRange(CopyFileRange<'a>),
#[cfg(target_os = "macos")]
SetVolName(SetVolName<'a>),
#[cfg(target_os = "macos")]
GetXTimes(GetXTimes<'a>),
#[cfg(target_os = "macos")]
Exchange(Exchange<'a>),
#[cfg(feature = "abi-7-12")]
CuseInit(CuseInit<'a>),
}
impl<'a> fmt::Display for Operation<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Operation::Lookup(x) => write!(f, "LOOKUP name {:?}", x.name()),
Operation::Forget(x) => write!(f, "FORGET nlookup {}", x.nlookup()),
Operation::GetAttr(_) => write!(f, "GETATTR"),
Operation::SetAttr(x) => x.fmt(f),
Operation::ReadLink(_) => write!(f, "READLINK"),
Operation::SymLink(x) => {
write!(f, "SYMLINK target {:?}, link {:?}", x.target(), x.link())
}
Operation::MkNod(x) => write!(
f,
"MKNOD name {:?}, mode {:#05o}, rdev {}",
x.name(),
x.mode(),
x.rdev()
),
Operation::MkDir(x) => write!(f, "MKDIR name {:?}, mode {:#05o}", x.name(), x.mode()),
Operation::Unlink(x) => write!(f, "UNLINK name {:?}", x.name()),
Operation::RmDir(x) => write!(f, "RMDIR name {:?}", x.name),
Operation::Rename(x) => write!(f, "RENAME src {:?}, dest {:?}", x.src(), x.dest()),
Operation::Link(x) => write!(f, "LINK ino {:?}, dest {:?}", x.inode_no(), x.dest()),
Operation::Open(x) => write!(f, "OPEN flags {:#x}", x.flags()),
Operation::Read(x) => write!(
f,
"READ fh {:?}, offset {}, size {}",
x.file_handle(),
x.offset(),
x.size()
),
Operation::Write(x) => write!(
f,
"WRITE fh {:?}, offset {}, size {}, write flags {:#x}",
x.file_handle(),
x.offset(),
x.data().len(),
x.write_flags()
),
Operation::StatFs(_) => write!(f, "STATFS"),
Operation::Release(x) => write!(
f,
"RELEASE fh {:?}, flags {:#x}, flush {}, lock owner {:?}",
x.file_handle(),
x.flags(),
x.flush(),
x.lock_owner()
),
Operation::FSync(x) => write!(
f,
"FSYNC fh {:?}, fsync fdatasync {}",
x.file_handle(),
x.fdatasync()
),
Operation::SetXAttr(x) => write!(
f,
"SETXATTR name {:?}, size {}, flags {:#x}",
x.name(),
x.value().len(),
x.flags()
),
Operation::GetXAttr(x) => {
write!(f, "GETXATTR name {:?}, size {:?}", x.name(), x.size())
}
Operation::ListXAttr(x) => write!(f, "LISTXATTR size {}", x.size()),
Operation::RemoveXAttr(x) => write!(f, "REMOVEXATTR name {:?}", x.name()),
Operation::Flush(x) => write!(
f,
"FLUSH fh {:?}, lock owner {:?}",
x.file_handle(),
x.lock_owner()
),
Operation::Init(x) => write!(
f,
"INIT kernel ABI {}, capabilities {:#x}, max readahead {}",
x.version(),
x.capabilities(),
x.max_readahead()
),
Operation::OpenDir(x) => write!(f, "OPENDIR flags {:#x}", x.flags()),
Operation::ReadDir(x) => write!(
f,
"READDIR fh {:?}, offset {}, size {}",
x.file_handle(),
x.offset(),
x.size()
),
Operation::ReleaseDir(x) => write!(
f,
"RELEASEDIR fh {:?}, flags {:#x}, flush {}, lock owner {:?}",
x.file_handle(),
x.flags(),
x.flush(),
x.lock_owner()
),
Operation::FSyncDir(x) => write!(
f,
"FSYNCDIR fh {:?}, fsync fdatasync: {}",
x.file_handle(),
x.fdatasync()
),
Operation::GetLk(x) => write!(
f,
"GETLK fh {:?}, lock owner {:?}",
x.file_handle(),
x.lock_owner()
),
Operation::SetLk(x) => write!(
f,
"SETLK fh {:?}, lock owner {:?}",
x.file_handle(),
x.lock_owner()
),
Operation::SetLkW(x) => write!(
f,
"SETLKW fh {:?}, lock owner {:?}",
x.file_handle(),
x.lock_owner()
),
Operation::Access(x) => write!(f, "ACCESS mask {:#05o}", x.mask()),
Operation::Create(x) => write!(
f,
"CREATE name {:?}, mode {:#05o}, flags {:#x}",
x.name(),
x.mode(),
x.flags()
),
Operation::Interrupt(x) => write!(f, "INTERRUPT unique {:?}", x.unique()),
Operation::BMap(x) => write!(f, "BMAP blocksize {}, ids {}", x.block_size(), x.block()),
Operation::Destroy(_) => write!(f, "DESTROY"),
#[cfg(feature = "abi-7-11")]
Operation::IoCtl(x) => write!(
f,
"IOCTL fh {:?}, cmd {}, data size {}, flags {:#x}",
x.file_handle(),
x.command(),
x.in_data().len(),
x.flags()
),
#[cfg(feature = "abi-7-11")]
Operation::Poll(x) => write!(f, "POLL fh {:?}", x.file_handle()),
#[cfg(feature = "abi-7-15")]
Operation::NotifyReply(_) => write!(f, "NOTIFYREPLY"),
#[cfg(feature = "abi-7-16")]
Operation::BatchForget(x) => write!(f, "BATCHFORGET nodes {:?}", x.nodes()),
#[cfg(feature = "abi-7-19")]
Operation::FAllocate(_) => write!(f, "FALLOCATE"),
#[cfg(feature = "abi-7-21")]
Operation::ReadDirPlus(x) => write!(
f,
"READDIRPLUS fh {:?}, offset {}, size {}",
x.file_handle(),
x.offset(),
x.size()
),
#[cfg(feature = "abi-7-23")]
Operation::Rename2(x) => write!(f, "RENAME2 from {:?}, to {:?}", x.from(), x.to()),
#[cfg(feature = "abi-7-24")]
Operation::Lseek(x) => write!(
f,
"LSEEK fh {:?}, offset {}, whence {}",
x.file_handle(),
x.offset(),
x.whence()
),
#[cfg(feature = "abi-7-28")]
Operation::CopyFileRange(x) => write!(
f,
"COPY_FILE_RANGE src {:?}, dest {:?}, len {}",
x.src(),
x.dest(),
x.len()
),
#[cfg(target_os = "macos")]
Operation::SetVolName(x) => write!(f, "SETVOLNAME name {:?}", x.name()),
#[cfg(target_os = "macos")]
Operation::GetXTimes(_) => write!(f, "GETXTIMES"),
#[cfg(target_os = "macos")]
Operation::Exchange(x) => write!(
f,
"EXCHANGE from {:?}, to {:?}, options {:#x}",
x.from(),
x.to(),
x.options()
),
#[cfg(feature = "abi-7-12")]
Operation::CuseInit(_) => write!(f, "CUSE_INIT"),
}
}
}
#[derive(Debug)]
pub struct AnyRequest<'a> {
header: &'a fuse_in_header,
data: &'a [u8],
}
impl_request!(AnyRequest<'_>);
impl<'a> AnyRequest<'a> {
pub fn operation(&self) -> Result<Operation<'a>, RequestError> {
let opcode = fuse_opcode::try_from(self.header.opcode)
.map_err(|_: InvalidOpcodeError| RequestError::UnknownOperation(self.header.opcode))?;
op::parse(self.header, &opcode, self.data).ok_or(RequestError::InsufficientData)
}
}
impl<'a> fmt::Display for AnyRequest<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(op) = self.operation() {
write!(
f,
"FUSE({:3}) ino {:#018x} {}",
self.header.unique, self.header.nodeid, op
)
} else {
write!(
f,
"FUSE({:3}) ino {:#018x}",
self.header.unique, self.header.nodeid
)
}
}
}
impl<'a> TryFrom<&'a [u8]> for AnyRequest<'a> {
type Error = RequestError;
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
let data_len = data.len();
let mut arg_iter = ArgumentIterator::new(data);
let header: &fuse_in_header = arg_iter
.fetch()
.ok_or_else(|| RequestError::ShortReadHeader(arg_iter.len()))?;
if data_len < header.len as usize {
return Err(RequestError::ShortRead(data_len, header.len as usize));
}
Ok(Self {
header,
data: &data[mem::size_of::<fuse_in_header>()..header.len as usize],
})
}
}
#[cfg(test)]
mod tests {
use super::super::test::AlignedData;
use super::*;
use std::ffi::OsStr;
#[cfg(target_endian = "big")]
const INIT_REQUEST: AlignedData<[u8; 56]> = AlignedData([
0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1a, 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, ]);
#[cfg(target_endian = "little")]
const INIT_REQUEST: AlignedData<[u8; 56]> = AlignedData([
0x38, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]);
#[cfg(target_endian = "big")]
const MKNOD_REQUEST: AlignedData<[u8; 56]> = [
0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, ];
#[cfg(all(target_endian = "little", not(feature = "abi-7-12")))]
const MKNOD_REQUEST: AlignedData<[u8; 56]> = AlignedData([
0x38, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, ]);
#[cfg(all(target_endian = "little", feature = "abi-7-12"))]
const MKNOD_REQUEST: AlignedData<[u8; 64]> = AlignedData([
0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, ]);
#[test]
fn short_read_header() {
match AnyRequest::try_from(&INIT_REQUEST[..20]) {
Err(RequestError::ShortReadHeader(20)) => (),
_ => panic!("Unexpected request parsing result"),
}
}
#[test]
fn short_read() {
match AnyRequest::try_from(&INIT_REQUEST[..48]) {
Err(RequestError::ShortRead(48, 56)) => (),
_ => panic!("Unexpected request parsing result"),
}
}
#[test]
fn init() {
let req = AnyRequest::try_from(&INIT_REQUEST[..]).unwrap();
assert_eq!(req.header.len, 56);
assert_eq!(req.header.opcode, 26);
assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d));
assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788));
assert_eq!(req.uid(), 0xc001_d00d);
assert_eq!(req.gid(), 0xc001_cafe);
assert_eq!(req.pid(), 0xc0de_ba5e);
match req.operation().unwrap() {
Operation::Init(x) => {
assert_eq!(x.version(), Version(7, 8));
assert_eq!(x.max_readahead(), 4096);
}
_ => panic!("Unexpected request operation"),
}
}
#[test]
fn mknod() {
let req = AnyRequest::try_from(&MKNOD_REQUEST[..]).unwrap();
#[cfg(not(feature = "abi-7-12"))]
assert_eq!(req.header.len, 56);
#[cfg(feature = "abi-7-12")]
assert_eq!(req.header.len, 64);
assert_eq!(req.header.opcode, 8);
assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d));
assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788));
assert_eq!(req.uid(), 0xc001_d00d);
assert_eq!(req.gid(), 0xc001_cafe);
assert_eq!(req.pid(), 0xc0de_ba5e);
match req.operation().unwrap() {
Operation::MkNod(x) => {
assert_eq!(x.mode(), 0o644);
#[cfg(feature = "abi-7-12")]
assert_eq!(x.umask(), 0o755);
assert_eq!(x.name(), OsStr::new("foo.txt"));
}
_ => panic!("Unexpected request operation"),
}
}
}