int state_status(struct snapraid_state* state) { block_off_t blockmax; block_off_t i; snapraid_info* infomap; time_t now; block_off_t bad; block_off_t rehash; block_off_t count; unsigned dayoldest, daymedian, daynewest; unsigned bar[GRAPH_COLUMN]; unsigned barpos; unsigned barmax; time_t oldest, newest, median; unsigned x, y; tommy_node* node_disk; unsigned file_count; unsigned file_fragmented; unsigned extra_fragment; uint64_t file_size; unsigned unsynced_blocks; /* get the present time */ now = time(0); blockmax = parity_size(state); /* count fragments */ file_count = 0; file_size = 0; file_fragmented = 0; extra_fragment = 0; for(node_disk=state->disklist;node_disk!=0;node_disk=node_disk->next) { struct snapraid_disk* disk = node_disk->data; tommy_node* node; unsigned disk_file_count = 0; unsigned disk_file_fragmented = 0; unsigned disk_extra_fragment = 0; uint64_t disk_file_size = 0; /* for each file in the disk */ node = disk->filelist; while (node) { struct snapraid_file* file; file = node->data; node = node->next; /* next node */ /* check fragmentation */ if (file->size) { block_off_t prev_pos; int fragmented; fragmented = 0; prev_pos = file->blockvec[0].parity_pos; for(i=1;i<file->blockmax;++i) { block_off_t parity_pos = file->blockvec[i].parity_pos; if (prev_pos + 1 != parity_pos) { fragmented = 1; ++extra_fragment; ++disk_extra_fragment; } prev_pos = parity_pos; } if (fragmented) { ++file_fragmented; ++disk_file_fragmented; } } /* count files */ ++file_count; ++disk_file_count; file_size += file->size; disk_file_size += file->size; } fprintf(stdlog, "summary:disk_file_count:%s:%u\n", disk->name, disk_file_count); fprintf(stdlog, "summary:disk_fragmented_file_count:%s:%u\n", disk->name, disk_file_fragmented); fprintf(stdlog, "summary:disk_excess_fragment_count:%s:%u\n", disk->name, disk_extra_fragment); fprintf(stdlog, "summary:disk_file_size:%s:%"PRIu64"\n", disk->name, disk_file_size); } printf("\n"); printf("Files: %u\n", file_count); printf("Fragmented files: %u\n", file_fragmented); printf("Excess fragments: %u\n", extra_fragment); printf("Files size: %"PRIu64" GiB\n", file_size / (1024*1024*1024) ); printf("Parity size: %"PRIu64" GiB\n", blockmax * (uint64_t)state->block_size / (1024*1024*1024) ); printf("\n"); fprintf(stdlog, "summary:file_count:%u\n", file_count); fprintf(stdlog, "summary:fragmented_file_count:%u\n", file_fragmented); fprintf(stdlog, "summary:excess_fragment_count:%u\n", extra_fragment); fprintf(stdlog, "summary:file_size:%"PRIu64"\n", file_size); fprintf(stdlog, "summary:parity_size:%"PRIu64"\n", blockmax * (uint64_t)state->block_size); fprintf(stdlog, "summary:hash:%s\n", hash_config_name(state->hash)); fprintf(stdlog, "summary:prev_hash:%s\n", hash_config_name(state->prevhash)); fprintf(stdlog, "summary:best_hash:%s\n", hash_config_name(state->besthash)); fprintf(stdlog, "summary:block_count:%u\n", blockmax); fflush(stdlog); /* copy the info a temp vector, and count bad/rehash/unsynced blocks */ infomap = malloc_nofail(blockmax * sizeof(snapraid_info)); bad = 0; count = 0; rehash = 0; unsynced_blocks = 0; for(i=0;i<blockmax;++i) { int one_invalid; int one_valid; snapraid_info info = info_get(&state->infoarr, i); /* for each disk */ one_invalid = 0; one_valid = 0; for(node_disk=state->disklist;node_disk!=0;node_disk=node_disk->next) { struct snapraid_disk* disk = node_disk->data; struct snapraid_block* block = disk_block_get(disk, i); if (block_has_file(block)) one_valid = 1; if (block_has_invalid_parity(block)) one_invalid = 1; } /* if both valid and invalid, we need to update */ if (one_invalid && one_valid) { ++unsynced_blocks; } /* skip unused blocks */ if (info != 0) { if (info_get_bad(info)) ++bad; if (info_get_rehash(info)) ++rehash; infomap[count++] = info; } if (state->opt.gui) { if (info != 0) fprintf(stdlog, "block:%u:%"PRIu64":%s:%s:%s:%s\n", i, (uint64_t)info_get_time(info), one_valid ? "used": "", one_invalid ? "unsynced" : "", info_get_bad(info) ? "bad" : "", info_get_rehash(info) ? "rehash" : ""); else fprintf(stdlog, "block_noinfo:%u:%s:%s\n", i, one_valid ? "used": "", one_invalid ? "unsynced" : ""); } } fprintf(stdlog, "summary:has_unsynced:%u\n", unsynced_blocks); fprintf(stdlog, "summary:has_rehash:%u\n", rehash); fprintf(stdlog, "summary:has_bad:%u\n", bad); fprintf(stdlog, "summary:time_count:%u\n", count); fflush(stdlog); if (!count) { fprintf(stderr, "The array is empty.\n"); free(infomap); return 0; } /* sort the info to get the time info */ qsort(infomap, count, sizeof(snapraid_info), info_time_compare); /* info for making the graph */ i = 0; while (i < count) { unsigned j = i + 1; while (j < count && info_get_time(infomap[i]) == info_get_time(infomap[j])) ++j; fprintf(stdlog, "time:%"PRIu64":%u\n", (uint64_t)info_get_time(infomap[i]), j - i); i = j; } fflush(stdlog); oldest = info_get_time(infomap[0]); median = info_get_time(infomap[count / 2]); newest = info_get_time(infomap[count - 1]); dayoldest = day_ago(oldest, now); daymedian = day_ago(median, now); daynewest = day_ago(newest, now); /* compute graph limits */ barpos = 0; barmax = 0; for(i=0;i<GRAPH_COLUMN;++i) { time_t limit; unsigned step; limit = oldest + (newest - oldest) * (i+1) / GRAPH_COLUMN; step = 0; while (barpos < count && info_get_time(infomap[barpos]) <= limit) { ++barpos; ++step; } if (step > barmax) barmax = step; bar[i] = step; } /* print the graph */ for(y=0;y<GRAPH_ROW;++y) { if (y == 0) printf("%3u%% ", barmax * 100 / count); else if (y == GRAPH_ROW - 1) printf(" 0%% "); else if (y == GRAPH_ROW/2) printf("%3u%% ", barmax * 50 / count); else printf(" "); for(x=0;x<GRAPH_COLUMN;++x) { unsigned pivot = barmax * (GRAPH_ROW-y) / GRAPH_ROW; /* if it's the baseline */ if (y == GRAPH_ROW-1) { if (bar[x] >= pivot) { printf("*"); } else if (bar[x] > 0) { printf("+"); } else { printf("_"); } } else { unsigned halfpivot = pivot - barmax / GRAPH_ROW / 2; if (bar[x] >= pivot) { printf("*"); } else if (bar[x] >= halfpivot) { printf("+"); } else { printf(" "); } } } printf("\n"); } printf(" %3u days ago of the last scrub %3u\n", dayoldest, daynewest); printf("\n"); printf("The oldest block was scrubbed %u days ago, the median %u, the newest %u.\n", dayoldest, daymedian, daynewest); printf("\n"); if (unsynced_blocks) { printf("WARNING! The array is NOT fully synced.\n"); printf("You have a sync in progress at %u%%.\n", (blockmax - unsynced_blocks) * 100 / blockmax); } else { printf("No sync is in progress.\n"); } if (rehash) { printf("You have a rehash in progress at %u%%.\n", (count - rehash) * 100 / count); } else { if (state->besthash != state->hash) { printf("No rehash is in progress, but for optimal performance one is recommended.\n"); } else { printf("No rehash is in progress or needed.\n"); } } if (bad) { printf("DANGER! In the array there are %u silent errors!\n\n", bad); printf("They are at blocks:"); /* print all the errors */ for(i=0;i<blockmax;++i) { snapraid_info info = info_get(&state->infoarr, i); /* skip unused blocks */ if (info == 0) continue; if (info_get_bad(info)) printf(" %u", i); } printf("\n\n"); printf("To fix them use the command 'snapraid -e fix'.\n"); printf("The errors will disapper from the 'status' at the next 'scrub' command.\n"); } else { printf("No silent error detected.\n"); } /* free the temp vector */ free(infomap); return 0; }
static int state_dry_process(struct snapraid_state* state, struct snapraid_parity** parity, block_off_t blockstart, block_off_t blockmax) { struct snapraid_handle* handle; unsigned diskmax; block_off_t i; unsigned j; void* buffer_alloc; unsigned char* buffer_aligned; int ret; data_off_t countsize; block_off_t countpos; block_off_t countmax; unsigned error; unsigned l; handle = handle_map(state, &diskmax); buffer_aligned = malloc_nofail_align(state->block_size, &buffer_alloc); error = 0; /* dry all the blocks in files */ countmax = blockmax - blockstart; countsize = 0; countpos = 0; state_progress_begin(state, blockstart, blockmax, countmax); for(i=blockstart;i<blockmax;++i) { /* for each disk, process the block */ for(j=0;j<diskmax;++j) { int read_size; struct snapraid_block* block = BLOCK_EMPTY; if (handle[j].disk) block = disk_block_get(handle[j].disk, i); if (!block_has_file(block)) { /* if no file, nothing to do */ continue; } /* if the file is closed or different than the current one */ if (handle[j].file == 0 || handle[j].file != block_file_get(block)) { /* close the old one, if any */ ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "DANGER! Unexpected close error in a data disk, it isn't possible to dry.\n"); printf("Stopping at block %u\n", i); ++error; goto bail; /* LCOV_EXCL_STOP */ } /* open the file only for reading */ ret = handle_open(&handle[j], block_file_get(block), state->opt.skip_sequential, stdlog); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "DANGER! Unexpected open error in a data disk, it isn't possible to dry.\n"); printf("Stopping at block %u\n", i); ++error; goto bail; /* LCOV_EXCL_STOP */ } } /* read from the file */ read_size = handle_read(&handle[j], block, buffer_aligned, state->block_size, stdlog); if (read_size == -1) { fprintf(stdlog, "error:%u:%s:%s: Read error at position %u\n", i, handle[j].disk->name, block_file_get(block)->sub, block_file_pos(block)); ++error; continue; } countsize += read_size; } /* read the parity */ for(l=0;l<state->level;++l) { if (parity[l]) { ret = parity_read(parity[l], i, buffer_aligned, state->block_size, stdlog); if (ret == -1) { fprintf(stdlog, "parity_error:%u:%s: Read error\n", i, lev_config_name(l)); ++error; } } } /* count the number of processed block */ ++countpos; /* progress */ if (state_progress(state, i, countpos, countmax, countsize)) { /* LCOV_EXCL_START */ break; /* LCOV_EXCL_STOP */ } } state_progress_end(state, countpos, countmax, countsize); bail: /* close all the files left open */ for(j=0;j<diskmax;++j) { ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ /* LCOV_EXCL_STOP */ } } if (error) { printf("%u read errors\n", error); } else { printf("Everything OK\n"); } free(handle); free(buffer_alloc); if (error != 0) return -1; return 0; }
static int state_scrub_process(struct snapraid_state* state, struct snapraid_parity** parity, block_off_t blockstart, block_off_t blockmax, time_t timelimit, block_off_t countlimit, time_t now) { struct snapraid_handle* handle; void* rehandle_alloc; struct snapraid_rehash* rehandle; unsigned diskmax; block_off_t i; unsigned j; void* buffer_alloc; void** buffer; unsigned buffermax; data_off_t countsize; block_off_t countpos; block_off_t countmax; block_off_t recountmax; block_off_t autosavedone; block_off_t autosavelimit; block_off_t autosavemissing; int ret; unsigned error; unsigned silent_error; unsigned l; /* maps the disks to handles */ handle = handle_map(state, &diskmax); /* rehash buffers */ rehandle = malloc_nofail_align(diskmax * sizeof(struct snapraid_rehash), &rehandle_alloc); /* we need disk + 2 for each parity level buffers */ buffermax = diskmax + state->level * 2; buffer = malloc_nofail_vector_align(diskmax, buffermax, state->block_size, &buffer_alloc); if (!state->opt.skip_self) mtest_vector(buffermax, state->block_size, buffer); error = 0; silent_error = 0; /* first count the number of blocks to process */ countmax = 0; for(i=blockstart;i<blockmax;++i) { time_t blocktime; snapraid_info info; /* if it's unused */ info = info_get(&state->infoarr, i); if (info == 0) { /* skip it */ continue; } /* blocks marked as bad are always checked */ if (!info_get_bad(info)) { /* if it's too new */ blocktime = info_get_time(info); if (blocktime > timelimit) { /* skip it */ continue; } /* skip odd blocks, used only for testing */ if (state->opt.force_scrub_even && (i % 2) != 0) { /* skip it */ continue; } /* if the time is less than the limit, always include */ /* otherwise, check if we reached the max count */ if (blocktime == timelimit) { /* if we reached the count limit */ if (countmax >= countlimit) { /* skip it */ continue; } } } ++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 */ autosavelimit = state->autosave / (diskmax * state->block_size); autosavemissing = countmax; /* blocks to do */ autosavedone = 0; /* blocks done */ countsize = 0; countpos = 0; state_progress_begin(state, blockstart, blockmax, countmax); recountmax = 0; for(i=blockstart;i<blockmax;++i) { time_t blocktime; snapraid_info info; int error_on_this_block; int silent_error_on_this_block; int block_is_unsynced; int rehash; /* if it's unused */ info = info_get(&state->infoarr, i); if (info == 0) { /* skip it */ continue; } /* blocks marked as bad are always checked */ if (!info_get_bad(info)) { /* if it's too new */ blocktime = info_get_time(info); if (blocktime > timelimit) { /* skip it */ continue; } /* skip odd blocks, used only for testing */ if (state->opt.force_scrub_even && (i % 2) != 0) { /* skip it */ continue; } /* if the time is less than the limit, always include */ /* otherwise, check if we reaced the count max */ if (blocktime == timelimit) { /* if we reached the count limit */ if (recountmax >= countlimit) { /* skip it */ continue; } } } ++recountmax; /* one more block processed for autosave */ ++autosavedone; --autosavemissing; /* by default process the block, and skip it if something goes wrong */ error_on_this_block = 0; silent_error_on_this_block = 0; /* if all the blocks at this address are synced */ /* if not, parity is not even checked */ block_is_unsynced = 0; /* if we have to use the old hash */ rehash = info_get_rehash(info); /* for each disk, process the block */ for(j=0;j<diskmax;++j) { int read_size; unsigned char hash[HASH_SIZE]; struct snapraid_block* block; int file_is_unsynced; /* if the file on this disk is synced */ /* if not, silent errors are assumed as expected error */ file_is_unsynced = 0; /* by default no rehash in case of "continue" */ rehandle[j].block = 0; /* if the disk position is not used */ if (!handle[j].disk) { /* use an empty block */ memset(buffer[j], 0, state->block_size); continue; } /* if the block is not used */ block = disk_block_get(handle[j].disk, i); if (!block_has_file(block)) { /* use an empty block */ memset(buffer[j], 0, state->block_size); continue; } /* if the block is unsynced, errors are expected */ if (block_has_invalid_parity(block)) { /* report that the block and the file are not synced */ block_is_unsynced = 1; file_is_unsynced = 1; /* follow */ } /* if the file is different than the current one, close it */ if (handle[j].file != 0 && handle[j].file != block_file_get(block)) { /* keep a pointer at the file we are going to close for error reporting */ struct snapraid_file* file = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ /* This one is really an unexpected error, because we are only reading */ /* and closing a descriptor should never fail */ fprintf(stdlog, "error:%u:%s:%s: Close error. %s\n", i, handle[j].disk->name, file->sub, strerror(errno)); fprintf(stderr, "DANGER! Unexpected close error in a data disk, it isn't possible to scrub.\n"); printf("Stopping at block %u\n", i); ++error; goto bail; /* LCOV_EXCL_STOP */ } } ret = handle_open(&handle[j], block_file_get(block), state->opt.skip_sequential, stderr); if (ret == -1) { /* file we have tried to open for error reporting */ struct snapraid_file* file = block_file_get(block); fprintf(stdlog, "error:%u:%s:%s: Open error. %s\n", i, handle[j].disk->name, file->sub, strerror(errno)); ++error; error_on_this_block = 1; continue; } /* check if the file is changed */ if (handle[j].st.st_size != block_file_get(block)->size || handle[j].st.st_mtime != block_file_get(block)->mtime_sec || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec || handle[j].st.st_ino != block_file_get(block)->inode ) { /* report that the block and the file are not synced */ block_is_unsynced = 1; file_is_unsynced = 1; /* 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. */ read_size = handle_read(&handle[j], block, buffer[j], state->block_size, stderr); if (read_size == -1) { fprintf(stdlog, "error:%u:%s:%s: Read error at position %u\n", i, handle[j].disk->name, handle[j].file->sub, block_file_pos(block)); ++error; error_on_this_block = 1; continue; } countsize += read_size; /* now compute the hash */ if (rehash) { memhash(state->prevhash, state->prevhashseed, hash, buffer[j], read_size); /* compute the new hash, and store it */ rehandle[j].block = block; memhash(state->hash, state->hashseed, rehandle[j].hash, buffer[j], read_size); } else { memhash(state->hash, state->hashseed, hash, buffer[j], read_size); } if (block_has_updated_hash(block)) { /* compare the hash */ if (memcmp(hash, block->hash, HASH_SIZE) != 0) { fprintf(stdlog, "error:%u:%s:%s: Data error at position %u\n", i, handle[j].disk->name, handle[j].file->sub, block_file_pos(block)); /* it's a silent error only if we are dealing with synced files */ if (file_is_unsynced) { ++error; error_on_this_block = 1; } else { fprintf(stderr, "Data error in file '%s' at position '%u'\n", handle[j].path, block_file_pos(block)); fprintf(stderr, "WARNING! Unexpected data error in a data disk! The block is now marked as bad!\n"); fprintf(stderr, "Try with 'snapraid -e fix' to recover!\n"); ++silent_error; silent_error_on_this_block = 1; } continue; } } } /* if we have read all the data required and it's correct, proceed with the parity check */ if (!error_on_this_block && !silent_error_on_this_block) { unsigned char* buffer_recov[LEV_MAX]; /* buffers for parity read and not computed */ for(l=0;l<state->level;++l) buffer_recov[l] = buffer[diskmax + state->level + l]; for(;l<LEV_MAX;++l) buffer_recov[l] = 0; /* read the parity */ for(l=0;l<state->level;++l) { ret = parity_read(parity[l], i, buffer_recov[l], state->block_size, stdlog); if (ret == -1) { buffer_recov[l] = 0; fprintf(stdlog, "parity_error:%u:%s: Read error\n", i, lev_config_name(l)); ++error; error_on_this_block = 1; /* follow */ } } /* compute the parity */ raid_gen(diskmax, state->level, state->block_size, buffer); /* compare the parity */ for(l=0;l<state->level;++l) { if (buffer_recov[l] && memcmp(buffer[diskmax + l], buffer_recov[l], state->block_size) != 0) { fprintf(stdlog, "parity_error:%u:%s: Data error\n", i, lev_config_name(l)); /* it's a silent error only if we are dealing with synced blocks */ if (block_is_unsynced) { ++error; error_on_this_block = 1; } else { fprintf(stderr, "Data error in parity '%s' at position '%u'\n", lev_config_name(l), i); fprintf(stderr, "WARNING! Unexpected data error in a parity disk! The block is now marked as bad!\n"); fprintf(stderr, "Try with 'snapraid -e fix' to recover!\n"); ++silent_error; silent_error_on_this_block = 1; } } } } if (silent_error_on_this_block) { /* set the error status keeping the existing time and hash */ info_set(&state->infoarr, i, 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 */ } else { /* if rehash is needed */ if (rehash) { /* store all the new hash already computed */ for(j=0;j<diskmax;++j) { if (rehandle[j].block) memcpy(rehandle[j].block->hash, rehandle[j].hash, HASH_SIZE); } } /* update the time info of the block */ /* and clear any other flag */ info_set(&state->infoarr, i, info_make(now, 0, 0)); } /* mark the state as needing write */ state->need_write = 1; /* count the number of processed block */ ++countpos; /* progress */ if (state_progress(state, i, countpos, countmax, countsize)) { /* LCOV_EXCL_START */ break; /* LCOV_EXCL_STOP */ } /* autosave */ if (state->autosave != 0 && autosavedone >= autosavelimit /* if we have reached the limit */ && autosavemissing >= autosavelimit /* if we have at least a full step to do */ ) { autosavedone = 0; /* restart the counter */ state_progress_stop(state); printf("Autosaving...\n"); state_write(state); state_progress_restart(state); } } state_progress_end(state, countpos, countmax, countsize); if (error || silent_error) { printf("\n"); printf("%8u read errors\n", error); printf("%8u data errors\n", silent_error); printf("WARNING! There are errors!\n"); } else { /* print the result only if processed something */ if (countpos != 0) printf("Everything OK\n"); } fprintf(stdlog, "summary:error_read:%u\n", error); fprintf(stdlog, "summary:error_data:%u\n", silent_error); if (error + silent_error == 0) fprintf(stdlog, "summary:exit:ok\n"); else fprintf(stdlog, "summary:exit:error\n"); fflush(stdlog); bail: for(j=0;j<diskmax;++j) { ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ fprintf(stderr, "DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ /* LCOV_EXCL_STOP */ } } free(handle); free(buffer_alloc); free(buffer); free(rehandle_alloc); if (state->opt.expect_recoverable) { if (error + silent_error == 0) return -1; } else { if (error + silent_error != 0) return -1; } return 0; }
static int state_dry_process(struct snapraid_state* state, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax) { struct snapraid_handle* handle; unsigned diskmax; block_off_t i; unsigned j; void* buffer_alloc; unsigned char* buffer_aligned; int ret; data_off_t countsize; block_off_t countpos; block_off_t countmax; unsigned error; unsigned l; handle = handle_map(state, &diskmax); buffer_aligned = malloc_nofail_align(state->block_size, &buffer_alloc); error = 0; /* drop until now */ state_usage_waste(state); countmax = blockmax - blockstart; countsize = 0; countpos = 0; state_progress_begin(state, blockstart, blockmax, countmax); for (i = blockstart; i < blockmax; ++i) { /* for each disk, process the block */ for (j = 0; j < diskmax; ++j) { int read_size; struct snapraid_block* block = BLOCK_EMPTY; struct snapraid_disk* disk = handle[j].disk; if (!disk) { /* if no disk, nothing to do */ continue; } block = disk_block_get(disk, i); if (!block_has_file(block)) { /* if no file, nothing to do */ continue; } /* until now is CPU */ state_usage_cpu(state); /* if the file is closed or different than the current one */ if (handle[j].file == 0 || handle[j].file != block_file_get(block)) { struct snapraid_file* file = handle[j].file; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ msg_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); msg_error("DANGER! Unexpected close error in a data disk, it isn't possible to dry.\n"); msg_error("Stopping at block %u\n", i); ++error; goto bail; /* LCOV_EXCL_STOP */ } /* open the file only for reading */ ret = handle_open(&handle[j], block_file_get(block), state->file_mode, msg_error); if (ret == -1) { /* LCOV_EXCL_START */ msg_error("DANGER! Unexpected open error in a data disk, it isn't possible to dry.\n"); msg_error("Stopping at block %u\n", i); ++error; goto bail; /* LCOV_EXCL_STOP */ } } /* read from the file */ read_size = handle_read(&handle[j], block, buffer_aligned, state->block_size, msg_warning); if (read_size == -1) { msg_tag("error:%u:%s:%s: Read error at position %u\n", i, disk->name, esc(block_file_get(block)->sub), block_file_pos(block)); ++error; continue; } /* until now is disk */ state_usage_disk(state, disk); countsize += read_size; } /* read the parity */ for (l = 0; l < state->level; ++l) { if (parity[l]) { /* until now is CPU */ state_usage_cpu(state); ret = parity_read(parity[l], i, buffer_aligned, state->block_size, msg_warning); if (ret == -1) { msg_tag("parity_error:%u:%s: Read error\n", i, lev_config_name(l)); ++error; } /* until now is parity */ state_usage_parity(state, l); } } /* count the number of processed block */ ++countpos; /* progress */ if (state_progress(state, i, countpos, countmax, countsize)) { /* LCOV_EXCL_START */ break; /* LCOV_EXCL_STOP */ } } state_progress_end(state, countpos, countmax, countsize); state_usage_print(state); bail: /* close all the files left open */ for (j = 0; j < diskmax; ++j) { struct snapraid_file* file = handle[j].file; struct snapraid_disk* disk = handle[j].disk; ret = handle_close(&handle[j]); if (ret == -1) { /* LCOV_EXCL_START */ msg_tag("error:%u:%s:%s: Close error. %s\n", i, disk->name, esc(file->sub), strerror(errno)); msg_error("DANGER! Unexpected close error in a data disk.\n"); ++error; /* continue, as we are already exiting */ /* LCOV_EXCL_STOP */ } } if (error) { msg_status("\n"); msg_status("%8u errors\n", error); } else { msg_status("Everything OK\n"); } if (error) msg_error("DANGER! Unexpected errors!\n"); free(handle); free(buffer_alloc); if (error != 0) return -1; return 0; }