From 3dfab5232c0fbd9200d1e76832ee83fbe39b8034 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 17 Aug 2026 23:03:28 +0200 Subject: [PATCH] Prevent partial fix from finalizing incomplete files --- cmdline/check.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/cmdline/check.c b/cmdline/check.c index 4de90be2..9afcf9f3 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -645,7 +645,7 @@ static int repair(struct snapraid_state* state, int rehash, block_off_t pos, uns * In such case, the check/fix command won't report any information of the * files partially checked. */ -static int file_post(struct snapraid_state* state, int fix, block_off_t i, struct snapraid_handle* handle, unsigned diskmax) +static int file_post(struct snapraid_state* state, int fix, int partial, block_off_t i, struct snapraid_handle* handle, unsigned diskmax) { unsigned j; int ret; @@ -701,6 +701,17 @@ static int file_post(struct snapraid_state* state, int fix, block_off_t i, struc } was_unrecoverable = handle[j].is_unrecoverable; + /* + * A partial fix operates only at block level. Reaching the last block of a + * file doesn't imply that the whole file was processed, so don't perform any + * file-level finalization, including marking it finished, promoting an + * .unrecoverable file, reporting it recovered, or restoring its timestamp. + * This is intentional also when an explicit block range happens to cover all + * the parity blocks, to keep -S/-B behavior independent of the parity size. + */ + if (partial) + goto close_and_continue; + /* * Mark that we finished with this file * to identify later any NOT finished ones @@ -971,7 +982,7 @@ static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct return 0; } -static int state_check_process(struct snapraid_state* state, int fix, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax) +static int state_check_process(struct snapraid_state* state, int fix, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax, int partial) { struct snapraid_handle* handle; unsigned diskmax; @@ -1616,7 +1627,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna } /* post process the files */ - ret = file_post(state, fix, i, handle, diskmax); + ret = file_post(state, fix, partial, i, handle, diskmax); if (ret == -1) { /* LCOV_EXCL_START */ log_fatal(errno, "Stopping at block %" PRIu64 "\n", i); @@ -2233,6 +2244,7 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b block_off_t blockmax; data_off_t size; int ret; + int partial; struct snapraid_parity_handle parity[LEV_MAX]; struct snapraid_parity_handle* parity_ptr[LEV_MAX]; unsigned process_error; @@ -2240,6 +2252,12 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b msg_progress("Initializing...\n"); + /* + * partial follows the explicit -S/-B request, not its clipped effective + * coverage; a nonzero -B remains partial even if it reaches the parity end. + */ + partial = blockstart != 0 || blockcount != 0; + blockmax = parity_allocated_size(state); size = blockmax * (data_off_t)state->block_size; @@ -2335,7 +2353,7 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b /* skip degenerated cases of empty parity, or skipping all */ if (blockstart < blockmax) { - ret = state_check_process(state, fix, parity_ptr, blockstart, blockmax); + ret = state_check_process(state, fix, parity_ptr, blockstart, blockmax, partial); if (ret == -1) { /* LCOV_EXCL_START */ ++process_error;