# Filesystem Compatibility & Conformance

Swarmfile mounts as a real drive - `~/Swarmfile` on macOS and Linux, a drive letter on Windows - through FUSE/FUSE-T and WinFsp, not through a synced local folder. The exact location is per-machine and can be configured (the engine's `mount_point`; Windows picks its drive letter at runtime), so read yours from the Desktop App's Status pill rather than hard-coding a path. That distinction matters for exactly the kind of file your native apps care about: does a rename actually rename, does a lock actually stop a second writer, does opening a 200 GB file touch the whole thing or just the bytes you read.

This page states plainly what works today, per platform, and what's a known gap.

## What's verified

Most rows below are exercised by automated write-semantics tests that drive the *mounted filesystem* the way an application does - not the internal storage code directly - on a live mount per platform. A few rows (adapter capabilities and platform-specific behavior) are covered by targeted tests instead; the Notes column says when.

| Capability | macOS | Linux | Windows | Notes |
|---|---|---|---|---|
| Create / read / write | Verified | Verified | Verified | |
| Delete (file & directory) | Verified | Verified | Verified | On Windows, verified via Win32 `RemoveDirectory`, `cmd rmdir /s` and .NET's recursive `Directory.Delete`, including on empty folders. PowerShell's `Remove-Item -Recurse` fails through the mount (known limitation - use one of the above instead) |
| Rename - same directory | Verified | Verified | Verified | |
| Rename - cross-directory (move) | Asserted | Asserted | Verified | Entry identity, history, comments, and locks move with the file - this is not a copy-then-delete. Windows verified on a live mount; macOS/Linux covered by the conformance suite |
| Exclusive create (`O_EXCL` / Win32 `CREATE_NEW`) | Verified | Verified | Verified | A create that races an existing name is refused (`EEXIST` / `STATUS_OBJECT_NAME_COLLISION`), enforced at our layer - so it holds even offline, not just when the OS routes the open |
| Overwrite / truncate an existing file | Verified | Verified | Verified | Content-checksummed, not just size-checked |
| Directory listing at scale | Verified | Verified | Verified | Wildcard/exact-name filters are verified on Windows only - POSIX shells and apps glob client-side over a plain readdir, so there's no equivalent operation to assert there |
| Case handling | Case-preserving | Case-preserving | Case-insensitive | Matches each platform's native convention. Names also resolve across Unicode forms on every platform: `café.txt` created as NFC opens when asked for the NFD spelling, an ASCII-case-only difference resolves, and the Turkish dotted/dotless I family (`İ`/`ı` vs `i`) is treated as one name - the store keeps the spelling you created with. Verified live on Windows in both request directions and unit-tested cross-platform; the WinFsp boundary reports the requested spelling when the stored one differs by more than case, which is what lets the open succeed |
| Free-space reporting (`statfs`) | Verified | Verified | Verified | |
| `fsync` durability | Verified | Verified | Verified | A write is not reported durable until it actually is; on Windows this is the `FlushFileBuffers` path |
| Extended attributes (get/set/list/remove) | Local-only, via macOS's `._` files (see below) | Implemented | Not implemented | Windows EA specifically - see below. Not the same thing as alternate data streams, the row below. On Linux the adapter implements all four operations; nothing on a Linux mount creates them the way macOS does, so end-to-end use isn't exercised |
| Alternate data streams - create/read/write/enumerate/delete | N/A | N/A | Verified, incl. sync between two clients | Windows-only concept (`file.ext:streamname`). A stream also survives a copy through the mount rather than being silently dropped, the way most non-native sync tools drop it. The `Zone.Identifier` stream (Windows' Mark of the Web) stays on the machine that wrote it and never syncs - see [Sync Exclusions](https://swarmfile.com/docs/guides/sync-exclusions#windows-mark-of-the-web-never-syncs). Every other stream's content travels the same hub upload/commit pipeline as file content. Verified on real Windows mounts: these operations, and sync between two clients through the hub. A stream written or changed on one client reads back identical on the other, a stream deletion reaches the other client, and a `Zone.Identifier` never leaves the client that wrote it |
| Alternate data streams - rename | N/A | N/A | Not supported (see below) | |
| Symlinks - create | Verified | Verified | Not supported (see below) | |
| Symlinks - read / follow / list / delete | Verified | Verified | Read, follow & list | On Windows, `readlink` (reparse-point resolution), following a link to its target, and listing it as a reparse point are all verified; delete-through-the-mount isn't verified on Windows |
| Executable bit (`chmod +x` / `-x`) | Verified | Verified | Preserved (no executable bit) | Any `x` bit sets the project's per-file executable flag; the drive reports `0755` / `0644`. A create with mode `0755` (including `git checkout` of a `100755` file) comes out executable. Windows has no mode bit: edits and safe-saves there keep the flag, and `swarmfile chmod` sets it from any platform |
| Byte-range locking (API) | Verified | Verified | Verified | Hub-enforced acquire/release/overlap-detection, driven over the engine control socket - the same primitive a native worksharing plugin (e.g. for Revit) would call. Release is always an explicit unlock call over that socket; on Windows in particular, a lock is not auto-released when the OS closes the file handle, so a caller must unlock explicitly. Revit/SolidWorks worksharing itself needs no plugin on Windows - Swarmfile honors the native OS file lock those apps already take and turns it into a hub-enforced claim across machines; on macOS/Linux only cooperative locks apply. A deployment that needs an open refused whenever the hub can't be reached can enable that (see [Environment Variables](https://swarmfile.com/docs/reference/environment-variables)). This byte-range API is the lower-level primitive for finer, element-level integrations beyond that. See [Swarmfile vs. LucidLink](https://swarmfile.com/compare/lucidlink) for how the underlying lock guarantee compares |
| Folder/file ACL enforcement | Verified | Verified | Verified, incl. Explorer-visible permissions | Enforced hub-side for every client; only Windows additionally enforces and presents ACLs at the mount itself (Explorer-visible DACL). On macOS and Linux, Finder and `ls` show the mode bits from `getattr`, not the project's ACLs |

## Declared gaps

Each of these is a documented limitation:

- **Hard links.** Not supported on any platform. Our metadata model is one entry = one path = one content id; a hard link needs a content-identity indirection the schema doesn't have.
- **Symlink creation on Windows.** Windows mounts read and follow symlinks correctly but refuse to create one. Creating a Windows symlink as an unprivileged process requires Developer Mode or a specific privilege - support would work for some users and silently fail for others on the same drive, so we refuse consistently instead.
- **Renaming an alternate data stream itself.** Renaming the base file that carries a stream works normally, and the stream travels with it. Renaming just the stream - `file.txt:secret` to `file.txt:hidden` - is out of scope and refused outright, not silently mishandled: we won't rename the base file when you only asked to rename one of its streams, and we won't leave the stream qualifier in a request that no path would ever resolve to.
- **Extended attributes on Windows.** Not implemented - a deliberate scope decision, separate from alternate data streams (see the verified-capabilities table above, which streams *are* supported).
- **A save the hub refuses reaches the app as `EIO`.** When a save is refused (a file held elsewhere, a plan or policy refusal, a root-create restriction, quarantine), the application only sees a generic I/O error; the reason is written under **Why did my save fail?** in the tray and printed by `swarmfile sync stuck`. The errno is deliberately generic, so read the reason there rather than from the app's error dialog.
- **`fallocate` / `copy_file_range` / `lseek(SEEK_HOLE)`.** Not implemented on the POSIX adapters.
- **`fcntl()` byte-range advisory locks are kernel-local only.** They don't coordinate across machines - that's a different mechanism from the hub-enforced byte-range locking API above. An app that relies on plain POSIX advisory locks for multi-writer coordination (rather than calling into the lock API) gets locking that looks like it works but only protects against other processes on the same machine.
- **Windows: a memory-mapped view can outlive the handle for reads, not for writes.** Reading through a memory-mapped view works, including when an application closes the file handle before reading the map - `git` does exactly that with `.git/config`, which is why `git init`/`commit`/`status` work on the mount. Writing through a mapping is only served while a file handle is open: an application that maps a file **read-write**, closes the handle, and then writes through the view can have those write-backs fail. Keep the handle open until the mapping is unmapped, or write through the handle. Read-only mappings (the common case, git included) are unaffected.
- **A file isn't readable the instant it's written - for about a second, on Windows.** The engine itself publishes the new content *synchronously* as the flush completes (the local metadata row, size included, is updated before close returns), so a script that opens the file through a fresh handle gets the new bytes on macOS and Linux. Windows can still report the old attributes (0 bytes for a new file, the previous size for an overwrite) for up to its **1-second Cache Manager revalidation timeout**, because the kernel caches file info between revalidations. It matters for any workflow that writes a file and immediately reads it back - a save-then-verify step, a render job that re-opens its own output, or an app that reloads the file right after saving. A person clicking around is usually slower than the one-second window, but not always: reopening a just-saved file within that second can still see the old size. If you need certainty, wait on the engine rather than the clock: `swarmfile uploads --wait` blocks until the upload queue is empty, and `swarmfile status` splits the rest - `pending uploads` (blocks still going up), `commits pending` (bytes up, commit not landed; a plan-held commit shows under `quota-held commits`). (A plain `sync` forces pending writes to flush locally but does not wait for the hub upload and commit, so use the counters, not `sync`, when that distinction matters.)

- **`access()` / `faccessat()` aren't enforced locally.** Permission is decided hub-side by the ACL system, not by local file mode bits, so a pre-flight `access()` check reports success even for a write the hub will later refuse.

- **A read can WAIT, for a file that is still uploading.**
A file that has never finished uploading can be opened - it reports the
size it is going to be, and reads work. A read into a range that has not arrived
yet waits for it, bounded at 15 s by default, and returns a *retryable* error -
`EAGAIN` on macOS and Linux, `STATUS_IO_TIMEOUT` on Windows - rather than an I/O
error if the wait times out, because an application that sees an I/O error part
way through a file usually decides the file is damaged and discards your
document. A stall is the correct answer for a player; a corrupt-file error is
not. It only ever applies to a file with no committed version yet, so nothing
you are already reading changes behavior.

- **macOS: `ditto` works.** Plain `cp`/`cp -R`, `rsync`, Finder, and the installers and build scripts that reach for `ditto` all work.
- **macOS extended attributes are local-only.** The drive is mounted without NFS named attributes, so macOS keeps Finder tags, comments, and the like the way it does on any such volume: in an AppleDouble `._name` file beside the original. Swarmfile keeps those `._` files, and `.DS_Store`, on the machine that wrote them and **never syncs** them (see [Sync Exclusions](https://swarmfile.com/docs/guides/sync-exclusions#macos-metadata-files-never-sync)) - `cp -p`, `ditto`, and `xattr` work with them on that machine, but a teammate (or that machine after a fresh install) won't see them. Windows alternate data streams are the exception and do travel (except the `Zone.Identifier` Mark of the Web, which stays local); POSIX xattrs crossing machines would be a hub-side format addition, not a mount fix.

## How we verify this

Each platform has its own conformance suite driving the live mount, and a shared manifest keeps the three platforms' coverage aligned - a capability missing on one platform is caught rather than left for you to find. Known gaps are listed above.

## How this compares to typical cloud storage clients

Most general-purpose cloud storage clients - the kind built primarily for syncing documents and photos - are sync engines wearing a filesystem's clothing: a background process reconciling a local folder against the cloud, not a filesystem driver with defined semantics for the operations above. That's a fine model for a folder of PDFs. It tends to fall over for exactly the workloads Swarmfile targets: whole-file download before an app can open anything, no cross-process byte-range locking, and "two people edited it" resolved by keeping both files rather than by a lock.

This isn't a hypothetical concern - it shows up in vendor documentation for the professional tools this product is built for. Autodesk's own support article on [using cloud-synced services with Revit files](https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Revit-Using-Revit-files-on-Dropbox-Box-or-OneDrive.html) states that file-based worksharing isn't supported on common cloud-sync storage, and names corruption of the central model and lost work as the consequence. Esri's knowledge base similarly [documents common problems running ArcGIS Pro against cloud storage services](https://support.esri.com/en-us/knowledge-base/problem-arcgis-pro-and-cloud-storage-services-000025605). If you're evaluating any filesystem - including this one - for this kind of workload, the questions worth asking are the ones this page answers: is locking enforced per byte range or just per whole file, is it enforced on every platform your team actually uses, and is any of it independently verified rather than asserted.
