Filesystems
How the kernel turns a flat array of disk blocks into named files with metadata — and keeps them consistent across a crash
Getting Started
A block device is a linear array of fixed-size sectors; a filesystem is the code that imposes structure on it — a hierarchy of named files, their contents, and the metadata (sizes, timestamps, permissions, block maps) that ties them together. The structure is the easy part. The hard part is keeping it consistent when power fails mid-write — and almost every design decision a Linux filesystem makes is, at bottom, an answer to "how do we not corrupt everything on a crash?"
This section explains the why behind the major Linux filesystems — the trade-offs behind journaling versus copy-on-write, extents versus block maps, in-place updates versus never overwriting — not just their on-disk formats.
Application: open("/data/file", O_RDONLY)
│
▼
VFS layer ← generic operations (see vfs/)
│
▼
Filesystem driver ← ext4, btrfs, xfs, tmpfs, ...
│
▼
Page cache ← in-memory cache of file data
│
▼
Block layer (bio) ← submits I/O to disk
│
▼
Block device ← NVMe, SATA, virtio-blk
The one question every filesystem answers
Writing a file touches several places on disk — the data blocks, the block map, the inode, the free-space bitmap. A crash between those writes leaves the filesystem inconsistent: an inode pointing at blocks the allocator still thinks are free, or data in blocks the inode doesn't yet reference. There are two dominant strategies:
- Journaling (ext4, XFS) — write the intended changes to a log first and commit them atomically, then apply them in place. After a crash, replay the log. Updates still happen in place, but the journal makes them recoverable.
- Copy-on-write (btrfs) — never overwrite live data. Write new versions into free space, then atomically flip a single pointer to the new tree root. A crash simply leaves the old, consistent tree intact.
Nearly everything else — extents, delayed allocation, snapshots, checksums — follows from committing to one of these.
Prerequisites
Familiarity with the VFS (the layer above, which dispatches operations to these filesystems), the page cache (where file data lives in memory), and the block layer (below, which carries the I/O to the device).
Suggested reading order
- ext4 — the default: extents, and journaling with jbd2
- ext4 Journaling Deep Dive — jbd2 internals; ordered vs. journaled data modes
- XFS — allocation groups, delayed allocation, and a log built for scale
- btrfs — the copy-on-write B-tree, subvolumes, and snapshots
- tmpfs and ramfs — a filesystem with no disk at all
- overlayfs — union mounts and copy-up, the basis of container images
- Crash Consistency and Recovery — journaling vs. copy-on-write, and what
fsync()really promises - iomap — the modern extent-based I/O framework that replaced
buffer_head - FUSE — filesystems implemented in userspace
- War Stories — ext4's
O_PONIESsaga, an overlayfs privilege escalation, and the btrfs write hole
What you'll learn
| Textbook idea | Linux reality |
|---|---|
| "The filesystem writes files to disk" | It writes to the page cache; the real disk write is deferred to writeback, which is why fsync() exists |
| "A journal logs everything" | ext4 defaults to journaling metadata only (data=ordered); full data journaling exists but roughly halves write throughput |
| "Copy-on-write means snapshots" | CoW is a crash-consistency strategy first; snapshots then come cheap — a snapshot is just an old tree root kept instead of reclaimed |
| "Deleting a file frees its space" | Only the metadata immediately; actual reclamation on SSDs involves discard/TRIM, and on CoW filesystems, reference counting |
Documentation
| Page | What it covers |
|---|---|
| ext4 | On-disk layout, extents, journaling with jbd2 |
| ext4 Journaling Deep Dive | jbd2 internals, ordered vs journaled data |
| XFS | Allocation groups, delayed allocation, log design |
| btrfs | Copy-on-Write B-tree, subvolumes, snapshots |
| tmpfs and ramfs | Memory-backed filesystems, size limits |
| overlayfs | Union mounts, copy-up, container layers |
| Crash Consistency and Recovery | Journaling vs CoW, flush/FUA, and fsync() durability |
| iomap | The extent-based I/O framework behind XFS, ext4, and btrfs |
| FUSE | Userspace filesystems: the /dev/fuse protocol and its costs |
| War Stories | ext4 O_PONIES, overlayfs CVE-2023-0386, the btrfs write hole |
Choosing a filesystem
| Filesystem | Use case | Key feature |
|---|---|---|
| ext4 | General purpose, servers | Stable, journaled |
| btrfs | Desktop, NAS | CoW, snapshots, RAID (0/1/10) |
| xfs | High-performance servers | High scalability |
| tmpfs | /tmp, /run, shared memory | RAM-backed, fast |
| overlayfs | Container images | Union of layers |
| squashfs | Read-only (LiveCD, containers) | Compressed, read-only |
| erofs | Android, embedded | Compressed, fast read |
Further reading
- Kernel docs: filesystems — per-filesystem and VFS reference
- VFS · page cache · block layer — the layers above and below