winfsp / cgofuse

Cross-platform FUSE library for Go - Works on Windows, macOS, Linux, FreeBSD, NetBSD, OpenBSD
https://winfsp.dev
MIT License
511 stars 82 forks source link

Need to know when the fuse service is ready #71

Closed thallgren closed 1 year ago

thallgren commented 1 year ago

The ci-tests for my go-fuseftp sometimes fail, especially on Windows. The only way to get the tests consistently green is by inserting a fairly long sleep after the actual FileSystemHost.Mount call, which begs the question:

Is there any event sent from the FileSystemHost when it is ready for action? I've seen a message printed on the console (only on windows) saying "The service fs.test has been started" ("fs.test" is the name of my binary). I guess what I'm asking for is some way to wait until that message has been printed (or something similar).

billziss-gh commented 1 year ago

Due to differences in how the various FUSE libraries work, there is no straightforward way to do this.

See my comment here for an approach on Windows:

https://github.com/winfsp/winfsp/discussions/440#discussioncomment-3573309

I copy the relevant part:

One indirect way would be to sleep a bit (say 500ms) then try to access the new drive (e.g. by opening the "volume" using the syntax \.\X: as detailed in the CreateFileW documentation). If that succeeds then the drive is up and running, if not sleep another 500ms and try again, etc.

thallgren commented 1 year ago

Thanks for explaining. I'll add something like that.

thallgren commented 1 year ago

The retry-access method didn't work for me on Linux/macOS because the directory exists before the actual Mount happens. So I ended up using another approach which I'll convey here. Perhaps it can help someone.

A added a started chan struct{} to my FileSystemInterface implementation. If this channel is not nil, it will be closed and set to nil when the first call to Getattr is received. This call seems to be the first thing that all FUSE implementations do when started. So, in essence, my logic does this:

  1. Create the started channel.
  2. Create the FileSystemInterface implementation with that channel.
  3. Create the FileSystemHost and call Mount in a separate goroutine.
  4. Wait for the channel to close.
  5. Continue processing, assuming that the fuse service is up and running.
billziss-gh commented 1 year ago

The retry-access method didn't work for me on Linux/macOS because the directory exists before the actual Mount happens.

An approach for Linux/macOS (along the same lines as the hack for Windows) would be to check if the st_dev and st_ino fields change after a stat(2) call. (I am on Windows right now and cannot confirm that this works, but it should work in principle.)

So I ended up using another approach which I'll convey here. Perhaps it can help someone...

FYI, at least on Windows, you will receive a Getattr prior to the mountpoint existing.

This is because the WinFsp-FUSE implementation needs to gather information in order to create the in-kernel file system objects and it does so by issuing init, statfs, getattr, etc. (see relevant code) prior to creating the in-kernel file system objects and mounting the file system (see relevant code).

Source: I am the WinFsp author.