use std::convert::TryInto;
use std::io;
use std::time::Duration;
use crate::abi::fuse_abi as fuse;
use crate::file_traits::FileReadWriteVolatile;
pub use fuse::FsOptions;
pub use fuse::OpenOptions;
pub use fuse::SetattrValid;
pub use fuse::ROOT_ID;
use crate::abi::fuse_abi::{ino64_t, stat64};
#[cfg(feature = "async-io")]
mod async_io;
#[cfg(feature = "async-io")]
pub use async_io::{AsyncFileSystem, AsyncZeroCopyReader, AsyncZeroCopyWriter};
mod sync_io;
pub use sync_io::FileSystem;
#[derive(Copy, Clone)]
pub struct Entry {
pub inode: u64,
pub generation: u64,
pub attr: stat64,
pub attr_flags: u32,
pub attr_timeout: Duration,
pub entry_timeout: Duration,
}
impl From<Entry> for fuse::EntryOut {
fn from(entry: Entry) -> fuse::EntryOut {
fuse::EntryOut {
nodeid: entry.inode,
generation: entry.generation,
entry_valid: entry.entry_timeout.as_secs(),
attr_valid: entry.attr_timeout.as_secs(),
entry_valid_nsec: entry.entry_timeout.subsec_nanos(),
attr_valid_nsec: entry.attr_timeout.subsec_nanos(),
attr: fuse::Attr::with_flags(entry.attr, entry.attr_flags),
}
}
}
impl Default for Entry {
fn default() -> Self {
Entry {
inode: 0,
generation: 0,
attr: unsafe { std::mem::zeroed() },
attr_flags: 0,
attr_timeout: Duration::default(),
entry_timeout: Duration::default(),
}
}
}
#[derive(Copy, Clone)]
pub struct DirEntry<'a> {
pub ino: ino64_t,
pub offset: u64,
pub type_: u32,
pub name: &'a [u8],
}
#[derive(Copy, Clone)]
pub struct FileLock {
pub start: u64,
pub end: u64,
pub lock_type: u32,
pub pid: u32,
}
impl From<fuse::FileLock> for FileLock {
fn from(l: fuse::FileLock) -> FileLock {
FileLock {
start: l.start,
end: l.end,
lock_type: l.type_,
pid: l.pid,
}
}
}
impl From<FileLock> for fuse::FileLock {
fn from(l: FileLock) -> fuse::FileLock {
fuse::FileLock {
start: l.start,
end: l.end,
type_: l.lock_type,
pid: l.pid,
}
}
}
#[derive(Default, Clone)]
pub struct IoctlData<'a> {
pub result: i32,
pub data: Option<&'a [u8]>,
}
pub enum GetxattrReply {
Value(Vec<u8>),
Count(u32),
}
pub enum ListxattrReply {
Names(Vec<u8>),
Count(u32),
}
pub trait ZeroCopyReader: io::Read {
fn read_to(
&mut self,
f: &mut dyn FileReadWriteVolatile,
count: usize,
off: u64,
) -> io::Result<usize>;
fn read_exact_to(
&mut self,
f: &mut dyn FileReadWriteVolatile,
mut count: usize,
mut off: u64,
) -> io::Result<()> {
let c = count
.try_into()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
if off.checked_add(c).is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"`off` + `count` must be less than u64::MAX",
));
}
while count > 0 {
match self.read_to(f, count, off) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to fill whole buffer",
))
}
Ok(n) => {
count -= n;
off += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
fn copy_to_end(
&mut self,
f: &mut dyn FileReadWriteVolatile,
mut off: u64,
) -> io::Result<usize> {
let mut out = 0;
loop {
match self.read_to(f, ::std::usize::MAX, off) {
Ok(0) => return Ok(out),
Ok(n) => {
off = off.saturating_add(n as u64);
out += n;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
}
}
pub trait ZeroCopyWriter: io::Write {
fn write_from(
&mut self,
f: &mut dyn FileReadWriteVolatile,
count: usize,
off: u64,
) -> io::Result<usize>;
fn write_all_from(
&mut self,
f: &mut dyn FileReadWriteVolatile,
mut count: usize,
mut off: u64,
) -> io::Result<()> {
let c = count
.try_into()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
if off.checked_add(c).is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"`off` + `count` must be less than u64::MAX",
));
}
while count > 0 {
match self.write_from(f, count, off) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to write whole buffer",
))
}
Ok(n) => {
count -= n;
off += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
fn copy_to_end(
&mut self,
f: &mut dyn FileReadWriteVolatile,
mut off: u64,
) -> io::Result<usize> {
let mut out = 0;
loop {
match self.write_from(f, ::std::usize::MAX, off) {
Ok(0) => return Ok(out),
Ok(n) => {
off = off.saturating_add(n as u64);
out += n;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
}
}
#[derive(Default, Clone, Copy, Debug)]
pub struct Context {
pub uid: libc::uid_t,
pub gid: libc::gid_t,
pub pid: libc::pid_t,
}
impl Context {
pub fn new() -> Self {
Self::default()
}
}
impl From<&fuse::InHeader> for Context {
fn from(source: &fuse::InHeader) -> Self {
Context {
uid: source.uid,
gid: source.gid,
pid: source.pid as i32,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::fuse_abi::Attr;
#[test]
fn test_from_fuse_header() {
let fuse_header = &fuse::InHeader {
len: 16,
opcode: 0,
unique: 1,
nodeid: 2,
uid: 3,
gid: 4,
pid: 5,
padding: 0,
};
let header: Context = fuse_header.into();
assert_eq!(header.uid, 3);
assert_eq!(header.gid, 4);
assert_eq!(header.pid, 5);
}
#[test]
fn test_into_fuse_entry() {
let attr = Attr {
..Default::default()
};
let entry = Entry {
inode: 1,
generation: 2,
attr: attr.into(),
..Default::default()
};
let fuse_entry: fuse::EntryOut = entry.into();
assert_eq!(fuse_entry.nodeid, 1);
assert_eq!(fuse_entry.generation, 2);
}
}