mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Change commenting style
This commit is contained in:
parent
20d7a9d513
commit
21673eb805
22 changed files with 1044 additions and 603 deletions
419
cmdline/check.c
419
cmdline/check.c
|
|
@ -115,17 +115,21 @@ static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned d
|
|||
}
|
||||
}
|
||||
|
||||
/* if nothing checked, we reject it */
|
||||
/* note that we are excluding this case at upper level */
|
||||
/* but checking again doesn't hurt */
|
||||
/*
|
||||
* If nothing checked, we reject it
|
||||
* note that we are excluding this case at upper level
|
||||
* but checking again doesn't hurt
|
||||
*/
|
||||
if (!hash_checked) {
|
||||
/* LCOV_EXCL_START */
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* if we checked something, and no block failed the check */
|
||||
/* recompute all the redundancy information */
|
||||
/*
|
||||
* If we checked something, and no block failed the check
|
||||
* recompute all the redundancy information
|
||||
*/
|
||||
raid_gen(diskmax, state->level, state->block_size, buffer);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -184,8 +188,10 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
|
|||
for (i = 0; i < failed_count; ++i)
|
||||
id[i] = failed[failed_map[i]].index;
|
||||
|
||||
/* check if there is at least a failed block that can be checked for correctness using the hash */
|
||||
/* if there isn't, we have to sacrifice a parity block to check that the result is correct */
|
||||
/*
|
||||
* Check if there is at least a failed block that can be checked for correctness using the hash
|
||||
* if there isn't, we have to sacrifice a parity block to check that the result is correct
|
||||
*/
|
||||
has_hash = 0;
|
||||
for (i = 0; i < failed_count; ++i) {
|
||||
/* if we are expected to recover this block */
|
||||
|
|
@ -196,8 +202,10 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
|
|||
has_hash = 1;
|
||||
}
|
||||
|
||||
/* if we don't have a hash, but we have an extra parity */
|
||||
/* (strictly-less failures than number of parities) */
|
||||
/*
|
||||
* If we don't have a hash, but we have an extra parity
|
||||
* (strictly-less failures than number of parities)
|
||||
*/
|
||||
if (!has_hash && failed_count < n) {
|
||||
/* number of parity to use, one more to check the recovering */
|
||||
unsigned r = failed_count + 1;
|
||||
|
|
@ -236,8 +244,10 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
|
|||
} while (combination_next(r, n, ip));
|
||||
}
|
||||
|
||||
/* if we have a hash, and enough parities */
|
||||
/* (less-or-equal failures than number of parities) */
|
||||
/*
|
||||
* If we have a hash, and enough parities
|
||||
* (less-or-equal failures than number of parities)
|
||||
*/
|
||||
if (has_hash && failed_count <= n) {
|
||||
/* number of parities to use equal to the number of failures */
|
||||
unsigned r = failed_count;
|
||||
|
|
@ -343,21 +353,27 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
}
|
||||
}
|
||||
|
||||
/* Here we have to try two different strategies to recover, because in case the 'sync' */
|
||||
/* process is aborted, we don't know if the parity data is really updated just like after 'sync', */
|
||||
/* or if it still represents the state before the 'sync'. */
|
||||
/*
|
||||
* Here we have to try two different strategies to recover, because in case the 'sync'
|
||||
* process is aborted, we don't know if the parity data is really updated just like after 'sync',
|
||||
* or if it still represents the state before the 'sync'.
|
||||
*/
|
||||
|
||||
/* Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks */
|
||||
/* and the two strategies are identical */
|
||||
/*
|
||||
* Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks
|
||||
* and the two strategies are identical
|
||||
*/
|
||||
|
||||
/* As first, we assume that the parity IS updated for the current state */
|
||||
/* and that we are going to recover the state after the last 'sync'. */
|
||||
/* In this case, parity contains info from BLK, REP and CHG blocks, */
|
||||
/* but not for DELETED. */
|
||||
/* We need to put in the recovering process only the bad blocks, because all the */
|
||||
/* others already contains the correct data read from disk, and the parity is correctly computed for them. */
|
||||
/* We are interested to recover BLK, REP and CHG blocks if they are marked as bad, */
|
||||
/* but we are not interested in DELETED ones. */
|
||||
/*
|
||||
* As first, we assume that the parity IS updated for the current state
|
||||
* and that we are going to recover the state after the last 'sync'.
|
||||
* In this case, parity contains info from BLK, REP and CHG blocks,
|
||||
* but not for DELETED.
|
||||
* We need to put in the recovering process only the bad blocks, because all the
|
||||
* others already contains the correct data read from disk, and the parity is correctly computed for them.
|
||||
* We are interested to recover BLK, REP and CHG blocks if they are marked as bad,
|
||||
* but we are not interested in DELETED ones.
|
||||
*/
|
||||
|
||||
n = 0;
|
||||
something_to_recover = 0; /* keep track if there is at least one block to fix */
|
||||
|
|
@ -397,17 +413,21 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
|
||||
if (ret == 0) {
|
||||
/* reprocess the CHG blocks, for which we don't have a hash to check */
|
||||
/* if they were BAD we have to use some heuristics to ensure that we have recovered */
|
||||
/* the state after the sync. If unsure, we assume the worst case */
|
||||
/*
|
||||
* Reprocess the CHG blocks, for which we don't have a hash to check
|
||||
* if they were BAD we have to use some heuristics to ensure that we have recovered
|
||||
* the state after the sync. If unsure, we assume the worst case
|
||||
*/
|
||||
|
||||
for (j = 0; j < failed_count; ++j) {
|
||||
/* we take care only of BAD blocks we have to write back */
|
||||
if (failed[j].is_bad) {
|
||||
unsigned block_state = block_state_get(failed[j].block);
|
||||
|
||||
/* BLK and REP blocks are always OK, because at this point */
|
||||
/* we have already checked their hash */
|
||||
/*
|
||||
* BLK and REP blocks are always OK, because at this point
|
||||
* we have already checked their hash
|
||||
*/
|
||||
if (block_state != BLOCK_STATE_CHG) {
|
||||
assert(block_state == BLOCK_STATE_BLK || block_state == BLOCK_STATE_REP);
|
||||
continue;
|
||||
|
|
@ -415,20 +435,24 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|
||||
/* for CHG blocks we have to 'guess' if they are correct or not */
|
||||
|
||||
/* if the hash is invalid we cannot check the result */
|
||||
/* this could happen if we have lost this information */
|
||||
/* after an aborted sync */
|
||||
/*
|
||||
* If the hash is invalid we cannot check the result
|
||||
* this could happen if we have lost this information
|
||||
* after an aborted sync
|
||||
*/
|
||||
if (hash_is_invalid(failed[j].block->hash)) {
|
||||
/* it may contain garbage */
|
||||
failed[j].is_outofdate = 1;
|
||||
|
||||
log_tag("repair:hash_unknown:%u: Unknown hash\n", j);
|
||||
} else if (hash_is_zero(failed[j].block->hash)) {
|
||||
/* if the block is not filled with 0, we are sure to have */
|
||||
/* restored it to the state after the 'sync' */
|
||||
/* instead, if the block is filled with 0, it could be either that the */
|
||||
/* block after the sync is really filled by 0, or that */
|
||||
/* we restored the block before the 'sync'. */
|
||||
/*
|
||||
* If the block is not filled with 0, we are sure to have
|
||||
* restored it to the state after the 'sync'
|
||||
* instead, if the block is filled with 0, it could be either that the
|
||||
* block after the sync is really filled by 0, or that
|
||||
* we restored the block before the 'sync'.
|
||||
*/
|
||||
if (memcmp(buffer[failed[j].index], buffer_zero, state->block_size) == 0) {
|
||||
/* it may contain garbage */
|
||||
failed[j].is_outofdate = 1;
|
||||
|
|
@ -436,11 +460,13 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
log_tag("repair_hash_unknown:%u: Maybe old zero\n", j);
|
||||
}
|
||||
} else {
|
||||
/* if the hash is different than the previous one, we are sure to have */
|
||||
/* restored it to the state after the 'sync' */
|
||||
/* 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 the hash is different than the previous one, we are sure to have
|
||||
* restored it to the state after the 'sync'
|
||||
* 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'.
|
||||
*/
|
||||
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 */
|
||||
|
|
@ -462,14 +488,16 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
else
|
||||
log_tag("recover_sync:%u:%u: Failed with %d attempts\n", pos, n, ret);
|
||||
|
||||
/* Now assume that the parity IS NOT updated at the current state, */
|
||||
/* but still represent the state before the last 'sync' process. */
|
||||
/* In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks, */
|
||||
/* but not for REP (new version) and CHG (new version). */
|
||||
/* We are interested to recover BLK ones marked as bad, */
|
||||
/* but we are not interested to recover CHG (new version) and REP (new version) blocks, */
|
||||
/* even if marked as bad, because we don't have parity for them and it's just impossible, */
|
||||
/* and we are not interested to recover DELETED ones. */
|
||||
/*
|
||||
* Now assume that the parity IS NOT updated at the current state,
|
||||
* but still represent the state before the last 'sync' process.
|
||||
* In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks,
|
||||
* but not for REP (new version) and CHG (new version).
|
||||
* We are interested to recover BLK ones marked as bad,
|
||||
* but we are not interested to recover CHG (new version) and REP (new version) blocks,
|
||||
* even if marked as bad, because we don't have parity for them and it's just impossible,
|
||||
* and we are not interested to recover DELETED ones.
|
||||
*/
|
||||
n = 0;
|
||||
something_to_recover = 0; /* keep track if there is at least one block to fix */
|
||||
something_unsynced = 0; /* keep track if we have some unsynced info to process */
|
||||
|
|
@ -480,49 +508,63 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
|| block_state == BLOCK_STATE_CHG
|
||||
|| block_state == BLOCK_STATE_REP
|
||||
) {
|
||||
/* If the block is CHG, REP or DELETED, we don't have the original content of block, */
|
||||
/* and we must try to recover it. */
|
||||
/* This applies to CHG and REP blocks even if they are not marked bad, */
|
||||
/* because the parity is computed with old content, and not with the new one. */
|
||||
/* Note that this recovering is done just to make it possible to recover any other BLK one, */
|
||||
/* we are not really interested in DELETED, CHG (old version) and REP (old version). */
|
||||
/*
|
||||
* If the block is CHG, REP or DELETED, we don't have the original content of block,
|
||||
* and we must try to recover it.
|
||||
* This applies to CHG and REP blocks even if they are not marked bad,
|
||||
* because the parity is computed with old content, and not with the new one.
|
||||
* Note that this recovering is done just to make it possible to recover any other BLK one,
|
||||
* we are not really interested in DELETED, CHG (old version) and REP (old version).
|
||||
*/
|
||||
something_unsynced = 1;
|
||||
|
||||
if (block_state == BLOCK_STATE_CHG
|
||||
&& hash_is_zero(failed[j].block->hash)
|
||||
) {
|
||||
/* If the block was a ZERO block, restore it to the original 0 as before the 'sync' */
|
||||
/* We do this to just allow recovering of other BLK ones */
|
||||
/*
|
||||
* If the block was a ZERO block, restore it to the original 0 as before the 'sync'
|
||||
* We do this to just allow recovering of other BLK ones
|
||||
*/
|
||||
|
||||
memset(buffer[failed[j].index], 0, state->block_size);
|
||||
/* note that from now the buffer is definitely lost */
|
||||
/* we can do this only because it's the last retry of recovering */
|
||||
/*
|
||||
* Note that from now the buffer is definitely lost
|
||||
* we can do this only because it's the last retry of recovering
|
||||
*/
|
||||
|
||||
/* try to fetch the old block using the old hash for CHG and DELETED blocks */
|
||||
} else if ((block_state == BLOCK_STATE_CHG || block_state == BLOCK_STATE_DELETED)
|
||||
&& hash_is_unique(failed[j].block->hash)
|
||||
&& state_import_fetch(state, rehash, failed[j].block, buffer[failed[j].index]) == 0) {
|
||||
|
||||
/* note that from now the buffer is definitely lost */
|
||||
/* we can do this only because it's the last retry of recovering */
|
||||
/*
|
||||
* Note that from now the buffer is definitely lost
|
||||
* we can do this only because it's the last retry of recovering
|
||||
*/
|
||||
} else {
|
||||
/* otherwise try to recover it */
|
||||
failed_map[n] = j;
|
||||
++n;
|
||||
|
||||
/* note that we don't set something_to_recover, because we are */
|
||||
/* not really interested to recover *only* old blocks. */
|
||||
/*
|
||||
* Note that we don't set something_to_recover, because we are
|
||||
* not really interested to recover *only* old blocks.
|
||||
*/
|
||||
}
|
||||
|
||||
/* avoid using the hash of this block to verify the recovering */
|
||||
/* this applies to REP blocks because we are going to recover the old state */
|
||||
/* and the REP hash represents the new one */
|
||||
/* it also applies to CHG and DELETE blocks because we want to have */
|
||||
/* a successful recovering only if a BLK one is matching */
|
||||
/*
|
||||
* Avoid using the hash of this block to verify the recovering
|
||||
* this applies to REP blocks because we are going to recover the old state
|
||||
* and the REP hash represents the new one
|
||||
* it also applies to CHG and DELETE blocks because we want to have
|
||||
* a successful recovering only if a BLK one is matching
|
||||
*/
|
||||
failed[j].is_outofdate = 1;
|
||||
} else if (failed[j].is_bad) {
|
||||
/* If the block is bad we don't know its content, and we try to recover it */
|
||||
/* At this point, we can have only BLK ones */
|
||||
/*
|
||||
* If the block is bad we don't know its content, and we try to recover it
|
||||
* At this point, we can have only BLK ones
|
||||
*/
|
||||
|
||||
assert(block_state == BLOCK_STATE_BLK);
|
||||
|
||||
|
|
@ -535,14 +577,18 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
}
|
||||
}
|
||||
|
||||
/* if nothing to fix, we just don't try */
|
||||
/* if nothing unsynced we also don't retry, because it's the same try as before */
|
||||
/*
|
||||
* If nothing to fix, we just don't try
|
||||
* if nothing unsynced we also don't retry, because it's the same try as before
|
||||
*/
|
||||
if (something_to_recover && something_unsynced) {
|
||||
ret = repair_step(state, rehash, pos, diskmax, failed, failed_map, n, buffer, buffer_recov, buffer_zero);
|
||||
if (ret == 0) {
|
||||
/* reprocess the REP and CHG blocks, for which we have recovered an old state */
|
||||
/* that we don't want to save into disk */
|
||||
/* we have already marked them, but we redo it for logging */
|
||||
/*
|
||||
* Reprocess the REP and CHG blocks, for which we have recovered an old state
|
||||
* that we don't want to save into disk
|
||||
* we have already marked them, but we redo it for logging
|
||||
*/
|
||||
|
||||
for (j = 0; j < failed_count; ++j) {
|
||||
/* we take care only of BAD blocks we have to write back */
|
||||
|
|
@ -552,8 +598,10 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
|
|||
if (block_state == BLOCK_STATE_CHG
|
||||
|| block_state == BLOCK_STATE_REP
|
||||
) {
|
||||
/* mark that we have restored an old state */
|
||||
/* and we don't want to write it to the disk */
|
||||
/*
|
||||
* Mark that we have restored an old state
|
||||
* and we don't want to write it to the disk
|
||||
*/
|
||||
failed[j].is_outofdate = 1;
|
||||
|
||||
log_tag("repair_hash_unknown:%u: Surely old data\n", j);
|
||||
|
|
@ -599,8 +647,11 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
{
|
||||
unsigned j;
|
||||
int ret;
|
||||
/* for all the files print the final status, and do the final time fix */
|
||||
/* we also ensure to close files after processing the last block */
|
||||
|
||||
/*
|
||||
* For all the files print the final status, and do the final time fix
|
||||
* we also ensure to close files after processing the last block
|
||||
*/
|
||||
for (j = 0; j < diskmax; ++j) {
|
||||
struct snapraid_block* block;
|
||||
struct snapraid_disk* disk;
|
||||
|
|
@ -638,8 +689,10 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
|
||||
/* finish the fix process if it's the last block of the files */
|
||||
if (fix) {
|
||||
/* mark that we finished with this file */
|
||||
/* to identify later any NOT finished ones */
|
||||
/*
|
||||
* Mark that we finished with this file
|
||||
* to identify later any NOT finished ones
|
||||
*/
|
||||
file_flag_set(file, FILE_IS_FINISHED);
|
||||
|
||||
/* if the file is damaged, meaning that a fix failed */
|
||||
|
|
@ -686,8 +739,10 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
goto close_and_continue;
|
||||
}
|
||||
|
||||
/* if the file is closed or different than the one expected, reopen it */
|
||||
/* a different open file could happen when filtering for bad blocks */
|
||||
/*
|
||||
* If the file is closed or different than the one expected, reopen it
|
||||
* a different open file could happen when filtering for bad blocks
|
||||
*/
|
||||
if (handle[j].file != file) {
|
||||
/* keep a pointer at the file we are going to close for error reporting */
|
||||
struct snapraid_file* report = handle[j].file;
|
||||
|
|
@ -700,8 +755,10 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* reopen it as readonly, as to set the mtime readonly access it's enough */
|
||||
/* we know that the file exists because it has the FILE_IS_FIXED tag */
|
||||
/*
|
||||
* Reopen it as readonly, as to set the mtime readonly access it's enough
|
||||
* we know that the file exists because it has the FILE_IS_FIXED tag
|
||||
*/
|
||||
ret = handle_open(&handle[j], file, state->file_mode, 0);
|
||||
if (ret != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -720,14 +777,16 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
/* search for the corresponding inode */
|
||||
collide_file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode));
|
||||
|
||||
/* if the inode is already in the database and it refers to a different file name, */
|
||||
/* we can fix the file time ONLY if the time and size allow to differentiate */
|
||||
/* between the two files */
|
||||
|
||||
/* for example, suppose we delete a bunch of files with all the same size and time, */
|
||||
/* when recreating them the inodes may be reused in a different order, */
|
||||
/* and at the next sync some files may have matching inode/size/time even if different name */
|
||||
/* not allowing sync to detect that the file is changed and not renamed */
|
||||
/*
|
||||
* If the inode is already in the database and it refers to a different file name,
|
||||
* we can fix the file time ONLY if the time and size allow to differentiate
|
||||
* between the two files
|
||||
*
|
||||
* For example, suppose we delete a bunch of files with all the same size and time,
|
||||
* when recreating them the inodes may be reused in a different order,
|
||||
* and at the next sync some files may have matching inode/size/time even if different name
|
||||
* not allowing sync to detect that the file is changed and not renamed
|
||||
*/
|
||||
if (!collide_file /* if not in the database, there is no collision */
|
||||
|| strcmp(collide_file->sub, file->sub) == 0 /* if the name is the same, it's the right collision */
|
||||
|| collide_file->size != file->size /* if the size is different, the collision is identified */
|
||||
|
|
@ -750,8 +809,10 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
log_tag("collision:%s:%s:%s: Not setting modification time to avoid inode collision\n", disk->name, esc_tag(file->sub), esc_tag(collide_file->sub));
|
||||
}
|
||||
} else {
|
||||
/* we are not fixing, but only checking */
|
||||
/* print just the final status */
|
||||
/*
|
||||
* We are not fixing, but only checking
|
||||
* print just the final status
|
||||
*/
|
||||
if (file_flag_has(file, FILE_IS_DAMAGED)) {
|
||||
log_tag("status:unrecoverable:%s:%s\n", disk->name, esc_tag(file->sub));
|
||||
msg_info("unrecoverable %s\n", fmt_term(disk, file->sub));
|
||||
|
|
@ -768,12 +829,16 @@ static int file_post(struct snapraid_state* state, int fix, unsigned i, struct s
|
|||
}
|
||||
|
||||
close_and_continue:
|
||||
/* if the opened file is the correct one, close it */
|
||||
/* in case of excluded and fragmented files it's possible */
|
||||
/* that the opened file is not the current one */
|
||||
/*
|
||||
* If the opened file is the correct one, close it
|
||||
* in case of excluded and fragmented files it's possible
|
||||
* that the opened file is not the current one
|
||||
*/
|
||||
if (handle[j].file == file) {
|
||||
/* ensure to close the file just after finishing with it */
|
||||
/* to avoid keeping it open without any possible use */
|
||||
/*
|
||||
* Ensure to close the file just after finishing with it
|
||||
* to avoid keeping it open without any possible use
|
||||
*/
|
||||
ret = handle_close(&handle[j]);
|
||||
if (ret != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -848,8 +913,10 @@ static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct
|
|||
|
||||
block = fs_par2block_find(handle[j].disk, i);
|
||||
|
||||
/* try to recover all files, even the ones without hash */
|
||||
/* because in some cases we can recover also them */
|
||||
/*
|
||||
* Try to recover all files, even the ones without hash
|
||||
* because in some cases we can recover also them
|
||||
*/
|
||||
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 */
|
||||
|
|
@ -958,11 +1025,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
continue;
|
||||
}
|
||||
|
||||
/* If we have valid parity, and it makes sense to check its content. */
|
||||
/* If we already know that the parity is invalid, we just read the file */
|
||||
/* but we don't report parity errors */
|
||||
/* Note that with auditonly, we anyway skip the full parity check, */
|
||||
/* because we also don't read it at all */
|
||||
/*
|
||||
* If we have valid parity, and it makes sense to check its content.
|
||||
* If we already know that the parity is invalid, we just read the file
|
||||
* but we don't report parity errors
|
||||
* Note that with auditonly, we anyway skip the full parity check,
|
||||
* because we also don't read it at all
|
||||
*/
|
||||
valid_parity = 1;
|
||||
|
||||
/* If the parity is used by at least one file */
|
||||
|
|
@ -1008,8 +1077,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
|
||||
/* if the parity is not valid */
|
||||
if (block_has_invalid_parity(block)) {
|
||||
/* mark the parity as invalid, and don't try to check/fix it */
|
||||
/* because it will be recomputed at the next sync */
|
||||
/*
|
||||
* Mark the parity as invalid, and don't try to check/fix it
|
||||
* because it will be recomputed at the next sync
|
||||
*/
|
||||
valid_parity = 0;
|
||||
/* follow */
|
||||
}
|
||||
|
|
@ -1019,8 +1090,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
/* use an empty block */
|
||||
memset(buffer[j], 0, state->block_size);
|
||||
|
||||
/* store it in the failed set, because potentially */
|
||||
/* the parity may be still computed with the previous content */
|
||||
/*
|
||||
* Store it in the failed set, because potentially
|
||||
* the parity may be still computed with the previous content
|
||||
*/
|
||||
failed[failed_count].is_bad = 0; /* note that is_bad==0 <=> file==0 */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
|
|
@ -1041,9 +1114,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
|
||||
/* 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)) {
|
||||
/* use an empty block */
|
||||
/* in true, this is unnecessary, because we are not checking any parity */
|
||||
/* but we keep it for completeness */
|
||||
/*
|
||||
* Use an empty block
|
||||
* in true, this is unnecessary, because we are not checking any parity
|
||||
* but we keep it for completeness
|
||||
*/
|
||||
memset(buffer[j], 0, state->block_size);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1081,8 +1156,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
|
||||
/* check if the file was just created */
|
||||
if (handle[j].created != 0) {
|
||||
/* if fragmented, it may be reopened, so remember that the file */
|
||||
/* was originally missing */
|
||||
/*
|
||||
* If fragmented, it may be reopened, so remember that the file
|
||||
* was originally missing
|
||||
*/
|
||||
file_flag_set(file, FILE_IS_CREATED);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1113,9 +1190,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
++soft_error;
|
||||
}
|
||||
|
||||
/* mark the file as missing, to avoid to retry to open it again */
|
||||
/* note that this can be done only if we are not fixing it */
|
||||
/* otherwise, it could be recreated */
|
||||
/*
|
||||
* Mark the file as missing, to avoid to retry to open it again
|
||||
* note that this can be done only if we are not fixing it
|
||||
* otherwise, it could be recreated
|
||||
*/
|
||||
file_flag_set(file, FILE_IS_MISSING);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1164,9 +1243,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
}
|
||||
}
|
||||
|
||||
/* mark the file as opened at least one time */
|
||||
/* this is used to avoid to check the unsynced and size */
|
||||
/* more than one time, in case the file is reopened later */
|
||||
/*
|
||||
* Mark the file as opened at least one time
|
||||
* this is used to avoid to check the unsynced and size
|
||||
* more than one time, in case the file is reopened later
|
||||
*/
|
||||
file_flag_set(file, FILE_IS_OPENED);
|
||||
}
|
||||
|
||||
|
|
@ -1207,12 +1288,16 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
|
||||
countsize += read_size;
|
||||
|
||||
/* always insert CHG blocks, the repair functions needs all of them */
|
||||
/* because the parity may be still referring at the old state */
|
||||
/* and the repair must be aware of it */
|
||||
/*
|
||||
* Always insert CHG blocks, the repair functions needs all of them
|
||||
* because the parity may be still referring at the old state
|
||||
* and the repair must be aware of it
|
||||
*/
|
||||
if (block_state == BLOCK_STATE_CHG) {
|
||||
/* we DO NOT mark them as bad to avoid to overwrite them with wrong data. */
|
||||
/* if we don't have a hash, we always assume the first read of the block correct. */
|
||||
/*
|
||||
* We DO NOT mark them as bad to avoid to overwrite them with wrong data.
|
||||
* if we don't have a hash, we always assume the first read of the block correct.
|
||||
*/
|
||||
failed[failed_count].is_bad = 0; /* we assume the CHG block correct */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
failed[failed_count].index = j;
|
||||
|
|
@ -1254,9 +1339,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
continue;
|
||||
}
|
||||
|
||||
/* always insert REP blocks, the repair functions needs all of them */
|
||||
/* because the parity may be still referring at the old state */
|
||||
/* and the repair must be aware of it */
|
||||
/*
|
||||
* Always insert REP blocks, the repair functions needs all of them
|
||||
* because the parity may be still referring at the old state
|
||||
* and the repair must be aware of it
|
||||
*/
|
||||
if (block_state == BLOCK_STATE_REP) {
|
||||
failed[failed_count].is_bad = 0; /* it's not bad */
|
||||
failed[failed_count].is_outofdate = 0;
|
||||
|
|
@ -1325,9 +1412,11 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
file_flag_set(failed[j].file, FILE_IS_DAMAGED);
|
||||
}
|
||||
} else {
|
||||
/* now counts partial recovers */
|
||||
/* note that this could happen only when we have an incomplete 'sync' */
|
||||
/* and that we have recovered is the state before the 'sync' */
|
||||
/*
|
||||
* Now counts partial recovers
|
||||
* note that this could happen only when we have an incomplete 'sync'
|
||||
* and that we have recovered is the state before the 'sync'
|
||||
*/
|
||||
int partial_recover_error = 0;
|
||||
|
||||
/* print a list of all the errors in files */
|
||||
|
|
@ -1401,8 +1490,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
continue;
|
||||
}
|
||||
|
||||
/* mark the file as containing some fixes */
|
||||
/* note that it could be also marked as damaged in other iterations */
|
||||
/*
|
||||
* Mark the file as containing some fixes
|
||||
* note that it could be also marked as damaged in other iterations
|
||||
*/
|
||||
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].disk->name, esc_tag(failed[j].file->sub), failed[j].file_pos);
|
||||
|
|
@ -1447,8 +1538,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
}
|
||||
}
|
||||
} else {
|
||||
/* if we are not fixing, we just set the FIXED flag */
|
||||
/* meaning that we could fix this file if we try */
|
||||
/*
|
||||
* If we are not fixing, we just set the FIXED flag
|
||||
* 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(failed[j].file, FILE_IS_FIXED);
|
||||
|
|
@ -1457,8 +1550,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
}
|
||||
}
|
||||
} else {
|
||||
/* if we are not checking, we just set the DAMAGED flag */
|
||||
/* to report that the file is damaged, and we don't know if we can fix it */
|
||||
/*
|
||||
* If we are not checking, we just set the DAMAGED flag
|
||||
* 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(failed[j].file, FILE_IS_DAMAGED);
|
||||
|
|
@ -1578,8 +1673,10 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* create it */
|
||||
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
|
||||
/*
|
||||
* Create it
|
||||
* O_NOFOLLOW: do not follow links to ensure to open the real file
|
||||
*/
|
||||
f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600);
|
||||
if (f == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1684,11 +1781,13 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna
|
|||
if (errno == ENOENT) {
|
||||
unrecoverable = 1;
|
||||
if (fix) {
|
||||
/* if the target doesn't exist, it's unrecoverable */
|
||||
/* because we cannot create an hardlink of a file that */
|
||||
/* doesn't exists */
|
||||
/* but in check, we can assume that fixing will recover */
|
||||
/* such missing file, so we assume a less drastic error */
|
||||
/*
|
||||
* If the target doesn't exist, it's unrecoverable
|
||||
* because we cannot create an hardlink of a file that
|
||||
* doesn't exists
|
||||
* but in check, we can assume that fixing will recover
|
||||
* such missing file, so we assume a less drastic error
|
||||
*/
|
||||
++unrecoverable_error;
|
||||
}
|
||||
}
|
||||
|
|
@ -1914,8 +2013,10 @@ bail:
|
|||
}
|
||||
}
|
||||
|
||||
/* remove all the files created from scratch that have not finished the processing */
|
||||
/* it happens only when aborting pressing Ctrl+C or other reason. */
|
||||
/*
|
||||
* Remove all the files created from scratch that have not finished the processing
|
||||
* it happens only when aborting pressing Ctrl+C or other reason.
|
||||
*/
|
||||
if (fix) {
|
||||
/* for each disk */
|
||||
for (i = 0; i < diskmax; ++i) {
|
||||
|
|
@ -1947,9 +2048,11 @@ bail:
|
|||
continue;
|
||||
}
|
||||
|
||||
/* if the file was originally missing, and processing not yet finished */
|
||||
/* we have to throw it away to ensure that at the next run we will retry */
|
||||
/* to fix it, in case we select to undelete missing files */
|
||||
/*
|
||||
* If the file was originally missing, and processing not yet finished
|
||||
* we have to throw it away to ensure that at the next run we will retry
|
||||
* to fix it, in case we select to undelete missing files
|
||||
*/
|
||||
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
|
||||
|
||||
ret = remove(path);
|
||||
|
|
@ -2097,8 +2200,10 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b
|
|||
}
|
||||
|
||||
if (fix) {
|
||||
/* if fixing, create the file and open for writing */
|
||||
/* if it fails, we cannot continue */
|
||||
/*
|
||||
* If fixing, create the file and open for writing
|
||||
* if it fails, we cannot continue
|
||||
*/
|
||||
for (l = 0; l < state->level; ++l) {
|
||||
/* skip parity disks that are not accessible */
|
||||
if (state->parity[l].skip_access) {
|
||||
|
|
@ -2144,8 +2249,10 @@ int state_check(struct snapraid_state* state, int fix, block_off_t blockstart, b
|
|||
}
|
||||
}
|
||||
} else if (!state->opt.auditonly) {
|
||||
/* if checking, open the file for reading */
|
||||
/* it may fail if the file doesn't exist, in this case we continue to check the files */
|
||||
/*
|
||||
* If checking, open the file for reading
|
||||
* it may fail if the file doesn't exist, in this case we continue to check the files
|
||||
*/
|
||||
for (l = 0; l < state->level; ++l) {
|
||||
parity_ptr[l] = &parity[l];
|
||||
ret = parity_open(parity_ptr[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
|
||||
|
|
|
|||
|
|
@ -413,8 +413,10 @@ static double smart_afr(devinfo_t* devinfo)
|
|||
}
|
||||
|
||||
if (smart[187].raw != SMART_UNASSIGNED) {
|
||||
/* with some disks, only the lower 16 bits are significant */
|
||||
/* See: http://web.archive.org/web/20130507072056/http://media.kingston.com/support/downloads/MKP_306_SMART_attribute.pdf */
|
||||
/*
|
||||
* With some disks, only the lower 16 bits are significant
|
||||
* See: http://web.archive.org/web/20130507072056/http://media.kingston.com/support/downloads/MKP_306_SMART_attribute.pdf
|
||||
*/
|
||||
double r = smart_afr_value(SMART_187_R, SMART_187_STEP, smart[187].raw & mask16);
|
||||
if (afr < r)
|
||||
afr = r;
|
||||
|
|
@ -430,8 +432,10 @@ static double smart_afr(devinfo_t* devinfo)
|
|||
*/
|
||||
strncmp(devinfo->model, "ST", 2) != 0 && smart[188].raw != SMART_UNASSIGNED
|
||||
) {
|
||||
/* with Seagate disks, there are three different 16 bits value reported */
|
||||
/* the lowest one is the most significant */
|
||||
/*
|
||||
* With Seagate disks, there are three different 16 bits value reported
|
||||
* the lowest one is the most significant
|
||||
*/
|
||||
double r = smart_afr_value(SMART_188_R, SMART_188_STEP, smart[188].raw & mask16);
|
||||
if (afr < r)
|
||||
afr = r;
|
||||
|
|
@ -488,12 +492,16 @@ static double raid_prob_of_one_or_more_failures(double array_failure_rate, doubl
|
|||
if (array_failure_rate == 0)
|
||||
return 0;
|
||||
|
||||
/* get the Mean Time Between Failure of a single disk */
|
||||
/* from the array failure rate */
|
||||
/*
|
||||
* Get the Mean Time Between Failure of a single disk
|
||||
* from the array failure rate
|
||||
*/
|
||||
MTBF = n / array_failure_rate;
|
||||
|
||||
/* get the Mean Time Between Repair (the time that a failed disk is replaced) */
|
||||
/* from the repair rate */
|
||||
/*
|
||||
* Get the Mean Time Between Repair (the time that a failed disk is replaced)
|
||||
* from the repair rate
|
||||
*/
|
||||
MTTR = 1.0 / replace_rate;
|
||||
|
||||
/* use the approximated MTTDL equation */
|
||||
|
|
@ -697,8 +705,10 @@ static void state_smart(struct snapraid_state* state, unsigned n, tommy_list* lo
|
|||
if (flag & (SMARTCTL_FLAG_FAIL | SMARTCTL_FLAG_PREFAIL))
|
||||
make_it_fail = 1;
|
||||
|
||||
/* note that in older smartmontools, like 5.x, rotation rate is not present */
|
||||
/* and then it could remain unassigned */
|
||||
/*
|
||||
* Note that in older smartmontools, like 5.x, rotation rate is not present
|
||||
* and then it could remain unassigned
|
||||
*/
|
||||
|
||||
if (flag & (SMARTCTL_FLAG_UNSUPPORTED | SMARTCTL_FLAG_OPEN)) {
|
||||
/* if error running smartctl, skip AFR estimation */
|
||||
|
|
|
|||
|
|
@ -60,8 +60,10 @@ static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task
|
|||
ret = handle_close(handle);
|
||||
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 */
|
||||
/*
|
||||
* This one is really an unexpected error, because we are only reading
|
||||
* and closing a descriptor should never fail
|
||||
*/
|
||||
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(report->sub), strerror(errno));
|
||||
log_fatal_errno(errno, disk->name);
|
||||
log_fatal(errno, "Stopping at block %u\n", blockcur);
|
||||
|
|
@ -437,8 +439,10 @@ int state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
blockmax = blockstart + blockcount;
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
/* it may fail if the file doesn't exist, in this case we continue to dry the files */
|
||||
/*
|
||||
* Open the file for reading
|
||||
* it may fail if the file doesn't exist, in this case we continue to dry the files
|
||||
*/
|
||||
for (l = 0; l < state->level; ++l) {
|
||||
ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
|
||||
if (ret == -1) {
|
||||
|
|
|
|||
|
|
@ -274,8 +274,10 @@ static int filter_element(tommy_list* filterlist, struct snapraid_filter** reaso
|
|||
}
|
||||
}
|
||||
|
||||
/* directories are always included by default, otherwise we cannot apply rules */
|
||||
/* to the contained files */
|
||||
/*
|
||||
* Directories are always included by default, otherwise we cannot apply rules
|
||||
* to the contained files
|
||||
*/
|
||||
if (is_def_include)
|
||||
return 0;
|
||||
|
||||
|
|
@ -897,8 +899,10 @@ int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax)
|
|||
{
|
||||
struct extent_disk_empty arg = { blockmax };
|
||||
|
||||
/* if there is an element, it's not empty */
|
||||
/* even if links and dirs have no block allocation */
|
||||
/*
|
||||
* If there is an element, it's not empty
|
||||
* even if links and dirs have no block allocation
|
||||
*/
|
||||
if (!tommy_list_empty(&disk->filelist))
|
||||
return 0;
|
||||
if (!tommy_list_empty(&disk->linklist))
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, in
|
|||
/* initial values, changed later if required */
|
||||
handle->created = 0;
|
||||
|
||||
/* flags for opening */
|
||||
/* O_BINARY: open as binary file (Windows only) */
|
||||
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
|
||||
/*
|
||||
* Flags for opening
|
||||
* O_BINARY: open as binary file (Windows only)
|
||||
* O_NOFOLLOW: do not follow links to ensure to open the real file
|
||||
*/
|
||||
flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
|
||||
|
||||
/* open for read write */
|
||||
|
|
@ -144,9 +146,11 @@ int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int
|
|||
/* for sure not created */
|
||||
handle->created = 0;
|
||||
|
||||
/* flags for opening */
|
||||
/* O_BINARY: open as binary file (Windows only) */
|
||||
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
|
||||
/*
|
||||
* Flags for opening
|
||||
* O_BINARY: open as binary file (Windows only)
|
||||
* O_NOFOLLOW: do not follow links to ensure to open the real file
|
||||
*/
|
||||
flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
|
||||
|
||||
/* open for read */
|
||||
|
|
|
|||
|
|
@ -248,11 +248,13 @@ static void import_dir(struct snapraid_state* state, const char* dir)
|
|||
/* convert dirent to lstat result */
|
||||
dirent_lstat(dd, &st);
|
||||
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
|
||||
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
|
||||
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
|
||||
/* of file is it, and lstat_sync() doesn't always work */
|
||||
/*
|
||||
* If the st_mode field is missing, takes care to fill it using normal lstat()
|
||||
* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined),
|
||||
* because we use a directory reading method that doesn't read info about ReparsePoint.
|
||||
* Note that here we cannot call here lstat_sync(), because we don't know what kind
|
||||
* of file is it, and lstat_sync() doesn't always work
|
||||
*/
|
||||
if (st.st_mode == 0) {
|
||||
if (lstat(path_next, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
|
|||
62
cmdline/io.c
62
cmdline/io.c
|
|
@ -287,8 +287,10 @@ static struct snapraid_task* io_reader_step(struct snapraid_worker* worker)
|
|||
while (1) {
|
||||
unsigned next_index;
|
||||
|
||||
/* check if the worker has to exit */
|
||||
/* even if there is work to do */
|
||||
/*
|
||||
* Check if the worker has to exit
|
||||
* even if there is work to do
|
||||
*/
|
||||
if (io->done) {
|
||||
thread_mutex_unlock(&io->io_mutex);
|
||||
return 0;
|
||||
|
|
@ -378,8 +380,10 @@ static struct snapraid_task* io_writer_step(struct snapraid_worker* worker, int
|
|||
return task;
|
||||
}
|
||||
|
||||
/* check if the worker has to exit */
|
||||
/* but only if there is no work to do */
|
||||
/*
|
||||
* Check if the worker has to exit
|
||||
* but only if there is no work to do
|
||||
*/
|
||||
if (io->done) {
|
||||
thread_mutex_unlock(&io->io_mutex);
|
||||
return 0;
|
||||
|
|
@ -503,8 +507,10 @@ static void io_refresh_thread(struct snapraid_io* io)
|
|||
worker->handle->disk->cached_blocks = cached;
|
||||
}
|
||||
|
||||
/* for all writers, count the number of written blocks */
|
||||
/* note that this is a kind of "opposite" of cached blocks */
|
||||
/*
|
||||
* For all writers, count the number of written blocks
|
||||
* note that this is a kind of "opposite" of cached blocks
|
||||
*/
|
||||
for (i = 0; i < io->writer_max; ++i) {
|
||||
unsigned begin, end, cached;
|
||||
struct snapraid_worker* worker = &io->writer_map[i];
|
||||
|
|
@ -573,9 +579,11 @@ static struct snapraid_task* io_task_read_thread(struct snapraid_io* io, unsigne
|
|||
unsigned* let;
|
||||
unsigned busy_index;
|
||||
|
||||
/* get the index the IO is using */
|
||||
/* we must ensure that this index has not a read in progress */
|
||||
/* to avoid a concurrent access */
|
||||
/*
|
||||
* Get the index the IO is using
|
||||
* we must ensure that this index has not a read in progress
|
||||
* to avoid a concurrent access
|
||||
*/
|
||||
busy_index = io->reader_index;
|
||||
|
||||
/* search for a worker that has already finished */
|
||||
|
|
@ -605,8 +613,10 @@ static struct snapraid_task* io_task_read_thread(struct snapraid_io* io, unsigne
|
|||
|
||||
task = &worker->task_map[io->reader_index];
|
||||
|
||||
/* mark the worker as processed */
|
||||
/* setting the previous one to point at the next one */
|
||||
/*
|
||||
* Mark the worker as processed
|
||||
* setting the previous one to point at the next one
|
||||
*/
|
||||
*let = io->reader_list[i + 1];
|
||||
|
||||
thread_mutex_unlock(&io->io_mutex);
|
||||
|
|
@ -661,11 +671,13 @@ static void io_parity_write_thread(struct snapraid_io* io, unsigned* pos, unsign
|
|||
unsigned* let;
|
||||
unsigned busy_index;
|
||||
|
||||
/* get the next index the IO is going to use */
|
||||
/* we must ensure that this index has not a write in progress */
|
||||
/* to avoid a concurrent access */
|
||||
/* note that we are already sure that a write is not in progress */
|
||||
/* at the index the IO is using at now */
|
||||
/*
|
||||
* Get the next index the IO is going to use
|
||||
* we must ensure that this index has not a write in progress
|
||||
* to avoid a concurrent access
|
||||
* note that we are already sure that a write is not in progress
|
||||
* at the index the IO is using at now
|
||||
*/
|
||||
busy_index = (io->writer_index + 1) % io->io_max;
|
||||
|
||||
/* search for a worker that has already finished */
|
||||
|
|
@ -691,8 +703,10 @@ static void io_parity_write_thread(struct snapraid_io* io, unsigned* pos, unsign
|
|||
|
||||
/* if the worker has finished this index */
|
||||
if (busy_index != worker->index) {
|
||||
/* mark the worker as processed */
|
||||
/* setting the previous one to point at the next one */
|
||||
/*
|
||||
* Mark the worker as processed
|
||||
* setting the previous one to point at the next one
|
||||
*/
|
||||
*let = io->writer_list[i + 1];
|
||||
|
||||
thread_mutex_unlock(&io->io_mutex);
|
||||
|
|
@ -822,8 +836,10 @@ static void io_start_thread(struct snapraid_io* io,
|
|||
for (i = 0; i < IO_WRITER_ERROR_MAX; ++i)
|
||||
io->writer_error[i] = 0;
|
||||
|
||||
/* setup the initial read pending tasks, except the latest one, */
|
||||
/* the latest will be initialized at the fist io_read_next() call */
|
||||
/*
|
||||
* Setup the initial read pending tasks, except the latest one,
|
||||
* the latest will be initialized at the fist io_read_next() call
|
||||
*/
|
||||
for (i = 0; i < io->io_max - 1; ++i) {
|
||||
block_off_t blockcur = io_position_next(io);
|
||||
|
||||
|
|
@ -920,8 +936,10 @@ void io_init(struct snapraid_io* io, struct snapraid_state* state,
|
|||
|
||||
#if HAVE_THREAD
|
||||
if (io_cache == 0) {
|
||||
/* default is 16 MiB of cache */
|
||||
/* this seems to be a good tradeoff between speed and memory usage */
|
||||
/*
|
||||
* Default is 16 MiB of cache
|
||||
* this seems to be a good tradeoff between speed and memory usage
|
||||
*/
|
||||
io->io_max = 16 * 1024 * 1024 / state->block_size;
|
||||
if (io->io_max < IO_MIN)
|
||||
io->io_max = IO_MIN;
|
||||
|
|
|
|||
|
|
@ -227,10 +227,12 @@ void state_locate_mark_tail_blocks_for_resync(struct snapraid_state* state, uint
|
|||
struct snapraid_file* file = entry->file;
|
||||
struct snapraid_disk* disk = entry->disk;
|
||||
|
||||
/* reallocate the full file, not only the part of in the free zone */
|
||||
/* this is required because the files blocks have to be in order in */
|
||||
/* in the parity file. */
|
||||
/* not reallocating the file head will prevent the reallocation of the file tail */
|
||||
/*
|
||||
* Reallocate the full file, not only the part of in the free zone
|
||||
* this is required because the files blocks have to be in order in
|
||||
* in the parity file.
|
||||
* not reallocating the file head will prevent the reallocation of the file tail
|
||||
*/
|
||||
for (block_off_t f = 0; f < file->blockmax; ++f) {
|
||||
block_off_t parity_pos = fs_file2par_find(disk, file, f);
|
||||
|
||||
|
|
|
|||
|
|
@ -161,8 +161,10 @@ void os_init(int opt)
|
|||
/* get pointer to RtlGenRandom, note that it was reported missing in some cases */
|
||||
ptr_RtlGenRandom = (void*)GetProcAddress(dll_advapi32, "SystemFunction036");
|
||||
|
||||
/* set the thread execution level to avoid sleep */
|
||||
/* first try for Windows 7 */
|
||||
/*
|
||||
* Set the thread execution level to avoid sleep
|
||||
* first try for Windows 7
|
||||
*/
|
||||
if (SetThreadExecutionState(WIN32_ES_CONTINUOUS | WIN32_ES_SYSTEM_REQUIRED | WIN32_ES_AWAYMODE_REQUIRED) == 0) {
|
||||
/* retry with the XP variant */
|
||||
SetThreadExecutionState(WIN32_ES_CONTINUOUS | WIN32_ES_SYSTEM_REQUIRED);
|
||||
|
|
@ -348,13 +350,17 @@ static wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_requ
|
|||
|
||||
dst = conv_buf;
|
||||
|
||||
/* note that we always check for both / and \ because the path is blindly */
|
||||
/* converted to unix format by path_import() */
|
||||
/*
|
||||
* Note that we always check for both / and \ because the path is blindly
|
||||
* converted to unix format by path_import()
|
||||
*/
|
||||
|
||||
if (only_if_required && strlen(src) < 260 - 12) {
|
||||
/* it's a short path */
|
||||
/* 260 is the MAX_PATH, note that it includes the space for the terminating NUL */
|
||||
/* 12 is an additional space for filename, required when creating directory */
|
||||
/*
|
||||
* It's a short path
|
||||
* 260 is the MAX_PATH, note that it includes the space for the terminating NUL
|
||||
* 12 is an additional space for filename, required when creating directory
|
||||
*/
|
||||
|
||||
/* do nothing */
|
||||
} else if (is_slash(src[0]) && is_slash(src[1]) && (src[2] == '?' || src[2] == '.') && is_slash(src[3])) {
|
||||
|
|
@ -400,10 +406,12 @@ static wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_requ
|
|||
os_abort();
|
||||
}
|
||||
|
||||
/* convert any / to \ */
|
||||
/* note that in UTF-16, it's not possible to have '/' used as part */
|
||||
/* of a pair of codes representing a single UNICODE char */
|
||||
/* See: http://en.wikipedia.org/wiki/UTF-16 */
|
||||
/*
|
||||
* Convert any / to \
|
||||
* note that in UTF-16, it's not possible to have '/' used as part
|
||||
* of a pair of codes representing a single UNICODE char
|
||||
* See: http://en.wikipedia.org/wiki/UTF-16
|
||||
*/
|
||||
while (*dst) {
|
||||
if (*dst == L'/')
|
||||
*dst = L'\\';
|
||||
|
|
@ -571,9 +579,11 @@ static int windows_stream2stat(const BY_HANDLE_FILE_INFORMATION* info, const FIL
|
|||
{
|
||||
int64_t mtime;
|
||||
|
||||
/* The FILE_ID_BOTH_DIR_INFO doesn't have the ReparseTag information */
|
||||
/* we could use instead FILE_ID_EXTD_DIR_INFO, but it's available only */
|
||||
/* from Windows Server 2012 */
|
||||
/*
|
||||
* The FILE_ID_BOTH_DIR_INFO doesn't have the ReparseTag information
|
||||
* we could use instead FILE_ID_EXTD_DIR_INFO, but it's available only
|
||||
* from Windows Server 2012
|
||||
*/
|
||||
windows_attr2stat(stream->FileAttributes, 0, st);
|
||||
|
||||
st->st_size = stream->EndOfFile.QuadPart;
|
||||
|
|
@ -694,9 +704,11 @@ static void windows_errno(DWORD error)
|
|||
{
|
||||
switch (error) {
|
||||
case ERROR_INVALID_HANDLE :
|
||||
/* we check for a bad handle calling _get_osfhandle() */
|
||||
/* and in such case we return EBADF */
|
||||
/* Other cases are here identified with EINVAL */
|
||||
/*
|
||||
* We check for a bad handle calling _get_osfhandle()
|
||||
* and in such case we return EBADF
|
||||
* Other cases are here identified with EINVAL
|
||||
*/
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case ERROR_HANDLE_EOF : /* in ReadFile() over the end of the file */
|
||||
|
|
@ -866,13 +878,17 @@ static BOOL GetFilePhysicalOffset(HANDLE h, uint64_t* physical)
|
|||
if (!ret) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_MORE_DATA) {
|
||||
/* we ignore ERROR_MODE_DATA because we are interested only at the first entry */
|
||||
/* and this is the expected error if the files has more entries */
|
||||
/*
|
||||
* We ignore ERROR_MODE_DATA because we are interested only at the first entry
|
||||
* and this is the expected error if the files has more entries
|
||||
*/
|
||||
} else if (error == ERROR_HANDLE_EOF) {
|
||||
/* if the file is small, it can be stored in the Master File Table (MFT) */
|
||||
/* and then it doesn't have a physical address */
|
||||
/* In such case we report a specific fake address, to report this special condition */
|
||||
/* that it's different from the 0 offset reported by the underline file system */
|
||||
/*
|
||||
* If the file is small, it can be stored in the Master File Table (MFT)
|
||||
* and then it doesn't have a physical address
|
||||
* In such case we report a specific fake address, to report this special condition
|
||||
* that it's different from the 0 offset reported by the underline file system
|
||||
*/
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
return TRUE;
|
||||
} else if (error == ERROR_NOT_SUPPORTED) {
|
||||
|
|
@ -1649,8 +1665,10 @@ static windows_dir* windows_opendir_stream(const char* dir)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* get dir information for the VolumeSerialNumber */
|
||||
/* this value is used for all the files in the dir */
|
||||
/*
|
||||
* Get dir information for the VolumeSerialNumber
|
||||
* this value is used for all the files in the dir
|
||||
*/
|
||||
if (!GetFileInformationByHandle(dirstream->h, &dirstream->info)) {
|
||||
DWORD error = GetLastError();
|
||||
CloseHandle(dirstream->h);
|
||||
|
|
@ -1789,10 +1807,12 @@ int windows_symlink(const char* existing, const char* file)
|
|||
wchar_t conv_buf_file[CONV_MAX];
|
||||
wchar_t conv_buf_existing[CONV_MAX];
|
||||
|
||||
/* We must convert to the extended-length \\?\ format if the path is too long */
|
||||
/* otherwise the link creation fails. */
|
||||
/* But we don't want to always convert it, to avoid to recreate */
|
||||
/* user symlinks different than they were before */
|
||||
/*
|
||||
* We must convert to the extended-length \\?\ format if the path is too long
|
||||
* otherwise the link creation fails.
|
||||
* But we don't want to always convert it, to avoid to recreate
|
||||
* user symlinks different than they were before
|
||||
*/
|
||||
if (!CreateSymbolicLinkW(convert(conv_buf_file, file), convert_if_required(conv_buf_existing, existing),
|
||||
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
|
||||
) {
|
||||
|
|
@ -1971,8 +1991,10 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* if it doesn't exist, we assume a file */
|
||||
/* and we check for the containing dir */
|
||||
/*
|
||||
* If it doesn't exist, we assume a file
|
||||
* and we check for the containing dir
|
||||
*/
|
||||
attr = 0;
|
||||
}
|
||||
|
||||
|
|
@ -1994,8 +2016,10 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har
|
|||
slash[1] = 0;
|
||||
}
|
||||
|
||||
/* get the free space of the directory */
|
||||
/* note that it must be a directory */
|
||||
/*
|
||||
* Get the free space of the directory
|
||||
* note that it must be a directory
|
||||
*/
|
||||
if (!GetDiskFreeSpaceExW(convert(conv_buf, dir), 0, &total_bytes, &total_free_bytes)) {
|
||||
windows_errno(GetLastError());
|
||||
return -1;
|
||||
|
|
@ -2383,8 +2407,10 @@ static int devresolve(const char* mount, char* file, size_t file_size, char* wfi
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* remove the final slash, otherwise CreateFile() opens the file-system */
|
||||
/* and not the volume */
|
||||
/*
|
||||
* Remove the final slash, otherwise CreateFile() opens the file-system
|
||||
* and not the volume
|
||||
*/
|
||||
i = 0;
|
||||
while (volume_guid[i] != 0)
|
||||
++i;
|
||||
|
|
|
|||
|
|
@ -148,12 +148,14 @@ void cmd_generate_file(const char* path, int size)
|
|||
|
||||
data = malloc(size);
|
||||
|
||||
/* We don't write zero bytes because we want to test */
|
||||
/* the recovering of new files, after an aborted sync */
|
||||
/* If the files contains full blocks at zero */
|
||||
/* this is an impossible condition to recover */
|
||||
/* because we cannot differentiate between an unused block */
|
||||
/* and a file filled with 0 */
|
||||
/*
|
||||
* We don't write zero bytes because we want to test
|
||||
* the recovering of new files, after an aborted sync
|
||||
* If the files contains full blocks at zero
|
||||
* this is an impossible condition to recover
|
||||
* because we cannot differentiate between an unused block
|
||||
* and a file filled with 0
|
||||
*/
|
||||
rndnz_range(data, size);
|
||||
|
||||
f = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW, 0600);
|
||||
|
|
@ -351,8 +353,10 @@ void cmd_damage(const char* path, int size)
|
|||
{
|
||||
struct stat st;
|
||||
|
||||
/* here a 0 size means to change nothing */
|
||||
/* as also the timestamp should not be changed */
|
||||
/*
|
||||
* Here a 0 size means to change nothing
|
||||
* as also the timestamp should not be changed
|
||||
*/
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@ block_off_t parity_allocated_size(struct snapraid_state* state)
|
|||
/* start from the declared size */
|
||||
block_off_t block = fs_size(disk);
|
||||
|
||||
/* decrease the block until an allocated one, but part of a file */
|
||||
/* we don't stop at deleted blocks, because we want to have them cleared */
|
||||
/* if they are at the end of the parity */
|
||||
/*
|
||||
* Decrease the block until an allocated one, but part of a file
|
||||
* we don't stop at deleted blocks, because we want to have them cleared
|
||||
* if they are at the end of the parity
|
||||
*/
|
||||
while (block > parity_block && !block_has_file(fs_par2block_find(disk, block - 1)))
|
||||
--block;
|
||||
|
||||
|
|
@ -384,8 +386,10 @@ static int parity_handle_fill(struct snapraid_split_handle* split, data_off_t si
|
|||
/* present size */
|
||||
base = split->st.st_size;
|
||||
|
||||
/* truncate it to block size multiplier */
|
||||
/* in case of damage the size may get wrong */
|
||||
/*
|
||||
* Truncate it to block size multiplier
|
||||
* in case of damage the size may get wrong
|
||||
*/
|
||||
base &= ~block_mask;
|
||||
|
||||
/* size we have to increase */
|
||||
|
|
@ -432,8 +436,10 @@ static int parity_handle_fill(struct snapraid_split_handle* split, data_off_t si
|
|||
}
|
||||
#endif
|
||||
|
||||
/* shrink to the expected size to ensure to throw away any extra */
|
||||
/* data allocated when the grow operation fails */
|
||||
/*
|
||||
* Shrink to the expected size to ensure to throw away any extra
|
||||
* data allocated when the grow operation fails
|
||||
*/
|
||||
return parity_handle_shrink(split, base);
|
||||
}
|
||||
|
||||
|
|
@ -528,8 +534,10 @@ int parity_chsize(struct snapraid_parity_handle* handle, struct snapraid_parity*
|
|||
data_off_t run;
|
||||
|
||||
if (is_fixed) {
|
||||
/* if the required size is smaller, we have to reduce also the file */
|
||||
/* ignoring the previous size */
|
||||
/*
|
||||
* If the required size is smaller, we have to reduce also the file
|
||||
* ignoring the previous size
|
||||
*/
|
||||
if (size <= split->size) {
|
||||
/* mark it as not fixed anymore for the later check */
|
||||
is_fixed = 0;
|
||||
|
|
@ -637,8 +645,10 @@ int parity_open(struct snapraid_parity_handle* handle, const struct snapraid_par
|
|||
split->size = parity->split_map[s].size;
|
||||
split->limit_size = PARITY_LIMIT(limit_size, s, level);
|
||||
|
||||
/* open for read */
|
||||
/* O_NOATIME: do not change access time */
|
||||
/*
|
||||
* Open for read
|
||||
* O_NOATIME: do not change access time
|
||||
*/
|
||||
flags = O_RDONLY | O_BINARY | advise_flags(&split->advise);
|
||||
|
||||
split->f = open_noatime(split->path, flags);
|
||||
|
|
@ -712,9 +722,11 @@ int parity_sync(struct snapraid_parity_handle* handle)
|
|||
struct snapraid_split_handle* split = &handle->split_map[s];
|
||||
int ret;
|
||||
|
||||
/* Ensure that data changes are written to disk. */
|
||||
/* This is required to ensure that parity is more updated than content */
|
||||
/* in case of a system crash. */
|
||||
/*
|
||||
* Ensure that data changes are written to disk.
|
||||
* This is required to ensure that parity is more updated than content
|
||||
* in case of a system crash.
|
||||
*/
|
||||
ret = fsync(split->f);
|
||||
if (ret != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -766,9 +778,11 @@ int parity_close(struct snapraid_parity_handle* handle)
|
|||
ret = close(split->f);
|
||||
if (ret != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
/* This is a serious error, as it may be the result of a failed write */
|
||||
/* identified at later time. */
|
||||
/* In a normal file-system (not NFS) it should never happen */
|
||||
/*
|
||||
* This is a serious error, as it may be the result of a failed write
|
||||
* identified at later time.
|
||||
* In a normal file-system (not NFS) it should never happen
|
||||
*/
|
||||
log_fatal(errno, "Error closing parity file '%s'. %s.\n", split->path, strerror(errno));
|
||||
f_ret = -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
|
|
|||
|
|
@ -95,8 +95,10 @@ static int clean_dir(const char* dir)
|
|||
/* convert dirent to lstat result */
|
||||
dirent_lstat(dd, &st);
|
||||
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows */
|
||||
/*
|
||||
* If the st_mode field is missing, takes care to fill it using normal lstat()
|
||||
* at now this can happen only in Windows
|
||||
*/
|
||||
if (st.st_mode == 0) {
|
||||
if (lstat(path_next, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -126,9 +128,11 @@ static int clean_dir(const char* dir)
|
|||
if (ret < 0) {
|
||||
#ifdef _WIN32
|
||||
if (errno == EACCES) {
|
||||
/* in Windows just ignore EACCES errors removing directories */
|
||||
/* because it could happen that the directory is in use */
|
||||
/* and it cannot be removed */
|
||||
/*
|
||||
* In Windows just ignore EACCES errors removing directories
|
||||
* because it could happen that the directory is in use
|
||||
* and it cannot be removed
|
||||
*/
|
||||
log_fatal(errno, "Directory '%s' not removed because it's in use.\n", path_next);
|
||||
full = 1;
|
||||
} else
|
||||
|
|
@ -207,8 +211,10 @@ static void read_dir(tommy_hashdyn* poolset, const char* base_dir, const char* s
|
|||
/* convert dirent to lstat result */
|
||||
dirent_lstat(dd, &st);
|
||||
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows */
|
||||
/*
|
||||
* If the st_mode field is missing, takes care to fill it using normal lstat()
|
||||
* at now this can happen only in Windows
|
||||
*/
|
||||
if (st.st_mode == 0) {
|
||||
if (lstat(path_next, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
|
|||
232
cmdline/scan.c
232
cmdline/scan.c
|
|
@ -264,8 +264,10 @@ static void scan_file_allocate(struct snapraid_scan* scan, struct snapraid_file*
|
|||
|
||||
/* if the block is an empty one */
|
||||
if (over_state == BLOCK_STATE_EMPTY) {
|
||||
/* the block was empty and filled with zeros */
|
||||
/* set the hash to the special ZERO value */
|
||||
/*
|
||||
* The block was empty and filled with zeros
|
||||
* set the hash to the special ZERO value
|
||||
*/
|
||||
hash_zero_set(block->hash);
|
||||
} else {
|
||||
/* otherwise it's a DELETED one */
|
||||
|
|
@ -325,10 +327,12 @@ static void scan_file_deallocate(struct snapraid_scan* scan, struct snapraid_fil
|
|||
/* state changed */
|
||||
scan->need_write = 1;
|
||||
|
||||
/* here we are supposed to adjust the ::first_free_block position */
|
||||
/* with the parity position we are deleting */
|
||||
/* but we also know that we do only delayed insert, after all the deletion, */
|
||||
/* so at this point ::first_free_block is always at 0, and we don't need to update it */
|
||||
/*
|
||||
* Here we are supposed to adjust the ::first_free_block position
|
||||
* with the parity position we are deleting
|
||||
* but we also know that we do only delayed insert, after all the deletion,
|
||||
* so at this point ::first_free_block is always at 0, and we don't need to update it
|
||||
*/
|
||||
if (disk->first_free_block != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Internal inconsistency for first free position at '%u' deallocating file '%s'\n", disk->first_free_block, file->sub);
|
||||
|
|
@ -341,8 +345,10 @@ static void scan_file_deallocate(struct snapraid_scan* scan, struct snapraid_fil
|
|||
struct snapraid_block* block = fs_file2block_get(file, i);
|
||||
unsigned block_state;
|
||||
|
||||
/* in case we scan after an aborted sync, */
|
||||
/* we could get also intermediate states */
|
||||
/*
|
||||
* In case we scan after an aborted sync,
|
||||
* we could get also intermediate states
|
||||
*/
|
||||
block_state = block_state_get(block);
|
||||
switch (block_state) {
|
||||
case BLOCK_STATE_BLK :
|
||||
|
|
@ -603,9 +609,11 @@ static void scan_file_refresh(struct snapraid_scan* scan, const char* sub, struc
|
|||
log_fatal(ESOFT, "DANGER! Detected uncached inode change from %" PRIu64 " to %" PRIu64 " for file '%s'\n",
|
||||
(uint64_t)st->st_ino, (uint64_t)synced_st.st_ino, sub);
|
||||
log_fatal(ESOFT, "It's better if you run SnapRAID without other processes running.\n");
|
||||
/* at this point, it's too late to change inode */
|
||||
/* and having inconsistent inodes may result to internal failures */
|
||||
/* so, it's better to abort */
|
||||
/*
|
||||
* At this point, it's too late to change inode
|
||||
* and having inconsistent inodes may result to internal failures
|
||||
* so, it's better to abort
|
||||
*/
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
|
@ -729,8 +737,10 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
*/
|
||||
int has_past_inodes = !disk->has_volatile_inodes && !disk->has_different_uuid && !disk->has_unsupported_uuid;
|
||||
|
||||
/* always search with the new inode, in the all new inodes found until now, */
|
||||
/* with the eventual presence of also the past inodes */
|
||||
/*
|
||||
* Always search with the new inode, in the all new inodes found until now,
|
||||
* with the eventual presence of also the past inodes
|
||||
*/
|
||||
uint64_t inode = st->st_ino;
|
||||
|
||||
file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode));
|
||||
|
|
@ -741,9 +751,11 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
if (file->size == st->st_size
|
||||
&& file->mtime_sec == st->st_mtime
|
||||
&& (file->mtime_nsec == STAT_NSEC(st)
|
||||
/* always accept the stored value if it's STAT_NSEC_INVALID */
|
||||
/* it happens when upgrading from an old version of SnapRAID */
|
||||
/* not yet supporting the nanosecond field */
|
||||
/*
|
||||
* Always accept the stored value if it's STAT_NSEC_INVALID
|
||||
* it happens when upgrading from an old version of SnapRAID
|
||||
* not yet supporting the nanosecond field
|
||||
*/
|
||||
|| file->mtime_nsec == STAT_NSEC_INVALID
|
||||
)
|
||||
) {
|
||||
|
|
@ -765,8 +777,10 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
/* mark as present */
|
||||
file_flag_set(file, FILE_IS_PRESENT);
|
||||
|
||||
/* update the nanoseconds mtime only if different */
|
||||
/* to avoid unneeded updates */
|
||||
/*
|
||||
* Update the nanoseconds mtime only if different
|
||||
* to avoid unneeded updates
|
||||
*/
|
||||
if (file->mtime_nsec == STAT_NSEC_INVALID
|
||||
&& STAT_NSEC(st) != file->mtime_nsec
|
||||
) {
|
||||
|
|
@ -858,18 +872,22 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* assume a previously used inode, it's the worst case */
|
||||
/* and we handle it removing the duplicate stored inode. */
|
||||
/* If the file is found by name later, it will have the inode restored, */
|
||||
/* otherwise, it will get removed */
|
||||
/*
|
||||
* Assume a previously used inode, it's the worst case
|
||||
* and we handle it removing the duplicate stored inode.
|
||||
* If the file is found by name later, it will have the inode restored,
|
||||
* otherwise, it will get removed
|
||||
*/
|
||||
|
||||
/* remove from the inode set */
|
||||
tommy_hashdyn_remove_existing(&disk->inodeset, &file->nodeset);
|
||||
|
||||
/* clear the inode */
|
||||
/* this is not really needed for correct functionality */
|
||||
/* because we are going to set FILE_IS_WITHOUT_INODE */
|
||||
/* but it's easier for debugging to have invalid inodes set to 0 */
|
||||
/*
|
||||
* Clear the inode
|
||||
* this is not really needed for correct functionality
|
||||
* because we are going to set FILE_IS_WITHOUT_INODE
|
||||
* but it's easier for debugging to have invalid inodes set to 0
|
||||
*/
|
||||
file->inode = 0;
|
||||
|
||||
/* mark as missing inode */
|
||||
|
|
@ -921,17 +939,21 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
if (file->size == st->st_size
|
||||
&& file->mtime_sec == st->st_mtime
|
||||
&& (file->mtime_nsec == STAT_NSEC(st)
|
||||
/* always accept the stored value if it's STAT_NSEC_INVALID */
|
||||
/* it happens when upgrading from an old version of SnapRAID */
|
||||
/* not yet supporting the nanosecond field */
|
||||
/*
|
||||
* Always accept the stored value if it's STAT_NSEC_INVALID
|
||||
* it happens when upgrading from an old version of SnapRAID
|
||||
* not yet supporting the nanosecond field
|
||||
*/
|
||||
|| file->mtime_nsec == STAT_NSEC_INVALID
|
||||
)
|
||||
) {
|
||||
/* mark as present */
|
||||
file_flag_set(file, FILE_IS_PRESENT);
|
||||
|
||||
/* update the nano seconds mtime only if different */
|
||||
/* to avoid unneeded updates */
|
||||
/*
|
||||
* Update the nano seconds mtime only if different
|
||||
* to avoid unneeded updates
|
||||
*/
|
||||
if (file->mtime_nsec == STAT_NSEC_INVALID
|
||||
&& STAT_NSEC(st) != STAT_NSEC_INVALID
|
||||
) {
|
||||
|
|
@ -943,11 +965,13 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
|
||||
/* if when processing the disk we used the past inodes values */
|
||||
if (has_past_inodes) {
|
||||
/* if persistent inodes are supported, we are sure that the inode number */
|
||||
/* is now different, because otherwise the file would have been found */
|
||||
/* when searching by inode. */
|
||||
/* if the inode is different, it means a rewritten file with the same path */
|
||||
/* like when restoring a backup that restores also the timestamp */
|
||||
/*
|
||||
* If persistent inodes are supported, we are sure that the inode number
|
||||
* is now different, because otherwise the file would have been found
|
||||
* when searching by inode.
|
||||
* if the inode is different, it means a rewritten file with the same path
|
||||
* like when restoring a backup that restores also the timestamp
|
||||
*/
|
||||
++scan->count_restore;
|
||||
|
||||
log_tag("scan:restore:%s:%s\n", disk->name, esc_tag(sub));
|
||||
|
|
@ -967,9 +991,11 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
/* we have to save the new inode */
|
||||
scan->need_write = 1;
|
||||
} else {
|
||||
/* otherwise it's the case of not persistent inode, where doesn't */
|
||||
/* matter if the inode is different or equal, because they have no */
|
||||
/* meaning, and then we don't even save them */
|
||||
/*
|
||||
* Otherwise it's the case of not persistent inode, where doesn't
|
||||
* matter if the inode is different or equal, because they have no
|
||||
* meaning, and then we don't even save them
|
||||
*/
|
||||
++scan->count_equal;
|
||||
|
||||
if (state->opt.gui_verbose) {
|
||||
|
|
@ -1004,15 +1030,19 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
file_already_present_mtime_nsec = 0;
|
||||
}
|
||||
|
||||
/* refresh the info, to ensure that they are synced, */
|
||||
/* note that we refresh only the info of the new or modified files */
|
||||
/* because this is slow operation */
|
||||
/*
|
||||
* Refresh the info, to ensure that they are synced,
|
||||
* note that we refresh only the info of the new or modified files
|
||||
* because this is slow operation
|
||||
*/
|
||||
scan_file_refresh(scan, sub, st, &physical);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* do a safety check to ensure that the common ext4 case of zeroing */
|
||||
/* the size of a file after a crash doesn't propagate to the backup */
|
||||
/* this check is specific for Linux, so we disable it on Windows */
|
||||
/*
|
||||
* Do a safety check to ensure that the common ext4 case of zeroing
|
||||
* the size of a file after a crash doesn't propagate to the backup
|
||||
* this check is specific for Linux, so we disable it on Windows
|
||||
*/
|
||||
if (is_original_file_size_different_than_zero && st->st_size == 0) {
|
||||
if (!state->opt.force_zero) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1037,11 +1067,13 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
/* mark it as present */
|
||||
file_flag_set(file, FILE_IS_PRESENT);
|
||||
|
||||
/* if copy detection is enabled */
|
||||
/* note that the copy detection is tried also for updated files */
|
||||
/* this makes sense because it may happen to have two different copies */
|
||||
/* of the same file, and we move the right one over the wrong one */
|
||||
/* in such case we have a "copy" over an "update" */
|
||||
/*
|
||||
* If copy detection is enabled
|
||||
* note that the copy detection is tried also for updated files
|
||||
* this makes sense because it may happen to have two different copies
|
||||
* of the same file, and we move the right one over the wrong one
|
||||
* in such case we have a "copy" over an "update"
|
||||
*/
|
||||
if (!state->opt.force_nocopy) {
|
||||
tommy_uint32_t hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
|
||||
|
||||
|
|
@ -1053,8 +1085,10 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
char sub_other[PATH_MAX];
|
||||
|
||||
stamp_lock(other_disk);
|
||||
/* if the nanosecond part of the time stamp is valid, search */
|
||||
/* for name and stamp, otherwise for path and stamp */
|
||||
/*
|
||||
* If the nanosecond part of the time stamp is valid, search
|
||||
* for name and stamp, otherwise for path and stamp
|
||||
*/
|
||||
if (file->mtime_nsec != 0 && file->mtime_nsec != STAT_NSEC_INVALID)
|
||||
other_file = tommy_hashdyn_search(&other_disk->stampset, file_namestamp_compare, file, hash);
|
||||
else
|
||||
|
|
@ -1105,8 +1139,10 @@ static void scan_file(struct snapraid_scan* scan, int is_diff, const char* sub,
|
|||
}
|
||||
}
|
||||
|
||||
/* if not yet reported, do it now */
|
||||
/* we postpone this to avoid to print two times the copied files */
|
||||
/*
|
||||
* If not yet reported, do it now
|
||||
* we postpone this to avoid to print two times the copied files
|
||||
*/
|
||||
if (!is_file_reported) {
|
||||
if (is_file_already_present) {
|
||||
++scan->count_change;
|
||||
|
|
@ -1396,15 +1432,19 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa
|
|||
}
|
||||
|
||||
if (state->opt.force_order == SORT_ALPHA) {
|
||||
/* if requested sort alphabetically */
|
||||
/* this is mainly done for testing to ensure to always */
|
||||
/* process in the same way in different platforms */
|
||||
/*
|
||||
* If requested sort alphabetically
|
||||
* this is mainly done for testing to ensure to always
|
||||
* process in the same way in different platforms
|
||||
*/
|
||||
tommy_list_sort(&list, dd_name_compare);
|
||||
}
|
||||
#if HAVE_STRUCT_DIRENT_D_INO
|
||||
else if (!disk->has_volatile_inodes) {
|
||||
/* if inodes are persistent */
|
||||
/* sort the list of dir entries by inodes */
|
||||
/*
|
||||
* If inodes are persistent
|
||||
* sort the list of dir entries by inodes
|
||||
*/
|
||||
tommy_list_sort(&list, dd_ino_compare);
|
||||
}
|
||||
/* otherwise just keep the insertion order */
|
||||
|
|
@ -1446,11 +1486,13 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa
|
|||
st = DSTAT(path_next, dd, &st_buf);
|
||||
|
||||
#if HAVE_STRUCT_DIRENT_D_STAT
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
|
||||
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
|
||||
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
|
||||
/* of file is it, and lstat_sync() doesn't always work */
|
||||
/*
|
||||
* If the st_mode field is missing, takes care to fill it using normal lstat()
|
||||
* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined),
|
||||
* because we use a directory reading method that doesn't read info about ReparsePoint.
|
||||
* Note that here we cannot call here lstat_sync(), because we don't know what kind
|
||||
* of file is it, and lstat_sync() doesn't always work
|
||||
*/
|
||||
if (st->st_mode == 0) {
|
||||
if (lstat(path_next, st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1481,8 +1523,10 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa
|
|||
st = DSTAT(path_next, dd, &st_buf);
|
||||
|
||||
#if HAVE_LSTAT_SYNC
|
||||
/* if the st_ino field is missing, takes care to fill it using the extended lstat() */
|
||||
/* this can happen only in Windows */
|
||||
/*
|
||||
* If the st_ino field is missing, takes care to fill it using the extended lstat()
|
||||
* this can happen only in Windows
|
||||
*/
|
||||
if (st->st_ino == 0 || st->st_nlink == 0) {
|
||||
if (lstat_sync(path_next, st, 0) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1632,10 +1676,12 @@ static void* scan_disk(void* arg)
|
|||
|
||||
/* if inodes or UUID are not persistent/changed/unsupported */
|
||||
if (disk->has_volatile_inodes || disk->has_different_uuid || disk->has_unsupported_uuid) {
|
||||
/* remove all the inodes from the inode collection */
|
||||
/* if they are not persistent, all of them could be changed now */
|
||||
/* and we don't want to find false matching ones */
|
||||
/* see scan_file() for more details */
|
||||
/*
|
||||
* Remove all the inodes from the inode collection
|
||||
* if they are not persistent, all of them could be changed now
|
||||
* and we don't want to find false matching ones
|
||||
* See scan_file() for more details
|
||||
*/
|
||||
tommy_node* node = disk->filelist;
|
||||
while (node) {
|
||||
struct snapraid_file* file = node->data;
|
||||
|
|
@ -1716,9 +1762,11 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* we split the search in two phases because to detect files */
|
||||
/* moved from one disk to another we have to start deletion */
|
||||
/* only when all disks have all the new files found */
|
||||
/*
|
||||
* We split the search in two phases because to detect files
|
||||
* moved from one disk to another we have to start deletion
|
||||
* only when all disks have all the new files found
|
||||
*/
|
||||
|
||||
/* now process all the new and deleted files */
|
||||
for (i = scanlist; i != 0; i = i->next) {
|
||||
|
|
@ -1788,9 +1836,11 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
}
|
||||
}
|
||||
|
||||
/* sort the files before inserting them */
|
||||
/* we use a stable sort to ensure that if the reported physical offset/inode */
|
||||
/* are always 0, we keep at least the directory order */
|
||||
/*
|
||||
* Sort the files before inserting them
|
||||
* we use a stable sort to ensure that if the reported physical offset/inode
|
||||
* are always 0, we keep at least the directory order
|
||||
*/
|
||||
switch (state->opt.force_order) {
|
||||
case SORT_PHYSICAL :
|
||||
tommy_list_sort(&scan->file_insert_list, file_physical_compare);
|
||||
|
|
@ -1806,9 +1856,11 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
break;
|
||||
}
|
||||
|
||||
/* insert all the new files, we insert them only after the deletion */
|
||||
/* to reuse the just freed space */
|
||||
/* also check if the physical offset reported are fakes or not */
|
||||
/*
|
||||
* Insert all the new files, we insert them only after the deletion
|
||||
* to reuse the just freed space
|
||||
* also check if the physical offset reported are fakes or not
|
||||
*/
|
||||
node = scan->file_insert_list;
|
||||
phy_dup = 0;
|
||||
phy_last = FILEPHY_UNREAD_OFFSET;
|
||||
|
|
@ -1822,8 +1874,10 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
/* files without offset are expected to have duplicates */
|
||||
&& phy_last != FILEPHY_WITHOUT_OFFSET
|
||||
) {
|
||||
/* if verbose, print the list of duplicates real offsets */
|
||||
/* other cases are for offsets not supported, so we don't need to report them file by file */
|
||||
/*
|
||||
* If verbose, print the list of duplicates real offsets
|
||||
* other cases are for offsets not supported, so we don't need to report them file by file
|
||||
*/
|
||||
if (phy_last >= FILEPHY_REAL_OFFSET) {
|
||||
log_error(ESOFT, "WARNING! Files '%s%s' and '%s%s' share the same physical offset %" PRId64 ".\n", disk->mount_point, phy_file_last->sub, disk->mount_point, file->sub, phy_last);
|
||||
}
|
||||
|
|
@ -1840,8 +1894,10 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
scan_file_allocate(scan, file);
|
||||
}
|
||||
|
||||
/* mark the disk without reliable physical offset if it has duplicates */
|
||||
/* here it should never happen because we already sorted out hardlinks */
|
||||
/*
|
||||
* Mark the disk without reliable physical offset if it has duplicates
|
||||
* here it should never happen because we already sorted out hardlinks
|
||||
*/
|
||||
if (state->opt.force_order == SORT_PHYSICAL && phy_dup > 0) {
|
||||
disk->has_unreliable_physical = 1;
|
||||
}
|
||||
|
|
@ -1969,10 +2025,12 @@ static int state_diffscan(struct snapraid_state* state, int is_diff)
|
|||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
|
||||
/* don't print the message if the UUID changed because before */
|
||||
/* it was no set. */
|
||||
/* this is the normal condition for an empty disk because it */
|
||||
/* isn't stored */
|
||||
/*
|
||||
* Don't print the message if the UUID changed because before
|
||||
* it was no set.
|
||||
* This is the normal condition for an empty disk because it
|
||||
* isn't stored
|
||||
*/
|
||||
if (disk->has_different_uuid && !disk->had_empty_uuid) {
|
||||
if (!done) {
|
||||
done = 1;
|
||||
|
|
|
|||
|
|
@ -79,8 +79,10 @@ static int block_is_enabled(struct snapraid_plan* plan, block_off_t* countlast,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* if the time is less than the limit, always include */
|
||||
/* otherwise, check if we reached the last limit count */
|
||||
/*
|
||||
* If the time is less than the limit, always include
|
||||
* otherwise, check if we reached the last limit count
|
||||
*/
|
||||
if (blocktime == plan->timelimit) {
|
||||
/* if we reached the count limit */
|
||||
if (*countlast >= plan->lastlimit) {
|
||||
|
|
@ -132,8 +134,10 @@ static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_ta
|
|||
ret = handle_close(handle);
|
||||
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 */
|
||||
/*
|
||||
* This one is really an unexpected error, because we are only reading
|
||||
* and closing a descriptor should never fail
|
||||
*/
|
||||
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(report->sub), strerror(errno));
|
||||
log_fatal_errno(errno, disk->name);
|
||||
log_fatal(errno, "Stopping at block %u\n", blockcur);
|
||||
|
|
@ -175,9 +179,11 @@ static void scrub_data_reader(struct snapraid_worker* worker, struct snapraid_ta
|
|||
/* follow */
|
||||
}
|
||||
|
||||
/* note that we intentionally don't abort if the file has different attributes */
|
||||
/* from the last sync, as we are expected to return errors if running */
|
||||
/* in an unsynced array. This is just like the check command. */
|
||||
/*
|
||||
* Note that we intentionally don't abort if the file has different attributes
|
||||
* from the last sync, as we are expected to return errors if running
|
||||
* in an unsynced array. This is just like the check command.
|
||||
*/
|
||||
|
||||
task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, 0);
|
||||
if (task->read_size == -1) {
|
||||
|
|
@ -288,9 +294,11 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
++countmax;
|
||||
}
|
||||
|
||||
/* compute the autosave size for all disk, even if not read */
|
||||
/* this makes sense because the speed should be almost the same */
|
||||
/* if the disks are read in parallel */
|
||||
/*
|
||||
* Compute the autosave size for all disk, even if not read
|
||||
* this makes sense because the speed should be almost the same
|
||||
* if the disks are read in parallel
|
||||
*/
|
||||
autosavelimit = state->autosave / (diskmax * state->block_size);
|
||||
autosavemissing = countmax; /* blocks to do */
|
||||
autosavedone = 0; /* blocks done */
|
||||
|
|
@ -340,8 +348,10 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
silent_error_on_this_block = 0;
|
||||
io_error_on_this_block = 0;
|
||||
|
||||
/* if all the blocks at this address are synced */
|
||||
/* if not, parity is not even checked */
|
||||
/*
|
||||
* If all the blocks at this address are synced
|
||||
* if not, parity is not even checked
|
||||
*/
|
||||
block_is_unsynced = 0;
|
||||
|
||||
/* get block specific info */
|
||||
|
|
@ -362,8 +372,10 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
block_off_t file_pos;
|
||||
unsigned diskcur;
|
||||
|
||||
/* if the file on this disk is synced */
|
||||
/* if not, silent errors are assumed as expected error */
|
||||
/*
|
||||
* If the file on this disk is synced
|
||||
* if not, silent errors are assumed as expected error
|
||||
*/
|
||||
file_is_unsynced = 0;
|
||||
|
||||
/* until now is misc */
|
||||
|
|
@ -586,8 +598,10 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
/* set the error status keeping other info */
|
||||
info_set(&state->infoarr, blockcur, info_set_bad(info));
|
||||
} else if (error_on_this_block) {
|
||||
/* do nothing, as this is a generic error */
|
||||
/* likely caused by a not synced array */
|
||||
/*
|
||||
* Do nothing, as this is a generic error
|
||||
* likely caused by a not synced array
|
||||
*/
|
||||
} else {
|
||||
/* if rehash is needed */
|
||||
if (rehash) {
|
||||
|
|
@ -598,8 +612,10 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
|
|||
}
|
||||
}
|
||||
|
||||
/* update the time info of the block */
|
||||
/* and clear any other flag */
|
||||
/*
|
||||
* Update the time info of the block
|
||||
* and clear any other flag
|
||||
*/
|
||||
info_set(&state->infoarr, blockcur, info_make(now, 0, 0, 0));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,11 +192,13 @@ static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk,
|
|||
/* convert dirent to lstat result */
|
||||
dirent_lstat(dd, &st);
|
||||
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
|
||||
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
|
||||
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
|
||||
/* of file is it, and lstat_sync() doesn't always work */
|
||||
/*
|
||||
* If the st_mode field is missing, takes care to fill it using normal lstat()
|
||||
* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined),
|
||||
* because we use a directory reading method that doesn't read info about ReparsePoint.
|
||||
* Note that here we cannot call here lstat_sync(), because we don't know what kind
|
||||
* of file is it, and lstat_sync() doesn't always work
|
||||
*/
|
||||
if (st.st_mode == 0) {
|
||||
if (lstat(path_next, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
|
|||
|
|
@ -1036,15 +1036,19 @@ int snapraid_main(int argc, char* argv[])
|
|||
opt.expected_missing = 1;
|
||||
break;
|
||||
case 'e' :
|
||||
/* when processing only error, we filter files */
|
||||
/* and we apply fixes only to synced ones */
|
||||
/*
|
||||
* When processing only error, we filter files
|
||||
* and we apply fixes only to synced ones
|
||||
*/
|
||||
filter_error = 1;
|
||||
opt.badfileonly = 1;
|
||||
opt.syncedonly = 1;
|
||||
break;
|
||||
case 'b' :
|
||||
/* when processing only block with error, we filter both files and blocks */
|
||||
/* and we apply fixes only to synced ones */
|
||||
/*
|
||||
* When processing only block with error, we filter both files and blocks
|
||||
* and we apply fixes only to synced ones
|
||||
*/
|
||||
filter_error = 1;
|
||||
opt.badfileonly = 1;
|
||||
opt.badblockonly = 1;
|
||||
|
|
@ -1635,8 +1639,10 @@ int snapraid_main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
/* errors must be always fixed on all disks */
|
||||
/* because we don't keep the information on what disk is the error */
|
||||
/*
|
||||
* Errors must be always fixed on all disks
|
||||
* because we don't keep the information on what disk is the error
|
||||
*/
|
||||
if (filter_error != 0 && !tommy_list_empty(&filterlist_disk)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EUSER, "You cannot use -e, --filter-error and -d, --filter-disk simultaneously\n");
|
||||
|
|
@ -1798,8 +1804,10 @@ int snapraid_main(int argc, char* argv[])
|
|||
} else if (operation == OPERATION_SYNC) {
|
||||
state_read(&state);
|
||||
|
||||
/* mark the files that have to be reallocated */
|
||||
/* it will happen in inside scan_file_keep() called in state_scan() */
|
||||
/*
|
||||
* Mark the files that have to be reallocated
|
||||
* it will happen in inside scan_file_keep() called in state_scan()
|
||||
*/
|
||||
if (state.opt.force_realloc)
|
||||
state_locate_mark_tail_blocks_for_resync(&state, opt.parity_tail);
|
||||
|
||||
|
|
@ -1853,13 +1861,15 @@ int snapraid_main(int argc, char* argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
/* waits some time to ensure that any concurrent modification done at the files, */
|
||||
/* using the same mtime read by the scan process, will be read by sync. */
|
||||
/* Note that any later modification done, potentially not read by sync, will have */
|
||||
/* a different mtime, and it will be synchronized at the next sync. */
|
||||
/* The worst case is the FAT file-system with a two seconds resolution for mtime. */
|
||||
/* If you don't use FAT, the wait is not needed, because most file-systems have now */
|
||||
/* at least microseconds resolution, but better to be safe. */
|
||||
/*
|
||||
* Waits some time to ensure that any concurrent modification done at the files,
|
||||
* using the same mtime read by the scan process, will be read by sync.
|
||||
* Note that any later modification done, potentially not read by sync, will have
|
||||
* a different mtime, and it will be synchronized at the next sync.
|
||||
* The worst case is the FAT file-system with a two seconds resolution for mtime.
|
||||
* If you don't use FAT, the wait is not needed, because most file-systems have now
|
||||
* at least microseconds resolution, but better to be safe.
|
||||
*/
|
||||
if (!opt.skip_self)
|
||||
sleep(2);
|
||||
|
||||
|
|
|
|||
128
cmdline/state.c
128
cmdline/state.c
|
|
@ -287,9 +287,11 @@ static void state_config_check(struct snapraid_state* state, const char* path, t
|
|||
struct snapraid_disk* other = j->data;
|
||||
if (disk->device == other->device) {
|
||||
if (state->opt.force_device) {
|
||||
/* note that we just ignore the issue */
|
||||
/* and we DON'T mark the disk to be skipped */
|
||||
/* because we want to use these disks */
|
||||
/*
|
||||
* Note that we just ignore the issue
|
||||
* and we DON'T mark the disk to be skipped
|
||||
* because we want to use these disks
|
||||
*/
|
||||
if (!state->opt.no_warnings)
|
||||
log_fatal(EUSER, "DANGER! Ignoring that disks '%s' and '%s' are on the same device\n", disk->name, other->name);
|
||||
} else {
|
||||
|
|
@ -317,9 +319,11 @@ static void state_config_check(struct snapraid_state* state, const char* path, t
|
|||
for (s = 0; s < state->parity[l].split_mac; ++s) {
|
||||
if (disk->device == state->parity[l].split_map[s].device) {
|
||||
if (state->opt.force_device) {
|
||||
/* note that we just ignore the issue */
|
||||
/* and we DON'T mark the disk to be skipped */
|
||||
/* because we want to use these disks */
|
||||
/*
|
||||
* Note that we just ignore the issue
|
||||
* and we DON'T mark the disk to be skipped
|
||||
* because we want to use these disks
|
||||
*/
|
||||
if (!state->opt.no_warnings)
|
||||
log_fatal(EUSER, "DANGER! Ignoring that disks '%s' and %s '%s' are on the same device\n", disk->mount_point, lev_name(l), state->parity[l].split_map[s].path);
|
||||
} else {
|
||||
|
|
@ -378,9 +382,11 @@ static void state_config_check(struct snapraid_state* state, const char* path, t
|
|||
for (t = 0; t < state->parity[j].split_mac; ++t) {
|
||||
if (state->parity[l].split_map[s].device == state->parity[j].split_map[t].device) {
|
||||
if (state->opt.force_device) {
|
||||
/* note that we just ignore the issue */
|
||||
/* and we DON'T mark the disk to be skipped */
|
||||
/* because we want to use these disks */
|
||||
/*
|
||||
* Note that we just ignore the issue
|
||||
* and we DON'T mark the disk to be skipped
|
||||
* because we want to use these disks
|
||||
*/
|
||||
if (!state->opt.no_warnings)
|
||||
log_fatal(EUSER, "DANGER! Skipping parities '%s' and '%s' on the same device\n", lev_config_name(l), lev_config_name(j));
|
||||
} else {
|
||||
|
|
@ -1544,9 +1550,11 @@ static struct snapraid_disk* find_disk_by_uuid(struct snapraid_state* state, con
|
|||
tommy_node* i;
|
||||
struct snapraid_disk* found = 0;
|
||||
|
||||
/* special test case to find the first matching UUID */
|
||||
/* when testing UUID are all equal or not supported */
|
||||
/* and we should handle this case specifically */
|
||||
/*
|
||||
* Special test case to find the first matching UUID
|
||||
* when testing UUID are all equal or not supported
|
||||
* and we should handle this case specifically
|
||||
*/
|
||||
if (state->opt.match_first_uuid)
|
||||
return state->disklist->data;
|
||||
|
||||
|
|
@ -1580,10 +1588,12 @@ static void state_map(struct snapraid_state* state)
|
|||
unsigned diskcount;
|
||||
unsigned l, s;
|
||||
|
||||
/* remove all the mapping without a disk */
|
||||
/* this happens when a disk is removed from the configuration file */
|
||||
/* From SnapRAID 4.0 mappings are automatically removed if a disk is not used */
|
||||
/* when saving the content file, but we keep this code to import older content files. */
|
||||
/*
|
||||
* Remove all the mapping without a disk
|
||||
* this happens when a disk is removed from the configuration file
|
||||
* From SnapRAID 4.0 mappings are automatically removed if a disk is not used
|
||||
* when saving the content file, but we keep this code to import older content files.
|
||||
*/
|
||||
for (i = state->maplist; i != 0; ) {
|
||||
struct snapraid_map* map = i->data;
|
||||
struct snapraid_disk* disk;
|
||||
|
|
@ -1600,8 +1610,10 @@ static void state_map(struct snapraid_state* state)
|
|||
}
|
||||
}
|
||||
|
||||
/* maps each unmapped disk present in the configuration file in the first available hole */
|
||||
/* this happens when you add disks for the first time in the configuration file */
|
||||
/*
|
||||
* Maps each unmapped disk present in the configuration file in the first available hole
|
||||
* this happens when you add disks for the first time in the configuration file
|
||||
*/
|
||||
hole = 0; /* first position to try */
|
||||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
struct snapraid_disk* disk = i->data;
|
||||
|
|
@ -1684,8 +1696,10 @@ static void state_map(struct snapraid_state* state)
|
|||
++uuid_mismatch;
|
||||
log_error(ESOFT, "UUID change for disk '%s' from '%s' to '%s'\n", disk->name, map->uuid, disk->uuid);
|
||||
} else {
|
||||
/* no message here, because having a disk without */
|
||||
/* UUID is the normal state of an empty disk */
|
||||
/*
|
||||
* No message here, because having a disk without
|
||||
* UUID is the normal state of an empty disk
|
||||
*/
|
||||
disk->had_empty_uuid = 1;
|
||||
}
|
||||
|
||||
|
|
@ -1894,8 +1908,10 @@ void state_refresh(struct snapraid_state* state)
|
|||
}
|
||||
|
||||
|
||||
/* note what we don't set need_write = 1, because we don't want */
|
||||
/* to update the content file only for the free space info. */
|
||||
/*
|
||||
* Note what we don't set need_write = 1, because we don't want
|
||||
* to update the content file only for the free space info.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2349,9 +2365,11 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
hash_zero_set(block->hash);
|
||||
}
|
||||
|
||||
/* if we are disabling the copy optimization */
|
||||
/* we want also to clear any already previously stored information */
|
||||
/* in other sync commands */
|
||||
/*
|
||||
* If we are disabling the copy optimization
|
||||
* we want also to clear any already previously stored information
|
||||
* in other sync commands
|
||||
*/
|
||||
if (state->opt.force_nocopy && block_state_get(block) == BLOCK_STATE_REP) {
|
||||
/* set the hash value to INVALID */
|
||||
hash_invalid_set(block->hash);
|
||||
|
|
@ -2471,8 +2489,10 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
} else {
|
||||
/* extra info are accepted for backward compatibility */
|
||||
/* they are discarded at the first write */
|
||||
/*
|
||||
* Extra info are accepted for backward compatibility
|
||||
* they are discarded at the first write
|
||||
*/
|
||||
}
|
||||
|
||||
if (fs_is_block_unsynced(state, v_pos))
|
||||
|
|
@ -2956,8 +2976,10 @@ static void state_read_content(struct snapraid_state* state, const char* path, S
|
|||
tommy_array_set(&disk_mapping, mapping_max, disk);
|
||||
++mapping_max;
|
||||
} else if (c == 'P') {
|
||||
/* from SnapRAID 7.0 the 'P' command includes the free space */
|
||||
/* from SnapRAID 11.0 the 'P' command is deprecated by 'Q' */
|
||||
/*
|
||||
* From SnapRAID 7.0 the 'P' command includes the free space
|
||||
* from SnapRAID 11.0 the 'P' command is deprecated by 'Q'
|
||||
*/
|
||||
char v_uuid[UUID_MAX];
|
||||
uint32_t v_level;
|
||||
uint32_t v_total_blocks;
|
||||
|
|
@ -3667,8 +3689,10 @@ static void* state_write_thread(void* arg)
|
|||
++begin;
|
||||
}
|
||||
} else {
|
||||
/* write the run of blocks without hash */
|
||||
/* they can be either used or empty blocks */
|
||||
/*
|
||||
* Write the run of blocks without hash
|
||||
* they can be either used or empty blocks
|
||||
*/
|
||||
sputc('O', f);
|
||||
|
||||
/* next begin position */
|
||||
|
|
@ -3784,8 +3808,10 @@ static void* state_write_thread(void* arg)
|
|||
/* get the file crc */
|
||||
crc = scrc(f);
|
||||
|
||||
/* compare the crc of the data written to file */
|
||||
/* with the one of the data written to the stream */
|
||||
/*
|
||||
* Compare the crc of the data written to file
|
||||
* with the one of the data written to the stream
|
||||
*/
|
||||
if (crc != scrc_stream(f)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(ECONTENT, "CRC mismatch while writing the content stream.\n");
|
||||
|
|
@ -3861,8 +3887,10 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
/* check the file-system on all disks */
|
||||
state_fscheck(state, "before write");
|
||||
|
||||
/* clear the info for unused blocks */
|
||||
/* and get some other info */
|
||||
/*
|
||||
* Clear the info for unused blocks
|
||||
* and get some other info
|
||||
*/
|
||||
info_oldest = 0; /* oldest time in info */
|
||||
info_now = time(0); /* get the present time */
|
||||
info_has_rehash = 0; /* if there is a rehash info */
|
||||
|
|
@ -4002,8 +4030,10 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
} else {
|
||||
STREAM* f = context->f;
|
||||
|
||||
/* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
|
||||
/* than even in a system crash event we have one valid copy of the file. */
|
||||
/*
|
||||
* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure
|
||||
* than even in a system crash event we have one valid copy of the file.
|
||||
*/
|
||||
if (sflush(f) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(errno, "Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
|
||||
|
|
@ -4130,8 +4160,10 @@ static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
|
||||
/* than even in a system crash event we have one valid copy of the file. */
|
||||
/*
|
||||
* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure
|
||||
* than even in a system crash event we have one valid copy of the file.
|
||||
*/
|
||||
if (sflush(f) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(errno, "Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
|
||||
|
|
@ -4328,8 +4360,10 @@ void state_read(struct snapraid_state* state)
|
|||
node = node->next;
|
||||
}
|
||||
|
||||
/* start with a undefined default. */
|
||||
/* it's for compatibility with version 1.0 where MD5 was implicit. */
|
||||
/*
|
||||
* Start with a undefined default.
|
||||
* it's for compatibility with version 1.0 where MD5 was implicit.
|
||||
*/
|
||||
state->hash = HASH_UNDEFINED;
|
||||
|
||||
/* start with a zero seed, it was the default in old versions */
|
||||
|
|
@ -5152,14 +5186,18 @@ int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_o
|
|||
/* number of past measures */
|
||||
past = state->progress_tick;
|
||||
|
||||
/* drop the oldest ones, to promptly */
|
||||
/* skip the startup phase */
|
||||
/*
|
||||
* Drop the oldest ones, to promptly
|
||||
* skip the startup phase
|
||||
*/
|
||||
past -= past / 5;
|
||||
|
||||
/* check how much we can go in the past */
|
||||
if (past >= PROGRESS_MAX - 1) {
|
||||
/* the vector is filled, so we are already in position */
|
||||
/* to get the possible oldest one */
|
||||
/*
|
||||
* The vector is filled, so we are already in position
|
||||
* to get the possible oldest one
|
||||
*/
|
||||
oldest = state->progress_ptr + 1;
|
||||
} else {
|
||||
/* go backward the number of positions selected */
|
||||
|
|
|
|||
|
|
@ -176,18 +176,22 @@ int state_status(struct snapraid_state* state)
|
|||
disk_block_max_by_space = disk_block_count + disk->free_blocks;
|
||||
disk_block_max_by_parity = blockmax + parity_block_free;
|
||||
|
||||
/* the maximum usable space in a disk is limited by the smallest */
|
||||
/* of the disk size and the parity size */
|
||||
/* the wasted space is the space that we have to leave */
|
||||
/* free on the data disk, when the parity is filled up */
|
||||
/*
|
||||
* The maximum usable space in a disk is limited by the smallest
|
||||
* of the disk size and the parity size
|
||||
* the wasted space is the space that we have to leave
|
||||
* free on the data disk, when the parity is filled up
|
||||
*/
|
||||
if (disk_block_max_by_space < disk_block_max_by_parity) {
|
||||
disk_block_max = disk_block_max_by_space;
|
||||
} else {
|
||||
disk_block_max = disk_block_max_by_parity;
|
||||
}
|
||||
|
||||
/* wasted space is the difference of the two maximum size */
|
||||
/* if negative, it's extra space available in parity */
|
||||
/*
|
||||
* Wasted space is the difference of the two maximum size
|
||||
* if negative, it's extra space available in parity
|
||||
*/
|
||||
wasted = (int64_t)disk_block_max_by_space - (int64_t)disk_block_max_by_parity;
|
||||
wasted *= state->block_size;
|
||||
|
||||
|
|
|
|||
|
|
@ -500,8 +500,10 @@ void msg_info(const char* format, ...)
|
|||
|
||||
lock_msg();
|
||||
|
||||
/* don't output in stdlog as these messages */
|
||||
/* are always paired with a msg_tag() call */
|
||||
/*
|
||||
* Don't output in stdlog as these messages
|
||||
* are always paired with a msg_tag() call
|
||||
*/
|
||||
|
||||
if (msg_level >= MSG_INFO) {
|
||||
va_start(ap, format);
|
||||
|
|
@ -547,9 +549,11 @@ void msg_bar(const char* format, ...)
|
|||
|
||||
lock_msg();
|
||||
|
||||
/* don't output in stdlog as these messages */
|
||||
/* are intended for screen only */
|
||||
/* also don't flush stdout as they are intended to be partial messages */
|
||||
/*
|
||||
* Don't output in stdlog as these messages
|
||||
* are intended for screen only
|
||||
* also don't flush stdout as they are intended to be partial messages
|
||||
*/
|
||||
|
||||
if (msg_level >= MSG_BAR) {
|
||||
va_start(ap, format);
|
||||
|
|
@ -1080,8 +1084,10 @@ void pathimport(char* dst, size_t size, const char* src)
|
|||
pathcpy(dst, size, src);
|
||||
|
||||
#ifdef _WIN32
|
||||
/* convert the Windows dir separator '\' to C '/', */
|
||||
/* and the Windows escaping char '^' to the fnmatch '\' */
|
||||
/*
|
||||
* Convert the Windows dir separator '\' to C '/',
|
||||
* and the Windows escaping char '^' to the fnmatch '\'
|
||||
*/
|
||||
while (*dst) {
|
||||
switch (*dst) {
|
||||
case '\\' :
|
||||
|
|
@ -1429,8 +1435,10 @@ int advise_open(struct advise_struct* advise, int f)
|
|||
) {
|
||||
int ret;
|
||||
|
||||
/* advise noreuse access, this avoids to pollute the page cache */
|
||||
/* supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a */
|
||||
/*
|
||||
* Advise noreuse access, this avoids to pollute the page cache
|
||||
* supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a
|
||||
*/
|
||||
ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_NOREUSE);
|
||||
if (ret == ENOSYS) {
|
||||
/* call is not supported */
|
||||
|
|
@ -1861,12 +1869,14 @@ void* malloc_nofail(size_t size)
|
|||
}
|
||||
|
||||
#ifndef CHECKER /* Don't preinitialize when running for valgrind */
|
||||
/* Here we preinitialize the memory to ensure that the OS is really allocating it */
|
||||
/* and not only reserving the addressable space. */
|
||||
/* Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process. */
|
||||
/* Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to */
|
||||
/* get a real error from malloc() instead than a process killed. */
|
||||
/* Note that calloc() doesn't have the same effect. */
|
||||
/*
|
||||
* Here we preinitialize the memory to ensure that the OS is really allocating it
|
||||
* and not only reserving the addressable space.
|
||||
* Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process.
|
||||
* Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to
|
||||
* get a real error from malloc() instead than a process killed.
|
||||
* Note that calloc() doesn't have the same effect.
|
||||
*/
|
||||
memset(ptr, 0xA5, size);
|
||||
#endif
|
||||
|
||||
|
|
@ -2569,16 +2579,20 @@ int thread_cond_signal_outside = 0;
|
|||
void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
|
||||
{
|
||||
if (thread_cond_signal_outside) {
|
||||
/* without the thread checker unlock before signaling, */
|
||||
/* this reduces the number of context switches */
|
||||
/*
|
||||
* Without the thread checker unlock before signaling,
|
||||
* this reduces the number of context switches
|
||||
*/
|
||||
thread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
thread_cond_signal(cond);
|
||||
|
||||
if (!thread_cond_signal_outside) {
|
||||
/* with the thread checker unlock after signaling */
|
||||
/* to make explicit the condition and mutex relation */
|
||||
/*
|
||||
* With the thread checker unlock after signaling
|
||||
* to make explicit the condition and mutex relation
|
||||
*/
|
||||
thread_mutex_unlock(mutex);
|
||||
}
|
||||
}
|
||||
|
|
@ -2586,16 +2600,20 @@ void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
|
|||
void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
|
||||
{
|
||||
if (thread_cond_signal_outside) {
|
||||
/* without the thread checker unlock before signaling, */
|
||||
/* this reduces the number of context switches */
|
||||
/*
|
||||
* Without the thread checker unlock before signaling,
|
||||
* this reduces the number of context switches
|
||||
*/
|
||||
thread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
thread_cond_broadcast(cond);
|
||||
|
||||
if (!thread_cond_signal_outside) {
|
||||
/* with the thread checker unlock after signaling */
|
||||
/* to make explicit the condition and mutex relation */
|
||||
/*
|
||||
* With the thread checker unlock after signaling
|
||||
* to make explicit the condition and mutex relation
|
||||
*/
|
||||
thread_mutex_unlock(mutex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
242
cmdline/sync.c
242
cmdline/sync.c
|
|
@ -133,8 +133,10 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
|
|||
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 */
|
||||
/*
|
||||
* This one is really an unexpected error, because we are only reading
|
||||
* and closing a descriptor should never fail
|
||||
*/
|
||||
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(report->sub), strerror(errno));
|
||||
log_fatal_errno(errno, disk->name);
|
||||
log_fatal(errno, "Stopping at block %u\n", blockcur);
|
||||
|
|
@ -156,8 +158,10 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
|
|||
log_error_errno(errno, disk->name);
|
||||
|
||||
++soft_error;
|
||||
/* if the file is missing, it means that it was removed during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
/*
|
||||
* If the file is missing, it means that it was removed during sync
|
||||
* this isn't a serious error, so we skip this block, and continue with others
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -204,8 +208,10 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
|
|||
|
||||
++soft_error;
|
||||
|
||||
/* if the file is changed, it means that it was modified during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
/*
|
||||
* If the file is changed, it means that it was modified during sync
|
||||
* this isn't a serious error, so we skip this block, and continue with others
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -295,8 +301,10 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
|
|||
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 */
|
||||
/*
|
||||
* This one is really an unexpected error, because we are only reading
|
||||
* and closing a descriptor should never fail
|
||||
*/
|
||||
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockmax, disk->name, esc_tag(report->sub), strerror(errno));
|
||||
log_fatal_errno(errno, disk->name);
|
||||
log_fatal(errno, "Stopping at block %u\n", blockmax);
|
||||
|
|
@ -315,8 +323,10 @@ static int state_hash_process(struct snapraid_state* state, block_off_t blocksta
|
|||
end:
|
||||
state_progress_end(state, countpos, countmax, countsize, "Nothing to hash.\n");
|
||||
|
||||
/* note that at this point no io_error is possible */
|
||||
/* because at the first one we bail out */
|
||||
/*
|
||||
* Note that at this point no io_error is possible
|
||||
* because at the first one we bail out
|
||||
*/
|
||||
assert(io_error == 0);
|
||||
|
||||
if (soft_error || io_error || silent_error) {
|
||||
|
|
@ -471,8 +481,10 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
|
|||
/* get the block */
|
||||
task->block = fs_par2block_find(disk, blockcur);
|
||||
|
||||
/* if the block has no file, meaning that it's EMPTY or DELETED, */
|
||||
/* it doesn't participate in the new parity computation */
|
||||
/*
|
||||
* If the block has no file, meaning that it's EMPTY or DELETED,
|
||||
* it doesn't participate in the new parity computation
|
||||
*/
|
||||
if (!block_has_file(task->block)) {
|
||||
/* use an empty block */
|
||||
memset(buffer, 0, state->block_size);
|
||||
|
|
@ -490,8 +502,10 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
|
|||
ret = handle_close(handle);
|
||||
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 */
|
||||
/*
|
||||
* This one is really an unexpected error, because we are only reading
|
||||
* and closing a descriptor should never fail
|
||||
*/
|
||||
log_tag("%s:%u:%s:%s: Close error. %s.\n", es(errno), blockcur, disk->name, esc_tag(report->sub), strerror(errno));
|
||||
log_fatal_errno(errno, disk->name);
|
||||
log_fatal(errno, "Stopping at block %u\n", blockcur);
|
||||
|
|
@ -512,8 +526,10 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
|
|||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, disk->name);
|
||||
|
||||
/* if the file is missing, it means that it was removed during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
/*
|
||||
* If the file is missing, it means that it was removed during sync
|
||||
* this isn't a serious error, so we skip this block, and continue with others
|
||||
*/
|
||||
task->state = TASK_STATE_ERROR_CONTINUE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -557,8 +573,10 @@ static void sync_data_reader(struct snapraid_worker* worker, struct snapraid_tas
|
|||
}
|
||||
log_error_errno(ENOENT, disk->name); /* same message for ENOENT */
|
||||
|
||||
/* if the file is changed, it means that it was modified during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
/*
|
||||
* If the file is changed, it means that it was modified during sync
|
||||
* this isn't a serious error, so we skip this block, and continue with others
|
||||
*/
|
||||
task->state = TASK_STATE_ERROR_CONTINUE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -701,9 +719,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
++countmax;
|
||||
}
|
||||
|
||||
/* compute the autosave size for all disk, even if not read */
|
||||
/* this makes sense because the speed should be almost the same */
|
||||
/* if the disks are read in parallel */
|
||||
/*
|
||||
* Compute the autosave size for all disk, even if not read
|
||||
* this makes sense because the speed should be almost the same
|
||||
* if the disks are read in parallel
|
||||
*/
|
||||
autosavelimit = state->autosave / (diskmax * state->block_size);
|
||||
autosavemissing = countmax; /* blocks to do */
|
||||
autosavedone = 0; /* blocks done */
|
||||
|
|
@ -781,8 +801,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* if the parity is going to be updated */
|
||||
parity_going_to_be_updated = 0;
|
||||
|
||||
/* if the block is marked as bad, we force the parity update */
|
||||
/* because the bad block may be the result of a wrong parity */
|
||||
/*
|
||||
* If the block is marked as bad, we force the parity update
|
||||
* because the bad block may be the result of a wrong parity
|
||||
*/
|
||||
if (info_get_bad(info))
|
||||
parity_needs_to_be_updated = 1;
|
||||
|
||||
|
|
@ -825,26 +847,34 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* get the state of the block */
|
||||
block_state = block_state_get(block);
|
||||
|
||||
/* if the block has invalid parity, */
|
||||
/* we have to take care of it in case of recover */
|
||||
/*
|
||||
* If the block has invalid parity,
|
||||
* we have to take care of it in case of recover
|
||||
*/
|
||||
if (block_has_invalid_parity(block)) {
|
||||
/* store it in the failed set, because */
|
||||
/* the parity may be still computed with the previous content */
|
||||
/*
|
||||
* Store it in the failed set, because
|
||||
* the parity may be still computed with the previous content
|
||||
*/
|
||||
failed[failed_count].index = diskcur;
|
||||
failed[failed_count].size = state->block_size;
|
||||
failed[failed_count].block = block;
|
||||
++failed_count;
|
||||
|
||||
/* if the block has invalid parity, we have to update the parity */
|
||||
/* to include this block change */
|
||||
/* This also apply to CHG blocks, but we are going to handle */
|
||||
/* later this case to do the updates only if really needed */
|
||||
/*
|
||||
* If the block has invalid parity, we have to update the parity
|
||||
* to include this block change
|
||||
* This also apply to CHG blocks, but we are going to handle
|
||||
* later this case to do the updates only if really needed
|
||||
*/
|
||||
if (block_state != BLOCK_STATE_CHG)
|
||||
parity_needs_to_be_updated = 1;
|
||||
|
||||
/* note that DELETE blocks are skipped in the next check */
|
||||
/* and we have to store them in the failed blocks */
|
||||
/* before skipping */
|
||||
/*
|
||||
* Note that DELETE blocks are skipped in the next check
|
||||
* and we have to store them in the failed blocks
|
||||
* before skipping
|
||||
*/
|
||||
|
||||
/* follow */
|
||||
}
|
||||
|
|
@ -928,8 +958,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
|
||||
++soft_error;
|
||||
|
||||
/* if the file is changed, it means that it was modified during sync */
|
||||
/* this isn't a serious error, so we skip this block, and continue with others */
|
||||
/*
|
||||
* If the file is changed, it means that it was modified during sync
|
||||
* this isn't a serious error, so we skip this block, and continue with others
|
||||
*/
|
||||
error_on_this_block = 1;
|
||||
continue;
|
||||
} else { /* otherwise it's a BLK with silent error */
|
||||
|
|
@ -943,9 +975,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
failed[failed_count].block = block;
|
||||
++failed_count;
|
||||
|
||||
/* silent errors are very rare, and are not a signal that a disk */
|
||||
/* is going to fail. So, we just continue marking the block as bad */
|
||||
/* just like in scrub */
|
||||
/*
|
||||
* Silent errors are very rare, and are not a signal that a disk
|
||||
* is going to fail. So, we just continue marking the block as bad
|
||||
* just like in scrub
|
||||
*/
|
||||
++silent_error;
|
||||
silent_error_on_this_block = 1;
|
||||
continue;
|
||||
|
|
@ -954,9 +988,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
} else {
|
||||
/* if until now the parity doesn't need to be updated */
|
||||
if (!parity_needs_to_be_updated) {
|
||||
/* for sure it's a CHG block, because EMPTY are processed before with "continue" */
|
||||
/* and BLK and REP have "block_has_updated_hash()" as 1, and all the others */
|
||||
/* have "parity_needs_to_be_updated" already at 1 */
|
||||
/*
|
||||
* For sure it's a CHG block, because EMPTY are processed before with "continue"
|
||||
* and BLK and REP have "block_has_updated_hash()" as 1, and all the others
|
||||
* have "parity_needs_to_be_updated" already at 1
|
||||
*/
|
||||
assert(block_state_get(block) == BLOCK_STATE_CHG);
|
||||
|
||||
/*
|
||||
|
|
@ -1003,25 +1039,33 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
}
|
||||
}
|
||||
|
||||
/* copy the hash in the block, but doesn't mark the block as hashed */
|
||||
/* this allow in case of skipped block to do not save the failed computation */
|
||||
/*
|
||||
* Copy the hash in the block, but doesn't mark the block as hashed
|
||||
* this allow in case of skipped block to do not save the failed computation
|
||||
*/
|
||||
memcpy(block->hash, hash, BLOCK_HASH_SIZE);
|
||||
|
||||
/* note that in case of rehash, this is the wrong hash, */
|
||||
/* but it will be overwritten later */
|
||||
/*
|
||||
* Note that in case of rehash, this is the wrong hash,
|
||||
* but it will be overwritten later
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have only silent errors we can try to fix them on-the-fly */
|
||||
/* note the fix is not written to disk, but used only to */
|
||||
/* compute the new parity */
|
||||
/*
|
||||
* If we have only silent errors we can try to fix them on-the-fly
|
||||
* note the fix is not written to disk, but used only to
|
||||
* compute the new parity
|
||||
*/
|
||||
if (!error_on_this_block && !io_error_on_this_block && silent_error_on_this_block) {
|
||||
unsigned failed_mac;
|
||||
int something_to_recover = 0;
|
||||
|
||||
/* sort the failed vector */
|
||||
/* because with threads it may be in any order */
|
||||
/* but RAID requires the indexes to be sorted */
|
||||
/*
|
||||
* Sort the failed vector
|
||||
* because with threads it may be in any order
|
||||
* but RAID requires the indexes to be sorted
|
||||
*/
|
||||
qsort(failed, failed_count, sizeof(failed[0]), failed_compare_by_index);
|
||||
|
||||
/* setup the blocks to recover */
|
||||
|
|
@ -1035,15 +1079,19 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
if (block_state == BLOCK_STATE_BLK)
|
||||
something_to_recover = 1;
|
||||
|
||||
/* save a copy of the content just read */
|
||||
/* that it's going to be overwritten by the recovering function */
|
||||
/*
|
||||
* Save a copy of the content just read
|
||||
* that it's going to be overwritten by the recovering function
|
||||
*/
|
||||
memcpy(block_copy, block_buffer, state->block_size);
|
||||
|
||||
if (block_state == BLOCK_STATE_CHG
|
||||
&& hash_is_zero(failed[j].block->hash)
|
||||
) {
|
||||
/* if the block was filled with 0, restore this state */
|
||||
/* and avoid to recover it */
|
||||
/*
|
||||
* If the block was filled with 0, restore this state
|
||||
* and avoid to recover it
|
||||
*/
|
||||
memset(block_buffer, 0, state->block_size);
|
||||
} else {
|
||||
/* if we have too many failures, we cannot recover */
|
||||
|
|
@ -1060,9 +1108,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
/* until now is misc */
|
||||
state_usage_misc(state);
|
||||
|
||||
/* read the parity */
|
||||
/* we are sure that parity exists because */
|
||||
/* we have at least one BLK block */
|
||||
/*
|
||||
* Read the parity
|
||||
* we are sure that parity exists because
|
||||
* we have at least one BLK block
|
||||
*/
|
||||
for (l = 0; l < state->level; ++l) {
|
||||
ret = parity_read(&parity_handle[l], blockcur, buffer[diskmax + l], state->block_size);
|
||||
if (ret == -1) {
|
||||
|
|
@ -1095,10 +1145,12 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
|
||||
/* if no error in parity read */
|
||||
if (!io_error_on_this_block) {
|
||||
/* try to fix the data */
|
||||
/* note that this is a simple fix algorithm, that doesn't take into */
|
||||
/* account the case of a wrong parity */
|
||||
/* only 'fix' supports the most advanced fixing */
|
||||
/*
|
||||
* Try to fix the data
|
||||
* note that this is a simple fix algorithm, that doesn't take into
|
||||
* account the case of a wrong parity
|
||||
* only 'fix' supports the most advanced fixing
|
||||
*/
|
||||
raid_rec(failed_mac, failed_map, diskmax, state->level, state->block_size, buffer);
|
||||
|
||||
/* until now is raid */
|
||||
|
|
@ -1134,9 +1186,11 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
if (size < state->block_size)
|
||||
memset(block_buffer + size, 0, state->block_size - size);
|
||||
} else {
|
||||
/* otherwise restore the content */
|
||||
/* because we are not interested in the old state */
|
||||
/* that it's recovered for CHG, REP and DELETED blocks */
|
||||
/*
|
||||
* Otherwise restore the content
|
||||
* because we are not interested in the old state
|
||||
* that it's recovered for CHG, REP and DELETED blocks
|
||||
*/
|
||||
memcpy(block_buffer, block_copy, state->block_size);
|
||||
}
|
||||
}
|
||||
|
|
@ -1189,11 +1243,13 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
block_state_set(block, BLOCK_STATE_BLK);
|
||||
}
|
||||
|
||||
/* we update the info block only if we really have updated the parity */
|
||||
/* because otherwise the time/justsynced info would be misleading as we didn't */
|
||||
/* wrote the parity at this time */
|
||||
/* we also update the info block only if no silent error was found */
|
||||
/* because has no sense to refresh the time for data that we know bad */
|
||||
/*
|
||||
* We update the info block only if we really have updated the parity
|
||||
* because otherwise the time/justsynced info would be misleading as we didn't
|
||||
* wrote the parity at this time
|
||||
* we also update the info block only if no silent error was found
|
||||
* because has no sense to refresh the time for data that we know bad
|
||||
*/
|
||||
if (parity_needs_to_be_updated
|
||||
&& !silent_error_on_this_block
|
||||
) {
|
||||
|
|
@ -1206,24 +1262,30 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
}
|
||||
}
|
||||
|
||||
/* update the time info of the block */
|
||||
/* we are also clearing any previous bad and rehash flag */
|
||||
/*
|
||||
* Update the time info of the block
|
||||
* we are also clearing any previous bad and rehash flag
|
||||
*/
|
||||
info_set(&state->infoarr, blockcur, info_make(now, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* if a silent (even if corrected) or input/output error was found */
|
||||
/* mark the block as bad to have check/fix to handle it */
|
||||
/* because our correction is in memory only and not yet written */
|
||||
/*
|
||||
* If a silent (even if corrected) or input/output error was found
|
||||
* mark the block as bad to have check/fix to handle it
|
||||
* because our correction is in memory only and not yet written
|
||||
*/
|
||||
if (silent_error_on_this_block || io_error_on_this_block) {
|
||||
/* set the error status keeping the other info */
|
||||
info_set(&state->infoarr, blockcur, info_set_bad(info));
|
||||
}
|
||||
|
||||
/* finally schedule parity write */
|
||||
/* Note that the calls to io_parity_write() are mandatory */
|
||||
/* even if the parity doesn't need to be updated */
|
||||
/* This because we want to keep track of the time usage */
|
||||
/*
|
||||
* Finally schedule parity write
|
||||
* Note that the calls to io_parity_write() are mandatory
|
||||
* even if the parity doesn't need to be updated
|
||||
* This because we want to keep track of the time usage
|
||||
*/
|
||||
state_usage_misc(state);
|
||||
|
||||
/* write start */
|
||||
|
|
@ -1327,8 +1389,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
|
||||
msg_progress("Autosaving...\n");
|
||||
|
||||
/* before writing the new content file we ensure that */
|
||||
/* the parity is really written flushing the disk cache */
|
||||
/*
|
||||
* Before writing the new content file we ensure that
|
||||
* the parity is really written flushing the disk cache
|
||||
*/
|
||||
ret = state_flush(state, &io, parity_handle, blockcur);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1351,8 +1415,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
|
|||
end:
|
||||
state_progress_end(state, countpos, countmax, countsize, "Nothing to sync.\n");
|
||||
|
||||
/* before returning we ensure that */
|
||||
/* the parity is really written flushing the disk cache */
|
||||
/*
|
||||
* Before returning we ensure that
|
||||
* the parity is really written flushing the disk cache
|
||||
*/
|
||||
ret = state_flush(state, &io, parity_handle, blockcur);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1558,9 +1624,11 @@ int state_sync(struct snapraid_state* state, block_off_t blockstart, block_off_t
|
|||
for (l = 0; l < state->level; ++l) {
|
||||
int is_modified;
|
||||
|
||||
/* change the size of the parity file, truncating or extending it */
|
||||
/* from this point all the DELETED blocks after the end of the parity are invalid */
|
||||
/* and they are automatically removed when we save the new content file */
|
||||
/*
|
||||
* Change the size of the parity file, truncating or extending it
|
||||
* from this point all the DELETED blocks after the end of the parity are invalid
|
||||
* and they are automatically removed when we save the new content file
|
||||
*/
|
||||
ret = parity_chsize(&parity_handle[l], &state->parity[l], &is_modified, size, state->block_size, state->opt.skip_fallocate, state->opt.skip_space_holder);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ void state_touch(struct snapraid_state* state)
|
|||
for (j = disk->filelist; j != 0; j = j->next) {
|
||||
struct snapraid_file* file = j->data;
|
||||
|
||||
/* if the file has a zero nanosecond timestamp */
|
||||
/* note that symbolic links are not in the file list */
|
||||
/* and then are not processed */
|
||||
/*
|
||||
* If the file has a zero nanosecond timestamp
|
||||
* note that symbolic links are not in the file list
|
||||
* and then are not processed
|
||||
*/
|
||||
if (file->mtime_nsec == 0) {
|
||||
char path[PATH_MAX];
|
||||
struct stat st;
|
||||
|
|
@ -52,8 +54,10 @@ void state_touch(struct snapraid_state* state)
|
|||
nsec = nano % 1000000000;
|
||||
} while (nsec == 0);
|
||||
|
||||
/* O_BINARY: open as binary file (Windows only) */
|
||||
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
|
||||
/*
|
||||
* O_BINARY: open as binary file (Windows only)
|
||||
* O_NOFOLLOW: do not follow links to ensure to open the real file
|
||||
*/
|
||||
flags = O_BINARY | O_NOFOLLOW;
|
||||
#ifdef _WIN32
|
||||
/* in Windows we must have write access at the file */
|
||||
|
|
@ -90,8 +94,10 @@ void state_touch(struct snapraid_state* state)
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* get the present timestamp, that may be different than the one */
|
||||
/* in the content file */
|
||||
/*
|
||||
* Get the present timestamp, that may be different than the one
|
||||
* in the content file
|
||||
*/
|
||||
ret = fstat(f, &st);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -111,9 +117,11 @@ void state_touch(struct snapraid_state* state)
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* uses fstat again to get the present timestamp */
|
||||
/* this is needed because the value read */
|
||||
/* may be different than the written one */
|
||||
/*
|
||||
* Uses fstat again to get the present timestamp
|
||||
* this is needed because the value read
|
||||
* may be different than the written one
|
||||
*/
|
||||
ret = fstat(f, &st);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -132,10 +140,12 @@ void state_touch(struct snapraid_state* state)
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* set the same nanosecond value in the content file */
|
||||
/* note that if the seconds value is already matching */
|
||||
/* the file won't be synced because the content file will */
|
||||
/* contain the new updated timestamp */
|
||||
/*
|
||||
* Set the same nanosecond value in the content file
|
||||
* note that if the seconds value is already matching
|
||||
* the file won't be synced because the content file will
|
||||
* contain the new updated timestamp
|
||||
*/
|
||||
file->mtime_nsec = STAT_NSEC(&st);
|
||||
|
||||
/* state changed, we need to update it */
|
||||
|
|
|
|||
|
|
@ -964,8 +964,10 @@ int devuuid(uint64_t device_id, const char* device_path, char* uuid, size_t uuid
|
|||
int filephy(const char* path, uint64_t size, uint64_t* physical)
|
||||
{
|
||||
#if HAVE_LINUX_FIEMAP_H
|
||||
/* In Linux get the real physical address of the file */
|
||||
/* Note that FIEMAP doesn't require root permission */
|
||||
/*
|
||||
* In Linux get the real physical address of the file
|
||||
* Note that FIEMAP doesn't require root permission
|
||||
*/
|
||||
int f;
|
||||
struct fiemap* fiemap;
|
||||
size_t fiemap_size;
|
||||
|
|
@ -976,8 +978,10 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* first try with FIEMAP */
|
||||
/* if works for ext2, ext3, ext4, xfs, btrfs */
|
||||
/*
|
||||
* First try with FIEMAP
|
||||
* if works for ext2, ext3, ext4, xfs, btrfs
|
||||
*/
|
||||
fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
|
||||
fiemap = malloc_nofail(fiemap_size);
|
||||
memset(fiemap, 0, fiemap_size);
|
||||
|
|
@ -998,8 +1002,10 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
/* if the offset is unknown, we don't have an offset to report */
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
} else if (offset == 0) {
|
||||
/* 0 is the general fallback for file-systems when */
|
||||
/* they don't have an offset to report */
|
||||
/*
|
||||
* 0 is the general fallback for file-systems when
|
||||
* they don't have an offset to report
|
||||
*/
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
} else {
|
||||
/* finally report the real offset */
|
||||
|
|
@ -1023,9 +1029,11 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* then try with FIBMAP */
|
||||
/* it works for jfs, reiserfs, ntfs-3g */
|
||||
/* in exfat it always returns 0, that it's anyway better than the fake inodes */
|
||||
/*
|
||||
* Then try with FIBMAP
|
||||
* it works for jfs, reiserfs, ntfs-3g
|
||||
* in exfat it always returns 0, that it's anyway better than the fake inodes
|
||||
*/
|
||||
blknum = 0; /* first block */
|
||||
if (ioctl(f, FIBMAP, &blknum) != -1) {
|
||||
*physical = blknum + FILEPHY_REAL_OFFSET;
|
||||
|
|
@ -1034,22 +1042,26 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* otherwise don't use anything, and keep the directory traversal order */
|
||||
/* at now this should happen only for vfat */
|
||||
/* and it's surely better than using fake inodes */
|
||||
/*
|
||||
* Otherwise don't use anything, and keep the directory traversal order
|
||||
* at now this should happen only for vfat
|
||||
* and it's surely better than using fake inodes
|
||||
*/
|
||||
*physical = FILEPHY_UNREPORTED_OFFSET;
|
||||
if (close(f) == -1)
|
||||
return -1;
|
||||
#else
|
||||
/* In a generic Unix use a dummy value for all the files */
|
||||
/* We don't want to risk to use the inode without knowing */
|
||||
/* if it really improves performance. */
|
||||
/* In this way we keep them in the directory traversal order */
|
||||
/* that at least keeps files in the same directory together. */
|
||||
/* Note also that in newer file-system with snapshot, like ZFS, */
|
||||
/* the inode doesn't represent even more the disk position, because files */
|
||||
/* are not overwritten in place, but rewritten in another location */
|
||||
/* of the disk. */
|
||||
/*
|
||||
* In a generic Unix use a dummy value for all the files
|
||||
* We don't want to risk to use the inode without knowing
|
||||
* if it really improves performance.
|
||||
* In this way we keep them in the directory traversal order
|
||||
* that at least keeps files in the same directory together.
|
||||
* Note also that in newer file-system with snapshot, like ZFS,
|
||||
* the inode doesn't represent even more the disk position, because files
|
||||
* are not overwritten in place, but rewritten in another location
|
||||
* of the disk.
|
||||
*/
|
||||
*physical = FILEPHY_UNREPORTED_OFFSET;
|
||||
|
||||
(void)path; /* not used here */
|
||||
|
|
@ -1267,8 +1279,10 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* if it doesn't exist, we assume a file */
|
||||
/* and we check for the containing dir */
|
||||
/*
|
||||
* If it doesn't exist, we assume a file
|
||||
* and we check for the containing dir
|
||||
*/
|
||||
if (strlen(path) + 1 > sizeof(dir)) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
|
|
@ -1345,8 +1359,10 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har
|
|||
/* get the filesystem type directly from the struct (Mac OS X) */
|
||||
ptype = st.f_fstypename;
|
||||
#elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
|
||||
/* get the filesystem type from f_type (Linux) */
|
||||
/* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go */
|
||||
/*
|
||||
* Get the filesystem type from f_type (Linux)
|
||||
* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go
|
||||
*/
|
||||
for (int i = 0; FILESYSTEMS[i].id != 0; ++i) {
|
||||
if (st.f_type == FILESYSTEMS[i].id) {
|
||||
ptype = FILESYSTEMS[i].name;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue