LCOV - code coverage report
Current view: top level - cmdline - dry.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 122 146 83.6 %
Date: 2025-10-28 11:59:11 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "util.h"
      21             : #include "elem.h"
      22             : #include "state.h"
      23             : #include "parity.h"
      24             : #include "handle.h"
      25             : #include "io.h"
      26             : #include "raid/raid.h"
      27             : 
      28             : /****************************************************************************/
      29             : /* dry */
      30             : 
      31       57102 : static void dry_data_reader(struct snapraid_worker* worker, struct snapraid_task* task)
      32             : {
      33       57102 :         struct snapraid_io* io = worker->io;
      34       57102 :         struct snapraid_state* state = io->state;
      35       57102 :         struct snapraid_handle* handle = worker->handle;
      36       57102 :         struct snapraid_disk* disk = handle->disk;
      37       57102 :         block_off_t blockcur = task->position;
      38       57102 :         unsigned char* buffer = task->buffer;
      39             :         int ret;
      40             :         char esc_buffer[ESC_MAX];
      41             : 
      42             :         /* if the disk position is not used */
      43       57102 :         if (!disk) {
      44             :                 /* use an empty block */
      45        4687 :                 memset(buffer, 0, state->block_size);
      46        4687 :                 task->state = TASK_STATE_DONE;
      47        5528 :                 return;
      48             :         }
      49             : 
      50             :         /* get the block */
      51       52415 :         task->block = fs_par2block_find(disk, blockcur);
      52             : 
      53             :         /* if the block is not used */
      54       52415 :         if (!block_has_file(task->block)) {
      55             :                 /* use an empty block */
      56         841 :                 memset(buffer, 0, state->block_size);
      57         841 :                 task->state = TASK_STATE_DONE;
      58         841 :                 return;
      59             :         }
      60             : 
      61             :         /* get the file of this block */
      62       51574 :         task->file = fs_par2file_get(disk, blockcur, &task->file_pos);
      63             : 
      64             :         /* if the file is different than the current one, close it */
      65       51574 :         if (handle->file != 0 && handle->file != task->file) {
      66             :                 /* keep a pointer at the file we are going to close for error reporting */
      67       20803 :                 struct snapraid_file* report = handle->file;
      68       20803 :                 ret = handle_close(handle);
      69       20803 :                 if (ret == -1) {
      70             :                         /* LCOV_EXCL_START */
      71             :                         /* This one is really an unexpected error, because we are only reading */
      72             :                         /* and closing a descriptor should never fail */
      73             :                         if (errno == EIO) {
      74             :                                 log_tag("error:%u:%s:%s: Close EIO error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
      75             :                                 log_fatal("DANGER! Unexpected input/output close error in a data disk, it isn't possible to dry.\n");
      76             :                                 log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
      77             :                                 log_fatal("Stopping at block %u\n", blockcur);
      78             :                                 task->state = TASK_STATE_IOERROR;
      79             :                                 return;
      80             :                         }
      81             : 
      82             :                         log_tag("error:%u:%s:%s: Close error. %s\n", blockcur, disk->name, esc_tag(report->sub, esc_buffer), strerror(errno));
      83             :                         log_fatal("WARNING! Unexpected close error in a data disk, it isn't possible to dry.\n");
      84             :                         log_fatal("Ensure that file '%s' can be accessed.\n", handle->path);
      85             :                         log_fatal("Stopping at block %u\n", blockcur);
      86             :                         task->state = TASK_STATE_ERROR;
      87             :                         return;
      88             :                         /* LCOV_EXCL_STOP */
      89             :                 }
      90             :         }
      91             : 
      92       51574 :         ret = handle_open(handle, task->file, state->file_mode, log_error, 0);
      93       51574 :         if (ret == -1) {
      94             :                 /* LCOV_EXCL_START */
      95             :                 if (errno == EIO) {
      96             :                         log_tag("error:%u:%s:%s: Open EIO error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
      97             :                         log_fatal("DANGER! Unexpected input/output open error in a data disk, it isn't possible to dry.\n");
      98             :                         log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, handle->path);
      99             :                         log_fatal("Stopping at block %u\n", blockcur);
     100             :                         task->state = TASK_STATE_IOERROR;
     101             :                         return;
     102             :                 }
     103             : 
     104             :                 log_tag("error:%u:%s:%s: Open error. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), strerror(errno));
     105             :                 task->state = TASK_STATE_ERROR_CONTINUE;
     106             :                 return;
     107             :                 /* LCOV_EXCL_STOP */
     108             :         }
     109             : 
     110       51574 :         task->read_size = handle_read(handle, task->file_pos, buffer, state->block_size, log_error, 0);
     111       51574 :         if (task->read_size == -1) {
     112             :                 /* LCOV_EXCL_START */
     113             :                 if (errno == EIO) {
     114             :                         log_tag("error:%u:%s:%s: Read EIO error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
     115             :                         log_error("Input/Output error in file '%s' at position '%u'\n", handle->path, task->file_pos);
     116             :                         task->state = TASK_STATE_IOERROR_CONTINUE;
     117             :                         return;
     118             :                 }
     119             : 
     120             :                 log_tag("error:%u:%s:%s: Read error at position %u. %s\n", blockcur, disk->name, esc_tag(task->file->sub, esc_buffer), task->file_pos, strerror(errno));
     121             :                 task->state = TASK_STATE_ERROR_CONTINUE;
     122             :                 return;
     123             :                 /* LCOV_EXCL_STOP */
     124             :         }
     125             : 
     126             :         /* store the path of the opened file */
     127       51574 :         pathcpy(task->path, sizeof(task->path), handle->path);
     128             : 
     129       51574 :         task->state = TASK_STATE_DONE;
     130             : }
     131             : 
     132       57102 : static void dry_parity_reader(struct snapraid_worker* worker, struct snapraid_task* task)
     133             : {
     134       57102 :         struct snapraid_io* io = worker->io;
     135       57102 :         struct snapraid_state* state = io->state;
     136       57102 :         struct snapraid_parity_handle* parity_handle = worker->parity_handle;
     137       57102 :         unsigned level = parity_handle->level;
     138       57102 :         block_off_t blockcur = task->position;
     139       57102 :         unsigned char* buffer = task->buffer;
     140             :         int ret;
     141             : 
     142             :         /* read the parity */
     143       57102 :         ret = parity_read(parity_handle, blockcur, buffer, state->block_size, log_error);
     144       57102 :         if (ret == -1) {
     145             :                 /* LCOV_EXCL_START */
     146             :                 if (errno == EIO) {
     147             :                         log_tag("parity_error:%u:%s: Read EIO error. %s\n", blockcur, lev_config_name(level), strerror(errno));
     148             :                         log_error("Input/Output error in parity '%s' at position '%u'\n", lev_config_name(level), blockcur);
     149             :                         task->state = TASK_STATE_IOERROR_CONTINUE;
     150             :                         return;
     151             :                 }
     152             : 
     153             :                 log_tag("parity_error:%u:%s: Read error. %s\n", blockcur, lev_config_name(level), strerror(errno));
     154             :                 task->state = TASK_STATE_ERROR_CONTINUE;
     155             :                 return;
     156             :                 /* LCOV_EXCL_STOP */
     157             :         }
     158             : 
     159       57102 :         task->state = TASK_STATE_DONE;
     160             : }
     161             : 
     162           3 : static int state_dry_process(struct snapraid_state* state, struct snapraid_parity_handle* parity_handle, block_off_t blockstart, block_off_t blockmax)
     163             : {
     164             :         struct snapraid_io io;
     165             :         struct snapraid_handle* handle;
     166             :         unsigned diskmax;
     167             :         block_off_t blockcur;
     168             :         unsigned j;
     169             :         unsigned buffermax;
     170             :         int ret;
     171             :         data_off_t countsize;
     172             :         block_off_t countpos;
     173             :         block_off_t countmax;
     174             :         unsigned error;
     175             :         unsigned io_error;
     176             :         unsigned l;
     177             :         unsigned* waiting_map;
     178             :         unsigned waiting_mac;
     179             :         char esc_buffer[ESC_MAX];
     180             : 
     181           3 :         handle = handle_mapping(state, &diskmax);
     182             : 
     183             :         /* we need 1 * data + 2 * parity */
     184           3 :         buffermax = diskmax + 2 * state->level;
     185             : 
     186             :         /* initialize the io threads */
     187           3 :         io_init(&io, state, state->opt.io_cache, buffermax, dry_data_reader, handle, diskmax, dry_parity_reader, 0, parity_handle, state->level);
     188             : 
     189             :         /* possibly waiting disks */
     190           3 :         waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX;
     191           3 :         waiting_map = malloc_nofail(waiting_mac * sizeof(unsigned));
     192             : 
     193           3 :         error = 0;
     194           3 :         io_error = 0;
     195             : 
     196             :         /* drop until now */
     197           3 :         state_usage_waste(state);
     198             : 
     199           3 :         countmax = blockmax - blockstart;
     200           3 :         countsize = 0;
     201           3 :         countpos = 0;
     202             : 
     203             :         /* start all the worker threads */
     204           3 :         io_start(&io, blockstart, blockmax, 0);
     205             : 
     206           3 :         if (!state_progress_begin(state, blockstart, blockmax, countmax))
     207           0 :                 goto end;
     208             : 
     209        9517 :         while (1) {
     210             :                 void** buffer;
     211             : 
     212             :                 /* go to the next block */
     213        9520 :                 blockcur = io_read_next(&io, &buffer);
     214        9520 :                 if (blockcur >= blockmax)
     215           3 :                         break;
     216             : 
     217             :                 /* until now is scheduling */
     218        9517 :                 state_usage_sched(state);
     219             : 
     220             :                 /* for each disk, process the block */
     221       66619 :                 for (j = 0; j < diskmax; ++j) {
     222             :                         struct snapraid_task* task;
     223             :                         int read_size;
     224             :                         struct snapraid_block* block;
     225             :                         struct snapraid_disk* disk;
     226             :                         unsigned diskcur;
     227             : 
     228             :                         /* until now is misc */
     229       57102 :                         state_usage_misc(state);
     230             : 
     231             :                         /* get the next task */
     232       57102 :                         task = io_data_read(&io, &diskcur, waiting_map, &waiting_mac);
     233             : 
     234             :                         /* until now is disk */
     235       57102 :                         state_usage_disk(state, handle, waiting_map, waiting_mac);
     236             : 
     237             :                         /* get the task results */
     238       57102 :                         disk = task->disk;
     239       57102 :                         block = task->block;
     240       57102 :                         read_size = task->read_size;
     241             : 
     242             :                         /* if the disk position is not used */
     243       57102 :                         if (!disk)
     244        5528 :                                 continue;
     245             : 
     246       52415 :                         state_usage_file(state, disk, task->file);
     247             : 
     248             :                         /* if the block is not used */
     249       52415 :                         if (!block_has_file(block))
     250         841 :                                 continue;
     251             : 
     252             :                         /* handle error conditions */
     253       51574 :                         if (task->state == TASK_STATE_IOERROR) {
     254             :                                 /* LCOV_EXCL_START */
     255             :                                 ++io_error;
     256             :                                 goto bail;
     257             :                                 /* LCOV_EXCL_STOP */
     258             :                         }
     259       51574 :                         if (task->state == TASK_STATE_ERROR) {
     260             :                                 /* LCOV_EXCL_START */
     261             :                                 ++error;
     262             :                                 goto bail;
     263             :                                 /* LCOV_EXCL_STOP */
     264             :                         }
     265       51574 :                         if (task->state == TASK_STATE_ERROR_CONTINUE) {
     266           0 :                                 ++error;
     267           0 :                                 continue;
     268             :                         }
     269       51574 :                         if (task->state == TASK_STATE_IOERROR_CONTINUE) {
     270           0 :                                 ++io_error;
     271           0 :                                 if (io_error >= state->opt.io_error_limit) {
     272             :                                         /* LCOV_EXCL_START */
     273             :                                         log_fatal("DANGER! Too many input/output read error in a data disk, it isn't possible to scrub.\n");
     274             :                                         log_fatal("Ensure that disk '%s' is sane and that file '%s' can be accessed.\n", disk->dir, task->path);
     275             :                                         log_fatal("Stopping at block %u\n", blockcur);
     276             :                                         goto bail;
     277             :                                         /* LCOV_EXCL_STOP */
     278             :                                 }
     279             : 
     280             :                                 /* otherwise continue */
     281           0 :                                 continue;
     282             :                         }
     283       51574 :                         if (task->state != TASK_STATE_DONE) {
     284             :                                 /* LCOV_EXCL_START */
     285             :                                 log_fatal("Internal inconsistency in task state\n");
     286             :                                 os_abort();
     287             :                                 /* LCOV_EXCL_STOP */
     288             :                         }
     289             : 
     290       51574 :                         countsize += read_size;
     291             :                 }
     292             : 
     293             :                 /* until now is misc */
     294        9517 :                 state_usage_misc(state);
     295             : 
     296             :                 /* read the parity */
     297       66619 :                 for (l = 0; l < state->level; ++l) {
     298             :                         struct snapraid_task* task;
     299             :                         unsigned levcur;
     300             : 
     301       57102 :                         task = io_parity_read(&io, &levcur, waiting_map, &waiting_mac);
     302             : 
     303             :                         /* until now is parity */
     304       57102 :                         state_usage_parity(state, waiting_map, waiting_mac);
     305             : 
     306             :                         /* handle error conditions */
     307       57102 :                         if (task->state == TASK_STATE_IOERROR) {
     308             :                                 /* LCOV_EXCL_START */
     309             :                                 ++io_error;
     310             :                                 goto bail;
     311             :                                 /* LCOV_EXCL_STOP */
     312             :                         }
     313       57102 :                         if (task->state == TASK_STATE_ERROR) {
     314             :                                 /* LCOV_EXCL_START */
     315             :                                 ++error;
     316             :                                 goto bail;
     317             :                                 /* LCOV_EXCL_STOP */
     318             :                         }
     319       57102 :                         if (task->state == TASK_STATE_ERROR_CONTINUE) {
     320           0 :                                 ++error;
     321           0 :                                 continue;
     322             :                         }
     323       57102 :                         if (task->state == TASK_STATE_IOERROR_CONTINUE) {
     324           0 :                                 ++io_error;
     325           0 :                                 if (io_error >= state->opt.io_error_limit) {
     326             :                                         /* LCOV_EXCL_START */
     327             :                                         log_fatal("DANGER! Too many input/output read error in the %s disk, it isn't possible to scrub.\n", lev_name(levcur));
     328             :                                         log_fatal("Ensure that disk '%s' is sane and can be read.\n", lev_config_name(levcur));
     329             :                                         log_fatal("Stopping at block %u\n", blockcur);
     330             :                                         goto bail;
     331             :                                         /* LCOV_EXCL_STOP */
     332             :                                 }
     333           0 :                                 continue;
     334             :                         }
     335       57102 :                         if (task->state != TASK_STATE_DONE) {
     336             :                                 /* LCOV_EXCL_START */
     337             :                                 log_fatal("Internal inconsistency in task state\n");
     338             :                                 os_abort();
     339             :                                 /* LCOV_EXCL_STOP */
     340             :                         }
     341             :                 }
     342             : 
     343             :                 /* count the number of processed block */
     344        9517 :                 ++countpos;
     345             : 
     346             :                 /* progress */
     347        9517 :                 if (state_progress(state, &io, blockcur, countpos, countmax, countsize)) {
     348             :                         /* LCOV_EXCL_START */
     349             :                         break;
     350             :                         /* LCOV_EXCL_STOP */
     351             :                 }
     352             : 
     353             :                 /* thermal control */
     354        9517 :                 if (state_thermal_alarm(state)) {
     355             :                         /* until now is misc */
     356           0 :                         state_usage_misc(state);
     357             : 
     358           0 :                         state_progress_stop(state);
     359             : 
     360           0 :                         state_thermal_cooldown(state);
     361             : 
     362           0 :                         state_progress_restart(state);
     363             : 
     364             :                         /* drop until now */
     365           0 :                         state_usage_waste(state);
     366             :                 }
     367             :         }
     368             : 
     369           3 : end:
     370           3 :         state_progress_end(state, countpos, countmax, countsize, "Nothing to dry.\n");
     371             : 
     372           3 :         state_usage_print(state);
     373             : 
     374           3 : bail:
     375             :         /* stop all the worker threads */
     376           3 :         io_stop(&io);
     377             : 
     378          21 :         for (j = 0; j < diskmax; ++j) {
     379          18 :                 struct snapraid_file* file = handle[j].file;
     380          18 :                 struct snapraid_disk* disk = handle[j].disk;
     381          18 :                 ret = handle_close(&handle[j]);
     382          18 :                 if (ret == -1) {
     383             :                         /* LCOV_EXCL_START */
     384             :                         log_tag("error:%u:%s:%s: Close error. %s\n", blockmax, disk->name, esc_tag(file->sub, esc_buffer), strerror(errno));
     385             :                         log_fatal("DANGER! Unexpected close error in a data disk.\n");
     386             :                         ++error;
     387             :                         /* continue, as we are already exiting */
     388             :                         /* LCOV_EXCL_STOP */
     389             :                 }
     390             :         }
     391             : 
     392           3 :         if (error || io_error) {
     393           0 :                 msg_status("\n");
     394           0 :                 msg_status("%8u file errors\n", error);
     395           0 :                 msg_status("%8u io errors\n", io_error);
     396             :         } else {
     397           3 :                 msg_status("Everything OK\n");
     398             :         }
     399             : 
     400           3 :         if (error)
     401           0 :                 log_fatal("DANGER! Unexpected errors!\n");
     402           3 :         if (io_error)
     403           0 :                 log_fatal("DANGER! Unexpected input/output errors!\n");
     404             : 
     405           3 :         free(handle);
     406           3 :         free(waiting_map);
     407           3 :         io_done(&io);
     408             : 
     409           3 :         if (error + io_error != 0)
     410           0 :                 return -1;
     411           3 :         return 0;
     412             : }
     413             : 
     414           3 : int state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t blockcount)
     415             : {
     416             :         block_off_t blockmax;
     417             :         int ret;
     418             :         struct snapraid_parity_handle parity_handle[LEV_MAX];
     419             :         unsigned error;
     420             :         unsigned l;
     421             : 
     422           3 :         msg_progress("Drying...\n");
     423             : 
     424           3 :         blockmax = parity_allocated_size(state);
     425             : 
     426           3 :         if (blockstart > blockmax) {
     427             :                 /* LCOV_EXCL_START */
     428             :                 log_fatal("Error in the specified starting block %u. It's larger than the parity size %u.\n", blockstart, blockmax);
     429             :                 exit(EXIT_FAILURE);
     430             :                 /* LCOV_EXCL_STOP */
     431             :         }
     432             : 
     433             :         /* adjust the number of block to process */
     434           3 :         if (blockcount != 0 && blockstart + blockcount < blockmax) {
     435           0 :                 blockmax = blockstart + blockcount;
     436             :         }
     437             : 
     438             :         /* open the file for reading */
     439             :         /* it may fail if the file doesn't exist, in this case we continue to dry the files */
     440          21 :         for (l = 0; l < state->level; ++l) {
     441          18 :                 ret = parity_open(&parity_handle[l], &state->parity[l], l, state->file_mode, state->block_size, state->opt.parity_limit_size);
     442          18 :                 if (ret == -1) {
     443             :                         /* LCOV_EXCL_START */
     444             :                         log_fatal("WARNING! Without an accessible %s file, it isn't possible to dry.\n", lev_name(l));
     445             :                         exit(EXIT_FAILURE);
     446             :                         /* LCOV_EXCL_STOP */
     447             :                 }
     448             :         }
     449             : 
     450           3 :         error = 0;
     451             : 
     452             :         /* skip degenerated cases of empty parity, or skipping all */
     453           3 :         if (blockstart < blockmax) {
     454           3 :                 ret = state_dry_process(state, parity_handle, blockstart, blockmax);
     455           3 :                 if (ret == -1) {
     456             :                         /* LCOV_EXCL_START */
     457             :                         ++error;
     458             :                         /* continue, as we are already exiting */
     459             :                         /* LCOV_EXCL_STOP */
     460             :                 }
     461             :         }
     462             : 
     463             :         /* try to close only if opened */
     464          21 :         for (l = 0; l < state->level; ++l) {
     465          18 :                 ret = parity_close(&parity_handle[l]);
     466          18 :                 if (ret == -1) {
     467             :                         /* LCOV_EXCL_START */
     468             :                         ++error;
     469             :                         /* continue, as we are already exiting */
     470             :                         /* LCOV_EXCL_STOP */
     471             :                 }
     472             :         }
     473             : 
     474           3 :         if (error != 0)
     475           0 :                 return -1;
     476           3 :         return 0;
     477             : }
     478             : 

Generated by: LCOV version 1.0