@photostructure/fs-metadata 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/CLAUDE.md +13 -0
  3. package/claude.sh +29 -5
  4. package/dist/index.cjs +86 -26
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +39 -3
  7. package/dist/index.d.mts +39 -3
  8. package/dist/index.d.ts +39 -3
  9. package/dist/index.mjs +86 -27
  10. package/dist/index.mjs.map +1 -1
  11. package/doc/SECURITY_AUDIT_2025.md +1 -1
  12. package/doc/SECURITY_AUDIT_2026.md +361 -0
  13. package/doc/TPP-GUIDE.md +144 -0
  14. package/doc/system-volume-detection.md +268 -0
  15. package/package.json +11 -11
  16. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  17. package/prebuilds/darwin-x64/@photostructure+fs-metadata.glibc.node +0 -0
  18. package/prebuilds/linux-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  19. package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
  20. package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
  21. package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
  22. package/prebuilds/win32-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  23. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
  24. package/src/common/volume_metadata.h +10 -3
  25. package/src/common/volume_mount_points.h +7 -1
  26. package/src/darwin/da_mutex.h +23 -0
  27. package/src/darwin/raii_utils.h +39 -0
  28. package/src/darwin/system_volume.h +156 -0
  29. package/src/darwin/volume_metadata.cpp +18 -2
  30. package/src/darwin/volume_mount_points.cpp +46 -14
  31. package/src/index.ts +22 -0
  32. package/src/linux/mtab.ts +6 -0
  33. package/src/options.ts +7 -17
  34. package/src/path.ts +16 -1
  35. package/src/system_volume.ts +5 -9
  36. package/src/test-utils/assert.ts +4 -0
  37. package/src/types/mount_point.ts +28 -1
  38. package/src/volume_metadata.ts +97 -2
  39. package/src/windows/system_volume.h +21 -16
  40. package/src/windows/volume_metadata.cpp +13 -7
  41. package/src/windows/volume_mount_points.cpp +11 -7
