Filesystems & Mounts · Section 2
stat(2)
Get file metadata (size, permissions, timestamps, owner) by pathname.
Signature
#include <sys/stat.h>
int stat(const char * pathname, struct stat * statbuf);- pathname
- Path of the file to stat. Symlinks are followed (use lstat() to keep them).
- statbuf
- Output buffer; the kernel writes a struct stat (or struct stat64 on 32-bit ABIs).
Description
stat() fills *statbuf with metadata about the file at pathname: device, inode, mode (type + permission bits), nlink, uid, gid, rdev (for special files), size, block size, blocks allocated, and atime/mtime/ctime. The call does NOT open the file — it only reads inode information — and dereferences symlinks (use lstat() to stat the link itself). Modern code prefers statx() or newfstatat() because (a) they avoid the legacy 32-bit time_t cast that broke pre-2038 stat()s on i386, (b) statx() lets the caller select only the fields it actually needs (cheaper on networked filesystems), and (c) newfstatat()/statx() support dirfd-relative paths for race-free traversal. On aarch64 stat() and lstat() are not exported as syscalls at all — only newfstatat()/statx() exist, and the libc wrappers translate.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 106 | i386 | sys_newstat |
| x64 (x86_64) | 4 | common | sys_newstat |
Kernel history
Introduced in Linux 1.0.
1.0
stat() is one of the original Linux syscalls (1.0). The 'newstat'/'sys_newstat' entry name reflects the kernel-internal split between the original stat() (returning a struct of a fixed older layout) and the post-1.4 expansion to struct stat64; modern userspace always reaches the new layout via glibc.
2.6.16
fstatat() (also called newfstatat()) was added with the *at family (2.6.16) to support dirfd-relative path lookup, eliminating the chdir() races classic stat() suffered.
4.11
statx() (Linux 4.11) added a request mask so callers can ask for only certain fields, returned a struct timespec64 for all timestamps (post-2038 safe), and exposed Linux-specific information (btime, attribute flags, mount ID). The modern recommended interface.
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
The stat family (stat, lstat, fstat, newfstatat, statx) is allowed by every default profile. Every program calls one of them constantly — ls, cp, dynamic linker, every shell — so blocking them is impractical. The seccomp lever for filesystem confinement is at openat() and Landlock, not at stat.
libseccomp
// Allow the stat family
for (int s : { SCMP_SYS(stat), SCMP_SYS(lstat), SCMP_SYS(fstat),
SCMP_SYS(newfstatat), SCMP_SYS(statx) })
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, s, 0);strace example
$ strace -e stat,fstat,newfstatat,statx ls /etc/hostname
newfstatat(AT_FDCWD, "/etc/hostname", {st_mode=S_IFREG|0644, st_size=15, …}, 0) = 0
statx(0, NULL, AT_STATX_SYNC_AS_STAT, STATX_ALL, NULL) = -1 EFAULTModern strace shows newfstatat() for what userspace calls stat() (because glibc rewrites it). Use -e trace=stat to cover the whole family symbolically. -y resolves dirfd to a path. The first ~100 stat()s of any program startup are the dynamic linker walking the search path — uninteresting; the application-level activity starts after that.
Security & observability
stat() on its own is read-only and rarely interesting to monitor — too noisy, low impact. The exceptions: stat() of /etc/shadow, /root/.ssh/, or /proc/<pid>/mem from a non-root process is a strong reconnaissance signal (the attacker is checking whether they can later open them). For container security, comparing the inode and mount of /proc/self/exe against expected values can detect file-substitution rootkits — the data comes from a stat() call. eBPF tracepoint sys_enter_newfstatat captures every modern stat call; pair with the LSM file_open hook for higher-fidelity decision-making.
Errors
- EACCES
- Search permission denied on a component of pathname (not on the file itself — stat() only needs traverse permission).
- EBADF
- —
- EFAULT
- —
- ELOOP
- Too many symbolic-link loops were encountered.
- ENAMETOOLONG
- pathname is longer than PATH_MAX.
- ENOENT
- A component of pathname does not exist or pathname is empty.
- ENOMEM
- —
- ENOTDIR
- A component of pathname (not the final one) is not a directory.
- EOVERFLOW
- One of the fields exceeds the range representable in this ABI's struct stat (e.g. >2 GiB file on a 32-bit non-LFS build). statx() avoids this.