diff --git a/check.c b/check.c index c2ee95d3..545024cb 100644 --- a/check.c +++ b/check.c @@ -89,6 +89,106 @@ static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_bl return 0; } +/** + * Combinations of 1 of 4 elements. + */ +static const struct combo1 { + int a; +} COMBO1[] = { + { 0 }, + { 1 }, + { 2 }, + { 3 }, +}; + +/** + * Combinations of 2 of 4 elements. + */ +static const struct combo2 { + int a; + int b; +} COMBO2[] = { + { 0, 1 }, + { 0, 2 }, + { 0, 3 }, + { 1, 2 }, + { 1, 3 }, + { 2, 3 } +}; + +/** + * Combinations of 3 of 4 elements. + */ +static const struct combo3 { + int a; + int b; + int c; +} COMBO3[] = { + { 0, 1, 2 }, + { 0, 1, 3 }, + { 0, 2, 3 }, + { 1, 2, 3 } +}; + +/** + * Combinations of 4 of 4 elements. + */ +static const struct combo4 { + int a; + int b; + int c; + int d; +} COMBO4[] = { + { 0, 1, 2, 4 }, +}; + +/** + * Checks if the hash of at least one failed block is now matching. + */ +static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, unsigned char** buffer, unsigned char* buffer_zero) +{ + unsigned j; + int hash_checked; + + hash_checked = 0; /* keep track if we check at least one block */ + + /* check if the recovered blocks are OK */ + for(j=0;jlevel, buffer, diskmax, state->block_size); + return 1; + } + + return 0; +} + +/** + * Checks if specified parity is now matching with a recomputed one. + */ +static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, unsigned i, unsigned char** buffer, unsigned char** buffer_recov) +{ + /* recompute parity, note that we don't need parity over i */ + raid_gen(i + 1, buffer, diskmax, state->block_size); + + /* if the recovered parity block matches */ + if (memcmp(buffer[diskmax+i], buffer_recov[i], state->block_size) == 0) { + /* recompute all the redundancy information */ + raid_gen(state->level, buffer, diskmax, state->block_size); + return 1; + } + + return 0; +} + /** * Repair errors. * Returns <0 if failure for missing strategy, >0 if data is wrong and we cannot rebuild correctly, 0 on success. @@ -96,13 +196,9 @@ static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_bl */ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, unsigned char** buffer, unsigned char** buffer_recov, unsigned char* buffer_zero) { - unsigned j; - int error = 0; + unsigned i, j; + int error; int has_hash; - int hash_checked; - unsigned char* buffer_parity = buffer_recov[0]; - unsigned char* buffer_qarity = buffer_recov[1]; - unsigned char* buffer_rarity = buffer_recov[2]; /* no fix required */ if (failed_count == 0) { @@ -111,6 +207,8 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u return 0; } + error = 0; + /* check if there is at least a failed block that can be checked for correctness using the hash */ /* if there isn't, we have to sacrifice a parity block to check that the result is correct */ has_hash = 0; @@ -119,288 +217,160 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u has_hash = 1; } - /* RAID5 recovering from p */ - if (failed_count == 1 && buffer_parity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax], buffer_parity, state->block_size); + /* recover one block with hash */ + if (failed_count == 1 && has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO1[i].a], buffer_recov[COMBO1[i].a], state->block_size); - hash_checked = 0; /* keep track if we check at least one block */ + /* recover data */ + raid_recov_1data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, COMBO1[i].a, buffer_zero); - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:parity: Data error\n", pos); - ++error; } - /* RAID6 recovering from q, ignoring p */ - if (failed_count == 1 && buffer_qarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); + /* recover one block without hash */ + if (failed_count == 1 && !has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO2[i].a], buffer_recov[COMBO2[i].a], state->block_size); - hash_checked = 0; /* keep track if we check at least one block */ + /* recover data */ + raid_recov_1data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, COMBO2[i].a, buffer_zero); - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:q-parity: Data error\n", pos); - ++error; } - /* RAID6 recovering from q, using p to check the result */ - if (failed_count == 1 && buffer_parity != 0 && buffer_qarity != 0 && !has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); + /* recover two blocks with hash */ + if (failed_count == 2 && has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO2[i].a], buffer_recov[COMBO2[i].a], state->block_size); + memcpy(buffer[diskmax+COMBO2[i].b], buffer_recov[COMBO2[i].b], state->block_size); - /* recompute parity with RAID5 to get p from it */ - raid_gen(1, buffer, diskmax, state->block_size); + /* recover data */ + raid_recov_2data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO2[i].a, COMBO2[i].b, buffer_zero); - /* if the recovered parity block matches */ - if (memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0) { - /* recompute all the redundancy information */ - raid_gen(state->level, buffer, diskmax, state->block_size); - return 0; + if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero)) + return 0; + + fprintf(stdlog, "error:%u:%s/%s: Data error\n", pos, lev_config_name(COMBO2[i].a), lev_config_name(COMBO2[i].b)); + ++error; } - - fprintf(stdlog, "error:%u:parity/q-parity: Data error\n", pos); - ++error; } - /* RAID6 recovering from p+q */ - if (failed_count == 2 && buffer_parity != 0 && buffer_qarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax], buffer_parity, state->block_size); - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); + /* recover two blocks without hash */ + if (failed_count == 2 && !has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO3[i].a], buffer_recov[COMBO3[i].a], state->block_size); + memcpy(buffer[diskmax+COMBO3[i].b], buffer_recov[COMBO3[i].b], state->block_size); - hash_checked = 0; /* keep track if we check at least one block */ + /* recover data */ + raid_recov_2data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO3[i].a, COMBO3[i].b, buffer_zero); - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:parity/q-parity: Data error\n", pos); - ++error; } - /* RAIDTP recovering from r, ignoring p,q */ - if (failed_count == 1 && buffer_rarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); + /* recover three blocks with hash */ + if (failed_count == 3 && has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO3[i].a], buffer_recov[COMBO3[i].a], state->block_size); + memcpy(buffer[diskmax+COMBO3[i].b], buffer_recov[COMBO3[i].b], state->block_size); + memcpy(buffer[diskmax+COMBO3[i].c], buffer_recov[COMBO3[i].c], state->block_size); - hash_checked = 0; /* keep track if we check at least one block */ + /* recover data */ + raid_recov_3data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, COMBO3[i].a, COMBO3[i].b, COMBO3[i].c, buffer_zero); - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:rarity: Data error\n", pos); - ++error; } - /* RAIDTP recovering from r, using p or q to check the result */ - if (failed_count == 1 && (buffer_parity != 0 || buffer_qarity != 0) && buffer_rarity != 0 && !has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); + /* recover three blocks without hash */ + if (failed_count == 3 && !has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO4[i].a], buffer_recov[COMBO4[i].a], state->block_size); + memcpy(buffer[diskmax+COMBO4[i].b], buffer_recov[COMBO4[i].b], state->block_size); + memcpy(buffer[diskmax+COMBO4[i].c], buffer_recov[COMBO4[i].c], state->block_size); - /* recompute parity with RAID6 to get p and q from it */ - raid_gen(2, buffer, diskmax, state->block_size); + /* recover data */ + raid_recov_3data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, COMBO4[i].a, COMBO4[i].b, COMBO4[i].c, buffer_zero); - /* if the recovered parity block matches */ - if ((buffer_parity != 0 && memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0) - || (buffer_qarity != 0 && memcmp(buffer[diskmax+1], buffer_qarity, state->block_size) == 0) - ) { - /* recompute all the redundancy information */ - raid_gen(state->level, buffer, diskmax, state->block_size); - return 0; + if (is_parity_matching(state, diskmax, COMBO4[i].d, buffer, buffer_recov)) + return 0; + + fprintf(stdlog, "error:%u:%s/%s/%s/%s: Data error\n", pos, lev_config_name(COMBO4[i].a), lev_config_name(COMBO4[i].b), lev_config_name(COMBO4[i].c), lev_config_name(COMBO4[i].d)); + ++error; } - - fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos); - ++error; } - /* RAIDTP recovering from p+r */ - if (failed_count == 2 && buffer_parity != 0 && buffer_rarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax], buffer_parity, state->block_size); - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); + /* recover four blocks with hash */ + if (failed_count == 4 && has_hash) { + for(i=0;iblock_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero); + /* copy the redundancy to use */ + memcpy(buffer[diskmax+COMBO4[i].a], buffer_recov[COMBO4[i].a], state->block_size); + memcpy(buffer[diskmax+COMBO4[i].b], buffer_recov[COMBO4[i].b], state->block_size); + memcpy(buffer[diskmax+COMBO4[i].c], buffer_recov[COMBO4[i].c], state->block_size); + memcpy(buffer[diskmax+COMBO4[i].d], buffer_recov[COMBO4[i].d], state->block_size); - hash_checked = 0; /* keep track if we check at least one block */ + /* recover data */ + raid_recov_4data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, failed[failed_map[3]].index, COMBO4[i].a, COMBO4[i].b, COMBO4[i].c, COMBO4[i].d, buffer_zero); - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:parity/rarity: Data error\n", pos); - ++error; - } - - /* RAIDTP recovering from q+r */ - if (failed_count == 2 && buffer_qarity != 0 && buffer_rarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); - - /* recover 2 data */ - raidTP_recov_2datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero); - - hash_checked = 0; /* keep track if we check at least one block */ - - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:qarity/rarity: Data error\n", pos); - ++error; - } - - /* RAIDTP recovering from q+r, using p to check the result */ - if (failed_count == 2 && buffer_parity != 0 && buffer_qarity != 0 && buffer_rarity != 0 && !has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); - - /* recover data */ - raidTP_recov_2datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero); - - /* recompute parity with RAID5 to get p from it */ - raid_gen(1, buffer, diskmax, state->block_size); - - /* if the recovered parity block matches */ - if (buffer_parity != 0 && memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0) { - /* recompute all the redundancy information */ - raid_gen(state->level, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos); - ++error; - } - - /* RAIDTP recovering from p+q+r */ - if (failed_count == 3 && buffer_parity != 0 && buffer_qarity != 0 && buffer_rarity != 0 && has_hash) { - /* copy the redundancy to use */ - memcpy(buffer[diskmax], buffer_parity, state->block_size); - memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); - memcpy(buffer[diskmax+2], buffer_rarity, state->block_size); - - /* recover 3 data */ - raidTP_recov_3data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, buffer_zero); - - hash_checked = 0; /* keep track if we check at least one block */ - - /* check if the recovered blocks are OK */ - for(j=0;jlevel, buffer, diskmax, state->block_size); - return 0; - } - - fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos); - ++error; } /* return the number of failed attempts, or -1 if no strategy */ @@ -914,19 +884,12 @@ static int state_check_process(struct snapraid_state* state, int check, int fix, unsigned char* buffer_zero; /* buffers for parity read and not computed */ - if (state->level == 1) { - buffer_recov[0] = buffer[diskmax + 1]; - buffer_recov[1] = 0; - buffer_recov[2] = 0; - } else if (state->level == 2) { - buffer_recov[0] = buffer[diskmax + 2]; - buffer_recov[1] = buffer[diskmax + 3]; - buffer_recov[2] = 0; - } else { - buffer_recov[0] = buffer[diskmax + 3]; - buffer_recov[1] = buffer[diskmax + 4]; - buffer_recov[2] = buffer[diskmax + 5]; - } + for(l=0;llevel;++l) + buffer_recov[l] = buffer[diskmax + state->level + l]; + for(;l