use std::ffi::{OsStr, OsString};
use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use fuser::{
AccessFlags, Errno, FileHandle, FopenFlags, Generation, INodeNo, LockOwner, OpenFlags,
RenameFlags, TimeOrNow, WriteFlags,
};
use super::AsyncFilesystem;
use crate::FileType;
use crate::directory_cache::DirectoryCache;
use crate::inode_table::{InodeTable, InodeToPath};
use crate::types::*;
trait IntoRequestInfo {
fn info(&self) -> RequestInfo;
}
impl IntoRequestInfo for fuser::Request {
fn info(&self) -> RequestInfo {
RequestInfo {
unique: self.unique().0,
uid: self.uid(),
gid: self.gid(),
pid: self.pid(),
}
}
}
fn fuse_fileattr(attr: FileAttr, ino: INodeNo) -> fuser::FileAttr {
fuser::FileAttr {
ino,
size: attr.size,
blocks: attr.blocks,
atime: attr.atime,
mtime: attr.mtime,
ctime: attr.ctime,
crtime: attr.crtime,
kind: attr.kind,
perm: attr.perm,
nlink: attr.nlink,
uid: attr.uid,
gid: attr.gid,
rdev: attr.rdev,
blksize: attr.blksize,
flags: attr.flags,
}
}
trait TimeOrNowExt {
fn time(self) -> SystemTime;
}
impl TimeOrNowExt for TimeOrNow {
fn time(self) -> SystemTime {
match self {
TimeOrNow::SpecificTime(time) => time,
TimeOrNow::Now => SystemTime::now(),
}
}
}
#[derive(Debug)]
struct AsyncFuserNGInner<T> {
target: T,
table: InodeTable,
directory_cache: RwLock<DirectoryCache>,
}
impl<T> AsyncFuserNGInner<T> {
fn get_path(&self, ino: INodeNo) -> Option<EntryName> {
self.table.get_path(ino.0)
}
fn add_or_get_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
self.table.add_or_get_dir(parent.0, name)
}
fn add_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
self.table.add_or_get_leaf(parent.0, name)
}
fn create_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(bool, u64, u64)> {
self.table.create_or_get_leaf(parent.0, name)
}
fn lookup(&self, ino: u64) {
self.table.lookup(ino);
}
fn forget(&self, ino: INodeNo, count: u64) -> u64 {
self.table.forget(ino.0, count)
}
fn add_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
self.table.add_leaf(parent.0, name)
}
fn add_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
self.table.add_dir(parent.0, name)
}
fn inode_unlink(&self, parent: INodeNo, name: &OsStr) {
self.table.unlink(parent.0, name)
}
fn get_parent_inode(&self, ino: INodeNo) -> Option<u64> {
self.table.get_parent_inode(ino.0)
}
fn inode_rename(
&self,
oldparent: INodeNo,
oldname: &OsStr,
newparent: INodeNo,
newname: &OsStr,
) -> Option<()> {
self.table
.rename(oldparent.0, oldname, newparent.0, newname)
}
}
mod executor {
use std::future::Future;
pub(super) type Executor = tokio::runtime::Handle;
pub(super) fn spawn(executor: &Executor, future: impl Future<Output = ()> + Send + 'static) {
drop(executor.spawn(future));
}
}
#[derive(Debug)]
pub struct AsyncFuserNG<T> {
inner: Arc<AsyncFuserNGInner<T>>,
executor: executor::Executor,
}
impl<T: AsyncFilesystem + Sync + Send + 'static> AsyncFuserNG<T> {
pub fn new(target_fs: T, executor: tokio::runtime::Handle) -> Self {
Self {
inner: Arc::new(AsyncFuserNGInner {
target: target_fs,
table: InodeTable::new(),
directory_cache: RwLock::new(DirectoryCache::new()),
}),
executor,
}
}
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static) {
executor::spawn(&self.executor, future);
}
}
macro_rules! get_entry_name {
($inner:expr, $ino:expr, $reply:expr) => {
if let Some(path) = $inner.get_path($ino) {
path
} else {
$reply.error(Errno::EINVAL);
return;
}
};
}
macro_rules! resolve_from_parent {
($inner:expr, $ino:expr, $name:expr, $reply:expr) => {
if let Some(path) = $inner.table.resolve_from_parent($ino.0, $name.into()) {
path
} else {
$reply.error(Errno::EINVAL);
return;
}
};
}
macro_rules! get_resolved_path {
($inner:expr, $ino:expr, $reply:expr) => {{ get_entry_name!($inner, $ino, $reply).with($ino.0) }};
}
impl<T: AsyncFilesystem + Sync + Send + 'static> fuser::Filesystem for AsyncFuserNG<T> {
fn init(
&mut self,
req: &fuser::Request,
config: &mut fuser::KernelConfig,
) -> Result<(), std::io::Error> {
debug!("init");
self.inner.target.init(req.info(), config)
}
fn destroy(&mut self) {
debug!("destroy");
self.inner.target.destroy();
}
fn lookup(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
reply: fuser::ReplyEntry,
) {
let inner = Arc::clone(&self.inner);
let path = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let req = req.info();
debug!("lookup: {:?}", path);
self.spawn(async move {
match inner
.target
.getattr(req, EntryRef::Lookup(path), None)
.await
{
Ok((ttl, attr)) => {
let value = if attr.kind == FileType::Directory {
inner.add_or_get_dir(parent, &name)
} else {
inner.add_or_get_leaf(parent, &name)
};
if let Some((ino, generation)) = value {
inner.lookup(ino);
reply.entry(
&ttl,
&fuse_fileattr(attr, INodeNo(ino)),
Generation(generation),
);
} else {
reply.error(Errno::EINVAL);
}
}
Err(error) => reply.error(error.into()),
}
});
}
fn forget(&self, _req: &fuser::Request, ino: INodeNo, nlookup: u64) {
let lookups = self.inner.forget(ino, nlookup);
let path = self.inner.get_path(ino).unwrap_or_else(|| {
EntryName::new(
Arc::new(PathBuf::from(OsStr::new(""))).into(),
OsString::from("[unknown]").into(),
)
});
debug!(
"forget: inode {} ({:?}) now at {} lookups",
ino, path, lookups
);
}
fn getattr(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: Option<FileHandle>,
reply: fuser::ReplyAttr,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("getattr: {:?}", path);
self.spawn(async move {
match inner
.target
.getattr(req, EntryRef::Resolved(path), fh.map(|fh| fh.0))
.await
{
Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
Err(error) => reply.error(error.into()),
}
});
}
#[allow(clippy::too_many_arguments)]
fn setattr(
&self,
req: &fuser::Request,
ino: INodeNo,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<TimeOrNow>,
mtime: Option<TimeOrNow>,
_ctime: Option<SystemTime>,
fh: Option<FileHandle>,
crtime: Option<SystemTime>,
chgtime: Option<SystemTime>,
bkuptime: Option<SystemTime>,
flags: Option<fuser::BsdFileFlags>,
reply: fuser::ReplyAttr,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
let fh = fh.map(|fh| fh.0);
debug!("setattr: {:?}", path);
self.spawn(async move {
if let Some(mode) = mode
&& let Err(error) = inner.target.chmod(req, path.clone(), fh, mode).await
{
reply.error(error.into());
return;
}
if (uid.is_some() || gid.is_some())
&& let Err(error) = inner.target.chown(req, path.clone(), fh, uid, gid).await
{
reply.error(error.into());
return;
}
if let Some(size) = size
&& let Err(error) = inner.target.truncate(req, path.clone(), fh, size).await
{
reply.error(error.into());
return;
}
if atime.is_some() || mtime.is_some() {
let atime = atime.map(TimeOrNowExt::time);
let mtime = mtime.map(TimeOrNowExt::time);
if let Err(error) = inner
.target
.utimens(req, path.clone(), fh, atime, mtime)
.await
{
reply.error(error.into());
return;
}
}
if (crtime.is_some() || chgtime.is_some() || bkuptime.is_some() || flags.is_some())
&& let Err(error) = inner
.target
.utimens_macos(
req,
path.clone(),
fh,
crtime,
chgtime,
bkuptime,
flags.map(|flags| flags.bits()),
)
.await
{
reply.error(error.into());
return;
}
match inner
.target
.getattr(req, EntryRef::Resolved(path), fh)
.await
{
Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
Err(error) => reply.error(error.into()),
}
});
}
fn readlink(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyData) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("readlink: {:?}", path);
self.spawn(async move {
match inner.target.readlink(req, path).await {
Ok(data) => reply.data(&data),
Err(error) => reply.error(error.into()),
}
});
}
fn mknod(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
mode: u32,
_umask: u32,
rdev: u32,
reply: fuser::ReplyEntry,
) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let req = req.info();
debug!("mknod: {:?}", entry);
self.spawn(async move {
match inner.target.mknod(req, entry, mode, rdev).await {
Ok((ttl, attr)) => {
if let Some((ino, generation)) = inner.add_leaf(parent, &name) {
reply.entry(
&ttl,
&fuse_fileattr(attr, INodeNo(ino)),
Generation(generation),
);
} else {
reply.error(Errno::EINVAL);
}
}
Err(error) => reply.error(error.into()),
}
});
}
fn mkdir(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
mode: u32,
_umask: u32,
reply: fuser::ReplyEntry,
) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let req = req.info();
debug!("mkdir: {:?} (mode={:#o})", entry, mode);
self.spawn(async move {
match inner.target.mkdir(req, entry, mode).await {
Ok((ttl, attr)) => {
if let Some((ino, generation)) = inner.add_dir(parent, &name) {
reply.entry(
&ttl,
&fuse_fileattr(attr, INodeNo(ino)),
Generation(generation),
);
} else {
reply.error(Errno::EINVAL);
}
}
Err(error) => reply.error(error.into()),
}
});
}
fn unlink(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let req = req.info();
debug!("unlink: {:?}", entry);
self.spawn(async move {
match inner.target.unlink(req, entry).await {
Ok(()) => {
inner.inode_unlink(parent, &name);
reply.ok();
}
Err(error) => reply.error(error.into()),
}
});
}
fn rmdir(&self, req: &fuser::Request, parent: INodeNo, name: &OsStr, reply: fuser::ReplyEmpty) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let req = req.info();
debug!("rmdir: {:?}", entry);
self.spawn(async move {
match inner.target.rmdir(req, entry).await {
Ok(()) => {
inner.inode_unlink(parent, &name);
reply.ok();
}
Err(error) => reply.error(error.into()),
}
});
}
fn symlink(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
link: &Path,
reply: fuser::ReplyEntry,
) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let name = name.to_os_string();
let link = link.to_path_buf();
let req = req.info();
debug!("symlink: {:?} -> {:?}", entry, link);
self.spawn(async move {
match inner.target.symlink(req, entry, link).await {
Ok((ttl, attr)) => {
if let Some((ino, generation)) = inner.add_leaf(parent, &name) {
reply.entry(
&ttl,
&fuse_fileattr(attr, INodeNo(ino)),
Generation(generation),
);
} else {
reply.error(Errno::EINVAL);
}
}
Err(error) => reply.error(error.into()),
}
});
}
fn rename(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
newparent: INodeNo,
newname: &OsStr,
_flags: RenameFlags,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let entry = resolve_from_parent!(inner, parent, name, reply);
let new_entry = resolve_from_parent!(inner, newparent, newname, reply);
let name = name.to_os_string();
let newname = newname.to_os_string();
let req = req.info();
debug!("rename: {:?} -> {:?}", entry, new_entry);
self.spawn(async move {
match inner.target.rename(req, entry, new_entry).await {
Ok(()) => {
inner.inode_rename(parent, &name, newparent, &newname);
reply.ok();
}
Err(error) => reply.error(error.into()),
}
});
}
fn link(
&self,
req: &fuser::Request,
ino: INodeNo,
newparent: INodeNo,
newname: &OsStr,
reply: fuser::ReplyEntry,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let new_entry = resolve_from_parent!(inner, newparent, newname, reply);
let newname = newname.to_os_string();
let req = req.info();
debug!("link: {:?} -> {:?}", path, new_entry);
self.spawn(async move {
match inner.target.link(req, path, new_entry).await {
Ok((ttl, attr)) => {
if let Some((new_ino, generation)) = inner.add_leaf(newparent, &newname) {
reply.entry(
&ttl,
&fuse_fileattr(attr, INodeNo(new_ino)),
Generation(generation),
);
} else {
reply.error(Errno::EINVAL);
}
}
Err(error) => reply.error(error.into()),
}
});
}
fn open(&self, req: &fuser::Request, ino: INodeNo, flags: OpenFlags, reply: fuser::ReplyOpen) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("open: {:?}", path);
self.spawn(async move {
match inner.target.open(req, path, flags.0 as u32).await {
Ok((fh, flags)) => {
reply.opened(FileHandle(fh), FopenFlags::from_bits_retain(flags));
}
Err(error) => reply.error(error.into()),
}
});
}
fn read(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
offset: u64,
size: u32,
_flags: OpenFlags,
_lock_owner: Option<LockOwner>,
reply: fuser::ReplyData,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("read: {:?} {:#x} @ {:#x}", path, size, offset);
self.spawn(async move {
match inner.target.read(req, path, fh.0, offset, size).await {
Ok(data) => reply.data(&data),
Err(error) => reply.error(error.into()),
}
});
}
#[allow(clippy::too_many_arguments)]
fn write(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
offset: u64,
data: &[u8],
_write_flags: WriteFlags,
flags: OpenFlags,
_lock_owner: Option<LockOwner>,
reply: fuser::ReplyWrite,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let data = data.to_vec();
let req = req.info();
debug!("write: {:?} {:#x} @ {:#x}", path, data.len(), offset);
self.spawn(async move {
match inner
.target
.write(req, path, fh.0, offset, data, flags.0 as u32)
.await
{
Ok(written) => reply.written(written),
Err(error) => reply.error(error.into()),
}
});
}
fn flush(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
lock_owner: LockOwner,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("flush: {:?}", path);
self.spawn(async move {
match inner.target.flush(req, path, fh.0, lock_owner.0).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
#[allow(clippy::too_many_arguments)]
fn release(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
flags: OpenFlags,
lock_owner: Option<LockOwner>,
flush: bool,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("release: {:?}", path);
self.spawn(async move {
match inner
.target
.release(
req,
path,
fh.0,
flags.0 as u32,
lock_owner.map(|owner| owner.0).unwrap_or(0),
flush,
)
.await
{
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn fsync(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
datasync: bool,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("fsync: {:?}", path);
self.spawn(async move {
match inner.target.fsync(req, path, fh.0, datasync).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn opendir(
&self,
req: &fuser::Request,
ino: INodeNo,
flags: OpenFlags,
reply: fuser::ReplyOpen,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("opendir: {:?}", path);
self.spawn(async move {
match inner.target.opendir(req, path, flags.0 as u32).await {
Ok((fh, flags)) => {
let cache_key = inner.directory_cache.write().unwrap().new_entry(fh);
reply.opened(FileHandle(cache_key), FopenFlags::from_bits_retain(flags));
}
Err(error) => reply.error(error.into()),
}
});
}
fn readdir(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
offset: u64,
mut reply: fuser::ReplyDirectory,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("readdir: {:?} @ {}", path, offset);
let parent_inode = if ino == INodeNo::ROOT {
ino
} else {
match inner.get_parent_inode(ino) {
Some(inode) => INodeNo(inode),
None => {
error!("readdir: unable to get parent inode for {:?}", path);
reply.error(Errno::EIO);
return;
}
}
};
self.spawn(async move {
let cached_entries = {
inner
.directory_cache
.write()
.unwrap()
.get_mut(fh.0)
.entries
.clone()
};
let entries = match cached_entries {
Some(entries) => entries,
None => {
let real_fh = inner.directory_cache.read().unwrap().real_fh(fh.0);
match inner.target.readdir(req, path, real_fh).await {
Ok(entries) => {
inner.directory_cache.write().unwrap().get_mut(fh.0).entries =
Some(entries.clone());
entries
}
Err(error) => {
reply.error(error.into());
return;
}
}
}
};
for (index, entry) in entries.iter().skip(offset as usize).enumerate() {
let entry_inode = if entry.name == Path::new(".") {
ino
} else if entry.name == Path::new("..") {
parent_inode
} else {
INodeNo(!1u64)
};
let buffer_full = reply.add(
entry_inode,
offset + index as u64 + 1,
entry.kind,
entry.name.as_os_str(),
);
if buffer_full {
break;
}
}
reply.ok();
});
}
fn releasedir(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
flags: OpenFlags,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let real_fh = inner.directory_cache.read().unwrap().real_fh(fh.0);
let req = req.info();
debug!("releasedir: {:?}", path);
self.spawn(async move {
match inner
.target
.releasedir(req, path, real_fh, flags.0 as u32)
.await
{
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
inner.directory_cache.write().unwrap().delete(fh.0);
});
}
fn fsyncdir(
&self,
req: &fuser::Request,
ino: INodeNo,
fh: FileHandle,
datasync: bool,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let real_fh = inner.directory_cache.read().unwrap().real_fh(fh.0);
let req = req.info();
debug!("fsyncdir: {:?} (datasync: {:?})", path, datasync);
self.spawn(async move {
match inner.target.fsyncdir(req, path, real_fh, datasync).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn statfs(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyStatfs) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("statfs: {:?}", path);
self.spawn(async move {
match inner.target.statfs(req, path).await {
Ok(statfs) => reply.statfs(
statfs.blocks,
statfs.bfree,
statfs.bavail,
statfs.files,
statfs.ffree,
statfs.bsize,
statfs.namelen,
statfs.frsize,
),
Err(error) => reply.error(error.into()),
}
});
}
#[allow(clippy::too_many_arguments)]
fn setxattr(
&self,
req: &fuser::Request,
ino: INodeNo,
name: &OsStr,
value: &[u8],
flags: i32,
position: u32,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let name = name.to_os_string();
let value = value.to_vec();
let req = req.info();
debug!("setxattr: {:?} {:?}", path, name);
self.spawn(async move {
match inner
.target
.setxattr(req, path, name, value, flags as u32, position)
.await
{
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn getxattr(
&self,
req: &fuser::Request,
ino: INodeNo,
name: &OsStr,
size: u32,
reply: fuser::ReplyXattr,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let name = name.to_os_string();
let req = req.info();
debug!("getxattr: {:?} {:?}", path, name);
self.spawn(async move {
match inner.target.getxattr(req, path, name, size).await {
Ok(Xattr::Size(size)) => reply.size(size),
Ok(Xattr::Data(data)) => reply.data(&data),
Err(error) => reply.error(error.into()),
}
});
}
fn listxattr(&self, req: &fuser::Request, ino: INodeNo, size: u32, reply: fuser::ReplyXattr) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("listxattr: {:?}", path);
self.spawn(async move {
match inner.target.listxattr(req, path, size).await {
Ok(Xattr::Size(size)) => reply.size(size),
Ok(Xattr::Data(data)) => reply.data(&data),
Err(error) => reply.error(error.into()),
}
});
}
fn removexattr(
&self,
req: &fuser::Request,
ino: INodeNo,
name: &OsStr,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let name = name.to_os_string();
let req = req.info();
debug!("removexattr: {:?}, {:?}", path, name);
self.spawn(async move {
match inner.target.removexattr(req, path, name).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn access(
&self,
req: &fuser::Request,
ino: INodeNo,
mask: AccessFlags,
reply: fuser::ReplyEmpty,
) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("access: {:?}, mask={:#o}", path, mask.bits());
self.spawn(async move {
match inner.target.access(req, path, mask.bits() as u32).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
fn create(
&self,
req: &fuser::Request,
parent: INodeNo,
name: &OsStr,
mode: u32,
_umask: u32,
flags: i32,
reply: fuser::ReplyCreate,
) {
let inner = Arc::clone(&self.inner);
let (created, path, generation) = match inner.create_or_get_leaf(parent, name) {
Some((created, ino, generation)) => (
created,
get_resolved_path!(inner, INodeNo(ino), reply),
generation,
),
None => {
reply.error(Errno::EINVAL);
return;
}
};
let name = name.to_os_string();
let req = req.info();
debug!("create: {:?} (mode={:#o}, flags={:#x})", path, mode, flags);
self.spawn(async move {
match inner
.target
.create(req, path.clone(), mode, flags as u32)
.await
{
Ok(create) => {
if !created {
inner.lookup(path.ino());
}
let attr = fuse_fileattr(create.attr, INodeNo(path.ino()));
reply.created(
&create.ttl,
&attr,
Generation(generation),
FileHandle(create.fh),
FopenFlags::from_bits_retain(create.flags),
);
}
Err(error) => {
if created {
inner.inode_unlink(parent, &name);
inner.forget(INodeNo(path.ino()), 1);
}
reply.error(error.into());
}
}
});
}
#[cfg(target_os = "macos")]
fn setvolname(&self, req: &fuser::Request, name: &OsStr, reply: fuser::ReplyEmpty) {
let inner = Arc::clone(&self.inner);
let name = name.to_os_string();
let req = req.info();
debug!("setvolname: {:?}", name);
self.spawn(async move {
match inner.target.setvolname(req, name).await {
Ok(()) => reply.ok(),
Err(error) => reply.error(error.into()),
}
});
}
#[cfg(target_os = "macos")]
fn getxtimes(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyXTimes) {
let inner = Arc::clone(&self.inner);
let path = get_resolved_path!(inner, ino, reply);
let req = req.info();
debug!("getxtimes: {:?}", path);
self.spawn(async move {
match inner.target.getxtimes(req, path).await {
Ok(xtimes) => reply.xtimes(xtimes.bkuptime, xtimes.crtime),
Err(error) => reply.error(error.into()),
}
});
}
}