use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use log::{error, info};
#[cfg(feature = "libfuse")]
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};
use std::thread::{self, JoinHandle};
use std::{fmt, ptr};
use crate::channel::{self, Channel};
use crate::request::Request;
use crate::Filesystem;
#[cfg(not(feature = "libfuse"))]
use crate::MountOption;
pub const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024;
const BUFFER_SIZE: usize = MAX_WRITE_SIZE + 4096;
#[derive(Debug)]
pub struct Session<FS: Filesystem> {
pub filesystem: FS,
ch: Channel,
pub proto_major: u32,
pub proto_minor: u32,
pub initialized: bool,
pub destroyed: bool,
}
impl<FS: Filesystem> Session<FS> {
#[cfg(feature = "libfuse")]
pub fn new(filesystem: FS, mountpoint: &Path, options: &[&OsStr]) -> io::Result<Session<FS>> {
info!("Mounting {}", mountpoint.display());
Channel::new(mountpoint, options).map(|ch| Session {
filesystem,
ch,
proto_major: 0,
proto_minor: 0,
initialized: false,
destroyed: false,
})
}
#[cfg(not(feature = "libfuse"))]
pub fn new2(
filesystem: FS,
mountpoint: &Path,
options: &[MountOption],
) -> io::Result<Session<FS>> {
info!("Mounting {}", mountpoint.display());
Channel::new2(mountpoint, options).map(|ch| Session {
filesystem,
ch,
proto_major: 0,
proto_minor: 0,
initialized: false,
destroyed: false,
})
}
pub fn mountpoint(&self) -> &Path {
&self.ch.mountpoint()
}
pub fn run(&mut self) -> io::Result<()> {
let mut buffer: Vec<u8> = Vec::with_capacity(BUFFER_SIZE);
loop {
match self.ch.receive(&mut buffer) {
Ok(()) => match Request::new(self.ch.sender(), &buffer) {
Some(req) => req.dispatch(self),
None => break,
},
Err(err) => match err.raw_os_error() {
Some(ENOENT) => continue,
Some(EINTR) => continue,
Some(EAGAIN) => continue,
Some(ENODEV) => break,
_ => return Err(err),
},
}
}
Ok(())
}
}
impl<FS: 'static + Filesystem + Send> Session<FS> {
pub fn spawn(self) -> io::Result<BackgroundSession> {
BackgroundSession::new(self)
}
}
impl<FS: Filesystem> Drop for Session<FS> {
fn drop(&mut self) {
info!("Unmounted {}", self.mountpoint().display());
}
}
pub struct BackgroundSession {
pub mountpoint: PathBuf,
pub guard: JoinHandle<io::Result<()>>,
fuse_session: *mut libc::c_void,
fd: libc::c_int,
}
impl BackgroundSession {
pub fn new<FS: Filesystem + Send + 'static>(
mut se: Session<FS>,
) -> io::Result<BackgroundSession> {
let mountpoint = se.mountpoint().to_path_buf();
let fuse_session = se.ch.fuse_session;
let fd = se.ch.fd;
se.ch.fuse_session = ptr::null_mut();
let guard = thread::spawn(move || {
let mut se = se;
se.run()
});
Ok(BackgroundSession {
mountpoint,
guard,
fuse_session,
fd,
})
}
}
impl Drop for BackgroundSession {
fn drop(&mut self) {
info!("Unmounting {}", self.mountpoint.display());
match channel::unmount(&self.mountpoint, self.fuse_session, self.fd) {
Ok(()) => (),
Err(err) => error!("Failed to unmount {}: {}", self.mountpoint.display(), err),
}
}
}
impl<'a> fmt::Debug for BackgroundSession {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"BackgroundSession {{ mountpoint: {:?}, guard: JoinGuard<()> }}",
self.mountpoint
)
}
}