Closed cehteh closed 2 years ago
Have you tried using spawn_mount
? I believe it provides this functionality if you drop the BackgroundSession
object that it returns
On 2021-09-21 18:25, Christopher Berner wrote:
Have you tried using
spawn_mount
? I believe it provides this functionality if you drop theBackgroundSession
object that it returns
having the main thread just linger for closing/unmounting the filesystem thread looked a bit unelegant to me. I am calling process::Command("fusermount -u ...") so far. That the filesystem unmounts itself is only a rare case anyway (ctrl-c handler in foreground/debug mode)
-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/cberner/fuser/issues/179#issuecomment-924507459
Just some thinking: since fuser implements the event loop this could use some (atomic) bool or enum for the loop condition to be shutting down in a controlled way. I may implement such an event loop for my own filesystem, ping me when you are interested in merging such an facility.
Just some thinking: since fuser implements the event loop this could use some (atomic) bool or enum for the loop condition to be shutting down in a controlled way. I may implement such an event loop for my own filesystem, ping me when you are interested in merging such an facility.
.. which is not as trivial as I thought because the Request api's are not public, one can't write his own event loop. I am postponing this now but will revisit it in future.
I've got some plans in this area. Roughly:
read()
ing from the FUSE fd we'd poll()
the FUSE fd and the interrupt FD exiting the main loop when the write end of the pipe is closed. This would still be generic over FS type. We could then also implement different mainloop function implementations for different requirements - multi-threaded, async, etc.This was going to be a part of #136, but I haven't had time to work on fuse-rs for quite some time.
1. Separate out mounting the filesystem and running init from actually running the main loop. This would be a natural place to return errors, and would solve [RFC: Callback before entering the event loop #175](https://github.com/cberner/fuser/issues/175) as well. This would involve the typestate pattern to distinguish mounts that have been inited from ones that haven't. It would be separate from session as it would not need to be generic over FS type.
Sounds good to me, i'd like when the mainloop body would be exposed too, then one could write a custom main loop as well.
2. Have a separate function for running the main loop. We'd pass a token into the main loop function to allow interrupting the main loop. This would be implemented in terms of a UNIX pipe, then in the main loop instead of `read()`ing from the FUSE fd we'd `poll()` the FUSE fd and the interrupt FD exiting the main loop when the write end of the pipe is closed. This would still be generic over FS type. We could then also implement different mainloop function implementations for different requirements - multi-threaded, async, etc.
Ideally one could opt out from the pipe, because these take resources (fd) while being only used very rarely. For in-process communication an atomic bool (plus perhaps caching an result/error within the session) to abort the mainloop could be enough. Maybe a signal to abort the wait for the kernel request.
I want to add that currently there’s no way to install a race-free signal handler that will always unmount the fuse filesystem, as far as I can see:
let _guard = fuser::mount2(…)
… install signal handler here using the guard …
this pseudo-code has a toctou issue between getting the guard and installing the signal handler. But until you have the guard, you can’t install the signal handler.
You could pass auto_unmount
to the mount2
call, but then you run into https://github.com/cberner/fuser/issues/167 :(
I kinda worked around it now by using this:
fn main() {
let (send, recv) = std::sync::mpsc::channel();
ctrlc::set_handler(move || {
println!("ctrl+c handler running");
send.send(()).unwrap();
})
.unwrap();
let guard = fuser::spawn_mount(
fuse_client(),
&"/home/philip/tmp/fuse",
&vec![],
)
.unwrap();
let () = recv.recv().unwrap();
drop(guard)
}
but no idea how racy it is in practice. It seems to work for ctrl+c.
That looks race free to me, and is how I would recommend doing it
@Profpatsch
This works well for Ctrl+C, but how do you handle the case where the filesystem is unmounted and the background thread finishes? Ideally one would like the program to quit, but here the main thread will hang on recv.recv()
because it never receives a signal.
I've worked around this by patching fuser::spawn_mount()
to accept a Option<std::sync::mpsc::Sender<()>>
, so that a message also gets sent to the channel when the thread quits. See https://github.com/abspoel/fuser
fn main() {
let (send, recv) = std::sync::mpsc::channel();
let send_ctrlc = send.clone();
ctrlc::set_handler(move || {
println!("ctrl+c handler running");
send_ctrlc.send(()).unwrap();
})
.unwrap();
let send_finished = send.clone();
let guard = fuser::spawn_mount(
fuse_client(),
&"/home/philip/tmp/fuse",
&vec![],
Some(send_finished),
)
.unwrap();
let () = recv.recv().unwrap();
drop(guard)
}
Is this the most straightforward way or is there a better alternative? (Maybe one that does not involve modifying the spawn_mount
function.)
@abspoel I think you can pass the Sender object into your Filesystem and call it in Filesystem::destroy()
, without patching spawn_mount. That's called when the Session is dropped
@cberner Thanks, that works!
I'm going to close this, since all the use cases so far can be supported with the existing spawn_mount
function. Feel free to re-open if there's a use case that it does not support
Eventually one wants to unmount a filesystem. Besides manual unmounting by the user, a programm may decide to unmount the filesystem reacting to some event or signal. This can be done by
But this can be inconsistent/fallible depending on where the fuse 'fusermount' tool is installed. This Library already does some detection thereof and it would be more ergonomic to have a 'umount' as API.