How Do ZFS and Btrfs Ensure Atomic Snapshots?

Copy-on-write file systems such as ZFS and Btrfs guarantee atomic state transitions and instant snapshots by never overwriting live data in place. Instead of modifying existing disk blocks, they write updated data and metadata to newly allocated blocks, propagate those changes up a tree of pointers, and commit the entire transaction by atomically updating a single root pointer. Because old blocks remain untouched on disk until they are no longer referenced, capturing a point-in-time snapshot requires only preserving the current root pointer.

Never Overwriting Live Data

Traditional file systems often update data in place, which creates a risk of corruption if power is lost mid-write. To prevent this, they rely on write-ahead logging or journaling, writing changes twice: once to a journal and once to their final location.

Copy-on-write (CoW) file systems eliminate the need for a traditional journal by treating disk blocks as immutable once written. When an application modifies a 4 KB block inside a larger file, the file system allocates a fresh block elsewhere on the storage pool, writes the new contents there, and leaves the original block completely intact.

The Tree of Pointers and Root Swapping

Both ZFS and Btrfs organize all data and metadata into a hierarchical tree structure. In ZFS, this is a Merkle-like tree of block pointers rooted at an uberblock. In Btrfs, it is a forest of B-trees rooted at a superblock. Every parent node in the tree stores the physical addresses—and in ZFS, cryptographic checksums—of its child nodes.

When a leaf data block is written to a new location, its parent metadata block must be updated to point to the new address. Because metadata is also copy-on-write, the parent block is copied to a new location with the updated pointer. This cascading update continues all the way up the tree until it reaches the root:

\[\text{New Leaf} \rightarrow \text{New Indirect Block} \rightarrow \dots \rightarrow \text{New Root Pointer}\]

Until the final root pointer is updated, the storage pool's active state still points to the old tree. If a crash occurs at any point during the allocation or writing of the new blocks, the incomplete tree is simply ignored upon reboot, and the file system remains in its previous, consistent state.

Atomic State Transitions

To make state transitions atomic, the file system groups pending modifications into batches—called Transaction Groups (TXGs) in ZFS or transactions in Btrfs. All dirty data and metadata blocks for a transaction are flushed to disk first, followed by a storage barrier (cache flush) to ensure the blocks are physically persisted.

Once the new tree is safely on disk, the file system performs a single atomic write to update the active root pointer (the uberblock or superblock) to reference the new root. Because modern storage hardware guarantees that a single sector write of the root pointer either succeeds completely or fails without tearing, the transition from state \(S_t\) to state \(S_{t+1}\) is strictly atomic.

Instant Snapshots

Because the old tree's blocks are never overwritten during a transaction, creating a snapshot is nearly instantaneous and consumes almost no additional space initially. Taking a snapshot simply instructs the file system to preserve the current root pointer and prevent the garbage collector from freeing any blocks reachable from that root.

At the moment of creation, the snapshot and the live file system share 100% of their underlying data and metadata blocks. As the live file system continues to accept writes, copy-on-write allocates new blocks for modified data and updates the live root pointer, while the snapshot's root pointer continues to reference the original, unmodified blocks. Space is consumed only by the delta between the snapshot and the live state.

Crash Consistency and Integrity

By combining copy-on-write with end-to-end checksumming, ZFS and Btrfs also protect against silent data corruption. Every pointer in the tree stores a checksum of the block it references. When reading data, the file system verifies the checksum against the block's contents, ensuring that any bit rot or torn write is immediately detected rather than silently propagated.