zargony / fuse-rs

Rust library for filesystems in userspace (FUSE)
MIT License
1.07k stars 130 forks source link

Cleanup on terminate or prior to mount #58

Open parasyte opened 8 years ago

parasyte commented 8 years ago

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.

zargony commented 8 years ago

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(?)

parasyte commented 8 years ago

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

cuviper commented 8 years ago

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.

parasyte commented 8 years ago

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.

gkoz commented 8 years ago

@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).

zargony commented 8 years ago

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.

cuviper commented 8 years ago

Interesting, I didn't know about chan-signal. But I agree the application should take control of signals, not the library.

parasyte commented 8 years ago

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:

wfraser commented 8 years ago

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.

cuviper commented 8 years ago

@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.

parasyte commented 8 years ago

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.

kahing commented 7 years ago

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.

dsluijk commented 5 years ago

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();
ylxdzsw commented 5 years ago

I believe you need to pass them separately:

options.push(String::from("-o"))
options.push(String::from("auto_unmount"))
samuela commented 4 years ago

What's the consensus on this issue? Should I also just copy-paste unmount into my project?

samuela commented 4 years ago

Also I can confirm that auto_unmount doesn't work with osxfuse.