Feat/macos port clean#202
Conversation
Spike proving the live fuse3 backend works on macOS via macFUSE, so no fuser port is needed: - system/filesystem: widen statvfs block counts (u32 on macOS) to u64 - fuse3/shared: add macOS-only crtime/flags fields to FileAttr literals - fuse3/filesystem: implement open/release; macFUSE lacks FUSE_NO_OPEN_SUPPORT and forwards ENOSYS from open to read() - add ignored macos_mount_spike integration test (mount, read, readdir)
- add system::unmount::unmount_fuse: platform-abstracted unmount (fusermount on Linux, umount -f on macOS) used by both the panic handler and the SpawnedMountHandle Drop fallback, replacing the duplicated fusermount shell-outs - verify the production Fuse3OrthoUnionFS mounts on macFUSE end-to-end (mount, read scenery file, readdir, unmount) in macos_mount_spike - cover unmount_fuse with a fast non-mountpoint unit test The async MountHandle::unmount path remains the normal (fast) teardown; the shell-out helper is only the panic/Drop fallback, so macOS force- detaches rather than risk blocking.
macFUSE rejects some read replies with EINVAL under load; the previously pinned fuse3 commit treated that as fatal and tore down the mount. Point at dlicudi/fuse3 fix/macos-reply-error-resilience, which is samsoir's configurable-background plus a patch that drops the bad reply and keeps the session alive.
is_mounted() only read /proc/mounts, which doesn't exist on macOS, so it always returned false and the umount fallback in unmount_sync never ran. The fuse3 task completing also doesn't prove macFUSE released the mount. Make is_mounted() platform-correct (mount(8) on macOS) and always verify + escalate to umount -f rather than trusting task completion. Ends the stale-mount that blocked every restart.
BSD split on macOS supports neither --version nor --bytes, causing 'split command failed' panics in the publisher on stock macOS. Probe availability with a portable no-op (split -b 1 /dev/null) and invoke with -b <size>, which both GNU and BSD variants accept. Part naming (.aa/.ab suffixes) is identical across variants, so manifests are unaffected. Fixes test_build_archive_splits_large_file on macOS.
The X-Plane installer writes x-plane_install_12.txt to ~/Library/Preferences on macOS, not ~/.x-plane (Linux). Auto-detection of the installation and Custom Scenery directory silently failed on macOS, so 'packages install' could not create overlay symlinks without a manually configured custom_scenery_path. Add a macos cfg branch to get_install_reference_path() and update the config docs that hardcoded the Linux path.
The run command only read packages.custom_scenery_path and xplane.scenery_dir from config, while the packages commands also fall back to detect_custom_scenery(). With both keys unset, run failed with a configuration error even when the X-Plane installation is discoverable. Add the same auto-detect fallback for consistency. Required for out-of-the-box startup on macOS, where the install reference file detection was previously broken (see prior commit) and setup left both keys empty.
Replace the mutable branch reference to dlicudi/fuse3 with an immutable rev pin. The branch was rebased onto upstream master (which now includes samsoir's configurable-background PR samsoir#136), carrying the single reply-resilience patch pending as an upstream PR. Required for the macOS port: macFUSE can transiently reject reply writes under load; without the patch the whole session tears down.
A run that dies without unmounting (crash, SIGKILL, system sleep) leaves a dead mount on the mountpoint; the next mount attempt fails with ENXIO 'Device not configured' and the user must umount by hand. Detect the corpse before mounting and force-unmount it. Detection must use opendir(2), not stat(2): macOS serves cached attributes for a dead macFUSE mountpoint, so stat succeeds while reading the directory fails with ENXIO (verified against a real killed mount; a metadata()-based probe silently missed it). Linux's equivalent dead-mount errno ENOTCONN is classified too. Also make the mount-failure hints platform-aware: the previous text suggested apt/fusermount on macOS; now it points at macFUSE install, Privacy & Security approval, and umount. Verified end-to-end on macOS: mount, SIGKILL, restart -> WARN 'Stale FUSE mount detected from a previous run; force-unmounting' -> healthy mount.
The reply-resilience patch is now submitted upstream as Sherlock-Holo/fuse3#137. Once merged, repoint the dependency to Sherlock-Holo/fuse3 (which also retires main's samsoir/fuse3 branch pin, merged upstream as samsoir#136).
Update platform support in README and CLAUDE.md: macOS works with macFUSE (Apple Silicon tested, multi-hour X-Plane flights). Notes the fuse3 fork rev pin and its pending upstream PR (Sherlock-Holo/fuse3#137).
|
Thanks for this, a huge high-quality contribution that I would like progress towards main safely. The macOS-specific reasoning is exactly right (the Process: This is shaping up as a 0.5.0 line, and we still have 0.4.x work to finish on main, so I'm going to spin up a 0.5 integration branch and I'll ask you to retarget this PR onto it. I'll also get CI running on it (it didn't trigger here) and add a macOS job, since right now none of the Must-fix:
Should-fix for "complete" macOS support:
Dependency note: nice work getting onto upstream-master + a single patch. That's a better position than our current samsoir/fuse3 pin. One ask: so we're not depending on a personal fork that could move, I'll mirror 849e84c into our org fork (or we wait for #137 upstream) before this ships. Personal note: I really appreciate this contribution. If we can address the points above, macOS support can be a major component of the 0.5.0 release. I am still traveling until early July, so unable to do any local testing until I return. When I am back home I will start laying the ground for 0.5.x development. |
|
Quick housekeeping note: I edited the PR description to qualify the fuse3 references. The fuse3 bullet's bare The same bare refs still live in two commit messages ( |
Fuse3PassthroughFS::open() hardcoded FOPEN_DIRECT_IO for virtual inodes (with a locally redefined constant), bypassing the platform-aware VIRTUAL_DDS_OPEN_FLAGS that Fuse3OrthoUnionFS already used. On macOS that reintroduces the macFUSE mmap crash: X-Plane memory-maps DDS textures and faults with EXC_BAD_ACCESS when a direct_io file is mapped, since direct_io has no page cache for mmap to use. Lift FOPEN_DIRECT_IO and VIRTUAL_DDS_OPEN_FLAGS into shared.rs as the single source of truth and have both filesystems report it from open(), so the macOS page-cache-not-direct_io rule cannot drift between mounts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mount detection was implemented twice: SpawnedMountHandle::is_mounted (cross-platform, ported in this branch) and manager's check_mount_status (Linux-only, returned Unknown on macOS). So `packages list` always showed "Unknown" mount status on macOS even for correctly mounted packages. Extract one platform-aware is_path_mounted() into system::unmount alongside unmount_fuse / is_stale_fuse_mount (the platform mount-table module), move the pure /proc/mounts and mount(8) parsers + their tests there, and have both SpawnedMountHandle::is_mounted and check_mount_status delegate to it. macOS now reports real Mounted/NotMounted status instead of Unknown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fuse3 instruments every FUSE handler with an INFO span, so at the default `info` level it emits a log line per syscall. On macOS, Finder / Spotlight / the open-save panel constantly stat the mount, and combined with the debug build's FmtSpan::CLOSE + pretty() format this floods the log at ~10 MB/s (observed: a 187 GB xearthlayer.log) and burns ~1.5 cores formatting it. Pin `fuse3=warn` in the default and --debug filter arms (the --profile arms already did this), matching the established pattern. fuse3 goes quiet while xearthlayer's own logs are untouched; RUST_LOG still overrides. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
91c9f01 to
8ae9839
Compare
detect_total_memory() only parsed /proc/meminfo (Linux); on macOS it fell through to the 8GB fallback. The setup wizard sizes the memory cache as RAM/12, so every Mac got memory_size ≈ 682 MB regardless of actual RAM — a quietly undersized config (e.g. a 48 GB Mac should get ~4 GB). Add a macOS arm reading hw.memsize via sysctl(8), with a pure, unit-tested parser. Other platforms keep the 8GB fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
detect_storage_type() was Linux-only (/sys/block rotational); on macOS it warned and returned None, so Auto always fell back to the SSD profile — under- using NVMe (64 vs 256 concurrent I/O) on every Apple Silicon Mac. Add a macOS arm: resolve the cache path to its backing device with df, query diskutil info, and map Solid State + Protocol to a profile (Apple Fabric / PCI-Express / NVMe -> Nvme, other SSD -> Ssd, spinning -> Hdd). Output parsing is split into pure, unit-tested functions; unclassifiable devices return None so the caller keeps the safe SSD default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The diagnostics report's collectors read Linux-only sources (/proc/cpuinfo, /proc/meminfo, /etc/os-release, `ip route`), so on macOS the Operating System, Hardware, and Network sections came out blank — useless for Mac bug reports. Add macOS branches: CPU/memory via sysctl, OS name via sw_vers, default interface via `route -n get default`. Output parsing is a pure, unit-tested function; Linux collectors are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ixes Repoint the dlicudi/fuse3 pin from the reply-resilience rev (849e84c) to feat/macos-mount-options (74abf55), which adds two macOS fixes on top: - comma-join mount options so all `-o` flags actually apply on macFUSE (the builder dropped every option after the first) - log recoverable reply drops at debug, not warn — macOS file browsers (Finder) crawl the mount and produce a harmless ~40/sec stream of "dropping a failed fuse reply" warnings; demoting to debug silences the flood while the session-continues handling is unchanged. Verified on macOS: reply-drop log rate 0/sec after the bump. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@samsoir just a quick note, pushed a few more changes to this branch, but still work in progress. Looking forward to 0.5 branch, I can rebase this macOS port branch to 0.5 once it's available. For the fuse3 fork, I agree best to rely on your fork; my experimental branch is only pinned to my fuse3 fork for purposes of development/testing. Will post again once I've had a chance to pick this up again and review remaining items. |
Summary
Ports XEarthLayer to macOS. The branch is strictly portability: no feature
changes, no behavior changes on Linux. Tested on Apple Silicon (M5 Max,
macOS 26, macFUSE 5.2) against stable X-Plane 12.4.2 with multi-hour
flights and no session losses.
Developed with AI assistance (Claude); all changes flight-tested by me.
Changes
mount verification (
system/unmount.rs)SIGKILL leaves a dead mount; detection must use opendir, not stat,
because macOS serves cached attributes for dead macFUSE mountpoints)
on macOS instead of apt/fusermount)
(~/Library/Preferences/x-plane_install_12.txt)
splitinvocation in the publisher (probe and-b)runfalls back to Custom Scenery auto-detection like the packagescommands already did
submitted upstream as fix(macos): don't kill the session on a transient reply error Sherlock-Holo/fuse3#137 (rebased on upstream
master, which already includes feat(init): add configurable max_background and congestion_threshold to ReplyInit Sherlock-Holo/fuse3#136). Once
fix(macos): don't kill the session on a transient reply error Sherlock-Holo/fuse3#137 merges, one commit
repoints to upstream and also retires the samsoir/fuse3 branch pin
currently on main.
Related Issues
dependency, pending)
Test Plan
make pre-commitpasses (fmt + clippy + tests)xearthlayer run --airport <ICAO>mounts, streamstiles, and unmounts cleanly on Ctrl-C; 2h and 4h+ real X-Plane
flights without crashes
observe "Stale FUSE mount detected" warning and healthy remount
build and tests)
Checklist
cargo clippy