[fix] Windows: WinFsp has getdir before readlink in fuse_operations - #52
ThomasWaldmann wants to merge 3 commits into
Conversation
1740095 to
6bcd675
Compare
|
Force-pushed: the first version failed the mypy check ( About the other red jobs of the first run: |
|
CI status of the 2nd run: Static-Code-Checks pass now. The two jobs that really fail are not related to this change (everything else red was cancelled by fail-fast):
|
…failure
perfuse is part of the NetBSD base system, there is no such package. pkgin
now fails when asked to install a package that does not exist:
perfuse is not available in the repository
fail-fast: false, so one can see which platforms a failure affects.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
mfusepy looked up the macfuse_version symbol in libfuse to detect the
legacy MacFuse (and then used an old 32 bit inode struct stat layout).
macFUSE does not export that symbol, so this was a lookup of a missing
symbol on every import.
With macFUSE 5.3.x, libfuse depends on a lot of system frameworks (dyld
loads about 500 images for it) and on macOS 14, that lookup makes dyld abort:
dyld[29263]: Assertion failed: (_usedCount < _allocCount), function push_back, file Array.h, line 64.
So "import mfusepy" crashed the Python interpreter there (macOS 15 is fine).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
The unrelated CI failures (macos-14 dyld abort at import, NetBSD perfuse package) are dealt with in #53. Once that is in, I'll rebase this one. |
libfuse 2 has getattr, readlink, getdir at the start of struct fuse_operations, but WinFsp has getattr, getdir, readlink, see: https://github.com/winfsp/winfsp/blob/master/inc/fuse/fuse.h With the libfuse order, the readlink callback ended up in the getdir slot (which WinFsp never calls if there is a readdir) and readlink was NULL for WinFsp. WinFsp then decides that the file system does not support symlinks and shows all symlinks as regular files. Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
Rebased onto #53 (the CI fixes), so CI can be green here. Until #53 is merged, this PR shows its 2 commits as well - the only commit that belongs to this PR is the last one ( About the struct order: the WinFsp author confirmed in winfsp/winfsp#693 that it "was an early accident that unfortunately no longer can be fixed due to backwards compatibility", so the bindings have to follow WinFsp's order. |
6bcd675 to
3e98551
Compare
On Windows (WinFsp), symlinks of a mfusepy file system show up as regular files and
readlinkof the operations class is never called.Cause
libfuse 2 starts
struct fuse_operationswithgetattr, readlink, getdir, but WinFsp'sinc/fuse/fuse.hhas:All other members are in the libfuse order. So with mfusepy's (libfuse) order:
readlinkcallback lands in WinFsp'sgetdirslot. WinFsp only callsgetdirif there is noreaddir, so this went unnoticed (it would crash otherwise).getdir(NULL) lands in WinFsp'sreadlinkslot. WinFsp checks0 != f->ops.readlinkwhen starting the file system (fuse_loop.c,has_symlinks) and, as it is NULL, reportsS_IFLNKfiles without the reparse point attribute, i.e. as regular files.I guess fusepy's WinFsp port always had this.
Fix
Use the WinFsp order of these two fields for Windows / Cygwin (cygfuse is built with the same WinFsp header).
Testing
Windows 11, WinFsp 2.1, MSYS2 UCRT64 Python 3.14, using
borg mount(borgbackup/borg#10391) on an archive with a file symlink, a directory symlink and a dangling symlink:before:
after:
Not tested on Cygwin. Everything else I tried via WinFsp worked fine with mfusepy 3.1.1 (getattr, readdir, open / read / release, statfs, init with
fuse_conn_info.want, timestamps incl. birthtime).Note: once WinFsp sees a
readlink, it callsreadlink("/")when starting the file system and expects that to fail with ENOSYS or EINVAL, so areadlinkimplementation should raiseFuseOSError(errno.EINVAL)for a non-symlink rather than a random exception (which mfusepy logs with a traceback).🤖 Generated with Claude Code