Skip to content

feat(apple-fs): implement AppleDouble + resource-fork support#3556

Merged
oferchen merged 3 commits into
masterfrom
feat/apple-fs-appledouble-resource-fork
May 2, 2026
Merged

feat(apple-fs): implement AppleDouble + resource-fork support#3556
oferchen merged 3 commits into
masterfrom
feat/apple-fs-appledouble-resource-fork

Conversation

@oferchen
Copy link
Copy Markdown
Owner

@oferchen oferchen commented May 2, 2026

Summary

Expand the apple-fs crate with three new pieces of standalone surface
that close the audit follow-ups in docs/audits/apple-fs-roundtrip.md:

  • apple_double module: a self-contained AppleDouble v2 (RFC 1740)
    container parser and encoder. Pure-data, no FFI. Validates magic,
    version, descriptor table, and per-entry offset/length bounds. The
    encoder writes entries in ascending id order so identical logical
    content yields identical bytes.
  • resource_fork module: safe accessors for com.apple.ResourceFork
    and com.apple.FinderInfo. On macOS they delegate to the third-party
    xattr crate (the same safe wrapper used by crates/metadata); on
    every other target the readers return Ok(None) and writers return
    Ok(()).
  • AppleDouble companion-name helpers: is_apple_double_name and
    apple_double_companion translate between foo and ._foo
    lexically, cross-platform.

Public API surface added

apple_fs::APPLE_DOUBLE_PREFIX                       // "._"
apple_fs::is_apple_double_name(&OsStr) -> bool
apple_fs::apple_double_companion(&Path) -> Option<PathBuf>

apple_fs::apple_double::AppleDouble                 // container
apple_fs::apple_double::Entry
apple_fs::apple_double::EntryId                     // RFC 1740 ids
apple_fs::apple_double::APPLE_DOUBLE_MAGIC          // 0x00051607
apple_fs::apple_double::APPLE_SINGLE_MAGIC          // 0x00051600
apple_fs::apple_double::APPLE_DOUBLE_VERSION_2      // 0x00020000
apple_fs::apple_double::HEADER_SIZE
apple_fs::apple_double::ENTRY_DESCRIPTOR_SIZE

apple_fs::RESOURCE_FORK_XATTR                       // "com.apple.ResourceFork"
apple_fs::FINDER_INFO_XATTR                         // "com.apple.FinderInfo"
apple_fs::FINDER_INFO_LEN                           // 32
apple_fs::read_resource_fork(&Path)  -> io::Result<Option<Vec<u8>>>
apple_fs::write_resource_fork(&Path, &[u8]) -> io::Result<()>
apple_fs::remove_resource_fork(&Path) -> io::Result<()>
apple_fs::read_finder_info(&Path)    -> io::Result<Option<[u8; 32]>>
apple_fs::write_finder_info(&Path, &[u8; 32]) -> io::Result<()>
apple_fs::remove_finder_info(&Path)  -> io::Result<()>

Design choice: xattr-backed accessors, not sidecar synthesis

Upstream rsync 3.4.1 has no dedicated resource-fork pathway. macOS
exposes the resource fork and the 32-byte Finder info as ordinary
extended attributes named com.apple.ResourceFork and
com.apple.FinderInfo; upstream reads and writes them via standard
getxattr(2) / setxattr(2) (xattrs.c -> rsync_xal_get /
rsync_xal_set). This PR mirrors that approach for the in-process
accessors and keeps the AppleDouble container format as a standalone
helper for tools, audits, and any future opt-in merge feature.

Rationale: the existing transfer pipeline already round-trips
com.apple.* xattrs symmetrically via metadata::xattr and
protocol::xattr::wire (see docs/audits/apple-fs-roundtrip.md,
"Round-trip path" section). Adding a parallel sidecar path inside
apple-fs would either duplicate that work or change wire behaviour,
neither of which is upstream-compatible. The accessors here are
therefore orthogonal to the live pipeline and intended for tooling.

