Closed mmmarcos closed 3 weeks ago
Alternative to macFuse ? https://developer.apple.com/documentation/fileprovider
Possible other alternative: https://www.fuse-t.org/
Yet another alternative to macFUSE: https://www.fuse-t.org/
Our thoughts about it:
@touilleMan
It's based on a local NFS server that macOS connects to, so there's no driver problem
like with macFUSE on the other hand, in terms of security, this means that data can
more easily remain in macOS cache even after Parsec has been shut down (NFS is used
to expose hard disks across the network, so the OS tends to do quite a lot of caching to
limit latency...) the downside is that this thing isn't open-source (the source code isn't
available on GitHub), so I imagine that if you take out a license for commercial use, you'll
need access to the source code...
@mmmarcos
IMO, not sure that's the right approach: from what I've read, Apple is increasingly
discouraging kernel extensions, but they don't yet have alternative APIs to do it all.
That's probably why extensions still exist.
MacFUSE seems to me to be a good example, because it's a stable product that doesn't seem
ready to make the switch until the OS offers something that holds up.
Fuse-T is probably a good project but, in addition to the problems you highlight, we're not sure
about its stability and robustness. Incidentally, here's some not-so-positive feedback:
https://github.com/osxfuse/osxfuse/issues/987#issuecomment-1932795467
Better the devil you know than the devil you don’t ^^
Anyway, it may be interesting to make some test. We could also ask them about the cache.
See also: https://github.com/macos-fuse-t/fuse-t/wiki#unsupported-features
I will describe the current state of the investigation to add support of mounpoint on MacOS.
The work to add the support is located in the Draft #7420. It is based on the previous work provided by @TimeEngineer in the Draft #7016.
Working on the Draft #7420 and on cberner/fuser#286 allow me to make the following observation:
We cannot run mountpoint's tests on macOS, since it require installing the kernel extension which is not possible on the provided github runner (it require to restart the VM at minimum)
We could run the mountpoint test if we add a custom macOS runner with the required software already installed.
macOS do not have the move semantic (RENAME_EXCHANGE
, RENAME_NOREPLACE
) (or I could not find any documentation for that).
Even tho we could not run the mountpoint's tests doesn't mean the mountpoint on macOS don't work.
We can run the application with the mountpoint enabled it "work" but with some caveats:
cp
will display error because we don't support chmod
operations.
But the file are still copied and their content are okish.
mv
don't work at all.
Once had a weird issue when importing a file where its first thousand bytes where nul, it gives a #7585 vibes.
mount
command to list mountpoint don't seems to list the mountpoint prior a handcheck of somesort (juste create a fuser::Mountpoint
and not running it won't work)
I'm currently working on implementing a MemFS using the fuser
crate, for me it as the following benefit:
fuser
internals.* We cannot run mountpoint's tests on macOS, since it require installing the kernel extension which is not possible on the provided github runner (it require to restart the VM at minimum)
Why this is not the case on V2?
* `cp` will display error because we don't support `chmod` operations. But the file are still copied and their content are okish.
How is the error displayed? (console? GUI Dialog?). Depending on that, this may be acceptable, since it seems to be the case in Parsec V2:
* `mv` don't work at all.
Can you elaborate a little more the tests you performed? (mv a file? mv a dir? mv into the same dir? move into another dir?). Does the rename operation work?
There is a comment in the V2 that might be related: https://github.com/Scille/parsec-cloud/blob/d397c035d1369e498a48dea26837d93b7291dfe4/parsec/core/mountpoint/fuse_operations.py#L305-L317
* `mount` command to list mountpoint don't seems to list the mountpoint prior a handcheck of somesort (juste create a `fuser::Mountpoint` and not running it won't work)
What exactly is "handcheck" in this case? Does the mount command it work after it? Eventually, we can consider it as temporary hack on macOS platform while waiting for a full fix.
Why this is not the case on V2?
Yes, it's not the case on v2 for macOS
How is the error displayed?
Console
What exactly is "handcheck" in this case? Does the mount command it work after it?
The "handcheck" is the operation that append just after you create the mountpoint and start processing event from the kernel, it correspond to the FUSE INIT
event.
So if you don't "run" the filesystem, the init event is never processed and causing the mountpoint to not be displayed on macOS
Can you elaborate a little more the tests you performed?
cd $PARSEC_MNT
touch foo
mv foo bar
The last command don't work
There is a comment in the V2 that might be related: [...]
To give a bit of context about this workaround: in parsec v2, renaming a file from a directory to another one (typically when moving a file) was not supported. Instead, trying this renaming would raise a FSCrossDeviceError
to indicate to the program performing this file operation that it is not supported. In this case, the program would typically fall back to a copy-then-delete kind of move. This is typically what happens when you try to move a file from one drive to another one (since it's obviously not possible to simply rename the file without copying it).
This workaround of delegating the copy-then-delete to the program accessing the mountpoint works fine for most cases but still failed for some specific cases, including the macos temporary files mentioned in the comment. In this case, parsec performs the copy-then-delete even though it is not an atomic operation.
In parsec v3, renaming a file from a directory to another is supported (@touilleMan can you confirm this?), so this workaround should not be necessary anymore.
Why this is not the case on V2?
Yes, it's not the case on v2 for macOS
The point was : do you have any ideas why it worked before and not now? (fuser issue? changes in our ci? etc.)
What exactly is "handcheck" in this case? Does the mount command it work after it?
The "handcheck" is the operation that append just after you create the mountpoint and start processing event from the kernel, it correspond to the
FUSE INIT
event. So if you don't "run" the filesystem, the init event is never processed and causing the mountpoint to not be displayed on macOS
All in all, we might accept the problems you listed and consider them bugs to fix in later releases.
I suggest we merge your PR to enable macOS so our colleagues using Mac can test it.
Why this is not the case on V2?
Yes, it's not the case on v2 for macOS
The point was : do you have any ideas why it worked before and not now? (fuser issue? changes in our ci? etc.)
What worked in the CI in V2? We never had mountpoint tests for macOS in v2
For debugging the issue about mv not working:
Run cargo run -p libparsec_platform_mountpoint --example minimal $HOME/parsec-test
this will mount a workspace in path $HOME/parsec-test/wksp1
.
Note the mountpoint correspond to minimal_client_ready
testbed template, and the console ouput DEBUG log for each operation involving the mountpoint
After that, you can move into $HOME/parsec-test/wksp1/
and do the touch a
(not we are not creating a foo
file here, this is given there is already a foo
folder existing in the workspace).
Once this is done, repetedly smash enter on the console that is running cargo run -p libparsec_platform_mountpoint --example minimal $HOME/parsec-test
. This way is very easy to see where start the logs that will occur for the next operation.
Next operation that, of course, is the mv a b
.
Once this is done you can provide here the DEBUG logs you got ;-)
For instance on Linux here is what we have when doing mv a b
:
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(518) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(520) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(522) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(524) ino 0x0000000000000001 LOOKUP name "a"
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] lookup(parent: 0x1, name: "a")
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(526) ino 0x0000000000000001 LOOKUP name "b"
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] lookup(parent: 0x1, name: "b")
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(528) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(530) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(532) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(534) ino 0x0000000000000001 LOOKUP name "b"
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] lookup(parent: 0x1, name: "b")
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(536) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(538) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(540) ino 0x0000000000000001 LOOKUP name "a"
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] lookup(parent: 0x1, name: "a")
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(542) ino 0x0000000000000001 LOOKUP name "b"
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] lookup(parent: 0x1, name: "b")
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(544) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(546) ino 0x0000000000000001 GETATTR
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] getattr(ino: 0x1)
[2024-06-26T07:58:54Z DEBUG fuser::request] FUSE(548) ino 0x0000000000000001 RENAME src FilenameInDir { dir: INodeNo(1), name: "a" }, dest FilenameInDir { dir: INodeNo(1), name: "b" }
[2024-06-26T07:58:54Z DEBUG libparsec_platform_mountpoint::unix::filesystem] [FUSE] rename(src_parent: 0x1, src_name: "a", dst_parent: 0x1, \
std_name: "b", flags: 0)
[2024-06-26T07:58:54Z DEBUG sqlx::query] summary="INSERT INTO vlobs(vlob_id, need_sync, …" db.statement="\n\nINSERT INTO\n vlobs(\n vlob_id,\n need_sync,\n blob,\n base_version,\n remote_version\n )\nVALUES\n (?1, ?2, ?3, ?4, ?5) ON CONFLICT DO\nUPDATE\nSET\n need_sync = excluded.need_sync,\n blob = excluded.blob,\n base_version = excluded.base_version,\n remote_version = MAX(excluded.remote_version, remote_version)\n" rows_affected=1 rows_returned=0 elapsed=36.97µs
What I noticed when I tested the mountpoint on macOS.
Feature wanted | By finder | By app |
---|---|---|
Create folder “no-name” | ✅ | ✅ |
Rename file/folder | ❌ | ✅ |
Move file/folder | ❌ | ✅ |
Delete file/folder | ✅ | ✅ |
Copy/paste | ❌ | ✅ |
Add new file/folder | ❌ | ✅ |
I performed some additional tests on my side using a Custom MemFS using fuser. Here is the following capabilities I was able to achieved:
That's a nice set of capabilities, but something important is missing:
The abilities to rename files!
Currently when doing a rename operation:
fn rename(src_dir: Inode, src_name: &OsStr, dst_dir: Inode, dst_name: &OsStr) {
// ...
}
macFUSE do not provide src_name
& dst_name
(the value are blank) making it impossible to perform the operation.
rename(1, "", 2, "")
[!NOTE] I say
macFUSE
but I haven't checked who is at fault between:
- The kernel extension
macFUSE
- The interation between
macFUSE
andfuser
I've tested Parsec using the same tests script than MemFS. It result in the same results: The rename do not work!
I've also tried some basic operation on the Finder:
Creating a folder don't work because finder
first create a folder named untitled folder
then tries to rename it.
You still obtain a folder named
untitled folder
tho
Creating a file using TextEdit
don't work for the same reason as for the folder:
Since the rename fail here, it also do a
unlink
step meaning the file is removed.
What need to do first is to investigate macFUSE
:
Does macFUSE
allow to rename a file?
Here we would need to test macFUSE
without using fuser
.
first we could try using the fuse high-level function and depending if it's possible to perform a rename operation try the low-level stuff
[!NOTE] If the
high-level
test fail that mean game-over formacFUSE
but since we where able to provide the mountpoint for parsec-v2. I expect that should work and the problem lies on the low-level stuff where it differ from linux and macos.
After some investigation, I've found out that fuser
was doing a bad parsing of the data provided by macFUSE on the rename operation.
I've provided a fix for that (cberner/fuser#307), in the mean time we will use that fix early (#8766)
Parsec packages are already available for macOS (although not yet signed). The only thing missing seems to be file system integration (mountpoints).
Implementation should probably be based on
macFUSE
because already used for V2, although it may be useful to do some research about other APIs (see comments below).