Filesystems · Journaling and Copy-on-Write
Metadata-Only Journaling and the Three ext3 Data Modes
What the data=journal, data=ordered, and data=writeback modes each guarantee about file contents after a crash, and why the ordered mode costs a dependency the other two avoid.
A journal that records only metadata blocks is doing less work than one that records data as well, and the difference is not merely a factor of two in bytes written. It changes what a crash can leave behind in a file’s contents while leaving every structural invariant intact. The three modes ext3 popularized, data journalled, data ordered, and data writeback, are three points on the spectrum between “no guarantee about contents” and “contents are as recoverable as metadata”, and the middle point is the default for a reason worth understanding precisely.
Metadata consistency is not data consistency
Suppose a process appends 8 KiB to a file. The filesystem allocates two data blocks, extends the inode’s size by 8192 bytes, and adds two block pointers. Under metadata-only journaling the inode and the allocation bitmap are recorded in the journal; the two data blocks are written directly to their final locations and are not journalled.
If a crash occurs after the transaction commits but before the data blocks reach the disk, recovery replays the metadata and the file’s size becomes the new, larger value. The block pointers are valid and the allocation is correct. But the two blocks at those addresses hold whatever was there before, either zeros from a freshly formatted region or the previous contents of blocks that were freed and reused. The file now has a hole-shaped region of stale data inside its valid length. Every structural invariant holds; the contents are wrong.
This outcome is not a bug in the journal. It is the direct consequence of the journal’s scope, and it is why POSIX does not promise that a crash preserves the contents of a file that was being written without an explicit synchronization. What POSIX does require is that metadata operations which completed before the crash are visible afterwards, and that the filesystem is mountable and structurally sound. A file whose tail contains garbage from before the crash is within the contract as long as the operation that extended it was not itself synchronized.
The three modes differ in what they do about that tail.
Data journalling: contents in the log
The strongest option records data blocks in the journal alongside the metadata. The transaction for the append contains the bitmap, the inode, and both data blocks. If the commit record is durable, all four are replayed and the file contains the new bytes; if it is not, none of them are, and the file is exactly as it was. There is no window in which the size has grown but the contents are stale.
The cost is straightforward: every byte written to a file passes through the journal before reaching its final location, so the write amplification for file data is 2x on top of the metadata amplification. For a workload that writes large files sequentially, the journal becomes the bottleneck and its capacity must be large enough to hold the in-flight data, which is a poor fit for the ring-buffer design, since a large streaming write can consume the entire journal in one transaction and force a stall. This mode is rarely the default for that reason, and it is chosen when the contents of a file must survive a crash with the same guarantee as the directory entry that names it.
Ordered mode: barrier between data and commit
The default in ext3 and ext4 is a compromise. Metadata goes through the journal. Data blocks bypass the journal and are written directly. But before a transaction’s commit record is issued, every data block belonging to that transaction must be durable.
The mechanism is a dependency, not a copy. When the append’s transaction is prepared, the filesystem knows which data blocks the transaction’s metadata refers to. It issues those data writes, then a barrier, then the commit record. The barrier guarantees that the data blocks are on media before the commit can be. Recovery therefore cannot find an inode whose size was extended by a committed transaction while the corresponding data blocks are still absent, because if the data blocks were absent, the commit could not have happened.
The guarantee this produces is narrower than data journalling’s and stronger than writeback’s: a file that was extended by a committed transaction has its new data, but data written in place without changing the file’s size or allocation, which is to say overwriting an existing block, receives no guarantee at all, because no metadata changed and therefore no transaction and no barrier was involved. Ordered mode protects the mapping from logical offsets to blocks, not the contents at those offsets.
The cost is the barrier. Every committing transaction waits for the data writes of every file operation it covers. Under a mixed workload of many small metadata operations plus a few large data writes, the large writes can delay the commit of unrelated metadata transactions that happen to be batched with them, and the reverse: a metadata transaction can force a flush of dirty data pages it does not need. Implementations mitigate this by tracking which pages a transaction actually depends on and forcing only those, but the dependency remains.
Writeback mode: no ordering at all
The third mode journals metadata and writes data whenever the page cache decides to write it. No barrier links the two. A committed transaction can extend a file’s size while the data blocks that back the extension are still dirty in memory, so after a crash the file can have the new length, valid block pointers, and stale contents.
Writeback mode is the fastest of the three because it introduces no additional barriers and lets the
writeback of data proceed on its own schedule. It is also the mode that produces the widest class of
post-crash content anomalies: files with garbage tails, files whose length is larger than the data
written, and files whose data was partially persisted in an order unrelated to the metadata. On a
filesystem whose only consumer is a database that maintains its own journal, the weaker guarantee is
acceptable, because the database’s recovery does not trust the file contents anyway. For general use
it means that a crash can silently corrupt a file that was being written without an fsync.
What the mode does not change
Two properties are identical across all three modes. The first is structural: recovery always produces
a filesystem whose allocation bitmaps, inode link counts, and directory entries agree, because all
three modes journal every metadata block that participates in those structures. The second is that
fsync overrides the mode entirely. A caller that synchronizes a file gets its data and metadata
durable regardless of which mode the filesystem is mounted in, at the cost of the flush itself.
That second property is the reason the mode question matters less than it appears. A program that needs its data to survive a crash must call the synchronization primitive; a program that does not call it has no guarantee in any mode. The mode determines what happens to data that was in flight without an explicit request, which is a large class of writes. The page cache writes back dirty pages on its own timer, and a machine that loses power between two writeback passes can lose anything written since the last one. Ordered mode narrows the resulting damage from “any dirty data” to “no data whose metadata committed”, which is the practical meaning of its status as default.
The dependency direction and why it cannot be reversed
The ordering in ordered mode is data before commit, never commit before data. The reversed order would be useless: a commit record durable before the data blocks means a crash can replay the metadata while the data is still missing, which is exactly the writeback failure. The forward order is what makes recovery safe.
There is a subtler reason the direction is fixed. Suppose the filesystem could delay the data write until after the commit and still claim ordered semantics by re-issuing the data write during recovery. That would require the journal to contain the data, which is data journalling. Without a copy of the data in the log, recovery has no way to complete a missing data write; the only safe action is to prevent the metadata from committing until the data is already there. The three modes are therefore not arbitrary policy settings but the three possible answers to “where does the data copy live”: in the journal, in a forced-before-commit write, or nowhere.
Ext4 extends the same reasoning to delayed allocation, where blocks are not assigned until writeback time. There, the ordering constraint is enforced at the point of allocation: a transaction that records the allocation cannot commit until the data for the allocated blocks is written. The commit record’s role is unchanged; only the moment at which the filesystem learns which blocks the transaction depends on moves later. The barrier semantics that make the ordering enforceable are a property of the storage stack rather than the filesystem, and a device that ignores flushes collapses all three modes to writeback behaviour.
References
- ext4 Documentation — Journal (jbd2) — Linux kernel documentation
- ext4 Documentation — Overview of the ext4 filesystem — Linux kernel documentation
- Operating Systems: Three Easy Pieces — Crash Consistency: FSCK and Journaling — Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau