fs-metadata uses a two-layer detection strategy across all platforms:
MountPoint gets isSystemVolume set by native code.true — they only upgrade
false to true.The isSystemVolume field is exposed on both MountPoint and VolumeMetadata.
By default, system volumes are excluded from getAllVolumeMetadata() results
on Linux and macOS (includeSystemVolumes: false), but included on Windows
because C: is both a system drive and the primary user storage location.
On macOS 10.15 (Catalina) and later, the boot volume is split into multiple APFS volumes within a single container:
| Volume | Mount Point | APFS Role | Description |
|---|---|---|---|
| Macintosh HD | / (snapshot) |
System | Sealed, read-only OS snapshot |
| Macintosh HD - Data | /System/Volumes/Data |
Data | User data (firmlinked to /Users, /Applications, etc.) |
| VM | /System/Volumes/VM |
VM | Virtual memory swap |
| Preboot | /System/Volumes/Preboot |
Preboot | Boot-time resources |
| Recovery | /System/Volumes/Recovery |
Recovery | Recovery OS |
| Update | /System/Volumes/Update |
Update | OS update staging |
| Hardware | /System/Volumes/Hardware |
Hardware | Hardware-specific data |
| xarts | /System/Volumes/xarts |
xART | Secure token storage |
| iSCPreboot | /System/Volumes/iSCPreboot |
Prelogin | Pre-login resources |
The volume mounted at / is an APFS sealed system snapshot — a
cryptographically signed, read-only image of the OS. Its UUID changes on
every macOS update. This makes it unsuitable for persistent identification
(e.g., licensing fingerprints, asset URIs).
The Data volume at /System/Volumes/Data has a stable UUID and contains
all user data. Users access it transparently through APFS firmlinks (/Users,
/Applications, /Library, etc.).
Detection uses a combined formula:
isSystemVolume = MNT_SNAPSHOT || (MNT_DONTBROWSE && hasApfsRole && role != "Data")
This is implemented in ClassifyMacVolume() in src/darwin/system_volume.h.
Each APFS volume has a role stored in its superblock (an unsigned 16-bit integer). We read this via:
DADiskCreateFromBSDName() to get a DiskArbitration disk refDADiskCopyIOMedia() to get the IOKit IOMedia serviceIORegistryEntryCreateCFProperty(media, "Role") to read the role arrayFor APFS snapshots (e.g., / is disk3s7s1, a snapshot of disk3s7), the
snapshot's IOMedia entry has no Role property — we walk one parent up in
the IOService plane to find the parent volume's role.
The role string is exposed as volumeRole in both MountPoint and
VolumeMetadata (e.g., "System", "Data", "VM").
System volume classification combines two signals:
MNT_SNAPSHOT alone marks a volume as system. This catches sealed APFS
snapshots (/ and Recovery).MNT_DONTBROWSE combined with a non-"Data" APFS role marks a volume
as system. This catches all infrastructure volumes (VM, Preboot, Update,
Hardware, xART, etc.) while correctly excluding the Data volume.Why this works: MNT_DONTBROWSE means "hidden from Finder" and is set on
all /System/Volumes/* infrastructure mounts. The only false positive would
be /System/Volumes/Data, which has MNT_DONTBROWSE but a "Data" role —
so the role check excludes it.
Why not a role whitelist? The previous approach maintained a whitelist of
13 system role strings. The flags+exclusion approach is simpler and
future-proof: if Apple adds new infrastructure roles with MNT_DONTBROWSE,
they're auto-detected without code changes.
Non-APFS MNT_DONTBROWSE mounts (e.g., devfs at /dev, or a
hypothetical NFS mount with nobrowse) have no APFS role, so the
MNT_DONTBROWSE branch doesn't fire. These fall through to TypeScript
heuristics (Layer 2).
MNT_SNAPSHOT onlyIf a DiskArbitration session can't be created, we fall back to checking only
MNT_SNAPSHOT in the statfs f_flags. This catches the sealed system
snapshots (/ and /System/Volumes/Recovery) but misses the other
infrastructure volumes (VM, Preboot, etc.).
| Mount Point | APFS Role | MNT_SNAPSHOT | MNT_DONTBROWSE | isSystemVolume |
|---|---|---|---|---|
/ |
System (via parent) | yes | no | yes |
/System/Volumes/Data |
Data | no | yes | no |
/System/Volumes/VM |
VM | no | yes | yes |
/System/Volumes/Preboot |
Preboot | no | yes | yes |
/System/Volumes/Recovery |
Recovery | yes | no | yes |
/System/Volumes/Update |
Update | no | yes | yes |
/System/Volumes/xarts |
xART | no | yes | yes |
/System/Volumes/iSCPreboot |
Prelogin | no | yes | yes |
/System/Volumes/Hardware |
Hardware | no | yes | yes |
/dev |
(no IOMedia) | no | yes | yes (fstype) |
/Volumes/sandisk-extreme |
(no role) | no | no | no |
Linux has no unified "system volume" concept. Instead, the kernel exposes dozens of pseudo-filesystems for kernel interfaces, and distributions mount various infrastructure paths at boot. Container runtimes add additional overlay and bind mounts.
There is no native C++ system volume detection on Linux. All classification happens in TypeScript (Layer 2) using filesystem type matching and path pattern globs.
Unlike macOS (which has APFS roles and MNT_SNAPSHOT/MNT_DONTBROWSE flags)
or Windows (which has CSIDL_WINDOWS and volume capability flags), Linux has
no kernel API that directly identifies "this is a system volume." The closest
signal is the filesystem type from /proc/self/mounts, which is already
available in TypeScript without a native call.
The SystemFsTypesDefault list in src/options.ts identifies pseudo-filesystems
that don't represent real storage:
| Category | Filesystem Types |
|---|---|
| Process/kernel interfaces | proc, sysfs, debugfs, tracefs, configfs, securityfs, bpf |
| Device pseudo-filesystems | devpts, devtmpfs |
| Memory/temporary | tmpfs, ramfs, rootfs, hugetlbfs |
| Cgroups | cgroup, cgroup2 |
| Boot/firmware | efivarfs, pstore, binfmt_misc |
| Automount | autofs, fusectl |
| Container/sandbox | fuse.lxcfs, fuse.portal, fuse.snapfuse, squashfs |
| Kernel internal | nsfs, mqueue, rpc_pipefs, none |
The SystemPathPatternsDefault list in src/options.ts catches system mount
points by path glob:
| Category | Patterns |
|---|---|
| Core system | /boot, /boot/efi, /dev, /dev/**, /proc/**, /sys/** |
| Runtime | /run, /run/lock, /run/credentials/** |
| Temporary | /tmp, /var/tmp |
| Container runtimes | /run/docker/**, /var/lib/docker/**, /run/containerd/**, /var/lib/containerd/**, /run/containers/**, /var/lib/containers/**, /var/lib/kubelet/** |
| Linux containers | /var/lib/lxc/**, /var/lib/lxd/** |
| Snap/Flatpak | /snap/**, /run/snapd/**, /run/flatpak/**, /run/user/*/doc, /run/user/*/gvfs |
| WSL infrastructure | /mnt/wslg/distro, /mnt/wslg/doc, /usr/lib/wsl/drivers |
| Snapshot dirs | **/#snapshot |
| Mount Point | fstype | Detected By | isSystemVolume |
|---|---|---|---|
/ |
ext4 |
(none) | no |
/boot |
ext4 |
path | yes |
/boot/efi |
vfat |
path | yes |
/dev |
devtmpfs |
fstype + path | yes |
/proc |
proc |
fstype + path | yes |
/sys |
sysfs |
fstype + path | yes |
/run |
tmpfs |
fstype + path | yes |
/tmp |
tmpfs |
fstype + path | yes |
/home |
ext4 |
(none) | no |
/var/lib/docker/overlay2/... |
overlay |
path | yes |
/mnt/data |
ext4 |
(none) | no |
Both lists are configurable via Options.systemFsTypes and
Options.systemPathPatterns, allowing callers to add or replace detection
rules for specialized environments.
Windows has a simpler model: drives are identified by letter (C:\, D:,
etc.), and one drive is the "system drive" containing the Windows
installation. Unlike macOS's split-volume architecture, the system drive also
holds user data (C:\Users).
IsSystemVolume() in src/windows/system_volume.h uses two checks:
SHGetFolderPathW(CSIDL_WINDOWS) — retrieves the Windows system
folder path (e.g., C:\Windows), extracts the drive letter, and compares
it against the volume being tested. This is the primary detection method.
Volume capability flags — calls GetVolumeInformationW() and checks
for FILE_SUPPORTS_SYSTEM_PATHS (0x00100000) and
FILE_SUPPORTS_SYSTEM_FILES (0x00200000). These are modern (Windows 10+)
volume flags that indicate system volume capabilities.
The TypeScript layer (src/system_volume.ts) provides a redundant check using
process.env.SystemDrive (typically "C:"), normalized to "C:\" for
comparison.
includeSystemVolumes defaults to true on WindowsOn macOS and Linux, system volumes (pseudo-filesystems, sealed snapshots) are
genuinely uninteresting to most callers. On Windows, C:\ is both the system
drive and the primary user storage location — excluding it would hide the
most important volume. So IncludeSystemVolumesDefault = true on Windows
(see src/options.ts).
| Mount Point | Detection Method | isSystemVolume |
|---|---|---|
C:\ |
CSIDL_WINDOWS + SystemDrive | yes |
D: |
(none) | no |
E:\ |
(none) | no |
\\server\share |
(none) | no |
The assignSystemVolume() function in src/system_volume.ts runs after native
enumeration and applies the shared heuristic layer:
process.env.SystemDrivefstype against SystemFsTypesDefault (configurable)mountPoint against SystemPathPatternsDefault glob patterns
(configurable)Native isSystemVolume: true is never downgraded — only upgraded from
false to true.
src/darwin/system_volume.h — ClassifyMacVolume(): macOS flags + APFS role detectionsrc/darwin/raii_utils.h — RAII wrappers for DA/CF/IOKit resourcessrc/windows/system_volume.h — IsSystemVolume(): Windows CSIDL + volume flagssrc/options.ts — SystemFsTypesDefault, SystemPathPatternsDefault, IncludeSystemVolumesDefaultsrc/system_volume.ts — isSystemVolume(), assignSystemVolume(): TypeScript heuristic layersrc/types/mount_point.ts — MountPoint.volumeRole / isSystemVolume / isReadOnly fields