mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Merges NEW and CHG blocks and introduces REP blocks.
This is a preparation for using REP blocks for containing hashes of new data.
This commit is contained in:
parent
e14966eef1
commit
ba1d6ed2b6
6 changed files with 376 additions and 235 deletions
209
check.c
209
check.c
|
|
@ -34,7 +34,8 @@
|
|||
*/
|
||||
struct failed_struct {
|
||||
/**
|
||||
* If the block needs to be recovered and rewritten to the disk.
|
||||
* If we know for sure that the block is garbage or missing
|
||||
* and it needs to be recovered and rewritten to the disk.
|
||||
*/
|
||||
int is_bad;
|
||||
|
||||
|
|
@ -102,7 +103,11 @@ static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned d
|
|||
|
||||
/* check if the recovered blocks are OK */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
|
||||
/* if we are expected to recover this block */
|
||||
if (!failed[failed_map[j]].is_outofdate
|
||||
/* if the block has a hash to check */
|
||||
&& block_has_updated_hash(failed[failed_map[j]].block)
|
||||
) {
|
||||
hash_checked = 1;
|
||||
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
|
||||
break;
|
||||
|
|
@ -173,7 +178,11 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
|
|||
/* if there isn't, we have to sacrifice a parity block to check that the result is correct */
|
||||
has_hash = 0;
|
||||
for(i=0;i<failed_count;++i) {
|
||||
if (block_has_updated_hash(failed[failed_map[i]].block)) /* if the block can be checked */
|
||||
/* if we are expected to recover this block */
|
||||
if (!failed[failed_map[i]].is_outofdate
|
||||
/* if the block has a hash to check */
|
||||
&& block_has_updated_hash(failed[failed_map[i]].block)
|
||||
)
|
||||
has_hash = 1;
|
||||
}
|
||||
|
||||
|
|
@ -274,18 +283,26 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
error = 0;
|
||||
|
||||
/* if nothing failed, just recompute the parity */
|
||||
if (failed_count == 0) {
|
||||
raid_gen(diskmax, state->level, state->block_size, buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Here we have to try two different strategies to recover, because in case the 'sync' */
|
||||
/* process is aborted, we don't know if the parity data is really updated just like after 'sync', */
|
||||
/* or if it still represents the state before the 'sync'. */
|
||||
|
||||
/* As first, we assume that the parity is already computed for the current state */
|
||||
/* Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks */
|
||||
/* and the two strategies are identical */
|
||||
|
||||
/* As first, we assume that the parity IS updated for the current state */
|
||||
/* and that we are going to recover the state after the last 'sync'. */
|
||||
/* This is the normal condition. */
|
||||
/* In this case, parity contains correct info for BLK, CHG (new version) and NEW blocks, */
|
||||
/* and not for DELETED ones. */
|
||||
/* In this case, parity contains info from BLK, REP and CHG blocks, */
|
||||
/* but not for DELETED. */
|
||||
/* We need to put in the recovering process only the bad blocks, because all the */
|
||||
/* others already contains the correct data read frin disk, and the parity is correctly computed for them. */
|
||||
/* We are interested to recover BLK, CHG and NEW blocks if they are marked as bad, */
|
||||
/* others already contains the correct data read from disk, and the parity is correctly computed for them. */
|
||||
/* We are interested to recover BLK, REP and CHG blocks if they are marked as bad, */
|
||||
/* but we are not interested in DELETED ones. */
|
||||
|
||||
n = 0;
|
||||
|
|
@ -296,22 +313,14 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
assert(block_state != BLOCK_STATE_DELETED); /* we cannot have bad DELETED blocks */
|
||||
|
||||
if (block_state == BLOCK_STATE_BLK) { /* if we have the hash for it */
|
||||
/* try to fetch the block using the hash */
|
||||
if (state_import_fetch(state, rehash, failed[j].block->hash, buffer[failed[j].index]) == 0) {
|
||||
/* we have corrected it! */
|
||||
} else {
|
||||
/* otherwise try to recover it */
|
||||
failed_map[n] = j;
|
||||
++n;
|
||||
|
||||
/* we have something to try to recover */
|
||||
something_to_recover = 1;
|
||||
}
|
||||
/* if we have the hash for it */
|
||||
if ((block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP)
|
||||
/* try to fetch the block using the known hash */
|
||||
&& state_import_fetch(state, rehash, failed[j].block->hash, buffer[failed[j].index]) == 0
|
||||
) {
|
||||
/* we already have corrected it! */
|
||||
} else {
|
||||
assert(block_state == BLOCK_STATE_CHG || block_state == BLOCK_STATE_NEW);
|
||||
|
||||
/* otherwise it's CHG or NEW and we try to recover it */
|
||||
/* otherwise try to recover it */
|
||||
failed_map[n] = j;
|
||||
++n;
|
||||
|
||||
|
|
@ -330,7 +339,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
|
||||
if (ret == 0) {
|
||||
/* reprocess the blocks for NEW and CHG ones, for which we don't have a hash to check */
|
||||
/* reprocess the CHG blocks, for which we don't have a hash to check */
|
||||
/* if they were BAD we have to use some euristics to ensure that we have recovered */
|
||||
/* the state after the sync. If unsure, we assume the worst case */
|
||||
|
||||
|
|
@ -339,7 +348,22 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
if (failed[j].is_bad) {
|
||||
unsigned block_state = block_state_get(failed[j].block);
|
||||
|
||||
if (block_state == BLOCK_STATE_NEW) {
|
||||
/* BLK and REP blocks are always OK, because at this point */
|
||||
/* we have already checked their hash */
|
||||
if (block_state != BLOCK_STATE_CHG) {
|
||||
assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* for CHG blocks we have to 'guess' if they are correct or not */
|
||||
|
||||
/* if the hash is invalid we cannot check the result */
|
||||
/* this could happen if we have lost this information */
|
||||
/* after an aborted sync */
|
||||
if (hash_is_invalid(failed[j].block->hash)) {
|
||||
/* it may contain garbage */
|
||||
failed[j].is_outofdate = 1;
|
||||
} else if (hash_is_zero(failed[j].block->hash)) {
|
||||
/* if the block is not filled with 0, we are sure to have */
|
||||
/* restored it to the state after the 'sync' */
|
||||
/* instead, if the block is filled with 0, it could be either that the */
|
||||
|
|
@ -349,14 +373,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
/* it may contain garbage */
|
||||
failed[j].is_outofdate = 1;
|
||||
}
|
||||
} else if (block_state == BLOCK_STATE_CHG) {
|
||||
/* if the hash is a bogus value we cannot check the result */
|
||||
/* this could happen if we have lost this information */
|
||||
/* after an aborted sync */
|
||||
if (memcmp(failed[j].block->hash, buffer_zero, HASH_SIZE) == 0) {
|
||||
/* it may contain garbage */
|
||||
failed[j].is_outofdate = 1;
|
||||
} else
|
||||
} else {
|
||||
/* if the hash is different than the previous one, we are sure to have */
|
||||
/* restored it to the state after the 'sync' */
|
||||
/* instead, if the hash matches, it could be either that the */
|
||||
|
|
@ -375,14 +392,13 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
if (ret > 0)
|
||||
error += ret;
|
||||
|
||||
/* Now assume that the parity computation was not updated at the current state, */
|
||||
/* Now assume that the parity IS NOT updated at the current state, */
|
||||
/* but still represent the state before the last 'sync' process. */
|
||||
/* This may happen only if a 'sync' is aborted by a crash or manually. */
|
||||
/* In this case, parity contains info for BLK, CHG (old version) and DELETED blocks, */
|
||||
/* but not for CHG (new version) and NEW ones. */
|
||||
/* In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks, */
|
||||
/* but not for REP (new version) and CHG (new version). */
|
||||
/* We are interested to recover BLK ones marked as bad, */
|
||||
/* but we are not interested to recover NEW and CHG (new version) blocks, even if marked as bad, */
|
||||
/* because we don't have parity for them and it's just impossible, */
|
||||
/* but we are not interested to recover CHG (new version) and REP (new version) blocks, */
|
||||
/* even if marked as bad, because we don't have parity for them and it's just impossible, */
|
||||
/* and we are not interested to recover DELETED ones. */
|
||||
n = 0;
|
||||
something_to_recover = 0; /* keep track if there is at least one block to fix */
|
||||
|
|
@ -391,16 +407,30 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
if (block_state == BLOCK_STATE_DELETED
|
||||
|| block_state == BLOCK_STATE_CHG
|
||||
|| block_state == BLOCK_STATE_REP
|
||||
) {
|
||||
/* If the block is CHG or DELETED, we don't have the original content of block, */
|
||||
/* If the block is CHG, REP or DELETED, we don't have the original content of block, */
|
||||
/* and we must try to recover it. */
|
||||
/* This apply to CHG blocks even if they are not marked bad, */
|
||||
/* This apply to CHG and REP blocks even if they are not marked bad, */
|
||||
/* because the parity is computed with old content, and not with the new one. */
|
||||
/* Note that this recovering is done just to make possible to recover any other BLK one, */
|
||||
/* we are not really interested in DELETED and CHG (old version) ones. */
|
||||
/* we are not really interested in DELETED, CHG (old version) and REP (old version). */
|
||||
|
||||
if (block_state == BLOCK_STATE_CHG
|
||||
&& hash_is_zero(failed[j].block->hash)
|
||||
) {
|
||||
/* If the block was a ZERO block, restore it to the original 0 as before the 'sync' */
|
||||
/* We do this to just allow recovering of other BLK ones */
|
||||
|
||||
memset(buffer[failed[j].index], 0, state->block_size);
|
||||
/* note that from now the buffer is definitively lost */
|
||||
/* we can do this only because it's the last retry of recovering */
|
||||
|
||||
/* try to fetch the old block using the old hash for CHG and DELETED blocks */
|
||||
} else if ((block_state == BLOCK_STATE_CHG || block_state == BLOCK_STATE_DELETED)
|
||||
&& hash_is_real(failed[j].block->hash)
|
||||
&& state_import_fetch(state, rehash, failed[j].block->hash, buffer[failed[j].index]) == 0) {
|
||||
|
||||
/* try to fetch the old block using the old hash */
|
||||
if (state_import_fetch(state, rehash, failed[j].block->hash, buffer[failed[j].index]) == 0) {
|
||||
/* note that from now the buffer is definitively lost */
|
||||
/* we can do this only because it's the last retry of recovering */
|
||||
} else {
|
||||
|
|
@ -416,18 +446,6 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
/* note that if the block is not marked as is_bad, */
|
||||
/* we are not going to write it in the disk */
|
||||
failed[j].is_outofdate = 1;
|
||||
} else if (block_state == BLOCK_STATE_NEW) {
|
||||
/* If the block is NEW, restore it to the original 0 as before the 'sync' */
|
||||
/* Also in this case, we do this to just allow recovering of other BLK ones */
|
||||
|
||||
memset(buffer[failed[j].index], 0, state->block_size);
|
||||
/* note that from now the buffer is definitively lost */
|
||||
/* we can do this only because it's the last retry of recovering */
|
||||
|
||||
/* mark that we have restored an old state */
|
||||
/* note that if the block is not marked as is_bad, */
|
||||
/* we are not going to write it in the disk */
|
||||
failed[j].is_outofdate = 1;
|
||||
} else if (failed[j].is_bad) {
|
||||
/* If the block is bad we don't know its content, and we try to recover it */
|
||||
/* At this point, we can have only BLK ones */
|
||||
|
|
@ -436,7 +454,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
/* we have something we are interested to recover */
|
||||
something_to_recover = 1;
|
||||
|
||||
|
||||
/* we try to recover it */
|
||||
failed_map[n] = j;
|
||||
++n;
|
||||
|
|
@ -447,7 +465,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
if (something_to_recover) {
|
||||
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
|
||||
if (ret == 0) {
|
||||
/* we alreay marked as outdated NEW and CHG blocks, we don't need to do it again */
|
||||
/* we alreay marked as outdated CHG and REP blocks, we don't need to do it again */
|
||||
return 0;
|
||||
}
|
||||
if (ret > 0)
|
||||
|
|
@ -608,6 +626,8 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
|
|||
/* mark the parity as invalid, and don't try to check/fix it */
|
||||
/* because it will be recomputed at the next sync */
|
||||
valid_parity = 0;
|
||||
|
||||
/* follow */
|
||||
}
|
||||
|
||||
/* if the block is DELETED */
|
||||
|
|
@ -714,7 +734,7 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
|
|||
read_size = handle_read(&handle[j], block, buffer[j], state->block_size, stdlog);
|
||||
if (read_size == -1) {
|
||||
/* save the failed block for the check/fix */
|
||||
failed[failed_count].is_bad = 1;
|
||||
failed[failed_count].is_bad = 1; /* it's bad because we cannot read it */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].block = block;
|
||||
|
|
@ -728,11 +748,13 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
|
|||
|
||||
countsize += read_size;
|
||||
|
||||
/* if the block is potentially not updated */
|
||||
if (block_state == BLOCK_STATE_NEW || block_state == BLOCK_STATE_CHG) {
|
||||
/* store it in the failed set, because potentially */
|
||||
/* the parity may be still computed with the previous content */
|
||||
failed[failed_count].is_bad = 0;
|
||||
/* always insert CHG blocks, the repair functions needs all of them */
|
||||
/* because the parity may be still referring at the old state */
|
||||
/* and the repair must be aware of it */
|
||||
if (block_state == BLOCK_STATE_CHG) {
|
||||
/* we DO NOT mark them as bad to avoid to overwrite them with wrong data. */
|
||||
/* if we don't have a hash, we always assume the first read of the block correct. */
|
||||
failed[failed_count].is_bad = 0; /* we assume the CHG block correct */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].block = block;
|
||||
|
|
@ -741,29 +763,44 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* if the block has the hash */
|
||||
if (block_state == BLOCK_STATE_BLK) {
|
||||
/* compute the hash of the block just read */
|
||||
if (rehash) {
|
||||
memhash(state->prevhash, state->prevhashseed, hash, buffer[j], read_size);
|
||||
} else {
|
||||
memhash(state->hash, state->hashseed, hash, buffer[j], read_size);
|
||||
}
|
||||
assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
|
||||
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_SIZE) != 0) {
|
||||
/* save the failed block for the check/fix */
|
||||
failed[failed_count].is_bad = 1;
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].block = block;
|
||||
failed[failed_count].handle = &handle[j];
|
||||
++failed_count;
|
||||
/* compute the hash of the block just read */
|
||||
if (rehash) {
|
||||
memhash(state->prevhash, state->prevhashseed, hash, buffer[j], read_size);
|
||||
} else {
|
||||
memhash(state->hash, state->hashseed, hash, buffer[j], read_size);
|
||||
}
|
||||
|
||||
fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, block_file_get(block)->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_SIZE) != 0) {
|
||||
/* save the failed block for the check/fix */
|
||||
failed[failed_count].is_bad = 1; /* it's bad because the hash doesn't match */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].block = block;
|
||||
failed[failed_count].handle = &handle[j];
|
||||
++failed_count;
|
||||
|
||||
fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, block_file_get(block)->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* always insert REP blocks, the repair functions needs all of them */
|
||||
/* because the parity may be still referring at the old state */
|
||||
/* and the repair must be aware of it */
|
||||
if (block_state == BLOCK_STATE_REP) {
|
||||
failed[failed_count].is_bad = 0; /* it's not bad */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].block = block;
|
||||
failed[failed_count].handle = &handle[j];
|
||||
++failed_count;
|
||||
|
||||
fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, block_file_get(block)->sub, block_file_pos(block));
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
132
elem.h
132
elem.h
|
|
@ -77,19 +77,15 @@ struct snapraid_filter {
|
|||
#define BLOCK_STATE_BLK 1
|
||||
|
||||
/**
|
||||
* The block is new and not yet hashed, and it's using a space previously empty.
|
||||
* The block is new and not yet hashed.
|
||||
* This happens when a new block overwrite a just removed block, or an empty space.
|
||||
*
|
||||
* The block hash field IS NOT set.
|
||||
* The parity for this disk is not updated, but it contains 0.
|
||||
*/
|
||||
#define BLOCK_STATE_NEW 2
|
||||
|
||||
/**
|
||||
* The block is new and not yet hashed, and it's using the space of a block not existing anymore.
|
||||
* This happens when a new block overwrite a just removed block.
|
||||
* The block hash field IS set but it may be also INVALID or ZERO,
|
||||
* and it represents the hash of the OLD data.
|
||||
*
|
||||
* If the block was empty, the hash is set to the special ZERO value.
|
||||
* If the block was lost, the hash is set to the special INVALID value.
|
||||
*
|
||||
* The block hash field IS set, and it represents the hash of the previous data,
|
||||
* but only if it's different by all 0.
|
||||
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
|
||||
*
|
||||
* If the state is read from an incomplete sync, we don't really know if the hash is referring at the
|
||||
|
|
@ -101,7 +97,16 @@ struct snapraid_filter {
|
|||
* Check and fix are instead able to work with unsynched hashes.
|
||||
* Scrub is not affected because it ignores CHG/DELETED blocks.
|
||||
*/
|
||||
#define BLOCK_STATE_CHG 3
|
||||
#define BLOCK_STATE_CHG 2
|
||||
|
||||
/**
|
||||
* The block is new and hashed.
|
||||
* This happens when a new block overwrite a just removed block, or an empty space.
|
||||
*
|
||||
* The block hash field IS set, and it represents the hash of the NEW data.
|
||||
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
|
||||
*/
|
||||
#define BLOCK_STATE_REP 3
|
||||
|
||||
/**
|
||||
* This block is a deleted one.
|
||||
|
|
@ -462,42 +467,83 @@ static inline void block_state_set(struct snapraid_block* block, unsigned state)
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified block is valid and has an updated hash.
|
||||
* Checks if the specified block has an updated hash.
|
||||
*
|
||||
* Note that EMPTY / CHG / NEW / DELETED return 0.
|
||||
* Note that EMPTY / CHG / DELETED return 0.
|
||||
*/
|
||||
static inline int block_has_updated_hash(const struct snapraid_block* block)
|
||||
{
|
||||
unsigned state = block_state_get(block);
|
||||
|
||||
return state == BLOCK_STATE_BLK;
|
||||
return state == BLOCK_STATE_BLK || state == BLOCK_STATE_REP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified block has any kind of hash.
|
||||
* Even a not updated one.
|
||||
* Checks if the specified block has an a past hash.
|
||||
*
|
||||
* Note that for DELETED / CHG with a NULL hash returns 0,
|
||||
* because in these cases the hash is lost.
|
||||
* Note that EMPTY / BLK / REP return 0.
|
||||
*/
|
||||
static inline int block_has_any_hash(const struct snapraid_block* block)
|
||||
static inline int block_has_past_hash(const struct snapraid_block* block)
|
||||
{
|
||||
unsigned state = block_state_get(block);
|
||||
|
||||
return state == BLOCK_STATE_CHG || state == BLOCK_STATE_DELETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified hash is invalid.
|
||||
*
|
||||
* An invalid hash is represented with all bytes at 0x00.
|
||||
*/
|
||||
static inline int hash_is_invalid(const unsigned char* hash)
|
||||
{
|
||||
unsigned block_state = block_state_get(block);
|
||||
unsigned i;
|
||||
|
||||
switch (block_state) {
|
||||
case BLOCK_STATE_BLK :
|
||||
return 1;
|
||||
case BLOCK_STATE_CHG :
|
||||
case BLOCK_STATE_DELETED :
|
||||
/* the hash is valid only if different than 0 */
|
||||
for(i=0;i<HASH_SIZE;++i)
|
||||
if (block->hash[i] != 0)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
for(i=0;i<HASH_SIZE;++i)
|
||||
if (hash[i] != 0x00)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified hash represent the zero block.
|
||||
*
|
||||
* A zero hash is represented with all bytes at 0xFF.
|
||||
*/
|
||||
static inline int hash_is_zero(const unsigned char* hash)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for(i=0;i<HASH_SIZE;++i)
|
||||
if (hash[i] != 0xFF)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified hash is a real hash.
|
||||
*/
|
||||
static inline int hash_is_real(const unsigned char* hash)
|
||||
{
|
||||
return !hash_is_zero(hash) && !hash_is_invalid(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hash to the special INVALID value.
|
||||
*/
|
||||
static inline void hash_invalid_set(unsigned char* hash)
|
||||
{
|
||||
memset(hash, 0x00, HASH_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hash to the special ZERO value.
|
||||
*/
|
||||
static inline void hash_zero_set(unsigned char* hash)
|
||||
{
|
||||
memset(hash, 0xFF, HASH_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -509,21 +555,8 @@ static inline int block_has_file(const struct snapraid_block* block)
|
|||
{
|
||||
unsigned state = block_state_get(block);
|
||||
|
||||
return state == BLOCK_STATE_BLK || state == BLOCK_STATE_NEW || state == BLOCK_STATE_CHG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified block has the same presence state,
|
||||
* meaning that it's now present when before it was also present,
|
||||
* or it's now absent and before it was also absent.
|
||||
*
|
||||
* Note that NEW / DELETED return 0.
|
||||
*/
|
||||
static inline int block_has_same_presence(const struct snapraid_block* block)
|
||||
{
|
||||
unsigned state = block_state_get(block);
|
||||
|
||||
return state == BLOCK_STATE_EMPTY || state == BLOCK_STATE_BLK || state == BLOCK_STATE_CHG;
|
||||
return state == BLOCK_STATE_BLK
|
||||
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -535,7 +568,8 @@ static inline int block_has_invalid_parity(const struct snapraid_block* block)
|
|||
{
|
||||
unsigned state = block_state_get(block);
|
||||
|
||||
return state == BLOCK_STATE_DELETED || state == BLOCK_STATE_NEW || state == BLOCK_STATE_CHG;
|
||||
return state == BLOCK_STATE_DELETED
|
||||
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
55
scan.c
55
scan.c
|
|
@ -170,24 +170,27 @@ static void scan_file_deallocate(struct snapraid_state* state, struct snapraid_d
|
|||
case BLOCK_STATE_BLK :
|
||||
/* we keep the hash making it an "old" hash, because the parity is still containing data for it */
|
||||
break;
|
||||
case BLOCK_STATE_NEW :
|
||||
/* NEW blocks don't have a hash, then we set it to the 'unspecified' 0 value */
|
||||
memset(block->hash, 0, HASH_SIZE);
|
||||
break;
|
||||
case BLOCK_STATE_CHG :
|
||||
/* if we have not already cleared the undeterminated hash */
|
||||
if (!state->clear_undeterminate_hash) {
|
||||
/* in these cases we don't know if the old state is still the one */
|
||||
/* stored inside the parity, because after an aborted sync, the parity */
|
||||
/* may be or may be not have been updated with the new data */
|
||||
/* Then we reset the hash to a bogus value */
|
||||
/* Note that this condition is possible only if: */
|
||||
/* - new files added/modified */
|
||||
/* - aborted sync, without saving the content file */
|
||||
/* - files deleted after the aborted sync */
|
||||
memset(block->hash, 0, HASH_SIZE);
|
||||
/* may be or may be not have been updated with the data that it's now */
|
||||
/* deleted. Then we reset the hash to a bogus value. */
|
||||
/* For example: */
|
||||
/* - One file is added */
|
||||
/* - Sync aborted after updating the parity to the new state, */
|
||||
/* but without saving the content file representing this new state. */
|
||||
/* - File is now deleted after the aborted sync */
|
||||
/* - Sync again, deleting the blocks (exactly here) */
|
||||
/* with the hash of CHG block not represeting the real parity state */
|
||||
hash_invalid_set(block->hash);
|
||||
}
|
||||
break;
|
||||
case BLOCK_STATE_REP :
|
||||
/* we just don't know the old hash, and then we set it to invalid */
|
||||
hash_invalid_set(block->hash);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Internal state inconsistency in scanning for block %u state %u\n", block->parity_pos, block_state);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -205,7 +208,7 @@ static void scan_file_deallocate(struct snapraid_state* state, struct snapraid_d
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is completely formed of NEW or CHG blocks,
|
||||
* Checks if a file is completely formed of NEW/CHG/REP blocks,
|
||||
* and if it has at least one block.
|
||||
*/
|
||||
static int file_is_full_invalid(struct snapraid_file* file)
|
||||
|
|
@ -226,7 +229,7 @@ static int file_is_full_invalid(struct snapraid_file* file)
|
|||
|
||||
block_state = block_state_get(block);
|
||||
|
||||
if (block_state != BLOCK_STATE_NEW && block_state != BLOCK_STATE_CHG)
|
||||
if (block_state != BLOCK_STATE_CHG && block_state != BLOCK_STATE_REP)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -290,6 +293,7 @@ static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk*
|
|||
block_max = tommy_arrayblk_size(&disk->blockarr);
|
||||
for(i=0;i<file->blockmax;++i) {
|
||||
struct snapraid_block* block;
|
||||
unsigned block_state;
|
||||
|
||||
/* find a free block */
|
||||
while (block_pos < block_max && block_has_file(tommy_arrayblk_get(&disk->blockarr, block_pos)))
|
||||
|
|
@ -307,12 +311,19 @@ static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk*
|
|||
/* block to overwrite */
|
||||
block = tommy_arrayblk_get(&disk->blockarr, block_pos);
|
||||
|
||||
/* state of the block */
|
||||
block_state = block_state_get(block);
|
||||
|
||||
/* if the block is an empty one */
|
||||
if (block == BLOCK_EMPTY) {
|
||||
/* we just overwrite it with a NEW one */
|
||||
block_state_set(&file->blockvec[i], BLOCK_STATE_NEW);
|
||||
if (block_state == BLOCK_STATE_EMPTY) {
|
||||
block_state_set(&file->blockvec[i], BLOCK_STATE_CHG);
|
||||
|
||||
/* the block was empty and filled with zeros */
|
||||
/* set the hash to the special ZERO value */
|
||||
hash_zero_set(file->blockvec[i].hash);
|
||||
} else {
|
||||
/* otherwise it's a DELETED one */
|
||||
assert(block_state == BLOCK_STATE_DELETED);
|
||||
|
||||
/* if we have not already cleared the undeterminated hash */
|
||||
if (!state->clear_undeterminate_hash) {
|
||||
|
|
@ -320,11 +331,13 @@ static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk*
|
|||
/* stored inside the parity, because after an aborted sync, the parity */
|
||||
/* may be or may be not have been updated with the new data */
|
||||
/* Then we reset the hash to a bogus value */
|
||||
/* Note that this condition is possible only if: */
|
||||
/* - files are deleted */
|
||||
/* - aborted sync, without saving the content file */
|
||||
/* - files are readded after the aborted sync */
|
||||
memset(block->hash, 0, HASH_SIZE);
|
||||
/* For example: */
|
||||
/* - One file is deleted */
|
||||
/* - Sync aborted after, updating the parity to the new state, */
|
||||
/* but without saving the content file representing this new state. */
|
||||
/* - Another file is added again (exactly here) */
|
||||
/* with the hash of DELETED block not represeting the real parity state */
|
||||
hash_invalid_set(block->hash);
|
||||
}
|
||||
|
||||
block_state_set(&file->blockvec[i], BLOCK_STATE_CHG);
|
||||
|
|
|
|||
11
scrub.c
11
scrub.c
|
|
@ -182,6 +182,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
silent_error_on_this_block = 0;
|
||||
|
||||
/* if all the blocks at this address are synched */
|
||||
/* if not, parity is not even checked */
|
||||
block_is_unsynched = 0;
|
||||
|
||||
/* if we have to use the old hash */
|
||||
|
|
@ -195,6 +196,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
int file_is_unsynched;
|
||||
|
||||
/* if the file on this disk is synched */
|
||||
/* if not, silent errors are assumed as expected error */
|
||||
file_is_unsynched = 0;
|
||||
|
||||
/* by default no rehash in case of "continue" */
|
||||
|
|
@ -215,6 +217,15 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
continue;
|
||||
}
|
||||
|
||||
/* if the block is unsynched, errors are expected */
|
||||
if (block_has_invalid_parity(block)) {
|
||||
/* report that the block and the file are not synched */
|
||||
block_is_unsynched = 1;
|
||||
file_is_unsynched = 1;
|
||||
|
||||
/* follow */
|
||||
}
|
||||
|
||||
/* if the file is different than the current one, close it */
|
||||
if (handle[j].file != 0 && handle[j].file != block_file_get(block)) {
|
||||
/* keep a pointer at the file we are going to close for error reporting */
|
||||
|
|
|
|||
87
state.c
87
state.c
|
|
@ -1088,8 +1088,10 @@ static void state_read_text(struct snapraid_state* state, const char* path, STRE
|
|||
sungetc(c, f);
|
||||
}
|
||||
|
||||
if (strcmp(tag, "blk") == 0 || strcmp(tag, "new") == 0 || strcmp(tag, "chg") == 0) {
|
||||
/* "blk"/""new"/"chg" command */
|
||||
if (strcmp(tag, "blk") == 0 || strcmp(tag, "new") == 0
|
||||
|| strcmp(tag, "chg") == 0 || strcmp(tag, "rep") == 0
|
||||
) {
|
||||
/* "blk"/""new"/"chg"/"rep" command */
|
||||
block_off_t v_pos;
|
||||
struct snapraid_block* block;
|
||||
|
||||
|
|
@ -1126,11 +1128,15 @@ static void state_read_text(struct snapraid_state* state, const char* path, STRE
|
|||
paritymax = v_pos + 1;
|
||||
break;
|
||||
case 'n' :
|
||||
block_state_set(block, BLOCK_STATE_NEW);
|
||||
/* deprecated NEW blocks are converted to CHG ones */
|
||||
block_state_set(block, BLOCK_STATE_CHG);
|
||||
break;
|
||||
case 'c' :
|
||||
block_state_set(block, BLOCK_STATE_CHG);
|
||||
break;
|
||||
case 'p' :
|
||||
block_state_set(block, BLOCK_STATE_REP);
|
||||
break;
|
||||
}
|
||||
|
||||
block->parity_pos = v_pos;
|
||||
|
|
@ -1139,7 +1145,7 @@ static void state_read_text(struct snapraid_state* state, const char* path, STRE
|
|||
if (v_pos + 1 > blockmax)
|
||||
blockmax = v_pos + 1;
|
||||
|
||||
/* read the hash only for 'blk/chg', and not for 'new' */
|
||||
/* read the hash only for 'blk/chg/rep', and not for 'new' */
|
||||
if (tag[0] != 'n') {
|
||||
c = sgetc(f);
|
||||
if (c != ' ') {
|
||||
|
|
@ -1147,17 +1153,23 @@ static void state_read_text(struct snapraid_state* state, const char* path, STRE
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* set the hash only if present */
|
||||
ret = sgethex(f, block->hash, HASH_SIZE);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* set the ZERO hash for deprecated NEW blocks */
|
||||
hash_zero_set(block->hash);
|
||||
}
|
||||
|
||||
/* clear undeterminated hashes before a sync */
|
||||
if (tag[0] != 'b' && state->clear_undeterminate_hash) {
|
||||
memset(block->hash, 0, HASH_SIZE);
|
||||
}
|
||||
/* if the block contains a hash of past data */
|
||||
/* and we are clearing such undeterminated hashes */
|
||||
if (state->clear_undeterminate_hash
|
||||
&& block_has_past_hash(block)
|
||||
) {
|
||||
/* set the hash value to INVALID */
|
||||
hash_invalid_set(block->hash);
|
||||
}
|
||||
|
||||
/* we must not overwrite existing blocks */
|
||||
|
|
@ -1281,9 +1293,10 @@ static void state_read_text(struct snapraid_state* state, const char* path, STRE
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* clear undeterminated hashes before a sync */
|
||||
/* if we are clearing undeterminated hashes */
|
||||
if (state->clear_undeterminate_hash) {
|
||||
memset(deleted->block.hash, 0, HASH_SIZE);
|
||||
/* set the hash value to INVALID */
|
||||
hash_invalid_set(deleted->block.hash);
|
||||
}
|
||||
|
||||
/* we must not overwrite existing blocks */
|
||||
|
|
@ -1997,12 +2010,12 @@ static void state_write_text(struct snapraid_state* state, STREAM* f)
|
|||
case BLOCK_STATE_BLK :
|
||||
sputsl("blk ", f);
|
||||
break;
|
||||
case BLOCK_STATE_NEW :
|
||||
sputsl("new ", f);
|
||||
break;
|
||||
case BLOCK_STATE_CHG :
|
||||
sputsl("chg ", f);
|
||||
break;
|
||||
case BLOCK_STATE_REP :
|
||||
sputsl("rep ", f);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Internal state inconsistency in saving for block %u state %u\n", block->parity_pos, block_state);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -2010,10 +2023,8 @@ static void state_write_text(struct snapraid_state* state, STREAM* f)
|
|||
|
||||
sputu32(block->parity_pos, f);
|
||||
|
||||
if (block_state != BLOCK_STATE_NEW) {
|
||||
sputc(' ', f);
|
||||
sputhex(block->hash, HASH_SIZE, f);
|
||||
}
|
||||
sputc(' ', f);
|
||||
sputhex(block->hash, HASH_SIZE, f);
|
||||
|
||||
sputeol(f);
|
||||
if (serror(f)) {
|
||||
|
|
@ -2383,11 +2394,15 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
paritymax = v_pos + 1;
|
||||
break;
|
||||
case 'n' :
|
||||
block_state_set(block, BLOCK_STATE_NEW);
|
||||
/* deprecated NEW blocks are converted to CHG ones */
|
||||
block_state_set(block, BLOCK_STATE_CHG);
|
||||
break;
|
||||
case 'g' :
|
||||
block_state_set(block, BLOCK_STATE_CHG);
|
||||
break;
|
||||
case 'p' :
|
||||
block_state_set(block, BLOCK_STATE_REP);
|
||||
break;
|
||||
default:
|
||||
decoding_error(path, f);
|
||||
fprintf(stderr, "Invalid block type!\n");
|
||||
|
|
@ -2396,18 +2411,25 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
|
||||
block->parity_pos = v_pos;
|
||||
|
||||
/* read the hash only for 'blk/chg', and not for 'new' */
|
||||
/* read the hash only for 'blk/chg/rep', and not for 'new' */
|
||||
if (c != 'n') {
|
||||
/* set the hash only if present */
|
||||
ret = sread(f, block->hash, HASH_SIZE);
|
||||
if (ret < 0) {
|
||||
decoding_error(path, f);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* set the ZERO hash for deprecated NEW blocks */
|
||||
hash_zero_set(block->hash);
|
||||
}
|
||||
|
||||
/* clear undeterminated hashes before a sync */
|
||||
if (c != 'b' && state->clear_undeterminate_hash)
|
||||
memset(block->hash, 0, HASH_SIZE);
|
||||
/* if the block contains a hash of past data */
|
||||
/* and we are clearing such undeterminated hashes */
|
||||
if (state->clear_undeterminate_hash
|
||||
&& block_has_past_hash(block)
|
||||
) {
|
||||
/* set the hash value to INVALID */
|
||||
hash_invalid_set(block->hash);
|
||||
}
|
||||
|
||||
/* we must not overwrite existing blocks */
|
||||
|
|
@ -2573,9 +2595,10 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* clear undeterminated hashes before a sync */
|
||||
/* if we are clearing undeterminated hashes */
|
||||
if (state->clear_undeterminate_hash) {
|
||||
memset(deleted->block.hash, 0, HASH_SIZE);
|
||||
/* set the hash value to INVALID */
|
||||
hash_invalid_set(deleted->block.hash);
|
||||
}
|
||||
|
||||
/* we must not overwrite existing blocks */
|
||||
|
|
@ -3092,12 +3115,12 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
case BLOCK_STATE_BLK :
|
||||
sputc('b', f);
|
||||
break;
|
||||
case BLOCK_STATE_NEW :
|
||||
sputc('n', f);
|
||||
break;
|
||||
case BLOCK_STATE_CHG :
|
||||
sputc('g', f);
|
||||
break;
|
||||
case BLOCK_STATE_REP :
|
||||
sputc('p', f);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Internal state inconsistency in saving for block %u state %u\n", v_pos, block_state);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -3108,10 +3131,8 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f)
|
|||
v_count = end - begin;
|
||||
sputb32(v_count, f);
|
||||
|
||||
if (block_state != BLOCK_STATE_NEW) {
|
||||
for(b=begin;b<end;++b) {
|
||||
swrite(blockvec[b].hash, HASH_SIZE, f);
|
||||
}
|
||||
for(b=begin;b<end;++b) {
|
||||
swrite(blockvec[b].hash, HASH_SIZE, f);
|
||||
}
|
||||
|
||||
if (serror(f)) {
|
||||
|
|
|
|||
117
sync.c
117
sync.c
|
|
@ -209,6 +209,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
int read_size;
|
||||
unsigned char hash[HASH_SIZE];
|
||||
struct snapraid_block* block;
|
||||
unsigned block_state;
|
||||
|
||||
/* by default no rehash in case of "continue" */
|
||||
rehandle[j].block = 0;
|
||||
|
|
@ -223,27 +224,31 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* get the block */
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
|
||||
/* if the block is NEW or DELETED, we have to update the parity */
|
||||
/* to include this block change */
|
||||
if (!block_has_same_presence(block)) {
|
||||
parity_needs_to_be_updated = 1;
|
||||
/* get the state of the block */
|
||||
block_state = block_state_get(block);
|
||||
|
||||
/* it's important to check this before any other check */
|
||||
/* because for DELETED block, we skip at the next check */
|
||||
/* now continue with processing */
|
||||
}
|
||||
|
||||
/* if the block is NEW, DELETED or CHG, */
|
||||
/* if the block has invalid parity, */
|
||||
/* we have to take care of it in case of recover */
|
||||
if (block_has_invalid_parity(block)) {
|
||||
/* store it in the failed set, because */
|
||||
/* the parity is still computed with the previous content */
|
||||
/* the parity may be still computed with the previous content */
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].size = state->block_size;
|
||||
failed[failed_count].block = block;
|
||||
++failed_count;
|
||||
|
||||
/* now continue with processing */
|
||||
/* if the block has invalid parity, we have to update the parity */
|
||||
/* to include this block change */
|
||||
/* This also apply to CHG blocks, but we are going to handle */
|
||||
/* later this case to do the updates only if really needed */
|
||||
if (block_state != BLOCK_STATE_CHG)
|
||||
parity_needs_to_be_updated = 1;
|
||||
|
||||
/* note that DELETE blocks are skipped in the next check */
|
||||
/* and we have to store them in the failed blocks */
|
||||
/* before skipping */
|
||||
|
||||
/* follow */
|
||||
}
|
||||
|
||||
/* if the block has no file, meanining that it's EMPTY or DELETED, */
|
||||
|
|
@ -361,34 +366,48 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
if (block_has_updated_hash(block)) {
|
||||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_SIZE) != 0) {
|
||||
fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, handle[j].file->sub, block_file_pos(block));
|
||||
fprintf(stderr, "Data error at file '%s' at position '%u'\n", handle[j].path, block_file_pos(block));
|
||||
fprintf(stderr, "WARNING! Unexpected data error in a data disk! The block is now marked as bad!\n");
|
||||
fprintf(stderr, "Try with 'snapraid -e fix' to recover!\n");
|
||||
/* if the file has invalid parity, it's a REP changed during the sync */
|
||||
if (block_has_invalid_parity(block)) {
|
||||
fprintf(stdlog, "error:%u:%s:%s: Unexpected change\n", i, handle[j].disk->name, handle[j].file->sub);
|
||||
fprintf(stderr, "WARNING! You cannot modify files during a sync.\n");
|
||||
fprintf(stderr, "Rerun the sync command when finished.\n");
|
||||
|
||||
/* save the failed block for the fix */
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].size = read_size;
|
||||
failed[failed_count].block = block;
|
||||
++failed_count;
|
||||
++error;
|
||||
|
||||
/* silent errors are very rare, and are not a signal that a disk */
|
||||
/* is going to fail. So, we just continue marking the block as bad */
|
||||
/* just like in scrub */
|
||||
++silent_error;
|
||||
silent_error_on_this_block = 1;
|
||||
continue;
|
||||
/* if the file is changed, it means that it was modified during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
error_on_this_block = 1;
|
||||
continue;
|
||||
} else { /* otherwise it's a BLK with silent error */
|
||||
fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, handle[j].file->sub, block_file_pos(block));
|
||||
fprintf(stderr, "Data error at file '%s' at position '%u'\n", handle[j].path, block_file_pos(block));
|
||||
fprintf(stderr, "WARNING! Unexpected data error in a data disk! The block is now marked as bad!\n");
|
||||
fprintf(stderr, "Try with 'snapraid -e fix' to recover!\n");
|
||||
|
||||
/* save the failed block for the fix */
|
||||
failed[failed_count].index = j;
|
||||
failed[failed_count].size = read_size;
|
||||
failed[failed_count].block = block;
|
||||
++failed_count;
|
||||
|
||||
/* silent errors are very rare, and are not a signal that a disk */
|
||||
/* is going to fail. So, we just continue marking the block as bad */
|
||||
/* just like in scrub */
|
||||
++silent_error;
|
||||
silent_error_on_this_block = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* if until now the parity doesn't need to be update */
|
||||
/* if until now the parity doesn't need to be updated */
|
||||
if (!parity_needs_to_be_updated) {
|
||||
/* for sure it's a CHG block, because EMPTY are processed before with "continue" */
|
||||
/* and BLK have "block_has_updated_hash()" as 1, and all the others */
|
||||
/* and BLK and REP have "block_has_updated_hash()" as 1, and all the others */
|
||||
/* have "parity_needs_to_be_updated" already at 1 */
|
||||
assert(block_state_get(block) == BLOCK_STATE_CHG);
|
||||
|
||||
/* if there is an hash */
|
||||
if (block_has_any_hash(block)) {
|
||||
if (hash_is_real(block->hash)) {
|
||||
/* check if the hash is changed */
|
||||
if (memcmp(hash, block->hash, HASH_SIZE) != 0) {
|
||||
/* the block is different, and we must update parity */
|
||||
|
|
@ -412,6 +431,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* if we have only silent errors we can try to fix them on-the-fly */
|
||||
if (!error_on_this_block && silent_error_on_this_block) {
|
||||
unsigned failed_mac;
|
||||
int something_to_recover = 0;
|
||||
|
||||
/* setup the blocks to recover */
|
||||
failed_mac = 0;
|
||||
|
|
@ -420,29 +440,35 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
unsigned char* block_copy = buffer[diskmax + state->level + failed[j].index];
|
||||
unsigned block_state = block_state_get(failed[j].block);
|
||||
|
||||
/* make a copy of new content just read */
|
||||
/* we try to recover only if at least one BLK is present */
|
||||
if (block_state == BLOCK_STATE_BLK)
|
||||
something_to_recover = 1;
|
||||
|
||||
/* save a copy of the content just read */
|
||||
/* that it's going to be overwritten by the recovering function */
|
||||
memcpy(block_copy, block_buffer, state->block_size);
|
||||
|
||||
if (block_state == BLOCK_STATE_NEW) {
|
||||
/* restore the data to parity state */
|
||||
if (block_state == BLOCK_STATE_CHG
|
||||
&& hash_is_zero(failed[j].block->hash)
|
||||
) {
|
||||
/* if the block was filled with 0, restore this state */
|
||||
/* and avoid to recover it */
|
||||
memset(block_buffer, 0, state->block_size);
|
||||
} else {
|
||||
/* if we have too many failure */
|
||||
if (failed_mac >= state->level) {
|
||||
/* we cannot recover */
|
||||
/* if we have too many failures, we cannot recover */
|
||||
if (failed_mac >= state->level)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* otherwise it has to be recovered */
|
||||
failed_map[failed_mac++] = failed[j].index;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have enough parity to fix */
|
||||
if (j == failed_count) {
|
||||
/* if we have something to recover and enough parity */
|
||||
if (something_to_recover && j == failed_count) {
|
||||
/* read the parity */
|
||||
/* we are sure that parity exists because a silent error */
|
||||
/* implies a BLK block with compute parity */
|
||||
/* we are sure that parity exists because */
|
||||
/* we have at least one BLK block */
|
||||
for(l=0;l<state->level;++l) {
|
||||
ret = parity_read(parity[l], i, buffer[diskmax+l], state->block_size, stdlog);
|
||||
if (ret == -1) {
|
||||
|
|
@ -485,10 +511,9 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
if (size < state->block_size)
|
||||
memset(block_buffer + size, 0, state->block_size - size);
|
||||
} else {
|
||||
/* otherwise restore the new content */
|
||||
/* because we are not interested in recovered */
|
||||
/* state for CHG and DELETED blocks */
|
||||
/* and we have to restore also NEW zeroed blocks */
|
||||
/* otherwise restore the content */
|
||||
/* because we are not interested in the old state */
|
||||
/* that it's recovered for CHG, REP and DELETED blocks */
|
||||
memcpy(block_buffer, block_copy, state->block_size);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue