# \[Pitch\] StatFS and supporting types

**URL:** <https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151>\
**Category:** System\
**Created:** [July 8, 2026, 6:19pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151 "2026-07-08T18:19:14Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jrflat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrflat/32/31407_2.png) [@jrflat](https://forums.swift.org/u/jrflat)\
**Post date:** [July 8, 2026, 6:19pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/1 "2026-07-08T18:19:14Z")

</div>

Hi System enthusiasts!

I'd like to pitch new Swift API for the `statfs` family of syscalls and supporting types. The following shows a proposed solution with Swift API to C mappings, then some topics for discussion. Feedback is greatly appreciated!

## Proposed solution

This proposal adds a `struct StatFS` that is available on Unix-like platforms. For Windows, a future pitch could provide Swift-native wrappers of functions like `GetVolumeInformation` and `DeviceIoControl` with their associated types.

`StatFS` is a Swift wrapper around the C `statfs` or `statvfs` struct, which provides information about file systems. On Darwin and BSD operating systems, this type uses `statfs` for the additional information it provides. On other Unix-like operating systems, this type uses the standard `statvfs` interface. All initializers use a typed `throws(Errno)` and require Swift 6.0 or later (note swift-system 1.7.x already requires Swift \>= 6.1).

```swift
// Get file system information from a path
let statfs = try StatFS("/")

// From FileDescriptor
let statfs = try fd.statfs()

// From FilePath
let statfs = try filePath.statfs()

print("File system ID: \(statfs.fileSystemID)")
print("Total space: \(statfs.totalSpace) bytes")
print("Available space: \(statfs.availableSpace) bytes")

// Check mount flags
if statfs.mountFlags.contains(.readOnly) {
    print("File system is read-only")
}

// Platform-specific information when available
#if os(anyAppleOS) || os(FreeBSD) || os(OpenBSD)
print("File system is mounted at \(statfs.mountPoint)")
print("File system is mounted from \(statfs.mountSource)")
#endif

```

Alongside `StatFS`, we would also add types for `MountFlags`, `FileSystemID`, `FileSystemType`, and `FileSystemSubtype` members of the struct. The available values for `MountFlags` differ significantly between platforms as shown in the mappings below. `FileSystemType` is available on Darwin and FreeBSD where `statfs` reports `f_type`. `FileSystemSubtype` is Darwin-only.

## Swift API to C Mapping

The following tables show the mapping between Swift APIs and their underlying C system calls across different operating systems:

### `StatFS` Initializer Mappings

The `retryOnInterrupt: Bool = true` parameter is omitted for clarity.

| Swift API | Darwin / FreeBSD / OpenBSD | Linux / Android / WASI |
| --- | --- | --- |
| `StatFS(_ path: UnsafePointer<CChar>)` | `statfs()` | `statvfs()` |
| `StatFS(_ path: FilePath)` | `statfs()` | `statvfs()` |
| `FilePath.statfs()` | `statfs()` | `statvfs()` |
| | | |
| `StatFS(_ fd: FileDescriptor)` | `fstatfs()` | `fstatvfs()` |
| `FileDescriptor.statfs()` | `fstatfs()` | `fstatvfs()` |

### `StatFS` Property Mappings

`"` denotes the same property name across all operating systems.

| Swift Property | Darwin (`statfs`) | FreeBSD (`statfs`) | OpenBSD (`statfs`) | Linux (`statvfs`) | Android (`statvfs`) | WASI (`statvfs`) |
| --- | --- | --- | --- | --- | --- | --- |
| `blockSize` | `f_bsize` | " | " | " | " | " |
| `preferredIOBlockSize` | `f_iosize` | `f_iosize` | `f_iosize` | N/A | N/A | N/A |
| `fragmentSize` | N/A | N/A | N/A | `f_frsize` | `f_frsize` | `f_frsize` |
| `totalBlocks` | `f_blocks` | " | " | " | " | " |
| `freeBlocks` | `f_bfree` | " | " | " | " | " |
| `availableBlocks` | `f_bavail` | " | " | " | " | " |
| `totalInodes` | `f_files` | " | " | " | " | " |
| `freeInodes` | `f_ffree` | " | " | " | " | " |
| `availableInodes` | N/A | N/A | `f_favail` | `f_favail` | `f_favail` | `f_favail` |
| `maximumNameLength` | N/A | `f_namemax` | `f_namemax` | `f_namemax` | `f_namemax` | `f_namemax` |
| `fileSystemID` | `f_fsid` | " | " | " | " | " |
| `mountFlags` | `f_flags` | `f_flags` | `f_flags` | `f_flag` | `f_flag` | `f_flag` |
| `type` | `f_type` | `f_type` | N/A | N/A | N/A | N/A |
| `subtype` | `f_fssubtype` | N/A | N/A | N/A | N/A | N/A |
| `owner` | `f_owner` | `f_owner` | `f_owner` | N/A | N/A | N/A |
| `typeName` | `f_fstypename` | `f_fstypename` | `f_fstypename` | N/A | N/A | N/A |
| `mountPoint` | `f_mntonname` | `f_mntonname` | `f_mntonname` | N/A | N/A | N/A |
| `mountSource` | `f_mntfromname` | `f_mntfromname` | `f_mntfromname` | N/A | N/A | N/A |

### `MountFlags` Mappings

| Swift Flag | Darwin | FreeBSD | OpenBSD | Linux | Android | WASI |
| --- | --- | --- | --- | --- | --- | --- |
| `readOnly` | `MNT_RDONLY` | `MNT_RDONLY` | `MNT_RDONLY` | `ST_RDONLY` | `ST_RDONLY` | `ST_RDONLY` |
| `synchronous` | `MNT_SYNCHRONOUS` | `MNT_SYNCHRONOUS` | `MNT_SYNCHRONOUS` | `ST_SYNCHRONOUS` | `ST_SYNCHRONOUS` | `ST_SYNCHRONOUS` |
| `noExecution` | `MNT_NOEXEC` | `MNT_NOEXEC` | `MNT_NOEXEC` | `ST_NOEXEC` | `ST_NOEXEC` | `ST_NOEXEC` |
| `noSetUserID` | `MNT_NOSUID` | `MNT_NOSUID` | `MNT_NOSUID` | `ST_NOSUID` | `ST_NOSUID` | `ST_NOSUID` |
| `noAccessTime` | `MNT_NOATIME` | `MNT_NOATIME` | `MNT_NOATIME` | `ST_NOATIME` | `ST_NOATIME` | `ST_NOATIME` |
| `noDevices` | `MNT_NODEV` | N/A | `MNT_NODEV` | `ST_NODEV` | `ST_NODEV` | `ST_NODEV` |
| `mandatoryLockingPermitted` | N/A | N/A | N/A | `ST_MANDLOCK` | `ST_MANDLOCK` | `ST_MANDLOCK` |
| `noDirectoryAccessTime` | N/A | N/A | N/A | `ST_NODIRATIME` | `ST_NODIRATIME` | `ST_NODIRATIME` |
| `relativeAccessTime` | N/A | N/A | N/A | `ST_RELATIME` | `ST_RELATIME` | `ST_RELATIME` |
| `asynchronous` | `MNT_ASYNC` | `MNT_ASYNC` | `MNT_ASYNC` | N/A | N/A | N/A |
| `exported` | `MNT_EXPORTED` | `MNT_EXPORTED` | `MNT_EXPORTED` | N/A | N/A | N/A |
| `local` | `MNT_LOCAL` | `MNT_LOCAL` | `MNT_LOCAL` | N/A | N/A | N/A |
| `quota` | `MNT_QUOTA` | `MNT_QUOTA` | `MNT_QUOTA` | N/A | N/A | N/A |
| `rootFileSystem` | `MNT_ROOTFS` | `MNT_ROOTFS` | `MNT_ROOTFS` | N/A | N/A | N/A |
| `union` | `MNT_UNION` | `MNT_UNION` | N/A | N/A | N/A | N/A |
| `automounted` | `MNT_AUTOMOUNTED` | `MNT_AUTOMOUNTED` | N/A | N/A | N/A | N/A |
| `multiLabel` | `MNT_MULTILABEL` | `MNT_MULTILABEL` | N/A | N/A | N/A | N/A |
| `exportedReadOnly` | N/A | `MNT_EXRDONLY` | `MNT_EXRDONLY` | N/A | N/A | N/A |
| `exportedByDefault` | N/A | `MNT_DEFEXPORTED` | `MNT_DEFEXPORTED` | N/A | N/A | N/A |
| `exportedAnonymously` | N/A | `MNT_EXPORTANON` | `MNT_EXPORTANON` | N/A | N/A | N/A |
| `softUpdates` | N/A | `MNT_SOFTDEP` | `MNT_SOFTDEP` | N/A | N/A | N/A |
| `contentProtection` | `MNT_CPROTECT` | N/A | N/A | N/A | N/A | N/A |
| `removable` | `MNT_REMOVABLE` | N/A | N/A | N/A | N/A | N/A |
| `quarantine` | `MNT_QUARANTINE` | N/A | N/A | N/A | N/A | N/A |
| `volumeFileSystem` | `MNT_DOVOLFS` | N/A | N/A | N/A | N/A | N/A |
| `noBrowsing` | `MNT_DONTBROWSE` | N/A | N/A | N/A | N/A | N/A |
| `ignoreOwnership` | `MNT_IGNORE_OWNERSHIP` | N/A | N/A | N/A | N/A | N/A |
| `journaled` | `MNT_JOURNALED` | N/A | N/A | N/A | N/A | N/A |
| `noUserExtendedAttributes` | `MNT_NOUSERXATTR` | N/A | N/A | N/A | N/A | N/A |
| `deferWrites` | `MNT_DEFWRITE` | N/A | N/A | N/A | N/A | N/A |
| `noSymlinkFollowAtMountPoint` | `MNT_NOFOLLOW` | N/A | N/A | N/A | N/A | N/A |
| `snapshot` | `MNT_SNAPSHOT` | N/A | N/A | N/A | N/A | N/A |
| `strictAccessTime` | `MNT_STRICTATIME` | N/A | N/A | N/A | N/A | N/A |
| `exportedKerberos` | N/A | `MNT_EXKERB` | N/A | N/A | N/A | N/A |
| `exportedPublic` | N/A | `MNT_EXPUBLIC` | N/A | N/A | N/A | N/A |
| `posixACLs` | N/A | `MNT_ACLS` | N/A | N/A | N/A | N/A |
| `geomJournaled` | N/A | `MNT_GJOURNAL` | N/A | N/A | N/A | N/A |
| `excludedFromDiskFreeReports` | N/A | `MNT_IGNORE` | N/A | N/A | N/A | N/A |
| `nfs4ACLs` | N/A | `MNT_NFS4ACLS` | N/A | N/A | N/A | N/A |
| `noClusterRead` | N/A | `MNT_NOCLUSTERR` | N/A | N/A | N/A | N/A |
| `noClusterWrite` | N/A | `MNT_NOCLUSTERW` | N/A | N/A | N/A | N/A |
| `setUserIDDirectory` | N/A | `MNT_SUIDDIR` | N/A | N/A | N/A | N/A |
| `softUpdateJournaling` | N/A | `MNT_SUJ` | N/A | N/A | N/A | N/A |
| `untrusted` | N/A | `MNT_UNTRUSTED` | N/A | N/A | N/A | N/A |
| `mountedByUser` | N/A | `MNT_USER` | N/A | N/A | N/A | N/A |
| `verified` | N/A | `MNT_VERIFIED` | N/A | N/A | N/A | N/A |
| `noPermissionChecks` | N/A | N/A | `MNT_NOPERM` | N/A | N/A | N/A |
| `writeExecuteAllowed` | N/A | N/A | `MNT_WXALLOWED` | N/A | N/A | N/A |
| `write` | N/A | N/A | N/A | `ST_WRITE` | N/A | `ST_WRITE` |
| `appendOnly` | N/A | N/A | N/A | `ST_APPEND` | N/A | `ST_APPEND` |
| `immutable` | N/A | N/A | N/A | `ST_IMMUTABLE` | N/A | `ST_IMMUTABLE` |
| `noSymlinkFollow` | N/A | `MNT_NOSYMFOLLOW` | N/A | `ST_NOSYMFOLLOW` | `ST_NOSYMFOLLOW` | N/A |

## Discussion

### 1. Handling platform-availability of mount flags

As shown in the matrix above, many flag values are unique across the vast array of platforms. The current approach would be to `#if` guard the flags available on each platform, with each flag's doc comment detailing the platforms where it's available. However, this may not easily answer the question: "What are all the flags that are available on X platform?" Having the table mapping as documentation could help alleviate this, but is there perhaps a better option?

### 2. `StatFS` as a single type for `statfs` and `statvfs`

Currently, the proposal maps `StatFS` to `statfs` for the additional information it provides on platforms where it's available, and `statvfs` otherwise. We could introduce a separate `StatVFS`, but having separate types for `statfs` and `statvfs` might increase cognitive overhead and confusion for developers deciding which to choose. It would also require additional documentation explaining differences that are otherwise handled by platform availability, and would require duplicate code. However, one upside is that all properties of `StatVFS` would be available where the struct itself is available. Does this convenience outweigh the cost of having two types?

### 3. WASI Support

WASI is included for parity, and `statvfs`/`fstatvfs` are present in `wasi-libc`, so this API would compile and link there. However, in the current `wasi-libc`, `statvfs` and `fstatvfs` are unconditional stubs (in `libc-bottom-half/sources/posix.c`) that set `errno` to `ENOSYS` and return `-1`:

```c
int statvfs(const char * __restrict path, struct statvfs *__ restrict buf) {
    // TODO: We plan to support this eventually in WASI, but not yet.
    errno = ENOSYS;
    return -1;
}

```

As a result, on WASI these `StatFS` initializers would always fail at runtime and throw `Errno.noFunction`. Callers targeting WASI must therefore be prepared for `StatFS` to be effectively unavailable at runtime. The current proposal keeps the API surface available on WASI so that code remains source-compatible across Unix-like platforms and can benefit automatically if a WASI gains real `statvfs` support in the future. I think this is reasonable because clients are expected to handle a potential `Errno` anyway, and it means once `wasi-libc` gains real `statvfs` support, System picks it up automatically without needing to track it. But I'm interested to hear your feedback!

Thanks again for your feedback and consideration!

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [July 8, 2026, 8:44pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/2 "2026-07-08T20:44:49Z")

</div>

One thing that I think that need to understand is how to actually ensure that these types do not get adopted within the toolchain itself (e.g. LSP). With the module being unavailable on Windows, this would be something that becomes problematic as that means that you could end up using APIs that are not available on Windows and might result in functionality being left out of the platform level support.

---

<div class="post-metadata">

**Author:** ![jrflat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrflat/32/31407_2.png) [@jrflat](https://forums.swift.org/u/jrflat)\
**Post date:** [July 8, 2026, 9:26pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/3 "2026-07-08T21:26:03Z")

</div>

To make sure I'm understanding correctly, the concern is that:

1. A toolchain project adds new functionality using `Stat/StatFS`, which builds locally on their macOS/Linux setup
2. Windows CI breaks because `Stat/StatFS` are `#if !os(Windows)`
3. The project decides to `#if !os(Windows)` guard their new feature, detracting from the goal of first-class Swift support on Windows

Is that correct?

I'm not sure System as a low-level library can technically prevent toolchain projects from adopting these types. I think the best answer might be to use these platform-specific types as a foundation for more portable types (e.g. in Foundation or another project), and to document the unavailability loudly. We could use an annotation like `@available(*, unavailable, message: "StatFS is unavailable on Windows; use <portable API> for cross-platform code")`.

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [July 8, 2026, 10:15pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/4 "2026-07-08T22:15:33Z")

</div>

Yes, that is correct. There have many instances of Windows being excluded this way (macros, bootstrapping, etc all of which had to then be repaired urgently as a last second request, often without assistance from the feature's author).

> [@jrflat](#):
>
> I think the best answer might be to use these platform-specific types as a foundation for more portable types (e.g. in Foundation or another project), and to document the unavailability loudly. We could use an annotation like `@available(*, unavailable, message: "StatFS is unavailable on Windows; use <portable API> for cross-platform code")`.

Yes, a loud warning which cannot be silenced would be reasonable enough for this I think.  
I think that should hopefully achieve the goal.

---

<div class="post-metadata">

**Author:** ![jrflat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrflat/32/31407_2.png) [@jrflat](https://forums.swift.org/u/jrflat)\
**Post date:** [July 23, 2026, 9:30pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/5 "2026-07-23T21:30:12Z")

</div>

I've opened draft PRs for the full `StatFS` proposal and implementation, including the Windows unavailability annotation. (Side note: I have a prototype for a Windows `GetFileInformationByHandleEx` wrapper that I'm hoping to pitch soon!)

Proposal: [[Proposal] StatFS and supporting types by jrflat · Pull Request #362 · apple/swift-system · GitHub](https://github.com/apple/swift-system/pull/362)  
Implementation: [[Implementation] StatFS and supporting types by jrflat · Pull Request #361 · apple/swift-system · GitHub](https://github.com/apple/swift-system/pull/361)

The implementation builds and passes tests on all supported platforms. Feedback welcome!

---

<div class="post-metadata">

**Author:** ![jrflat](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrflat/32/31407_2.png) [@jrflat](https://forums.swift.org/u/jrflat)\
**Post date:** [October 21, 2026, 7:52pm UTC](https://forums.swift.org/t/pitch-statfs-and-supporting-types/88151/6 "2026-10-21T19:52:30Z")

</div>

The proposal and implementation have a few updates!

- **v2:**

- **v3:**

- **v4:** Add `FileSystemTypeName` wrapper

Proposal: [[Proposal] StatFS and supporting types by jrflat · Pull Request #362 · apple/swift-system · GitHub](https://github.com/apple/swift-system/pull/362)  
Implementation: [[Implementation] StatFS and supporting types by jrflat · Pull Request #361 · apple/swift-system · GitHub](https://github.com/apple/swift-system/pull/361)

Feedback on the PRs or here in the forums is appreciated!
