Skip to content

Analyses / Incremental recovery

Keeping Btrfs incremental backups recoverable

18 October 2026 / Source review and controlled parser experiment

Keep an unchanged replication base, inventory every external clone source, and isolate the destination while receiving. A readable delta stream and a matching UUID are not a replacement for those conditions or for a restore test.

The backup includes its dependencies

A Btrfs send stream carries reconstruction commands. With -p, the receiver starts from a previously sent parent; -c makes additional snapshots available for cloning. Without an explicit parent, send may choose one from those clone sources. All snapshots participating in send must have their subvolume read-only property set; a read-only mount alone is insufficient. [1]

Retention follows references, not filenames. Our practical inference: a stored incremental stream is usable only with the state it actually references. Keeping every chronological predecessor is not the rule; preserving a reconstructible parent and the external clone sources used by the stream is. Audit dependencies before pruning either snapshots or archived streams.

Documented modes and their recovery implications, not measured outcomes.
InputWhat must surviveAcceptance check
Full sendThe complete data-bearing streamReceive and validate the intended subvolume
Incremental sendStream, matching parent and referenced external clone sourcesResolve dependencies and test reconstruction
--no-dataMetadata description onlyReject as a data-recovery copy

The last mode deliberately omits file payload and cannot transfer file-data changes. Clone sources must represent identical state on both sides. [1] This dependency inventory complements the existing snapshot and backup comparison.

A received UUID identifies lineage, not content

The destination has its own subvolume UUID. received_uuid records the source identity used for replication. The stream format represents snapshot and clone references with UUID and transaction fields; it is abstracted from disk structures, and destination inode numbers can differ. Replication should not be described as a sector image. [3] [4]

There is a consequential implementation detail. In btrfs-progs v6.17, search_source_subvol first searches received UUIDs, then falls back to local UUIDs. It passes a transaction argument into subvol_uuid_search. [5] However, the latter function's body does not use its transid parameter: it looks up the UUID, reads root metadata and resolves a path. It performs no file-content comparison. [6, subvol_uuid_search]

Therefore, describing that lookup as a verified UUID-and-generation match overstates the code. This is a narrow source observation, not a claim that all receiver or kernel checks are absent, nor a demonstrated corruption case. Record identity metadata, but do not treat it as a content digest.

Restore into a working copy, not the replication base

Btrfs documents safeguards introduced in btrfs-progs 5.14.2 for switching a received snapshot to writable state. Older writable subvolumes carrying a received identity can still exist. Installing newer tools does not establish that those historical objects are valid bases. Nested subvolumes are another inventory boundary: their contents are excluded from a parent snapshot, and even the empty snapshot stubs are omitted by send. [3]

The pinned prop_read_only implementation refuses a read-only-to-writable switch with a received UUID unless forced. Its forced path changes the flag and then attempts to clear the identity. A reset failure emits a warning; that branch does not turn the function's final return into failure. [7] This is source inspection, not an observed ioctl failure.

Our operational conclusion is to keep the received base unchanged and create a separate writable snapshot for recovery work. Do not use force as routine error handling. When investigating a prior state change, retain warnings and inspect the resulting flags and identity, rather than inferring them from the requested action.

Stream validation stops before restoration

The v6.17 stream reader recomputes each command's CRC32C with its checksum field zeroed, rejects mismatches, and checks attribute boundaries. These are checks of command representation, not an end-to-end comparison with the source filesystem. [8]

btrfs receive --dump selects printing callbacks instead of do_receive. [5] It leaves the filesystem unchanged. [2] Our inference is that a successful dump cannot establish that a target possesses the necessary parent, has enough space, or reconstructs an application's usable state.

There is also a trust boundary. The receive manual warns against untrusted streams: crafted incremental instructions can create references to files elsewhere in the same filesystem. It recommends protecting trusted streams in transit. [2, BUGS] A command CRC is not sender authentication. A trusted transport and a restore test address different requirements.

A checksum pass does not prove a complete backup

We constructed six small protocol-v1 streams and passed each once to btrfs receive --dump, using the installed btrfs-progs v6.6.3 on Linux. This does not apply commands to a destination. The original Python harness builds the command bytes and records their SHA-256, stdout, stderr and exit status. This runtime is distinct from the v6.17 source review above.

