fuse-rs
fuse-rs copied to clipboard
Cleanup on terminate or prior to mount
The file system is not unmounted when the application is terminated. E.g. ctrl+c in the hello example. Attempting to run the application again (on OSX) errors out with:
mount_osxfusefs: mount point /private/tmp/test is itself on a OSXFUSE volume
I wanted to use the unmount function prior to calling mount, but it's private. Hoping this would do the necessary cleanup to attempt unmounting orphaned file systems, and let the application happily continue running.
The unmount method is private since resources are meant to be managed by RAII and the FUSE filesystem lives as long as the Session exists. Dropping the Session will unmount the filesystem to clean up.
If you try to mount a filesystem at a path that already has a filesystem mounted, the correct behavior is to error. If you want to unmount an existing mount for convenience, you could try using the libc umount method.
The problem with Ctrl-C is, that the Rust program gets interrupted and immediately quits without running Session::drop. We need a chance to free the resource (unmount) even with Ctrl-C. C/C++ programs typically use the libc atexit mechanism for this, but afaik Rust does not provide such a mechanism yet(?)
That's right. There seems to be little interest in getting atexit working in Rust at the moment: https://github.com/rust-lang/rfcs/issues/712 And it sounds like it won't be foolproof, anyway: https://github.com/rust-lang/rust/issues/11695#issuecomment-32834967
It can never be foolproof - e.g. SIGKILL doesn't give a process any chance to react. But it sure would be nice to handle catchable signals like SIGINT (crtl-c), SIGHUP, SIGTERM.
That was my conclusion, and reasoning for pre-mount cleanup. :wink: It looks like the private unmount is perfect for that, since it takes care of some important details like the EPERM case on Linux.
I've copy-pasted it into my crate for the time being. That will do if the function cannot be made public.
@parasyte A superior solution IMO would be handling signals in the application (e.g. with the chan-signal crate) and letting the cleanup happen naturally (through RAII).
We could also think about adding signal handling to rust-fuse. With chan-signal it seems pretty easy and could be enabled/disabled via a cargo feature flag. This sounds convenient, however I'm not sure if I like it, since signal handling should belong to the application rather than a filesystem library.
Interesting, I didn't know about chan-signal. But I agree the
application should take control of signals, not the library.
Performing cleanup steps with signal handlers is a fine idea, but it doesn't eliminate the utility of a public umount function. I can think of at least two cases where it would be convenient:
fuse::mountonly returns after the filesystem is unmounted: https://github.com/zargony/rust-fuse/blob/573233b236715a7bf56dd3202639aada983159c7/src/lib.rs#L374-L375- Handling the worst-case scenarios, e.g.
SIGKILLprovides no opportunity to cleanup. But we can still unmount the killed mount on startup.
I have fuse 2.9.5 on Linux, and it has the "-o auto_unmount" option which achieves what you want. Pass those options to the fuse::mount function, and on program exit, the filesystem will be unmounted.
@wfraser nice tip! From libfuse/NEWS, that option was added in 2.9.0, almost 4 years ago.
AFAICS this requires no kernel support -- the spawned fusermount just daemonizes and waits for its socket to close, indicating that the original process is gone. It does mean that fusermount is always used, even if current user privileges wouldn't otherwise require it for setuid.
I believe OSXFUSE is built on libfuse too, but I have no idea if they include fusermount to allow this.
OSXFUSE is implemented by mount_bsd.c, which doesn't include support for the auto_unmount option. Good to know it exists for Linux, though.
auto-unmounting is probably a bad idea. Imagine a background process that runs rsync --delete fuse/mnt/ dir/, and the fuse process crashes. If the filesystem was unmounted rsync would happily delete everything in dir/, whereas you'd get an error if it was still mounted.
I'm currently having issues with this. The -o auto_unmount seems like a good solution, but it does not seem to work for me. I still get the error Transport endpoint is not connected, which is solved by manually running umount. This is on Arch Linux. Is it since the last update on this ticket possible to do it another way?
Relevant bit of my code:
let mut options = Vec::new();
options.push(format!("-o fsname={:?}", &conf.name));
options.push(String::from("-o ro"));
if conf.allow_others {
debug!("Enabling others access to FUSE.");
options.push(String::from("-o allow_other"));
}
if conf.auto_unmount {
debug!("Automatic unmounting of FUSE enabled.");
options.push(String::from("-o auto_unmount"));
}
let option_slice = options.iter().map(|o| o.as_ref()).collect::<Vec<&OsStr>>();
mount(ShareFS, conf.mountpoint, &option_slice).unwrap();
I believe you need to pass them separately:
options.push(String::from("-o"))
options.push(String::from("auto_unmount"))
What's the consensus on this issue? Should I also just copy-paste unmount into my project?
Also I can confirm that auto_unmount doesn't work with osxfuse.