Unsafe-code encapsulation

The crate keeps #![deny(unsafe_code)]. macOS xattr syscalls reach
the kernel through xattr 1.6, which contains the only unsafe
involved. No #[allow(unsafe_code)] was added to apple-fs, in line
with the workspace unsafe-code policy that routes platform FFI through
pre-vetted wrapper crates or the fast_io crate.

Test plan

  • Cross-platform unit tests for the AppleDouble parser/encoder
    (round-trip, magic / version / descriptor / payload-bound
    validation, per-entry replacement, ordering).
  • Cross-platform unit tests for is_apple_double_name and
    apple_double_companion (round-trip pairing, root and
    bare-prefix edge cases).
  • Cross-platform unit tests for the non-macOS resource-fork stubs
    (readers return Ok(None), writers / removers are no-ops).
  • macOS-gated integration test
    (tests/apple_double_round_trip.rs::macos_resource_fork_pipeline_matches_apple_double_payload)
    that writes payloads through write_resource_fork /
    write_finder_info, reads them back, and verifies the
    AppleDouble container encodes / decodes the same bytes.
  • macOS-gated unit tests for round-trip of resource fork and
    Finder info via the safe accessors, plus rejection of malformed
    Finder-info length.
  • cargo fmt --all clean.
  • Full CI suite (fmt + clippy, nextest stable, Windows, macOS,
    Linux musl) - relies on CI; not run locally per workspace policy.

Follow-ups (not in this PR)

  • Audit follow-up F-2: decide whether to wire an opt-in
    --apple-double-merge analogue once or if upstream picks up the
    relevant patch. The standalone container helpers added here are the
    building block such a feature would need.
  • Audit follow-up F-1: trim the workspace README.md:223 line that
    still mentions "clonefile, FSEvents" for apple-fs (out of scope
    for this functional PR).

Expand the apple-fs crate with three new pieces of standalone surface
that close the audit follow-ups in docs/audits/apple-fs-roundtrip.md:

- apple_double: a self-contained AppleDouble v2 (RFC 1740) container
  parser and encoder. Pure-data, no FFI. Validates magic, version,
  descriptor table, and per-entry offset/length bounds. Encoder writes
  entries in ascending id order so identical logical content yields
  identical bytes.

- resource_fork: safe accessors for com.apple.ResourceFork and
  com.apple.FinderInfo. On macOS they delegate to the third-party
  xattr crate (the same safe wrapper used by crates/metadata); on
  every other target the readers return Ok(None) and writers return
  Ok(()) so cross-platform callers do not abort on absent forks.

- AppleDouble companion-name helpers: is_apple_double_name and
  apple_double_companion translate between foo and ._foo lexically,
  cross-platform.

The crate keeps its #![deny(unsafe_code)] declaration. macOS xattr
syscalls (getxattr(2), setxattr(2), removexattr(2)) are reached through
xattr 1.6, which contains the only unsafe code involved. This honours
the workspace unsafe-code policy that forbids #[allow(unsafe_code)] in
apple-fs and routes platform FFI through pre-vetted wrapper crates.

Tests:
- Cross-platform unit tests for the parser/encoder, name detection,
  and stub behaviour.
- A macOS-gated integration test in tests/apple_double_round_trip.rs
  that writes the synthesised payloads back as native xattrs and
  reads them through the safe accessors. Closes audit follow-up F-3.

The resource-fork pipeline that runs during real transfers continues
to flow through metadata::xattr -> protocol::xattr::wire as before;
this PR adds standalone primitives for inspection tools, audit
harnesses, and any future opt-in AppleDouble merge feature (audit
follow-up F-2). Integration of the new accessors into the transfer
pipeline is intentionally out of scope and tracked separately.

References:
- RFC 1740 section 5 (AppleDouble v2 container format).
- Apple TN1188 (AppleSingle/AppleDouble Formats).
- Upstream rsync 3.4.1 xattrs.c (rsync_xal_get / rsync_xal_set):
  the canonical com.apple.* names cross the wire through the same
  generic xattr path with no Mac-specific code.
@github-actions github-actions Bot added the enhancement New feature or request label May 2, 2026
oferchen added 2 commits May 2, 2026 10:59
PR #3556 added `xattr` and `tempfile` to crates/apple-fs/Cargo.toml but
did not update Cargo.lock. Both dependency entries already existed in
the lockfile via other crates (xattr 1.6.1 used by metadata; tempfile
3.27.0 widely used), so this only adds the apple-fs -> {xattr,tempfile}
edges to the dependency graph.

Fixes the `--locked` failure in `fmt + clippy` and `MSRV 1.88` jobs.
@oferchen oferchen merged commit b1c754c into master May 2, 2026
38 checks passed
@oferchen oferchen deleted the feat/apple-fs-appledouble-resource-fork branch May 2, 2026 13:01
oferchen added a commit that referenced this pull request May 5, 2026
* feat(apple-fs): implement AppleDouble + resource-fork support

Expand the apple-fs crate with three new pieces of standalone surface
that close the audit follow-ups in docs/audits/apple-fs-roundtrip.md:

- apple_double: a self-contained AppleDouble v2 (RFC 1740) container
  parser and encoder. Pure-data, no FFI. Validates magic, version,
  descriptor table, and per-entry offset/length bounds. Encoder writes
  entries in ascending id order so identical logical content yields
  identical bytes.

- resource_fork: safe accessors for com.apple.ResourceFork and
  com.apple.FinderInfo. On macOS they delegate to the third-party
  xattr crate (the same safe wrapper used by crates/metadata); on
  every other target the readers return Ok(None) and writers return
  Ok(()) so cross-platform callers do not abort on absent forks.

- AppleDouble companion-name helpers: is_apple_double_name and
  apple_double_companion translate between foo and ._foo lexically,
  cross-platform.

The crate keeps its #![deny(unsafe_code)] declaration. macOS xattr
syscalls (getxattr(2), setxattr(2), removexattr(2)) are reached through
xattr 1.6, which contains the only unsafe code involved. This honours
the workspace unsafe-code policy that forbids #[allow(unsafe_code)] in
apple-fs and routes platform FFI through pre-vetted wrapper crates.

Tests:
- Cross-platform unit tests for the parser/encoder, name detection,
  and stub behaviour.
- A macOS-gated integration test in tests/apple_double_round_trip.rs
  that writes the synthesised payloads back as native xattrs and
  reads them through the safe accessors. Closes audit follow-up F-3.

The resource-fork pipeline that runs during real transfers continues
to flow through metadata::xattr -> protocol::xattr::wire as before;
this PR adds standalone primitives for inspection tools, audit
harnesses, and any future opt-in AppleDouble merge feature (audit
follow-up F-2). Integration of the new accessors into the transfer
pipeline is intentionally out of scope and tracked separately.

References:
- RFC 1740 section 5 (AppleDouble v2 container format).
- Apple TN1188 (AppleSingle/AppleDouble Formats).
- Upstream rsync 3.4.1 xattrs.c (rsync_xal_get / rsync_xal_set):
  the canonical com.apple.* names cross the wire through the same
  generic xattr path with no Mac-specific code.

* chore(deps): regenerate Cargo.lock for apple-fs xattr/tempfile deps

PR #3556 added `xattr` and `tempfile` to crates/apple-fs/Cargo.toml but
did not update Cargo.lock. Both dependency entries already existed in
the lockfile via other crates (xattr 1.6.1 used by metadata; tempfile
3.27.0 widely used), so this only adds the apple-fs -> {xattr,tempfile}
edges to the dependency graph.

Fixes the `--locked` failure in `fmt + clippy` and `MSRV 1.88` jobs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant