Skip to content

feat(metadata): implement Windows xattrs via NTFS Alternate Data Streams (#1867)#3536

Merged
oferchen merged 4 commits into
masterfrom
feat/windows-xattrs-1867
May 2, 2026
Merged

feat(metadata): implement Windows xattrs via NTFS Alternate Data Streams (#1867)#3536
oferchen merged 4 commits into
masterfrom
feat/windows-xattrs-1867

Conversation

@oferchen
Copy link
Copy Markdown
Owner

@oferchen oferchen commented May 1, 2026

Summary

  • Adds a Windows backend for the cross-platform xattr layer that maps each named extended attribute onto an NTFS Alternate Data Stream using the path:streamname:$DATA syntax.
  • Wires FindFirstStreamW/FindNextStreamW for listing, CreateFileW + ReadFile/WriteFile for read/write, and DeleteFileW for removal. Stream-name parsing strips the : prefix and :$DATA suffix so wire-format names match the existing Unix xattr names.
  • Refactors crates/metadata/src/xattr.rs to dispatch through a platform backend module (xattr_unix or xattr_windows) while keeping the public API (read_xattrs_for_wire, sync_xattrs, apply_xattrs_from_list) and Linux namespace policy unchanged.
  • Contains unsafe FFI to xattr_windows.rs (#![allow(unsafe_code)]) and uses RAII wrappers around FindClose and CloseHandle for handle cleanup. Matches the convention used by acl_windows, ownership, and id_lookup in the same crate.

Refs #1867

Test plan

  • CI: fmt + clippy
  • CI: nextest (stable, Linux musl, macOS, Windows)
  • Round-trip write/read/list/remove on an NTFS volume (covered by xattr_windows::tests, gracefully skips on FAT32)
  • Stream-name parser unit tests (prefix/suffix stripping, Unicode names, default ::$DATA skip, non-data-stream rejection)
  • stream_path_wide input validation (empty, embedded :, non-UTF-8)
  • xattr.rs cross-platform tests on Linux (existing tests still green via xattr_unix backend)

@github-actions github-actions Bot added the enhancement New feature or request label May 1, 2026
@oferchen oferchen force-pushed the feat/windows-xattrs-1867 branch 2 times, most recently from af09a8d to c8b5bcf Compare May 1, 2026 22:20
oferchen added 3 commits May 2, 2026 04:27
…ams (#1867)

Add a Windows backend for the cross-platform xattr layer that maps each
named extended attribute onto an NTFS Alternate Data Stream using the
`path:streamname:$DATA` syntax. The four POSIX primitives are now backed
by `FindFirstStreamW`/`FindNextStreamW` for listing, `CreateFileW` +
`ReadFile`/`WriteFile` for read and write, and `DeleteFileW` for
removal. Stream-name parsing strips the `:` prefix and `:$DATA` suffix
returned by NTFS so the wire-format names match the Unix xattr names
exposed by the existing `xattr` crate backend.

Refactor the per-attribute primitives in `crates/metadata/src/xattr.rs`
to take raw `&[u8]` names and dispatch to a platform backend module
(`xattr_unix` or `xattr_windows`). The Unix backend continues to wrap
the `xattr` crate; the new Windows backend lives in a separate file
under the same `feature = "xattr"` gate so the cross-platform layer
remains backend-agnostic. The public API (`read_xattrs_for_wire`,
`sync_xattrs`, `apply_xattrs_from_list`) is unchanged and the upstream
rsync namespace-filtering policy still applies on Linux.

Unsafe FFI is contained in `xattr_windows.rs` with `#![allow(unsafe_code)]`
matching the existing convention for platform wrappers in the metadata
crate (alongside `acl_windows`, `ownership`, `id_lookup`). Resource
management uses RAII wrappers around `FindClose` and `CloseHandle` so
handles are released on every error path.

Tests cover the stream-name parser, input validation in
`stream_path_wide`, and a write/read/list/remove round-trip that
gracefully skips when the test volume does not support ADS (e.g.
FAT32 runners).

Refs #1867
The Windows ADS xattr backend used `windows::Win32::Storage::FileSystem::{
ReadFile, WriteFile}`, which are gated behind the `Win32_System_IO`
feature in `windows = "0.62"`. Cross-compiling to
`x86_64-pc-windows-gnu` therefore failed with `no `ReadFile` /
`WriteFile` in `Win32::Storage::FileSystem``. `FindFirstStreamW`'s
`dwflags` argument also takes `Option<u32>`, but the call site passed a
bare `0`.

Replace the raw `ReadFile`/`WriteFile` calls with `std::fs::File`
constructed from the Win32 `HANDLE` via `FromRawHandle`, so the read
and write paths use stdlib I/O instead of FFI bindings that depend on
an additional `windows` crate feature. The `File` takes ownership of
the handle and closes it on drop, replacing the now-unused
`OwnedHandle` wrapper. Wrap the reserved-must-be-zero `dwflags`
argument in `Some(0)` to match the binding signature.

No new Cargo features and no changes outside `crates/metadata/`.
@oferchen oferchen force-pushed the feat/windows-xattrs-1867 branch from c8b5bcf to 72a0c68 Compare May 2, 2026 01:27
@oferchen oferchen merged commit 2b8d221 into master May 2, 2026
37 checks passed
@oferchen oferchen deleted the feat/windows-xattrs-1867 branch May 2, 2026 03:50
oferchen added a commit that referenced this pull request May 5, 2026
…ams (#1867) (#3536)

* feat(metadata): implement Windows xattrs via NTFS Alternate Data Streams (#1867)

Add a Windows backend for the cross-platform xattr layer that maps each
named extended attribute onto an NTFS Alternate Data Stream using the
`path:streamname:$DATA` syntax. The four POSIX primitives are now backed
by `FindFirstStreamW`/`FindNextStreamW` for listing, `CreateFileW` +
`ReadFile`/`WriteFile` for read and write, and `DeleteFileW` for
removal. Stream-name parsing strips the `:` prefix and `:$DATA` suffix
returned by NTFS so the wire-format names match the Unix xattr names
exposed by the existing `xattr` crate backend.

Refactor the per-attribute primitives in `crates/metadata/src/xattr.rs`
to take raw `&[u8]` names and dispatch to a platform backend module
(`xattr_unix` or `xattr_windows`). The Unix backend continues to wrap
the `xattr` crate; the new Windows backend lives in a separate file
under the same `feature = "xattr"` gate so the cross-platform layer
remains backend-agnostic. The public API (`read_xattrs_for_wire`,
`sync_xattrs`, `apply_xattrs_from_list`) is unchanged and the upstream
rsync namespace-filtering policy still applies on Linux.

Unsafe FFI is contained in `xattr_windows.rs` with `#![allow(unsafe_code)]`
matching the existing convention for platform wrappers in the metadata
crate (alongside `acl_windows`, `ownership`, `id_lookup`). Resource
management uses RAII wrappers around `FindClose` and `CloseHandle` so
handles are released on every error path.

Tests cover the stream-name parser, input validation in
`stream_path_wide`, and a write/read/list/remove round-trip that
gracefully skips when the test volume does not support ADS (e.g.
FAT32 runners).

Refs #1867

* style(metadata): cargo fmt

* fix(metadata): make ADS xattr impl cross-compile for windows-gnu

The Windows ADS xattr backend used `windows::Win32::Storage::FileSystem::{
ReadFile, WriteFile}`, which are gated behind the `Win32_System_IO`
feature in `windows = "0.62"`. Cross-compiling to
`x86_64-pc-windows-gnu` therefore failed with `no `ReadFile` /
`WriteFile` in `Win32::Storage::FileSystem``. `FindFirstStreamW`'s
`dwflags` argument also takes `Option<u32>`, but the call site passed a
bare `0`.

Replace the raw `ReadFile`/`WriteFile` calls with `std::fs::File`
constructed from the Win32 `HANDLE` via `FromRawHandle`, so the read
and write paths use stdlib I/O instead of FFI bindings that depend on
an additional `windows` crate feature. The `File` takes ownership of
the handle and closes it on drop, replacing the now-unused
`OwnedHandle` wrapper. Wrap the reserved-must-be-zero `dwflags`
argument in `Some(0)` to match the binding signature.

No new Cargo features and no changes outside `crates/metadata/`.

* style(metadata): cargo fmt xattr_windows imports
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