use super::ll::{fuse_abi as abi, Errno, Response};
use log::{debug, error, warn};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::path::Path;
use super::channel::ChannelSender;
use super::ll::Request as _;
use super::reply::ReplyDirectoryPlus;
use super::reply::{Reply, ReplyDirectory, ReplySender};
use super::session::{Session, SessionACL};
use super::Filesystem;
use super::PollHandle;
use super::{ll, KernelConfig};
#[derive(Debug)]
pub struct Request<'a> {
ch: ChannelSender,
#[allow(unused)]
data: &'a [u8],
request: ll::AnyRequest<'a>,
}
impl<'a> Request<'a> {
pub(crate) fn new(ch: ChannelSender, data: &'a [u8]) -> Option<Request<'a>> {
let request = match ll::AnyRequest::try_from(data) {
Ok(request) => request,
Err(err) => {
error!("{err}");
return None;
}
};
Some(Self { ch, data, request })
}
pub(crate) fn dispatch<FS: Filesystem>(&self, se: &mut Session<FS>) {
debug!("{}", self.request);
let unique = self.request.unique();
let res = match self.dispatch_req(se) {
Ok(Some(resp)) => resp,
Ok(None) => return,
Err(errno) => self.request.reply_err(errno),
}
.with_iovec(unique, |iov| self.ch.send(iov));
if let Err(err) = res {
warn!("Request {unique:?}: Failed to send reply: {err}");
}
}
fn dispatch_req<FS: Filesystem>(
&self,
se: &mut Session<FS>,
) -> Result<Option<Response<'_>>, Errno> {
let op = self.request.operation().map_err(|_| Errno::ENOSYS)?;
if (se.allowed == SessionACL::RootAndOwner
&& self.request.uid() != se.session_owner
&& self.request.uid() != 0)
|| (se.allowed == SessionACL::Owner && self.request.uid() != se.session_owner)
{
#[cfg(feature = "abi-7-21")]
{
match op {
ll::Operation::Init(_)
| ll::Operation::Destroy(_)
| ll::Operation::Read(_)
| ll::Operation::ReadDir(_)
| ll::Operation::ReadDirPlus(_)
| ll::Operation::BatchForget(_)
| ll::Operation::Forget(_)
| ll::Operation::Write(_)
| ll::Operation::FSync(_)
| ll::Operation::FSyncDir(_)
| ll::Operation::Release(_)
| ll::Operation::ReleaseDir(_) => {}
_ => {
return Err(Errno::EACCES);
}
}
}
#[cfg(not(feature = "abi-7-21"))]
{
match op {
ll::Operation::Init(_)
| ll::Operation::Destroy(_)
| ll::Operation::Read(_)
| ll::Operation::ReadDir(_)
| ll::Operation::BatchForget(_)
| ll::Operation::Forget(_)
| ll::Operation::Write(_)
| ll::Operation::FSync(_)
| ll::Operation::FSyncDir(_)
| ll::Operation::Release(_)
| ll::Operation::ReleaseDir(_) => {}
_ => {
return Err(Errno::EACCES);
}
}
}
}
match op {
ll::Operation::Init(x) => {
let v = x.version();
if v < ll::Version(7, 6) {
error!("Unsupported FUSE ABI version {v}");
return Err(Errno::EPROTO);
}
se.proto_major = v.major();
se.proto_minor = v.minor();
let mut config = KernelConfig::new(x.capabilities(), x.max_readahead());
se.filesystem
.init(self, &mut config)
.map_err(Errno::from_i32)?;
debug!(
"INIT response: ABI {}.{}, flags {:#x}, max readahead {}, max write {}",
abi::FUSE_KERNEL_VERSION,
abi::FUSE_KERNEL_MINOR_VERSION,
x.capabilities() & config.requested,
config.max_readahead,
config.max_write
);
se.initialized = true;
return Ok(Some(x.reply(&config)));
}
_ if !se.initialized => {
warn!("Ignoring FUSE operation before init: {}", self.request);
return Err(Errno::EIO);
}
ll::Operation::Destroy(x) => {
se.filesystem.destroy();
se.destroyed = true;
return Ok(Some(x.reply()));
}
_ if se.destroyed => {
warn!("Ignoring FUSE operation after destroy: {}", self.request);
return Err(Errno::EIO);
}
ll::Operation::Interrupt(_) => {
return Err(Errno::ENOSYS);
}
ll::Operation::Lookup(x) => {
se.filesystem.lookup(
self,
self.request.nodeid().into(),
x.name().as_ref(),
self.reply(),
);
}
ll::Operation::Forget(x) => {
se.filesystem
.forget(self, self.request.nodeid().into(), x.nlookup());
}
ll::Operation::GetAttr(_attr) => {
se.filesystem.getattr(
self,
self.request.nodeid().into(),
_attr.file_handle().map(std::convert::Into::into),
self.reply(),
);
}
ll::Operation::SetAttr(x) => {
se.filesystem.setattr(
self,
self.request.nodeid().into(),
x.mode(),
x.uid(),
x.gid(),
x.size(),
x.atime(),
x.mtime(),
x.ctime(),
x.file_handle().map(std::convert::Into::into),
x.crtime(),
x.chgtime(),
x.bkuptime(),
x.flags(),
self.reply(),
);
}
ll::Operation::ReadLink(_) => {
se.filesystem
.readlink(self, self.request.nodeid().into(), self.reply());
}
ll::Operation::MkNod(x) => {
se.filesystem.mknod(
self,
self.request.nodeid().into(),
x.name().as_ref(),
x.mode(),
x.umask(),
x.rdev(),
self.reply(),
);
}
ll::Operation::MkDir(x) => {
se.filesystem.mkdir(
self,
self.request.nodeid().into(),
x.name().as_ref(),
x.mode(),
x.umask(),
self.reply(),
);
}
ll::Operation::Unlink(x) => {
se.filesystem.unlink(
self,
self.request.nodeid().into(),
x.name().as_ref(),
self.reply(),
);
}
ll::Operation::RmDir(x) => {
se.filesystem.rmdir(
self,
self.request.nodeid().into(),
x.name().as_ref(),
self.reply(),
);
}
ll::Operation::SymLink(x) => {
se.filesystem.symlink(
self,
self.request.nodeid().into(),
x.link_name().as_ref(),
Path::new(x.target()),
self.reply(),
);
}
ll::Operation::Rename(x) => {
se.filesystem.rename(
self,
self.request.nodeid().into(),
x.src().name.as_ref(),
x.dest().dir.into(),
x.dest().name.as_ref(),
0,
self.reply(),
);
}
ll::Operation::Link(x) => {
se.filesystem.link(
self,
x.inode_no().into(),
self.request.nodeid().into(),
x.dest().name.as_ref(),
self.reply(),
);
}
ll::Operation::Open(x) => {
se.filesystem
.open(self, self.request.nodeid().into(), x.flags(), self.reply());
}
ll::Operation::Read(x) => {
se.filesystem.read(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
x.size(),
x.flags(),
x.lock_owner().map(std::convert::Into::into),
self.reply(),
);
}
ll::Operation::Write(x) => {
se.filesystem.write(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
x.data(),
x.write_flags(),
x.flags(),
x.lock_owner().map(std::convert::Into::into),
self.reply(),
);
}
ll::Operation::Flush(x) => {
se.filesystem.flush(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.lock_owner().into(),
self.reply(),
);
}
ll::Operation::Release(x) => {
se.filesystem.release(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.flags(),
x.lock_owner().map(std::convert::Into::into),
x.flush(),
self.reply(),
);
}
ll::Operation::FSync(x) => {
se.filesystem.fsync(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.fdatasync(),
self.reply(),
);
}
ll::Operation::OpenDir(x) => {
se.filesystem
.opendir(self, self.request.nodeid().into(), x.flags(), self.reply());
}
ll::Operation::ReadDir(x) => {
se.filesystem.readdir(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
ReplyDirectory::new(
self.request.unique().into(),
self.ch.clone(),
x.size() as usize,
),
);
}
ll::Operation::ReleaseDir(x) => {
se.filesystem.releasedir(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.flags(),
self.reply(),
);
}
ll::Operation::FSyncDir(x) => {
se.filesystem.fsyncdir(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.fdatasync(),
self.reply(),
);
}
ll::Operation::StatFs(_) => {
se.filesystem
.statfs(self, self.request.nodeid().into(), self.reply());
}
ll::Operation::SetXAttr(x) => {
se.filesystem.setxattr(
self,
self.request.nodeid().into(),
x.name(),
x.value(),
x.flags(),
x.position(),
self.reply(),
);
}
ll::Operation::GetXAttr(x) => {
se.filesystem.getxattr(
self,
self.request.nodeid().into(),
x.name(),
x.size_u32(),
self.reply(),
);
}
ll::Operation::ListXAttr(x) => {
se.filesystem
.listxattr(self, self.request.nodeid().into(), x.size(), self.reply());
}
ll::Operation::RemoveXAttr(x) => {
se.filesystem.removexattr(
self,
self.request.nodeid().into(),
x.name(),
self.reply(),
);
}
ll::Operation::Access(x) => {
se.filesystem
.access(self, self.request.nodeid().into(), x.mask(), self.reply());
}
ll::Operation::Create(x) => {
se.filesystem.create(
self,
self.request.nodeid().into(),
x.name().as_ref(),
x.mode(),
x.umask(),
x.flags(),
self.reply(),
);
}
ll::Operation::GetLk(x) => {
se.filesystem.getlk(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.lock_owner().into(),
x.lock().range.0,
x.lock().range.1,
x.lock().typ,
x.lock().pid,
self.reply(),
);
}
ll::Operation::SetLk(x) => {
se.filesystem.setlk(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.lock_owner().into(),
x.lock().range.0,
x.lock().range.1,
x.lock().typ,
x.lock().pid,
false,
self.reply(),
);
}
ll::Operation::SetLkW(x) => {
se.filesystem.setlk(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.lock_owner().into(),
x.lock().range.0,
x.lock().range.1,
x.lock().typ,
x.lock().pid,
true,
self.reply(),
);
}
ll::Operation::BMap(x) => {
se.filesystem.bmap(
self,
self.request.nodeid().into(),
x.block_size(),
x.block(),
self.reply(),
);
}
ll::Operation::IoCtl(x) => {
if x.unrestricted() {
return Err(Errno::ENOSYS);
}
se.filesystem.ioctl(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.flags(),
x.command(),
x.in_data(),
x.out_size(),
self.reply(),
);
}
ll::Operation::Poll(x) => {
let ph = PollHandle::new(se.ch.sender(), x.kernel_handle());
se.filesystem.poll(
self,
self.request.nodeid().into(),
x.file_handle().into(),
ph,
x.events(),
x.flags(),
self.reply(),
);
}
ll::Operation::NotifyReply(_) => {
return Err(Errno::ENOSYS);
}
ll::Operation::BatchForget(x) => {
se.filesystem.batch_forget(self, x.nodes());
}
#[cfg(feature = "abi-7-19")]
ll::Operation::FAllocate(x) => {
se.filesystem.fallocate(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
x.len(),
x.mode(),
self.reply(),
);
}
#[cfg(feature = "abi-7-21")]
ll::Operation::ReadDirPlus(x) => {
se.filesystem.readdirplus(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
ReplyDirectoryPlus::new(
self.request.unique().into(),
self.ch.clone(),
x.size() as usize,
),
);
}
#[cfg(feature = "abi-7-23")]
ll::Operation::Rename2(x) => {
se.filesystem.rename(
self,
x.from().dir.into(),
x.from().name.as_ref(),
x.to().dir.into(),
x.to().name.as_ref(),
x.flags(),
self.reply(),
);
}
#[cfg(feature = "abi-7-24")]
ll::Operation::Lseek(x) => {
se.filesystem.lseek(
self,
self.request.nodeid().into(),
x.file_handle().into(),
x.offset(),
x.whence(),
self.reply(),
);
}
#[cfg(feature = "abi-7-28")]
ll::Operation::CopyFileRange(x) => {
let (i, o) = (x.src(), x.dest());
se.filesystem.copy_file_range(
self,
i.inode.into(),
i.file_handle.into(),
i.offset,
o.inode.into(),
o.file_handle.into(),
o.offset,
x.len(),
x.flags().try_into().unwrap(),
self.reply(),
);
}
#[cfg(target_os = "macos")]
ll::Operation::SetVolName(x) => {
se.filesystem.setvolname(self, x.name(), self.reply());
}
#[cfg(target_os = "macos")]
ll::Operation::GetXTimes(x) => {
se.filesystem
.getxtimes(self, x.nodeid().into(), self.reply());
}
#[cfg(target_os = "macos")]
ll::Operation::Exchange(x) => {
se.filesystem.exchange(
self,
x.from().dir.into(),
x.from().name.as_ref(),
x.to().dir.into(),
x.to().name.as_ref(),
x.options(),
self.reply(),
);
}
ll::Operation::CuseInit(_) => {
return Err(Errno::ENOSYS);
}
}
Ok(None)
}
fn reply<T: Reply>(&self) -> T {
Reply::new(self.request.unique().into(), self.ch.clone())
}
#[inline]
pub fn unique(&self) -> u64 {
self.request.unique().into()
}
#[inline]
pub fn uid(&self) -> u32 {
self.request.uid()
}
#[inline]
pub fn gid(&self) -> u32 {
self.request.gid()
}
#[inline]
pub fn pid(&self) -> u32 {
self.request.pid()
}
}