Skip to content

Add runtime detection for macFUSE 4.x/5.x rename protocol#453

Open
zah wants to merge 1 commit intocberner:masterfrom
blocksense-network:agent-harbor
Open

Add runtime detection for macFUSE 4.x/5.x rename protocol#453
zah wants to merge 1 commit intocberner:masterfrom
blocksense-network:agent-harbor

Conversation

@zah
Copy link

@zah zah commented Jan 14, 2026

Summary

This patch adds support for macFUSE's extended rename format and the RENAME_SWAP/RENAME_EXCL capabilities, enabling atomic rename operations on macOS.

Problem

macFUSE supports extended rename operations (renamex_np syscall with RENAME_SWAP and RENAME_EXCL flags) through an extended fuse_rename_in structure. However, there's an ABI incompatibility between macFUSE versions:

  • macFUSE 4.x: Had a bug where it sent the extended 16-byte format unconditionally, regardless of whether RENAME_SWAP/RENAME_EXCL capabilities were negotiated during FUSE_INIT.

  • macFUSE 5.x: Fixed this behavior - only sends the extended format when capabilities ARE granted; otherwise sends the standard 8-byte format.

This caused fuser to break on macFUSE 5.x when parsing rename requests, as the library wasn't handling both format variations.

References

Solution

This patch implements a two-pronged approach:

1. Capability Negotiation (lib.rs)

Request FUSE_RENAME_SWAP and FUSE_RENAME_EXCL capabilities during FUSE_INIT:

#[cfg(target_os = "macos")]
const INIT_FLAGS: u64 = FUSE_ASYNC_READ
    | FUSE_CASE_INSENSITIVE
    | FUSE_VOL_RENAME
    | FUSE_XTIMES
    | FUSE_RENAME_SWAP
    | FUSE_RENAME_EXCL;

This tells macFUSE 5.x that we want extended rename support, so it will grant the capabilities and send the extended format.

2. Runtime Format Detection (request.rs)

Parse FUSE_RENAME requests with runtime detection of the format:

Short format (8 bytes):
  newdir: u64
  <filename bytes follow immediately>

Extended format (16 bytes):
  newdir: u64
  flags: u32
  padding: u32
  <filename bytes follow>

The detection heuristic is based on the observation that filenames cannot start with null bytes or ASCII control characters (< 32). By inspecting the first byte after newdir:

  • If < 32: Extended format (flags field contains a small number or zero)
  • If >= 32: Short format (filename starts with a printable character)

This handles:

  1. macFUSE 5.x with capabilities granted (extended format)
  2. macFUSE 5.x with capabilities refused (short format)
  3. macFUSE 4.x bug behavior (extended format unconditionally)

Files Changed

src/lib.rs

  • Added FUSE_RENAME_SWAP and FUSE_RENAME_EXCL to macOS INIT_FLAGS
  • Added documentation explaining the macFUSE version differences

src/ll/fuse_abi.rs

  • Added FUSE_RENAME_SWAP (bit 25) and FUSE_RENAME_EXCL (bit 26) capability constants for macOS
  • Updated fuse_rename_in documentation to explain the variable format

src/ll/request.rs

  • Added flags field to the Rename struct
  • Implemented runtime format detection in FUSE_RENAME parsing
  • Added comprehensive documentation of the detection algorithm

src/ll/argument.rs

  • Added skip_bytes() method to ArgumentIterator for skipping optional fields
  • Added peek_byte() method for non-consuming byte inspection

src/request.rs

  • Updated dispatch to pass x.flags() to Filesystem::rename() instead of hardcoded 0

  macFUSE 5.x changed the FUSE_RENAME protocol format:
  - macFUSE 4.x sent extended 16-byte format unconditionally (bug)
  - macFUSE 5.x only sends extended format when capabilities are granted

  This patch adds:
  - FUSE_RENAME_SWAP and FUSE_RENAME_EXCL capability negotiation during FUSE_INIT
  - Runtime format detection using filename byte inspection heuristic
  - Zero-copy skip_bytes() and peek_byte() helpers for argument parsing

  Fixes rename operations that were broken after upgrading to macFUSE 5.x.
  See macfuse/macfuse#839 for context.
// 1. The kernel may refuse to grant the capabilities
// 2. We need backward compatibility with macFUSE 4.x behavior
//
// Detection heuristic:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filenames can definitely contain ASCII control sequences. Finder won't allow it, but the terminal and system calls will. The only actual restriction is that filenames cannot contain NUL or /.

// - flags: u32 (4 bytes)
// - padding: u32 (4 bytes)
//
// The problem:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this detection mechanism? macFUSE 4.x always uses extended. macFUSE 5.x will always use extended since we request the capabilities. I guess there is a theoretical chance the kernel refuses the capabilities, but I don't know if that's possible in practice.

@cacay
Copy link

cacay commented Mar 19, 2026

@zah I'm looking to support the renamex_np callback on macOS. From what I can tell, fuser doesn't pipe these flags to Filesystem::rename. Any change you'll revisit this PR?

There is now a Linux-only RenameFlags struct. Maybe that could be extended to macOS given this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants