use std::io;
use std::ffi::OsStr;
use std::fmt;
use std::path::{PathBuf, Path};
use thread_scoped::{scoped, JoinGuard};
use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel::{self, Channel};
use Filesystem;
use request;
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> {
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: filesystem,
ch: 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::request(self.ch.sender(), &buffer) {
Some(req) => request::dispatch(&req, 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<'a, FS: Filesystem + Send + 'a> Session<FS> {
pub unsafe fn spawn(self) -> io::Result<BackgroundSession<'a>> {
BackgroundSession::new(self)
}
}
impl<FS: Filesystem> Drop for Session<FS> {
fn drop(&mut self) {
info!("Unmounted {}", self.mountpoint().display());
}
}
pub struct BackgroundSession<'a> {
pub mountpoint: PathBuf,
pub guard: JoinGuard<'a, io::Result<()>>,
}
impl<'a> BackgroundSession<'a> {
pub unsafe fn new<FS: Filesystem + Send + 'a>(se: Session<FS>) -> io::Result<BackgroundSession<'a>> {
let mountpoint = se.mountpoint().to_path_buf();
let guard = scoped(move || {
let mut se = se;
se.run()
});
Ok(BackgroundSession { mountpoint: mountpoint, guard: guard })
}
}
impl<'a> Drop for BackgroundSession<'a> {
fn drop(&mut self) {
info!("Unmounting {}", self.mountpoint.display());
match channel::unmount(&self.mountpoint) {
Ok(()) => (),
Err(err) => error!("Failed to unmount {}: {}", self.mountpoint.display(), err),
}
}
}
impl<'a> fmt::Debug for BackgroundSession<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "BackgroundSession {{ mountpoint: {:?}, guard: JoinGuard<()> }}", self.mountpoint)
}
}