diff --git a/HISTORY b/HISTORY index 22e1b2c2..ec1b5dfa 100644 --- a/HISTORY +++ b/HISTORY @@ -9,13 +9,18 @@ SnapRAID HISTORY * If the content file is placed in a data disk, it's automatically excluded from the sync process. * Increased by one the minimum number of content files. Before it was only - a suggestion, but now it's a requirement in case you put them in data disks. + a suggestion, but now it's a requirement because you are allowed to put + content files in data disks. * Added checks to ensure that data and parity disks are diffent, and to correctly count the number of copies of "content" files in different disks. * Removed the dependency of the 'disk' order specification in the configuration file. The used order is now saved in the content file to avoid to damage the dual parity in case the order is changed by the user. It also allows to remove or add disks from the array when using a dual parity. +* Improved the 'fix' performance when a lot of files or the parity have to be + recreated from scratch. +* When getting unrecoverable errors, the printed log line now starts with + 'unrecoverable:' instead of 'error:' to allow an easier identification. 1.6 2011/9 - THIS UPDATE IS HIGHLY RECOMMENDED! =============================================== diff --git a/check.c b/check.c index 203ef90a..80890a56 100644 --- a/check.c +++ b/check.c @@ -154,7 +154,7 @@ static int repair(struct snapraid_state* state, unsigned i, unsigned diskmax, st return -1; } -static int state_check_process(struct snapraid_state* state, int fix, int parity_f, int qarity_f, block_off_t blockstart, block_off_t blockmax) +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) { struct snapraid_handle* handle; unsigned diskmax; @@ -396,8 +396,8 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity buffer_zero = buffer[buffermax-1]; /* read the parity */ - if (parity_f != -1) { - ret = parity_read(state->parity, parity_f, i, buffer_parity, state->block_size); + if (parity) { + ret = parity_read(parity, i, buffer_parity, state->block_size); if (ret == -1) { buffer_parity = 0; /* no parity to use */ @@ -410,8 +410,8 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity /* read the qarity */ if (state->level >= 2) { - if (qarity_f != -1) { - ret = parity_read(state->qarity, qarity_f, i, buffer_qarity, state->block_size); + if (qarity) { + ret = parity_read(qarity, i, buffer_qarity, state->block_size); if (ret == -1) { buffer_qarity = 0; /* no qarity to use */ @@ -439,7 +439,7 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity /* print a list of all the errors in files */ for(j=0;jdisk->name, block_file_get(failed[j].block)->sub, block_file_pos(failed[j].block)); + 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)); } } else { /* check parity and q-parity only if all the blocks have it computed */ @@ -499,8 +499,8 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity /* for blocks that are going to have it computed in the sync completion */ if (all_parity) { /* update the parity */ - if (buffer_parity == 0 && parity_f != -1) { - ret = parity_write(state->parity, parity_f, i, buffer[diskmax], state->block_size); + 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 Parity disk, it isn't possible to fix errors on it.\n"); @@ -515,8 +515,8 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity /* update the qarity */ if (state->level >= 2) { - if (buffer_qarity == 0 && qarity_f != -1) { - ret = parity_write(state->qarity, qarity_f, i, buffer[diskmax + 1], state->block_size); + 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"); @@ -758,13 +758,13 @@ bail: void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, block_off_t blockcount) { - char parity_path[PATH_MAX]; - char qarity_path[PATH_MAX]; block_off_t blockmax; data_off_t size; int ret; - int parity_f; - int qarity_f; + struct snapraid_parity parity; + struct snapraid_parity qarity; + struct snapraid_parity* parity_ptr; + struct snapraid_parity* qarity_ptr; unsigned error; if (fix) @@ -785,44 +785,47 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, blockmax = blockstart + blockcount; } - pathcpy(parity_path, sizeof(parity_path), state->parity); - pathcpy(qarity_path, sizeof(qarity_path), state->qarity); - if (fix) { /* if fixing, create the file and open for writing */ /* if it fails, we cannot continue */ - parity_f = parity_create(parity_path, size); - if (parity_f == -1) { + parity_ptr = &parity; + ret = parity_create(parity_ptr, state->parity, size); + if (ret == -1) { fprintf(stderr, "WARNING! Without an accessible Parity file, it isn't possible to fix any error.\n"); exit(EXIT_FAILURE); } if (state->level >= 2) { - qarity_f = parity_create(qarity_path, size); - if (qarity_f == -1) { + qarity_ptr = &qarity; + ret = parity_create(qarity_ptr, state->qarity, size); + if (ret == -1) { fprintf(stderr, "WARNING! Without an accessible Q-Parity file, it isn't possible to fix any error.\n"); exit(EXIT_FAILURE); } } else { - qarity_f = -1; + qarity_ptr = 0; } } else { /* 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_f = parity_open(parity_path); - if (parity_f == -1) { + parity_ptr = &parity; + ret = parity_open(parity_ptr, state->parity); + if (ret == -1) { printf("No accessible Parity file, only files will be checked.\n"); /* continue anyway */ + parity_ptr = 0; } if (state->level >= 2) { - qarity_f = parity_open(qarity_path); - if (qarity_f == -1) { + qarity_ptr = &qarity; + ret = parity_open(qarity_ptr, state->qarity); + if (ret == -1) { printf("No accessible Q-Parity file, only files will be checked.\n"); /* continue anyway */ + qarity_ptr = 0; } } else { - qarity_f = -1; + qarity_ptr = 0; } } @@ -830,7 +833,7 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, /* skip degenerated cases of empty parity, or skipping all */ if (blockstart < blockmax) { - ret = state_check_process(state, fix, parity_f, qarity_f, blockstart, blockmax); + ret = state_check_process(state, fix, parity_ptr, qarity_ptr, blockstart, blockmax); if (ret == -1) { ++error; /* continue, as we are already exiting */ @@ -838,8 +841,8 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, } /* try to close only if opened */ - if (parity_f != -1) { - ret = parity_close(parity_path, parity_f); + if (parity_ptr) { + ret = parity_close(parity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected close error in Parity disk.\n"); ++error; @@ -848,8 +851,8 @@ void state_check(struct snapraid_state* state, int fix, block_off_t blockstart, } if (state->level >= 2) { - if (qarity_f != -1) { - ret = parity_close(qarity_path, qarity_f); + if (qarity_ptr) { + ret = parity_close(qarity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected close error in Q-Parity disk.\n"); ++error; diff --git a/dry.c b/dry.c index b4427523..1f437786 100644 --- a/dry.c +++ b/dry.c @@ -26,7 +26,7 @@ /****************************************************************************/ /* dry */ -static int state_dry_process(struct snapraid_state* state, int parity_f, int qarity_f, block_off_t blockstart, block_off_t blockmax) +static int state_dry_process(struct snapraid_state* state, struct snapraid_parity* parity, struct snapraid_parity* qarity, block_off_t blockstart, block_off_t blockmax) { struct snapraid_handle* handle; unsigned diskmax; @@ -89,8 +89,8 @@ static int state_dry_process(struct snapraid_state* state, int parity_f, int qar } /* read the parity */ - if (parity_f != -1) { - ret = parity_read(state->parity, parity_f, i, buffer, state->block_size); + if (parity) { + ret = parity_read(parity, i, buffer, state->block_size); if (ret == -1) { fprintf(stderr, "error:%u:parity: Read error\n", i); ++error; @@ -99,8 +99,8 @@ static int state_dry_process(struct snapraid_state* state, int parity_f, int qar /* read the qarity */ if (state->level >= 2) { - if (qarity_f != -1) { - ret = parity_read(state->qarity, qarity_f, i, buffer, state->block_size); + if (qarity) { + ret = parity_read(qarity, i, buffer, state->block_size); if (ret == -1) { fprintf(stderr, "error:%u:qarity: Read error\n", i); ++error; @@ -146,12 +146,12 @@ bail: void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount) { - char parity_path[PATH_MAX]; - char qarity_path[PATH_MAX]; block_off_t blockmax; int ret; - int parity_f; - int qarity_f; + struct snapraid_parity parity; + struct snapraid_parity qarity; + struct snapraid_parity* parity_ptr; + struct snapraid_parity* qarity_ptr; unsigned error; printf("Drying...\n"); @@ -168,32 +168,33 @@ void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax = blockstart + blockcount; } - pathcpy(parity_path, sizeof(parity_path), state->parity); - pathcpy(qarity_path, sizeof(qarity_path), state->qarity); - /* open the file for reading */ /* it may fail if the file doesn't exist, in this case we continue to dry the files */ - parity_f = parity_open(parity_path); - if (parity_f == -1) { + parity_ptr = &parity; + ret = parity_open(parity_ptr, state->parity); + if (ret == -1) { printf("No accessible Parity file.\n"); /* continue anyway */ + parity_ptr = 0; } if (state->level >= 2) { - qarity_f = parity_open(qarity_path); - if (qarity_f == -1) { + qarity_ptr = &qarity; + ret = parity_open(qarity_ptr, state->qarity); + if (ret == -1) { printf("No accessible Q-Parity file.\n"); /* continue anyway */ + qarity_ptr = 0; } } else { - qarity_f = -1; + qarity_ptr = 0; } error = 0; /* skip degenerated cases of empty parity, or skipping all */ if (blockstart < blockmax) { - ret = state_dry_process(state, parity_f, qarity_f, blockstart, blockmax); + ret = state_dry_process(state, parity_ptr, qarity_ptr, blockstart, blockmax); if (ret == -1) { ++error; /* continue, as we are already exiting */ @@ -201,8 +202,8 @@ void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t } /* try to close only if opened */ - if (parity_f != -1) { - ret = parity_close(parity_path, parity_f); + if (parity_ptr) { + ret = parity_close(parity_ptr); if (ret == -1) { ++error; /* continue, as we are already exiting */ @@ -210,8 +211,8 @@ void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t } if (state->level >= 2) { - if (qarity_f != -1) { - ret = parity_close(qarity_path, qarity_f); + if (qarity_ptr) { + ret = parity_close(qarity_ptr); if (ret == -1) { ++error; /* continue, as we are already exiting */ diff --git a/handle.c b/handle.c index 47fdb9e8..af51f446 100644 --- a/handle.c +++ b/handle.c @@ -94,6 +94,7 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file) /* invalidate for error */ handle->file = 0; handle->f = -1; + handle->valid_size = 0; fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno)); return -1; @@ -109,25 +110,13 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file) return -1; } - if (handle->st.st_size < file->size) { -#if HAVE_POSIX_FALLOCATE - /* allocate real space */ - ret = posix_fallocate(handle->f, 0, file->size); -#else - /* allocate using a sparse file */ - ret = ftruncate(handle->f, file->size); -#endif - if (ret != 0) { - if (errno == ENOSPC) { - fprintf(stderr, "Failed to grow file '%s' due lack of space.\n", handle->path); - } else if (errno == EACCES) { - fprintf(stderr, "Failed to grow file '%s' for missing write permission.\n", handle->path); - } else { - fprintf(stderr, "Error growing file '%s'. %s.\n", handle->path, strerror(errno)); - } - return -1; - } - } else if (handle->st.st_size > file->size) { + /* get the size of the exising data */ + handle->valid_size = handle->st.st_size; + + /* Here we only truncate the file and we don't grow it */ + /* as allocating real space for it would imply a too big performance drop, */ + /* and allocating sparse space wouldn't improve the safety of subsequent writes. */ + if (handle->st.st_size > file->size) { ret = ftruncate(handle->f, file->size); if (ret != 0) { if (errno == EACCES) { @@ -137,6 +126,9 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file) } return -1; } + + /* adjust the size to the truncated size */ + handle->valid_size = file->size; } #if HAVE_POSIX_FADVISE @@ -175,6 +167,7 @@ int handle_open(struct snapraid_handle* handle, struct snapraid_file* file) /* invalidate for error */ handle->file = 0; handle->f = -1; + handle->valid_size = 0; fprintf(stderr, "Error opening file '%s'. %s.\n", handle->path, strerror(errno)); return -1; @@ -190,6 +183,9 @@ int handle_open(struct snapraid_handle* handle, struct snapraid_file* file) return -1; } + /* get the size of the exising data */ + handle->valid_size = handle->st.st_size; + #if HAVE_POSIX_FADVISE /* advise sequential access */ ret = posix_fadvise(handle->f, 0, 0, POSIX_FADV_SEQUENTIAL); @@ -213,6 +209,7 @@ int handle_close(struct snapraid_handle* handle) /* invalidate for error */ handle->file = 0; handle->f = -1; + handle->valid_size = 0; fprintf(stderr, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno)); return -1; @@ -222,6 +219,7 @@ int handle_close(struct snapraid_handle* handle) /* reset the descriptor */ handle->file = 0; handle->f = -1; + handle->valid_size = 0; return 0; } @@ -234,6 +232,12 @@ int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, un offset = block_file_pos(block) * (data_off_t)block_size; + /* check if we are going to read only not initialized data */ + if (offset >= handle->valid_size) { + fprintf(stderr, "Reading missing data from file '%s' at offset %"PRIu64".\n", handle->path, offset); + return -1; + } + read_size = block_file_size(block, block_size); #if HAVE_PREAD @@ -293,6 +297,11 @@ int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, u return -1; } + /* adjust the size of the valid data */ + if (handle->valid_size < offset + write_size) { + handle->valid_size = offset + write_size; + } + /* Here doesn't make sense to call posix_fadvise(..., POSIX_FADV_DONTNEED) because */ /* at this time the data is still in not yet written and it cannot be discharged. */ @@ -346,6 +355,7 @@ struct snapraid_handle* handle_map(struct snapraid_state* state, unsigned* handl handle[j].disk = 0; handle[j].file = 0; handle[j].f = -1; + handle[j].valid_size = 0; } /* set the vector */ diff --git a/handle.h b/handle.h index a816f469..499580b1 100644 --- a/handle.h +++ b/handle.h @@ -29,6 +29,7 @@ struct snapraid_handle { struct snapraid_file* file; /**< File opened. */ int f; /**< Handle of the file. */ struct stat st; /**< Stat info of the opened file. */ + data_off_t valid_size; /**< Size of the valid data. */ }; /** diff --git a/parity.c b/parity.c index 2b905634..763af830 100644 --- a/parity.c +++ b/parity.c @@ -49,98 +49,120 @@ block_off_t parity_resize(struct snapraid_state* state) return parity_block; } -int parity_create(const char* path, data_off_t size) +int parity_create(struct snapraid_parity* parity, const char* path, data_off_t size) { - struct stat st; int ret; - int f; + + pathcpy(parity->path, sizeof(parity->path), path); /* opening in sequential mode in Windows */ - f = open(path, O_RDWR | O_CREAT | O_BINARY | O_SEQUENTIAL, 0600); - if (f == -1) { - fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno)); + parity->f = open(parity->path, O_RDWR | O_CREAT | O_BINARY | O_SEQUENTIAL, 0600); + if (parity->f == -1) { + fprintf(stderr, "Error opening parity file '%s'. %s.\n", parity->path, strerror(errno)); return -1; } - ret = fstat(f, &st); + /* get the stat info */ + ret = fstat(parity->f, &parity->st); if (ret != 0) { - fprintf(stderr, "Error accessing parity file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error accessing parity file '%s'. %s.\n", parity->path, strerror(errno)); goto bail; } - if (st.st_size < size) { + /* get the size of the exising data */ + parity->valid_size = parity->st.st_size; + + if (parity->st.st_size < size) { #if HAVE_POSIX_FALLOCATE /* allocate real space */ - ret = posix_fallocate(f, 0, size); + ret = posix_fallocate(parity->f, 0, size); #else /* allocate using a sparse file */ - ret = ftruncate(f, size); + ret = ftruncate(parity->f, size); #endif if (ret != 0) { if (errno == ENOSPC) { - fprintf(stderr, "Failed to grow parity file '%s' to size %"PRIu64" due lack of space.\n", path, size); + fprintf(stderr, "Failed to grow parity file '%s' to size %"PRIu64" due lack of space.\n", parity->path, size); } else { - fprintf(stderr, "Error growing parity file '%s' to size %"PRIu64". Do you have enough space? %s.\n", path, size, strerror(errno)); + fprintf(stderr, "Error growing parity file '%s' to size %"PRIu64". Do you have enough space? %s.\n", parity->path, size, strerror(errno)); } goto bail; } - } else if (st.st_size > size) { - ret = ftruncate(f, size); + } else if (parity->st.st_size > size) { + ret = ftruncate(parity->f, size); if (ret != 0) { - fprintf(stderr, "Error truncating parity file '%s' to size %"PRIu64". %s.\n", path, size, strerror(errno)); + fprintf(stderr, "Error truncating parity file '%s' to size %"PRIu64". %s.\n", parity->path, size, strerror(errno)); goto bail; } + + /* adjust the size to the truncated size */ + parity->valid_size = size; } #if HAVE_POSIX_FADVISE /* advise sequential access */ - ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL); + ret = posix_fadvise(parity->f, 0, 0, POSIX_FADV_SEQUENTIAL); if (ret != 0) { - fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error advising parity file '%s'. %s.\n", parity->path, strerror(errno)); goto bail; } #endif - return f; + return 0; bail: - close(f); + close(parity->f); + parity->f = -1; + parity->valid_size = 0; return -1; } -int parity_open(const char* path) +int parity_open(struct snapraid_parity* parity, const char* path) { #if HAVE_POSIX_FADVISE int ret; #endif - int f; + + pathcpy(parity->path, sizeof(parity->path), path); /* opening in sequential mode in Windows */ - f = open(path, O_RDONLY | O_BINARY | O_SEQUENTIAL); - if (f == -1) { - fprintf(stderr, "Error opening parity file '%s'. %s.\n", path, strerror(errno)); + parity->f = open(parity->path, O_RDONLY | O_BINARY | O_SEQUENTIAL); + if (parity->f == -1) { + fprintf(stderr, "Error opening parity file '%s'. %s.\n", parity->path, strerror(errno)); return -1; } + /* get the stat info */ + ret = fstat(parity->f, &parity->st); + if (ret != 0) { + fprintf(stderr, "Error accessing parity file '%s'. %s.\n", parity->path, strerror(errno)); + goto bail; + } + + /* get the size of the exising data */ + parity->valid_size = parity->st.st_size; + #if HAVE_POSIX_FADVISE /* advise sequential access */ - ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL); + ret = posix_fadvise(parity->f, 0, 0, POSIX_FADV_SEQUENTIAL); if (ret != 0) { - fprintf(stderr, "Error advising parity file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error advising parity file '%s'. %s.\n", parity->path, strerror(errno)); goto bail; } #endif - return f; + return 0; #if HAVE_POSIX_FADVISE bail: - close(f); + close(parity->f); + parity->f = -1; + parity->valid_size = 0; return -1; #endif } -int parity_sync(const char* path, int f) +int parity_sync(struct snapraid_parity* parity) { #if HAVE_FSYNC int ret; @@ -148,9 +170,9 @@ int parity_sync(const char* path, int f) /* Ensure that data changes are written to disk. */ /* This is required to ensure that parity is more updated than content */ /* in case of a system crash. */ - ret = fsync(f); + ret = fsync(parity->f); if (ret != 0) { - fprintf(stderr, "Error synching parity file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error synching parity file '%s'. %s.\n", parity->path, strerror(errno)); return -1; } #endif @@ -158,23 +180,31 @@ int parity_sync(const char* path, int f) return 0; } -int parity_close(const char* path, int f) +int parity_close(struct snapraid_parity* parity) { int ret; - ret = close(f); + ret = close(parity->f); if (ret != 0) { + /* invalidate for error */ + parity->f = -1; + parity->valid_size = 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)); + fprintf(stderr, "Error closing parity file '%s'. %s.\n", parity->path, strerror(errno)); return -1; } + /* reset the descriptor */ + parity->f = -1; + parity->valid_size = 0; + return 0; } -int parity_write(const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size) +int parity_write(struct snapraid_parity* parity, block_off_t pos, unsigned char* block_buffer, unsigned block_size) { ssize_t write_ret; data_off_t offset; @@ -182,57 +212,68 @@ int parity_write(const char* path, int f, block_off_t pos, unsigned char* block_ offset = pos * (data_off_t)block_size; #if HAVE_PWRITE - write_ret = pwrite(f, block_buffer, block_size, offset); + write_ret = pwrite(parity->f, block_buffer, block_size, offset); #else - if (lseek(f, offset, SEEK_SET) != offset) { + if (lseek(parity->f, offset, SEEK_SET) != offset) { if (errno == ENOSPC) { - fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", path); + fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", parity->path); } else { - fprintf(stderr, "Error seeking file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error seeking file '%s'. %s.\n", parity->path, strerror(errno)); } return -1; } - write_ret = write(f, block_buffer, block_size); + write_ret = write(parity->f, block_buffer, block_size); #endif if (write_ret != (ssize_t)block_size) { /* conversion is safe because block_size is always small */ if (errno == ENOSPC) { - fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", path); + fprintf(stderr, "Failed to grow parity file '%s' due lack of space.\n", parity->path); } else { - fprintf(stderr, "Error writing file '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "Error writing file '%s'. %s.\n", parity->path, strerror(errno)); } return -1; } + /* adjust the size of the valid data */ + if (parity->valid_size < offset + block_size) { + parity->valid_size = offset + block_size; + } + /* Here doesn't make sense to call posix_fadvise(..., POSIX_FADV_DONTNEED) because */ /* at this time the data is still in not yet written and it cannot be discharged. */ return 0; } -int parity_read(const char* path, int f, block_off_t pos, unsigned char* block_buffer, unsigned block_size) +int parity_read(struct snapraid_parity* parity, block_off_t pos, unsigned char* block_buffer, unsigned block_size) { ssize_t read_ret; data_off_t offset; offset = pos * (data_off_t)block_size; -#if HAVE_PREAD - read_ret = pread(f, block_buffer, block_size, offset); -#else - if (lseek(f, offset, SEEK_SET) != offset) { - fprintf(stderr, "Error seeking file '%s' at offset %"PRIu64". %s.\n", path, offset, strerror(errno)); + /* check if we are going to read only not initialized data */ + if (offset >= parity->valid_size) { + fprintf(stderr, "Reading missing data from file '%s' at offset %"PRIu64".\n", parity->path, offset); return -1; } - read_ret = read(f, block_buffer, block_size); +#if HAVE_PREAD + read_ret = pread(parity->f, block_buffer, block_size, offset); +#else + if (lseek(parity->f, offset, SEEK_SET) != offset) { + fprintf(stderr, "Error seeking file '%s' at offset %"PRIu64". %s.\n", parity->path, offset, strerror(errno)); + return -1; + } + + read_ret = read(parity->f, block_buffer, block_size); #endif if (read_ret < 0) { - fprintf(stderr, "Error reading file '%s' at offset %"PRIu64". %s.\n", path, offset, strerror(errno)); + fprintf(stderr, "Error reading file '%s' at offset %"PRIu64". %s.\n", parity->path, offset, strerror(errno)); return -1; } if (read_ret != (ssize_t)block_size) { /* signed conversion is safe because block_size is always small */ - fprintf(stderr, "File '%s' is smaller than expected. Read %d bytes instead of %d at offset %"PRIu64".\n", path, (int)read_ret, block_size, offset); + fprintf(stderr, "File '%s' is smaller than expected. Read %d bytes instead of %d at offset %"PRIu64".\n", parity->path, (int)read_ret, block_size, offset); return -1; } diff --git a/parity.h b/parity.h index 2967050c..5b0d65d3 100644 --- a/parity.h +++ b/parity.h @@ -21,6 +21,13 @@ /****************************************************************************/ /* parity */ +struct snapraid_parity { + char path[PATH_MAX]; /**< Path of the file. */ + int f; /**< Handle of the file. */ + struct stat st; /**< Stat info of the opened file. */ + data_off_t valid_size; /**< Size of the valid data. */ +}; + /** * Computes the size of the parity data in number of blocks. */ @@ -29,32 +36,32 @@ block_off_t parity_resize(struct snapraid_state* state); /** * Creates the parity file. */ -int parity_create(const char* path, data_off_t size); +int parity_create(struct snapraid_parity* parity, const char* path, data_off_t size); /** * Opens an already existing parity file. */ -int parity_open(const char* path); +int parity_open(struct snapraid_parity* parity, const char* path); /** * Flushes the parity file in the disk. */ -int parity_sync(const char* path, int f); +int parity_sync(struct snapraid_parity* parity); /** * Closes the parity file. */ -int parity_close(const char* path, int f); +int parity_close(struct snapraid_parity* parity); /** * Read a block from the parity file. */ -int parity_read(const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size); +int parity_read(struct snapraid_parity* parity, block_off_t pos, unsigned char* block_buffer, unsigned block_size); /** * Writes a block in the parity file. */ -int parity_write(const char* parity_path, int parity_f, block_off_t pos, unsigned char* block_buffer, unsigned block_size); +int parity_write(struct snapraid_parity* parity, block_off_t pos, unsigned char* block_buffer, unsigned block_size); #endif diff --git a/sync.c b/sync.c index 7f6cab6e..4b324286 100644 --- a/sync.c +++ b/sync.c @@ -27,7 +27,7 @@ /****************************************************************************/ /* sync */ -static int state_sync_process(struct snapraid_state* state, int parity_f, int qarity_f, block_off_t blockstart, block_off_t blockmax) +static int state_sync_process(struct snapraid_state* state, struct snapraid_parity* parity, struct snapraid_parity* qarity, block_off_t blockstart, block_off_t blockmax) { struct snapraid_handle* handle; unsigned diskmax; @@ -224,7 +224,7 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa raid_gen(state->level, buffer, diskmax, state->block_size); /* write the parity */ - ret = parity_write(state->parity, parity_f, i, buffer[diskmax], state->block_size); + ret = parity_write(parity, i, buffer[diskmax], state->block_size); if (ret == -1) { fprintf(stderr, "DANGER! Write error in the Parity disk, it isn't possible to sync.\n"); printf("Stopping at block %u\n", i); @@ -234,7 +234,7 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa /* write the qarity */ if (state->level >= 2) { - ret = parity_write(state->qarity, qarity_f, i, buffer[diskmax+1], state->block_size); + ret = parity_write(qarity, i, buffer[diskmax+1], state->block_size); if (ret == -1) { fprintf(stderr, "DANGER! Write error in the Q-Parity disk, it isn't possible to sync.\n"); printf("Stopping at block %u\n", i); @@ -291,13 +291,13 @@ bail: int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount) { - char parity_path[PATH_MAX]; - char qarity_path[PATH_MAX]; block_off_t blockmax; data_off_t size; int ret; - int parity_f; - int qarity_f; + struct snapraid_parity parity; + struct snapraid_parity qarity; + struct snapraid_parity* parity_ptr; + struct snapraid_parity* qarity_ptr; unsigned unrecoverable_error; printf("Syncing...\n"); @@ -315,45 +315,44 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax = blockstart + blockcount; } - pathcpy(parity_path, sizeof(parity_path), state->parity); - pathcpy(qarity_path, sizeof(qarity_path), state->qarity); - /* create the file and open for writing */ - parity_f = parity_create(parity_path, size); - if (parity_f == -1) { + parity_ptr = &parity; + ret = parity_create(parity_ptr, state->parity, size); + if (ret == -1) { fprintf(stderr, "WARNING! Without an accessible Parity file, it isn't possible to sync.\n"); exit(EXIT_FAILURE); } if (state->level >= 2) { - qarity_f = parity_create(qarity_path, size); - if (qarity_f == -1) { + qarity_ptr = &qarity; + ret = parity_create(qarity_ptr, state->qarity, size); + if (ret == -1) { fprintf(stderr, "WARNING! Without an accessible Q-Parity file, it isn't possible to sync.\n"); exit(EXIT_FAILURE); } } else { - qarity_f = -1; + qarity_ptr = 0; } unrecoverable_error = 0; /* skip degenerated cases of empty parity, or skipping all */ if (blockstart < blockmax) { - ret = state_sync_process(state, parity_f, qarity_f, blockstart, blockmax); + ret = state_sync_process(state, parity_ptr, qarity_ptr, blockstart, blockmax); if (ret == -1) { ++unrecoverable_error; /* continue, as we are already exiting */ } } - ret = parity_sync(parity_path, parity_f); + ret = parity_sync(parity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected sync error in Parity disk.\n"); ++unrecoverable_error; /* continue, as we are already exiting */ } - ret = parity_close(parity_path, parity_f); + ret = parity_close(parity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected close error in Parity disk.\n"); ++unrecoverable_error; @@ -361,14 +360,14 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t } if (state->level >= 2) { - ret = parity_sync(qarity_path, qarity_f); + ret = parity_sync(qarity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected sync error in Q-Parity disk.\n"); ++unrecoverable_error; /* continue, as we are already exiting */ } - ret = parity_close(qarity_path, qarity_f); + ret = parity_close(qarity_ptr); if (ret == -1) { fprintf(stderr, "DANGER! Unexpected close error in Q-Parity disk.\n"); ++unrecoverable_error;