package/dist/index.d.cts CHANGED
@@ -88,11 +88,36 @@ interface MountPoint {
88
88
  * Indicates if this volume is primarily for system use (e.g., swap, snap
89
89
  * loopbacks, EFI boot, or only system directories).
90
90
  *
91
- * Note: This is a best-effort classification and is not 100% accurate.
91
+ * On macOS, the sealed APFS system snapshot at `/` is detected natively via
92
+ * `MNT_SNAPSHOT`; other infrastructure volumes under `/System/Volumes/*` are
93
+ * detected via APFS volume roles (IOKit). Note that `/System/Volumes/Data`
94
+ * is **not** a system volume — it holds all user data, accessed via firmlinks.
92
95
  *
93
96
  * @see {@link Options.systemPathPatterns} and {@link Options.systemFsTypes}
94
97
  */
95
98
  isSystemVolume?: boolean;
99
+ /**
100
+ * The APFS volume role, if available. Only present on macOS for APFS volumes.
101
+ *
102
+ * Common roles: `"System"`, `"Data"`, `"VM"`, `"Preboot"`, `"Recovery"`,
103
+ * `"Update"`, `"Hardware"`, `"xART"`, `"Prelogin"`, `"Backup"`.
104
+ *
105
+ * Used for system volume detection: volumes with a non-`"Data"` role and
106
+ * `MNT_DONTBROWSE` are classified as system volumes.
107
+ *
108
+ * @see https://eclecticlight.co/2024/11/21/how-do-apfs-volume-roles-work/
109
+ */
110
+ volumeRole?: string;
111
+ /**
112
+ * Whether the volume is mounted read-only.
113
+ *
114
+ * Examples of read-only volumes include the macOS APFS system snapshot at
115
+ * `/`, mounted ISO images, and write-protected media.
116
+ *
117
+ * Note that the macOS root volume (`/`) UUID changes on every OS update, so
118
+ * consumers should avoid using it for persistent identification.
119
+ */
120
+ isReadOnly?: boolean;
96
121
  /**
97
122
  * If there are non-critical errors while extracting metadata, those errors
98
123
  * may be added to this field.
@@ -262,7 +287,7 @@ declare function getTimeoutMsDefault(): number;
262
287
  /**
263
288
  * System paths and globs that indicate system volumes
264
289
  */
265
- declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm", "/System/Volumes/Hardware", "/System/Volumes/iSCPreboot", "/System/Volumes/Preboot", "/System/Volumes/Recovery", "/System/Volumes/Reserved", "/System/Volumes/Update", "/System/Volumes/VM", "/System/Volumes/xarts", "**/.DocumentRevisions-V100", "**/.fseventsd", "**/.Spotlight-V100", "**/.Trashes"];
290
+ declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm"];
266
291
  /**
267
292
  * Filesystem types that indicate system/virtual volumes.
268
293
  *
@@ -332,6 +357,17 @@ declare function getVolumeMountPoints(opts?: Partial<GetVolumeMountPointOptions>
332
357
  * @param opts Optional filesystem operation settings
333
358
  */
334
359
  declare function getVolumeMetadata(mountPoint: string, opts?: Partial<Pick<Options, "timeoutMs">>): Promise<VolumeMetadata>;
360
+ /**
361
+ * Get metadata for the volume that contains the given file or directory path.
362
+ *
363
+ * Unlike {@link getVolumeMetadata}, this accepts any path — not just mount
364
+ * points. Symlinks are resolved, and macOS APFS firmlinks (e.g. `/Users` →
365
+ * `/System/Volumes/Data`) are handled correctly, mirroring what `df` does.
366
+ *
367
+ * @param pathname Path to any file or directory
368
+ * @param opts Optional filesystem operation settings
369
+ */
370
+ declare function getVolumeMetadataForPath(pathname: string, opts?: Partial<Pick<Options, "timeoutMs" | "linuxMountTablePaths">>): Promise<VolumeMetadata>;
335
371
  /**
336
372
  * Retrieves metadata for all mounted volumes with optional filtering and
337
373
  * concurrency control.
@@ -393,4 +429,4 @@ declare function getHiddenMetadata(pathname: string): Promise<HiddenMetadata>;
393
429
  */
394
430
  declare function setHidden(pathname: string, hidden: boolean, method?: HideMethod): Promise<SetHiddenResult>;
395
431
 
396
- export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
432
+ export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMetadataForPath, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
package/dist/index.d.mts CHANGED
@@ -88,11 +88,36 @@ interface MountPoint {
88
88
  * Indicates if this volume is primarily for system use (e.g., swap, snap
89
89
  * loopbacks, EFI boot, or only system directories).
90
90
  *
91
- * Note: This is a best-effort classification and is not 100% accurate.
91
+ * On macOS, the sealed APFS system snapshot at `/` is detected natively via
92
+ * `MNT_SNAPSHOT`; other infrastructure volumes under `/System/Volumes/*` are
93
+ * detected via APFS volume roles (IOKit). Note that `/System/Volumes/Data`
94
+ * is **not** a system volume — it holds all user data, accessed via firmlinks.
92
95
  *
93
96
  * @see {@link Options.systemPathPatterns} and {@link Options.systemFsTypes}
94
97
  */
95
98
  isSystemVolume?: boolean;
99
+ /**
100
+ * The APFS volume role, if available. Only present on macOS for APFS volumes.
101
+ *
102
+ * Common roles: `"System"`, `"Data"`, `"VM"`, `"Preboot"`, `"Recovery"`,
103
+ * `"Update"`, `"Hardware"`, `"xART"`, `"Prelogin"`, `"Backup"`.
104
+ *
105
+ * Used for system volume detection: volumes with a non-`"Data"` role and
106
+ * `MNT_DONTBROWSE` are classified as system volumes.
107
+ *
108
+ * @see https://eclecticlight.co/2024/11/21/how-do-apfs-volume-roles-work/
109
+ */
110
+ volumeRole?: string;
111
+ /**
112
+ * Whether the volume is mounted read-only.
113
+ *
114
+ * Examples of read-only volumes include the macOS APFS system snapshot at
115
+ * `/`, mounted ISO images, and write-protected media.
116
+ *
117
+ * Note that the macOS root volume (`/`) UUID changes on every OS update, so
118
+ * consumers should avoid using it for persistent identification.
119
+ */
120
+ isReadOnly?: boolean;
96
121
  /**
97
122
  * If there are non-critical errors while extracting metadata, those errors
98
123
  * may be added to this field.
@@ -262,7 +287,7 @@ declare function getTimeoutMsDefault(): number;
262
287
  /**
263
288
  * System paths and globs that indicate system volumes
264
289
  */
265
- declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm", "/System/Volumes/Hardware", "/System/Volumes/iSCPreboot", "/System/Volumes/Preboot", "/System/Volumes/Recovery", "/System/Volumes/Reserved", "/System/Volumes/Update", "/System/Volumes/VM", "/System/Volumes/xarts", "**/.DocumentRevisions-V100", "**/.fseventsd", "**/.Spotlight-V100", "**/.Trashes"];
290
+ declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm"];
266
291
  /**
267
292
  * Filesystem types that indicate system/virtual volumes.
268
293
  *
@@ -332,6 +357,17 @@ declare function getVolumeMountPoints(opts?: Partial<GetVolumeMountPointOptions>
332
357
  * @param opts Optional filesystem operation settings
333
358
  */
334
359
  declare function getVolumeMetadata(mountPoint: string, opts?: Partial<Pick<Options, "timeoutMs">>): Promise<VolumeMetadata>;
360
+ /**
361
+ * Get metadata for the volume that contains the given file or directory path.
362
+ *
363
+ * Unlike {@link getVolumeMetadata}, this accepts any path — not just mount
364
+ * points. Symlinks are resolved, and macOS APFS firmlinks (e.g. `/Users` →
365
+ * `/System/Volumes/Data`) are handled correctly, mirroring what `df` does.
366
+ *
367
+ * @param pathname Path to any file or directory
368
+ * @param opts Optional filesystem operation settings
369
+ */
370
+ declare function getVolumeMetadataForPath(pathname: string, opts?: Partial<Pick<Options, "timeoutMs" | "linuxMountTablePaths">>): Promise<VolumeMetadata>;
335
371
  /**
336
372
  * Retrieves metadata for all mounted volumes with optional filtering and
337
373
  * concurrency control.
@@ -393,4 +429,4 @@ declare function getHiddenMetadata(pathname: string): Promise<HiddenMetadata>;
393
429
  */
394
430
  declare function setHidden(pathname: string, hidden: boolean, method?: HideMethod): Promise<SetHiddenResult>;
395
431
 
396
- export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
432
+ export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMetadataForPath, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
package/dist/index.d.ts CHANGED
@@ -88,11 +88,36 @@ interface MountPoint {
88
88
  * Indicates if this volume is primarily for system use (e.g., swap, snap
89
89
  * loopbacks, EFI boot, or only system directories).
90
90
  *
91
- * Note: This is a best-effort classification and is not 100% accurate.
91
+ * On macOS, the sealed APFS system snapshot at `/` is detected natively via
92
+ * `MNT_SNAPSHOT`; other infrastructure volumes under `/System/Volumes/*` are
93
+ * detected via APFS volume roles (IOKit). Note that `/System/Volumes/Data`
94
+ * is **not** a system volume — it holds all user data, accessed via firmlinks.
92
95
  *
93
96
  * @see {@link Options.systemPathPatterns} and {@link Options.systemFsTypes}
94
97
  */
95
98
  isSystemVolume?: boolean;
99
+ /**
100
+ * The APFS volume role, if available. Only present on macOS for APFS volumes.
101
+ *
102
+ * Common roles: `"System"`, `"Data"`, `"VM"`, `"Preboot"`, `"Recovery"`,
103
+ * `"Update"`, `"Hardware"`, `"xART"`, `"Prelogin"`, `"Backup"`.
104
+ *
105
+ * Used for system volume detection: volumes with a non-`"Data"` role and
106
+ * `MNT_DONTBROWSE` are classified as system volumes.
107
+ *
108
+ * @see https://eclecticlight.co/2024/11/21/how-do-apfs-volume-roles-work/
109
+ */
110
+ volumeRole?: string;
111
+ /**
112
+ * Whether the volume is mounted read-only.
113
+ *
114
+ * Examples of read-only volumes include the macOS APFS system snapshot at
115
+ * `/`, mounted ISO images, and write-protected media.
116
+ *
117
+ * Note that the macOS root volume (`/`) UUID changes on every OS update, so
118
+ * consumers should avoid using it for persistent identification.
119
+ */
120
+ isReadOnly?: boolean;
96
121
  /**
97
122
  * If there are non-critical errors while extracting metadata, those errors
98
123
  * may be added to this field.
@@ -262,7 +287,7 @@ declare function getTimeoutMsDefault(): number;
262
287
  /**
263
288
  * System paths and globs that indicate system volumes
264
289
  */
265
- declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm", "/System/Volumes/Hardware", "/System/Volumes/iSCPreboot", "/System/Volumes/Preboot", "/System/Volumes/Recovery", "/System/Volumes/Reserved", "/System/Volumes/Update", "/System/Volumes/VM", "/System/Volumes/xarts", "**/.DocumentRevisions-V100", "**/.fseventsd", "**/.Spotlight-V100", "**/.Trashes"];
290
+ declare const SystemPathPatternsDefault: readonly ["/boot", "/boot/efi", "/dev", "/dev/**", "/proc/**", "/run", "/run/credentials/**", "/run/flatpak/**", "/run/lock", "/run/snapd/**", "/run/user/*/doc", "/run/user/*/gvfs", "/snap/**", "/sys/**", "/tmp", "/var/tmp", "**/#snapshot", "/run/docker/**", "/var/lib/docker/**", "/run/containerd/**", "/var/lib/containerd/**", "/run/containers/**", "/var/lib/containers/**", "/var/lib/kubelet/**", "/var/lib/lxc/**", "/var/lib/lxd/**", "/mnt/wslg/distro", "/mnt/wslg/doc", "/mnt/wslg/versions.txt", "/usr/lib/wsl/drivers", "/private/var/vm"];
266
291
  /**
267
292
  * Filesystem types that indicate system/virtual volumes.
268
293
  *
@@ -332,6 +357,17 @@ declare function getVolumeMountPoints(opts?: Partial<GetVolumeMountPointOptions>
332
357
  * @param opts Optional filesystem operation settings
333
358
  */
334
359
  declare function getVolumeMetadata(mountPoint: string, opts?: Partial<Pick<Options, "timeoutMs">>): Promise<VolumeMetadata>;
360
+ /**
361
+ * Get metadata for the volume that contains the given file or directory path.
362
+ *
363
+ * Unlike {@link getVolumeMetadata}, this accepts any path — not just mount
364
+ * points. Symlinks are resolved, and macOS APFS firmlinks (e.g. `/Users` →
365
+ * `/System/Volumes/Data`) are handled correctly, mirroring what `df` does.
366
+ *
367
+ * @param pathname Path to any file or directory
368
+ * @param opts Optional filesystem operation settings
369
+ */
370
+ declare function getVolumeMetadataForPath(pathname: string, opts?: Partial<Pick<Options, "timeoutMs" | "linuxMountTablePaths">>): Promise<VolumeMetadata>;
335
371
  /**
336
372
  * Retrieves metadata for all mounted volumes with optional filtering and
337
373
  * concurrency control.
@@ -393,4 +429,4 @@ declare function getHiddenMetadata(pathname: string): Promise<HiddenMetadata>;
393
429
  */
394
430
  declare function setHidden(pathname: string, hidden: boolean, method?: HideMethod): Promise<SetHiddenResult>;
395
431
 
396
- export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
432
+ export { type GetVolumeMountPointOptions, type HiddenMetadata, type HideMethod, IncludeSystemVolumesDefault, LinuxMountTablePathsDefault, type MountPoint, NetworkFsTypesDefault, type Options, OptionsDefault, type SetHiddenResult, SkipNetworkVolumesDefault, type StringEnum, type StringEnumKeys, type StringEnumType, SystemFsTypesDefault, SystemPathPatternsDefault, type SystemVolumeConfig, type VolumeHealthStatus, VolumeHealthStatuses, type VolumeMetadata, getAllVolumeMetadata, getHiddenMetadata, getTimeoutMsDefault, getVolumeMetadata, getVolumeMetadataForPath, getVolumeMountPoints, isHidden, isHiddenRecursive, optionsWithDefaults, setHidden };
package/dist/index.mjs CHANGED
@@ -388,7 +388,7 @@ function toError(cause) {
388
388
  }
389
389
 
390
390
  // src/path.ts
391
- import { dirname as dirname2, resolve as resolve2 } from "path";
391
+ import { dirname as dirname2, resolve as resolve2, sep } from "path";
392
392
  function normalizePath(mountPoint) {
393
393
  if (isBlank(mountPoint)) return void 0;
394
394
  if (mountPoint.includes("..")) {
@@ -417,6 +417,11 @@ function isRootDirectory(path2) {
417
417
  const n = normalizePath(path2);
418
418
  return n == null ? false : isWindows ? dirname2(n) === n : n === "/";
419
419
  }
420
+ function isAncestorOrSelf(ancestor, descendant) {
421
+ if (ancestor === descendant) return true;
422
+ const prefix = isRootDirectory(ancestor) ? ancestor : ancestor + sep;
423
+ return descendant.startsWith(prefix);
424
+ }
420
425
 
421
426
  // src/hidden.ts
422
427
  var HiddenSupportByPlatform = {
@@ -643,23 +648,13 @@ var SystemPathPatternsDefault = [
643
648
  "/mnt/wslg/doc",
644
649
  "/mnt/wslg/versions.txt",
645
650
  "/usr/lib/wsl/drivers",
646
- // macOS system paths:
647
- "/private/var/vm",
648
- // macOS swap
649
- "/System/Volumes/Hardware",
650
- "/System/Volumes/iSCPreboot",
651
- "/System/Volumes/Preboot",
652
- "/System/Volumes/Recovery",
653
- "/System/Volumes/Reserved",
654
- "/System/Volumes/Update",
655
- "/System/Volumes/VM",
656
- "/System/Volumes/xarts",
657
- // macOS per-volume metadata (Spotlight, FSEvents, versioning, Trash):
658
- // https://eclecticlight.co/2021/01/28/spotlight-on-search-how-spotlight-works/
659
- "**/.DocumentRevisions-V100",
660
- "**/.fseventsd",
661
- "**/.Spotlight-V100",
662
- "**/.Trashes"
651
+ // macOS system volumes are detected natively via APFS volume roles
652
+ // (IOKit IOMedia "Role" property) with MNT_SNAPSHOT as a fallback.
653
+ // No path patterns needed. See src/darwin/system_volume.h.
654
+ //
655
+ // /private/var/vm is the macOS swap directory (not a mount point on most
656
+ // systems, but included for completeness if it appears as one).
657
+ "/private/var/vm"
663
658
  ];
664
659
  var SystemFsTypesDefault = [
665
660
  "autofs",
@@ -834,6 +829,10 @@ async function directoryStatus(dir, timeoutMs, canReaddirImpl = canReaddir) {
834
829
  return { status: VolumeHealthStatuses.unknown };
835
830
  }
836
831
 
832
+ // src/volume_metadata.ts
833
+ import { realpath } from "fs/promises";
834
+ import { dirname as dirname4 } from "path";
835
+
837
836
  // src/linux/dev_disk.ts
838
837
  import { readdir, readlink } from "fs/promises";
839
838
  import { join as join3, resolve as resolve3 } from "path";
@@ -1118,20 +1117,20 @@ function isSystemVolume(mountPoint, fstype, config = {}) {
1118
1117
  }
1119
1118
  function assignSystemVolume(mp, config) {
1120
1119
  const result = isSystemVolume(mp.mountPoint, mp.fstype, config);
1121
- if (isWindows) {
1122
- mp.isSystemVolume ??= result;
1123
- } else {
1124
- mp.isSystemVolume = result;
1125
- }
1120
+ mp.isSystemVolume = mp.isSystemVolume || result;
1126
1121
  }
1127
1122
 
1128
1123
  // src/linux/mtab.ts
1124
+ function isReadOnlyMount(fs_mntops) {
1125
+ return fs_mntops?.split(",").includes("ro") ?? false;
1126
+ }
1129
1127
  function mountEntryToMountPoint(entry) {
1130
1128
  const mountPoint = normalizePosixPath(entry.fs_file);
1131
1129
  const fstype = toNotBlank(entry.fs_vfstype) ?? toNotBlank(entry.fs_spec);
1132
1130
  return mountPoint == null || fstype == null ? void 0 : {
1133
1131
  mountPoint,
1134
- fstype
1132
+ fstype,
1133
+ isReadOnly: isReadOnlyMount(entry.fs_mntops)
1135
1134
  };
1136
1135
  }
1137
1136
  function mountEntryToPartialVolumeMetadata(entry, options = {}) {
@@ -1141,6 +1140,7 @@ function mountEntryToPartialVolumeMetadata(entry, options = {}) {
1141
1140
  fstype: entry.fs_vfstype,
1142
1141
  mountFrom: entry.fs_spec,
1143
1142
  isSystemVolume: isSystemVolume(entry.fs_file, entry.fs_vfstype, options),
1143
+ isReadOnly: isReadOnlyMount(entry.fs_mntops),
1144
1144
  remote: false,
1145
1145
  // < default to false, but it may be overridden by extractRemoteInfo
1146
1146
  ...extractRemoteInfo(entry.fs_spec, networkFsTypes)
@@ -1424,6 +1424,57 @@ async function _getVolumeMetadata(o, nativeFn2) {
1424
1424
  debug("[getVolumeMetadata] final result for %s: %o", o.mountPoint, result);
1425
1425
  return compactValues(result);
1426
1426
  }
1427
+ async function getVolumeMetadataForPathImpl(pathname, opts, nativeFn2) {
1428
+ if (isBlank(pathname)) {
1429
+ throw new TypeError("Invalid pathname: got " + JSON.stringify(pathname));
1430
+ }
1431
+ const resolved = await realpath(pathname);
1432
+ const resolvedStat = await statAsync(resolved);
1433
+ const dir = resolvedStat.isDirectory() ? resolved : dirname4(resolved);
1434
+ if (isMacOS) {
1435
+ const probe = await getVolumeMetadataImpl(
1436
+ { ...opts, mountPoint: dir },
1437
+ nativeFn2
1438
+ );
1439
+ const canonicalMountPoint = isNotBlank(probe.mountName) ? probe.mountName : dir;
1440
+ if (canonicalMountPoint === dir) return probe;
1441
+ return getVolumeMetadataImpl(
1442
+ { ...opts, mountPoint: canonicalMountPoint },
1443
+ nativeFn2
1444
+ );
1445
+ }
1446
+ const targetDev = resolvedStat.dev;
1447
+ const mountPoints = await getVolumeMountPointsImpl(
1448
+ { ...opts, includeSystemVolumes: true },
1449
+ nativeFn2
1450
+ );
1451
+ const prefixMatches = [];
1452
+ const deviceMatches = [];
1453
+ await Promise.all(
1454
+ mountPoints.map(async ({ mountPoint: mountPoint2 }) => {
1455
+ try {
1456
+ const mpDev = (await statAsync(mountPoint2)).dev;
1457
+ if (mpDev !== targetDev) return;
1458
+ if (isAncestorOrSelf(mountPoint2, resolved)) {
1459
+ prefixMatches.push(mountPoint2);
1460
+ } else {
1461
+ deviceMatches.push(mountPoint2);
1462
+ }
1463
+ } catch {
1464
+ }
1465
+ })
1466
+ );
1467
+ const candidates = prefixMatches.length > 0 ? prefixMatches : deviceMatches;
1468
+ if (candidates.length === 0) {
1469
+ throw new Error(
1470
+ "No mount point found for path: " + JSON.stringify(pathname)
1471
+ );
1472
+ }
1473
+ const mountPoint = candidates.reduce(
1474
+ (a, b) => a.length >= b.length ? a : b
1475
+ );
1476
+ return getVolumeMetadataImpl({ ...opts, mountPoint }, nativeFn2);
1477
+ }
1427
1478
  async function getAllVolumeMetadataImpl(opts, nativeFn2) {
1428
1479
  const o = optionsWithDefaults(opts);
1429
1480
  debug("[getAllVolumeMetadata] starting with options: %o", o);
@@ -1479,11 +1530,11 @@ async function getAllVolumeMetadataImpl(opts, nativeFn2) {
1479
1530
  var nativeFn = defer2(async () => {
1480
1531
  const start = Date.now();
1481
1532
  try {
1482
- const dirname4 = _dirname();
1483
- const dir = await findAncestorDir(dirname4, "binding.gyp");
1533
+ const dirname5 = _dirname();
1534
+ const dir = await findAncestorDir(dirname5, "binding.gyp");
1484
1535
  if (dir == null) {
1485
1536
  throw new Error(
1486
- "Could not find bindings.gyp in any ancestor directory of " + dirname4
1537
+ "Could not find bindings.gyp in any ancestor directory of " + dirname5
1487
1538
  );
1488
1539
  }
1489
1540
  const bindings = NodeGypBuild(dir);
@@ -1506,6 +1557,13 @@ function getVolumeMetadata(mountPoint, opts) {
1506
1557
  nativeFn
1507
1558
  );
1508
1559
  }
1560
+ function getVolumeMetadataForPath(pathname, opts) {
1561
+ return getVolumeMetadataForPathImpl(
1562
+ pathname,
1563
+ optionsWithDefaults(opts),
1564
+ nativeFn
1565
+ );
1566
+ }
1509
1567
  function getAllVolumeMetadata(opts) {
1510
1568
  return getAllVolumeMetadataImpl(optionsWithDefaults(opts), nativeFn);
1511
1569
  }
@@ -1534,6 +1592,7 @@ export {
1534
1592
  getHiddenMetadata,
1535
1593
  getTimeoutMsDefault,
1536
1594
  getVolumeMetadata,
1595
+ getVolumeMetadataForPath,
1537
1596
  getVolumeMountPoints,
1538
1597
  isHidden,
1539
1598
  isHiddenRecursive,