mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Better error management
This commit is contained in:
parent
a30c93024a
commit
f443a7f089
13 changed files with 272 additions and 138 deletions
1
HISTORY
1
HISTORY
|
|
@ -5,6 +5,7 @@ SnapRAID HISTORY
|
|||
==========
|
||||
* Exclusion list of files and directories
|
||||
* More automatic tests
|
||||
* Better error management
|
||||
* Better documentation
|
||||
|
||||
|
||||
|
|
|
|||
1
TODO
1
TODO
|
|
@ -24,4 +24,3 @@ http://isthe.com/chongo/src/fnv/hash_64a.c (C implementation)
|
|||
* Use the O_NOATIME flag if run as 'root'.
|
||||
Anyway a better solution is to mount the disk with the "noatime" and "nodiratime" options.
|
||||
|
||||
* During a sync operation periodicallly save the state to do not lose the job done in case of a system crash.
|
||||
|
|
|
|||
112
check.c
112
check.c
|
|
@ -26,7 +26,7 @@
|
|||
/****************************************************************************/
|
||||
/* check */
|
||||
|
||||
static int state_check_process(int ret_on_error, struct snapraid_state* state, int fix, int parity_f, block_off_t blockstart, block_off_t blockmax)
|
||||
static int state_check_process(struct snapraid_state* state, int fix, int parity_f, block_off_t blockstart, block_off_t blockmax)
|
||||
{
|
||||
struct snapraid_handle* handle;
|
||||
unsigned diskmax = tommy_array_size(&state->diskarr);
|
||||
|
|
@ -102,12 +102,24 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
if (!block->is_hashed)
|
||||
continue;
|
||||
|
||||
ret = handle_close_if_different(&handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fix) {
|
||||
/* if fixing, create the file and open for writing */
|
||||
handle_create(&handle[j], block->file);
|
||||
ret = handle_create(&handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
} else {
|
||||
/* if checking, open the file for reading */
|
||||
ret = handle_open(-1, &handle[j], block->file);
|
||||
ret = handle_open(&handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
/* save the failed block for the parity check */
|
||||
++failed;
|
||||
|
|
@ -120,7 +132,7 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
}
|
||||
}
|
||||
|
||||
read_size = handle_read(-1, &handle[j], block, block_buffer, state->block_size);
|
||||
read_size = handle_read(&handle[j], block, block_buffer, state->block_size);
|
||||
if (read_size == -1) {
|
||||
/* save the failed block for the parity check */
|
||||
++failed;
|
||||
|
|
@ -155,14 +167,20 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
count_size += read_size;
|
||||
}
|
||||
|
||||
if (failed == 0) {
|
||||
if (parity_f == -1) {
|
||||
/* all the cases with no parity file */
|
||||
if (failed != 0) {
|
||||
fprintf(stderr, "%u: UNRECOVERABLE errors for this block\n", i);
|
||||
++unrecoverable_error;
|
||||
}
|
||||
} else if (failed == 0) {
|
||||
int ret;
|
||||
int fixable;
|
||||
|
||||
fixable = 0;
|
||||
|
||||
/* read the parity */
|
||||
ret = parity_read(-1, state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
ret = parity_read(state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "%u: Parity read error\n", i);
|
||||
++error;
|
||||
|
|
@ -181,7 +199,12 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
if (fix && fixable) {
|
||||
/* fix the parity */
|
||||
/* if all the file hashes are matching, we can recompute the parity */
|
||||
parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
ret = parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without a working parity disk, it isn't possible to fix errors on it.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%u: Fixed\n", i);
|
||||
++recovered_error;
|
||||
|
|
@ -196,7 +219,7 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
fixable = 0;
|
||||
|
||||
/* read the parity */
|
||||
ret = parity_read(-1, state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
ret = parity_read(state->parity, parity_f, i, block_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "%u: Parity read error\n", i);
|
||||
fprintf(stderr, "%u: UNRECOVERABLE errors for this block\n", i);
|
||||
|
|
@ -225,7 +248,12 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
if (fix && fixable) {
|
||||
/* fix the file */
|
||||
/* if only one file is wrong, we already have recomputed it */
|
||||
handle_write(failed_handle, failed_block, block_buffer, state->block_size);
|
||||
ret = handle_write(failed_handle, failed_block, block_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%u: Fixed\n", i);
|
||||
++recovered_error;
|
||||
|
|
@ -239,17 +267,30 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
++count_block;
|
||||
|
||||
/* progress */
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size))
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size)) {
|
||||
printf("Stopping for interruption at block %u\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%u%% completed, %u MB processed\n", i * 100 / blockmax, (unsigned)(count_size / (1024*1024)));
|
||||
|
||||
bail:
|
||||
for(j=0;j<diskmax;++j) {
|
||||
ret = handle_close(&handle[j]);
|
||||
if (ret == -1) {
|
||||
if (fix) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
/* continue, as we are already exiting */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error || recovered_error || unrecoverable_error) {
|
||||
if (error)
|
||||
printf("%u read errors\n", error);
|
||||
printf("%u read/data errors\n", error);
|
||||
else
|
||||
printf("No read errors\n");
|
||||
printf("No read/data errors\n");
|
||||
if (fix) {
|
||||
if (recovered_error)
|
||||
printf("%u recovered errors\n", recovered_error);
|
||||
|
|
@ -264,15 +305,15 @@ static int state_check_process(int ret_on_error, struct snapraid_state* state, i
|
|||
printf("No error\n");
|
||||
}
|
||||
|
||||
for(j=0;j<diskmax;++j) {
|
||||
handle_close(&handle[j]);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
free(block_buffer);
|
||||
free(xor_buffer);
|
||||
|
||||
return unrecoverable_error != 0 ? ret_on_error : 0;
|
||||
/* signal if there are unrecoverable errors */
|
||||
if (unrecoverable_error != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void state_check(struct snapraid_state* state, int fix, block_off_t blockstart)
|
||||
|
|
@ -292,33 +333,52 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart)
|
|||
size = blockmax * (data_off_t)state->block_size;
|
||||
|
||||
if (blockstart > blockmax) {
|
||||
fprintf(stderr, "The specified starting block %u is bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
fprintf(stderr, "Error in the specified starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pathcpy(path, sizeof(path), state->parity);
|
||||
if (fix) {
|
||||
/* if fixing, create the file and open for writing */
|
||||
/* it never fails */
|
||||
/* if it fails, we cannot continue */
|
||||
f = parity_create(path, size);
|
||||
if (f == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible parity file, it isn't possible to fix any error.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* if checking, open the file for reading */
|
||||
/* it may fail if the file doesn't exist */
|
||||
f = parity_open(-1, path);
|
||||
/* it may fail if the file doesn't exist, in this case we continue to check the files */
|
||||
f = parity_open(path);
|
||||
if (f == -1) {
|
||||
printf("No accessible parity file, all the errors are going to be UNRECOVERABLE.\n");
|
||||
/* continue anyway */
|
||||
}
|
||||
}
|
||||
|
||||
/* skip degenerated cases of empty parity, or skipping all */
|
||||
if (blockstart < blockmax) {
|
||||
ret = state_check_process(-1, state, fix, f, blockstart, blockmax);
|
||||
ret = state_check_process(state, fix, f, blockstart, blockmax);
|
||||
if (ret == -1) {
|
||||
/* no extra message */
|
||||
if (f != -1) {
|
||||
ret = parity_close(path, f);
|
||||
if (ret != -1) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
/* continue, as we are already exiting */
|
||||
}
|
||||
}
|
||||
/* exit with a failure code */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Nothing to do\n");
|
||||
}
|
||||
|
||||
/* try to close only if opened */
|
||||
if (f != -1) {
|
||||
parity_close(path, f);
|
||||
ret = parity_close(path, f);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
97
handle.c
97
handle.c
|
|
@ -24,31 +24,56 @@
|
|||
/****************************************************************************/
|
||||
/* handle */
|
||||
|
||||
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
int handle_close_if_different(struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
/* if already opened, nothing to do */
|
||||
if (handle->file == file && handle->f != -1) {
|
||||
|
||||
/* if it's the same file, nothing to do */
|
||||
if (handle->file == file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reset the file */
|
||||
handle->file = 0;
|
||||
|
||||
/* close if already open */
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
/* invalidate for error */
|
||||
handle->f = -1;
|
||||
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the descriptor */
|
||||
handle->f = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
/* if it's the same file, and already opened, nothing to do */
|
||||
if (handle->file == file && handle->f != -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
|
||||
handle->f = open(handle->path, O_RDWR | O_CREAT | O_BINARY, 0600);
|
||||
|
||||
if (handle->f == -1) {
|
||||
/* invalidate for error */
|
||||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
|
||||
fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* just opened */
|
||||
|
|
@ -58,7 +83,7 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
|||
ret = fstat(handle->f, &st);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (st.st_size < file->size) {
|
||||
|
|
@ -75,13 +100,13 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
|||
} else {
|
||||
fprintf(stderr, "Error growing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
} else if (st.st_size > file->size) {
|
||||
ret = ftruncate(handle->f, file->size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,14 +115,14 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file)
|
|||
ret = posix_fadvise(handle->f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
int handle_open(struct snapraid_handle* handle, struct snapraid_file* file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -106,16 +131,6 @@ int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snaprai
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* close if already open */
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as close is supposed to never fail */
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
|
||||
handle->f = open(handle->path, O_RDONLY | O_BINARY);
|
||||
|
||||
|
|
@ -124,11 +139,8 @@ int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snaprai
|
|||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* just opened */
|
||||
|
|
@ -138,32 +150,37 @@ int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snaprai
|
|||
/* advise sequential access */
|
||||
ret = posix_fadvise(handle->f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as advise is supposed to never fail */
|
||||
fprintf(stderr, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_close(struct snapraid_handle* handle)
|
||||
int handle_close(struct snapraid_handle* handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (handle->f != -1) {
|
||||
ret = close(handle->f);
|
||||
if (ret != 0) {
|
||||
/* invalidate for error */
|
||||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
|
||||
fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
handle->file = 0;
|
||||
handle->f = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_read(int ret_on_error, struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t read_ret;
|
||||
data_off_t offset;
|
||||
|
|
@ -177,27 +194,21 @@ int handle_read(int ret_on_error, struct snapraid_handle* handle, struct snaprai
|
|||
read_ret = pread(handle->f, block_buffer, read_size, offset);
|
||||
#else
|
||||
if (lseek(handle->f, offset, SEEK_SET) != offset) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error seeking file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_ret = read(handle->f, block_buffer, read_size);
|
||||
#endif
|
||||
if (read_ret != read_size) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error reading file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read_size;
|
||||
}
|
||||
|
||||
void handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t write_ret;
|
||||
data_off_t offset;
|
||||
|
|
@ -212,14 +223,16 @@ void handle_write(struct snapraid_handle* handle, struct snapraid_block* block,
|
|||
#else
|
||||
if (lseek(handle->f, offset, SEEK_SET) != offset) {
|
||||
fprintf(stderr, "Error seeking file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_ret = write(handle->f, block_buffer, write_size);
|
||||
#endif
|
||||
if (write_ret != write_size) {
|
||||
fprintf(stderr, "Error writing file '%s'. %s.\n", handle->path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
19
handle.h
19
handle.h
|
|
@ -29,31 +29,36 @@ struct snapraid_handle {
|
|||
};
|
||||
|
||||
/**
|
||||
* Create a file.
|
||||
* Closes a file, if it's different from the specified one.
|
||||
*/
|
||||
int handle_close_if_different(struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
|
||||
/**
|
||||
* Creates a file.
|
||||
* The file is created if missing, and opening with write access.
|
||||
*/
|
||||
int handle_create(struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
|
||||
/**
|
||||
* Open a file.
|
||||
* Opens a file.
|
||||
* The file is opened for reading.
|
||||
*/
|
||||
int handle_open(int ret_on_error, struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
int handle_open(struct snapraid_handle* handle, struct snapraid_file* file);
|
||||
|
||||
/**
|
||||
* Close a file.
|
||||
* Closes a file.
|
||||
*/
|
||||
void handle_close(struct snapraid_handle* handle);
|
||||
int handle_close(struct snapraid_handle* handle);
|
||||
|
||||
/**
|
||||
* Read a block from a file.
|
||||
*/
|
||||
int handle_read(int ret_on_error, struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
/**
|
||||
* Writes a block to a file.
|
||||
*/
|
||||
void handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
70
parity.c
70
parity.c
|
|
@ -58,13 +58,13 @@ int parity_create(const char* path, data_off_t size)
|
|||
f = open(path, O_RDWR | O_CREAT | O_BINARY, 0600);
|
||||
if (f == -1) {
|
||||
fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fstat(f, &st);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error accessing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (st.st_size < size) {
|
||||
|
|
@ -81,13 +81,13 @@ int parity_create(const char* path, data_off_t size)
|
|||
} else {
|
||||
fprintf(stderr, "Error growing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
} else if (st.st_size > size) {
|
||||
ret = ftruncate(f, size);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error truncating parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,14 +96,18 @@ int parity_create(const char* path, data_off_t size)
|
|||
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
|
||||
bail:
|
||||
close(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parity_open(int ret_on_error, const char* path)
|
||||
int parity_open(const char* path)
|
||||
{
|
||||
#if HAVE_POSIX_FADVISE
|
||||
int ret;
|
||||
|
|
@ -112,26 +116,27 @@ int parity_open(int ret_on_error, const char* path)
|
|||
|
||||
f = open(path, O_RDONLY | O_BINARY);
|
||||
if (f == -1) {
|
||||
if (ret_on_error)
|
||||
return -1;
|
||||
fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/* advise sequential access */
|
||||
ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
if (ret != 0) {
|
||||
/* here we always abort on error, as advise is supposed to never fail */
|
||||
fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
|
||||
bail:
|
||||
close(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void parity_sync(const char* path, int f)
|
||||
int parity_sync(const char* path, int f)
|
||||
{
|
||||
#if HAVE_FSYNC
|
||||
int ret;
|
||||
|
|
@ -142,30 +147,37 @@ void parity_sync(const char* path, int f)
|
|||
ret = fsync(f);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error synching parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parity_close(const char* path, int f)
|
||||
int parity_close(const char* path, int f)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = close(f);
|
||||
if (ret != 0) {
|
||||
/* This is a serious error, as it may be the result of a failed write */
|
||||
/* identified at later time. */
|
||||
/* In a normal filesystem (not NFS) it should never happen */
|
||||
fprintf(stderr, "Error closing parity file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parity_write(const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
int parity_write(const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t write_ret;
|
||||
data_off_t offset;
|
||||
|
||||
offset = pos * (data_off_t)block_size;
|
||||
|
||||
#if HAVE_PWRITE
|
||||
#if HAVE_PWRITE
|
||||
write_ret = pwrite(f, block_buffer, block_size, offset);
|
||||
#else
|
||||
if (lseek(f, offset, SEEK_SET) != offset) {
|
||||
|
|
@ -174,7 +186,7 @@ void parity_write(const char* path, int f, block_off_t pos, unsigned char* block
|
|||
} else {
|
||||
fprintf(stderr, "Error seeking file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_ret = write(f, block_buffer, block_size);
|
||||
|
|
@ -185,40 +197,32 @@ void parity_write(const char* path, int f, block_off_t pos, unsigned char* block
|
|||
} else {
|
||||
fprintf(stderr, "Error writing file '%s'. %s.\n", path, strerror(errno));
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parity_read(int ret_on_error, const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
int parity_read(const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size)
|
||||
{
|
||||
ssize_t read_ret;
|
||||
data_off_t offset;
|
||||
|
||||
/* if the file is not opened, return an error without trying */
|
||||
if (f == -1 && ret_on_error)
|
||||
return -1;
|
||||
|
||||
offset = pos * (data_off_t)block_size;
|
||||
|
||||
#if HAVE_PREAD
|
||||
#if HAVE_PREAD
|
||||
read_ret = pread(f, block_buffer, block_size, offset);
|
||||
#else
|
||||
if (lseek(f, offset, SEEK_SET) != offset) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error seeking file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_ret = read(f, block_buffer, block_size);
|
||||
#endif
|
||||
if (read_ret != block_size) {
|
||||
if (ret_on_error)
|
||||
return ret_on_error;
|
||||
|
||||
fprintf(stderr, "Error reading file '%s'. %s.\n", path, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return block_size;
|
||||
|
|
|
|||
10
parity.h
10
parity.h
|
|
@ -34,27 +34,27 @@ int parity_create(const char* path, data_off_t size);
|
|||
/**
|
||||
* Opens an already existing parity file.
|
||||
*/
|
||||
int parity_open(int ret_on_error, const char* path);
|
||||
int parity_open(const char* path);
|
||||
|
||||
/**
|
||||
* Flushes the parity file in the disk.
|
||||
*/
|
||||
void parity_sync(const char* path, int f);
|
||||
int parity_sync(const char* path, int f);
|
||||
|
||||
/**
|
||||
* Closes the parity file.
|
||||
*/
|
||||
void parity_close(const char* path, int f);
|
||||
int parity_close(const char* path, int f);
|
||||
|
||||
/**
|
||||
* Read a block from the parity file.
|
||||
*/
|
||||
int parity_read(int ret_on_error, const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
int parity_read(const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
/**
|
||||
* Writes a block in the parity file.
|
||||
*/
|
||||
void parity_write(const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
int parity_write(const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
2
scan.c
2
scan.c
|
|
@ -117,7 +117,7 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state,
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* remove and reinsert it */
|
||||
++scan->count_change;
|
||||
--scan->count_insert;
|
||||
|
|
|
|||
|
|
@ -252,8 +252,8 @@ Options
|
|||
|
||||
-s, --start BLOCK
|
||||
Starts the processing from the specified
|
||||
block number. It could be useful to easy retry to fix
|
||||
some specific block, in case of a damaged disk.
|
||||
block number. It could be useful to easy retry to check or
|
||||
fix some specific block, in case of a damaged disk.
|
||||
|
||||
-Z, --force-zero
|
||||
Forces the insecure operation of synching a file with zero
|
||||
|
|
|
|||
|
|
@ -391,8 +391,8 @@ the file `/etc/snapraid.conf'.
|
|||
-s, --start BLOCK
|
||||
</strong></td></tr><tr><td>
|
||||
Starts the processing from the specified
|
||||
block number. It could be useful to easy retry to fix
|
||||
some specific block, in case of a damaged disk.
|
||||
block number. It could be useful to easy retry to check or
|
||||
fix some specific block, in case of a damaged disk.
|
||||
</td></tr>
|
||||
</table>
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="2">
|
||||
|
|
|
|||
|
|
@ -277,8 +277,8 @@ For example:
|
|||
|
||||
-s, --start BLOCK
|
||||
Starts the processing from the specified
|
||||
block number. It could be useful to easy retry to fix
|
||||
some specific block, in case of a damaged disk.
|
||||
block number. It could be useful to easy retry to check or
|
||||
fix some specific block, in case of a damaged disk.
|
||||
|
||||
-Z, --force-zero
|
||||
Forces the insecure operation of synching a file with zero
|
||||
|
|
|
|||
2
state.c
2
state.c
|
|
@ -513,7 +513,7 @@ int state_progress(time_t* start, time_t* last, block_off_t blockpos, block_off_
|
|||
|
||||
/* stop if requested */
|
||||
if (global_interrupt) {
|
||||
printf("\rStopping for interruption%s\n", PROGRESS_CLEAR);
|
||||
printf("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
84
sync.c
84
sync.c
|
|
@ -38,6 +38,7 @@ static void state_sync_process(struct snapraid_state* state, int parity_f, block
|
|||
data_off_t count_block;
|
||||
time_t start;
|
||||
time_t last;
|
||||
int ret;
|
||||
|
||||
block_buffer = malloc_nofail(state->block_size);
|
||||
xor_buffer = malloc_nofail(state->block_size);
|
||||
|
|
@ -56,6 +57,7 @@ static void state_sync_process(struct snapraid_state* state, int parity_f, block
|
|||
|
||||
for(i=blockstart;i<blockmax;++i) {
|
||||
int unhashed;
|
||||
int ret;
|
||||
|
||||
/* for each disk, search for an unhashed block */
|
||||
unhashed = 0;
|
||||
|
|
@ -84,9 +86,26 @@ static void state_sync_process(struct snapraid_state* state, int parity_f, block
|
|||
if (!block)
|
||||
continue;
|
||||
|
||||
handle_open(0, &handle[j], block->file);
|
||||
ret = handle_close_if_different(&handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible data disk, it isn't possible to sync.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
read_size = handle_read(0, &handle[j], block, block_buffer, state->block_size);
|
||||
ret = handle_open(&handle[j], block->file);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible data disk, it isn't possible to sync.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
read_size = handle_read(&handle[j], block, block_buffer, state->block_size);
|
||||
if (read_size == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible data disk, it isn't possible to sync.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* now compute the hash */
|
||||
md5_init(&md5);
|
||||
|
|
@ -97,15 +116,14 @@ static void state_sync_process(struct snapraid_state* state, int parity_f, block
|
|||
/* compare the hash */
|
||||
if (memcmp(hash, block->hash, HASH_MAX) != 0) {
|
||||
fprintf(stderr, "%u: Data error for file %s at position %u\n", i, block->file->sub, block_file_pos(block));
|
||||
fprintf(stderr, "WARNING! With errors in the data disk it isn't possible to sync.\n");
|
||||
fprintf(stderr, "Stopping to allow recovery. Try with 'snapraid -s %u fix'\n", i);
|
||||
exit(EXIT_FAILURE);
|
||||
goto bail;
|
||||
}
|
||||
} else {
|
||||
/* copy the hash */
|
||||
/* copy the hash, but doesn't mark the block as hashed */
|
||||
/* this allow on error to do not save the failed computation */
|
||||
memcpy(block->hash, hash, HASH_MAX);
|
||||
|
||||
/* mark the block as hashed */
|
||||
block->is_hashed = 1;
|
||||
}
|
||||
|
||||
/* compute the parity */
|
||||
|
|
@ -115,20 +133,43 @@ static void state_sync_process(struct snapraid_state* state, int parity_f, block
|
|||
}
|
||||
|
||||
/* write the parity */
|
||||
parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
ret = parity_write(state->parity, parity_f, i, xor_buffer, state->block_size);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible parity file, it isn't possible to sync.\n");
|
||||
fprintf(stderr, "Stopping at block %u\n", i);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* for each disk, mark the blocks as processed */
|
||||
for(j=0;j<diskmax;++j) {
|
||||
struct snapraid_block* block;
|
||||
|
||||
block = disk_block_get(handle[j].disk, i);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
block->is_hashed = 1;
|
||||
}
|
||||
|
||||
/* count the number of processed block */
|
||||
++count_block;
|
||||
|
||||
/* progress */
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size))
|
||||
if (state_progress(&start, &last, i, blockmax, count_block, count_size)) {
|
||||
printf("Stopping for interruption at block %u\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%u%% completed, %u MB processed\n", i * 100 / blockmax, (unsigned)(count_size / (1024*1024)));
|
||||
|
||||
bail:
|
||||
for(j=0;j<diskmax;++j) {
|
||||
handle_close(&handle[j]);
|
||||
ret = handle_close(&handle[j]);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
/* continue, as we are already exiting */
|
||||
}
|
||||
}
|
||||
|
||||
free(handle);
|
||||
|
|
@ -141,6 +182,7 @@ void state_sync(struct snapraid_state* state, block_off_t blockstart)
|
|||
char path[PATH_MAX];
|
||||
block_off_t blockmax;
|
||||
data_off_t size;
|
||||
int ret;
|
||||
int f;
|
||||
|
||||
printf("Syncing...\n");
|
||||
|
|
@ -150,22 +192,32 @@ void state_sync(struct snapraid_state* state, block_off_t blockstart)
|
|||
size = blockmax * (data_off_t)state->block_size;
|
||||
|
||||
if (blockstart > blockmax) {
|
||||
fprintf(stderr, "The specified starting block %u is bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
fprintf(stderr, "Error in the starting block %u. It's bigger than the parity size %u.\n", blockstart, blockmax);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pathcpy(path, sizeof(path), state->parity);
|
||||
f = parity_create(path, size);
|
||||
if (f == -1) {
|
||||
fprintf(stderr, "WARNING! Without an accessible parity file, it isn't possible to sync.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* skip degenerated cases of empty parity, or skipping all */
|
||||
if (blockstart < blockmax) {
|
||||
state_sync_process(state, f, blockstart, blockmax);
|
||||
} else {
|
||||
printf("Nothing to do\n");
|
||||
}
|
||||
|
||||
parity_sync(path, f);
|
||||
|
||||
parity_close(path, f);
|
||||
ret = parity_sync(path, f);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = parity_close(path, f);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "WARNING! A 'snapraid check' command is highly suggested.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue