More interface changes to prepare to the use of tree mapping

This commit is contained in:
Andrea Mazzoleni 2015-10-03 10:43:02 +02:00
parent f719ee4f57
commit fa775d24a8
12 changed files with 295 additions and 314 deletions

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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.
*/

View file

@ -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);

View file

@ -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.

View file

@ -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 */

View file

@ -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;
}

View file

@ -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);

View file

@ -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.

View file

@ -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 */
}

View file

@ -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;