mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Adds and new -A option to only check hashes.
This commit is contained in:
parent
06637e9264
commit
1078bd4500
9 changed files with 242 additions and 161 deletions
2
HISTORY
2
HISTORY
|
|
@ -3,6 +3,8 @@ SnapRAID HISTORY
|
|||
|
||||
1.13 2012/?
|
||||
===========
|
||||
* Added a new -A option to make the 'check' command to only check file hashes
|
||||
without checking the parity data.
|
||||
* The file modification time is now saved using nanosecond precision.
|
||||
This allows to restore the exact modification time in 'fix'.
|
||||
The new 'content' files written with this version are not backward compatible,
|
||||
|
|
|
|||
|
|
@ -97,13 +97,16 @@ check-local:
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(srcdir)/test-raid6.conf dup
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(srcdir)/test-raid6.conf sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf -A check
|
||||
echo --- Create some files, sync raid6 and check
|
||||
$(TESTENV) ./mktest$(EXEEXT) generate 1 3 $(CHECKCOUNT) $(CHECKSIZE)
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid6.conf diff
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid6.conf dup
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid6.conf sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -A check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf -A check
|
||||
echo --- Create some more files, sync raid5 and check
|
||||
$(TESTENV) ./mktest$(EXEEXT) generate 2 3 $(CHECKCOUNT) $(CHECKSIZE)
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf diff
|
||||
|
|
@ -293,18 +296,21 @@ check-local:
|
|||
echo --- Corrupt the parity, fix and check with raid5
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 1 100 10000 test/parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -A check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Corrupt the parity and the q-parity, fix and check with raid6
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 2 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 2 100 10000 test/q-parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid6.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf -A check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
echo --- Corrupt some files, fix and check with raid5
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 3 1 500 test/disk2/b*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 3 500 test/disk2/c*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 3 500 test/disk2/d*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf -A check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf fix 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check
|
||||
|
|
@ -317,6 +323,8 @@ check-local:
|
|||
$(TESTENV) ./mktest$(EXEEXT) append 4 500 test/disk3/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 4 500 test/disk2/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 4 500 test/disk3/_b/_b/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid5.conf -A check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid6.conf -A check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-unrecoverable -c $(srcdir)/test-raid5.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self --test-expect-recoverable -c $(srcdir)/test-raid6.conf check 2>> test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf fix 2>> test.log
|
||||
|
|
|
|||
5
TODO
5
TODO
|
|
@ -5,13 +5,8 @@ This is the list of TODO items for SnapRAID.
|
|||
|
||||
- v1.13
|
||||
|
||||
* In error growing, print the last file in the bigger data disk.
|
||||
|
||||
* Allow to filter the files to check/fix based on the drive where they reside.
|
||||
|
||||
* When checking, allow to disable the parity check, thus allowing to read only
|
||||
the filtered data to check, and not the one needed to compute the parity.
|
||||
|
||||
* In fix an existing symlink with the same name of a file to be recovered may stop
|
||||
the process making the create() operation to fail.
|
||||
|
||||
|
|
|
|||
332
check.c
332
check.c
|
|
@ -367,7 +367,7 @@ static int repair(struct snapraid_state* state, unsigned pos, unsigned diskmax,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int state_check_process(struct snapraid_state* state, int fix, struct snapraid_parity* parity, struct snapraid_parity* qarity, block_off_t blockstart, block_off_t blockmax)
|
||||
static int state_check_process(struct snapraid_state* state, int check, int fix, struct snapraid_parity* parity, struct snapraid_parity* qarity, block_off_t blockstart, block_off_t blockmax)
|
||||
{
|
||||
struct snapraid_handle* handle;
|
||||
unsigned diskmax;
|
||||
|
|
@ -439,9 +439,6 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
unsigned failed_count;
|
||||
int one_tocheck;
|
||||
int check_parity;
|
||||
unsigned char* buffer_parity;
|
||||
unsigned char* buffer_qarity;
|
||||
unsigned char* buffer_zero;
|
||||
|
||||
/* for each disk */
|
||||
one_tocheck = 0;
|
||||
|
|
@ -462,8 +459,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
if (!one_tocheck)
|
||||
continue;
|
||||
|
||||
check_parity = 1; /* if we have to check the parity */
|
||||
failed_count = 0; /* number of failed blocks */
|
||||
/* If we have to check the parity data read from disk. */
|
||||
/* Note that if check==0, we'll anyway skip the full parity check, */
|
||||
/* because we also don't read it at all */
|
||||
check_parity = 1;
|
||||
|
||||
/* keep track of the number of failed blocks */
|
||||
failed_count = 0;
|
||||
|
||||
/* for each disk, process the block */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
|
|
@ -509,6 +511,15 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
continue;
|
||||
}
|
||||
|
||||
/* if we are only hashing, we can skip excluded files and don't event read them */
|
||||
if (!check && file_flag_has(block_file_get(block), FILE_IS_EXCLUDED)) {
|
||||
/* use an empty block */
|
||||
/* in true, this is unnecessary, becase we are not checking any parity */
|
||||
/* but we keep it for completeness */
|
||||
memset(buffer[j], 0, state->block_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get the state of the block */
|
||||
block_state = block_state_get(block);
|
||||
|
||||
|
|
@ -555,7 +566,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
++recovered_error;
|
||||
}
|
||||
} else {
|
||||
/* if checking, open the file for reading */
|
||||
/* if checking or hashing, open the file for reading */
|
||||
ret = handle_open(&handle[j], block_file_get(block));
|
||||
if (ret == -1) {
|
||||
/* save the failed block for the check/fix */
|
||||
|
|
@ -585,6 +596,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
}
|
||||
}
|
||||
|
||||
/* read from the file */
|
||||
read_size = handle_read(&handle[j], block, buffer[j], state->block_size);
|
||||
if (read_size == -1) {
|
||||
/* save the failed block for the check/fix */
|
||||
|
|
@ -618,7 +630,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
|
||||
/* if the block has the hash */
|
||||
if (block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_INV) {
|
||||
/* now compute the hash */
|
||||
/* compute the hash of the block just read */
|
||||
memhash(state->hash, hash, buffer[j], read_size);
|
||||
|
||||
/* compare the hash */
|
||||
|
|
@ -638,184 +650,192 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
}
|
||||
}
|
||||
|
||||
/* buffers for parity read and not computed */
|
||||
if (state->level == 1) {
|
||||
buffer_parity = buffer[diskmax + 1];
|
||||
buffer_qarity = 0;
|
||||
} else {
|
||||
buffer_parity = buffer[diskmax + 2];
|
||||
buffer_qarity = buffer[diskmax + 3];
|
||||
}
|
||||
buffer_zero = buffer[buffermax-1];
|
||||
/* now read and check the parity if requested */
|
||||
if (check) {
|
||||
unsigned char* buffer_parity;
|
||||
unsigned char* buffer_qarity;
|
||||
unsigned char* buffer_zero;
|
||||
|
||||
/* read the parity */
|
||||
if (parity) {
|
||||
ret = parity_read(parity, i, buffer_parity, state->block_size);
|
||||
if (ret == -1) {
|
||||
buffer_parity = 0; /* no parity to use */
|
||||
|
||||
fprintf(stderr, "error:%u:parity: Read error\n", i);
|
||||
++error;
|
||||
/* buffers for parity read and not computed */
|
||||
if (state->level == 1) {
|
||||
buffer_parity = buffer[diskmax + 1];
|
||||
buffer_qarity = 0;
|
||||
} else {
|
||||
buffer_parity = buffer[diskmax + 2];
|
||||
buffer_qarity = buffer[diskmax + 3];
|
||||
}
|
||||
} else {
|
||||
buffer_parity = 0;
|
||||
}
|
||||
buffer_zero = buffer[buffermax-1];
|
||||
|
||||
/* read the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (qarity) {
|
||||
ret = parity_read(qarity, i, buffer_qarity, state->block_size);
|
||||
/* read the parity */
|
||||
if (parity) {
|
||||
ret = parity_read(parity, i, buffer_parity, state->block_size);
|
||||
if (ret == -1) {
|
||||
buffer_qarity = 0; /* no qarity to use */
|
||||
buffer_parity = 0; /* no parity to use */
|
||||
|
||||
fprintf(stderr, "error:%u:qarity: Read error\n", i);
|
||||
fprintf(stderr, "error:%u:parity: Read error\n", i);
|
||||
++error;
|
||||
}
|
||||
} else {
|
||||
buffer_qarity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = repair(state, i, diskmax, failed, failed_count, buffer, buffer_parity, buffer_qarity, buffer_zero);
|
||||
if (ret != 0) {
|
||||
/* increment the number of errors */
|
||||
if (ret > 0)
|
||||
error += ret;
|
||||
++unrecoverable_error;
|
||||
|
||||
/* print a list of all the errors in files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (failed[j].is_bad)
|
||||
fprintf(stderr, "unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
buffer_parity = 0;
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
/* keep track of damaged files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (failed[j].is_bad)
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* check if the recover was only partial */
|
||||
/* note that this could happen only when we have an incomplete 'sync' */
|
||||
/* and that we have recovered is the state before the 'sync' */
|
||||
int partial_recover_error = 0;
|
||||
/* read the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (qarity) {
|
||||
ret = parity_read(qarity, i, buffer_qarity, state->block_size);
|
||||
if (ret == -1) {
|
||||
buffer_qarity = 0; /* no qarity to use */
|
||||
|
||||
/* print a list of all the errors in files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (failed[j].is_bad && failed[j].is_outofdate) {
|
||||
++partial_recover_error;
|
||||
fprintf(stderr, "unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
}
|
||||
}
|
||||
if (partial_recover_error != 0) {
|
||||
error += partial_recover_error;
|
||||
++unrecoverable_error;
|
||||
}
|
||||
|
||||
/* check parity and q-parity only if all the blocks have it computed */
|
||||
/* if you check/fix after a partial sync, it's OK to have parity errors on the blocks with invalid parity */
|
||||
if (check_parity) {
|
||||
/* check the parity */
|
||||
if (buffer_parity != 0 && memcmp(buffer_parity, buffer[diskmax], state->block_size) != 0) {
|
||||
buffer_parity = 0;
|
||||
|
||||
fprintf(stderr, "error:%u:parity: Data error\n", i);
|
||||
++error;
|
||||
}
|
||||
|
||||
/* check the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (buffer_qarity != 0 && memcmp(buffer_qarity, buffer[diskmax + 1], state->block_size) != 0) {
|
||||
buffer_qarity = 0;
|
||||
|
||||
fprintf(stderr, "error:%u:qarity: Data error\n", i);
|
||||
fprintf(stderr, "error:%u:qarity: Read error\n", i);
|
||||
++error;
|
||||
}
|
||||
} else {
|
||||
buffer_qarity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
/* update the fixed files */
|
||||
ret = repair(state, i, diskmax, failed, failed_count, buffer, buffer_parity, buffer_qarity, buffer_zero);
|
||||
if (ret != 0) {
|
||||
/* increment the number of errors */
|
||||
if (ret > 0)
|
||||
error += ret;
|
||||
++unrecoverable_error;
|
||||
|
||||
/* print a list of all the errors in files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
/* nothing to do if it doesn't need recovering */
|
||||
if (!failed[j].is_bad)
|
||||
continue;
|
||||
|
||||
/* do not fix if the file filtered out */
|
||||
if (file_flag_has(block_file_get(failed[j].block), FILE_IS_EXCLUDED))
|
||||
continue;
|
||||
|
||||
ret = handle_write(failed[j].handle, failed[j].block, buffer[failed[j].index], state->block_size);
|
||||
if (ret == -1) {
|
||||
/* mark the file as damaged */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
|
||||
if (errno == EACCES) {
|
||||
fprintf(stderr, "WARNING! Please give write permission to the file.\n");
|
||||
} else {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
}
|
||||
printf("Stopping at block %u\n", i);
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* if we are not sure that the recovered content is uptodate */
|
||||
if (failed[j].is_outofdate) {
|
||||
/* mark the file as damaged */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* mark the file as fixed */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_FIXED);
|
||||
|
||||
fprintf(stderr, "fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
++recovered_error;
|
||||
if (failed[j].is_bad)
|
||||
fprintf(stderr, "unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
}
|
||||
|
||||
/* update parity and q-parity only if all the blocks have it computed */
|
||||
/* if you check/fix after a partial sync, you do not want to fix parity */
|
||||
/* for blocks that are going to have it computed in the sync completion */
|
||||
if (fix) {
|
||||
/* keep track of damaged files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (failed[j].is_bad)
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* check if the recover was only partial */
|
||||
/* note that this could happen only when we have an incomplete 'sync' */
|
||||
/* and that we have recovered is the state before the 'sync' */
|
||||
int partial_recover_error = 0;
|
||||
|
||||
/* print a list of all the errors in files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
if (failed[j].is_bad && failed[j].is_outofdate) {
|
||||
++partial_recover_error;
|
||||
fprintf(stderr, "unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
}
|
||||
}
|
||||
if (partial_recover_error != 0) {
|
||||
error += partial_recover_error;
|
||||
++unrecoverable_error;
|
||||
}
|
||||
|
||||
/* check parity and q-parity only if all the blocks have it computed */
|
||||
/* if you check/fix after a partial sync, it's OK to have parity errors on the blocks with invalid parity */
|
||||
if (check_parity) {
|
||||
/* update the parity */
|
||||
if (buffer_parity == 0 && parity) {
|
||||
ret = parity_write(parity, i, buffer[diskmax], state->block_size);
|
||||
/* check the parity */
|
||||
if (buffer_parity != 0 && memcmp(buffer_parity, buffer[diskmax], state->block_size) != 0) {
|
||||
buffer_parity = 0;
|
||||
|
||||
fprintf(stderr, "error:%u:parity: Data error\n", i);
|
||||
++error;
|
||||
}
|
||||
|
||||
/* check the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (buffer_qarity != 0 && memcmp(buffer_qarity, buffer[diskmax + 1], state->block_size) != 0) {
|
||||
buffer_qarity = 0;
|
||||
|
||||
fprintf(stderr, "error:%u:qarity: Data error\n", i);
|
||||
++error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
/* update the fixed files */
|
||||
for(j=0;j<failed_count;++j) {
|
||||
/* nothing to do if it doesn't need recovering */
|
||||
if (!failed[j].is_bad)
|
||||
continue;
|
||||
|
||||
/* do not fix if the file filtered out */
|
||||
if (file_flag_has(block_file_get(failed[j].block), FILE_IS_EXCLUDED))
|
||||
continue;
|
||||
|
||||
ret = handle_write(failed[j].handle, failed[j].block, buffer[failed[j].index], state->block_size);
|
||||
if (ret == -1) {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working Parity disk, it isn't possible to fix errors on it.\n");
|
||||
/* mark the file as damaged */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
|
||||
if (errno == EACCES) {
|
||||
fprintf(stderr, "WARNING! Please give write permission to the file.\n");
|
||||
} else {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
}
|
||||
printf("Stopping at block %u\n", i);
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "fixed:%u:parity: Fixed data error\n", i);
|
||||
/* if we are not sure that the recovered content is uptodate */
|
||||
if (failed[j].is_outofdate) {
|
||||
/* mark the file as damaged */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* mark the file as fixed */
|
||||
file_flag_set(block_file_get(failed[j].block), FILE_IS_FIXED);
|
||||
|
||||
fprintf(stderr, "fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].handle->disk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block));
|
||||
++recovered_error;
|
||||
}
|
||||
|
||||
/* update the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (buffer_qarity == 0 && qarity) {
|
||||
ret = parity_write(qarity, i, buffer[diskmax + 1], state->block_size);
|
||||
/* update parity and q-parity only if all the blocks have it computed */
|
||||
/* if you check/fix after a partial sync, you do not want to fix parity */
|
||||
/* for blocks that are going to have it computed in the sync completion */
|
||||
if (check_parity) {
|
||||
/* update the parity */
|
||||
if (buffer_parity == 0 && parity) {
|
||||
ret = parity_write(parity, i, buffer[diskmax], state->block_size);
|
||||
if (ret == -1) {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working Q-Parity disk, it isn't possible to fix errors on it.\n");
|
||||
fprintf(stderr, "WARNING! Without a working Parity disk, it isn't possible to fix errors on it.\n");
|
||||
printf("Stopping at block %u\n", i);
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "fixed:%u:qarity: Fixed data error\n", i);
|
||||
fprintf(stderr, "fixed:%u:parity: Fixed data error\n", i);
|
||||
++recovered_error;
|
||||
}
|
||||
|
||||
/* update the qarity */
|
||||
if (state->level >= 2) {
|
||||
if (buffer_qarity == 0 && qarity) {
|
||||
ret = parity_write(qarity, i, buffer[diskmax + 1], state->block_size);
|
||||
if (ret == -1) {
|
||||
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
|
||||
fprintf(stderr, "WARNING! Without a working Q-Parity disk, it isn't possible to fix errors on it.\n");
|
||||
printf("Stopping at block %u\n", i);
|
||||
++unrecoverable_error;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "fixed:%u:qarity: Fixed data error\n", i);
|
||||
++recovered_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now fix if requested */
|
||||
if (fix) {
|
||||
/* for all the files of this block check if we need to fix the modification time */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
|
|
@ -1124,8 +1144,11 @@ bail:
|
|||
}
|
||||
if (unrecoverable_error)
|
||||
printf("%u UNRECOVERABLE errors\n", unrecoverable_error);
|
||||
else
|
||||
printf("No unrecoverable errors\n");
|
||||
else {
|
||||
/* without checking, we don't know if they are really recoverable or not */
|
||||
if (check)
|
||||
printf("No unrecoverable errors\n");
|
||||
}
|
||||
} else {
|
||||
printf("No error\n");
|
||||
}
|
||||
|
|
@ -1160,7 +1183,7 @@ bail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount)
|
||||
void state_check(struct snapraid_state* state, int check, int fix, block_off_t blockstart, block_off_t blockcount)
|
||||
{
|
||||
block_off_t blockmax;
|
||||
data_off_t size;
|
||||
|
|
@ -1173,6 +1196,11 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart,
|
|||
|
||||
printf("Initializing...\n");
|
||||
|
||||
if (!check && fix) {
|
||||
fprintf(stderr, "Error in calling, you cannot fix without checking parity.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
blockmax = parity_resize(state);
|
||||
size = blockmax * (data_off_t)state->block_size;
|
||||
|
||||
|
|
@ -1206,7 +1234,7 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart,
|
|||
} else {
|
||||
qarity_ptr = 0;
|
||||
}
|
||||
} else {
|
||||
} else if (check) {
|
||||
/* if checking, open the file for reading */
|
||||
/* it may fail if the file doesn't exist, in this case we continue to check the files */
|
||||
parity_ptr = &parity;
|
||||
|
|
@ -1228,18 +1256,24 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart,
|
|||
} else {
|
||||
qarity_ptr = 0;
|
||||
}
|
||||
} else {
|
||||
/* otherwise don't use any parity */
|
||||
parity_ptr = 0;
|
||||
qarity_ptr = 0;
|
||||
}
|
||||
|
||||
if (fix)
|
||||
printf("Fixing...\n");
|
||||
else
|
||||
else if (check)
|
||||
printf("Checking...\n");
|
||||
else
|
||||
printf("Hashing...\n");
|
||||
|
||||
error = 0;
|
||||
|
||||
/* skip degenerated cases of empty parity, or skipping all */
|
||||
if (blockstart < blockmax) {
|
||||
ret = state_check_process(state, fix, parity_ptr, qarity_ptr, blockstart, blockmax);
|
||||
ret = state_check_process(state, check, fix, parity_ptr, qarity_ptr, blockstart, blockmax);
|
||||
if (ret == -1) {
|
||||
++error;
|
||||
/* continue, as we are already exiting */
|
||||
|
|
|
|||
14
snapraid.1
14
snapraid.1
|
|
@ -9,6 +9,10 @@ snapraid [\(hyc, \(hy\(hyconf CONFIG] [\(hyf, \(hy\(hyfilter PATTERN] [\(hyH, \(
|
|||
[\(hyN, \(hy\(hyfind\(hyby\(hyname]
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
[\(hyA, \(hy\(hyaudit\(hyonly]
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
[\(hyZ, \(hy\(hyforce\(hyzero] [\(hyE, \(hy\(hyforce\(hyempty]
|
||||
.PD 0
|
||||
|
|
@ -328,6 +332,8 @@ Nothing is modified.
|
|||
Checks all the files and the redundancy data.
|
||||
All the files are hashed and compared with the snapshot saved
|
||||
in the previous \(a"sync\(a" command.
|
||||
If you use the \(hyA, \(hy\(hyaudit\(hyonly option, only the file
|
||||
data is checked, and the redundandy data is ignored.
|
||||
.PP
|
||||
Files are identified by path, and checked by content.
|
||||
Nothing is modified.
|
||||
|
|
@ -355,7 +361,8 @@ the current directory in Windows.
|
|||
.TP
|
||||
.B \(hyf, \(hy\(hyfilter PATTERN
|
||||
Filters the files to operate on with the \(a"check\(a" and \(a"fix\(a"
|
||||
commands. This option is ignored with the \(a"sync\(a" command.
|
||||
commands.
|
||||
This option cannot be used with the \(a"sync\(a" command.
|
||||
See the PATTERN section for more details in the
|
||||
pattern specifications.
|
||||
This option can be used many times.
|
||||
|
|
@ -366,6 +373,11 @@ Filters out hidden files and directory. In Unix hidden files are
|
|||
the ones starting with \(cq.\(cq. In Windows they are the ones with the
|
||||
hidden attribute.
|
||||
.TP
|
||||
.B \(hyA, \(hy\(hyaudit\(hyonly
|
||||
When checking, only verify the hash of the files, without
|
||||
doing any kind of check on the redundancy data.
|
||||
This option can be used only with the \(a"check\(a" command.
|
||||
.TP
|
||||
.B \(hyN, \(hy\(hyfind\(hyby\(hyname
|
||||
When syncing finds the files by path instead than by inode.
|
||||
This option allows a fast sync command after having replaced
|
||||
|
|
|
|||
13
snapraid.c
13
snapraid.c
|
|
@ -83,6 +83,7 @@ struct option long_options[] = {
|
|||
{ "force-zero", 0, 0, 'Z' },
|
||||
{ "force-empty", 0, 0, 'E' },
|
||||
{ "find-by-name", 0, 0, 'N' },
|
||||
{ "audit-only", 0, 0, 'A' },
|
||||
{ "speed-test", 0, 0, 'T' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "gui", 0, 0, 'G' }, /* undocumented GUI interface command */
|
||||
|
|
@ -98,7 +99,7 @@ struct option long_options[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#define OPTIONS "c:f:Hs:t:ZENTvhVG"
|
||||
#define OPTIONS "c:f:Hs:t:ZENATvhVG"
|
||||
|
||||
volatile int global_interrupt = 0;
|
||||
|
||||
|
|
@ -127,6 +128,7 @@ int main(int argc, char* argv[])
|
|||
int force_empty;
|
||||
int filter_hidden;
|
||||
int find_by_name;
|
||||
int audit_only;
|
||||
int test_expect_unrecoverable;
|
||||
int test_expect_recoverable;
|
||||
int test_kill_after_sync;
|
||||
|
|
@ -151,6 +153,7 @@ int main(int argc, char* argv[])
|
|||
force_empty = 0;
|
||||
filter_hidden = 0;
|
||||
find_by_name = 0;
|
||||
audit_only = 0;
|
||||
test_expect_unrecoverable = 0;
|
||||
test_expect_recoverable = 0;
|
||||
test_kill_after_sync = 0;
|
||||
|
|
@ -206,6 +209,9 @@ int main(int argc, char* argv[])
|
|||
case 'N' :
|
||||
find_by_name = 1;
|
||||
break;
|
||||
case 'A' :
|
||||
audit_only = 1;
|
||||
break;
|
||||
case 'v' :
|
||||
verbose = 1;
|
||||
break;
|
||||
|
|
@ -349,7 +355,10 @@ int main(int argc, char* argv[])
|
|||
/* intercept Ctrl+C */
|
||||
signal(SIGINT, &signal_handler);
|
||||
|
||||
state_check(&state, operation == OPERATION_FIX, blockstart, blockcount);
|
||||
if (operation == OPERATION_CHECK)
|
||||
state_check(&state, !audit_only, 0, blockstart, blockcount);
|
||||
else /* it's fix */
|
||||
state_check(&state, 1, 1, blockstart, blockcount);
|
||||
}
|
||||
|
||||
state_done(&state);
|
||||
|
|
|
|||
11
snapraid.d
11
snapraid.d
|
|
@ -4,6 +4,7 @@ Name{number}
|
|||
Synopsis
|
||||
:snapraid [-c, --conf CONFIG] [-f, --filter PATTERN] [-H, --filter-nohidden]
|
||||
: [-N, --find-by-name]
|
||||
: [-A, --audit-only]
|
||||
: [-Z, --force-zero] [-E, --force-empty]
|
||||
: [-s, --start BLKSTART] [-t, --count BLKCOUNT]
|
||||
: [-v, --verbose]
|
||||
|
|
@ -206,6 +207,8 @@ Commands
|
|||
Checks all the files and the redundancy data.
|
||||
All the files are hashed and compared with the snapshot saved
|
||||
in the previous "sync" command.
|
||||
If you use the -A, --audit-only option, only the file
|
||||
data is checked, and the redundandy data is ignored.
|
||||
|
||||
Files are identified by path, and checked by content.
|
||||
Nothing is modified.
|
||||
|
|
@ -235,7 +238,8 @@ Options
|
|||
|
||||
-f, --filter PATTERN
|
||||
Filters the files to operate on with the "check" and "fix"
|
||||
commands. This option is ignored with the "sync" command.
|
||||
commands.
|
||||
This option cannot be used with the "sync" command.
|
||||
See the PATTERN section for more details in the
|
||||
pattern specifications.
|
||||
This option can be used many times.
|
||||
|
|
@ -246,6 +250,11 @@ Options
|
|||
the ones starting with '.'. In Windows they are the ones with the
|
||||
hidden attribute.
|
||||
|
||||
-A, --audit-only
|
||||
When checking, only verify the hash of the files, without
|
||||
doing any kind of check on the redundancy data.
|
||||
This option can be used only with the "check" command.
|
||||
|
||||
-N, --find-by-name
|
||||
When syncing finds the files by path instead than by inode.
|
||||
This option allows a fast sync command after having replaced
|
||||
|
|
|
|||
11
snapraid.txt
11
snapraid.txt
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
snapraid [-c, --conf CONFIG] [-f, --filter PATTERN] [-H, --filter-nohidden]
|
||||
[-N, --find-by-name]
|
||||
[-A, --audit-only]
|
||||
[-Z, --force-zero] [-E, --force-empty]
|
||||
[-s, --start BLKSTART] [-t, --count BLKCOUNT]
|
||||
[-v, --verbose]
|
||||
|
|
@ -230,6 +231,8 @@ Nothing is modified.
|
|||
Checks all the files and the redundancy data.
|
||||
All the files are hashed and compared with the snapshot saved
|
||||
in the previous "sync" command.
|
||||
If you use the -A, --audit-only option, only the file
|
||||
data is checked, and the redundandy data is ignored.
|
||||
|
||||
Files are identified by path, and checked by content.
|
||||
Nothing is modified.
|
||||
|
|
@ -264,7 +267,8 @@ SnapRAID provides the following options:
|
|||
|
||||
-f, --filter PATTERN
|
||||
Filters the files to operate on with the "check" and "fix"
|
||||
commands. This option is ignored with the "sync" command.
|
||||
commands.
|
||||
This option cannot be used with the "sync" command.
|
||||
See the PATTERN section for more details in the
|
||||
pattern specifications.
|
||||
This option can be used many times.
|
||||
|
|
@ -275,6 +279,11 @@ SnapRAID provides the following options:
|
|||
the ones starting with '.'. In Windows they are the ones with the
|
||||
hidden attribute.
|
||||
|
||||
-A, --audit-only
|
||||
When checking, only verify the hash of the files, without
|
||||
doing any kind of check on the redundancy data.
|
||||
This option can be used only with the "check" command.
|
||||
|
||||
-N, --find-by-name
|
||||
When syncing finds the files by path instead than by inode.
|
||||
This option allows a fast sync command after having replaced
|
||||
|
|
|
|||
7
state.h
7
state.h
|
|
@ -93,9 +93,11 @@ void state_scan(struct snapraid_state* state, int output);
|
|||
int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount);
|
||||
|
||||
/**
|
||||
* Checks (and fixes) all the files and the parity data.
|
||||
* Checks (and fixes) all the files and parity data.
|
||||
* \param check If we have to check also the parity.
|
||||
* \param check If we have to fix, after checking. It requires also check==1.
|
||||
*/
|
||||
void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount);
|
||||
void state_check(struct snapraid_state* state, int check, int fix, block_off_t blockstart, block_off_t blockcount);
|
||||
|
||||
/**
|
||||
* Dry the files.
|
||||
|
|
@ -109,6 +111,7 @@ void state_dup(struct snapraid_state* state);
|
|||
|
||||
/**
|
||||
* Filter files.
|
||||
* Apply and additional filter to list of file currently loaded.
|
||||
*/
|
||||
void state_filter(struct snapraid_state* state, tommy_list* filterlist);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue