Clarify physical_reach_size semantics and non-contiguous parity holes left by fix

This commit is contained in:
Andrea Mazzoleni 2026-09-08 11:55:11 +02:00
parent 34ac9a08e0
commit 76dd3d28ea
3 changed files with 33 additions and 15 deletions

View file

@ -3078,6 +3078,15 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b
*/
partial = blockstart != 0 || blockcount != 0;
/*
* Fix is best-effort on each stripe. If a stripe cannot be recovered, it
* is left unfixed and processing continues with the following stripes.
* Consequently, repaired parity is not guaranteed to be contiguous: an
* unrecoverable stripe may remain as a hole between successfully repaired
* stripes. This is intentional, as stopping would prevent recovery of all
* subsequent recoverable stripes.
*/
blockmax = parity_allocated_size(state);
size = blockmax * (data_off_t)state->block_size;

View file

@ -135,11 +135,11 @@ void parity_physical_reach_size(struct snapraid_parity_handle* handle, data_off_
/*
* Compute the physical reach of the logical parity layout across splits.
*
* physical_reach_size represents physical file presence, not parity validity.
* Parity is not necessarily contiguous or valid within this range: blocks
* may be invalid, stale, unwritten, or modified. Correctness is determined
* per-block by the content state and validated independently via data hashes
* and recomputation.
* physical_reach_size represents physical file presence, not parity validity
* or contiguity. Invalid, stale, unwritten, or sparse stripes may exist below
* this boundary, for example when fix cannot recover a stripe but continues
* with later ones. Such holes are allowed and need not be represented in the
* content state.
*
* For split parity, splits concatenate linearly. If an earlier split is
* physically shorter than its logical size, logical offsets beyond that
@ -218,8 +218,9 @@ int parity_create(struct snapraid_parity_handle* handle, const struct snapraid_p
* physical_reach_size does NOT imply that parity below this offset is valid.
* Parity may be invalid, stale, unwritten, or sparse in any region below
* this boundary. It only records the physical EOF boundary for I/O and
* truncation. Parity validity is tracked independently per block through
* the content state, data hashes, and recomputation.
* truncation. The physical contents below this boundary are not necessarily
* described by the content state. Recovery correctness is validated
* independently using data hashes and recomputation.
*
* While a handle is open, physical_reach_size can intentionally differ from EOF.
* In particular, preallocation may extend EOF without extending physical_reach_size.
@ -810,8 +811,9 @@ int parity_open(struct snapraid_parity_handle* handle, const struct snapraid_par
* physical_reach_size does NOT imply that parity below this offset is valid.
* Parity may be invalid, stale, unwritten, or sparse in any region below
* this boundary. It only records the physical EOF boundary for I/O and
* truncation. Parity validity is tracked independently per block through
* the content state, data hashes, and recomputation.
* truncation. The physical contents below this boundary are not necessarily
* described by the content state. Recovery correctness is validated
* independently using data hashes and recomputation.
*
* While a handle is open, physical_reach_size can intentionally differ from EOF.
* In particular, preallocation may extend EOF without extending physical_reach_size.

View file

@ -30,10 +30,15 @@ struct snapraid_split_handle {
*
* This is the physical truncation and read limit of the split.
*
* physical_reach_size does NOT indicate that data below this offset is valid parity.
* Parity may be invalid, stale, unwritten, or sparse at any point below this
* boundary. Correctness is tracked independently per block in the content state
* and verified through data hashes and recomputation.
* physical_reach_size does NOT indicate that data below this offset is
* valid or contiguous parity. Parity may be invalid, stale, unwritten,
* or sparse at any point below this boundary. In particular, fix may
* leave an unrecoverable stripe unfixed and continue repairing later
* stripes, intentionally leaving holes inside the physical reach.
*
* Such holes are allowed and are not necessarily represented in the
* content state. Recovery correctness is validated independently using
* the stored data hashes or the available recovery validation.
*
* Data beyond physical_reach_size is disposable/preallocated and discarded by
* parity_truncate(). A completed write advances physical_reach_size to the end of
@ -118,8 +123,10 @@ void parity_size(struct snapraid_parity_handle* handle, data_off_t* out_size);
* If the physical_reach_size of an earlier split is smaller than its logical size,
* later splits cannot extend the logical offset past that missing region.
*
* Parity within this range is not necessarily valid or synchronized;
* per-block validity is tracked independently in the content state.
* Parity within this range is not necessarily valid, synchronized, or contiguous.
* In particular, fix may leave an unrecoverable stripe unfixed and continue with
* later stripes, so holes inside this range are allowed and are not necessarily
* represented in the content state.
*
* For example, with two 100 GiB splits, if the first has physical_reach_size 90 GiB
* and the second has physical_reach_size 100 GiB, the result is 90 GiB, not 190 GiB.