stat vs fstat vs statx
stat, fstat, and statx are Linux system calls used to retrieve file metadata (inode information such as size, permissions, timestamps, and ownership). They represent three generations of file status APIs in Unix/Linux.
"stat" is short for "status" (as in "file status").
Core Comparison
| Feature | stat |
fstat |
statx |
|---|---|---|---|
| Input Target | Path name (const char *path) |
Open file descriptor (int fd) |
File descriptor + relative path (int dirfd, const char *pathname) |
| Introduced | Early Unix / POSIX.1 | Early Unix / POSIX.1 | Linux 4.11 (2017) |
| Time Resolution | Seconds / Nanoseconds (depending on POSIX version) | Seconds / Nanoseconds | Nanoseconds (statx_timestamp) |
Creation Time (btime) |
❌ Not supported | ❌ Not supported | Yes (stx_btime if FS supports it) |
| Selectable Fields | ❌ Returns fixed struct stat |
❌ Returns fixed struct stat |
Yes (Bitmask mask requests specific fields) |
| Network FS Sync Control | ❌ Default behavior (often forces sync) | ❌ N/A (FD based) | Yes (AT_STATX_SYNC_AS_STAT, AT_STATX_FORCE_SYNC, AT_STATX_DONT_SYNC) |
| Extended Attributes/Flags | ❌ None | ❌ None | Yes (FS flags like immutable, append, encrypted) |
Detailed Breakdown
stat() – Path-based Metadata Lookup
int stat(const char *restrict pathname, struct stat *restrict statbuf);
- Mechanism: Traverses the filesystem hierarchy using the provided pathname string to locate the inode and pull metadata.
- Race Condition Risk: Prone to Time-of-Check to Time-of-Use (TOCTOU) race conditions if the target file path is replaced or modified between the
stat()call and a subsequent operation (e.g.,open()). - Symlinks: Automatically resolves (follows) symbolic links. Use
lstat()if you want information about the symlink itself rather than its target.
fstat() – File-Descriptor-based Metadata Lookup
int fstat(int fd, struct stat *restrict statbuf);
- Mechanism: Queries the metadata directly from an already open file descriptor (
fd). - Performance & Safety: Eliminates pathname resolution overhead and avoids TOCTOU race conditions because it operates directly on the kernel object referenced by the open descriptor.
statx() – Modern, Extended & Masked Status Lookup
int statx(int dirfd, const char *restrict pathname, int flags,
unsigned int mask, struct statx *restrict statbuf);
- Mechanism: Introduced in Linux 4.11 to resolve historical limitations of
statandfstatacross modern local and networked filesystems (e.g., ext4, xfs, btrfs, NFS, SMB). - Key Advantages:
- Masked Retrieval (
mask): Allows callers to explicitly request only required fields (e.g.,STATX_SIZE | STATX_MODE). Networked filesystems (like NFS) do not need to fetch expensive uncached metadata attributes if they weren't requested. - Birth Time (
stx_btime): Provides native support for file creation time on supported filesystems (ext4, zfs, btrfs, etc.). - Explicit Sync Policy: Supports flags like
AT_STATX_DONT_SYNC(reads cached metadata without triggering network round-trips) orAT_STATX_FORCE_SYNC(forces flush/sync from server). - Filesystem Attributes: Exposes modern filesystem features via
stx_attributes(e.g., whether a file is encrypted, compressed, immutable, or append-only). - Flexible Path Resolution: Combines
fstatandstatpatterns using standard openat-style parameters (dirfd+pathname+flagslikeAT_EMPTY_PATHorAT_SYMLINK_NOFOLLOW).
- Masked Retrieval (
Which One Should You Use?
fstatwhen you already have an open file descriptor (fd). It is fast, safe against race conditions, and portable across all Unix-like systems.statxwhen writing modern Linux-specific code requiring creation time (btime), fine-grained cache/sync control on networked filesystems, or optimized partial metadata fetching.fstatat/statwhen writing strict POSIX-compliant code across non-Linux Unix systems wherestatxis not available.
How does statx handle TOCTOU?
Like fstat, statx eliminates Time-of-Check to Time-of-Use (TOCTOU) race conditions when you operate directly on an existing file descriptor rather than relying on path resolution.
Because statx uses the modern openat() interface conventions, it handles paths using three parameters: dirfd, pathname, and flags.
Operating Directly on an Open File Descriptor (FD-Based)
If you already have an open file descriptor (e.g., obtained via open(..., O_CLOEXEC)), you can query its status directly without re-resolving any path strings:
statx(fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &stx);
Why it avoids TOCTOU: Setting pathname to "" (an empty string) alongside the AT_EMPTY_PATH flag instructs the kernel to operate directly on fd. Even if another process deletes, renames, or swaps the path on disk, statx inspects the exact inode backing your open file descriptor.
Directory-Relative Path Resolution (dirfd + pathname)
When resolving paths relative to a directory descriptor:
statx(dirfd, "sub/file.txt", 0, STATX_BASIC_STATS, &stx);
Why it reduces path-traversal races: Using a pinned directory descriptor (dirfd) prevents intermediate path components above dirfd from being swapped out mid-traversal (a common vulnerability when using absolute path strings with traditional stat()).
Note: Standard path resolution for "sub/file.txt" within that directory can still be susceptible to TOCTOU if a concurrent process changes file.txt between your statx() check and a subsequent open. To completely eliminate TOCTOU, the safe pattern is to openat() first, then run statx() on the resulting descriptor.
Quick Comparison
| Call Pattern | Operates On | TOCTOU Safe? |
|---|---|---|
stat("/path/to/file", &st) |
Path string | ❌ No (path can change between check and use) |
fstat(fd, &st) |
Open Descriptor | Yes (pinned to the open inode) |
statx(fd, "", AT_EMPTY_PATH, ...) |
Open Descriptor | Yes (equivalent safety to fstat) |
statx(dirfd, "file", 0, ...) |
Relative Path | ⚠️ Partial (protects root traversal, not target path) |