diff --git a/cmdline/elem.c b/cmdline/elem.c index b5cb3be5..428aadea 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -472,7 +472,6 @@ int filter_snapshot(int enable, const char* sub, const char* name) struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode) { struct snapraid_file* file; - block_off_t i; block_off_t blockmax; blockmax = size / block_size; @@ -498,12 +497,6 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_ file->flag = 0; file->blockvec = nalloc_nofail((size_t)file->blockmax, block_sizeof()); - for (i = 0; i < file->blockmax; ++i) { - struct snapraid_block* block = file_block(file, i); - block_state_set(block, BLOCK_STATE_CHG); - hash_invalid_set(block->hash); - } - return file; } diff --git a/cmdline/elem.h b/cmdline/elem.h index 6e71169e..3ee1c0b4 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -883,7 +883,7 @@ static inline void file_flag_clear(struct snapraid_file* file, unsigned mask) } /** - * Allocate a file. + * Allocate a file without initializing its blocks. */ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode); diff --git a/cmdline/scan.c b/cmdline/scan.c index df3cb0d8..be036ab5 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -691,6 +691,7 @@ static void scan_file_apply(void* void_scan, void* void_disc) struct snapraid_disk* disk = scan->disk; int is_diff = scan->is_diff; struct snapraid_file* file; + block_off_t block_pos; tommy_node* i; int is_original_file_size_different_than_zero; int is_file_already_present; @@ -997,6 +998,18 @@ static void scan_file_apply(void* void_scan, void* void_disc) /* insert it */ file = file_alloc(state->block_size, sub, disc->size, disc->mtime_sec, disc->mtime_nsec, disc->inode); + /* + * Copy detection recognizes reused hashes from REP blocks before parity + * allocation. Initialize new scan blocks to CHG so uninitialized state can + * never be mistaken for a reusable hash. + */ + for (block_pos = 0; block_pos < file->blockmax; ++block_pos) { + struct snapraid_block* block = fs_file2block_get(file, block_pos); + + block_state_set(block, BLOCK_STATE_CHG); + hash_invalid_set(block->hash); + } + /* mark it as present and physically discovered during Phase 1 */ file_flag_set(file, FILE_IS_PRESENT); file_flag_set(file, FILE_IS_DISCOVERED); diff --git a/cmdline/state.c b/cmdline/state.c index 3a4d0066..0a9a6ca4 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -2422,7 +2422,10 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } - /* allocate the file */ + /* + * Allocate without initializing blocks. The runs below cover the whole + * file and set every state and hash before exposing the loaded state. + */ file = file_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec, v_inode); /* insert the file in the file containers */ @@ -2750,7 +2753,10 @@ static void state_read_content(struct snapraid_state* state, const char* path, S /* LCOV_EXCL_STOP */ } - /* allocate a fake deleted file */ + /* + * Allocate without initializing blocks. This deleted run sets every + * state and hash before the file is inserted in the extent map. + */ deleted = file_alloc(state->block_size, "", v_count * (data_off_t)state->block_size, 0, 0, 0); /* mark the file as deleted */