How File System Journaling Works: Ext4, Ordered Mode, and Why fsync Is Not Free
A database that calls fsync after a commit expects the bytes it wrote to survive a power cut. A filesystem that just renamed a temporary file over a permanent one expects the directory to point at the new inode, not a half-updated one. Neither promise is free. The mechanism that makes the second promise cheap enough to keep is a journal.
This article explains how filesystem journaling works, using ext4 and its JBD2 layer as the concrete case. It covers why a circular log of metadata changes is enough for crash consistency, the three data modes, how ordered mode interacts with the page cache, and why fsync still has to wait for more than a journal commit. It is not a second tour of the VFS objects, the page cache radix trees, the block layer request queues, or database write-ahead logging. Those live in other posts. The single search intent is how a journaling filesystem turns a multi-block metadata update into something that either fully survives a crash or does not appear at all.
Why journals exist
A filesystem update is rarely one block. Creating a file touches the directory, the inode table, the block bitmap, and the inode bitmap. Truncating a file touches the inode and the block bitmap. A crash that hits between those writes leaves the filesystem in a state no fsck can reliably repair without a full scan, and a full scan of a multi-terabyte volume takes hours.
The journal is a circular log, usually at a fixed location on the same device, that records the intent of those metadata changes before they are applied to their final locations. After a crash the recovery code replays the log up to the last complete commit. Incomplete transactions are discarded. The filesystem returns to a consistent metadata state in seconds instead of hours.
Data blocks are a separate question. Journaling every byte of user data doubles write traffic and destroys sequentiality. Most deployments therefore journal only metadata and use a mode that controls when data is allowed to hit disk relative to that metadata.
JBD2 and the transaction
ext4 does not implement its own journal format. It uses JBD2 (Journaling Block Device 2), a generic layer that can sit under any filesystem. A transaction is a set of metadata blocks that must appear atomically. The running transaction accepts new blocks until a timer, a size limit, or an explicit sync forces a commit.
A commit writes the journal records, then a commit block that marks the transaction complete. Only after that commit block is on stable storage is the transaction durable. Later, a checkpoint copies the metadata blocks from the journal (or from the in-memory copies) into their final locations and frees the journal space. Recovery walks the journal, applies every transaction that has a commit block, and ignores the rest.
Write barriers (or their modern equivalent, explicit flushes) sit between the journal data and the commit block, and again between the commit block and the checkpoint. Without them a drive with a volatile write cache can acknowledge the commit block while the preceding records are still only in DRAM. The next power loss loses the commit, or worse, leaves a commit that points at incomplete data.
The three data modes
ext4 exposes three mount options that change what the journal guarantees for user data:
- data=journal: both data and metadata go through the journal. After the commit block is durable the data is recoverable even if the final locations were never written. This is the strongest mode and the slowest. It also disables delayed allocation and O_DIRECT.
- data=ordered (the default): only metadata is journaled. Data blocks are written to their final locations before the metadata that references them is allowed to commit. A crash can lose the most recent data, but it cannot make a directory entry point at a block that still contains someone else's old content.
- data=writeback: only metadata is journaled, and data may be written after the metadata commits. This is the fastest mode. After a crash a file can contain a mixture of old and new bytes, or even old bytes from a previously deleted file.
Ordered mode is the practical compromise most systems ship with. The journal still makes metadata atomic. The ordering constraint prevents the classic "garbage in a newly allocated block" failure mode. The cost is that a metadata commit cannot finish until the related dirty data pages have been written out.
How ordered mode meets the page cache
A write() call copies bytes into the page cache and marks the pages dirty. Allocation may be delayed. The journal does not see those pages yet. Only when the filesystem decides to commit a transaction that includes the inode or the directory does ordered mode force the related data pages through writeback first.
That writeback uses the normal block layer path: bios are built, queued, and completed. The journal commit waits for those completions (and for the subsequent barrier) before it writes the commit block. Delayed allocation makes the window larger: blocks may not even be chosen until the writeback that ordered mode is forcing. Fast commits, present in recent ext4, shrink the amount of metadata that must be logged for some operations, but they do not remove the data-before-metadata rule in ordered mode.
fsync (or fdatasync) is the application's way of forcing this sequence now rather than waiting for the next periodic commit. It starts a transaction if needed, waits for the data pages (in ordered mode), waits for the journal commit, and waits for the device flush. That is why a tight loop of small writes followed by fsync is expensive: each call pays for at least one journal commit and one barrier, even if the data itself was already in the page cache.
What the journal does not promise
The journal guarantees metadata consistency after a crash. It does not guarantee that the most recent user data is present unless the application called fsync (or opened the file with O_SYNC). In ordered mode the data that is present is consistent with the metadata that points at it. In writeback mode even that is not true.
Copy-on-write filesystems such as btrfs and ZFS solve the same problem differently: they never overwrite live blocks and swing a root pointer atomically. They still need a durable record of intent; they simply keep that record in the form of the new tree rather than a separate journal. The search intent here remains the classic journal: a log of metadata changes that can be replayed.
Misconceptions
- Journaling means data is never lost. Only in data=journal mode, and only for data that has reached a committed transaction. Ordinary writes without fsync can still vanish.
- fsync only flushes the page cache. On a journaling filesystem it also forces the journal commit and the device barrier. Skipping either leaves a window.
- The journal is the same as a database WAL. Both are logs of intent. A filesystem journal is usually metadata-only and is checkpointed by the filesystem itself. A database WAL describes logical or physical row changes and is the source of truth until the next checkpoint. The mechanisms rhyme; the contracts differ.
- Ordered mode writes data into the journal. It does not. It only orders the data write relative to the metadata commit.
Takeaways
A journaling filesystem records metadata changes in a circular log before applying them to their final locations. Recovery replays complete transactions and discards incomplete ones, turning an hours-long fsck into a seconds-long replay.
ext4's default ordered mode journals only metadata but forces related data blocks to stable storage first. That prevents a directory from pointing at uninitialized blocks after a crash, at the cost of coupling data writeback to journal commits.
fsync is the call that makes the sequence happen immediately. It is not free because it must wait for data writeback, the journal commit block, and a device flush. Applications that need durability pay that cost; applications that only need metadata consistency can often avoid it.
Related reading: How the Linux VFS Works, How the Linux Page Cache Works, How the Linux Block Layer Works, and How Write-Ahead Logging Works.