diff --git a/cmdline/check.c b/cmdline/check.c index f2ccfe2c..2a9e3482 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -988,23 +988,33 @@ static int repair(struct snapraid_state* state, int rehash, block_off_t pos, uns if (!mem_is_zero(buffer[failed[j].index], state->block_size)) current_generation_proven = 1; } else { - size_t pos_size; + unsigned hash_kind; - pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size); + hash_kind = rehash ? state->prevhash : state->hash; /* * The stored CHG hash identifies OLD contents under the recovery invariant. * Generation detection must compare hash identity only and must not apply the - * CURRENT logical-size padding requirement. With MuseAir the hash covers the - * complete canonical RAID block, so a mismatch proves candidate != OLD and - * therefore identifies this validated RAID solution as CURRENT. + * CURRENT logical-size padding requirement. * - * Legacy hashes still depend on the logical size used to compute the hash. - * The OLD logical size is not available here, so size-changing CHG blocks may - * remain subject to the historical ambiguity with Murmur3/Spooky2. + * When the hash covers the complete canonical RAID block (memhash_is_block()), + * candidate identity is independent of logical size: a mismatch proves + * candidate != OLD and therefore identifies this validated RAID solution as CURRENT. + * + * For hashes that depend on the logical size (Murmur3/Spooky2), the OLD + * logical size is not available. Because a mismatch against CURRENT pos_size + * cannot distinguish candidate != OLD from a change in file size, the + * conservative approach keeps the generation ambiguous and does not treat + * the mismatch as proof of CURRENT. */ - if (block_hash_cmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index]) != 0) - current_generation_proven = 1; + if (memhash_is_block(hash_kind)) { + size_t pos_size; + + pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size); + + if (block_hash_cmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index]) != 0) + current_generation_proven = 1; + } } } diff --git a/cmdline/util.c b/cmdline/util.c index f018761a..ee94480e 100644 --- a/cmdline/util.c +++ b/cmdline/util.c @@ -779,29 +779,19 @@ void memhash_block(unsigned kind, const unsigned char* seed, void* digest, const { assert(logical_size <= block_size); - switch (kind) { - case HASH_MUSEAIR : + if (memhash_is_block(kind)) { /* - * MuseAir defines the identity of the complete canonical RAID - * block. The caller must have zero-padded the buffer after the - * logical end of the file block. + * Canonical block hashes define the identity of the complete + * canonical RAID block. The caller must have zero-padded the + * buffer after the logical end of the file block. */ memhash(kind, seed, digest, src, block_size); - break; - case HASH_MURMUR3 : - case HASH_SPOOKY2 : + } else { /* * Preserve the historical SnapRAID hash semantics for content * files using legacy hashes. */ memhash(kind, seed, digest, src, logical_size); - break; - default : - /* LCOV_EXCL_START */ - log_fatal(EINTERNAL, "Internal inconsistency in hash function %u\n", kind); - exit(EXIT_FAILURE); - break; - /* LCOV_EXCL_STOP */ } } diff --git a/cmdline/util.h b/cmdline/util.h index e5ff13a6..3b6a3abb 100644 --- a/cmdline/util.h +++ b/cmdline/util.h @@ -282,6 +282,19 @@ void memhash(unsigned kind, const unsigned char* seed, void* digest, const void* */ void memhash_block(unsigned kind, const unsigned char* seed, void* digest, const void* src, size_t logical_size, size_t block_size); +/** + * Return !=0 if the hash kind operates on the complete canonical RAID block. + * + * Legacy hashes (Murmur3, Spooky2) preserve historical semantics and hash + * only the logical file bytes. + * Modern hashes (MuseAir) hash the complete zero-padded RAID block up to + * block_size. + */ +static inline int memhash_is_block(unsigned kind) +{ + return kind == HASH_MUSEAIR; +} + /** * Return the hash name. */