use super::util::{einval, erofs};
use super::PassthroughFs;
use crate::filesystem::{
Context, Entry, Extensions, FileSystem, FsOptions, GetxattrReply, ListxattrReply, OpenOptions,
SerializableFileSystem, SetattrValid, SetxattrFlags, ZeroCopyReader, ZeroCopyWriter,
};
use crate::fuse;
use std::convert::TryInto;
use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
pub struct PassthroughFsRo(PassthroughFs);
impl PassthroughFsRo {
pub fn new(cfg: super::Config) -> io::Result<Self> {
let inner = PassthroughFs::new(cfg)?;
Ok(PassthroughFsRo(inner))
}
fn rofs_open<R, F: FnOnce(u32) -> io::Result<R>>(flags: u32, open_fn: F) -> io::Result<R> {
let cflags: libc::c_int = flags
.try_into()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
if cflags & libc::O_PATH != 0 {
return open_fn(flags);
}
if cflags & libc::O_ACCMODE != libc::O_RDONLY {
return Err(erofs());
}
if cflags & libc::O_EXCL == libc::O_EXCL {
return Err(erofs());
}
if cflags & libc::O_TMPFILE == libc::O_TMPFILE {
return Err(einval());
}
if cflags & libc::O_TRUNC == libc::O_TRUNC {
return Err(einval());
}
if cflags & libc::O_CREAT == 0 {
open_fn(flags)
} else {
open_fn(flags & !(libc::O_CREAT as u32)).map_err(|err| {
if err.kind() == io::ErrorKind::NotFound {
erofs()
} else {
err
}
})
}
}
}
macro_rules! ops_allow {
{
$(
fn $name:ident$(<$($gen_name:ident: $gen_trait:path),*>)?(
&self
$(, $($par_name:ident: $par_type:ty),*)?
$(,)?
)$( -> $ret:ty)?;
)*
} => {
$(
fn $name$(<$($gen_name: $gen_trait),*>)?(
&self
$(, $($par_name: $par_type),*)?
)$( -> $ret)? {
self.0.$name($($($par_name),*)?)
}
)*
}
}
macro_rules! ops_forbid {
{
$(
fn $name:ident$(<$($gen_name:ident: $gen_trait:path),*>)?(
&self
$(, $($par_name:ident: $par_type:ty),*)?
$(,)?
) -> io::Result<$ret_ok:ty>;
)*
} => {
$(
fn $name$(<$($gen_name: $gen_trait),*>)?(
&self
$(, $($par_name: $par_type),*)?
) -> io::Result<$ret_ok> {
Err(erofs())
}
)*
}
}
impl FileSystem for PassthroughFsRo {
type Inode = <PassthroughFs as FileSystem>::Inode;
type Handle = <PassthroughFs as FileSystem>::Handle;
type DirIter = <PassthroughFs as FileSystem>::DirIter;
ops_allow! {
fn init(&self, capable: FsOptions) -> io::Result<FsOptions>;
fn destroy(&self);
fn lookup(&self, ctx: Context, parent: Self::Inode, name: &CStr) -> io::Result<Entry>;
fn forget(&self, ctx: Context, inode: Self::Inode, count: u64);
fn batch_forget(&self, ctx: Context, requests: Vec<(Self::Inode, u64)>);
fn getattr(&self,
ctx: Context,
inode: Self::Inode,
handle: Option<Self::Handle>,
) -> io::Result<(fuse::Attr, Duration)>;
fn readlink(&self, ctx: Context, inode: Self::Inode) -> io::Result<Vec<u8>>;
fn read<W: ZeroCopyWriter>(
&self,
ctx: Context,
inode: Self::Inode,
handle: Self::Handle,
w: W,
size: u32,
offset: u64,
lock_owner: Option<u64>,
flags: u32,
) -> io::Result<usize>;
fn flush(
&self,
ctx: Context,
inode: Self::Inode,
handle: Self::Handle,
lock_owner: u64,
) -> io::Result<()>;
fn fsync(
&self,
ctx: Context,
inode: Self::Inode,
datasync: bool,
handle: Self::Handle,
) -> io::Result<()>;
fn release(
&self,
ctx: Context,
inode: Self::Inode,
flags: u32,
handle: Self::Handle,
flush: bool,
flock_release: bool,
lock_owner: Option<u64>,
) -> io::Result<()>;
fn statfs(&self, ctx: Context, inode: Self::Inode) -> io::Result<libc::statvfs64>;
fn getxattr(
&self,
ctx: Context,
inode: Self::Inode,
name: &CStr,
size: u32,
) -> io::Result<GetxattrReply>;
fn listxattr(
&self,
ctx: Context,
inode: Self::Inode,
size: u32,
) -> io::Result<ListxattrReply>;
fn readdir(
&self,
ctx: Context,
inode: Self::Inode,
handle: Self::Handle,
size: u32,
offset: u64,
) -> io::Result<Self::DirIter>;
fn fsyncdir(
&self,
ctx: Context,
inode: Self::Inode,
datasync: bool,
handle: Self::Handle,
) -> io::Result<()>;
fn releasedir(
&self,
ctx: Context,
inode: Self::Inode,
flags: u32,
handle: Self::Handle,
) -> io::Result<()>;
fn lseek(
&self,
ctx: Context,
inode: Self::Inode,
handle: Self::Handle,
offset: u64,
whence: u32,
) -> io::Result<u64>;
fn syncfs(&self, ctx: Context, inode: Self::Inode) -> io::Result<()>;
}
ops_forbid! {
fn setattr(
&self,
_ctx: Context,
_inode: Self::Inode,
_attr: fuse::SetattrIn,
_handle: Option<Self::Handle>,
_valid: SetattrValid,
) -> io::Result<(fuse::Attr, Duration)>;
fn symlink(
&self,
_ctx: Context,
_linkname: &CStr,
_parent: Self::Inode,
_name: &CStr,
_extensions: Extensions,
) -> io::Result<Entry>;
fn mknod(
&self,
_ctx: Context,
_parent: Self::Inode,
_name: &CStr,
_mode: u32,
_rdev: u32,
_umask: u32,
_extensions: Extensions,
) -> io::Result<Entry>;
fn mkdir(
&self,
_ctx: Context,
_parent: Self::Inode,
_name: &CStr,
_mode: u32,
_umask: u32,
_extensions: Extensions,
) -> io::Result<Entry>;
fn unlink(&self, _ctx: Context, _parent: Self::Inode, _name: &CStr) -> io::Result<()>;
fn rmdir(&self, _ctx: Context, _parent: Self::Inode, _name: &CStr) -> io::Result<()>;
fn rename(
&self,
_ctx: Context,
_olddir: Self::Inode,
_oldname: &CStr,
_newdir: Self::Inode,
_newname: &CStr,
_flags: u32,
) -> io::Result<()>;
fn link(
&self,
_ctx: Context,
_inode: Self::Inode,
_newparent: Self::Inode,
_newname: &CStr,
) -> io::Result<Entry>;
fn write<R: ZeroCopyReader>(
&self,
_ctx: Context,
_inode: Self::Inode,
_handle: Self::Handle,
_r: R,
_size: u32,
_offset: u64,
_lock_owner: Option<u64>,
_delayed_write: bool,
_kill_priv: bool,
_flags: u32,
) -> io::Result<usize>;
fn fallocate(
&self,
_ctx: Context,
_inode: Self::Inode,
_handle: Self::Handle,
_mode: u32,
_offset: u64,
_length: u64,
) -> io::Result<()>;
fn setxattr(
&self,
_ctx: Context,
_inode: Self::Inode,
_name: &CStr,
_value: &[u8],
_flags: u32,
_extra_flags: SetxattrFlags,
) -> io::Result<()>;
fn removexattr(&self, _ctx: Context, _inode: Self::Inode, _name: &CStr) -> io::Result<()>;
fn copyfilerange(
&self,
_ctx: Context,
_inode_in: Self::Inode,
_handle_in: Self::Handle,
_offset_in: u64,
_inode_out: Self::Inode,
_handle_out: Self::Handle,
_offset_out: u64,
_len: u64,
_flags: u64,
) -> io::Result<usize>;
}
fn open(
&self,
ctx: Context,
inode: Self::Inode,
kill_priv: bool,
flags: u32,
) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
Self::rofs_open(flags, |flags| self.0.open(ctx, inode, kill_priv, flags))
}
fn create(
&self,
ctx: Context,
parent: Self::Inode,
name: &CStr,
_mode: u32,
kill_priv: bool,
flags: u32,
_umask: u32,
_extensions: Extensions,
) -> io::Result<(Entry, Option<Self::Handle>, OpenOptions)> {
let entry = self.lookup(ctx, parent, name).map_err(|err| {
if err.kind() == io::ErrorKind::NotFound {
erofs()
} else {
err
}
})?;
let (handle, opts) = self.open(ctx, entry.inode, kill_priv, flags)?;
Ok((entry, handle, opts))
}
fn opendir(
&self,
ctx: Context,
inode: Self::Inode,
flags: u32,
) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
Self::rofs_open(flags, |flags| self.0.opendir(ctx, inode, flags))
}
fn access(&self, ctx: Context, inode: Self::Inode, mask: u32) -> io::Result<()> {
if mask & libc::W_OK as u32 != 0 {
Err(erofs())
} else {
self.0.access(ctx, inode, mask)
}
}
}
impl SerializableFileSystem for PassthroughFsRo {
ops_allow! {
fn prepare_serialization(&self, cancel: Arc<AtomicBool>);
fn serialize(&self, state_pipe: File) -> io::Result<()>;
fn deserialize_and_apply(&self, state_pipe: File) -> io::Result<()>;
}
}