Six synthetic parser controls. Exit 0 means dump accepted these bytes, not that files were restored.
Controlled inputBytesExit status
Six payload bytes in one WRITE command1560
Whole WRITE removed; all remaining command checksums unchanged1130
One payload bit flipped without updating its command checksum1561
END command removed1461
Last payload byte and END absent1451
Synthetic parent UUID; no destination or parent provided to dump1120

The bit flip, incomplete command and missing END were rejected. Yet removing the entire WRITE command left a stream that passed: no surviving command's CRC needed changing. Its dump listed file creation but no write. This is a deliberately different command sequence, not evidence of spontaneous data loss. Per-command checksums cannot determine which operations the sender originally intended.

The synthetic snapshot reference also passed without supplying any destination or parent. This directly demonstrates the scope of dump mode, not a failure of the real receive path. A parent-availability or restored-content claim would need a separate filesystem experiment.

For archived streams, our recommendation is to retain a trusted whole-stream digest and byte length, check the producer's completion status, then test reconstruction and application invariants. A digest computed only after an incomplete transfer cannot prove what was omitted. Authentication protects the expected digest; none of these checks replaces the others.

Exact observations and environment / Original reproduction harness. Run python3 btrfs-dump-controls.py --verify btrfs-dump-controls.json; a differing runtime can produce different output and must be reported separately. No storage devices or elevated privileges are required.

Read-only at the end does not protect the whole receive

In finish_subvol, receive records source identity and then sets BTRFS_SUBVOL_RDONLY. [5] The manual explicitly warns that other writers can alter the destination during reception, leaving a final read-only subvolume that differs from the sender. Protect that location until reception completes. [2]

For an acceptance record, retain the tool and kernel versions, stream provenance, parent/clone inventory, completion status, warnings and final subvolume identity. Then validate the recovered files and application. These are our recommended checks, not a claim that any particular backup has passed them. See backup verification and application recovery for the separate acceptance criteria.

Application recovery needs its own check

Pillai et al.'s OSDI 2014 study separates filesystem persistence behavior from application correctness. BOB explores reordered block traces; ALICE tests modeled crash states against workload-specific checker scripts. Neither is a Btrfs send validator. [9, sections 2.2 and 3.1-3.6]

Our transfer of that method is modest: define the application invariant before accepting a restore, such as matching committed records and their referenced files. A parser pass, or even a matching byte copy, cannot establish that an originally inconsistent application state has become usable. We did not reproduce ALICE, and its historical results are not current Btrfs failure rates.

Evidence and limits

This synthesis reviews eight upstream files at btrfs-progs v6.17, commit c03e29f083678f47a7d7d810db4d21d905979b77. No Btrfs volume was mounted, modified or restored. The separate six-case experiment exercised only the installed v6.6.3 dump parser with synthetic byte streams; no crash, real receive or filesystem fault injection was executed. Distribution changes and later versions need separate review.

The source manifest and SHA-256 records with source locations identify the reviewed inputs. The capture script verifies those bytes when run with --verify beside the two JSON files. It does not validate filesystem behavior. Compare the distinct Btrfs mount-time replay boundary before interpreting any read-only recovery claim.

Primary sources

  1. btrfs-progs v6.17: send modes and clone-source preconditions
  2. btrfs-progs v6.17: receive validation, write window and trust warning
  3. btrfs-progs v6.17: received identity, 5.14.2 safeguards and nested subvolumes
  4. btrfs-progs v6.17: send stream format and reference fields
  5. btrfs-progs v6.17: source lookup, receive finalization and dump dispatch
  6. btrfs-progs v6.17: subvol_uuid_search implementation
  7. btrfs-progs v6.17: prop_read_only and UUID reset error handling
  8. btrfs-progs v6.17: per-command CRC and attribute decoding
  9. Pillai et al., All File Systems Are Not Created Equal, OSDI 2014, sections 2.2 and 3.1-3.6

Published by Hesela team · Published · Updated

Report a correction · Editorial standards · Reuse guidance

Change history

: first published.

: latest recorded update. A detailed change note is not available for this revision.

New-publication RSS · Correction RSS

Cite this page
Hesela team. Keeping Btrfs incremental backups recoverable. Hesela. 2026-10-18. https://hesela.com/analyses/btrfs-incremental-send-recovery-contract/