#[cfg(feature = "libfuse2")]
mod fuse2;
#[cfg(any(feature = "libfuse", test))]
mod fuse2_sys;
#[cfg(feature = "libfuse3")]
mod fuse3;
#[cfg(feature = "libfuse3")]
mod fuse3_sys;
#[cfg(not(feature = "libfuse"))]
mod fuse_pure;
pub mod mount_options;
#[cfg(any(feature = "libfuse", test))]
use fuse2_sys::fuse_args;
#[cfg(any(test, not(feature = "libfuse")))]
use std::fs::File;
#[cfg(any(test, not(feature = "libfuse"), not(feature = "libfuse3")))]
use std::io;
#[cfg(any(feature = "libfuse", test))]
use mount_options::MountOption;
#[cfg(any(feature = "libfuse", test))]
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T>(options: &[MountOption], f: F) -> T {
use mount_options::option_to_string;
use std::ffi::CString;
let mut args = vec![CString::new("rust-fuse").unwrap()];
for x in options {
args.extend_from_slice(&[
CString::new("-o").unwrap(),
CString::new(option_to_string(x)).unwrap(),
]);
}
let argptrs: Vec<_> = args.iter().map(|s| s.as_ptr()).collect();
f(&fuse_args {
argc: argptrs.len() as i32,
argv: argptrs.as_ptr(),
allocated: 0,
})
}
#[cfg(feature = "libfuse2")]
pub use fuse2::Mount;
#[cfg(feature = "libfuse3")]
pub use fuse3::Mount;
#[cfg(not(feature = "libfuse"))]
pub use fuse_pure::Mount;
#[cfg(not(feature = "libfuse3"))]
use std::ffi::CStr;
#[cfg(not(feature = "libfuse3"))]
#[inline]
fn libc_umount(mnt: &CStr) -> io::Result<()> {
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "bitrig",
target_os = "netbsd"
))]
let r = unsafe { libc::unmount(mnt.as_ptr(), 0) };
#[cfg(not(any(
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "bitrig",
target_os = "netbsd"
)))]
let r = unsafe { libc::umount(mnt.as_ptr()) };
if r < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(any(test, not(feature = "libfuse")))]
fn is_mounted(fuse_device: &File) -> bool {
use libc::{poll, pollfd};
use std::os::unix::prelude::AsRawFd;
let mut poll_result = pollfd {
fd: fuse_device.as_raw_fd(),
events: 0,
revents: 0,
};
loop {
let res = unsafe { poll(&mut poll_result, 1, 0) };
break match res {
0 => true,
1 => (poll_result.revents & libc::POLLERR) != 0,
-1 => {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue;
} else {
panic!("Poll failed with error {}", err)
}
}
_ => unreachable!(),
};
}
}
#[cfg(test)]
mod test {
use super::*;
use std::{ffi::CStr, mem::ManuallyDrop};
#[test]
fn fuse_args() {
with_fuse_args(
&[
MountOption::CUSTOM("foo".into()),
MountOption::CUSTOM("bar".into()),
],
|args| {
let v: Vec<_> = (0..args.argc)
.map(|n| unsafe {
CStr::from_ptr(*args.argv.offset(n as isize))
.to_str()
.unwrap()
})
.collect();
assert_eq!(*v, ["rust-fuse", "-o", "foo", "-o", "bar"]);
},
);
}
fn cmd_mount() -> String {
std::str::from_utf8(
std::process::Command::new("sh")
.arg("-c")
.arg("mount | grep fuse")
.output()
.unwrap()
.stdout
.as_ref(),
)
.unwrap()
.to_owned()
}
#[test]
fn mount_unmount() {
let tmp = ManuallyDrop::new(tempfile::tempdir().unwrap());
let (file, mount) = Mount::new(tmp.path(), &[]).unwrap();
let mnt = cmd_mount();
eprintln!("Our mountpoint: {:?}\nfuse mounts:\n{}", tmp.path(), mnt,);
assert!(mnt.contains(&*tmp.path().to_string_lossy()));
assert!(is_mounted(&file));
drop(mount);
let mnt = cmd_mount();
eprintln!("Our mountpoint: {:?}\nfuse mounts:\n{}", tmp.path(), mnt,);
let detached = !mnt.contains(&*tmp.path().to_string_lossy());
#[cfg(target_os = "linux")]
assert!(detached);
if detached {
std::mem::ManuallyDrop::<_>::into_inner(tmp);
}
}
}