diff --git a/Cargo.toml b/Cargo.toml index 8794e7c..cfe6a45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ env_logger = "0.3" [lib] name = "fuse" path = "src/lib.rs" + +[features] +libfuse = [] diff --git a/build.rs b/build.rs index 0ead5ff..b615c79 100644 --- a/build.rs +++ b/build.rs @@ -7,5 +7,6 @@ static LIBFUSE_NAME: &str = "fuse"; static LIBFUSE_NAME: &str = "osxfuse"; fn main () { + #[cfg(feature = "libfuse")] pkg_config::Config::new().atleast_version("2.6.0").probe(LIBFUSE_NAME).unwrap(); } diff --git a/src/channel.rs b/src/channel.rs index af29db7..fef87bc 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -7,11 +7,14 @@ use std::ffi::{CString, CStr, OsStr}; use std::os::unix::ffi::OsStrExt; use std::path::{PathBuf, Path}; use libc::{self, c_int, c_void, size_t}; +use std::os::unix::io::{RawFd, AsRawFd, FromRawFd}; +#[cfg(feature = "libfuse")] use libfuse::{fuse_args, fuse_mount_compat25}; use reply::ReplySender; /// Helper function to provide options as a fuse_args struct /// (which contains an argc count and an argv pointer) +#[cfg(feature = "libfuse")] fn with_fuse_args T>(options: &[&OsStr], f: F) -> T { let mut args = vec![CString::new("rust-fuse").unwrap()]; args.extend(options.iter().map(|s| CString::new(s.as_bytes()).unwrap())); @@ -23,7 +26,7 @@ fn with_fuse_args T>(options: &[&OsStr], f: F) -> T #[derive(Debug)] pub struct Channel { mountpoint: PathBuf, - fd: c_int, + fd: RawFd, } impl Channel { @@ -31,6 +34,7 @@ impl Channel { /// given path. The kernel driver will delegate filesystem operations of /// the given path to the channel. If the channel is dropped, the path is /// unmounted. + #[cfg(feature = "libfuse")] pub fn new(mountpoint: &Path, options: &[&OsStr]) -> io::Result { let mountpoint = try!(mountpoint.canonicalize()); with_fuse_args(options, |args| { @@ -76,16 +80,26 @@ impl Drop for Channel { fn drop(&mut self) { // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on macOS before closing the fd // Close the communication channel to the kernel driver - // (closing it before unnmount prevents sync unmount deadlock) + // (closing it before unmount prevents sync unmount deadlock) unsafe { libc::close(self.fd); } // Unmount this channel's mount point + #[cfg(feature = "libfuse")] let _ = unmount(&self.mountpoint); } } +impl FromRawFd for Channel { + /// Creates a channel from a file descriptor of a initialized Fuse. + /// The file descriptor will be closed when `Channel` goes out of scope; + /// however the fuse will be not unmounted. + unsafe fn from_raw_fd(fd: RawFd) -> Channel { + Channel { fd: fd, mountpoint: PathBuf::from("") } + } +} + #[derive(Clone, Copy, Debug)] pub struct ChannelSender { - fd: c_int, + fd: RawFd, } impl ChannelSender { @@ -94,7 +108,7 @@ impl ChannelSender { let iovecs: Vec<_> = buffer.iter().map(|d| { libc::iovec { iov_base: d.as_ptr() as *mut c_void, iov_len: d.len() as size_t } }).collect(); - let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) }; + let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as RawFd) }; if rc < 0 { Err(io::Error::last_os_error()) } else { @@ -112,6 +126,7 @@ impl ReplySender for ChannelSender { } /// Unmount an arbitrary mount point +#[cfg(feature = "libfuse")] pub fn unmount(mountpoint: &Path) -> io::Result<()> { // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally, // it attempts to call realpath, which in turn calls into the filesystem. So diff --git a/src/lib.rs b/src/lib.rs index 4c1374a..b6889c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub use session::{Session, BackgroundSession}; mod argument; mod channel; mod kernel; +#[cfg(feature = "libfuse")] mod libfuse; mod reply; mod request; @@ -382,6 +383,7 @@ pub trait Filesystem { /// Mount the given filesystem to the given mountpoint. This function will /// not return until the filesystem is unmounted. +#[cfg(feature = "libfuse")] pub fn mount>(filesystem: FS, mountpoint: &P, options: &[&OsStr]) -> io::Result<()>{ Session::new(filesystem, mountpoint.as_ref(), options).and_then(|mut se| se.run()) } @@ -391,6 +393,7 @@ pub fn mount>(filesystem: FS, mountpoint: &P, opt /// and therefore returns immediately. The returned handle should be stored /// to reference the mounted filesystem. If it's dropped, the filesystem will /// be unmounted. +#[cfg(feature = "libfuse")] pub unsafe fn spawn_mount<'a, FS: Filesystem+Send+'a, P: AsRef>(filesystem: FS, mountpoint: &P, options: &[&OsStr]) -> io::Result> { Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.spawn()) } diff --git a/src/session.rs b/src/session.rs index a57f4ea..e04320a 100644 --- a/src/session.rs +++ b/src/session.rs @@ -8,6 +8,7 @@ use std::io; use std::ffi::OsStr; use std::fmt; +use std::os::unix::io::{RawFd, FromRawFd}; use std::path::{PathBuf, Path}; use thread_scoped::{scoped, JoinGuard}; use libc::{EAGAIN, EINTR, ENODEV, ENOENT}; @@ -43,6 +44,7 @@ pub struct Session { impl Session { /// Create a new session by mounting the given filesystem to the given mountpoint + #[cfg(feature = "libfuse")] pub fn new(filesystem: FS, mountpoint: &Path, options: &[&OsStr]) -> io::Result> { info!("Mounting {}", mountpoint.display()); Channel::new(mountpoint, options).map(|ch| { @@ -57,6 +59,18 @@ impl Session { }) } + /// Create a new session by using a file descriptor "/dev/fuse" + pub fn new_from_fd(filesystem: FS, fd: RawFd) -> Session { + Session { + filesystem: filesystem, + ch: unsafe { Channel::from_raw_fd(fd) }, + proto_major: 0, + proto_minor: 0, + initialized: false, + destroyed: false, + } + } + /// Return path of the mounted filesystem pub fn mountpoint(&self) -> &Path { &self.ch.mountpoint() @@ -138,6 +152,7 @@ impl<'a> Drop for BackgroundSession<'a> { info!("Unmounting {}", self.mountpoint.display()); // Unmounting the filesystem will eventually end the session loop, // drop the session and hence end the background thread. + #[cfg(feature = "libfuse")] match channel::unmount(&self.mountpoint) { Ok(()) => (), Err(err) => error!("Failed to unmount {}: {}", self.mountpoint.display(), err),