use IdGenerator;
use errors::KernelError;
use failure::Fallible;
use fuse;
use nix;
use nix::errno::Errno;
use nix::{sys, unistd};
use std::ffi::OsStr;
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::result::Result;
use std::sync::Arc;
mod caches;
pub use self::caches::{NoCache, PathCache};
pub mod conv;
mod dir;
pub use self::dir::Dir;
mod file;
pub use self::file::File;
mod symlink;
pub use self::symlink::Symlink;
pub trait Cache {
fn get_or_create(&self, _ids: &IdGenerator, _underlying_path: &Path, _attr: &fs::Metadata,
_writable: bool) -> ArcNode;
fn delete(&self, _path: &Path, _file_type: fuse::FileType);
fn rename(&self, _old_path: &Path, _new_path: PathBuf, _file_type: fuse::FileType);
}
pub type ArcCache = Arc<dyn Cache + Send + Sync>;
pub struct AttrDelta {
pub mode: Option<sys::stat::Mode>,
pub uid: Option<unistd::Uid>,
pub gid: Option<unistd::Gid>,
pub atime: Option<sys::time::TimeVal>,
pub mtime: Option<sys::time::TimeVal>,
pub size: Option<u64>,
}
pub type NodeResult<T> = Result<T, KernelError>;
fn try_path<O: Fn(&PathBuf) -> nix::Result<()>>(path: Option<&PathBuf>, op: O) -> nix::Result<()> {
match path {
Some(path) => op(path),
None => Ok(()),
}
}
fn setattr_mode(attr: &mut fuse::FileAttr, path: Option<&PathBuf>, mode: Option<sys::stat::Mode>)
-> Result<(), nix::Error> {
if mode.is_none() {
return Ok(())
}
let mode = mode.unwrap();
if mode.bits() > sys::stat::mode_t::from(std::u16::MAX) {
warn!("Got setattr with mode {:?} for {:?} (inode {}), which is too large; ignoring",
mode, path, attr.ino);
return Err(nix::Error::from_errno(Errno::EIO));
}
let perm = mode.bits() as u16;
if attr.kind == fuse::FileType::Symlink {
return Err(nix::Error::from_errno(Errno::EOPNOTSUPP));
}
let result = try_path(path, |p|
sys::stat::fchmodat(None, p, mode, sys::stat::FchmodatFlags::FollowSymlink));
if result.is_ok() {
attr.perm = perm;
}
result
}
fn setattr_owners(attr: &mut fuse::FileAttr, path: Option<&PathBuf>, uid: Option<unistd::Uid>,
gid: Option<unistd::Gid>) -> Result<(), nix::Error> {
if uid.is_none() && gid.is_none() {
return Ok(())
}
let result = try_path(path, |p|
unistd::fchownat(None, p, uid, gid, unistd::FchownatFlags::NoFollowSymlink));
if result.is_ok() {
attr.uid = uid.map_or(attr.uid, u32::from);
attr.gid = gid.map_or(attr.gid, u32::from);
}
result
}
fn setattr_times(attr: &mut fuse::FileAttr, path: Option<&PathBuf>,
atime: Option<sys::time::TimeVal>, mtime: Option<sys::time::TimeVal>)
-> Result<(), nix::Error> {
if atime.is_none() && mtime.is_none() {
return Ok(());
}
let atime = atime.unwrap_or_else(|| conv::timespec_to_timeval(attr.atime));
let mtime = mtime.unwrap_or_else(|| conv::timespec_to_timeval(attr.mtime));
#[allow(clippy::collapsible_if)]
let result = if cfg!(have_utimensat = "1") {
try_path(path, |p| sys::stat::utimensat(
None, p,
&conv::timeval_to_nix_timespec(atime), &conv::timeval_to_nix_timespec(mtime),
sys::stat::UtimensatFlags::NoFollowSymlink))
} else {
if attr.kind == fuse::FileType::Symlink {
eprintln!(
"utimensat not present; ignoring request to change symlink times for {:?}", path);
Err(nix::Error::from_errno(Errno::EOPNOTSUPP))
} else {
try_path(path, |p| sys::stat::utimes(p, &atime, &mtime))
}
};
if result.is_ok() {
attr.atime = conv::timeval_to_timespec(atime);
attr.mtime = conv::timeval_to_timespec(mtime);
if attr.mtime < attr.crtime {
attr.crtime = attr.mtime;
}
}
result
}
fn setattr_size(attr: &mut fuse::FileAttr, path: Option<&PathBuf>, size: Option<u64>)
-> Result<(), nix::Error> {
if size.is_none() {
return Ok(());
}
let size = size.unwrap();
let result = if size > ::std::i64::MAX as u64 {
warn!("truncate request got size {}, which is too large (exceeds i64's MAX)", size);
Err(nix::Error::invalid_argument())
} else {
try_path(path, |p| unistd::truncate(p, size as i64))
};
if result.is_ok() {
attr.size = size;
}
result
}
pub fn setattr(path: Option<&PathBuf>, attr: &fuse::FileAttr, delta: &AttrDelta)
-> Result<fuse::FileAttr, nix::Error> {
let updated_ctime = {
let mut now = time::now().to_timespec();
now.nsec = 0;
if attr.ctime > now {
attr.ctime
} else {
now
}
};
let mut new_attr = *attr;
let result = Ok(())
.and(setattr_mode(&mut new_attr, path, delta.mode))
.and(setattr_owners(&mut new_attr, path, delta.uid, delta.gid))
.and(setattr_times(&mut new_attr, path, delta.atime, delta.mtime))
.and(setattr_size(&mut new_attr, path, delta.size));
if !conv::fileattrs_eq(attr, &new_attr) {
new_attr.ctime = updated_ctime;
}
result.and(Ok(new_attr))
}
pub trait Handle {
fn read(&self, _offset: i64, _size: u32) -> NodeResult<Vec<u8>> {
panic!("Not implemented")
}
fn readdir(&self, _ids: &IdGenerator, _cache: &dyn Cache, _offset: i64,
_reply: &mut fuse::ReplyDirectory) -> NodeResult<()> {
panic!("Not implemented");
}
fn write(&self, _offset: i64, _data: &[u8]) -> NodeResult<u32> {
panic!("Not implemented");
}
}
pub type ArcHandle = Arc<dyn Handle + Send + Sync>;
pub trait Node {
fn inode(&self) -> u64;
fn writable(&self) -> bool;
fn file_type_cached(&self) -> fuse::FileType;
fn delete(&self, _cache: &dyn Cache);
fn set_underlying_path(&self, _path: &Path, _cache: &dyn Cache);
fn find_subdir(&self, _name: &OsStr, _ids: &IdGenerator) -> Fallible<ArcNode> {
panic!("Not implemented")
}
fn map(&self, _components: &[Component], _underlying_path: &Path, _writable: bool,
_ids: &IdGenerator, _cache: &dyn Cache) -> Fallible<ArcNode> {
panic!("Not implemented")
}
fn unmap(&self, _inodes: &mut Vec<u64>) -> Fallible<()> {
panic!("Not implemented")
}
fn unmap_subdir(&self, _name: &OsStr, __inodes: &mut Vec<u64>) -> Fallible<()> {
panic!("Not implemented")
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn create(&self, _name: &OsStr, _uid: unistd::Uid, _gid: unistd::Gid, _mode: u32, _flags: u32,
_ids: &IdGenerator, _cache: &dyn Cache)
-> NodeResult<(ArcNode, ArcHandle, fuse::FileAttr)> {
panic!("Not implemented")
}
fn getattr(&self) -> NodeResult<fuse::FileAttr>;
fn getxattr(&self, _name: &OsStr) -> NodeResult<Option<Vec<u8>>> {
panic!("Not implemented");
}
fn handle_from(&self, _file: fs::File) -> ArcHandle {
panic!("Not implemented");
}
fn listxattr(&self) -> NodeResult<Option<xattr::XAttrs>> {
panic!("Not implemented");
}
fn lookup(&self, _name: &OsStr, _ids: &IdGenerator, _cache: &dyn Cache)
-> NodeResult<(ArcNode, fuse::FileAttr)> {
panic!("Not implemented");
}
#[allow(clippy::too_many_arguments)]
fn mkdir(&self, _name: &OsStr, _uid: unistd::Uid, _gid: unistd::Gid, _mode: u32,
_ids: &IdGenerator, _cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
panic!("Not implemented")
}
#[allow(clippy::too_many_arguments)]
fn mknod(&self, _name: &OsStr, _uid: unistd::Uid, _gid: unistd::Gid, _mode: u32, _rdev: u32,
_ids: &IdGenerator, _cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
panic!("Not implemented")
}
fn open(&self, _flags: u32) -> NodeResult<ArcHandle> {
panic!("Not implemented");
}
fn readlink(&self) -> NodeResult<PathBuf> {
panic!("Not implemented");
}
fn removexattr(&self, _name: &OsStr) -> NodeResult<()> {
panic!("Not implemented");
}
fn rename(&self, _name: &OsStr, _new_name: &OsStr, _cache: &dyn Cache) -> NodeResult<()> {
panic!("Not implemented");
}
fn rename_and_move_source(&self, _old_name: &OsStr, _new_dir: ArcNode, _new_name: &OsStr,
_cache: &dyn Cache) -> NodeResult<()> {
panic!("Not implemented");
}
fn rename_and_move_target(&self, _dirent: &dir::Dirent, _old_path: &Path, _new_name: &OsStr,
_cache: &dyn Cache) -> NodeResult<()> {
Err(KernelError::from_errno(Errno::ENOTDIR))
}
fn rmdir(&self, _name: &OsStr, _cache: &dyn Cache) -> NodeResult<()> {
panic!("Not implemented");
}
fn setattr(&self, _delta: &AttrDelta) -> NodeResult<fuse::FileAttr>;
fn setxattr(&self, _name: &OsStr, _value: &[u8]) -> NodeResult<()> {
panic!("Not implemented");
}
fn symlink(&self, _name: &OsStr, _link: &Path, _uid: unistd::Uid, _gid: unistd::Gid,
_ids: &IdGenerator, _cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
panic!("Not implemented")
}
fn unlink(&self, _name: &OsStr, _cache: &dyn Cache) -> NodeResult<()> {
panic!("Not implemented");
}
}
pub type ArcNode = Arc<dyn Node + Send + Sync>;