From fa775d24a8cc92c37ed24bd7b83f9c82dadd88d4 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sat, 3 Oct 2015 10:43:02 +0200 Subject: [PATCH] More interface changes to prepare to the use of tree mapping --- cmdline/check.c | 119 ++++++++++++++++------------ cmdline/dry.c | 9 ++- cmdline/elem.c | 97 ++++++++++++++--------- cmdline/elem.h | 201 ++++++++++++++++++----------------------------- cmdline/handle.c | 12 +-- cmdline/handle.h | 4 +- cmdline/scan.c | 7 +- cmdline/scrub.c | 37 ++++----- cmdline/search.c | 8 +- cmdline/search.h | 2 +- cmdline/state.c | 21 +++-- cmdline/sync.c | 92 ++++++++++------------ 12 files changed, 295 insertions(+), 314 deletions(-) diff --git a/cmdline/check.c b/cmdline/check.c index c99bf893..878e514f 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -56,26 +56,26 @@ struct failed_struct { int is_outofdate; unsigned index; /**< Index of the failed block. */ - struct snapraid_block* block; /**< The failed block, or BLOCK_DELETED for a deleted block */ - struct snapraid_handle* handle; /**< The file containing the failed block, or 0 for a deleted block */ + struct snapraid_block* block; /**< The failed block */ + struct snapraid_disk* disk; /**< The failed disk. */ + struct snapraid_file* file; /**< The failed file. 0 for DELETED block. */ + block_off_t file_pos; /**< Offset inside the file */ + struct snapraid_handle* handle; /**< The handle containing the failed block, or 0 for a DELETED block */ }; /** * Checks if a block hash matches the specified buffer. * Return ==0 if equal */ -static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_block* block, unsigned char* buffer, unsigned char* buffer_zero) +static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_block* block, unsigned pos_size, unsigned char* buffer, unsigned char* buffer_zero) { unsigned char hash[HASH_SIZE]; - unsigned size; - - size = block_file_size(block, state->block_size); /* now compute the hash of the valid part */ if (rehash) { - memhash(state->prevhash, state->prevhashseed, hash, buffer, size); + memhash(state->prevhash, state->prevhashseed, hash, buffer, pos_size); } else { - memhash(state->hash, state->hashseed, hash, buffer, size); + memhash(state->hash, state->hashseed, hash, buffer, pos_size); } /* compare the hash */ @@ -84,8 +84,8 @@ static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_bl } /* compare to the end of the block */ - if (size < state->block_size) { - if (memcmp(buffer + size, buffer_zero + size, state->block_size - size) != 0) { + if (pos_size < state->block_size) { + if (memcmp(buffer + pos_size, buffer_zero + pos_size, state->block_size - pos_size) != 0) { return -1; } } @@ -111,7 +111,8 @@ static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned d && block_has_updated_hash(failed[failed_map[j]].block) ) { /* if a hash doesn't match, fail the check */ - if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0) { + unsigned pos_size = file_block_size(failed[failed_map[j]].file, failed[failed_map[j]].file_pos, state->block_size); + if (blockcmp(state, rehash, failed[failed_map[j]].block, pos_size, buffer[failed[failed_map[j]].index], buffer_zero) != 0) { log_tag("hash_error: Hash mismatch on entry %u\n", failed_map[j]); return 0; } @@ -188,7 +189,7 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u for (i = 0; i < failed_count; ++i) { /* if we are expected to recover this block */ if (!failed[failed_map[i]].is_outofdate - /* if the block has a hash to check */ + /* if the block has a hash to check */ && block_has_updated_hash(failed[failed_map[i]].block) ) has_hash = 1; @@ -331,14 +332,12 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign else data = "good"; - if (failed[j].handle) { - const char* disk; - const char* sub; - struct snapraid_handle* handle = failed[j].handle; - disk = handle->disk->name; - sub = block_file_get(block)->sub; + if (failed[j].file) { + struct snapraid_disk* disk = failed[j].disk; + struct snapraid_file* file = failed[j].file; + block_off_t file_pos = failed[j].file_pos; - log_tag("entry:%u:%s:%s:%s:%s:%s:%u:\n", j, desc, hash, data, disk, esc(sub), block_file_pos(block)); + log_tag("entry:%u:%s:%s:%s:%s:%s:%u:\n", j, desc, hash, data, disk->name, esc(file->sub), file_pos); } else { log_tag("entry:%u:%s:%s:%s:\n", j, desc, hash, data); } @@ -372,7 +371,7 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign if ((block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP) /* try to fetch the block using the known hash */ && (state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0 - || state_search_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0) + || state_search_fetch(state, rehash, failed[j].file, failed[j].file_pos, failed[j].block, buffer[failed[j].index]) == 0) ) { /* we already have corrected it! */ log_tag("hash_import: Fixed entry %u\n", j); @@ -442,7 +441,8 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign /* instead, if the hash matches, it could be either that the */ /* block after the sync has this hash, or that */ /* we restored the block before the 'sync'. */ - if (blockcmp(state, rehash, failed[j].block, buffer[failed[j].index], buffer_zero) == 0) { + unsigned pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size); + if (blockcmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index], buffer_zero) == 0) { /* it may contain garbage */ failed[j].is_outofdate = 1; @@ -610,6 +610,7 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s struct snapraid_disk* disk; struct snapraid_file* collide_file; struct snapraid_file* file; + block_off_t file_pos; char path[PATH_MAX]; uint64_t inode; @@ -625,11 +626,11 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s continue; } - file = block_file_get(block); + file = fs_par2file_get(disk, i, &file_pos); pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub); /* if it isn't the last block in the file */ - if (!block_is_last(block)) { + if (!file_block_is_last(file, file_pos)) { /* nothing to do */ continue; } @@ -823,11 +824,12 @@ static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct /* try to recover all files, even the ones without hash */ /* because in some cases we can recover also them */ - if (block_has_file(block) - && !file_flag_has(block_file_get(block), FILE_IS_EXCLUDED) /* only if the file is not filtered out */ - ) { - one_tocheck = 1; - break; + if (block_has_file(block)) { + struct snapraid_file* file = fs_par2file_get(handle[j].disk, i, 0); + if (!file_flag_has(file, FILE_IS_EXCLUDED)) { /* only if the file is not filtered out */ + one_tocheck = 1; + break; + } } } @@ -932,6 +934,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna struct snapraid_disk* disk; struct snapraid_block* block; struct snapraid_file* file; + block_off_t file_pos; unsigned block_state; /* if the disk position is not used */ @@ -968,17 +971,20 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* store it in the failed set, because potentially */ /* the parity may be still computed with the previous content */ - failed[failed_count].is_bad = 0; + failed[failed_count].is_bad = 0; /* note that is_bad==0 <=> file==0 */ failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = 0; + failed[failed_count].file_pos = 0; failed[failed_count].handle = 0; ++failed_count; continue; } /* get the file of this block */ - file = block_file_get(block); + file = fs_par2file_get(disk, i, &file_pos); /* if we are only hashing, we can skip excluded files and don't even read them */ if (state->opt.auditonly && file_flag_has(file, FILE_IS_EXCLUDED)) { @@ -1036,10 +1042,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = file; + failed[failed_count].file_pos = file_pos; failed[failed_count].handle = &handle[j]; ++failed_count; - log_tag("error:%u:%s:%s: Open error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); + log_tag("error:%u:%s:%s: Open error at position %u\n", i, disk->name, esc(file->sub), file_pos); ++error; continue; } @@ -1050,9 +1059,9 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna && !file_flag_has(file, FILE_IS_EXCLUDED)) { /* check if the file is changed */ - if (handle[j].st.st_size != block_file_get(block)->size - || handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec + if (handle[j].st.st_size != file->size + || handle[j].st.st_mtime != file->mtime_sec + || STAT_NSEC(&handle[j].st) != file->mtime_nsec /* don't check the inode to support filesystem without persistent inodes */ ) { /* report that the file is not synced */ @@ -1093,7 +1102,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, + read_size = handle_read(&handle[j], file_pos, buffer[j], state->block_size, log_error, state->opt.expected_missing ? log_expected : 0); if (read_size == -1) { /* save the failed block for the check/fix */ @@ -1101,10 +1110,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = file; + failed[failed_count].file_pos = file_pos; failed[failed_count].handle = &handle[j]; ++failed_count; - log_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); + log_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(file->sub), file_pos); ++error; continue; } @@ -1121,6 +1133,9 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = file; + failed[failed_count].file_pos = file_pos; failed[failed_count].handle = &handle[j]; ++failed_count; continue; @@ -1144,10 +1159,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = file; + failed[failed_count].file_pos = file_pos; failed[failed_count].handle = &handle[j]; ++failed_count; - log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(file->sub), block_file_pos(block), diff); + log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(file->sub), file_pos, diff); ++error; continue; } @@ -1160,6 +1178,9 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna failed[failed_count].is_outofdate = 0; failed[failed_count].index = j; failed[failed_count].block = block; + failed[failed_count].disk = disk; + failed[failed_count].file = file; + failed[failed_count].file_pos = file_pos; failed[failed_count].handle = &handle[j]; ++failed_count; continue; @@ -1206,13 +1227,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* print a list of all the errors in files */ for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad) - log_tag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); + log_tag("unrecoverable:%u:%s:%s: Unrecoverable error at position %u\n", i, failed[j].disk->name, esc(failed[j].file->sub), failed[j].file_pos); } /* 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); + file_flag_set(failed[j].file, FILE_IS_DAMAGED); } } else { /* now counts partial recovers */ @@ -1224,7 +1245,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad && failed[j].is_outofdate) { ++partial_recover_error; - log_tag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); + log_tag("unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u\n", i, failed[j].disk->name, esc(failed[j].file->sub), failed[j].file_pos); } } if (partial_recover_error != 0) { @@ -1258,15 +1279,15 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna continue; /* do not fix if the file is excluded */ - if (file_flag_has(block_file_get(failed[j].block), FILE_IS_EXCLUDED) - || (state->opt.syncedonly && file_flag_has(block_file_get(failed[j].block), FILE_IS_UNSYNCED))) + if (file_flag_has(failed[j].file, FILE_IS_EXCLUDED) + || (state->opt.syncedonly && file_flag_has(failed[j].file, FILE_IS_UNSYNCED))) continue; - ret = handle_write(failed[j].handle, failed[j].block, buffer[failed[j].index], state->block_size); + ret = handle_write(failed[j].handle, failed[j].file_pos, buffer[failed[j].index], state->block_size); if (ret == -1) { /* LCOV_EXCL_START */ /* mark the file as damaged */ - file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED); + file_flag_set(failed[j].file, FILE_IS_DAMAGED); if (errno == EACCES) { log_fatal("WARNING! Please give write permission to the file.\n"); @@ -1283,15 +1304,15 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* 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); + file_flag_set(failed[j].file, FILE_IS_DAMAGED); continue; } /* mark the file as containing some fixes */ /* note that it could be also marked as damaged in other iterations */ - file_flag_set(block_file_get(failed[j].block), FILE_IS_FIXED); + file_flag_set(failed[j].file, FILE_IS_FIXED); - log_tag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].handle->disk->name, esc(block_file_get(failed[j].block)->sub), block_file_pos(failed[j].block)); + log_tag("fixed:%u:%s:%s: Fixed data error at position %u\n", i, failed[j].disk->name, esc(failed[j].file->sub), failed[j].file_pos); ++recovered_error; } @@ -1323,7 +1344,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* meaning that we could fix this file if we try */ for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad) { - file_flag_set(block_file_get(failed[j].block), FILE_IS_FIXED); + file_flag_set(failed[j].file, FILE_IS_FIXED); } } } @@ -1333,7 +1354,7 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna /* to report that the file is damaged, and we don't know if we can fix it */ for (j = 0; j < failed_count; ++j) { if (failed[j].is_bad) { - file_flag_set(block_file_get(failed[j].block), FILE_IS_DAMAGED); + file_flag_set(failed[j].file, FILE_IS_DAMAGED); } } } diff --git a/cmdline/dry.c b/cmdline/dry.c index aaa32349..c9a460d2 100644 --- a/cmdline/dry.c +++ b/cmdline/dry.c @@ -58,9 +58,10 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit /* for each disk, process the block */ for (j = 0; j < diskmax; ++j) { int read_size; - struct snapraid_block* block = BLOCK_EMPTY; + struct snapraid_block* block; struct snapraid_disk* disk = handle[j].disk; struct snapraid_file* file; + block_off_t file_pos; if (!disk) { /* if no disk, nothing to do */ @@ -75,7 +76,7 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit } /* get the file of this block */ - file = block_file_get(block); + file = fs_par2file_get(disk, i, &file_pos); /* until now is CPU */ state_usage_cpu(state); @@ -108,9 +109,9 @@ static int state_dry_process(struct snapraid_state* state, struct snapraid_parit } /* read from the file */ - read_size = handle_read(&handle[j], block, buffer_aligned, state->block_size, log_error, 0); + read_size = handle_read(&handle[j], file_pos, buffer_aligned, state->block_size, log_error, 0); if (read_size == -1) { - log_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(file->sub), block_file_pos(block)); + log_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(file->sub), file_pos); ++error; continue; } diff --git a/cmdline/elem.c b/cmdline/elem.c index d147e353..8381b8d7 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -355,43 +355,22 @@ int filter_content(tommy_list* contentlist, const char* path) return 0; } -block_off_t block_file_pos(struct snapraid_block* block) +/** + * Sets the file containing the block. + */ +static void block_file_set(struct snapraid_block* block, struct snapraid_file* file) { - struct snapraid_file* file = block_file_get(block); + uintptr_t ptr = (uintptr_t)file; - if (block < file->blockvec || block >= file->blockvec + file->blockmax) { + /* ensure that the pointer doesn't use the flag space */ + if ((ptr & (uintptr_t)BLOCK_STATE_MASK) != 0) { /* LCOV_EXCL_START */ - log_fatal("Internal inconsistency in block %u ownership\n", block->parity_pos); + log_fatal("Internal error for pointer not aligned\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } - return block - file->blockvec; -} - -int block_is_last(struct snapraid_block* block) -{ - struct snapraid_file* file = block_file_get(block); - - return block == file->blockvec + file->blockmax - 1; -} - -unsigned block_file_size(struct snapraid_block* block, unsigned block_size) -{ - block_off_t pos = block_file_pos(block); - - /* if it's the last block */ - if (pos + 1 == block_file_get(block)->blockmax) { - unsigned remainder; - if (block_file_get(block)->size == 0) - return 0; - remainder = block_file_get(block)->size % block_size; - if (remainder == 0) - remainder = block_size; - return remainder; - } - - return block_size; + block->private_file_mixed = (block->private_file_mixed & (uintptr_t)BLOCK_STATE_MASK) | ptr; } struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical) @@ -411,8 +390,8 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_ file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block)); for (i = 0; i < file->blockmax; ++i) { - file->blockvec[i].parity_pos = POS_INVALID; - file->blockvec[i].file_mixed = 0; + file->blockvec[i].private_parity_pos = POS_INVALID; + file->blockvec[i].private_file_mixed = 0; /* set the file */ block_file_set(&file->blockvec[i], file); @@ -442,8 +421,8 @@ struct snapraid_file* file_dup(struct snapraid_file* copy) file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block)); for (i = 0; i < file->blockmax; ++i) { - file->blockvec[i].file_mixed = copy->blockvec[i].file_mixed; - file->blockvec[i].parity_pos = copy->blockvec[i].parity_pos; + file->blockvec[i].private_file_mixed = copy->blockvec[i].private_file_mixed; + file->blockvec[i].private_parity_pos = copy->blockvec[i].private_parity_pos; memcpy(file->blockvec[i].hash, copy->blockvec[i].hash, HASH_SIZE); /* set the file to overwrite the copied one */ @@ -515,6 +494,37 @@ const char* file_name(const struct snapraid_file* file) return r; } +unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size) +{ + /* if it's the last block */ + if (file_pos + 1 == file->blockmax) { + unsigned remainder; + if (file->size == 0) + return 0; + remainder = file->size % block_size; + if (remainder == 0) + remainder = block_size; + return remainder; + } + + return block_size; +} + +int file_block_is_last(struct snapraid_file* file, block_off_t file_pos) +{ + if (file_pos == 0 && file->blockmax == 0) + return 1; + + if (file_pos >= file->blockmax) { + /* LCOV_EXCL_START */ + log_fatal("Internal inconsistency in file block position\n"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + return file_pos == file->blockmax - 1; +} + int file_inode_compare_to_arg(const void* void_arg, const void* void_data) { const uint64_t* arg = void_arg; @@ -745,20 +755,29 @@ void fs_par2file_set(struct snapraid_disk* disk, block_off_t parity_pos, struct assert(file_pos < file->blockmax); - file->blockvec[file_pos].parity_pos = parity_pos; + file->blockvec[file_pos].private_parity_pos = parity_pos; } struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos) { struct snapraid_block* block = fs_par2block_get(disk, parity_pos); + struct snapraid_file* file = (struct snapraid_file*)(block->private_file_mixed & ~(uintptr_t)BLOCK_STATE_MASK); if (block == BLOCK_EMPTY) return 0; - if (file_pos) - *file_pos = block_file_pos(block); + if (file_pos) { + if (block < file->blockvec || block >= file->blockvec + file->blockmax) { + /* LCOV_EXCL_START */ + log_fatal("Internal inconsistency in block %u ownership\n", block->private_parity_pos); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } - return block_file_get(block); + *file_pos = block - file->blockvec; + } + + return file; } block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) @@ -767,7 +786,7 @@ block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* fi assert(file_pos < file->blockmax); - return file->blockvec[file_pos].parity_pos; + return file->blockvec[file_pos].private_parity_pos; } struct snapraid_block* fs_file2block_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos) diff --git a/cmdline/elem.h b/cmdline/elem.h index 5f488fe2..e0bcfe3c 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -68,6 +68,17 @@ struct snapraid_filter { tommy_node node; /**< Next node in the list. */ }; +/** + * Block pointer used to represent unused blocks. + */ +#define BLOCK_EMPTY 0 + +/** + * This block is an empty one. + * Note that an empty block is represent with ::BLOCK_EMPTY. + */ +#define BLOCK_STATE_EMPTY 0 + /** * The block has both the hash and the parity computed. * This is the normal state of a saved block. @@ -147,27 +158,15 @@ struct snapraid_filter { */ #define BLOCK_STATE_MASK 7 -/** - * This block is an empty one. - * Note that this state cannot be stored inside the block, - * and it's represented by the block ::BLOCK_EMPTY. - */ -#define BLOCK_STATE_EMPTY 8 - /** * Block of a file. */ struct snapraid_block { - uintptr_t file_mixed; /**< Back pointer to the file owning this block, mixed with some flags. */ - block_off_t parity_pos; /**< Position of the block in the parity. */ + uintptr_t private_file_mixed; /**< Back pointer to the file owning this block, mixed with some flags. */ + block_off_t private_parity_pos; /**< Position of the block in the parity. */ unsigned char hash[HASH_SIZE]; /**< Hash of the block. */ }; -/** - * Block pointer used to mark unused blocks. - */ -#define BLOCK_EMPTY 0 - /** * If a file is present in the disk. * It's used only in scan to detect present and missing files. @@ -465,85 +464,6 @@ int filter_dir(tommy_list* filterlist, struct snapraid_filter** reason, const ch */ int filter_content(tommy_list* contentlist, const char* path); -/** - * Gets the file containing the block. - */ -static inline struct snapraid_file* block_file_get(struct snapraid_block* block) -{ - return (struct snapraid_file*)(block->file_mixed & ~(uintptr_t)BLOCK_STATE_MASK); -} - -/** - * Sets the file containing the block. - */ -static inline void block_file_set(struct snapraid_block* block, struct snapraid_file* file) -{ - uintptr_t ptr = (uintptr_t)file; - - /* ensure that the pointer doesn't use the flag space */ - if ((ptr & (uintptr_t)BLOCK_STATE_MASK) != 0) { - /* LCOV_EXCL_START */ - log_fatal("Internal error for pointer not aligned\n"); - exit(EXIT_FAILURE); - /* LCOV_EXCL_STOP */ - } - - block->file_mixed = (block->file_mixed & (uintptr_t)BLOCK_STATE_MASK) | ptr; -} - -/** - * Get the state of the block. - * - * For this function, it's allowed to pass a NULL block - * pointer than results in the BLOCK_STATE_EMPTY state. - */ -static inline unsigned block_state_get(const struct snapraid_block* block) -{ - if (block == BLOCK_EMPTY) - return BLOCK_STATE_EMPTY; - - return block->file_mixed & BLOCK_STATE_MASK; -} - -static inline void block_state_set(struct snapraid_block* block, unsigned state) -{ - /* ensure that the state can be stored inside the file pointer */ - if ((state & BLOCK_STATE_MASK) != state) { - /* LCOV_EXCL_START */ - log_fatal("Internal error when setting the block state %u\n", state); - exit(EXIT_FAILURE); - /* LCOV_EXCL_STOP */ - } - - block->file_mixed &= ~(uintptr_t)BLOCK_STATE_MASK; - block->file_mixed |= state & BLOCK_STATE_MASK; -} - -/** - * Checks if the specified block has an updated hash. - * - * Note that EMPTY / CHG / DELETED return 0. - */ -static inline int block_has_updated_hash(const struct snapraid_block* block) -{ - unsigned state = block_state_get(block); - - return state == BLOCK_STATE_BLK || state == BLOCK_STATE_REP; -} - -/** - * Checks if the specified block has a past hash, - * i.e. the hash of the data that it's now overwritten or lost. - * - * Note that EMPTY / BLK / REP return 0. - */ -static inline int block_has_past_hash(const struct snapraid_block* block) -{ - unsigned state = block_state_get(block); - - return state == BLOCK_STATE_CHG || state == BLOCK_STATE_DELETED; -} - /** * Checks if the specified hash is invalid. * @@ -600,6 +520,59 @@ static inline void hash_zero_set(unsigned char* hash) memset(hash, 0xFF, HASH_SIZE); } +/** + * Get the state of the block. + * + * For this function, it's allowed to pass a NULL block + * pointer than results in the BLOCK_STATE_EMPTY state. + */ +static inline unsigned block_state_get(const struct snapraid_block* block) +{ + if (block == BLOCK_EMPTY) + return BLOCK_STATE_EMPTY; + + return block->private_file_mixed & BLOCK_STATE_MASK; +} + +static inline void block_state_set(struct snapraid_block* block, unsigned state) +{ + /* ensure that the state can be stored inside the file pointer */ + if ((state & BLOCK_STATE_MASK) != state) { + /* LCOV_EXCL_START */ + log_fatal("Internal error when setting the block state %u\n", state); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + block->private_file_mixed &= ~(uintptr_t)BLOCK_STATE_MASK; + block->private_file_mixed |= state & BLOCK_STATE_MASK; +} + +/** + * Checks if the specified block has an updated hash. + * + * Note that EMPTY / CHG / DELETED return 0. + */ +static inline int block_has_updated_hash(const struct snapraid_block* block) +{ + unsigned state = block_state_get(block); + + return state == BLOCK_STATE_BLK || state == BLOCK_STATE_REP; +} + +/** + * Checks if the specified block has a past hash, + * i.e. the hash of the data that it's now overwritten or lost. + * + * Note that EMPTY / BLK / REP return 0. + */ +static inline int block_has_past_hash(const struct snapraid_block* block) +{ + unsigned state = block_state_get(block); + + return state == BLOCK_STATE_CHG || state == BLOCK_STATE_DELETED; +} + /** * Checks if the specified block is part of a file. * @@ -638,37 +611,6 @@ static inline int block_has_file_and_valid_parity(const struct snapraid_block* b return state == BLOCK_STATE_BLK; } -/** - * Gets the relative position of a block inside the file. - */ -block_off_t block_file_pos(struct snapraid_block* block); - -/** - * Checks if the block is the last in the file. - */ -int block_is_last(struct snapraid_block* block); - -/** - * Gets the size in bytes of the block. - * If it's the last block of a file it could be less than block_size. - */ -unsigned block_file_size(struct snapraid_block* block, unsigned block_size); - -/** - * Allocates a deleted block. - */ -struct snapraid_deleted* deleted_alloc(void); - -/** - * Allocates a deleted block from a real one. - */ -struct snapraid_deleted* deleted_dup(struct snapraid_block* block); - -/** - * Frees a deleted block. - */ -void deleted_free(struct snapraid_deleted* deleted); - static inline int file_flag_has(const struct snapraid_file* file, unsigned mask) { return (file->flag & mask) == mask; @@ -714,6 +656,17 @@ void file_copy(struct snapraid_file* src_file, struct snapraid_file* dest_file); */ const char* file_name(const struct snapraid_file* file); +/** + * Checks if the block is the last in the file. + */ +int file_block_is_last(struct snapraid_file* file, block_off_t file_pos); + +/** + * Gets the size in bytes of the block. + * If it's the last block of a file it could be less than block_size. + */ +unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size); + /** * Compares a file with an inode. */ diff --git a/cmdline/handle.c b/cmdline/handle.c index bfb0506c..0f211814 100644 --- a/cmdline/handle.c +++ b/cmdline/handle.c @@ -248,14 +248,14 @@ int handle_close(struct snapraid_handle* handle) return 0; } -int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing) +int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing) { ssize_t read_ret; data_off_t offset; unsigned read_size; unsigned count; - offset = block_file_pos(block) * (data_off_t)block_size; + offset = file_pos * (data_off_t)block_size; if (!out_missing) out_missing = out; @@ -270,7 +270,7 @@ int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, un return -1; } - read_size = block_file_size(block, block_size); + read_size = file_block_size(handle->file, file_pos, block_size); #if !HAVE_PREAD if (lseek(handle->f, offset, SEEK_SET) != offset) { @@ -323,15 +323,15 @@ int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, un return read_size; } -int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size) +int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size) { ssize_t write_ret; data_off_t offset; unsigned write_size; - offset = block_file_pos(block) * (data_off_t)block_size; + offset = file_pos * (data_off_t)block_size; - write_size = block_file_size(block, block_size); + write_size = file_block_size(handle->file, file_pos, block_size); #if HAVE_PWRITE write_ret = pwrite(handle->f, block_buffer, write_size, offset); diff --git a/cmdline/handle.h b/cmdline/handle.h index 7ef2432a..9a58d5b2 100644 --- a/cmdline/handle.h +++ b/cmdline/handle.h @@ -64,12 +64,12 @@ int handle_close(struct snapraid_handle* handle); * Read a block from a file. * If the read block is shorter, it's padded with 0. */ -int handle_read(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing); +int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing); /** * Writes a block to a file. */ -int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, unsigned char* block_buffer, unsigned block_size); +int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size); /** * Changes the modification time of the file to the saved value. diff --git a/cmdline/scan.c b/cmdline/scan.c index 25add9c3..0ab47b0f 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -178,7 +178,7 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file* ++parity_pos; /* set the position */ - block->parity_pos = parity_pos; + block->private_parity_pos = parity_pos; /* get block specific info */ info = info_get(&state->infoarr, parity_pos); @@ -304,16 +304,13 @@ static void scan_file_deallocate(struct snapraid_scan* scan, struct snapraid_fil break; default : /* LCOV_EXCL_START */ - log_fatal("Internal inconsistency in deallocating for block %u state %u\n", block->parity_pos, block_state); + log_fatal("Internal inconsistency in deallocating for block %u state %u\n", parity_pos, block_state); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } /* set the block as deleted */ block_state_set(block, BLOCK_STATE_DELETED); - - /* remove the file reference */ - block_file_set(block, 0); } /* insert it in the list of deleted blocks */ diff --git a/cmdline/scrub.c b/cmdline/scrub.c index 89cae70d..1561b946 100644 --- a/cmdline/scrub.c +++ b/cmdline/scrub.c @@ -184,6 +184,8 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par struct snapraid_block* block; int file_is_unsynced; struct snapraid_disk* disk = handle[j].disk; + struct snapraid_file* file; + block_off_t file_pos; /* if the file on this disk is synced */ /* if not, silent errors are assumed as expected error */ @@ -207,6 +209,9 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par continue; } + /* get the file of this block */ + file = fs_par2file_get(disk, i, &file_pos); + /* if the block is unsynced, errors are expected */ if (block_has_invalid_parity(block)) { /* report that the block and the file are not synced */ @@ -219,16 +224,16 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par state_usage_cpu(state); /* if the file is different than the current one, close it */ - if (handle[j].file != 0 && handle[j].file != block_file_get(block)) { + if (handle[j].file != 0 && handle[j].file != file) { /* keep a pointer at the file we are going to close for error reporting */ - struct snapraid_file* file = handle[j].file; + struct snapraid_file* report = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to scrub.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -236,7 +241,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par goto bail; } - log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to scrub.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -246,10 +251,8 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par } } - ret = handle_open(&handle[j], block_file_get(block), state->file_mode, log_error, 0); + ret = handle_open(&handle[j], file, state->file_mode, log_error, 0); if (ret == -1) { - /* file we have tried to open for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ log_tag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); @@ -268,9 +271,9 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par } /* check if the file is changed */ - if (handle[j].st.st_size != block_file_get(block)->size - || handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec + if (handle[j].st.st_size != file->size + || handle[j].st.st_mtime != file->mtime_sec + || STAT_NSEC(&handle[j].st) != file->mtime_nsec /* don't check the inode to support filesystem without persistent inodes */ ) { /* report that the block and the file are not synced */ @@ -283,12 +286,10 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* from the last sync, as we are expected to return errors if running */ /* in an unsynced array. This is just like the check command. */ - read_size = handle_read(&handle[j], block, buffer[j], state->block_size, log_error, 0); + read_size = handle_read(&handle[j], file_pos, buffer[j], state->block_size, log_error, 0); if (read_size == -1) { - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); if (io_error >= state->opt.io_error_limit) { /* LCOV_EXCL_START */ log_fatal("DANGER! Too many input/output read error in a data disk, it isn't possible to scrub.\n"); @@ -299,13 +300,13 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par /* LCOV_EXCL_STOP */ } - log_error("Input/Output error in file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); + log_error("Input/Output error in file '%s' at position '%u'\n", handle[j].path, file_pos); ++io_error; io_error_on_this_block = 1; continue; } - log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); ++error; error_on_this_block = 1; continue; @@ -332,14 +333,14 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par if (memcmp(hash, block->hash, HASH_SIZE) != 0) { unsigned diff = memdiff(hash, block->hash, HASH_SIZE); - log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(handle[j].file->sub), block_file_pos(block), diff); + log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(file->sub), file_pos, diff); /* it's a silent error only if we are dealing with synced files */ if (file_is_unsynced) { ++error; error_on_this_block = 1; } else { - log_error("Data error in file '%s' at position '%u', diff bits %u\n", handle[j].path, block_file_pos(block), diff); + log_error("Data error in file '%s' at position '%u', diff bits %u\n", handle[j].path, file_pos, diff); ++silent_error; silent_error_on_this_block = 1; } diff --git a/cmdline/search.c b/cmdline/search.c index 031c4f03..cd34c136 100644 --- a/cmdline/search.c +++ b/cmdline/search.c @@ -130,7 +130,7 @@ int search_file_compare(const void* void_arg, const void* void_data) return 0; } -int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_block* missing_block, unsigned char* buffer) +int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer) { struct snapraid_search_file* file; tommy_uint32_t file_hash; @@ -138,10 +138,10 @@ int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapra arg.state = state; arg.block = missing_block; - arg.file = block_file_get(missing_block); + arg.file = missing_file; arg.buffer = buffer; - arg.offset = state->block_size * (data_off_t)block_file_pos(missing_block); - arg.read_size = block_file_size(missing_block, state->block_size); + arg.offset = state->block_size * (data_off_t)missing_file_pos; + arg.read_size = file_block_size(missing_file, missing_file_pos, state->block_size); arg.prevhash = prevhash; file_hash = file_stamp_hash(arg.file->size, arg.file->mtime_sec, arg.file->mtime_nsec); diff --git a/cmdline/search.h b/cmdline/search.h index 6664b625..8226a1d7 100644 --- a/cmdline/search.h +++ b/cmdline/search.h @@ -48,7 +48,7 @@ void search_file_free(struct snapraid_search_file* file); * Fetch a file from the size, timestamp and name. * Return ==0 if the block is found, and copied into buffer. */ -int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_block* missing_block, unsigned char* buffer); +int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer); /** * Import files from the specified directory. diff --git a/cmdline/state.c b/cmdline/state.c index e15a3f7b..2705506b 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -1723,7 +1723,7 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST while (v_count) { struct snapraid_block* block = &file->blockvec[v_idx]; - if (block->parity_pos != POS_INVALID) { + if (block->private_parity_pos != POS_INVALID) { /* LCOV_EXCL_START */ decoding_error(path, f); log_fatal("Internal inconsistency in block position!\n"); @@ -1753,7 +1753,7 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST /* LCOV_EXCL_STOP */ } - block->parity_pos = v_pos; + block->private_parity_pos = v_pos; /* read the hash only for 'blk/chg/rep', and not for 'new' */ if (c != 'n') { @@ -1981,11 +1981,8 @@ static void state_read_binary(struct snapraid_state* state, const char* path, ST /* set the block as deleted */ block_state_set(block, BLOCK_STATE_DELETED); - /* remove the file reference */ - block_file_set(block, 0); - /* set the position */ - block->parity_pos = v_pos; + block->private_parity_pos = v_pos; /* read the hash */ ret = sread(f, block->hash, HASH_SIZE); @@ -2669,7 +2666,7 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f) begin = 0; while (begin < file->blockmax) { unsigned block_state = block_state_get(blockvec + begin); - uint32_t v_pos = blockvec[begin].parity_pos; + uint32_t v_pos = blockvec[begin].private_parity_pos; uint32_t v_count; block_off_t end; @@ -2678,7 +2675,7 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f) end = begin + 1; while (end < file->blockmax && block_state == block_state_get(blockvec + end) - && blockvec[end - 1].parity_pos + 1 == blockvec[end].parity_pos + && blockvec[end - 1].private_parity_pos + 1 == blockvec[end].private_parity_pos ) { ++end; } @@ -2710,9 +2707,9 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f) struct snapraid_block* block = blockvec + b; /* consistency check */ - if (block != fs_par2block_get(disk, block->parity_pos)) { + if (block != fs_par2block_get(disk, block->private_parity_pos)) { /* LCOV_EXCL_START */ - log_fatal("Internal inconsistency in mismatch for block %u state %u in file '%s'\n", block->parity_pos, block_state, file->sub); + log_fatal("Internal inconsistency in mismatch for block %u state %u in file '%s'\n", block->private_parity_pos, block_state, file->sub); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } @@ -2812,9 +2809,9 @@ static void state_write_binary(struct snapraid_state* state, STREAM* f) struct snapraid_block* block = fs_par2block_get(disk, begin); /* consistency check */ - if (block->parity_pos != begin) { + if (block->private_parity_pos != begin) { /* LCOV_EXCL_START */ - log_fatal("Internal inconsistency in delete position %u/%u!\n", block->parity_pos, begin); + log_fatal("Internal inconsistency in delete position %u/%u!\n", block->private_parity_pos, begin); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } diff --git a/cmdline/sync.c b/cmdline/sync.c index 936a42ef..71c483b4 100644 --- a/cmdline/sync.c +++ b/cmdline/sync.c @@ -100,6 +100,8 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta int read_size; unsigned char hash[HASH_SIZE]; unsigned block_state; + struct snapraid_file* file; + block_off_t file_pos; block = fs_par2block_get(disk, i); @@ -110,6 +112,9 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta if (block_state != BLOCK_STATE_REP && block_state != BLOCK_STATE_CHG) continue; + /* get the file of this block */ + file = fs_par2file_get(disk, i, &file_pos); + /* get block specific info */ info = info_get(&state->infoarr, i); @@ -120,16 +125,16 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta state_usage_cpu(state); /* if the file is different than the current one, close it */ - if (handle[j].file != 0 && handle[j].file != block_file_get(block)) { + if (handle[j].file != 0 && handle[j].file != file) { /* keep a pointer at the file we are going to close for error reporting */ - struct snapraid_file* file = handle[j].file; + struct snapraid_file* report = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -137,7 +142,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -147,10 +152,8 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta } } - ret = handle_open(&handle[j], block_file_get(block), state->file_mode, log_error, 0); + ret = handle_open(&handle[j], file, state->file_mode, log_error, 0); if (ret == -1) { - /* file we have tried to open for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ log_tag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); @@ -194,13 +197,11 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta } /* check if the file is changed */ - if (handle[j].st.st_size != block_file_get(block)->size - || handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec - || handle[j].st.st_ino != block_file_get(block)->inode + if (handle[j].st.st_size != file->size + || handle[j].st.st_mtime != file->mtime_sec + || STAT_NSEC(&handle[j].st) != file->mtime_nsec + || handle[j].st.st_ino != file->inode ) { - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); log_tag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, esc(file->sub)); if (handle[j].st.st_size != file->size) { log_error("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle[j].path, file->size, handle[j].st.st_size); @@ -218,13 +219,11 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta continue; } - read_size = handle_read(&handle[j], block, buffer, state->block_size, log_fatal, 0); + read_size = handle_read(&handle[j], file_pos, buffer, state->block_size, log_fatal, 0); if (read_size == -1) { /* LCOV_EXCL_START */ - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); log_fatal("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -232,7 +231,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be read.\n", handle[j].path); log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -256,11 +255,8 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta if (block_state == BLOCK_STATE_REP) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); - log_tag("error:%u:%s:%s: Unexpected data change\n", i, disk->name, esc(file->sub)); - log_error("Data change at file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); + log_error("Data change at file '%s' at position '%u'\n", handle[j].path, file_pos); log_error("WARNING! Unexpected data modification of a file without parity!\n"); if (file_flag_has(file, FILE_IS_COPY)) { @@ -308,14 +304,14 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta /* close the last file in the disk */ if (handle[j].file != 0) { /* keep a pointer at the file we are going to close for error reporting */ - struct snapraid_file* file = handle[j].file; + struct snapraid_file* report = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockmax, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockmax, disk->name, esc(report->sub), strerror(errno)); log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); log_fatal("Stopping at block %u\n", blockmax); @@ -323,7 +319,7 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta goto bail; } - log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc(report->sub), strerror(errno)); log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path); log_fatal("Stopping at block %u\n", blockmax); @@ -572,6 +568,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari struct snapraid_block* block; unsigned block_state; struct snapraid_disk* disk = handle[j].disk; + struct snapraid_file* file; + block_off_t file_pos; /* by default no rehash in case of "continue" */ rehandle[j].block = 0; @@ -621,20 +619,23 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari continue; } + /* get the file of this block */ + file = fs_par2file_get(disk, i, &file_pos); + /* until now is CPU */ state_usage_cpu(state); /* if the file is different than the current one, close it */ - if (handle[j].file != 0 && handle[j].file != block_file_get(block)) { + if (handle[j].file != 0 && handle[j].file != file) { /* keep a pointer at the file we are going to close for error reporting */ - struct snapraid_file* file = handle[j].file; + struct snapraid_file* report = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ if (errno == EIO) { - log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close EIO error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -642,7 +643,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari goto bail; } - log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); + log_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(report->sub), strerror(errno)); log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be accessed.\n", handle[j].path); log_fatal("Stopping at block %u\n", i); @@ -652,10 +653,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } } - ret = handle_open(&handle[j], block_file_get(block), state->file_mode, log_error, 0); + ret = handle_open(&handle[j], file, state->file_mode, log_error, 0); if (ret == -1) { - /* file we have tried to open for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { /* LCOV_EXCL_START */ log_tag("error:%u:%s:%s: Open EIO error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); @@ -701,13 +700,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari } /* check if the file is changed */ - if (handle[j].st.st_size != block_file_get(block)->size - || handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec - || handle[j].st.st_ino != block_file_get(block)->inode + if (handle[j].st.st_size != file->size + || handle[j].st.st_mtime != file->mtime_sec + || STAT_NSEC(&handle[j].st) != file->mtime_nsec + || handle[j].st.st_ino != file->inode ) { - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); log_tag("error:%u:%s:%s: Unexpected attribute change\n", i, disk->name, esc(file->sub)); if (handle[j].st.st_size != file->size) { log_error("Unexpected size change at file '%s' from %" PRIu64 " to %" PRIu64 ".\n", handle[j].path, file->size, handle[j].st.st_size); @@ -726,13 +723,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari continue; } - read_size = handle_read(&handle[j], block, buffer[j], state->block_size, log_error, 0); + read_size = handle_read(&handle[j], file_pos, buffer[j], state->block_size, log_error, 0); if (read_size == -1) { /* LCOV_EXCL_START */ - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); if (errno == EIO) { - log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); if (io_error >= state->opt.io_error_limit) { log_fatal("DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that disk '%s' is sane and that file '%s' can be read.\n", disk->dir, handle[j].path); @@ -741,13 +736,13 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari goto bail; } - log_error("Input/Output error in file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); + log_error("Input/Output error in file '%s' at position '%u'\n", handle[j].path, file_pos); ++io_error; io_error_on_this_block = 1; continue; } - log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), block_file_pos(block), strerror(errno)); + log_tag("error:%u:%s:%s: Read error at position %u. %s\n", i, disk->name, esc(file->sub), file_pos, strerror(errno)); log_fatal("WARNING! Unexpected read error in a data disk, it isn't possible to sync.\n"); log_fatal("Ensure that file '%s' can be read.\n", handle[j].path); log_fatal("Stopping to allow recovery. Try with 'snapraid check -f %s'\n", file->sub); @@ -775,13 +770,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari if (block_has_updated_hash(block)) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { - /* file we are processing for error reporting */ - struct snapraid_file* file = block_file_get(block); - /* if the file has invalid parity, it's a REP changed during the sync */ if (block_has_invalid_parity(block)) { log_tag("error:%u:%s:%s: Unexpected data change\n", i, disk->name, esc(file->sub)); - log_error("Data change at file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); + log_error("Data change at file '%s' at position '%u'\n", handle[j].path, file_pos); log_error("WARNING! Unexpected data modification of a file without parity!\n"); if (file_flag_has(file, FILE_IS_COPY)) { @@ -801,8 +793,8 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari continue; } else { /* otherwise it's a BLK with silent error */ unsigned diff = memdiff(hash, block->hash, HASH_SIZE); - log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(file->sub), block_file_pos(block), diff); - log_error("Data error in file '%s' at position '%u', diff bits %u\n", handle[j].path, block_file_pos(block), diff); + log_tag("error:%u:%s:%s: Data error at position %u, diff bits %u\n", i, disk->name, esc(file->sub), file_pos, diff); + log_error("Data error in file '%s' at position '%u', diff bits %u\n", handle[j].path, file_pos, diff); /* save the failed block for the fix */ failed[failed_count].index = j;