LCOV - code coverage report
Current view: top level - cmdline - state.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1960 2195 89.3 %
Date: 2026-04-29 15:04:44 Functions: 57 58 98.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-3.0-or-later
       2             : // Copyright (C) 2011 Andrea Mazzoleni
       3             : 
       4             : #include "portable.h"
       5             : 
       6             : #include "elem.h"
       7             : #include "import.h"
       8             : #include "search.h"
       9             : #include "state.h"
      10             : #include "support.h"
      11             : #include "parity.h"
      12             : #include "stream.h"
      13             : #include "handle.h"
      14             : #include "io.h"
      15             : #include "thermal.h"
      16             : #include "raid/raid.h"
      17             : #include "raid/cpu.h"
      18             : 
      19             : /**
      20             :  * Configure the multithread support.
      21             :  *
      22             :  * Multi thread for write could be either faster or slower, depending
      23             :  * on the specific conditions. With multithreads it's likely faster
      24             :  * writing to disk, but you'll need to access multiple times the same data,
      25             :  * being potentially slower.
      26             :  *
      27             :  * Multi thread for verify is instead always generally faster,
      28             :  * so we enable it if possible.
      29             :  */
      30             : #if HAVE_THREAD
      31             : /* #define HAVE_MT_WRITE 1 */
      32             : #define HAVE_MT_VERIFY 1
      33             : #endif
      34             : 
      35           7 : const char* lev_name(unsigned l)
      36             : {
      37           7 :         switch (l) {
      38           2 :         case 0 : return "Parity";
      39           1 :         case 1 : return "2-Parity";
      40           1 :         case 2 : return "3-Parity";
      41           1 :         case 3 : return "4-Parity";
      42           1 :         case 4 : return "5-Parity";
      43           1 :         case 5 : return "6-Parity";
      44             :         }
      45             : 
      46           0 :         return "invalid";
      47             : }
      48             : 
      49      290009 : const char* lev_config_name(unsigned l)
      50             : {
      51      290009 :         switch (l) {
      52       96573 :         case 0 : return "parity";
      53       76786 :         case 1 : return "2-parity";
      54       39717 :         case 2 : return "3-parity";
      55       25672 :         case 3 : return "4-parity";
      56       25647 :         case 4 : return "5-parity";
      57       25614 :         case 5 : return "6-parity";
      58             :         }
      59             : 
      60           0 :         return "invalid";
      61             : }
      62             : 
      63        7691 : static int lev_config_scan(const char* s, unsigned* level, unsigned* mode)
      64             : {
      65        7691 :         if (strcmp(s, "parity") == 0 || strcmp(s, "1-parity") == 0) {
      66         705 :                 *level = 0;
      67         705 :                 return 0;
      68             :         }
      69             : 
      70        6986 :         if (strcmp(s, "q-parity") == 0 || strcmp(s, "2-parity") == 0) {
      71         248 :                 *level = 1;
      72         248 :                 return 0;
      73             :         }
      74             : 
      75        6738 :         if (strcmp(s, "r-parity") == 0 || strcmp(s, "3-parity") == 0) {
      76         213 :                 *level = 2;
      77         213 :                 return 0;
      78             :         }
      79             : 
      80        6525 :         if (strcmp(s, "4-parity") == 0) {
      81         210 :                 *level = 3;
      82         210 :                 return 0;
      83             :         }
      84             : 
      85        6315 :         if (strcmp(s, "5-parity") == 0) {
      86         207 :                 *level = 4;
      87         207 :                 return 0;
      88             :         }
      89             : 
      90        6108 :         if (strcmp(s, "6-parity") == 0) {
      91         203 :                 *level = 5;
      92         203 :                 return 0;
      93             :         }
      94             : 
      95        5905 :         if (strcmp(s, "z-parity") == 0) {
      96           3 :                 *level = 2;
      97           3 :                 if (mode)
      98           3 :                         *mode = RAID_MODE_VANDERMONDE;
      99           3 :                 return 0;
     100             :         }
     101             : 
     102        5902 :         return -1;
     103             : }
     104             : 
     105         315 : const char* lev_raid_name(unsigned mode, unsigned n)
     106             : {
     107         315 :         switch (n) {
     108          68 :         case 1 : return "par1";
     109          32 :         case 2 : return "par2";
     110           6 :         case 3 : if (mode == RAID_MODE_CAUCHY)
     111           3 :                         return "par3";
     112             :                 else
     113           3 :                         return "parz";
     114           3 :         case 4 : return "par4";
     115           4 :         case 5 : return "par5";
     116         202 :         case 6 : return "par6";
     117             :         }
     118             : 
     119           0 :         return 0;
     120             : }
     121             : 
     122         317 : void state_init(struct snapraid_state* state)
     123             : {
     124             :         unsigned l, s, i;
     125             : 
     126         317 :         memset(&state->opt, 0, sizeof(state->opt));
     127         317 :         state->mapped_device = 0;
     128         317 :         state->snapshot = 0;
     129         317 :         state->filter_hidden = 0;
     130         317 :         state->autosave = 0;
     131         317 :         state->need_write = 0;
     132         317 :         state->written = 0;
     133         317 :         state->checked_read = 0;
     134         317 :         state->block_size = 256 * KIBI; /* default 256 KiB */
     135         317 :         state->raid_mode = RAID_MODE_CAUCHY;
     136         317 :         state->file_mode = ADVISE_DEFAULT;
     137        2219 :         for (l = 0; l < LEV_MAX; ++l) {
     138        1902 :                 state->parity[l].split_mac = 0;
     139       17118 :                 for (s = 0; s < SPLIT_MAX; ++s) {
     140       15216 :                         state->parity[l].split_map[s].path[0] = 0;
     141       15216 :                         state->parity[l].split_map[s].uuid[0] = 0;
     142       15216 :                         state->parity[l].split_map[s].fstype[0] = 0;
     143       15216 :                         state->parity[l].split_map[s].fslabel[0] = 0;
     144       15216 :                         state->parity[l].split_map[s].size = PARITY_SIZE_INVALID;
     145       15216 :                         state->parity[l].split_map[s].device = 0;
     146             :                 }
     147        1902 :                 state->parity[l].smartctl[0] = 0;
     148        9510 :                 for (i = 0; i < SMART_IGNORE_MAX; ++i)
     149        7608 :                         state->parity[l].smartignore[i] = 0;
     150        1902 :                 state->parity[l].total_blocks = 0;
     151        1902 :                 state->parity[l].free_blocks = 0;
     152        1902 :                 state->parity[l].skip_access = 0;
     153        1902 :                 state->parity[l].tick = 0;
     154        1902 :                 state->parity[l].cached_blocks = 0;
     155        1902 :                 state->parity[l].is_excluded_by_filter = 0;
     156             :         }
     157         317 :         state->tick_io = 0;
     158         317 :         state->tick_misc = 0;
     159         317 :         state->tick_sched = 0;
     160         317 :         state->tick_raid = 0;
     161         317 :         state->tick_hash = 0;
     162         317 :         state->tick_last = os_tick();
     163         317 :         state->share[0] = 0;
     164         317 :         state->pool[0] = 0;
     165         317 :         state->pool_device = 0;
     166         317 :         state->lockfile[0] = 0;
     167         317 :         state->level = 1; /* default is the lowest protection */
     168         317 :         state->no_conf = 0;
     169        1585 :         for (i = 0; i < SMART_IGNORE_MAX; ++i)
     170        1268 :                 state->smartignore[i] = 0;
     171         317 :         state->rehash_blocks = 0;
     172         317 :         state->bad_blocks = 0;
     173         317 :         state->unsynced_blocks = 0;
     174         317 :         state->unscrubbed_blocks = 0;
     175         317 :         state->thermal_stop_gathering = 0;
     176         317 :         state->thermal_ambient_temperature = 0;
     177         317 :         state->thermal_highest_temperature = 0;
     178         317 :         state->thermal_first = 0;
     179         317 :         state->thermal_latest = 0;
     180         317 :         state->thermal_temperature_limit = 0;
     181         317 :         state->thermal_cooldown_time = 0;
     182             : 
     183         317 :         tommy_list_init(&state->disklist);
     184         317 :         tommy_list_init(&state->extralist);
     185         317 :         tommy_list_init(&state->maplist);
     186         317 :         tommy_list_init(&state->contentlist);
     187         317 :         tommy_list_init(&state->filterlist);
     188         317 :         tommy_list_init(&state->importlist);
     189         317 :         tommy_list_init(&state->thermallist);
     190         317 :         tommy_hashdyn_init(&state->importset);
     191         317 :         tommy_hashdyn_init(&state->previmportset);
     192         317 :         tommy_hashdyn_init(&state->searchset);
     193         317 :         tommy_arrayblkof_init(&state->infoarr, sizeof(snapraid_info));
     194         317 :         tommy_list_init(&state->bucketlist);
     195         317 : }
     196             : 
     197         292 : void state_done(struct snapraid_state* state)
     198             : {
     199         292 :         tommy_list_foreach(&state->disklist, (tommy_foreach_func*)disk_free);
     200         292 :         tommy_list_foreach(&state->extralist, (tommy_foreach_func*)extra_free);
     201         292 :         tommy_list_foreach(&state->maplist, (tommy_foreach_func*)map_free);
     202         292 :         tommy_list_foreach(&state->contentlist, (tommy_foreach_func*)content_free);
     203         292 :         tommy_list_foreach(&state->filterlist, (tommy_foreach_func*)filter_free);
     204         292 :         tommy_list_foreach(&state->importlist, (tommy_foreach_func*)import_file_free);
     205         292 :         tommy_list_foreach(&state->thermallist, (tommy_foreach_func*)thermal_free);
     206         292 :         tommy_hashdyn_foreach(&state->searchset, (tommy_foreach_func*)search_file_free);
     207         292 :         tommy_hashdyn_done(&state->importset);
     208         292 :         tommy_hashdyn_done(&state->previmportset);
     209         292 :         tommy_hashdyn_done(&state->searchset);
     210         292 :         tommy_arrayblkof_done(&state->infoarr);
     211         292 :         tommy_list_foreach(&state->bucketlist, (tommy_foreach_func*)bucket_free);
     212         292 : }
     213             : 
     214             : /**
     215             :  * Check the configuration.
     216             :  */
     217         315 : static void state_config_check(struct snapraid_state* state, const char* path, tommy_list* filterlist_disk)
     218             : {
     219             :         tommy_node* i;
     220             :         unsigned l, s;
     221             : 
     222             :         /* check for parity level */
     223         315 :         if (state->raid_mode == RAID_MODE_VANDERMONDE) {
     224           3 :                 if (state->level > 3) {
     225             :                         /* LCOV_EXCL_START */
     226             :                         log_fatal(EUSER, "Using z-parity limits you to a maximum of 3 parities.\n");
     227             :                         exit(EXIT_FAILURE);
     228             :                         /* LCOV_EXCL_STIO */
     229             :                 }
     230             :         }
     231             : 
     232             :         for (l = 0; l < state->level; ++l) {
     233             :                 if (state->parity[l].split_mac == 0) {
     234             :                         /* LCOV_EXCL_START */
     235             :                         log_fatal(EUSER, "Missing '%s' specification in '%s'\n", lev_config_name(l), path);
     236             :                         exit(EXIT_FAILURE);
     237             :                         /* LCOV_EXCL_STOP */
     238             :                 }
     239             :         }
     240             : 
     241         315 :         if (tommy_list_empty(&state->contentlist)) {
     242             :                 /* LCOV_EXCL_START */
     243             :                 log_fatal(EUSER, "Missing 'content' specification in '%s'\n", path);
     244             :                 exit(EXIT_FAILURE);
     245             :                 /* LCOV_EXCL_STOP */
     246             :         }
     247             : 
     248             :         /* check for equal paths */
     249        2031 :         for (i = state->contentlist; i != 0; i = i->next) {
     250        1716 :                 struct snapraid_content* content = i->data;
     251             : 
     252       10794 :                 for (l = 0; l < state->level; ++l) {
     253       45084 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     254       36006 :                                 if (pathcmp(state->parity[l].split_map[s].path, content->content) == 0) {
     255             :                                         /* LCOV_EXCL_START */
     256             :                                         log_fatal(EUSER, "Same path used for '%s' and 'content' as '%s'\n", lev_config_name(l), content->content);
     257             :                                         exit(EXIT_FAILURE);
     258             :                                         /* LCOV_EXCL_STOP */
     259             :                                 }
     260             :                         }
     261             :                 }
     262             :         }
     263             : 
     264             :         /* check device of data disks */
     265         315 :         if (!state->opt.skip_device && !state->opt.skip_disk_access) {
     266          77 :                 for (i = state->disklist; i != 0; i = i->next) {
     267             :                         tommy_node* j;
     268          70 :                         struct snapraid_disk* disk = i->data;
     269             : 
     270             :                         /* skip data disks that are not accessible */
     271          70 :                         if (disk->skip_access)
     272           0 :                                 continue;
     273             : 
     274             : #ifdef _WIN32
     275             :                         if (disk->dir_device == 0) {
     276             :                                 /* LCOV_EXCL_START */
     277             :                                 log_fatal(ESOFT, "Disk '%s' has a zero serial number.\n", disk->dir);
     278             :                                 log_fatal(ESOFT, "This is not necessarily wrong, but for using SnapRAID\n");
     279             :                                 log_fatal(ESOFT, "it's better to change the serial number of the disk.\n");
     280             :                                 log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     281             :                                 exit(EXIT_FAILURE);
     282             :                                 /* LCOV_EXCL_STOP */
     283             :                         }
     284             : #endif
     285             : 
     286         385 :                         for (j = i->next; j != 0; j = j->next) {
     287         315 :                                 struct snapraid_disk* other = j->data;
     288         315 :                                 if (disk->mount_device == other->mount_device) {
     289           0 :                                         if (state->opt.force_device) {
     290             :                                                 /*
     291             :                                                  * Note that we just ignore the issue
     292             :                                                  * and we DON'T mark the disk to be skipped
     293             :                                                  * because we want to use these disks
     294             :                                                  */
     295           0 :                                                 if (!state->opt.no_warnings)
     296           0 :                                                         log_fatal(EUSER, "DANGER! Ignoring that disks '%s' and '%s' are on the same device\n", disk->name, other->name);
     297             :                                         } else {
     298             :                                                 /* LCOV_EXCL_START */
     299             :                                                 log_fatal(ESOFT, "Disks '%s' and '%s' are on the same device.\n", disk->mount_point, other->mount_point);
     300             : #ifdef _WIN32
     301             :                                                 log_fatal(ESOFT, "Both have the serial number '%" PRIx64 "'.\n", disk->dir_device);
     302             :                                                 log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     303             :                                                 log_fatal(ESOFT, "to change one of the disk serial.\n");
     304             : #endif
     305             :                                                 /* in "fix" we allow to continue anyway */
     306             :                                                 if (strcmp(state->command, "fix") == 0) {
     307             :                                                         log_fatal(ESOFT, "You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     308             :                                                 }
     309             :                                                 exit(EXIT_FAILURE);
     310             :                                                 /* LCOV_EXCL_STOP */
     311             :                                         }
     312             :                                 }
     313             :                         }
     314             : 
     315             :                         /* skip data disks that are not accessible */
     316          70 :                         if (disk->skip_access)
     317           0 :                                 continue;
     318             : 
     319          70 :                         if (!state->opt.skip_parity_access) {
     320         210 :                                 for (l = 0; l < state->level; ++l) {
     321         280 :                                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     322         140 :                                                 if (disk->mount_device == state->parity[l].split_map[s].device) {
     323           0 :                                                         if (state->opt.force_device) {
     324             :                                                                 /*
     325             :                                                                  * Note that we just ignore the issue
     326             :                                                                  * and we DON'T mark the disk to be skipped
     327             :                                                                  * because we want to use these disks
     328             :                                                                  */
     329           0 :                                                                 if (!state->opt.no_warnings)
     330           0 :                                                                         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);
     331             :                                                         } else {
     332             :                                                                 /* LCOV_EXCL_START */
     333             :                                                                 log_fatal(ESOFT, "Disk '%s' and %s '%s' are on the same device.\n", disk->mount_point, lev_name(l), state->parity[l].split_map[s].path);
     334             : #ifdef _WIN32
     335             :                                                                 log_fatal(ESOFT, "Both have the serial number '%" PRIx64 "'.\n", disk->dir_device);
     336             :                                                                 log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     337             :                                                                 log_fatal(ESOFT, "to change one of the disk serial.\n");
     338             : #endif
     339             :                                                                 exit(EXIT_FAILURE);
     340             :                                                                 /* LCOV_EXCL_STOP */
     341             :                                                         }
     342             :                                                 }
     343             :                                         }
     344             :                                 }
     345             :                         }
     346             : 
     347          70 :                         if (state->pool[0] != 0 && disk->mount_device == state->pool_device) {
     348             :                                 /* LCOV_EXCL_START */
     349             :                                 log_fatal(ESOFT, "Disk '%s' and pool '%s' are on the same device.\n", disk->mount_point, state->pool);
     350             : #ifdef _WIN32
     351             :                                 log_fatal(ESOFT, "Both have the serial number '%" PRIx64 "'.\n", disk->dir_device);
     352             :                                 log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     353             :                                 log_fatal(ESOFT, "to change one of the disk serial.\n");
     354             : #endif
     355             :                                 exit(EXIT_FAILURE);
     356             :                                 /* LCOV_EXCL_STOP */
     357             :                         }
     358             :                 }
     359             :         }
     360             : 
     361             :         /* check device of parity disks */
     362         315 :         if (!state->opt.skip_device && !state->opt.skip_parity_access) {
     363          21 :                 for (l = 0; l < state->level; ++l) {
     364          28 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     365             :                                 unsigned j, t;
     366             : 
     367             :                                 /* skip parity disks that are not accessible */
     368          14 :                                 if (state->parity[l].skip_access)
     369           0 :                                         continue;
     370             : 
     371             : #ifdef _WIN32
     372             :                                 if (state->parity[l].split_map[s].device == 0) {
     373             :                                         /* LCOV_EXCL_START */
     374             :                                         log_fatal(ESOFT, "Disk '%s' has a zero serial number.\n", state->parity[l].split_map[s].path);
     375             :                                         log_fatal(ESOFT, "This is not necessarily wrong, but for using SnapRAID\n");
     376             :                                         log_fatal(ESOFT, "it's better to change the serial number of the disk.\n");
     377             :                                         log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     378             :                                         exit(EXIT_FAILURE);
     379             :                                         /* LCOV_EXCL_STOP */
     380             :                                 }
     381             : #endif
     382             : 
     383          21 :                                 for (j = l + 1; j < state->level; ++j) {
     384          14 :                                         for (t = 0; t < state->parity[j].split_mac; ++t) {
     385           7 :                                                 if (state->parity[l].split_map[s].device == state->parity[j].split_map[t].device) {
     386           0 :                                                         if (state->opt.force_device) {
     387             :                                                                 /*
     388             :                                                                  * Note that we just ignore the issue
     389             :                                                                  * and we DON'T mark the disk to be skipped
     390             :                                                                  * because we want to use these disks
     391             :                                                                  */
     392           0 :                                                                 if (!state->opt.no_warnings)
     393           0 :                                                                         log_fatal(EUSER, "DANGER! Skipping parities '%s' and '%s' on the same device\n", lev_config_name(l), lev_config_name(j));
     394             :                                                         } else {
     395             :                                                                 /* LCOV_EXCL_START */
     396             :                                                                 log_fatal(ESOFT, "Parity '%s' and '%s' are on the same device.\n", state->parity[l].split_map[s].path, state->parity[j].split_map[t].path);
     397             : #ifdef _WIN32
     398             :                                                                 log_fatal(ESOFT, "Both have the serial number '%" PRIx64 "'.\n", state->parity[l].split_map[s].device);
     399             :                                                                 log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     400             :                                                                 log_fatal(ESOFT, "to change one of the disk serial.\n");
     401             : #endif
     402             :                                                                 /* in "fix" we allow to continue anyway */
     403             :                                                                 if (strcmp(state->command, "fix") == 0) {
     404             :                                                                         log_fatal(ESOFT, "You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     405             :                                                                 }
     406             :                                                                 exit(EXIT_FAILURE);
     407             :                                                                 /* LCOV_EXCL_STOP */
     408             :                                                         }
     409             :                                                 }
     410             :                                         }
     411             :                                 }
     412             : 
     413          14 :                                 if (state->pool[0] != 0 && state->pool_device == state->parity[l].split_map[s].device) {
     414             :                                         /* LCOV_EXCL_START */
     415             :                                         log_fatal(ESOFT, "Pool '%s' and parity '%s' are on the same device.\n", state->pool, state->parity[l].split_map[s].path);
     416             : #ifdef _WIN32
     417             :                                         log_fatal(ESOFT, "Both have the serial number '%" PRIx64 "'.\n", state->pool_device);
     418             :                                         log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     419             :                                         log_fatal(ESOFT, "to change one of the disk serial.\n");
     420             : #endif
     421             :                                         exit(EXIT_FAILURE);
     422             :                                         /* LCOV_EXCL_STOP */
     423             :                                 }
     424             :                         }
     425             :                 }
     426             :         }
     427             : 
     428             :         /* check device of pool disk */
     429             : #ifdef _WIN32
     430             :         if (!state->opt.skip_device) {
     431             :                 if (state->pool[0] != 0 && state->pool_device == 0) {
     432             :                         /* LCOV_EXCL_START */
     433             :                         log_fatal(ESOFT, "Disk '%s' has a zero serial number.\n", state->pool);
     434             :                         log_fatal(ESOFT, "This is not necessarily wrong, but for using SnapRAID\n");
     435             :                         log_fatal(ESOFT, "it's better to change the serial number of the disk.\n");
     436             :                         log_fatal(ESOFT, "Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     437             :                         exit(EXIT_FAILURE);
     438             :                         /* LCOV_EXCL_STOP */
     439             :                 }
     440             :         }
     441             : #endif
     442             : 
     443             :         /* count the content files */
     444         315 :         if (!state->opt.skip_device && !state->opt.skip_content_access) {
     445             :                 unsigned content_count;
     446             : 
     447           7 :                 content_count = 0;
     448          35 :                 for (i = state->contentlist; i != 0; i = i->next) {
     449             :                         tommy_node* j;
     450          28 :                         struct snapraid_content* content = i->data;
     451             : 
     452             :                         /* check if there are others in the same disk */
     453          70 :                         for (j = i->next; j != 0; j = j->next) {
     454          42 :                                 struct snapraid_content* other = j->data;
     455          42 :                                 if (content->device == other->device) {
     456           0 :                                         log_fatal(ESOFT, "WARNING! Content files on the same disk: '%s' and '%s'.\n", content->content, other->content);
     457           0 :                                         break;
     458             :                                 }
     459             :                         }
     460          28 :                         if (j != 0) {
     461             :                                 /* skip it */
     462           0 :                                 continue;
     463             :                         }
     464             : 
     465          28 :                         ++content_count;
     466             :                 }
     467             : 
     468           7 :                 if (content_count < state->level + 1) {
     469             :                         /* LCOV_EXCL_START */
     470             :                         log_fatal(EUSER, "You must have at least %d 'content' files in different disks.\n", state->level + 1);
     471             :                         exit(EXIT_FAILURE);
     472             :                         /* LCOV_EXCL_STOP */
     473             :                 }
     474             :         }
     475             : 
     476             :         /* check for speed */
     477             : #ifdef CONFIG_X86
     478         315 :         if (!raid_cpu_has_ssse3())
     479             : #endif
     480           0 :         if (state->raid_mode == RAID_MODE_CAUCHY && !state->opt.no_warnings) {
     481           0 :                 if (state->level == 3) {
     482           0 :                         log_fatal(ESOFT, "WARNING! Your CPU doesn't have a fast implementation for triple parity.\n");
     483           0 :                         log_fatal(ESOFT, "WARNING! It's recommended to switch to 'z-parity' instead than '3-parity'.\n");
     484           0 :                 } else if (state->level > 3) {
     485           0 :                         log_fatal(ESOFT, "WARNING! Your CPU doesn't have a fast implementation beyond triple parity.\n");
     486           0 :                         log_fatal(ESOFT, "WARNING! It's recommended to reduce the parity levels to triple parity.\n");
     487             :                 }
     488             :         }
     489             : 
     490             :         /* ensure that specified filter disks are valid ones */
     491         331 :         for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
     492             :                 tommy_node* j;
     493          16 :                 struct snapraid_filter* filter = i->data;
     494          66 :                 for (j = state->disklist; j != 0; j = j->next) {
     495          59 :                         struct snapraid_disk* disk = j->data;
     496          59 :                         if (wnmatch(filter->pattern, disk->name) == 0)
     497           9 :                                 break;
     498             :                 }
     499          16 :                 if (j == 0) {
     500           7 :                         for (j = state->extralist; j != 0; j = j->next) {
     501           0 :                                 struct snapraid_extra* extra = j->data;
     502           0 :                                 if (wnmatch(filter->pattern, extra->name) == 0)
     503           0 :                                         break;
     504             :                         }
     505             :                 }
     506          16 :                 if (j == 0) {
     507             :                         /* check matching with parity disks */
     508          24 :                         for (l = 0; l < state->level; ++l)
     509          24 :                                 if (wnmatch(filter->pattern, lev_config_name(l)) == 0)
     510           7 :                                         break;
     511           7 :                         if (l == state->level) {
     512             :                                 /* LCOV_EXCL_START */
     513             :                                 log_fatal(EUSER, "Option -d, --filter-disk %s doesn't match any data or parity or extra disk.\n", filter->pattern);
     514             :                                 exit(EXIT_FAILURE);
     515             :                                 /* LCOV_EXCL_STOP */
     516             :                         }
     517             :                 }
     518             :         }
     519         315 : }
     520             : 
     521             : /**
     522             :  * Validate the smartctl command.
     523             :  *
     524             :  * It must contains only one %s string, and not other % chars.
     525             :  */
     526         390 : static int validate_smartctl(const char* custom)
     527             : {
     528         390 :         const char* s = custom;
     529         390 :         int arg = 0;
     530             : 
     531        2340 :         while (*s) {
     532        1950 :                 if (s[0] == '%' && s[1] == 's') {
     533         195 :                         if (arg) {
     534             :                                 /* LCOV_EXCL_START */
     535             :                                 return -1;
     536             :                                 /* LCOV_EXCL_STOP */
     537             :                         }
     538         195 :                         arg = 1;
     539        1755 :                 } else if (s[0] == '%') {
     540             :                         /* LCOV_EXCL_START */
     541             :                         return -1;
     542             :                         /* LCOV_EXCL_STOP */
     543             :                 }
     544             : 
     545        1950 :                 ++s;
     546             :         }
     547             : 
     548         390 :         return 0;
     549             : }
     550             : 
     551         316 : void state_config(struct snapraid_state* state, const char* path, const char* command, struct snapraid_option* opt, tommy_list* filterlist_disk)
     552             : {
     553             :         STREAM* f;
     554             :         unsigned line;
     555             :         tommy_node* i;
     556             :         unsigned l, s;
     557             :         char esc_buffer[ESC_MAX];
     558             :         char esc_buffer1[ESC_MAX];
     559             : 
     560             :         /* copy the options */
     561         316 :         state->opt = *opt;
     562             : 
     563             :         /* if unset, sort by physical order */
     564         316 :         if (!state->opt.force_order)
     565           8 :                 state->opt.force_order = SORT_PHYSICAL;
     566             : 
     567             :         /* adjust file mode */
     568         316 :         if (state->opt.file_mode != ADVISE_DEFAULT) {
     569         308 :                 state->file_mode = state->opt.file_mode;
     570             :         } else {
     571             :                 /* default mode, if nothing is specified */
     572           8 :                 state->file_mode = ADVISE_DISCARD;
     573             :         }
     574             : 
     575             :         /* store current command */
     576         316 :         state->command = command;
     577             : 
     578         316 :         log_tag("conf:file:%s\n", esc_tag(path, esc_buffer));
     579             : 
     580         316 :         f = sopen_read(path, 0);
     581         316 :         if (!f) {
     582             :                 /* LCOV_EXCL_START */
     583             :                 if (errno == ENOENT) {
     584             :                         log_fatal(errno, "No configuration file found at '%s'\n", path);
     585             :                 } else if (errno == EACCES) {
     586             :                         log_fatal(errno, "You do not have rights to access the configuration file '%s'\n", path);
     587             :                 } else {
     588             :                         log_fatal(errno, "Error opening the configuration file '%s'. %s.\n", path, strerror(errno));
     589             :                 }
     590             :                 exit(EXIT_FAILURE);
     591             :                 /* LCOV_EXCL_STOP */
     592             :         }
     593             : 
     594         316 :         line = 1;
     595        7152 :         while (1) {
     596             :                 char tag[PATH_MAX];
     597             :                 char buffer[PATH_MAX];
     598             :                 int ret;
     599             :                 int c;
     600             :                 unsigned level;
     601             : 
     602             :                 /* skip initial spaces */
     603        7468 :                 sgetspace(f);
     604             : 
     605             :                 /* read the command */
     606        7468 :                 ret = sgettok(f, tag, sizeof(tag));
     607        7468 :                 if (ret < 0) {
     608             :                         /* LCOV_EXCL_START */
     609             :                         log_fatal(EUSER, "Error reading the configuration file '%s' at line %u\n", path, line);
     610             :                         exit(EXIT_FAILURE);
     611             :                         /* LCOV_EXCL_STOP */
     612             :                 }
     613             : 
     614             :                 /* skip spaces after the command */
     615        7468 :                 sgetspace(f);
     616             : 
     617        7468 :                 if (strcmp(tag, "blocksize") == 0
     618             :                         /* block_size is the old format of the option */
     619        7159 :                         || strcmp(tag, "block_size") == 0) {
     620             : 
     621         316 :                         ret = sgetu32(f, &state->block_size);
     622         316 :                         if (ret < 0) {
     623             :                                 /* LCOV_EXCL_START */
     624             :                                 log_fatal(EUSER, "Invalid 'blocksize' specification in '%s' at line %u\n", path, line);
     625             :                                 exit(EXIT_FAILURE);
     626             :                                 /* LCOV_EXCL_STOP */
     627             :                         }
     628         316 :                         if (state->block_size < 1) {
     629             :                                 /* LCOV_EXCL_START */
     630             :                                 log_fatal(EUSER, "Too small 'blocksize' specification in '%s' at line %u\n", path, line);
     631             :                                 exit(EXIT_FAILURE);
     632             :                                 /* LCOV_EXCL_STOP */
     633             :                         }
     634         316 :                         if (state->block_size > 16 * KIBI) {
     635             :                                 /* LCOV_EXCL_START */
     636             :                                 log_fatal(EUSER, "Too big 'blocksize' specification in '%s' at line %u\n", path, line);
     637             :                                 exit(EXIT_FAILURE);
     638             :                                 /* LCOV_EXCL_STOP */
     639             :                         }
     640             :                         /* check if it's a power of 2 */
     641         316 :                         if ((state->block_size & (state->block_size - 1)) != 0) {
     642             :                                 /* LCOV_EXCL_START */
     643             :                                 log_fatal(EUSER, "Not power of 2 'blocksize' specification in '%s' at line %u\n", path, line);
     644             :                                 exit(EXIT_FAILURE);
     645             :                                 /* LCOV_EXCL_STOP */
     646             :                         }
     647         316 :                         state->block_size *= KIBI;
     648        7152 :                 } else if (strcmp(tag, "hashsize") == 0
     649        7107 :                         || strcmp(tag, "hash_size") == 0 /* v11.0 used incorrectly this one, kept now for backward compatibility */
     650          45 :                 ) {
     651             :                         uint32_t hash_size;
     652             : 
     653          45 :                         ret = sgetu32(f, &hash_size);
     654          45 :                         if (ret < 0) {
     655             :                                 /* LCOV_EXCL_START */
     656             :                                 log_fatal(EUSER, "Invalid 'hashsize' specification in '%s' at line %u\n", path, line);
     657             :                                 exit(EXIT_FAILURE);
     658             :                                 /* LCOV_EXCL_STOP */
     659             :                         }
     660          45 :                         if (hash_size < 2) {
     661             :                                 /* LCOV_EXCL_START */
     662             :                                 log_fatal(EUSER, "Too small 'hashsize' specification in '%s' at line %u\n", path, line);
     663             :                                 exit(EXIT_FAILURE);
     664             :                                 /* LCOV_EXCL_STOP */
     665             :                         }
     666          45 :                         if (hash_size > HASH_MAX) {
     667             :                                 /* LCOV_EXCL_START */
     668             :                                 log_fatal(EUSER, "Too big 'hashsize' specification in '%s' at line %u\n", path, line);
     669             :                                 exit(EXIT_FAILURE);
     670             :                                 /* LCOV_EXCL_STOP */
     671             :                         }
     672             :                         /* check if it's a power of 2 */
     673          45 :                         if ((hash_size & (hash_size - 1)) != 0) {
     674             :                                 /* LCOV_EXCL_START */
     675             :                                 log_fatal(EUSER, "Not power of 2 'hashsize' specification in '%s' at line %u\n", path, line);
     676             :                                 exit(EXIT_FAILURE);
     677             :                                 /* LCOV_EXCL_STOP */
     678             :                         }
     679             : 
     680          45 :                         BLOCK_HASH_SIZE = hash_size;
     681        7107 :                 } else if (lev_config_scan(tag, &level, &state->raid_mode) == 0) {
     682             :                         char device[PATH_MAX];
     683             :                         char* split_map[SPLIT_MAX + 1];
     684             :                         unsigned split_mac;
     685             :                         uint64_t dev;
     686             :                         int skip_access;
     687             : 
     688        1400 :                         if (state->parity[level].split_mac != 0) {
     689             :                                 /* LCOV_EXCL_START */
     690             :                                 log_fatal(EUSER, "Multiple '%s' specification in '%s' at line %u\n", tag, path, line);
     691             :                                 exit(EXIT_FAILURE);
     692             :                                 /* LCOV_EXCL_STOP */
     693             :                         }
     694             : 
     695        1400 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     696        1400 :                         if (ret < 0) {
     697             :                                 /* LCOV_EXCL_START */
     698             :                                 log_fatal(EUSER, "Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
     699             :                                 exit(EXIT_FAILURE);
     700             :                                 /* LCOV_EXCL_STOP */
     701             :                         }
     702             : 
     703        1400 :                         if (!*buffer) {
     704             :                                 /* LCOV_EXCL_START */
     705             :                                 log_fatal(EUSER, "Empty '%s' specification in '%s' at line %u\n", tag, path, line);
     706             :                                 exit(EXIT_FAILURE);
     707             :                                 /* LCOV_EXCL_STOP */
     708             :                         }
     709             : 
     710        1400 :                         split_mac = strsplit(split_map, SPLIT_MAX + 1, buffer, ",");
     711             : 
     712        1400 :                         if (split_mac > SPLIT_MAX) {
     713             :                                 /* LCOV_EXCL_START */
     714             :                                 log_fatal(EUSER, "Too many files in '%s' specification in '%s' at line %u\n", tag, path, line);
     715             :                                 exit(EXIT_FAILURE);
     716             :                                 /* LCOV_EXCL_STOP */
     717             :                         }
     718             : 
     719        1400 :                         skip_access = 0;
     720        1400 :                         state->parity[level].split_mac = split_mac;
     721        6885 :                         for (s = 0; s < split_mac; ++s) {
     722             :                                 char uuid[UUID_MAX];
     723        5486 :                                 pathimport(state->parity[level].split_map[s].path, sizeof(state->parity[level].split_map[s].path), split_map[s]);
     724             : 
     725        5486 :                                 if (!state->opt.skip_parity_access) {
     726             :                                         struct stat st;
     727             : 
     728             :                                         /* get the device of the directory containing the parity file */
     729        4772 :                                         pathimport(device, sizeof(device), split_map[s]);
     730             : 
     731             :                                         /* use only the dir, as the parity file may not exist yet */
     732        4772 :                                         pathcut(device);
     733             : 
     734        4772 :                                         if (stat(device, &st) == 0) {
     735        4767 :                                                 dev = st.st_dev;
     736             : 
     737             :                                                 /* read the uuid, if unsupported use an empty one */
     738        4767 :                                                 if (devuuid(dev, device, uuid, sizeof(uuid)) != 0) {
     739           0 :                                                         *uuid = 0;
     740             :                                                 }
     741             :                                         } else {
     742             :                                                 /* if the disk can be skipped */
     743           5 :                                                 if (state->opt.force_device) {
     744             :                                                         /* use a fake device, and mark the disk to be skipped */
     745           4 :                                                         dev = 0;
     746           4 :                                                         *uuid = 0;
     747           4 :                                                         skip_access = 1;
     748           4 :                                                         log_fatal(errno, "DANGER! Skipping inaccessible parity disk '%s'...\n", tag);
     749             :                                                 } else {
     750             :                                                         /* LCOV_EXCL_START */
     751             :                                                         log_fatal(errno, "Error accessing 'parity' dir '%s' specification in '%s' at line %u\n", device, path, line);
     752             : 
     753             :                                                         /* in "fix" we allow to continue anyway */
     754             :                                                         if (strcmp(state->command, "fix") == 0) {
     755             :                                                                 log_fatal(errno, "You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     756             :                                                         }
     757             :                                                         exit(EXIT_FAILURE);
     758             :                                                         /* LCOV_EXCL_STOP */
     759             :                                                 }
     760             :                                         }
     761             :                                 } else {
     762             :                                         /* use a fake device */
     763         714 :                                         dev = 0;
     764         714 :                                         *uuid = 0;
     765             :                                 }
     766             : 
     767        5485 :                                 state->parity[level].split_map[s].device = dev;
     768        5485 :                                 pathcpy(state->parity[level].split_map[s].uuid, sizeof(state->parity[level].split_map[s].uuid), uuid);
     769             :                         }
     770             : 
     771             :                         /* store the global parity skip_access */
     772        1399 :                         state->parity[level].skip_access = skip_access;
     773             : 
     774             :                         /* adjust the level */
     775        1399 :                         if (state->level < level + 1)
     776        1083 :                                 state->level = level + 1;
     777        5707 :                 } else if (strcmp(tag, "share") == 0) {
     778          45 :                         if (*state->share) {
     779             :                                 /* LCOV_EXCL_START */
     780             :                                 log_fatal(EUSER, "Multiple 'share' specification in '%s' at line %u\n", path, line);
     781             :                                 exit(EXIT_FAILURE);
     782             :                                 /* LCOV_EXCL_STOP */
     783             :                         }
     784             : 
     785          45 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     786          45 :                         if (ret < 0) {
     787             :                                 /* LCOV_EXCL_START */
     788             :                                 log_fatal(EUSER, "Invalid 'share' specification in '%s' at line %u\n", path, line);
     789             :                                 exit(EXIT_FAILURE);
     790             :                                 /* LCOV_EXCL_STOP */
     791             :                         }
     792             : 
     793          45 :                         if (!*buffer) {
     794             :                                 /* LCOV_EXCL_START */
     795             :                                 log_fatal(EUSER, "Empty 'share' specification in '%s' at line %u\n", path, line);
     796             :                                 exit(EXIT_FAILURE);
     797             :                                 /* LCOV_EXCL_STOP */
     798             :                         }
     799             : 
     800          45 :                         pathimport(state->share, sizeof(state->share), buffer);
     801        5662 :                 } else if (strcmp(tag, "pool") == 0) {
     802             :                         struct stat st;
     803             : 
     804          52 :                         if (*state->pool) {
     805             :                                 /* LCOV_EXCL_START */
     806             :                                 log_fatal(EUSER, "Multiple 'pool' specification in '%s' at line %u\n", path, line);
     807             :                                 exit(EXIT_FAILURE);
     808             :                                 /* LCOV_EXCL_STOP */
     809             :                         }
     810             : 
     811          52 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     812          52 :                         if (ret < 0) {
     813             :                                 /* LCOV_EXCL_START */
     814             :                                 log_fatal(EUSER, "Invalid 'pool' specification in '%s' at line %u\n", path, line);
     815             :                                 exit(EXIT_FAILURE);
     816             :                                 /* LCOV_EXCL_STOP */
     817             :                         }
     818             : 
     819          52 :                         if (!*buffer) {
     820             :                                 /* LCOV_EXCL_START */
     821             :                                 log_fatal(EUSER, "Empty 'pool' specification in '%s' at line %u\n", path, line);
     822             :                                 exit(EXIT_FAILURE);
     823             :                                 /* LCOV_EXCL_STOP */
     824             :                         }
     825             : 
     826          52 :                         pathimport(state->pool, sizeof(state->pool), buffer);
     827             : 
     828             :                         /* get the device of the directory containing the pool tree */
     829          52 :                         if (stat(buffer, &st) != 0) {
     830             :                                 /* LCOV_EXCL_START */
     831             :                                 log_fatal(errno, "Error accessing 'pool' dir '%s' specification in '%s' at line %u\n", buffer, path, line);
     832             :                                 exit(EXIT_FAILURE);
     833             :                                 /* LCOV_EXCL_STOP */
     834             :                         }
     835             : 
     836          52 :                         state->pool_device = st.st_dev;
     837        5610 :                 } else if (strcmp(tag, "content") == 0) {
     838             :                         struct snapraid_content* content;
     839             :                         char device[PATH_MAX];
     840             :                         char* slash;
     841             :                         struct stat st;
     842             :                         uint64_t dev;
     843             : 
     844        1716 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     845        1716 :                         if (ret < 0) {
     846             :                                 /* LCOV_EXCL_START */
     847             :                                 log_fatal(EUSER, "Invalid 'content' specification in '%s' at line %u\n", path, line);
     848             :                                 exit(EXIT_FAILURE);
     849             :                                 /* LCOV_EXCL_STOP */
     850             :                         }
     851             : 
     852        1716 :                         if (pathcmp(buffer, "/dev/null") == 0 || pathcmp(buffer, "NUL") == 0) {
     853             :                                 /* LCOV_EXCL_START */
     854             :                                 log_fatal(EUSER, "You cannot use the null device as 'content' specification in '%s' at line %u\n", path, line);
     855             :                                 exit(EXIT_FAILURE);
     856             :                                 /* LCOV_EXCL_STOP */
     857             :                         }
     858             : 
     859        1716 :                         if (!*buffer) {
     860             :                                 /* LCOV_EXCL_START */
     861             :                                 log_fatal(EUSER, "Empty 'content' specification in '%s' at line %u\n", path, line);
     862             :                                 exit(EXIT_FAILURE);
     863             :                                 /* LCOV_EXCL_STOP */
     864             :                         }
     865             : 
     866             :                         /* check if the content file is already specified */
     867        6269 :                         for (i = state->contentlist; i != 0; i = i->next) {
     868        4553 :                                 content = i->data;
     869        4553 :                                 if (pathcmp(content->content, buffer) == 0)
     870           0 :                                         break;
     871             :                         }
     872        1716 :                         if (i) {
     873             :                                 /* LCOV_EXCL_START */
     874             :                                 log_fatal(EUSER, "Duplicate 'content' specification in '%s' at line %u\n", path, line);
     875             :                                 exit(EXIT_FAILURE);
     876             :                                 /* LCOV_EXCL_STOP */
     877             :                         }
     878             : 
     879             :                         /* get the device of the directory containing the content file */
     880        1716 :                         pathimport(device, sizeof(device), buffer);
     881        1716 :                         slash = strrchr(device, '/');
     882        1716 :                         if (slash)
     883        1716 :                                 *(slash + 1) = 0;
     884             :                         else
     885           0 :                                 pathcpy(device, sizeof(device), ".");
     886        1716 :                         if (stat(device, &st) == 0) {
     887        1715 :                                 dev = st.st_dev;
     888             :                         } else {
     889           1 :                                 if (state->opt.skip_content_access) {
     890             :                                         /* use a fake device */
     891           1 :                                         dev = 0;
     892           1 :                                         log_fatal(errno, "WARNING! Skipping inaccessible content file '%s'...\n", buffer);
     893             :                                 } else {
     894             :                                         /* LCOV_EXCL_START */
     895             :                                         log_fatal(errno, "Error accessing 'content' dir '%s' specification in '%s' at line %u\n", device, path, line);
     896             :                                         exit(EXIT_FAILURE);
     897             :                                         /* LCOV_EXCL_STOP */
     898             :                                 }
     899             :                         }
     900             : 
     901             :                         /* set the lock file at the first accessible content file */
     902        1716 :                         if (state->lockfile[0] == 0 && dev != 0) {
     903         315 :                                 pathcpy(state->lockfile, sizeof(state->lockfile), buffer);
     904         315 :                                 pathcat(state->lockfile, sizeof(state->lockfile), ".lock");
     905             :                         }
     906             : 
     907        1716 :                         content = content_alloc(buffer, dev);
     908             : 
     909        1716 :                         tommy_list_insert_tail(&state->contentlist, &content->node, content);
     910        5738 :                 } else if (strcmp(tag, "data") == 0 || strcmp(tag, "disk") == 0) {
     911             :                         /* "disk" is the deprecated name up to SnapRAID 9.x */
     912             :                         char dir[PATH_MAX];
     913             :                         char device[PATH_MAX];
     914             :                         char uuid[UUID_MAX];
     915             :                         struct snapraid_disk* disk;
     916             :                         uint64_t dev;
     917             :                         int skip_access;
     918             : 
     919        1844 :                         ret = sgettok(f, buffer, sizeof(buffer));
     920        1844 :                         if (ret < 0) {
     921             :                                 /* LCOV_EXCL_START */
     922             :                                 log_fatal(EUSER, "Invalid 'data' name specification in '%s' at line %u\n", path, line);
     923             :                                 exit(EXIT_FAILURE);
     924             :                                 /* LCOV_EXCL_STOP */
     925             :                         }
     926             : 
     927        1844 :                         if (!*buffer) {
     928             :                                 /* LCOV_EXCL_START */
     929             :                                 log_fatal(EUSER, "Empty 'data' name specification in '%s' at line %u\n", path, line);
     930             :                                 exit(EXIT_FAILURE);
     931             :                                 /* LCOV_EXCL_STOP */
     932             :                         }
     933             : 
     934        1844 :                         sgetspace(f);
     935             : 
     936        1844 :                         ret = sgetlasttok(f, dir, sizeof(dir));
     937        1844 :                         if (ret < 0) {
     938             :                                 /* LCOV_EXCL_START */
     939             :                                 log_fatal(EUSER, "Invalid 'data' dir specification in '%s' at line %u\n", path, line);
     940             :                                 exit(EXIT_FAILURE);
     941             :                                 /* LCOV_EXCL_STOP */
     942             :                         }
     943             : 
     944        1844 :                         if (!*dir) {
     945             :                                 /* LCOV_EXCL_START */
     946             :                                 log_fatal(EUSER, "Empty 'data' dir specification in '%s' at line %u\n", path, line);
     947             :                                 exit(EXIT_FAILURE);
     948             :                                 /* LCOV_EXCL_STOP */
     949             :                         }
     950             : 
     951             :                         /* get the device of the dir */
     952        1844 :                         pathimport(device, sizeof(device), dir);
     953             : 
     954             :                         /* check if the disk name already exists */
     955        6478 :                         for (i = state->disklist; i != 0; i = i->next) {
     956        4634 :                                 disk = i->data;
     957        4634 :                                 if (strcmp(disk->name, buffer) == 0)
     958           0 :                                         break;
     959             :                         }
     960        1844 :                         if (i) {
     961             :                                 /* LCOV_EXCL_START */
     962             :                                 log_fatal(EUSER, "Duplicate 'data' name '%s' at line %u\n", buffer, line);
     963             :                                 exit(EXIT_FAILURE);
     964             :                                 /* LCOV_EXCL_STOP */
     965             :                         }
     966             : 
     967             :                         /* if the disk has to be present */
     968        1844 :                         skip_access = 0;
     969        1844 :                         if (!state->opt.skip_disk_access) {
     970             :                                 struct stat st;
     971             : 
     972        1682 :                                 if (stat(device, &st) == 0) {
     973        1681 :                                         dev = st.st_dev;
     974             : 
     975             :                                         /* read the uuid, if unsupported use an empty one */
     976        1681 :                                         if (devuuid(dev, device, uuid, sizeof(uuid)) != 0) {
     977           0 :                                                 *uuid = 0;
     978             :                                         }
     979             : 
     980             :                                         /* fake a different UUID when testing */
     981        1681 :                                         if (state->opt.fake_uuid) {
     982           2 :                                                 snprintf(uuid, sizeof(uuid), "fake-uuid-%d", state->opt.fake_uuid);
     983           2 :                                                 --state->opt.fake_uuid;
     984             :                                         }
     985             :                                 } else {
     986             :                                         /* if the disk can be skipped */
     987           1 :                                         if (state->opt.force_device) {
     988             :                                                 /* use a fake device, and mark the disk to be skipped */
     989           1 :                                                 dev = 0;
     990           1 :                                                 *uuid = 0;
     991           1 :                                                 skip_access = 1;
     992           1 :                                                 log_fatal(errno, "DANGER! Skipping inaccessible data disk '%s'...\n", buffer);
     993             :                                         } else {
     994             :                                                 /* LCOV_EXCL_START */
     995             :                                                 log_fatal(errno, "Error accessing 'disk' '%s' specification in '%s' at line %u\n", dir, device, line);
     996             : 
     997             :                                                 /* in "fix" we allow to continue anyway */
     998             :                                                 if (strcmp(state->command, "fix") == 0) {
     999             :                                                         log_fatal(errno, "You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
    1000             :                                                 }
    1001             :                                                 exit(EXIT_FAILURE);
    1002             :                                                 /* LCOV_EXCL_STOP */
    1003             :                                         }
    1004             :                                 }
    1005             :                         } else {
    1006             :                                 /* use a fake device */
    1007         162 :                                 dev = 0;
    1008         162 :                                 *uuid = 0;
    1009             :                         }
    1010             : 
    1011        1844 :                         disk = disk_alloc(buffer, dir, dev, uuid, skip_access);
    1012             : 
    1013        1844 :                         tommy_list_insert_tail(&state->disklist, &disk->node, disk);
    1014        2050 :                 } else if (strcmp(tag, "extra") == 0) {
    1015             :                         char dir[PATH_MAX];
    1016             :                         char device[PATH_MAX];
    1017             :                         char uuid[UUID_MAX];
    1018             :                         struct snapraid_extra* extra;
    1019             :                         uint64_t dev;
    1020             : 
    1021          14 :                         ret = sgettok(f, buffer, sizeof(buffer));
    1022          14 :                         if (ret < 0) {
    1023             :                                 /* LCOV_EXCL_START */
    1024             :                                 log_fatal(EUSER, "Invalid 'extra' name specification in '%s' at line %u\n", path, line);
    1025             :                                 exit(EXIT_FAILURE);
    1026             :                                 /* LCOV_EXCL_STOP */
    1027             :                         }
    1028             : 
    1029          14 :                         if (!*buffer) {
    1030             :                                 /* LCOV_EXCL_START */
    1031             :                                 log_fatal(EUSER, "Empty 'extra' name specification in '%s' at line %u\n", path, line);
    1032             :                                 exit(EXIT_FAILURE);
    1033             :                                 /* LCOV_EXCL_STOP */
    1034             :                         }
    1035             : 
    1036          14 :                         sgetspace(f);
    1037             : 
    1038          14 :                         ret = sgetlasttok(f, dir, sizeof(dir));
    1039          14 :                         if (ret < 0) {
    1040             :                                 /* LCOV_EXCL_START */
    1041             :                                 log_fatal(EUSER, "Invalid 'extra' dir specification in '%s' at line %u\n", path, line);
    1042             :                                 exit(EXIT_FAILURE);
    1043             :                                 /* LCOV_EXCL_STOP */
    1044             :                         }
    1045             : 
    1046          14 :                         if (!*dir) {
    1047             :                                 /* LCOV_EXCL_START */
    1048             :                                 log_fatal(EUSER, "Empty 'extra' dir specification in '%s' at line %u\n", path, line);
    1049             :                                 exit(EXIT_FAILURE);
    1050             :                                 /* LCOV_EXCL_STOP */
    1051             :                         }
    1052             : 
    1053             :                         /* get the device of the dir */
    1054          14 :                         pathimport(device, sizeof(device), dir);
    1055             : 
    1056             :                         /* check if the disk name already exists */
    1057          21 :                         for (i = state->extralist; i != 0; i = i->next) {
    1058           7 :                                 extra = i->data;
    1059           7 :                                 if (strcmp(extra->name, buffer) == 0)
    1060           0 :                                         break;
    1061             :                         }
    1062          14 :                         if (i) {
    1063             :                                 /* LCOV_EXCL_START */
    1064             :                                 log_fatal(EUSER, "Duplicate 'extra' name '%s' at line %u\n", buffer, line);
    1065             :                                 exit(EXIT_FAILURE);
    1066             :                                 /* LCOV_EXCL_STOP */
    1067             :                         }
    1068             : 
    1069             :                         /* if the disk has to be present */
    1070          14 :                         if (!state->opt.skip_disk_access) {
    1071             :                                 struct stat st;
    1072             : 
    1073          14 :                                 if (stat(device, &st) == 0) {
    1074          14 :                                         dev = st.st_dev;
    1075             : 
    1076             :                                         /* read the uuid, if unsupported use an empty one */
    1077          14 :                                         if (devuuid(dev, device, uuid, sizeof(uuid)) != 0) {
    1078           0 :                                                 *uuid = 0;
    1079             :                                         }
    1080             : 
    1081             :                                         /* fake a different UUID when testing */
    1082          14 :                                         if (state->opt.fake_uuid) {
    1083           0 :                                                 snprintf(uuid, sizeof(uuid), "fake-uuid-%d", state->opt.fake_uuid);
    1084           0 :                                                 --state->opt.fake_uuid;
    1085             :                                         }
    1086             :                                 } else {
    1087             :                                         /* use a fake device, and mark the disk to be skipped */
    1088           0 :                                         dev = 0;
    1089           0 :                                         *uuid = 0;
    1090             :                                 }
    1091             :                         } else {
    1092             :                                 /* use a fake device */
    1093           0 :                                 dev = 0;
    1094           0 :                                 *uuid = 0;
    1095             :                         }
    1096             : 
    1097          14 :                         extra = extra_alloc(buffer, dir, dev, uuid);
    1098             : 
    1099          14 :                         tommy_list_insert_tail(&state->extralist, &extra->node, extra);
    1100        2036 :                 } else if (strcmp(tag, "smartctl") == 0) {
    1101             :                         char custom[PATH_MAX];
    1102             : 
    1103         390 :                         ret = sgettok(f, buffer, sizeof(buffer));
    1104         390 :                         if (ret < 0) {
    1105             :                                 /* LCOV_EXCL_START */
    1106             :                                 log_fatal(EUSER, "Invalid 'smartctl' name in '%s' at line %u\n", path, line);
    1107             :                                 exit(EXIT_FAILURE);
    1108             :                                 /* LCOV_EXCL_STOP */
    1109             :                         }
    1110             : 
    1111         390 :                         if (!*buffer) {
    1112             :                                 /* LCOV_EXCL_START */
    1113             :                                 log_fatal(EUSER, "Empty 'smartctl' name in '%s' at line %u\n", path, line);
    1114             :                                 exit(EXIT_FAILURE);
    1115             :                                 /* LCOV_EXCL_STOP */
    1116             :                         }
    1117             : 
    1118         390 :                         sgetspace(f);
    1119             : 
    1120         390 :                         ret = sgetlasttok(f, custom, sizeof(custom));
    1121         390 :                         if (ret < 0) {
    1122             :                                 /* LCOV_EXCL_START */
    1123             :                                 log_fatal(EUSER, "Invalid 'smartctl' option in '%s' at line %u\n", path, line);
    1124             :                                 exit(EXIT_FAILURE);
    1125             :                                 /* LCOV_EXCL_STOP */
    1126             :                         }
    1127             : 
    1128         390 :                         if (!*custom) {
    1129             :                                 /* LCOV_EXCL_START */
    1130             :                                 log_fatal(EUSER, "Empty 'smartctl' option in '%s' at line %u\n", path, line);
    1131             :                                 exit(EXIT_FAILURE);
    1132             :                                 /* LCOV_EXCL_STOP */
    1133             :                         }
    1134             : 
    1135         390 :                         if (validate_smartctl(custom) != 0) {
    1136             :                                 /* LCOV_EXCL_START */
    1137             :                                 log_fatal(EUSER, "Invalid 'smartctl' option in '%s' at line %u\n", path, line);
    1138             :                                 exit(EXIT_FAILURE);
    1139             :                                 /* LCOV_EXCL_STOP */
    1140             :                         }
    1141             : 
    1142             :                         /* search for parity */
    1143         390 :                         if (lev_config_scan(buffer, &level, 0) == 0) {
    1144         195 :                                 if (state->parity[level].smartctl[0] != 0) {
    1145             :                                         /* LCOV_EXCL_START */
    1146             :                                         log_fatal(EUSER, "Duplicate parity 'smartctl' '%s' at line %u\n", buffer, line);
    1147             :                                         exit(EXIT_FAILURE);
    1148             :                                         /* LCOV_EXCL_STOP */
    1149             :                                 }
    1150             : 
    1151         195 :                                 pathcpy(state->parity[level].smartctl, sizeof(state->parity[level].smartctl), custom);
    1152             :                         } else {
    1153             :                                 /* search the disk */
    1154         195 :                                 struct snapraid_disk* found_disk = 0;
    1155         195 :                                 for (i = state->disklist; i != 0; i = i->next) {
    1156         195 :                                         struct snapraid_disk* disk = i->data;
    1157         195 :                                         if (strcmp(disk->name, buffer) == 0) {
    1158         195 :                                                 found_disk = disk;
    1159         195 :                                                 break;
    1160             :                                         }
    1161             :                                 }
    1162         195 :                                 struct snapraid_extra* found_extra = 0;
    1163         195 :                                 for (i = state->extralist; i != 0; i = i->next) {
    1164           0 :                                         struct snapraid_extra* extra = i->data;
    1165           0 :                                         if (strcmp(extra->name, buffer) == 0) {
    1166           0 :                                                 found_extra = extra;
    1167           0 :                                                 break;
    1168             :                                         }
    1169             :                                 }
    1170         195 :                                 if (found_disk) {
    1171         195 :                                         if (found_disk->smartctl[0] != 0) {
    1172             :                                                 /* LCOV_EXCL_START */
    1173             :                                                 log_fatal(EUSER, "Duplicate 'smartctl' name '%s' at line %u\n", buffer, line);
    1174             :                                                 exit(EXIT_FAILURE);
    1175             :                                                 /* LCOV_EXCL_STOP */
    1176             :                                         }
    1177             : 
    1178         195 :                                         pathcpy(found_disk->smartctl, sizeof(found_disk->smartctl), custom);
    1179           0 :                                 } else if (found_extra) {
    1180           0 :                                         if (found_extra->smartctl[0] != 0) {
    1181             :                                                 /* LCOV_EXCL_START */
    1182             :                                                 log_fatal(EUSER, "Duplicate 'smartctl' name '%s' at line %u\n", buffer, line);
    1183             :                                                 exit(EXIT_FAILURE);
    1184             :                                                 /* LCOV_EXCL_STOP */
    1185             :                                         }
    1186             : 
    1187           0 :                                         pathcpy(found_extra->smartctl, sizeof(found_extra->smartctl), custom);
    1188             :                                 } else {
    1189             :                                         /* LCOV_EXCL_START */
    1190             :                                         log_fatal(EUSER, "Unknown 'smartctl' name '%s' at line %u\n", buffer, line);
    1191             :                                         exit(EXIT_FAILURE);
    1192             :                                         /* LCOV_EXCL_STOP */
    1193             :                                 }
    1194             :                         }
    1195        1646 :                 } else if (strcmp(tag, "smartignore") == 0) {
    1196             :                         int* smart;
    1197             : 
    1198         388 :                         ret = sgettok(f, buffer, sizeof(buffer));
    1199         388 :                         if (ret < 0) {
    1200             :                                 /* LCOV_EXCL_START */
    1201             :                                 log_fatal(EUSER, "Invalid 'smartignore' name in '%s' at line %u\n", path, line);
    1202             :                                 exit(EXIT_FAILURE);
    1203             :                                 /* LCOV_EXCL_STOP */
    1204             :                         }
    1205             : 
    1206         388 :                         if (!*buffer) {
    1207             :                                 /* LCOV_EXCL_START */
    1208             :                                 log_fatal(EUSER, "Empty 'smartignore' name in '%s' at line %u\n", path, line);
    1209             :                                 exit(EXIT_FAILURE);
    1210             :                                 /* LCOV_EXCL_STOP */
    1211             :                         }
    1212             : 
    1213         388 :                         sgetspace(f);
    1214             : 
    1215         388 :                         if (strcmp(buffer, "*") == 0) {
    1216         194 :                                 smart = state->smartignore;
    1217         194 :                         } else if (lev_config_scan(buffer, &level, 0) == 0) { /* search for parity */
    1218         194 :                                 smart = state->parity[level].smartignore;
    1219             :                         } else {
    1220             :                                 /* search the disk */
    1221           0 :                                 struct snapraid_disk* found_disk = 0;
    1222           0 :                                 for (i = state->disklist; i != 0; i = i->next) {
    1223           0 :                                         struct snapraid_disk* disk = i->data;
    1224           0 :                                         if (strcmp(disk->name, buffer) == 0) {
    1225           0 :                                                 found_disk = disk;
    1226           0 :                                                 break;
    1227             :                                         }
    1228             :                                 }
    1229           0 :                                 struct snapraid_extra* found_extra = 0;
    1230           0 :                                 for (i = state->extralist; i != 0; i = i->next) {
    1231           0 :                                         struct snapraid_extra* extra = i->data;
    1232           0 :                                         if (strcmp(extra->name, buffer) == 0) {
    1233           0 :                                                 found_extra = extra;
    1234           0 :                                                 break;
    1235             :                                         }
    1236             :                                 }
    1237           0 :                                 if (found_disk) {
    1238           0 :                                         smart = found_disk->smartignore;
    1239           0 :                                 } else if (found_extra) {
    1240           0 :                                         smart = found_extra->smartignore;
    1241             :                                 } else {
    1242             :                                         /* LCOV_EXCL_START */
    1243             :                                         log_fatal(EUSER, "Unknown 'smartignore' name '%s' at line %u\n", buffer, line);
    1244             :                                         exit(EXIT_FAILURE);
    1245             :                                         /* LCOV_EXCL_STOP */
    1246             :                                 }
    1247             :                         }
    1248             : 
    1249         388 :                         int si = 0;
    1250             : 
    1251         388 :                         while (1) {
    1252             :                                 char* e;
    1253             : 
    1254         776 :                                 ret = sgettok(f, buffer, sizeof(buffer));
    1255         776 :                                 if (ret < 0) {
    1256             :                                         /* LCOV_EXCL_START */
    1257             :                                         log_fatal(EUSER, "Invalid 'smartignore' specification in '%s' at line %u\n", path, line);
    1258             :                                         exit(EXIT_FAILURE);
    1259             :                                         /* LCOV_EXCL_STOP */
    1260             :                                 }
    1261             : 
    1262         776 :                                 if (!*buffer)
    1263         388 :                                         break;
    1264             : 
    1265         388 :                                 if (si == SMART_IGNORE_MAX) {
    1266             :                                         /* LCOV_EXCL_START */
    1267             :                                         log_fatal(EUSER, "Too many 'smartignore' specification in '%s' at line %u\n", path, line);
    1268             :                                         exit(EXIT_FAILURE);
    1269             :                                         /* LCOV_EXCL_STOP */
    1270             :                                 }
    1271             : 
    1272         388 :                                 smart[si] = strtoul(buffer, &e, 0);
    1273             : 
    1274         388 :                                 if (!e || *e) {
    1275             :                                         /* LCOV_EXCL_START */
    1276             :                                         log_fatal(EUSER, "Invalid 'smartignore' specification in '%s' at line %u\n", path, line);
    1277             :                                         exit(EXIT_FAILURE);
    1278             :                                         /* LCOV_EXCL_STOP */
    1279             :                                 }
    1280             : 
    1281         388 :                                 ++si;
    1282             : 
    1283         388 :                                 sgetspace(f);
    1284             :                         }
    1285             : 
    1286         388 :                         if (si == 0) {
    1287             :                                 /* LCOV_EXCL_START */
    1288             :                                 log_fatal(EUSER, "Invalid 'smartignore' specification in '%s' at line %u\n", path, line);
    1289             :                                 exit(EXIT_FAILURE);
    1290             :                                 /* LCOV_EXCL_STOP */
    1291             :                         }
    1292        1258 :                 } else if (strcmp(tag, "temp_limit") == 0) {
    1293             :                         unsigned temp;
    1294             : 
    1295           8 :                         ret = sgetu32(f, &temp);
    1296           8 :                         if (ret < 0) {
    1297             :                                 /* LCOV_EXCL_START */
    1298             :                                 log_fatal(EUSER, "Invalid 'temp_limit' specification in '%s' at line %u\n", path, line);
    1299             :                                 exit(EXIT_FAILURE);
    1300             :                                 /* LCOV_EXCL_STOP */
    1301             :                         }
    1302           8 :                         if (temp < 40 || temp > 70) {
    1303             :                                 /* LCOV_EXCL_START */
    1304             :                                 log_fatal(EUSER, "Invalid 'temp_limit' temperature specification in '%s' at line %u. It must be between 40 and 70\n", path, line);
    1305             :                                 exit(EXIT_FAILURE);
    1306             :                                 /* LCOV_EXCL_STOP */
    1307             :                         }
    1308             : 
    1309           8 :                         state->thermal_temperature_limit = temp;
    1310        1250 :                 } else if (strcmp(tag, "temp_sleep") == 0) {
    1311             :                         unsigned time;
    1312             : 
    1313           8 :                         ret = sgetu32(f, &time);
    1314           8 :                         if (ret < 0) {
    1315             :                                 /* LCOV_EXCL_START */
    1316             :                                 log_fatal(EUSER, "Invalid 'temp_sleep' specification in '%s' at line %u\n", path, line);
    1317             :                                 exit(EXIT_FAILURE);
    1318             :                                 /* LCOV_EXCL_STOP */
    1319             :                         }
    1320           8 :                         if (time < 3 || time > 120) {
    1321             :                                 /* LCOV_EXCL_START */
    1322             :                                 log_fatal(EUSER, "Invalid 'temp_sleep' temperature specification in '%s' at line %u. It must be between 3 and 120\n", path, line);
    1323             :                                 exit(EXIT_FAILURE);
    1324             :                                 /* LCOV_EXCL_STOP */
    1325             :                         }
    1326             : 
    1327           8 :                         state->thermal_cooldown_time = time * 60;
    1328        1242 :                 } else if (strcmp(tag, "nohidden") == 0) {
    1329           0 :                         state->filter_hidden = 1;
    1330        1242 :                 } else if (strcmp(tag, "snapshot") == 0) {
    1331          23 :                         state->snapshot = 1;
    1332        1219 :                 } else if (strcmp(tag, "exclude") == 0) {
    1333             :                         struct snapraid_filter* filter;
    1334             : 
    1335         343 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1336         343 :                         if (ret < 0) {
    1337             :                                 /* LCOV_EXCL_START */
    1338             :                                 log_fatal(EUSER, "Invalid 'exclude' specification in '%s' at line %u\n", path, line);
    1339             :                                 exit(EXIT_FAILURE);
    1340             :                                 /* LCOV_EXCL_STOP */
    1341             :                         }
    1342             : 
    1343         343 :                         if (!*buffer) {
    1344             :                                 /* LCOV_EXCL_START */
    1345             :                                 log_fatal(EUSER, "Empty 'exclude' specification in '%s' at line %u\n", path, line);
    1346             :                                 exit(EXIT_FAILURE);
    1347             :                                 /* LCOV_EXCL_STOP */
    1348             :                         }
    1349             : 
    1350         343 :                         filter = filter_alloc_file(-1, "", buffer);
    1351         343 :                         if (!filter) {
    1352             :                                 /* LCOV_EXCL_START */
    1353             :                                 log_fatal(EUSER, "Invalid 'exclude' specification '%s' in '%s' at line %u\n", buffer, path, line);
    1354             :                                 exit(EXIT_FAILURE);
    1355             :                                 /* LCOV_EXCL_STOP */
    1356             :                         }
    1357         343 :                         tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
    1358         876 :                 } else if (strcmp(tag, "include") == 0) {
    1359             :                         struct snapraid_filter* filter;
    1360             : 
    1361         285 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1362         285 :                         if (ret < 0) {
    1363             :                                 /* LCOV_EXCL_START */
    1364             :                                 log_fatal(EUSER, "Invalid 'include' specification in '%s' at line %u\n", path, line);
    1365             :                                 exit(EXIT_FAILURE);
    1366             :                                 /* LCOV_EXCL_STOP */
    1367             :                         }
    1368             : 
    1369         285 :                         if (!*buffer) {
    1370             :                                 /* LCOV_EXCL_START */
    1371             :                                 log_fatal(EUSER, "Empty 'include' specification in '%s' at line %u\n", path, line);
    1372             :                                 exit(EXIT_FAILURE);
    1373             :                                 /* LCOV_EXCL_STOP */
    1374             :                         }
    1375             : 
    1376         285 :                         filter = filter_alloc_file(1, "", buffer);
    1377         285 :                         if (!filter) {
    1378             :                                 /* LCOV_EXCL_START */
    1379             :                                 log_fatal(EUSER, "Invalid 'include' specification '%s' in '%s' at line %u\n", buffer, path, line);
    1380             :                                 exit(EXIT_FAILURE);
    1381             :                                 /* LCOV_EXCL_STOP */
    1382             :                         }
    1383         285 :                         tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
    1384         591 :                 } else if (strcmp(tag, "autosave") == 0) {
    1385             :                         char* e;
    1386             : 
    1387          52 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1388          52 :                         if (ret < 0) {
    1389             :                                 /* LCOV_EXCL_START */
    1390             :                                 log_fatal(EUSER, "Invalid 'autosave' specification in '%s' at line %u\n", path, line);
    1391             :                                 exit(EXIT_FAILURE);
    1392             :                                 /* LCOV_EXCL_STOP */
    1393             :                         }
    1394             : 
    1395          52 :                         if (!*buffer) {
    1396             :                                 /* LCOV_EXCL_START */
    1397             :                                 log_fatal(EUSER, "Empty 'autosave' specification in '%s' at line %u\n", path, line);
    1398             :                                 exit(EXIT_FAILURE);
    1399             :                                 /* LCOV_EXCL_STOP */
    1400             :                         }
    1401             : 
    1402          52 :                         state->autosave = strtoul(buffer, &e, 0);
    1403             : 
    1404          52 :                         if (!e || *e) {
    1405             :                                 /* LCOV_EXCL_START */
    1406             :                                 log_fatal(EUSER, "Invalid 'autosave' specification in '%s' at line %u\n", path, line);
    1407             :                                 exit(EXIT_FAILURE);
    1408             :                                 /* LCOV_EXCL_STOP */
    1409             :                         }
    1410             : 
    1411             :                         /* convert to GB */
    1412          52 :                         state->autosave *= GIGA;
    1413         539 :                 } else if (tag[0] == 0) {
    1414             :                         /* allow empty lines */
    1415         115 :                 } else if (tag[0] == '#') {
    1416         115 :                         ret = sgetline(f, buffer, sizeof(buffer));
    1417         115 :                         if (ret < 0) {
    1418             :                                 /* LCOV_EXCL_START */
    1419             :                                 log_fatal(EUSER, "Invalid comment in '%s' at line %u\n", path, line);
    1420             :                                 exit(EXIT_FAILURE);
    1421             :                                 /* LCOV_EXCL_STOP */
    1422             :                         }
    1423             :                 } else {
    1424             :                         /* LCOV_EXCL_START */
    1425             :                         log_fatal(EUSER, "Invalid command '%s' in '%s' at line %u\n", tag, path, line);
    1426             :                         exit(EXIT_FAILURE);
    1427             :                         /* LCOV_EXCL_STOP */
    1428             :                 }
    1429             : 
    1430             :                 /* skip final spaces */
    1431        7467 :                 sgetspace(f);
    1432             : 
    1433             :                 /* next line */
    1434        7467 :                 c = sgeteol(f);
    1435        7467 :                 if (c == EOF) {
    1436         315 :                         break;
    1437             :                 }
    1438        7152 :                 if (c != '\n') {
    1439             :                         /* LCOV_EXCL_START */
    1440             :                         log_fatal(EUSER, "Extra data in '%s' at line %u\n", path, line);
    1441             :                         exit(EXIT_FAILURE);
    1442             :                         /* LCOV_EXCL_STOP */
    1443             :                 }
    1444        7152 :                 ++line;
    1445             :         }
    1446             : 
    1447         315 :         if (serror(f)) {
    1448             :                 /* LCOV_EXCL_START */
    1449             :                 log_fatal(errno, "Error reading the configuration file '%s' at line %u\n", path, line);
    1450             :                 exit(EXIT_FAILURE);
    1451             :                 /* LCOV_EXCL_STOP */
    1452             :         }
    1453             : 
    1454         315 :         sclose(f);
    1455             : 
    1456         315 :         state_config_check(state, path, filterlist_disk);
    1457             : 
    1458             :         /* select the default hash */
    1459         315 :         if (state->opt.force_murmur3) {
    1460           1 :                 state->besthash = HASH_MURMUR3;
    1461         314 :         } else if (state->opt.force_spooky2) {
    1462           1 :                 state->besthash = HASH_SPOOKY2;
    1463             :         } else {
    1464         313 :                 state->besthash = membesthash();
    1465             :         }
    1466             : 
    1467             :         /* by default use the best hash */
    1468         315 :         state->hash = state->besthash;
    1469             : 
    1470             :         /* by default use a random hash seed */
    1471         315 :         if (randomize(state->hashseed, HASH_MAX) != 0) {
    1472             :                 /* LCOV_EXCL_START */
    1473             :                 log_fatal(errno, "Failed to retrieve random values.\n");
    1474             :                 exit(EXIT_FAILURE);
    1475             :                 /* LCOV_EXCL_STOP */
    1476             :         }
    1477             : 
    1478             :         /* no previous hash by default */
    1479         315 :         state->prevhash = HASH_UNDEFINED;
    1480             : 
    1481             :         /* intentionally not set the prevhashseed, if used valgrind will warn about it */
    1482             : 
    1483         315 :         log_tag("blocksize:%u\n", state->block_size);
    1484        2159 :         for (i = state->disklist; i != 0; i = i->next) {
    1485        1844 :                 struct snapraid_disk* disk = i->data;
    1486        1844 :                 log_tag("data:%s:%s:%s\n", esc_tag(disk->name, esc_buffer), esc_tag(disk->mount_point, esc_buffer1), disk->uuid);
    1487             :         }
    1488         329 :         for (i = state->extralist; i != 0; i = i->next) {
    1489          14 :                 struct snapraid_extra* extra = i->data;
    1490          14 :                 log_tag("extra:%s:%s:%s\n", esc_tag(extra->name, esc_buffer), esc_tag(extra->dir, esc_buffer1), extra->uuid);
    1491             :         }
    1492             : 
    1493         315 :         log_tag("mode:%s\n", lev_raid_name(state->raid_mode, state->level));
    1494        1709 :         for (l = 0; l < state->level; ++l) {
    1495        1394 :                 log_tag("%s:%s:%s\n", lev_config_name(l), esc_tag(state->parity[l].split_map[0].path, esc_buffer), state->parity[l].split_map[0].uuid);
    1496        5465 :                 for (s = 1; s < state->parity[l].split_mac; ++s) {
    1497        4071 :                         log_tag("%s/%u:%s:%s\n", lev_config_name(l), s, esc_tag(state->parity[l].split_map[s].path, esc_buffer), state->parity[l].split_map[s].uuid);
    1498             :                 }
    1499             :         }
    1500         315 :         if (state->pool[0] != 0)
    1501          52 :                 log_tag("pool:%s\n", esc_tag(state->pool, esc_buffer));
    1502         315 :         if (state->share[0] != 0)
    1503          45 :                 log_tag("share:%s\n", esc_tag(state->share, esc_buffer));
    1504         315 :         if (state->autosave != 0)
    1505          52 :                 log_tag("autosave:%" PRIu64 "\n", state->autosave);
    1506         943 :         for (i = tommy_list_head(&state->filterlist); i != 0; i = i->next) {
    1507             :                 char out[PATH_MAX];
    1508         628 :                 struct snapraid_filter* filter = i->data;
    1509         628 :                 log_tag("filter:%s\n", esc_tag(filter_type(filter, out, sizeof(out)), esc_buffer));
    1510             :         }
    1511         315 :         if (state->filter_hidden)
    1512           0 :                 log_tag("filter:nohidden:\n");
    1513         315 :         log_flush();
    1514         315 : }
    1515             : 
    1516             : /**
    1517             :  * Find a disk by name.
    1518             :  */
    1519        8120 : static struct snapraid_disk* find_disk_by_name(struct snapraid_state* state, const char* name)
    1520             : {
    1521             :         tommy_node* i;
    1522             : 
    1523       27801 :         for (i = state->disklist; i != 0; i = i->next) {
    1524       27793 :                 struct snapraid_disk* disk = i->data;
    1525       27793 :                 if (strcmp(disk->name, name) == 0)
    1526        8112 :                         return disk;
    1527             :         }
    1528             : 
    1529           8 :         if (state->no_conf) {
    1530             :                 /* without a configuration file, add disks automatically */
    1531             :                 struct snapraid_disk* disk;
    1532             : 
    1533           6 :                 disk = disk_alloc(name, "DUMMY/", -1, "", 0);
    1534             : 
    1535           6 :                 tommy_list_insert_tail(&state->disklist, &disk->node, disk);
    1536             : 
    1537           6 :                 return disk;
    1538             :         }
    1539             : 
    1540           2 :         return 0;
    1541             : }
    1542             : 
    1543             : /**
    1544             :  * Find a disk by UUID.
    1545             :  */
    1546           2 : static struct snapraid_disk* find_disk_by_uuid(struct snapraid_state* state, const char* uuid)
    1547             : {
    1548             :         tommy_node* i;
    1549           2 :         struct snapraid_disk* found = 0;
    1550             : 
    1551             :         /*
    1552             :          * Special test case to find the first matching UUID
    1553             :          * when testing UUID are all equal or not supported
    1554             :          * and we should handle this case specifically
    1555             :          */
    1556           2 :         if (state->opt.match_first_uuid)
    1557           2 :                 return state->disklist->data;
    1558             : 
    1559             :         /* LCOV_EXCL_START */
    1560             :         /* never find an empty uuid */
    1561             :         if (!*uuid)
    1562             :                 return 0;
    1563             : 
    1564             :         for (i = state->disklist; i != 0; i = i->next) {
    1565             :                 struct snapraid_disk* disk = i->data;
    1566             :                 if (strcmp(disk->uuid, uuid) == 0) {
    1567             :                         /* never match duplicate UUID */
    1568             :                         if (found)
    1569             :                                 return 0;
    1570             :                         found = disk;
    1571             :                 }
    1572             :         }
    1573             : 
    1574             :         return found;
    1575             :         /* LCOV_EXCL_STOP */
    1576             : }
    1577             : 
    1578             : /**
    1579             :  * Update the disk mapping if required.
    1580             :  */
    1581         302 : static void state_map(struct snapraid_state* state)
    1582             : {
    1583             :         unsigned hole;
    1584             :         tommy_node* i;
    1585             :         unsigned uuid_mismatch;
    1586             :         unsigned diskcount;
    1587             :         unsigned l, s;
    1588             : 
    1589             :         /*
    1590             :          * Remove all the mapping without a disk
    1591             :          * this happens when a disk is removed from the configuration file
    1592             :          * From SnapRAID 4.0 mappings are automatically removed if a disk is not used
    1593             :          * when saving the content file, but we keep this code to import older content files.
    1594             :          */
    1595        1879 :         for (i = state->maplist; i != 0; ) {
    1596        1577 :                 struct snapraid_map* map = i->data;
    1597             :                 struct snapraid_disk* disk;
    1598             : 
    1599        1577 :                 disk = find_disk_by_name(state, map->name);
    1600             : 
    1601             :                 /* go to the next mapping before removing */
    1602        1577 :                 i = i->next;
    1603             : 
    1604        1577 :                 if (disk == 0) {
    1605             :                         /* disk not found, remove the mapping */
    1606           0 :                         tommy_list_remove_existing(&state->maplist, &map->node);
    1607           0 :                         map_free(map);
    1608             :                 }
    1609             :         }
    1610             : 
    1611             :         /*
    1612             :          * Maps each unmapped disk present in the configuration file in the first available hole
    1613             :          * this happens when you add disks for the first time in the configuration file
    1614             :          */
    1615         302 :         hole = 0; /* first position to try */
    1616        2040 :         for (i = state->disklist; i != 0; i = i->next) {
    1617        1738 :                 struct snapraid_disk* disk = i->data;
    1618             :                 struct snapraid_map* map;
    1619             :                 tommy_node* j;
    1620             : 
    1621             :                 /* check if the disk is already mapped */
    1622        5967 :                 for (j = state->maplist; j != 0; j = j->next) {
    1623        5806 :                         map = j->data;
    1624        5806 :                         if (strcmp(disk->name, map->name) == 0) {
    1625             :                                 /* mapping found */
    1626        1577 :                                 break;
    1627             :                         }
    1628             :                 }
    1629        1738 :                 if (j != 0) {
    1630             :                         /* mapping is present, then copy the free blocks into to disk */
    1631        1577 :                         disk->total_blocks = map->total_blocks;
    1632        1577 :                         disk->free_blocks = map->free_blocks;
    1633        1577 :                         continue;
    1634             :                 }
    1635             : 
    1636             :                 /* mapping not found, search for an hole */
    1637             :                 while (1) {
    1638        1109 :                         for (j = state->maplist; j != 0; j = j->next) {
    1639         948 :                                 map = j->data;
    1640         948 :                                 if (map->position == hole) {
    1641             :                                         /* position already used */
    1642         160 :                                         break;
    1643             :                                 }
    1644             :                         }
    1645         321 :                         if (j == 0) {
    1646             :                                 /* hole found */
    1647         161 :                                 break;
    1648             :                         }
    1649             : 
    1650             :                         /* try with the next one */
    1651         160 :                         ++hole;
    1652             :                 }
    1653             : 
    1654             :                 /* insert the new mapping */
    1655         161 :                 map = map_alloc(disk->name, hole, 0, 0, "");
    1656             : 
    1657         161 :                 tommy_list_insert_tail(&state->maplist, &map->node, map);
    1658             :         }
    1659             : 
    1660             :         /* without configuration don't check for number of data disks or uuid changes */
    1661         302 :         if (state->no_conf)
    1662           1 :                 return;
    1663             : 
    1664             :         /* counter for the number of UUID mismatches */
    1665         301 :         uuid_mismatch = 0;
    1666             : 
    1667             :         /* check if mapping match the disk uuid */
    1668         301 :         if (!state->opt.skip_disk_access) {
    1669        1843 :                 for (i = state->maplist; i != 0; i = i->next) {
    1670        1570 :                         struct snapraid_map* map = i->data;
    1671             :                         struct snapraid_disk* disk;
    1672             : 
    1673        1570 :                         disk = find_disk_by_name(state, map->name);
    1674        1570 :                         if (disk == 0) {
    1675             :                                 /* LCOV_EXCL_START */
    1676             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Mapping not found for '%s'\n", map->name);
    1677             :                                 os_abort();
    1678             :                                 /* LCOV_EXCL_STOP */
    1679             :                         }
    1680             : 
    1681        1570 :                         if (disk->has_unsupported_uuid) {
    1682             :                                 /* if uuid is not available, skip this one */
    1683           1 :                                 continue;
    1684             :                         }
    1685             : 
    1686             :                         /* if the uuid is changed */
    1687        1569 :                         if (strcmp(disk->uuid, map->uuid) != 0) {
    1688             :                                 /* mark the disk as with an UUID change */
    1689         116 :                                 disk->has_different_uuid = 1;
    1690             : 
    1691             :                                 /* if the previous uuid is available */
    1692         116 :                                 if (map->uuid[0] != 0) {
    1693             :                                         /* count the number of uuid change */
    1694           4 :                                         ++uuid_mismatch;
    1695           4 :                                         log_fatal(ESOFT, "UUID change for disk '%s' from '%s' to '%s'\n", disk->name, map->uuid, disk->uuid);
    1696             :                                 } else {
    1697             :                                         /*
    1698             :                                          * No message here, because having a disk without
    1699             :                                          * UUID is the normal state of an empty disk
    1700             :                                          */
    1701         112 :                                         disk->had_empty_uuid = 1;
    1702             :                                 }
    1703             : 
    1704             :                                 /* update the uuid in the mapping, */
    1705         116 :                                 pathcpy(map->uuid, sizeof(map->uuid), disk->uuid);
    1706             : 
    1707             :                                 /* write the new state with the new uuid */
    1708         116 :                                 state->need_write = 1;
    1709             :                         }
    1710             :                 }
    1711             :         }
    1712             : 
    1713             :         /* check the parity uuid */
    1714         301 :         if (!state->opt.skip_parity_access) {
    1715        1419 :                 for (l = 0; l < state->level; ++l) {
    1716        5727 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
    1717             :                                 char uuid[UUID_MAX];
    1718             :                                 int ret;
    1719             : 
    1720        4569 :                                 ret = devuuid(state->parity[l].split_map[s].device, state->parity[l].split_map[s].path, uuid, sizeof(uuid));
    1721             : 
    1722        4569 :                                 if (ret != 0) {
    1723             :                                         /* uuid not available, just ignore */
    1724           4 :                                         continue;
    1725             :                                 }
    1726             : 
    1727             :                                 /* if the uuid is changed */
    1728        4565 :                                 if (strcmp(uuid, state->parity[l].split_map[s].uuid) != 0) {
    1729             :                                         /* if the previous uuid is available */
    1730           0 :                                         if (state->parity[l].split_map[s].uuid[0] != 0) {
    1731             :                                                 /* count the number of uuid change */
    1732           0 :                                                 ++uuid_mismatch;
    1733           0 :                                                 log_fatal(ESOFT, "UUID change for parity '%s/%u' from '%s' to '%s'\n", lev_config_name(l), s, state->parity[l].split_map[s].uuid, uuid);
    1734             :                                         }
    1735             : 
    1736             :                                         /* update the uuid */
    1737           0 :                                         pathcpy(state->parity[l].split_map[s].uuid, sizeof(state->parity[l].split_map[s].uuid), uuid);
    1738             : 
    1739             :                                         /* write the new state with the new uuid */
    1740           0 :                                         state->need_write = 1;
    1741             :                                 }
    1742             :                         }
    1743             :                 }
    1744             :         }
    1745             : 
    1746         301 :         if (!state->opt.force_uuid && uuid_mismatch > state->level) {
    1747             :                 /* LCOV_EXCL_START */
    1748             :                 log_fatal(ESOFT, "Too many disks have changed UUIDs since the last 'sync'.\n");
    1749             :                 log_fatal(ESOFT, "If this happened because you actually replaced them,\n");
    1750             :                 log_fatal(ESOFT, "you can still '%s', using 'snapraid --force-uuid %s'.\n", state->command, state->command);
    1751             :                 log_fatal(ESOFT, "Alternatively, you may have misconfigured the disk mount points,\n");
    1752             :                 log_fatal(ESOFT, "and you need to restore the mount points to the state of the latest sync.\n");
    1753             :                 exit(EXIT_FAILURE);
    1754             :                 /* LCOV_EXCL_STOP */
    1755             :         }
    1756             : 
    1757             :         /* count the number of data disks, including holes left after removing some */
    1758         301 :         diskcount = 0;
    1759        2033 :         for (i = state->maplist; i != 0; i = i->next) {
    1760        1732 :                 struct snapraid_map* map = i->data;
    1761             : 
    1762        1732 :                 if (map->position + 1 > diskcount)
    1763        1544 :                         diskcount = map->position + 1;
    1764             :         }
    1765             : 
    1766             :         /* ensure to don't go over the limit of the RAID engine */
    1767         301 :         if (diskcount > RAID_DATA_MAX) {
    1768             :                 /* LCOV_EXCL_START */
    1769             :                 log_fatal(ESOFT, "Too many data disks. Maximum allowed is %u.\n", RAID_DATA_MAX);
    1770             :                 exit(EXIT_FAILURE);
    1771             :                 /* LCOV_EXCL_STOP */
    1772             :         }
    1773             : 
    1774             :         /* now count the real number of data disks, excluding holes left after removing some */
    1775         301 :         diskcount = tommy_list_count(&state->maplist);
    1776             : 
    1777             :         /* recommend number of parities */
    1778         301 :         if (!state->opt.no_warnings) {
    1779             :                 /* LCOV_EXCL_START */
    1780             :                 if (diskcount >= 36 && state->level < 6) {
    1781             :                         log_error(EUSER, "WARNING! For %u disks, it's recommended to use six parity levels.\n", diskcount);
    1782             :                 } else if (diskcount >= 29 && state->level < 5) {
    1783             :                         log_error(EUSER, "WARNING! For %u disks, it's recommended to use five parity levels.\n", diskcount);
    1784             :                 } else if (diskcount >= 22 && state->level < 4) {
    1785             :                         log_error(EUSER, "WARNING! For %u disks, it's recommended to use four parity levels.\n", diskcount);
    1786             :                 } else if (diskcount >= 15 && state->level < 3) {
    1787             :                         log_error(EUSER, "WARNING! For %u disks, it's recommended to use three parity levels.\n", diskcount);
    1788             :                 } else if (diskcount >= 5 && state->level < 2) {
    1789             :                         log_error(EUSER, "WARNING! For %u disks, it's recommended to use two parity levels.\n", diskcount);
    1790             :                 }
    1791             :                 /* LCOV_EXCL_STOP */
    1792             :         }
    1793             : }
    1794             : 
    1795         234 : void state_refresh(struct snapraid_state* state)
    1796             : {
    1797             :         char esc_buffer[ESC_MAX];
    1798             :         tommy_node* i;
    1799             :         unsigned l, s;
    1800         234 :         uint64_t bs = (uint64_t)state->block_size;
    1801             : 
    1802             :         /* for all disks */
    1803        1572 :         for (i = state->maplist; i != 0; i = i->next) {
    1804        1338 :                 struct snapraid_map* map = i->data;
    1805             :                 struct snapraid_disk* disk;
    1806             :                 uint64_t total_space;
    1807             :                 uint64_t free_space;
    1808             :                 int ret;
    1809             : 
    1810        1338 :                 disk = find_disk_by_name(state, map->name);
    1811        1338 :                 if (disk == 0) {
    1812             :                         /* LCOV_EXCL_START */
    1813             :                         log_fatal(EINTERNAL, "Internal inconsistency: Mapping not found for '%s'\n", map->name);
    1814             :                         os_abort();
    1815             :                         /* LCOV_EXCL_STOP */
    1816             :                 }
    1817             : 
    1818        1338 :                 ret = fsinfo(disk->mount_point, 0, 0, &total_space, &free_space, disk->fstype, sizeof(disk->fstype), disk->fslabel, sizeof(disk->fslabel));
    1819        1338 :                 if (ret != 0) {
    1820             :                         /* LCOV_EXCL_START */
    1821             :                         log_fatal(errno, "Error accessing disk '%s' to retrieve file-system info. %s.\n", disk->mount_point, strerror(errno));
    1822             :                         exit(EXIT_FAILURE);
    1823             :                         /* LCOV_EXCL_STOP */
    1824             :                 }
    1825             : 
    1826             :                 /* set the new free blocks */
    1827        1338 :                 map->total_blocks = total_space / bs;
    1828        1338 :                 map->free_blocks = free_space / bs;
    1829             : 
    1830             :                 /* also update the disk info */
    1831        1338 :                 disk->total_blocks = map->total_blocks;
    1832        1338 :                 disk->free_blocks = map->free_blocks;
    1833             : 
    1834        1338 :                 log_tag("fsinfo_data_split:%s:%" PRIu64 ":%" PRIu64 ":%s:%s\n", esc_tag(disk->name, esc_buffer), disk->total_blocks * bs, disk->free_blocks * bs, disk->fstype, disk->fslabel);
    1835        1338 :                 log_tag("fsinfo_data:%s:%" PRIu64 ":%" PRIu64 "\n", esc_tag(disk->name, esc_buffer), disk->total_blocks * bs, disk->free_blocks * bs);
    1836             :         }
    1837             : 
    1838             :         /* for all parities */
    1839        1428 :         for (l = 0; l < state->level; ++l) {
    1840             :                 /* set the new free blocks */
    1841        1194 :                 state->parity[l].total_blocks = 0;
    1842        1194 :                 state->parity[l].free_blocks = 0;
    1843             : 
    1844        5907 :                 for (s = 0; s < state->parity[l].split_mac; ++s) {
    1845        4713 :                         struct snapraid_split* split = &state->parity[l].split_map[s];
    1846             :                         uint64_t total_space;
    1847             :                         uint64_t free_space;
    1848             :                         int ret;
    1849             : 
    1850        4713 :                         ret = fsinfo(split->path, 0, 0, &total_space, &free_space, split->fstype, sizeof(split->fstype), split->fslabel, sizeof(split->fslabel));
    1851        4713 :                         if (ret != 0) {
    1852             :                                 /* LCOV_EXCL_START */
    1853             :                                 log_fatal(errno, "Error accessing file '%s' to retrieve file-system info. %s.\n", split->path, strerror(errno));
    1854             :                                 exit(EXIT_FAILURE);
    1855             :                                 /* LCOV_EXCL_STOP */
    1856             :                         }
    1857             : 
    1858             : #ifdef WIN32
    1859             :                         /*
    1860             :                          * Take into account the space we have to leave free
    1861             :                          * to avoid the waring about low space
    1862             :                          */
    1863             :                         if (free_space >= WINDOWS_SPACEHOLDER_SIZE)
    1864             :                                 free_space -= WINDOWS_SPACEHOLDER_SIZE;
    1865             :                         else
    1866             :                                 free_space = 0;
    1867             : #endif
    1868             : 
    1869             :                         /* add the new free blocks */
    1870        4713 :                         uint64_t split_total_blocks = total_space / bs;
    1871        4713 :                         uint64_t split_free_blocks = free_space / bs;
    1872             : 
    1873        4713 :                         state->parity[l].total_blocks += split_total_blocks;
    1874        4713 :                         state->parity[l].free_blocks += split_free_blocks;
    1875             : 
    1876        4713 :                         if (s == 0)
    1877        1194 :                                 log_tag("fsinfo_parity_split:%s:%" PRIu64 ":%" PRIu64 ":%s:%s\n", lev_config_name(l), split_total_blocks * bs, split_free_blocks * bs, split->fstype, split->fslabel);
    1878             :                         else
    1879        3519 :                                 log_tag("fsinfo_parity_split:%s/%u:%" PRIu64 ":%" PRIu64 ":%s:%s\n", lev_config_name(l), s, split_total_blocks * bs, split_free_blocks * bs, split->fstype, split->fslabel);
    1880             :                 }
    1881             : 
    1882        1194 :                 log_tag("fsinfo_parity:%s:%" PRIu64 ":%" PRIu64 "\n", lev_config_name(l), state->parity[l].total_blocks * bs, state->parity[l].free_blocks * bs);
    1883             :         }
    1884             : 
    1885             :         /* for all extra disks */
    1886         234 :         for (i = state->extralist; i != 0; i = i->next) {
    1887           0 :                 struct snapraid_extra* extra = i->data;
    1888             :                 uint64_t total_space;
    1889             :                 uint64_t free_space;
    1890             :                 int ret;
    1891             : 
    1892           0 :                 ret = fsinfo(extra->dir, 0, 0, &total_space, &free_space, extra->fstype, sizeof(extra->fstype), extra->fslabel, sizeof(extra->fslabel));
    1893           0 :                 if (ret != 0) {
    1894             :                         /* LCOV_EXCL_START */
    1895             :                         log_fatal(errno, "Error accessing disk '%s' to retrieve file-system info. %s.\n", extra->dir, strerror(errno));
    1896             :                         exit(EXIT_FAILURE);
    1897             :                         /* LCOV_EXCL_STOP */
    1898             :                 }
    1899             : 
    1900           0 :                 log_tag("fsinfo_extra:%s:%" PRIu64 ":%" PRIu64 "\n", esc_tag(extra->name, esc_buffer), total_space, free_space);
    1901             :         }
    1902             : 
    1903             : 
    1904             :         /*
    1905             :          * Note what we don't set need_write = 1, because we don't want
    1906             :          * to update the content file only for the free space info.
    1907             :          */
    1908         234 : }
    1909             : 
    1910             : /**
    1911             :  * Check the content.
    1912             :  */
    1913         294 : static void state_content_check(struct snapraid_state* state, const char* path)
    1914             : {
    1915             :         tommy_node* i;
    1916             : 
    1917             :         /* check that any map has different name and position */
    1918        1987 :         for (i = state->maplist; i != 0; i = i->next) {
    1919        1693 :                 struct snapraid_map* map = i->data;
    1920             :                 tommy_node* j;
    1921        5814 :                 for (j = i->next; j != 0; j = j->next) {
    1922        4121 :                         struct snapraid_map* other = j->data;
    1923        4121 :                         if (strcmp(map->name, other->name) == 0) {
    1924             :                                 /* LCOV_EXCL_START */
    1925             :                                 log_fatal(ECONTENT, "Conflicting 'map' disk specification in '%s'\n", path);
    1926             :                                 exit(EXIT_FAILURE);
    1927             :                                 /* LCOV_EXCL_STOP */
    1928             :                         }
    1929        4121 :                         if (map->position == other->position) {
    1930             :                                 /* LCOV_EXCL_START */
    1931             :                                 log_fatal(ECONTENT, "Conflicting 'map' index specification in '%s'\n", path);
    1932             :                                 exit(EXIT_FAILURE);
    1933             :                                 /* LCOV_EXCL_STOP */
    1934             :                         }
    1935             :                 }
    1936             :         }
    1937         294 : }
    1938             : 
    1939             : /**
    1940             :  * Check if the position is REQUIRED, or we can completely clear it from the state.
    1941             :  *
    1942             :  * Note that position with only DELETED blocks are discharged.
    1943             :  */
    1944      847980 : static int fs_position_is_required(struct snapraid_state* state, block_off_t pos)
    1945             : {
    1946             :         tommy_node* i;
    1947             : 
    1948             :         /* check for each disk */
    1949      931194 :         for (i = state->disklist; i != 0; i = i->next) {
    1950      929316 :                 struct snapraid_disk* disk = i->data;
    1951      929316 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1952             : 
    1953             :                 /* if we have at least one file, the position is needed */
    1954      929316 :                 if (block_has_file(block))
    1955      846102 :                         return 1;
    1956             :         }
    1957             : 
    1958        1878 :         return 0;
    1959             : }
    1960             : 
    1961             : /**
    1962             :  * Check if the info block is REQUIREQ.
    1963             :  *
    1964             :  * This is used to ensure that we keep the last check used for scrubbing.
    1965             :  * and that we add it when importing old context files.
    1966             :  *
    1967             :  * Note that you can have position without info blocks, for example
    1968             :  * if all the blocks are not synced.
    1969             :  *
    1970             :  * Note also that not requiring an info block, doesn't mean that if present it
    1971             :  * can be discarded.
    1972             :  */
    1973     1681497 : static int fs_info_is_required(struct snapraid_state* state, block_off_t pos)
    1974             : {
    1975             :         tommy_node* i;
    1976             : 
    1977             :         /* check for each disk */
    1978     2218263 :         for (i = state->disklist; i != 0; i = i->next) {
    1979     2164381 :                 struct snapraid_disk* disk = i->data;
    1980     2164381 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1981             : 
    1982             :                 /* if we have at least one synced file, the info is required */
    1983     2164381 :                 if (block_state_get(block) == BLOCK_STATE_BLK)
    1984     1627615 :                         return 1;
    1985             :         }
    1986             : 
    1987       53882 :         return 0;
    1988             : }
    1989             : 
    1990        1878 : static void fs_position_clear_deleted(struct snapraid_state* state, block_off_t pos)
    1991             : {
    1992             :         tommy_node* i;
    1993             : 
    1994             :         /* check for each disk if block is really used */
    1995       13146 :         for (i = state->disklist; i != 0; i = i->next) {
    1996       11268 :                 struct snapraid_disk* disk = i->data;
    1997       11268 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1998             : 
    1999             :                 /* if the block is deleted */
    2000       11268 :                 if (block_state_get(block) == BLOCK_STATE_DELETED) {
    2001             :                         /* set it to empty */
    2002         939 :                         fs_deallocate(disk, pos);
    2003             :                 }
    2004             :         }
    2005        1878 : }
    2006             : 
    2007             : /**
    2008             :  * Check if a block position in a disk is deleted.
    2009             :  */
    2010     5032492 : static int fs_is_block_deleted(struct snapraid_disk* disk, block_off_t pos)
    2011             : {
    2012     5032492 :         struct snapraid_block* block = fs_par2block_find(disk, pos);
    2013             : 
    2014     5032492 :         return block_state_get(block) == BLOCK_STATE_DELETED;
    2015             : }
    2016             : 
    2017             : /**
    2018             :  * Check if a block parity needs sync
    2019             :  *
    2020             :  * This is the same logic used in "status" to detect an incomplete "sync",
    2021             :  * that ignores invalid block, if they are not used by a file in any disk.
    2022             :  *
    2023             :  * This means that DELETED blocks won't necessarily imply an invalid parity.
    2024             :  */
    2025     2529477 : static int fs_is_block_unsynced(struct snapraid_state* state, block_off_t pos)
    2026             : {
    2027     2529477 :         int one_invalid = 0;
    2028     2529477 :         int one_valid = 0;
    2029    16275696 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    2030    13984350 :                 struct snapraid_disk* disk = i->data;
    2031    13984350 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    2032             : 
    2033    13984350 :                 if (block_has_file(block))
    2034    13578578 :                         one_valid = 1;
    2035    13984350 :                 if (block_has_invalid_parity(block))
    2036      239319 :                         one_invalid = 1;
    2037             : 
    2038    13984350 :                 if (one_invalid && one_valid)
    2039      238131 :                         return 1;
    2040             :         }
    2041             : 
    2042     2291346 :         return 0;
    2043             : }
    2044             : 
    2045             : /**
    2046             :  * Flush the file checking the final CRC.
    2047             :  * We exploit the fact that the CRC is always stored in the last 4 bytes.
    2048             :  */
    2049           1 : static void decoding_error(const char* path, STREAM* f)
    2050             : {
    2051             :         unsigned char buf[4];
    2052             :         uint32_t crc_stored;
    2053             :         uint32_t crc_computed;
    2054             : 
    2055           1 :         if (seof(f)) {
    2056             :                 /* LCOV_EXCL_START */
    2057             :                 log_fatal(ECONTENT, "Unexpected end of content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    2058             :                 log_fatal(ECONTENT, "This content file is truncated. Please use an alternate copy.\n");
    2059             :                 exit(EXIT_FAILURE);
    2060             :                 /* LCOV_EXCL_STOP */
    2061             :         }
    2062             : 
    2063           1 :         if (serror(f)) {
    2064             :                 /* LCOV_EXCL_START */
    2065             :                 log_fatal(errno, "Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    2066             :                 exit(EXIT_FAILURE);
    2067             :                 /* LCOV_EXCL_STOP */
    2068             :         }
    2069             : 
    2070           1 :         log_fatal(ECONTENT, "Error decoding '%s' at offset %" PRIi64 "\n", path, stell(f));
    2071             : 
    2072           1 :         if (sdeplete(f, buf) != 0) {
    2073             :                 /* LCOV_EXCL_START */
    2074             :                 log_fatal(errno, "Failed to flush content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    2075             :                 exit(EXIT_FAILURE);
    2076             :                 /* LCOV_EXCL_STOP */
    2077             :         }
    2078             : 
    2079             :         /* get the stored crc from the last four bytes */
    2080           1 :         crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
    2081             : 
    2082             :         /* get the computed crc */
    2083           1 :         crc_computed = scrc(f);
    2084             : 
    2085             :         /* adjust the stored crc to include itself */
    2086           1 :         crc_stored = crc32c(crc_stored, buf, 4);
    2087             : 
    2088           1 :         if (crc_computed != crc_stored) {
    2089           1 :                 log_fatal(ECONTENT, "CRC mismatch in '%s'\n", path);
    2090           1 :                 log_fatal(ECONTENT, "This content file is damaged! Please use an alternate copy.\n");
    2091           1 :                 exit(EXIT_FAILURE);
    2092             :         } else {
    2093           0 :                 log_fatal(ECONTENT, "The CRC of the file is correct!\n");
    2094             :         }
    2095           0 : }
    2096             : 
    2097         295 : static void state_read_content(struct snapraid_state* state, const char* path, STREAM* f)
    2098             : {
    2099             :         char esc_buffer[ESC_MAX];
    2100             :         char esc_buffer1[ESC_MAX];
    2101             :         block_off_t blockmax;
    2102             :         unsigned count_file;
    2103             :         unsigned count_hardlink;
    2104             :         unsigned count_symlink;
    2105             :         unsigned count_dir;
    2106             :         unsigned count_bad;
    2107             :         unsigned count_rehash;
    2108             :         unsigned count_unsynced;
    2109             :         unsigned count_unscrubbed;
    2110             :         int crc_checked;
    2111             :         char buffer[PATH_MAX];
    2112             :         int ret;
    2113             :         tommy_array disk_mapping;
    2114             :         uint32_t mapping_max;
    2115             :         tommy_hashdyn bucket_hash;
    2116             : 
    2117         295 :         blockmax = 0;
    2118         295 :         count_file = 0;
    2119         295 :         count_hardlink = 0;
    2120         295 :         count_symlink = 0;
    2121         295 :         count_dir = 0;
    2122         295 :         count_bad = 0;
    2123         295 :         count_rehash = 0;
    2124         295 :         count_unsynced = 0;
    2125         295 :         count_unscrubbed = 0;
    2126         295 :         crc_checked = 0;
    2127         295 :         mapping_max = 0;
    2128         295 :         tommy_array_init(&disk_mapping);
    2129         295 :         tommy_hashdyn_init(&bucket_hash);
    2130             : 
    2131             :         /* mark all disks as single threads */
    2132        1988 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    2133        1693 :                 struct snapraid_disk* disk = i->data;
    2134        1693 :                 disk->single_thread = 1;
    2135             :         }
    2136             : 
    2137         295 :         ret = sread(f, buffer, 12);
    2138         295 :         if (ret < 0) {
    2139             :                 /* LCOV_EXCL_START */
    2140             :                 decoding_error(path, f);
    2141             :                 log_fatal(ECONTENT, "Invalid header!\n");
    2142             :                 os_abort();
    2143             :                 /* LCOV_EXCL_STOP */
    2144             :         }
    2145             : 
    2146             :         /*
    2147             :          * File format versions:
    2148             :          *  - SNAPCNT1/SnapRAID 4.0 First version.
    2149             :          *  - SNAPCNT2/SnapRAID 7.0 Adds entries 'M' and 'P', to add free_blocks support.
    2150             :          *    The previous 'm' entry is now deprecated, but supported for importing.
    2151             :          *    Similarly for text file, we add 'mapping' and 'parity' deprecating 'map'.
    2152             :          *  - SNAPCNT3/SnapRAID 11.0 Adds entry 'y' for hash size.
    2153             :          *  - SNAPCNT3/SnapRAID 11.0 Adds entry 'Q' for multi parity file.
    2154             :          *    The previous 'P' entry is now deprecated, but supported for importing.
    2155             :          *  - SNAPCNT4/SnapRAID 15.0 Adds entry 'd' for dealloc file.
    2156             :          */
    2157         295 :         if (memcmp(buffer, "SNAPCNT1\n\3\0\0", 12) != 0
    2158         295 :                 && memcmp(buffer, "SNAPCNT2\n\3\0\0", 12) != 0
    2159         295 :                 && memcmp(buffer, "SNAPCNT3\n\3\0\0", 12) != 0
    2160          24 :                 && memcmp(buffer, "SNAPCNT4\n\3\0\0", 12) != 0
    2161             :         ) {
    2162             :                 /* LCOV_EXCL_START */
    2163             :                 if (memcmp(buffer, "SNAPCNT", 7) != 0) {
    2164             :                         decoding_error(path, f);
    2165             :                         log_fatal(ECONTENT, "Invalid header!\n");
    2166             :                         os_abort();
    2167             :                 } else {
    2168             :                         log_fatal(ECONTENT, "The content file '%s' was generated with a newer version of SnapRAID!\n", path);
    2169             :                         exit(EXIT_FAILURE);
    2170             :                 }
    2171             :                 /* LCOV_EXCL_STOP */
    2172             :         }
    2173             : 
    2174     4037419 :         while (1) {
    2175             :                 int c;
    2176             : 
    2177             :                 /* read the command */
    2178     4037714 :                 c = sgetc(f);
    2179     4037714 :                 if (c == EOF) {
    2180         294 :                         break;
    2181             :                 }
    2182             : 
    2183     4037420 :                 if (c == 'f') {
    2184             :                         /* file */
    2185             :                         char sub[PATH_MAX];
    2186             :                         uint64_t v_size;
    2187             :                         uint64_t v_mtime_sec;
    2188             :                         uint32_t v_mtime_nsec;
    2189             :                         uint64_t v_inode;
    2190             :                         uint32_t v_idx;
    2191             :                         struct snapraid_file* file;
    2192             :                         struct snapraid_disk* disk;
    2193             :                         uint32_t mapping;
    2194             : 
    2195     3903454 :                         ret = sgetb32(f, &mapping);
    2196     3903454 :                         if (ret < 0 || mapping >= mapping_max) {
    2197             :                                 /* LCOV_EXCL_START */
    2198             :                                 decoding_error(path, f);
    2199             :                                 log_fatal(EINTERNAL, "Internal inconsistency: File mapping index out of range\n");
    2200             :                                 os_abort();
    2201             :                                 /* LCOV_EXCL_STOP */
    2202             :                         }
    2203     3903454 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2204             : 
    2205     3903454 :                         ret = sgetb64(f, &v_size);
    2206     3903454 :                         if (ret < 0) {
    2207             :                                 /* LCOV_EXCL_START */
    2208             :                                 decoding_error(path, f);
    2209             :                                 os_abort();
    2210             :                                 /* LCOV_EXCL_STOP */
    2211             :                         }
    2212             : 
    2213     3903454 :                         if (state->block_size == 0) {
    2214             :                                 /* LCOV_EXCL_START */
    2215             :                                 decoding_error(path, f);
    2216             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Zero blocksize\n");
    2217             :                                 exit(EXIT_FAILURE);
    2218             :                                 /* LCOV_EXCL_STOP */
    2219             :                         }
    2220             : 
    2221             :                         /* check for impossible file size to avoid to crash for a too big allocation */
    2222     3903454 :                         if (v_size / state->block_size > blockmax) {
    2223             :                                 /* LCOV_EXCL_START */
    2224             :                                 decoding_error(path, f);
    2225             :                                 log_fatal(EINTERNAL, "Internal inconsistency: File size too big!\n");
    2226             :                                 os_abort();
    2227             :                                 /* LCOV_EXCL_STOP */
    2228             :                         }
    2229             : 
    2230     3903454 :                         ret = sgetb64(f, &v_mtime_sec);
    2231     3903454 :                         if (ret < 0) {
    2232             :                                 /* LCOV_EXCL_START */
    2233             :                                 decoding_error(path, f);
    2234             :                                 os_abort();
    2235             :                                 /* LCOV_EXCL_STOP */
    2236             :                         }
    2237             : 
    2238     3903454 :                         ret = sgetb32(f, &v_mtime_nsec);
    2239     3903454 :                         if (ret < 0) {
    2240             :                                 /* LCOV_EXCL_START */
    2241             :                                 decoding_error(path, f);
    2242             :                                 os_abort();
    2243             :                                 /* LCOV_EXCL_STOP */
    2244             :                         }
    2245             : 
    2246             :                         /* STAT_NSEC_INVALID is encoded as 0 */
    2247     3903454 :                         if (v_mtime_nsec == 0)
    2248           0 :                                 v_mtime_nsec = STAT_NSEC_INVALID;
    2249             :                         else
    2250     3903454 :                                 --v_mtime_nsec;
    2251             : 
    2252     3903454 :                         ret = sgetb64(f, &v_inode);
    2253     3903454 :                         if (ret < 0) {
    2254             :                                 /* LCOV_EXCL_START */
    2255             :                                 decoding_error(path, f);
    2256             :                                 os_abort();
    2257             :                                 /* LCOV_EXCL_STOP */
    2258             :                         }
    2259             : 
    2260     3903454 :                         ret = sgetbs(f, sub, sizeof(sub));
    2261     3903454 :                         if (ret < 0) {
    2262             :                                 /* LCOV_EXCL_START */
    2263             :                                 decoding_error(path, f);
    2264             :                                 os_abort();
    2265             :                                 /* LCOV_EXCL_STOP */
    2266             :                         }
    2267     3903454 :                         if (!*sub) {
    2268             :                                 /* LCOV_EXCL_START */
    2269             :                                 decoding_error(path, f);
    2270             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Null file!\n");
    2271             :                                 os_abort();
    2272             :                                 /* LCOV_EXCL_STOP */
    2273             :                         }
    2274             : 
    2275             :                         /* allocate the file */
    2276     3903454 :                         file = file_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec, v_inode, 0);
    2277             : 
    2278             :                         /* insert the file in the file containers */
    2279     3903454 :                         tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
    2280     3903454 :                         tommy_hashdyn_insert(&disk->pathset, &file->pathset, file, file_path_hash(file->sub));
    2281     3903454 :                         tommy_hashdyn_insert(&disk->stampset, &file->stampset, file, file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec));
    2282     3903454 :                         tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
    2283             : 
    2284             :                         /* read all the blocks */
    2285     3903454 :                         v_idx = 0;
    2286     7828972 :                         while (v_idx < file->blockmax) {
    2287             :                                 block_off_t v_pos;
    2288             :                                 uint32_t v_count;
    2289             : 
    2290             :                                 /* get the "subcommand */
    2291     3925518 :                                 c = sgetc(f);
    2292             : 
    2293     3925518 :                                 ret = sgetb32(f, &v_pos);
    2294     3925518 :                                 if (ret < 0) {
    2295             :                                         /* LCOV_EXCL_START */
    2296             :                                         decoding_error(path, f);
    2297             :                                         os_abort();
    2298             :                                         /* LCOV_EXCL_STOP */
    2299             :                                 }
    2300             : 
    2301     3925518 :                                 ret = sgetb32(f, &v_count);
    2302     3925518 :                                 if (ret < 0) {
    2303             :                                         /* LCOV_EXCL_START */
    2304             :                                         decoding_error(path, f);
    2305             :                                         os_abort();
    2306             :                                         /* LCOV_EXCL_STOP */
    2307             :                                 }
    2308             : 
    2309     3925518 :                                 if (v_idx + v_count > file->blockmax) {
    2310             :                                         /* LCOV_EXCL_START */
    2311             :                                         decoding_error(path, f);
    2312             :                                         log_fatal(EINTERNAL, "Internal inconsistency: Block number out of range\n");
    2313             :                                         os_abort();
    2314             :                                         /* LCOV_EXCL_STOP */
    2315             :                                 }
    2316             : 
    2317     3925518 :                                 if (v_pos + v_count > blockmax) {
    2318             :                                         /* LCOV_EXCL_START */
    2319             :                                         decoding_error(path, f);
    2320             :                                         log_fatal(EINTERNAL, "Internal inconsistency: Block size %u/%u!\n", blockmax, v_pos + v_count);
    2321             :                                         os_abort();
    2322             :                                         /* LCOV_EXCL_START */
    2323             :                                 }
    2324             : 
    2325             :                                 /* fill the blocks in the run */
    2326             :                                 while (v_count) {
    2327             :                                         struct snapraid_block* block = fs_file2block_get(file, v_idx);
    2328             : 
    2329             :                                         switch (c) {
    2330             :                                         case 'b' :
    2331             :                                                 block_state_set(block, BLOCK_STATE_BLK);
    2332             :                                                 break;
    2333             :                                         case 'n' :
    2334             :                                                 /* deprecated NEW blocks are converted to CHG ones */
    2335             :                                                 block_state_set(block, BLOCK_STATE_CHG);
    2336             :                                                 break;
    2337             :                                         case 'g' :
    2338             :                                                 block_state_set(block, BLOCK_STATE_CHG);
    2339             :                                                 break;
    2340             :                                         case 'p' :
    2341             :                                                 block_state_set(block, BLOCK_STATE_REP);
    2342             :                                                 break;
    2343             :                                         default :
    2344             :                                                 /* LCOV_EXCL_START */
    2345             :                                                 decoding_error(path, f);
    2346             :                                                 log_fatal(ECONTENT, "Invalid block type!\n");
    2347             :                                                 os_abort();
    2348             :                                                 /* LCOV_EXCL_STOP */
    2349             :                                         }
    2350             : 
    2351             :                                         /* read the hash only for 'blk/chg/rep', and not for 'new' */
    2352     9542162 :                                         if (c != 'n') {
    2353     9542162 :                                                 ret = sread(f, block->hash, BLOCK_HASH_SIZE);
    2354     9542162 :                                                 if (ret < 0) {
    2355             :                                                         /* LCOV_EXCL_START */
    2356             :                                                         decoding_error(path, f);
    2357             :                                                         os_abort();
    2358             :                                                         /* LCOV_EXCL_STOP */
    2359             :                                                 }
    2360             :                                         } else {
    2361             :                                                 /* set the ZERO hash for deprecated NEW blocks */
    2362           0 :                                                 hash_zero_set(block->hash);
    2363             :                                         }
    2364             : 
    2365             :                                         /*
    2366             :                                          * If we are disabling the copy optimization
    2367             :                                          * we want also to clear any already previously stored information
    2368             :                                          * in other sync commands
    2369             :                                          */
    2370     9542162 :                                         if (state->opt.force_nocopy && block_state_get(block) == BLOCK_STATE_REP) {
    2371             :                                                 /* set the hash value to INVALID */
    2372           1 :                                                 hash_invalid_set(block->hash);
    2373             : 
    2374             :                                                 /* convert from REP to CHG block */
    2375           1 :                                                 block_state_set(block, BLOCK_STATE_CHG);
    2376             :                                         }
    2377             : 
    2378             :                                         /* set the parity association */
    2379     9542162 :                                         fs_allocate(disk, v_pos, file, v_idx);
    2380             : 
    2381             :                                         /* go to the next block */
    2382     9542162 :                                         ++v_idx;
    2383     9542162 :                                         ++v_pos;
    2384     9542162 :                                         --v_count;
    2385             :                                 }
    2386             :                         }
    2387             : 
    2388             :                         /* stat */
    2389     3903454 :                         ++count_file;
    2390      133966 :                 } else if (c == 'i') {
    2391             :                         /* "inf" command */
    2392             :                         snapraid_info info;
    2393             :                         uint32_t v_pos;
    2394             :                         uint64_t v_oldest;
    2395             : 
    2396         294 :                         ret = sgetb64(f, &v_oldest);
    2397         294 :                         if (ret < 0) {
    2398             :                                 /* LCOV_EXCL_START */
    2399             :                                 decoding_error(path, f);
    2400             :                                 os_abort();
    2401             :                                 /* LCOV_EXCL_STOP */
    2402             :                         }
    2403             : 
    2404         294 :                         v_pos = 0;
    2405       46885 :                         while (v_pos < blockmax) {
    2406             :                                 int bad;
    2407             :                                 int rehash;
    2408             :                                 int justsynced;
    2409             :                                 uint64_t t64;
    2410             :                                 uint32_t flag;
    2411             :                                 uint32_t v_count;
    2412             : 
    2413       46591 :                                 ret = sgetb32(f, &v_count);
    2414       46591 :                                 if (ret < 0) {
    2415             :                                         /* LCOV_EXCL_START */
    2416             :                                         decoding_error(path, f);
    2417             :                                         os_abort();
    2418             :                                         /* LCOV_EXCL_STOP */
    2419             :                                 }
    2420             : 
    2421       46591 :                                 if (v_pos + v_count > blockmax) {
    2422             :                                         /* LCOV_EXCL_START */
    2423             :                                         decoding_error(path, f);
    2424             :                                         log_fatal(EINTERNAL, "Internal inconsistency: Info size %u/%u!\n", blockmax, v_pos + v_count);
    2425             :                                         os_abort();
    2426             :                                         /* LCOV_EXCL_STOP */
    2427             :                                 }
    2428             : 
    2429       46591 :                                 ret = sgetb32(f, &flag);
    2430       46591 :                                 if (ret < 0) {
    2431             :                                         /* LCOV_EXCL_START */
    2432             :                                         decoding_error(path, f);
    2433             :                                         os_abort();
    2434             :                                         /* LCOV_EXCL_STOP */
    2435             :                                 }
    2436             : 
    2437             :                                 /* if there is an info */
    2438       46591 :                                 if ((flag & 1) != 0) {
    2439             :                                         /* read the time */
    2440       46574 :                                         ret = sgetb64(f, &t64);
    2441       46574 :                                         if (ret < 0) {
    2442             :                                                 /* LCOV_EXCL_START */
    2443             :                                                 decoding_error(path, f);
    2444             :                                                 os_abort();
    2445             :                                                 /* LCOV_EXCL_STOP */
    2446             :                                         }
    2447             : 
    2448             :                                         /* analyze the flags */
    2449       46574 :                                         bad = (flag & 2) != 0;
    2450       46574 :                                         rehash = (flag & 4) != 0;
    2451       46574 :                                         justsynced = (flag & 8) != 0;
    2452             : 
    2453       46574 :                                         if (bad)
    2454        5268 :                                                 count_bad += v_count;
    2455       46574 :                                         if (rehash)
    2456       14268 :                                                 count_rehash += v_count;
    2457       46574 :                                         if (justsynced)
    2458       21404 :                                                 count_unscrubbed += v_count;
    2459             : 
    2460       46574 :                                         if (rehash && state->prevhash == HASH_UNDEFINED) {
    2461             :                                                 /* LCOV_EXCL_START */
    2462             :                                                 decoding_error(path, f);
    2463             :                                                 log_fatal(EINTERNAL, "Internal inconsistency: Missing previous checksum!\n");
    2464             :                                                 os_abort();
    2465             :                                                 /* LCOV_EXCL_STOP */
    2466             :                                         }
    2467             : 
    2468       46574 :                                         info = info_make(t64 + v_oldest, bad, rehash, justsynced);
    2469             : 
    2470       46574 :                                         bucket_insert(&bucket_hash, t64 + v_oldest, v_count, justsynced);
    2471             :                                 } else {
    2472          17 :                                         info = 0;
    2473             :                                 }
    2474             : 
    2475     1728088 :                                 while (v_count) {
    2476             :                                         /* insert the info in the array */
    2477     1681497 :                                         info_set(&state->infoarr, v_pos, info);
    2478             : 
    2479             :                                         /* ensure that an info is present only for used positions */
    2480     1681497 :                                         if (fs_info_is_required(state, v_pos)) {
    2481     1627615 :                                                 if (!info) {
    2482             :                                                         /* LCOV_EXCL_START */
    2483             :                                                         decoding_error(path, f);
    2484             :                                                         log_fatal(EINTERNAL, "Internal inconsistency: Missing info!\n");
    2485             :                                                         os_abort();
    2486             :                                                         /* LCOV_EXCL_STOP */
    2487             :                                                 }
    2488             :                                         } else {
    2489             :                                                 /*
    2490             :                                                  * Extra info are accepted for backward compatibility
    2491             :                                                  * they are discarded at the first write
    2492             :                                                  */
    2493             :                                         }
    2494             : 
    2495     1681497 :                                         if (fs_is_block_unsynced(state, v_pos))
    2496      120017 :                                                 ++count_unsynced;
    2497             : 
    2498             :                                         /* go to next block */
    2499     1681497 :                                         ++v_pos;
    2500     1681497 :                                         --v_count;
    2501             :                                 }
    2502             :                         }
    2503      133672 :                 } else if (c == 'h') {
    2504             :                         /* hole */
    2505             :                         uint32_t v_pos;
    2506             :                         struct snapraid_disk* disk;
    2507             :                         uint32_t mapping;
    2508             : 
    2509        1577 :                         ret = sgetb32(f, &mapping);
    2510        1577 :                         if (ret < 0 || mapping >= mapping_max) {
    2511             :                                 /* LCOV_EXCL_START */
    2512             :                                 decoding_error(path, f);
    2513             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Hole mapping index out of range\n");
    2514             :                                 os_abort();
    2515             :                                 /* LCOV_EXCL_STOP */
    2516             :                         }
    2517        1577 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2518             : 
    2519        1577 :                         v_pos = 0;
    2520        3252 :                         while (v_pos < blockmax) {
    2521             :                                 uint32_t v_idx;
    2522             :                                 uint32_t v_count;
    2523             :                                 struct snapraid_file* deleted;
    2524             : 
    2525        1675 :                                 ret = sgetb32(f, &v_count);
    2526        1675 :                                 if (ret < 0) {
    2527             :                                         /* LCOV_EXCL_START */
    2528             :                                         decoding_error(path, f);
    2529             :                                         os_abort();
    2530             :                                         /* LCOV_EXCL_STOP */
    2531             :                                 }
    2532             : 
    2533        1675 :                                 if (v_pos + v_count > blockmax) {
    2534             :                                         /* LCOV_EXCL_START */
    2535             :                                         decoding_error(path, f);
    2536             :                                         log_fatal(EINTERNAL, "Internal inconsistency: Hole size %u/%u!\n", blockmax, v_pos + v_count);
    2537             :                                         os_abort();
    2538             :                                         /* LCOV_EXCL_STOP */
    2539             :                                 }
    2540             : 
    2541             :                                 /* get the sub-command */
    2542        1675 :                                 c = sgetc(f);
    2543             : 
    2544        1675 :                                 switch (c) {
    2545          51 :                                 case 'o' :
    2546             :                                         /* if it's a run of deleted blocks */
    2547             : 
    2548             :                                         /* allocate a fake deleted file */
    2549          51 :                                         deleted = file_alloc(state->block_size, "<deleted>", v_count * (data_off_t)state->block_size, 0, 0, 0, 0);
    2550             : 
    2551             :                                         /* mark the file as deleted */
    2552          51 :                                         file_flag_set(deleted, FILE_IS_DELETED);
    2553             : 
    2554             :                                         /* insert it in the list of deleted files */
    2555          51 :                                         tommy_list_insert_tail(&disk->deletedlist, &deleted->nodelist, deleted);
    2556             : 
    2557             :                                         /* process all blocks */
    2558          51 :                                         v_idx = 0;
    2559       29990 :                                         while (v_count) {
    2560       29939 :                                                 struct snapraid_block* block = fs_file2block_get(deleted, v_idx);
    2561             : 
    2562             :                                                 /* set the block as deleted */
    2563       29939 :                                                 block_state_set(block, BLOCK_STATE_DELETED);
    2564             : 
    2565             :                                                 /* read the hash */
    2566       29939 :                                                 ret = sread(f, block->hash, BLOCK_HASH_SIZE);
    2567       29939 :                                                 if (ret < 0) {
    2568             :                                                         /* LCOV_EXCL_START */
    2569             :                                                         decoding_error(path, f);
    2570             :                                                         os_abort();
    2571             :                                                         /* LCOV_EXCL_STOP */
    2572             :                                                 }
    2573             : 
    2574             :                                                 /* insert the block in the block array */
    2575       29939 :                                                 fs_allocate(disk, v_pos, deleted, v_idx);
    2576             : 
    2577             :                                                 /* go to next block */
    2578       29939 :                                                 ++v_pos;
    2579       29939 :                                                 ++v_idx;
    2580       29939 :                                                 --v_count;
    2581             :                                         }
    2582          51 :                                         break;
    2583        1624 :                                 case 'O' :
    2584             :                                         /* go to the next run */
    2585        1624 :                                         v_pos += v_count;
    2586        1624 :                                         break;
    2587           0 :                                 default :
    2588             :                                         /* LCOV_EXCL_START */
    2589             :                                         decoding_error(path, f);
    2590             :                                         log_fatal(ECONTENT, "Invalid hole type!\n");
    2591             :                                         os_abort();
    2592             :                                         /* LCOV_EXCL_STOP */
    2593             :                                 }
    2594             :                         }
    2595      132095 :                 } else if (c == 'd') {
    2596             :                         /* dealloc */
    2597             :                         uint32_t v_count;
    2598             :                         uint32_t v_pos;
    2599             :                         struct snapraid_disk* disk;
    2600             :                         uint32_t mapping;
    2601             : 
    2602          28 :                         ret = sgetb32(f, &mapping);
    2603          28 :                         if (ret < 0 || mapping >= mapping_max) {
    2604             :                                 /* LCOV_EXCL_START */
    2605             :                                 decoding_error(path, f);
    2606             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Dealloc mapping index out of range\n");
    2607             :                                 os_abort();
    2608             :                                 /* LCOV_EXCL_STOP */
    2609             :                         }
    2610          28 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2611             : 
    2612          28 :                         ret = sgetb32(f, &v_count);
    2613          28 :                         if (ret < 0) {
    2614             :                                 /* LCOV_EXCL_START */
    2615             :                                 decoding_error(path, f);
    2616             :                                 os_abort();
    2617             :                                 /* LCOV_EXCL_STOP */
    2618             :                         }
    2619             : 
    2620          28 :                         log_tag("content_info:dealloc:%s:%u\n", esc_tag(disk->name, esc_buffer), v_count);
    2621             : 
    2622       29334 :                         for (v_pos = 0; v_pos < v_count; ++v_pos) {
    2623             :                                 char sub[PATH_MAX];
    2624             :                                 uint64_t v_size;
    2625             :                                 uint64_t v_mtime_sec;
    2626             :                                 uint32_t v_mtime_nsec;
    2627             : 
    2628       29306 :                                 ret = sgetbs(f, sub, sizeof(sub));
    2629       29306 :                                 if (ret < 0) {
    2630             :                                         /* LCOV_EXCL_START */
    2631             :                                         decoding_error(path, f);
    2632             :                                         os_abort();
    2633             :                                         /* LCOV_EXCL_STOP */
    2634             :                                 }
    2635       29306 :                                 if (!*sub) {
    2636             :                                         /* LCOV_EXCL_START */
    2637             :                                         decoding_error(path, f);
    2638             :                                         log_fatal(EINTERNAL, "Internal inconsistency: Null dealloc!\n");
    2639             :                                         os_abort();
    2640             :                                         /* LCOV_EXCL_STOP */
    2641             :                                 }
    2642             : 
    2643       29306 :                                 ret = sgetb64(f, &v_size);
    2644       29306 :                                 if (ret < 0) {
    2645             :                                         /* LCOV_EXCL_START */
    2646             :                                         decoding_error(path, f);
    2647             :                                         os_abort();
    2648             :                                         /* LCOV_EXCL_STOP */
    2649             :                                 }
    2650             : 
    2651       29306 :                                 ret = sgetb64(f, &v_mtime_sec);
    2652       29306 :                                 if (ret < 0) {
    2653             :                                         /* LCOV_EXCL_START */
    2654             :                                         decoding_error(path, f);
    2655             :                                         os_abort();
    2656             :                                         /* LCOV_EXCL_STOP */
    2657             :                                 }
    2658             : 
    2659       29306 :                                 ret = sgetb32(f, &v_mtime_nsec);
    2660       29306 :                                 if (ret < 0) {
    2661             :                                         /* LCOV_EXCL_START */
    2662             :                                         decoding_error(path, f);
    2663             :                                         os_abort();
    2664             :                                         /* LCOV_EXCL_STOP */
    2665             :                                 }
    2666             : 
    2667             :                                 /* STAT_NSEC_INVALID is encoded as 0 */
    2668       29306 :                                 if (v_mtime_nsec == 0)
    2669           0 :                                         v_mtime_nsec = STAT_NSEC_INVALID;
    2670             :                                 else
    2671       29306 :                                         --v_mtime_nsec;
    2672             : 
    2673             :                                 /* allocate the file */
    2674       29306 :                                 struct snapraid_dealloc* dealloc = dealloc_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec);
    2675             : 
    2676       29306 :                                 log_tag("content_info:dealloc_entry:%s:%s:%" PRIu64 ":%" PRIu64 ":%u\n", esc_tag(disk->name, esc_buffer), esc_tag(dealloc->sub, esc_buffer1), dealloc->size, dealloc->mtime_sec, dealloc->mtime_nsec);
    2677             : 
    2678             :                                 /* read all hashes */
    2679       90342 :                                 for (block_off_t k = 0; k < dealloc->blockmax; ++k) {
    2680       61036 :                                         unsigned char* hash = dealloc->blockhash + k * BLOCK_HASH_SIZE;
    2681             : 
    2682       61036 :                                         ret = sread(f, hash, BLOCK_HASH_SIZE);
    2683       61036 :                                         if (ret < 0) {
    2684             :                                                 /* LCOV_EXCL_START */
    2685             :                                                 decoding_error(path, f);
    2686             :                                                 os_abort();
    2687             :                                                 /* LCOV_EXCL_STOP */
    2688             :                                         }
    2689             :                                 }
    2690             : 
    2691             :                                 /* insert the dealloc in the dealloc containers */
    2692       29306 :                                 tommy_list_insert_tail(&disk->dealloclist, &dealloc->nodelist, dealloc);
    2693             :                         }
    2694      132067 :                 } else if (c == 's') {
    2695             :                         /* symlink */
    2696             :                         char sub[PATH_MAX];
    2697             :                         char linkto[PATH_MAX];
    2698             :                         struct snapraid_link* slink;
    2699             :                         struct snapraid_disk* disk;
    2700             :                         uint32_t mapping;
    2701             : 
    2702      125957 :                         ret = sgetb32(f, &mapping);
    2703      125957 :                         if (ret < 0 || mapping >= mapping_max) {
    2704             :                                 /* LCOV_EXCL_START */
    2705             :                                 decoding_error(path, f);
    2706             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Symlink mapping index out of range\n");
    2707             :                                 os_abort();
    2708             :                                 /* LCOV_EXCL_STOP */
    2709             :                         }
    2710      125957 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2711             : 
    2712      125957 :                         ret = sgetbs(f, sub, sizeof(sub));
    2713      125957 :                         if (ret < 0) {
    2714             :                                 /* LCOV_EXCL_START */
    2715             :                                 decoding_error(path, f);
    2716             :                                 os_abort();
    2717             :                                 /* LCOV_EXCL_STOP */
    2718             :                         }
    2719             : 
    2720      125957 :                         if (!*sub) {
    2721             :                                 /* LCOV_EXCL_START */
    2722             :                                 decoding_error(path, f);
    2723             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Null symlink!\n");
    2724             :                                 os_abort();
    2725             :                                 /* LCOV_EXCL_STOP */
    2726             :                         }
    2727             : 
    2728      125957 :                         ret = sgetbs(f, linkto, sizeof(linkto));
    2729      125957 :                         if (ret < 0) {
    2730             :                                 /* LCOV_EXCL_START */
    2731             :                                 decoding_error(path, f);
    2732             :                                 os_abort();
    2733             :                                 /* LCOV_EXCL_STOP */
    2734             :                         }
    2735             : 
    2736             :                         /* allocate the link as symbolic link */
    2737      125957 :                         slink = link_alloc(sub, linkto, FILE_IS_SYMLINK);
    2738             : 
    2739             :                         /* insert the link in the link containers */
    2740      125957 :                         tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
    2741      125957 :                         tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
    2742             : 
    2743             :                         /* stat */
    2744      125957 :                         ++count_symlink;
    2745        6110 :                 } else if (c == 'a') {
    2746             :                         /* hardlink */
    2747             :                         char sub[PATH_MAX];
    2748             :                         char linkto[PATH_MAX];
    2749             :                         struct snapraid_link* slink;
    2750             :                         struct snapraid_disk* disk;
    2751             :                         uint32_t mapping;
    2752             : 
    2753         651 :                         ret = sgetb32(f, &mapping);
    2754         651 :                         if (ret < 0 || mapping >= mapping_max) {
    2755             :                                 /* LCOV_EXCL_START */
    2756             :                                 decoding_error(path, f);
    2757             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Hardlink mapping index out of range!\n");
    2758             :                                 os_abort();
    2759             :                                 /* LCOV_EXCL_STOP */
    2760             :                         }
    2761         651 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2762             : 
    2763         651 :                         ret = sgetbs(f, sub, sizeof(sub));
    2764         651 :                         if (ret < 0) {
    2765             :                                 /* LCOV_EXCL_START */
    2766             :                                 decoding_error(path, f);
    2767             :                                 os_abort();
    2768             :                                 /* LCOV_EXCL_STOP */
    2769             :                         }
    2770             : 
    2771         651 :                         if (!*sub) {
    2772             :                                 /* LCOV_EXCL_START */
    2773             :                                 decoding_error(path, f);
    2774             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Null hardlink!\n");
    2775             :                                 os_abort();
    2776             :                                 /* LCOV_EXCL_STOP */
    2777             :                         }
    2778             : 
    2779         651 :                         ret = sgetbs(f, linkto, sizeof(linkto));
    2780         651 :                         if (ret < 0) {
    2781             :                                 /* LCOV_EXCL_START */
    2782             :                                 decoding_error(path, f);
    2783             :                                 os_abort();
    2784             :                                 /* LCOV_EXCL_STOP */
    2785             :                         }
    2786             : 
    2787         651 :                         if (!*linkto) {
    2788             :                                 /* LCOV_EXCL_START */
    2789             :                                 decoding_error(path, f);
    2790             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Empty hardlink '%s'!\n", sub);
    2791             :                                 os_abort();
    2792             :                                 /* LCOV_EXCL_STOP */
    2793             :                         }
    2794             : 
    2795             :                         /* allocate the link as hard link */
    2796         651 :                         slink = link_alloc(sub, linkto, FILE_IS_HARDLINK);
    2797             : 
    2798             :                         /* insert the link in the link containers */
    2799         651 :                         tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
    2800         651 :                         tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
    2801             : 
    2802             :                         /* stat */
    2803         651 :                         ++count_hardlink;
    2804        5459 :                 } else if (c == 'r') {
    2805             :                         /* dir */
    2806             :                         char sub[PATH_MAX];
    2807             :                         struct snapraid_dir* dir;
    2808             :                         struct snapraid_disk* disk;
    2809             :                         uint32_t mapping;
    2810             : 
    2811         862 :                         ret = sgetb32(f, &mapping);
    2812         862 :                         if (ret < 0 || mapping >= mapping_max) {
    2813             :                                 /* LCOV_EXCL_START */
    2814             :                                 decoding_error(path, f);
    2815             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Dir mapping index out of range!\n");
    2816             :                                 os_abort();
    2817             :                                 /* LCOV_EXCL_STOP */
    2818             :                         }
    2819         862 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2820             : 
    2821         862 :                         ret = sgetbs(f, sub, sizeof(sub));
    2822         862 :                         if (ret < 0) {
    2823             :                                 /* LCOV_EXCL_START */
    2824             :                                 decoding_error(path, f);
    2825             :                                 os_abort();
    2826             :                                 /* LCOV_EXCL_STOP */
    2827             :                         }
    2828             : 
    2829         862 :                         if (!*sub) {
    2830             :                                 /* LCOV_EXCL_START */
    2831             :                                 decoding_error(path, f);
    2832             :                                 log_fatal(EINTERNAL, "Internal inconsistency: Null dir!\n");
    2833             :                                 os_abort();
    2834             :                                 /* LCOV_EXCL_STOP */
    2835             :                         }
    2836             : 
    2837             :                         /* allocate the dir */
    2838         862 :                         dir = dir_alloc(sub);
    2839             : 
    2840             :                         /* insert the dir in the dir containers */
    2841         862 :                         tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
    2842         862 :                         tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
    2843             : 
    2844             :                         /* stat */
    2845         862 :                         ++count_dir;
    2846        4597 :                 } else if (c == 'c') {
    2847             :                         /* get the subcommand */
    2848         295 :                         c = sgetc(f);
    2849             : 
    2850         295 :                         switch (c) {
    2851         158 :                         case 'u' :
    2852         158 :                                 state->hash = HASH_MURMUR3;
    2853         158 :                                 break;
    2854         137 :                         case 'k' :
    2855         137 :                                 state->hash = HASH_SPOOKY2;
    2856         137 :                                 break;
    2857           0 :                         case 'm' :
    2858           0 :                                 state->hash = HASH_METRO;
    2859           0 :                                 break;
    2860           0 :                         default :
    2861             :                                 /* LCOV_EXCL_START */
    2862             :                                 decoding_error(path, f);
    2863             :                                 log_fatal(ECONTENT, "Invalid checksum!\n");
    2864             :                                 os_abort();
    2865             :                                 /* LCOV_EXCL_STOP */
    2866             :                         }
    2867             : 
    2868             :                         /* read the seed */
    2869         295 :                         ret = sread(f, state->hashseed, HASH_MAX);
    2870         295 :                         if (ret < 0) {
    2871             :                                 /* LCOV_EXCL_START */
    2872             :                                 decoding_error(path, f);
    2873             :                                 os_abort();
    2874             :                                 /* LCOV_EXCL_STOP */
    2875             :                         }
    2876        4302 :                 } else if (c == 'C') {
    2877             :                         /* get the sub-command */
    2878           4 :                         c = sgetc(f);
    2879             : 
    2880           4 :                         switch (c) {
    2881           4 :                         case 'u' :
    2882           4 :                                 state->prevhash = HASH_MURMUR3;
    2883           4 :                                 break;
    2884           0 :                         case 'k' :
    2885           0 :                                 state->prevhash = HASH_SPOOKY2;
    2886           0 :                                 break;
    2887           0 :                         case 'm' :
    2888           0 :                                 state->prevhash = HASH_METRO;
    2889           0 :                                 break;
    2890           0 :                         default :
    2891             :                                 /* LCOV_EXCL_START */
    2892             :                                 decoding_error(path, f);
    2893             :                                 log_fatal(ECONTENT, "Invalid checksum!\n");
    2894             :                                 os_abort();
    2895             :                                 /* LCOV_EXCL_STOP */
    2896             :                         }
    2897             : 
    2898             :                         /* read the seed */
    2899           4 :                         ret = sread(f, state->prevhashseed, HASH_MAX);
    2900           4 :                         if (ret < 0) {
    2901             :                                 /* LCOV_EXCL_START */
    2902             :                                 decoding_error(path, f);
    2903             :                                 os_abort();
    2904             :                                 /* LCOV_EXCL_STOP */
    2905             :                         }
    2906        4298 :                 } else if (c == 'z') {
    2907             :                         uint32_t block_size;
    2908             : 
    2909         295 :                         ret = sgetb32(f, &block_size);
    2910         295 :                         if (ret < 0) {
    2911             :                                 /* LCOV_EXCL_START */
    2912             :                                 decoding_error(path, f);
    2913             :                                 os_abort();
    2914             :                                 /* LCOV_EXCL_STOP */
    2915             :                         }
    2916             : 
    2917         295 :                         if (block_size == 0) {
    2918             :                                 /* LCOV_EXCL_START */
    2919             :                                 decoding_error(path, f);
    2920             :                                 log_fatal(ECONTENT, "Zero 'blocksize' specification in the content file!\n");
    2921             :                                 exit(EXIT_FAILURE);
    2922             :                                 /* LCOV_EXCL_STOP */
    2923             :                         }
    2924             : 
    2925             :                         /* without configuration, auto assign the block size */
    2926         295 :                         if (state->no_conf) {
    2927           1 :                                 state->block_size = block_size;
    2928             :                         }
    2929             : 
    2930         295 :                         if (block_size != state->block_size) {
    2931             :                                 /* LCOV_EXCL_START */
    2932             :                                 decoding_error(path, f);
    2933             :                                 log_fatal(EUSER, "Mismatching 'blocksize' specification in the content file!\n");
    2934             :                                 log_fatal(EUSER, "Please restore the 'blocksize' value in the configuration file to '%u'\n", block_size / KIBI);
    2935             :                                 exit(EXIT_FAILURE);
    2936             :                                 /* LCOV_EXCL_STOP */
    2937             :                         }
    2938        4003 :                 } else if (c == 'y') {
    2939             :                         uint32_t hash_size;
    2940             : 
    2941         295 :                         ret = sgetb32(f, &hash_size);
    2942         295 :                         if (ret < 0) {
    2943             :                                 /* LCOV_EXCL_START */
    2944             :                                 decoding_error(path, f);
    2945             :                                 os_abort();
    2946             :                                 /* LCOV_EXCL_STOP */
    2947             :                         }
    2948             : 
    2949         295 :                         if (hash_size < 2 || hash_size > HASH_MAX) {
    2950             :                                 /* LCOV_EXCL_START */
    2951             :                                 decoding_error(path, f);
    2952             :                                 log_fatal(ECONTENT, "Invalid 'hashsize' specification in the content file!\n");
    2953             :                                 exit(EXIT_FAILURE);
    2954             :                                 /* LCOV_EXCL_STOP */
    2955             :                         }
    2956             : 
    2957             :                         /* without configuration, auto assign the block size */
    2958         295 :                         if (state->no_conf) {
    2959           1 :                                 BLOCK_HASH_SIZE = hash_size;
    2960             :                         }
    2961             : 
    2962         295 :                         if ((int)hash_size != BLOCK_HASH_SIZE) {
    2963             :                                 /* LCOV_EXCL_START */
    2964             :                                 decoding_error(path, f);
    2965             :                                 log_fatal(EUSER, "Mismatching 'hashsize' specification in the content file!\n");
    2966             :                                 log_fatal(EUSER, "Please restore the 'hashsize' value in the configuration file to '%u'\n", hash_size);
    2967             :                                 exit(EXIT_FAILURE);
    2968             :                                 /* LCOV_EXCL_STOP */
    2969             :                         }
    2970        3708 :                 } else if (c == 'x') {
    2971         295 :                         ret = sgetb32(f, &blockmax);
    2972         295 :                         if (ret < 0) {
    2973             :                                 /* LCOV_EXCL_START */
    2974             :                                 decoding_error(path, f);
    2975             :                                 os_abort();
    2976             :                                 /* LCOV_EXCL_STOP */
    2977             :                         }
    2978        3413 :                 } else if (c == 'm' || c == 'M') {
    2979             :                         struct snapraid_map* map;
    2980             :                         char uuid[UUID_MAX];
    2981             :                         uint32_t v_pos;
    2982             :                         uint32_t v_total_blocks;
    2983             :                         uint32_t v_free_blocks;
    2984             :                         struct snapraid_disk* disk;
    2985             : 
    2986        1583 :                         ret = sgetbs(f, buffer, sizeof(buffer));
    2987        1583 :                         if (ret < 0) {
    2988             :                                 /* LCOV_EXCL_START */
    2989             :                                 decoding_error(path, f);
    2990             :                                 os_abort();
    2991             :                                 /* LCOV_EXCL_STOP */
    2992             :                         }
    2993             : 
    2994        1583 :                         ret = sgetb32(f, &v_pos);
    2995        1583 :                         if (ret < 0) {
    2996             :                                 /* LCOV_EXCL_START */
    2997             :                                 decoding_error(path, f);
    2998             :                                 os_abort();
    2999             :                                 /* LCOV_EXCL_STOP */
    3000             :                         }
    3001             : 
    3002             :                         /* from SnapRAID 7.0 the 'M' command includes the free space */
    3003        1583 :                         if (c == 'M') {
    3004        1583 :                                 ret = sgetb32(f, &v_total_blocks);
    3005        1583 :                                 if (ret < 0) {
    3006             :                                         /* LCOV_EXCL_START */
    3007             :                                         decoding_error(path, f);
    3008             :                                         os_abort();
    3009             :                                         /* LCOV_EXCL_STOP */
    3010             :                                 }
    3011             : 
    3012        1583 :                                 ret = sgetb32(f, &v_free_blocks);
    3013        1583 :                                 if (ret < 0) {
    3014             :                                         /* LCOV_EXCL_START */
    3015             :                                         decoding_error(path, f);
    3016             :                                         os_abort();
    3017             :                                         /* LCOV_EXCL_STOP */
    3018             :                                 }
    3019             : 
    3020        1583 :                                 log_tag("content_data:%s:%" PRIi64 ":%" PRIi64 "\n",
    3021             :                                         esc_tag(buffer, esc_buffer),
    3022        1583 :                                         v_total_blocks * (uint64_t)state->block_size,
    3023        1583 :                                         v_free_blocks * (uint64_t)state->block_size);
    3024             :                         } else {
    3025           0 :                                 v_total_blocks = 0;
    3026           0 :                                 v_free_blocks = 0;
    3027             :                         }
    3028             : 
    3029             :                         /* read the uuid */
    3030        1583 :                         ret = sgetbs(f, uuid, sizeof(uuid));
    3031        1583 :                         if (ret < 0) {
    3032             :                                 /* LCOV_EXCL_START */
    3033             :                                 decoding_error(path, f);
    3034             :                                 os_abort();
    3035             :                                 /* LCOV_EXCL_STOP */
    3036             :                         }
    3037             : 
    3038        1583 :                         log_tag("content_data_split:%s:%s\n",
    3039             :                                 esc_tag(buffer, esc_buffer),
    3040             :                                 uuid);
    3041             : 
    3042             :                         /* find the disk */
    3043        1583 :                         disk = find_disk_by_name(state, buffer);
    3044        1583 :                         if (!disk) {
    3045             :                                 /* search by UUID if renamed */
    3046           2 :                                 disk = find_disk_by_uuid(state, uuid);
    3047           2 :                                 if (disk) {
    3048           2 :                                         log_fatal(EUSER, "WARNING! Renaming disk '%s' to '%s'\n", buffer, disk->name);
    3049             : 
    3050             :                                         /* write the new state with the new name */
    3051           2 :                                         state->need_write = 1;
    3052             :                                 }
    3053             :                         }
    3054        1583 :                         if (!disk) {
    3055             :                                 /* LCOV_EXCL_START */
    3056             :                                 decoding_error(path, f);
    3057             :                                 log_fatal(EUSER, "Disk '%s' with uuid '%s' not present in the configuration file!\n", buffer, uuid);
    3058             :                                 log_fatal(EUSER, "If you have removed it from the configuration file, please restore it\n");
    3059             :                                 /* if it's a command without UUID, it cannot autorename using UUID */
    3060             :                                 if (state->opt.skip_disk_access)
    3061             :                                         log_fatal(EUSER, "If you have renamed it, run 'sync' to update the new name\n");
    3062             :                                 exit(EXIT_FAILURE);
    3063             :                                 /* LCOV_EXCL_STOP */
    3064             :                         }
    3065             : 
    3066        1583 :                         map = map_alloc(disk->name, v_pos, v_total_blocks, v_free_blocks, uuid);
    3067             : 
    3068        1583 :                         tommy_list_insert_tail(&state->maplist, &map->node, map);
    3069             : 
    3070             :                         /* insert in the mapping vector */
    3071        1583 :                         tommy_array_grow(&disk_mapping, mapping_max + 1);
    3072        1583 :                         tommy_array_set(&disk_mapping, mapping_max, disk);
    3073        1583 :                         ++mapping_max;
    3074        1830 :                 } else if (c == 'P') {
    3075             :                         /*
    3076             :                          * From SnapRAID 7.0 the 'P' command includes the free space
    3077             :                          * from SnapRAID 11.0 the 'P' command is deprecated by 'Q'
    3078             :                          */
    3079             :                         char v_uuid[UUID_MAX];
    3080             :                         uint32_t v_level;
    3081             :                         uint32_t v_total_blocks;
    3082             :                         uint32_t v_free_blocks;
    3083             : 
    3084           0 :                         ret = sgetb32(f, &v_level);
    3085           0 :                         if (ret < 0) {
    3086             :                                 /* LCOV_EXCL_START */
    3087             :                                 decoding_error(path, f);
    3088             :                                 os_abort();
    3089             :                                 /* LCOV_EXCL_STOP */
    3090             :                         }
    3091             : 
    3092           0 :                         ret = sgetb32(f, &v_total_blocks);
    3093           0 :                         if (ret < 0) {
    3094             :                                 /* LCOV_EXCL_START */
    3095             :                                 decoding_error(path, f);
    3096             :                                 os_abort();
    3097             :                                 /* LCOV_EXCL_STOP */
    3098             :                         }
    3099             : 
    3100           0 :                         ret = sgetb32(f, &v_free_blocks);
    3101           0 :                         if (ret < 0) {
    3102             :                                 /* LCOV_EXCL_START */
    3103             :                                 decoding_error(path, f);
    3104             :                                 os_abort();
    3105             :                                 /* LCOV_EXCL_STOP */
    3106             :                         }
    3107             : 
    3108           0 :                         ret = sgetbs(f, v_uuid, sizeof(v_uuid));
    3109           0 :                         if (ret < 0) {
    3110             :                                 /* LCOV_EXCL_START */
    3111             :                                 decoding_error(path, f);
    3112             :                                 os_abort();
    3113             :                                 /* LCOV_EXCL_STOP */
    3114             :                         }
    3115             : 
    3116           0 :                         if (v_level >= LEV_MAX) {
    3117             :                                 /* LCOV_EXCL_START */
    3118             :                                 decoding_error(path, f);
    3119             :                                 log_fatal(ECONTENT, "Invalid parity level '%u' in the configuration file!\n", v_level);
    3120             :                                 exit(EXIT_FAILURE);
    3121             :                                 /* LCOV_EXCL_STOP */
    3122             :                         }
    3123             : 
    3124           0 :                         log_tag("content_parity:%s:%" PRIi64 ":%" PRIi64 "\n",
    3125             :                                 lev_config_name(v_level),
    3126           0 :                                 v_total_blocks * (uint64_t)state->block_size,
    3127           0 :                                 v_free_blocks * (uint64_t)state->block_size);
    3128             : 
    3129             :                         /* auto configure if configuration is missing */
    3130           0 :                         if (state->no_conf) {
    3131           0 :                                 if (v_level >= state->level)
    3132           0 :                                         state->level = v_level + 1;
    3133             :                         }
    3134             : 
    3135             :                         /* if we use this parity entry */
    3136           0 :                         if (v_level < state->level) {
    3137             :                                 /* if the configuration has more splits, keep them */
    3138           0 :                                 if (state->parity[v_level].split_mac < 1)
    3139           0 :                                         state->parity[v_level].split_mac = 1;
    3140             :                                 /* set the parity info */
    3141           0 :                                 pathcpy(state->parity[v_level].split_map[0].uuid, sizeof(state->parity[v_level].split_map[0].uuid), v_uuid);
    3142           0 :                                 state->parity[v_level].total_blocks = v_total_blocks;
    3143           0 :                                 state->parity[v_level].free_blocks = v_free_blocks;
    3144             : 
    3145           0 :                                 log_tag("content_parity_split:%s:%s:%s:%" PRIi64 "\n",
    3146             :                                         lev_config_name(v_level), /* P command always has a single split */
    3147           0 :                                         state->parity[v_level].split_map[0].uuid,
    3148           0 :                                         esc_tag(state->parity[v_level].split_map[0].path, esc_buffer),
    3149             :                                         state->parity[v_level].split_map[0].size);
    3150             :                         }
    3151        1830 :                 } else if (c == 'Q') {
    3152             :                         /* from SnapRAID 11.0 the 'Q' command include size info and multi file support  */
    3153             :                         uint32_t v_level;
    3154             :                         uint32_t v_total_blocks;
    3155             :                         uint32_t v_free_blocks;
    3156             :                         uint32_t v_split_mac;
    3157             :                         unsigned s;
    3158             : 
    3159        1535 :                         ret = sgetb32(f, &v_level);
    3160        1535 :                         if (ret < 0) {
    3161             :                                 /* LCOV_EXCL_START */
    3162             :                                 decoding_error(path, f);
    3163             :                                 os_abort();
    3164             :                                 /* LCOV_EXCL_STOP */
    3165             :                         }
    3166             : 
    3167        1535 :                         ret = sgetb32(f, &v_total_blocks);
    3168        1535 :                         if (ret < 0) {
    3169             :                                 /* LCOV_EXCL_START */
    3170             :                                 decoding_error(path, f);
    3171             :                                 os_abort();
    3172             :                                 /* LCOV_EXCL_STOP */
    3173             :                         }
    3174             : 
    3175        1535 :                         ret = sgetb32(f, &v_free_blocks);
    3176        1535 :                         if (ret < 0) {
    3177             :                                 /* LCOV_EXCL_START */
    3178             :                                 decoding_error(path, f);
    3179             :                                 os_abort();
    3180             :                                 /* LCOV_EXCL_STOP */
    3181             :                         }
    3182             : 
    3183        1535 :                         ret = sgetb32(f, &v_split_mac);
    3184        1535 :                         if (ret < 0) {
    3185             :                                 /* LCOV_EXCL_START */
    3186             :                                 decoding_error(path, f);
    3187             :                                 os_abort();
    3188             :                                 /* LCOV_EXCL_STOP */
    3189             :                         }
    3190             : 
    3191        1535 :                         if (v_level >= LEV_MAX) {
    3192             :                                 /* LCOV_EXCL_START */
    3193             :                                 decoding_error(path, f);
    3194             :                                 log_fatal(ECONTENT, "Invalid parity level '%u' in the configuration file!\n", v_level);
    3195             :                                 exit(EXIT_FAILURE);
    3196             :                                 /* LCOV_EXCL_STOP */
    3197             :                         }
    3198             : 
    3199             :                         /* auto configure if configuration is missing */
    3200        1535 :                         if (state->no_conf) {
    3201           6 :                                 if (v_level >= state->level)
    3202           5 :                                         state->level = v_level + 1;
    3203           6 :                                 if (state->parity[v_level].split_mac < v_split_mac)
    3204           6 :                                         state->parity[v_level].split_mac = v_split_mac;
    3205             :                         }
    3206             : 
    3207             :                         /* if we use this parity entry */
    3208        1535 :                         if (v_level < state->level) {
    3209             :                                 /* set the parity info */
    3210        1267 :                                 state->parity[v_level].total_blocks = v_total_blocks;
    3211        1267 :                                 state->parity[v_level].free_blocks = v_free_blocks;
    3212             :                         }
    3213             : 
    3214        1535 :                         log_tag("content_parity:%s:%" PRIi64 ":%" PRIi64 "\n",
    3215             :                                 lev_config_name(v_level),
    3216        1535 :                                 v_total_blocks * (uint64_t)state->block_size,
    3217        1535 :                                 v_free_blocks * (uint64_t)state->block_size);
    3218             : 
    3219        7609 :                         for (s = 0; s < v_split_mac; ++s) {
    3220             :                                 char v_path[PATH_MAX];
    3221             :                                 char v_uuid[UUID_MAX];
    3222             :                                 uint64_t v_size;
    3223             : 
    3224        6074 :                                 ret = sgetbs(f, v_path, sizeof(v_path));
    3225        6074 :                                 if (ret < 0) {
    3226             :                                         /* LCOV_EXCL_START */
    3227             :                                         decoding_error(path, f);
    3228             :                                         os_abort();
    3229             :                                         /* LCOV_EXCL_STOP */
    3230             :                                 }
    3231             : 
    3232        6074 :                                 ret = sgetbs(f, v_uuid, sizeof(v_uuid));
    3233        6074 :                                 if (ret < 0) {
    3234             :                                         /* LCOV_EXCL_START */
    3235             :                                         decoding_error(path, f);
    3236             :                                         os_abort();
    3237             :                                         /* LCOV_EXCL_STOP */
    3238             :                                 }
    3239             : 
    3240        6074 :                                 ret = sgetb64(f, &v_size);
    3241        6074 :                                 if (ret < 0) {
    3242             :                                         /* LCOV_EXCL_START */
    3243             :                                         decoding_error(path, f);
    3244             :                                         os_abort();
    3245             :                                         /* LCOV_EXCL_STOP */
    3246             :                                 }
    3247             : 
    3248             :                                 /* if we use this parity entry */
    3249        6074 :                                 if (v_level < state->level) {
    3250             :                                         /* if this split was removed from the configuration */
    3251        5002 :                                         if (s >= state->parity[v_level].split_mac) {
    3252             :                                                 /* if the file is used, we really need it */
    3253           0 :                                                 if (v_size != 0) {
    3254             :                                                         /* LCOV_EXCL_START */
    3255             :                                                         decoding_error(path, f);
    3256             :                                                         log_fatal(EUSER, "Parity '%s' misses used file '%u'!\n", lev_config_name(v_level), s);
    3257             :                                                         log_fatal(EUSER, "If you have removed it from the configuration file, please restore it\n");
    3258             :                                                         exit(EXIT_FAILURE);
    3259             :                                                         /* LCOV_EXCL_STOP */
    3260             :                                                 }
    3261             : 
    3262             :                                                 /* otherwise we can drop it */
    3263           0 :                                                 log_fatal(EUSER, "WARNING! Dropping from '%s' unused split '%u'\n", lev_config_name(v_level), s);
    3264             :                                         } else {
    3265             :                                                 char parity_name[64];
    3266             : 
    3267             :                                                 /* we copy the path only if without configuration file */
    3268        5002 :                                                 if (state->no_conf)
    3269          24 :                                                         pathcpy(state->parity[v_level].split_map[s].path, sizeof(state->parity[v_level].split_map[s].path), v_path);
    3270             : 
    3271             :                                                 /* set the split info */
    3272        5002 :                                                 pathcpy(state->parity[v_level].split_map[s].uuid, sizeof(state->parity[v_level].split_map[s].uuid), v_uuid);
    3273        5002 :                                                 state->parity[v_level].split_map[s].size = v_size;
    3274             : 
    3275        5002 :                                                 if (s == 0)
    3276        1267 :                                                         pathcpy(parity_name, sizeof(parity_name), lev_config_name(v_level));
    3277             :                                                 else
    3278        3735 :                                                         pathprint(parity_name, sizeof(parity_name), "%s/%u", lev_config_name(v_level), s);
    3279       10004 :                                                 log_tag("content_parity_split:%s:%s:%s:%" PRIi64 "\n",
    3280             :                                                         parity_name,
    3281        5002 :                                                         state->parity[v_level].split_map[s].uuid,
    3282        5002 :                                                         esc_tag(state->parity[v_level].split_map[s].path, esc_buffer),
    3283             :                                                         state->parity[v_level].split_map[s].size);
    3284             :                                         }
    3285             :                                 }
    3286             :                         }
    3287         295 :                 } else if (c == 'N') {
    3288             :                         uint32_t crc_stored;
    3289             :                         uint32_t crc_computed;
    3290             : 
    3291             :                         /* get the crc before reading it from the file */
    3292         294 :                         crc_computed = scrc(f);
    3293             : 
    3294         294 :                         ret = sgetble32(f, &crc_stored);
    3295         294 :                         if (ret < 0) {
    3296             :                                 /* LCOV_EXCL_START */
    3297             :                                 /* here don't call decoding_error() because it's too late to get the crc */
    3298             :                                 log_fatal(errno, "Error reading the CRC in '%s' at offset %" PRIi64 "\n", path, stell(f));
    3299             :                                 log_fatal(errno, "This content file is damaged! Use an alternate copy.\n");
    3300             :                                 exit(EXIT_FAILURE);
    3301             :                                 /* LCOV_EXCL_STOP */
    3302             :                         }
    3303             : 
    3304         294 :                         if (crc_stored != crc_computed) {
    3305             :                                 /* LCOV_EXCL_START */
    3306             :                                 /* here don't call decoding_error() because it's too late to get the crc */
    3307             :                                 log_fatal(ECONTENT, "CRC mismatch in '%s'\n", path);
    3308             :                                 log_fatal(ECONTENT, "The content file is damaged! Please use an alternate copy.\n");
    3309             :                                 exit(EXIT_FAILURE);
    3310             :                                 /* LCOV_EXCL_STOP */
    3311             :                         }
    3312             : 
    3313         294 :                         crc_checked = 1;
    3314             :                 } else {
    3315             :                         /* LCOV_EXCL_START */
    3316             :                         decoding_error(path, f);
    3317             :                         log_fatal(ECONTENT, "Invalid command '%c'!\n", (char)c);
    3318             :                         os_abort();
    3319             :                         /* LCOV_EXCL_STOP */
    3320             :                 }
    3321             :         }
    3322             : 
    3323             :         /* mark all disks as multi threads */
    3324        1987 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    3325        1693 :                 struct snapraid_disk* disk = i->data;
    3326        1693 :                 disk->single_thread = 0;
    3327             :         }
    3328             : 
    3329         294 :         tommy_array_done(&disk_mapping);
    3330             : 
    3331         294 :         if (serror(f)) {
    3332             :                 /* LCOV_EXCL_START */
    3333             :                 log_fatal(errno, "Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    3334             :                 exit(EXIT_FAILURE);
    3335             :                 /* LCOV_EXCL_STOP */
    3336             :         }
    3337             : 
    3338         294 :         if (!crc_checked) {
    3339             :                 /* LCOV_EXCL_START */
    3340             :                 log_fatal(ECONTENT, "Reached the end of '%s' without finding the expected CRC\n", path);
    3341             :                 log_fatal(ECONTENT, "This content file is truncated or damaged! Use an alternate copy.\n");
    3342             :                 exit(EXIT_FAILURE);
    3343             :                 /* LCOV_EXCL_STOP */
    3344             :         }
    3345             : 
    3346             :         /* check the file-system on all disks */
    3347         294 :         state_fscheck(state, "after read");
    3348             : 
    3349             :         /* check that the stored parity size matches the loaded state */
    3350         294 :         if (blockmax != parity_allocated_size(state)) {
    3351             :                 /* LCOV_EXCL_START */
    3352             :                 log_fatal(EINTERNAL, "Internal inconsistency: Parity size %u/%u in '%s' at offset %" PRIi64 "\n", blockmax, parity_allocated_size(state), path, stell(f));
    3353             :                 if (state->opt.skip_content_check) {
    3354             :                         log_fatal(ECONTENT, "Overriding.\n");
    3355             :                         blockmax = parity_allocated_size(state);
    3356             :                 } else {
    3357             :                         exit(EXIT_FAILURE);
    3358             :                 }
    3359             :                 /* LCOV_EXCL_STOP */
    3360             :         }
    3361             : 
    3362         294 :         msg_verbose("%8u files\n", count_file);
    3363         294 :         msg_verbose("%8u hardlinks\n", count_hardlink);
    3364         294 :         msg_verbose("%8u symlinks\n", count_symlink);
    3365         294 :         msg_verbose("%8u empty dirs\n", count_dir);
    3366             : 
    3367         294 :         log_tag("content_info:file:%u\n", count_file);
    3368         294 :         log_tag("content_info:hardlink:%u\n", count_hardlink);
    3369         294 :         log_tag("content_info:symlink:%u\n", count_symlink);
    3370         294 :         log_tag("content_info:dir_empty:%u\n", count_dir);
    3371             : 
    3372         294 :         log_tag("content_info:block:%u\n", blockmax);
    3373         294 :         log_tag("content_info:block_bad:%u\n", count_bad);
    3374         294 :         log_tag("content_info:block_rehash:%u\n", count_rehash);
    3375         294 :         log_tag("content_info:block_unsynced:%u\n", count_unsynced);
    3376         294 :         log_tag("content_info:block_unscrubbed:%u\n", count_unscrubbed);
    3377             : 
    3378             :         /* store the blocks counters */
    3379         294 :         state->rehash_blocks = count_rehash;
    3380         294 :         state->bad_blocks = count_bad;
    3381         294 :         state->unsynced_blocks = count_unsynced;
    3382         294 :         state->unscrubbed_blocks = count_unscrubbed;
    3383             : 
    3384             :         /* store the bucket info list */
    3385         294 :         bucket_to_list(&bucket_hash, &state->bucketlist, &state->bucketcount);
    3386         294 :         tommy_hashdyn_done(&bucket_hash);
    3387         294 : }
    3388             : 
    3389             : struct state_write_thread_context {
    3390             :         struct snapraid_state* state;
    3391             : #if HAVE_MT_WRITE
    3392             :         thread_id_t thread;
    3393             : #endif
    3394             :         /* input */
    3395             :         block_off_t blockmax;
    3396             :         time_t info_oldest;
    3397             :         time_t info_now;
    3398             :         int info_has_rehash;
    3399             :         STREAM* f;
    3400             :         int first;
    3401             : 
    3402             :         /* output (required to postpone the output to the terminal after the latest write) */
    3403             :         uint32_t crc;
    3404             :         unsigned count_file;
    3405             :         unsigned count_hardlink;
    3406             :         unsigned count_symlink;
    3407             :         unsigned count_dir;
    3408             :         unsigned count_bad;
    3409             :         unsigned count_rehash;
    3410             :         unsigned count_unsynced;
    3411             :         unsigned count_unscrubbed;
    3412             : };
    3413             : 
    3414         179 : static void* state_write_thread(void* arg)
    3415             : {
    3416             :         char esc_buffer[ESC_MAX];
    3417             :         char esc_buffer1[ESC_MAX];
    3418         179 :         struct state_write_thread_context* context = arg;
    3419         179 :         struct snapraid_state* state = context->state;
    3420         179 :         block_off_t blockmax = context->blockmax;
    3421         179 :         time_t info_oldest = context->info_oldest;
    3422         179 :         time_t info_now = context->info_now;
    3423         179 :         int info_has_rehash = context->info_has_rehash;
    3424         179 :         STREAM* f = context->f;
    3425             :         uint32_t crc;
    3426             :         uint64_t t64;
    3427             :         unsigned count_file;
    3428             :         unsigned count_hardlink;
    3429             :         unsigned count_symlink;
    3430             :         unsigned count_dir;
    3431             :         unsigned count_bad;
    3432             :         unsigned count_rehash;
    3433             :         unsigned count_unsynced;
    3434             :         unsigned count_unscrubbed;
    3435             :         tommy_node* i;
    3436             :         block_off_t idx;
    3437             :         block_off_t begin;
    3438             :         unsigned l, s;
    3439             :         tommy_hashdyn bucket_hash;
    3440             : 
    3441         179 :         count_file = 0;
    3442         179 :         count_hardlink = 0;
    3443         179 :         count_symlink = 0;
    3444         179 :         count_dir = 0;
    3445         179 :         count_bad = 0;
    3446         179 :         count_rehash = 0;
    3447         179 :         count_unsynced = 0;
    3448         179 :         count_unscrubbed = 0;
    3449         179 :         tommy_hashdyn_init(&bucket_hash);
    3450             : 
    3451             :         /*
    3452             :          * Force at least version 3 a we want to always store the parity size
    3453             :          *
    3454             :          * If there is a dealloc list, force version 4
    3455             :          */
    3456         927 :         for (i = state->disklist; i != 0; i = i->next) {
    3457         800 :                 struct snapraid_disk* disk = i->data;
    3458         800 :                 if (!tommy_list_empty(&disk->dealloclist))
    3459          52 :                         break;
    3460             :         }
    3461             : 
    3462             :         /* write header */
    3463         179 :         if (i == 0)
    3464         127 :                 swrite("SNAPCNT3\n\3\0\0", 12, f);
    3465             :         else
    3466          52 :                 swrite("SNAPCNT4\n\3\0\0", 12, f);
    3467             : 
    3468             :         /* write block size and block max */
    3469         179 :         sputc('z', f);
    3470         179 :         sputb32(state->block_size, f);
    3471         179 :         sputc('x', f);
    3472         179 :         sputb32(blockmax, f);
    3473             : 
    3474             :         /* hash size */
    3475         179 :         sputc('y', f);
    3476         179 :         sputb32(BLOCK_HASH_SIZE, f);
    3477             : 
    3478         179 :         if (serror(f)) {
    3479             :                 /* LCOV_EXCL_START */
    3480             :                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3481             :                 goto bail;
    3482             :                 /* LCOV_EXCL_STOP */
    3483             :         }
    3484             : 
    3485         179 :         sputc('c', f);
    3486         179 :         if (state->hash == HASH_MURMUR3) {
    3487         107 :                 sputc('u', f);
    3488          72 :         } else if (state->hash == HASH_SPOOKY2) {
    3489          72 :                 sputc('k', f);
    3490           0 :         } else if (state->hash == HASH_METRO) {
    3491           0 :                 sputc('m', f);
    3492             :         } else {
    3493             :                 /* LCOV_EXCL_START */
    3494             :                 log_fatal(EINTERNAL, "Unexpected hash when writing the content file '%s'.\n", serrorfile(f));
    3495             :                 goto bail;
    3496             :                 /* LCOV_EXCL_STOP */
    3497             :         }
    3498         179 :         swrite(state->hashseed, HASH_MAX, f);
    3499         179 :         if (serror(f)) {
    3500             :                 /* LCOV_EXCL_START */
    3501             :                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3502             :                 goto bail;
    3503             :                 /* LCOV_EXCL_STOP */
    3504             :         }
    3505             : 
    3506             :         /* previous hash only present */
    3507         179 :         if (state->prevhash != HASH_UNDEFINED) {
    3508             :                 /* if at least one rehash tag found, we have to save the previous hash */
    3509           3 :                 if (info_has_rehash) {
    3510           2 :                         sputc('C', f);
    3511           2 :                         if (state->prevhash == HASH_MURMUR3) {
    3512           2 :                                 sputc('u', f);
    3513           0 :                         } else if (state->prevhash == HASH_SPOOKY2) {
    3514           0 :                                 sputc('k', f);
    3515           0 :                         } else if (state->prevhash == HASH_METRO) {
    3516           0 :                                 sputc('m', f);
    3517             :                         } else {
    3518             :                                 /* LCOV_EXCL_START */
    3519             :                                 log_fatal(EINTERNAL, "Unexpected prevhash when writing the content file '%s'.\n", serrorfile(f));
    3520             :                                 goto bail;
    3521             :                                 /* LCOV_EXCL_STOP */
    3522             :                         }
    3523           2 :                         swrite(state->prevhashseed, HASH_MAX, f);
    3524           2 :                         if (serror(f)) {
    3525             :                                 /* LCOV_EXCL_START */
    3526             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3527             :                                 goto bail;
    3528             :                                 /* LCOV_EXCL_STOP */
    3529             :                         }
    3530             :                 }
    3531             :         }
    3532             : 
    3533             :         /* for each map */
    3534        1202 :         for (i = state->maplist; i != 0; i = i->next) {
    3535        1023 :                 struct snapraid_map* map = i->data;
    3536             :                 struct snapraid_disk* disk;
    3537             : 
    3538             :                 /* find the disk for this mapping */
    3539        1023 :                 disk = find_disk_by_name(state, map->name);
    3540        1023 :                 if (!disk) {
    3541             :                         /* LCOV_EXCL_START */
    3542             :                         log_fatal(EINTERNAL, "Internal inconsistency: Unmapped disk '%s'\n", map->name);
    3543             :                         goto bail;
    3544             :                         /* LCOV_EXCL_STOP */
    3545             :                 }
    3546             : 
    3547             :                 /* save the mapping only if disk is mapped */
    3548        1023 :                 if (disk->mapping_idx != -1) {
    3549         931 :                         sputc('M', f);
    3550         931 :                         sputbs(map->name, f);
    3551         931 :                         sputb32(map->position, f);
    3552         931 :                         sputb32(map->total_blocks, f);
    3553         931 :                         sputb32(map->free_blocks, f);
    3554         931 :                         sputbs(map->uuid, f);
    3555         931 :                         if (context->first) {
    3556         931 :                                 log_tag("content_data:%s:%" PRIi64 ":%" PRIi64 "\n",
    3557         931 :                                         esc_tag(map->name, esc_buffer),
    3558         931 :                                         map->total_blocks * (uint64_t)state->block_size,
    3559         931 :                                         map->free_blocks * (uint64_t)state->block_size);
    3560         931 :                                 log_tag("content_data_split:%s:%s\n",
    3561         931 :                                         esc_tag(map->name, esc_buffer),
    3562         931 :                                         map->uuid);
    3563             :                         }
    3564         931 :                         if (serror(f)) {
    3565             :                                 /* LCOV_EXCL_START */
    3566             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3567             :                                 goto bail;
    3568             :                                 /* LCOV_EXCL_STOP */
    3569             :                         }
    3570             :                 }
    3571             :         }
    3572             : 
    3573             :         /* for each parity */
    3574        1113 :         for (l = 0; l < state->level; ++l) {
    3575         934 :                 sputc('Q', f);
    3576         934 :                 sputb32(l, f);
    3577         934 :                 sputb32(state->parity[l].total_blocks, f);
    3578         934 :                 sputb32(state->parity[l].free_blocks, f);
    3579         934 :                 if (context->first) {
    3580         934 :                         log_tag("content_parity:%s:%" PRIi64 ":%" PRIi64 "\n",
    3581             :                                 lev_config_name(l),
    3582         934 :                                 state->parity[l].total_blocks * (uint64_t)state->block_size,
    3583         934 :                                 state->parity[l].free_blocks * (uint64_t)state->block_size);
    3584             :                 }
    3585         934 :                 sputb32(state->parity[l].split_mac, f);
    3586        4619 :                 for (s = 0; s < state->parity[l].split_mac; ++s) {
    3587             :                         char parity_name[64];
    3588        3685 :                         sputbs(state->parity[l].split_map[s].path, f);
    3589        3685 :                         sputbs(state->parity[l].split_map[s].uuid, f);
    3590        3685 :                         sputb64(state->parity[l].split_map[s].size, f);
    3591        3685 :                         if (s == 0)
    3592         934 :                                 pathcpy(parity_name, sizeof(parity_name), lev_config_name(l));
    3593             :                         else
    3594        2751 :                                 pathprint(parity_name, sizeof(parity_name), "%s/%u", lev_config_name(l), s);
    3595        3685 :                         if (context->first) {
    3596        3685 :                                 log_tag("content_parity_split:%s:%s:%s:%" PRIi64 "\n",
    3597             :                                         parity_name,
    3598        3685 :                                         state->parity[l].split_map[s].uuid,
    3599        3685 :                                         state->parity[l].split_map[s].path,
    3600             :                                         state->parity[l].split_map[s].size);
    3601             :                         }
    3602             :                 }
    3603         934 :                 if (serror(f)) {
    3604             :                         /* LCOV_EXCL_START */
    3605             :                         log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3606             :                         goto bail;
    3607             :                         /* LCOV_EXCL_STOP */
    3608             :                 }
    3609             :         }
    3610             : 
    3611             :         /* for each disk */
    3612        1202 :         for (i = state->disklist; i != 0; i = i->next) {
    3613             :                 tommy_node* j;
    3614        1023 :                 struct snapraid_disk* disk = i->data;
    3615             : 
    3616             :                 /* if the disk is not mapped, skip it */
    3617        1023 :                 if (disk->mapping_idx < 0)
    3618          92 :                         continue;
    3619             : 
    3620             :                 /* for each file */
    3621     1964002 :                 for (j = disk->filelist; j != 0; j = j->next) {
    3622     1963071 :                         struct snapraid_file* file = j->data;
    3623             :                         uint64_t size;
    3624             :                         uint64_t mtime_sec;
    3625             :                         int32_t mtime_nsec;
    3626             :                         uint64_t inode;
    3627             : 
    3628     1963071 :                         size = file->size;
    3629     1963071 :                         mtime_sec = file->mtime_sec;
    3630     1963071 :                         mtime_nsec = file->mtime_nsec;
    3631     1963071 :                         inode = file->inode;
    3632             : 
    3633     1963071 :                         sputc('f', f);
    3634     1963071 :                         sputb32(disk->mapping_idx, f);
    3635     1963071 :                         sputb64(size, f);
    3636     1963071 :                         sputb64(mtime_sec, f);
    3637             :                         /* encode STAT_NSEC_INVALID as 0 */
    3638     1963071 :                         if (mtime_nsec == STAT_NSEC_INVALID)
    3639           0 :                                 sputb32(0, f);
    3640             :                         else
    3641     1963071 :                                 sputb32(mtime_nsec + 1, f);
    3642     1963071 :                         sputb64(inode, f);
    3643     1963071 :                         sputbs(file->sub, f);
    3644     1963071 :                         if (serror(f)) {
    3645             :                                 /* LCOV_EXCL_START */
    3646             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3647             :                                 goto bail;
    3648             :                                 /* LCOV_EXCL_STOP */
    3649             :                         }
    3650             : 
    3651             :                         /* for all the blocks of the file */
    3652     1963071 :                         begin = 0;
    3653     3936854 :                         while (begin < file->blockmax) {
    3654     1973783 :                                 unsigned v_state = block_state_get(fs_file2block_get(file, begin));
    3655     1973783 :                                 block_off_t v_pos = fs_file2par_get(disk, file, begin);
    3656             :                                 uint32_t v_count;
    3657             : 
    3658             :                                 block_off_t end;
    3659             : 
    3660             :                                 /* find the end of run of blocks */
    3661     1973783 :                                 end = begin + 1;
    3662     4786886 :                                 while (end < file->blockmax) {
    3663     2826549 :                                         if (v_state != block_state_get(fs_file2block_get(file, end)))
    3664          14 :                                                 break;
    3665     2826535 :                                         if (v_pos + (end - begin) != fs_file2par_get(disk, file, end))
    3666       13432 :                                                 break;
    3667     2813103 :                                         ++end;
    3668             :                                 }
    3669             : 
    3670     1973783 :                                 switch (v_state) {
    3671     1867092 :                                 case BLOCK_STATE_BLK :
    3672     1867092 :                                         sputc('b', f);
    3673     1867092 :                                         break;
    3674       72417 :                                 case BLOCK_STATE_CHG :
    3675       72417 :                                         sputc('g', f);
    3676       72417 :                                         break;
    3677       34274 :                                 case BLOCK_STATE_REP :
    3678       34274 :                                         sputc('p', f);
    3679       34274 :                                         break;
    3680           0 :                                 default :
    3681             :                                         /* LCOV_EXCL_START */
    3682             :                                         log_fatal(EINTERNAL, "Internal inconsistency: State for block %u state %u\n", v_pos, v_state);
    3683             :                                         goto bail;
    3684             :                                         /* LCOV_EXCL_STOP */
    3685             :                                 }
    3686             : 
    3687     1973783 :                                 sputb32(v_pos, f);
    3688             : 
    3689     1973783 :                                 v_count = end - begin;
    3690     1973783 :                                 sputb32(v_count, f);
    3691             : 
    3692             :                                 /* write hashes */
    3693     6760669 :                                 for (idx = begin; idx < end; ++idx) {
    3694     4786886 :                                         struct snapraid_block* block = fs_file2block_get(file, idx);
    3695             : 
    3696     4786886 :                                         swrite(block->hash, BLOCK_HASH_SIZE, f);
    3697             :                                 }
    3698             : 
    3699     1973783 :                                 if (serror(f)) {
    3700             :                                         /* LCOV_EXCL_START */
    3701             :                                         log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3702             :                                         goto bail;
    3703             :                                         /* LCOV_EXCL_STOP */
    3704             :                                 }
    3705             : 
    3706             :                                 /* next begin position */
    3707     1973783 :                                 begin = end;
    3708             :                         }
    3709             : 
    3710     1963071 :                         ++count_file;
    3711             :                 }
    3712             : 
    3713             :                 /* for each link */
    3714       65406 :                 for (j = disk->linklist; j != 0; j = j->next) {
    3715       64475 :                         struct snapraid_link* slink = j->data;
    3716             : 
    3717       64475 :                         switch (link_flag_get(slink, FILE_IS_LINK_MASK)) {
    3718         357 :                         case FILE_IS_HARDLINK :
    3719         357 :                                 sputc('a', f);
    3720         357 :                                 ++count_hardlink;
    3721         357 :                                 break;
    3722       64118 :                         case FILE_IS_SYMLINK :
    3723       64118 :                                 sputc('s', f);
    3724       64118 :                                 ++count_symlink;
    3725       64118 :                                 break;
    3726             :                         }
    3727             : 
    3728       64475 :                         sputb32(disk->mapping_idx, f);
    3729       64475 :                         sputbs(slink->sub, f);
    3730       64475 :                         sputbs(slink->linkto, f);
    3731       64475 :                         if (serror(f)) {
    3732             :                                 /* LCOV_EXCL_START */
    3733             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3734             :                                 goto bail;
    3735             :                                 /* LCOV_EXCL_STOP */
    3736             :                         }
    3737             :                 }
    3738             : 
    3739             :                 /* for each dir */
    3740        1406 :                 for (j = disk->dirlist; j != 0; j = j->next) {
    3741         475 :                         struct snapraid_dir* dir = j->data;
    3742             : 
    3743         475 :                         sputc('r', f);
    3744         475 :                         sputb32(disk->mapping_idx, f);
    3745         475 :                         sputbs(dir->sub, f);
    3746         475 :                         if (serror(f)) {
    3747             :                                 /* LCOV_EXCL_START */
    3748             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3749             :                                 goto bail;
    3750             :                                 /* LCOV_EXCL_STOP */
    3751             :                         }
    3752             : 
    3753         475 :                         ++count_dir;
    3754             :                 }
    3755             : 
    3756             :                 /* deleted blocks of the disk */
    3757         931 :                 sputc('h', f);
    3758         931 :                 sputb32(disk->mapping_idx, f);
    3759         931 :                 if (serror(f)) {
    3760             :                         /* LCOV_EXCL_START */
    3761             :                         log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3762             :                         goto bail;
    3763             :                         /* LCOV_EXCL_STOP */
    3764             :                 }
    3765         931 :                 begin = 0;
    3766        1971 :                 while (begin < blockmax) {
    3767             :                         int is_deleted;
    3768             :                         block_off_t end;
    3769             : 
    3770        1040 :                         is_deleted = fs_is_block_deleted(disk, begin);
    3771             : 
    3772             :                         /* find the end of run of blocks */
    3773        1040 :                         end = begin + 1;
    3774        1040 :                         while (end < blockmax
    3775     5032383 :                                 && is_deleted == fs_is_block_deleted(disk, end)
    3776             :                         ) {
    3777     5031343 :                                 ++end;
    3778             :                         }
    3779             : 
    3780        1040 :                         sputb32(end - begin, f);
    3781             : 
    3782        1040 :                         if (is_deleted) {
    3783             :                                 /* write the run of deleted blocks with hash */
    3784          65 :                                 sputc('o', f);
    3785             : 
    3786             :                                 /* write all the hash */
    3787       18973 :                                 while (begin < end) {
    3788       18908 :                                         struct snapraid_block* block = fs_par2block_get(disk, begin);
    3789             : 
    3790       18908 :                                         swrite(block->hash, BLOCK_HASH_SIZE, f);
    3791             : 
    3792       18908 :                                         ++begin;
    3793             :                                 }
    3794             :                         } else {
    3795             :                                 /*
    3796             :                                  * Write the run of blocks without hash
    3797             :                                  * they can be either used or empty blocks
    3798             :                                  */
    3799         975 :                                 sputc('O', f);
    3800             : 
    3801             :                                 /* next begin position */
    3802         975 :                                 begin = end;
    3803             :                         }
    3804             : 
    3805        1040 :                         if (serror(f)) {
    3806             :                                 /* LCOV_EXCL_START */
    3807             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3808             :                                 goto bail;
    3809             :                                 /* LCOV_EXCL_STOP */
    3810             :                         }
    3811             :                 }
    3812             : 
    3813             :                 /* deallocated files of the disk */
    3814         931 :                 if (!tommy_list_empty(&disk->dealloclist)) {
    3815          83 :                         sputc('d', f);
    3816          83 :                         sputb32(disk->mapping_idx, f);
    3817             : 
    3818          83 :                         uint32_t v_count = tommy_list_count(&disk->dealloclist);
    3819             : 
    3820          83 :                         sputb32(v_count, f);
    3821          83 :                         log_tag("content_info:dealloc:%s:%u\n", esc_tag(disk->name, esc_buffer), v_count);
    3822             : 
    3823             :                         /* for each file */
    3824       44062 :                         for (j = tommy_list_head(&disk->dealloclist); j != 0; j = j->next) {
    3825       43979 :                                 struct snapraid_dealloc* dealloc = j->data;
    3826       43979 :                                 sputbs(dealloc->sub, f);
    3827       43979 :                                 sputb64(dealloc->size, f);
    3828       43979 :                                 sputb64(dealloc->mtime_sec, f);
    3829             : 
    3830             :                                 /* encode STAT_NSEC_INVALID as 0 */
    3831       43979 :                                 if (dealloc->mtime_nsec == STAT_NSEC_INVALID)
    3832           0 :                                         sputb32(0, f);
    3833             :                                 else
    3834       43979 :                                         sputb32(dealloc->mtime_nsec + 1, f);
    3835             : 
    3836       43979 :                                 log_tag("content_info:dealloc_entry:%s:%s:%" PRIu64 ":%" PRIu64 ":%u\n", esc_tag(disk->name, esc_buffer), esc_tag(dealloc->sub, esc_buffer1), dealloc->size, dealloc->mtime_sec, dealloc->mtime_nsec);
    3837             : 
    3838             :                                 /* write all hashes */
    3839      146906 :                                 for (block_off_t k = 0; k < dealloc->blockmax; ++k) {
    3840      102927 :                                         unsigned char* hash = dealloc->blockhash + k * BLOCK_HASH_SIZE;
    3841             : 
    3842      102927 :                                         swrite(hash, BLOCK_HASH_SIZE, f);
    3843             :                                 }
    3844             :                         }
    3845             : 
    3846          83 :                         if (serror(f)) {
    3847             :                                 /* LCOV_EXCL_START */
    3848             :                                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3849             :                                 goto bail;
    3850             :                                 /* LCOV_EXCL_STOP */
    3851             :                         }
    3852             :                 } else {
    3853         848 :                         log_tag("content_info:dealloc:%s:0\n", esc_tag(disk->name, esc_buffer));
    3854             :                 }
    3855             :         }
    3856             : 
    3857             :         /* write the info for each block */
    3858         179 :         sputc('i', f);
    3859             :         /* ensure to write a 64 bit time */
    3860         179 :         t64 = info_oldest;
    3861         179 :         sputb64(t64, f);
    3862         179 :         begin = 0;
    3863       17550 :         while (begin < blockmax) {
    3864             :                 snapraid_info info;
    3865             :                 block_off_t end;
    3866             :                 block_off_t count;
    3867             :                 time_t t;
    3868             :                 unsigned flag;
    3869             : 
    3870       17371 :                 info = info_get(&state->infoarr, begin);
    3871             : 
    3872             :                 /* avoid this slow operation if not needed */
    3873       17371 :                 if (context->first && fs_is_block_unsynced(state, begin))
    3874         133 :                         ++count_unsynced;
    3875             : 
    3876             :                 /* find the end of run of blocks */
    3877       17371 :                 end = begin + 1;
    3878       17371 :                 while (end < blockmax
    3879      847980 :                         && info == info_get(&state->infoarr, end)
    3880             :                 ) {
    3881      830609 :                         if (context->first && fs_is_block_unsynced(state, end))
    3882      117981 :                                 ++count_unsynced;
    3883      830609 :                         ++end;
    3884             :                 }
    3885             : 
    3886       17371 :                 count = end - begin;
    3887       17371 :                 sputb32(count, f);
    3888             : 
    3889             :                 /* if there is info */
    3890       17371 :                 if (info) {
    3891             :                         /* other flags */
    3892       17342 :                         flag = 1; /* info is present */
    3893       17342 :                         if (info_get_bad(info)) {
    3894        1328 :                                 flag |= 2;
    3895        1328 :                                 if (context->first)
    3896        1328 :                                         count_bad += count;
    3897             :                         }
    3898       17342 :                         if (info_get_rehash(info)) {
    3899        4994 :                                 flag |= 4;
    3900        4994 :                                 if (context->first)
    3901        4994 :                                         count_rehash += count;
    3902             :                         }
    3903       17342 :                         if (info_get_justsynced(info)) {
    3904        8173 :                                 flag |= 8;
    3905        8173 :                                 if (context->first)
    3906        8173 :                                         count_unscrubbed += count;
    3907             :                         }
    3908       17342 :                         sputb32(flag, f);
    3909             : 
    3910       17342 :                         t = info_get_time(info);
    3911             : 
    3912       17342 :                         if (context->first)
    3913       17342 :                                 bucket_insert(&bucket_hash, t, count, flag & 8);
    3914             : 
    3915             :                         /* truncate any time that is in the future */
    3916       17342 :                         if (t > info_now)
    3917           0 :                                 t = info_now;
    3918             : 
    3919             :                         /* the oldest info is computed only on required blocks, so it may not be the absolute oldest */
    3920       17342 :                         if (t < info_oldest)
    3921           0 :                                 t = 0;
    3922             :                         else
    3923       17342 :                                 t -= info_oldest;
    3924             : 
    3925             :                         /* ensure to write a 64 bit time */
    3926       17342 :                         t64 = t;
    3927       17342 :                         sputb64(t64, f);
    3928             :                 } else {
    3929             :                         /* write a special 0 flag to mark missing info */
    3930          29 :                         sputb32(0, f);
    3931             :                 }
    3932             : 
    3933       17371 :                 if (serror(f)) {
    3934             :                         /* LCOV_EXCL_START */
    3935             :                         log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3936             :                         goto bail;
    3937             :                         /* LCOV_EXCL_STOP */
    3938             :                 }
    3939             : 
    3940             :                 /* next begin position */
    3941       17371 :                 begin = end;
    3942             :         }
    3943             : 
    3944         179 :         sputc('N', f);
    3945             : 
    3946             :         /* flush data written to the disk */
    3947         179 :         if (sflush(f)) {
    3948             :                 /* LCOV_EXCL_START */
    3949             :                 log_fatal(errno, "Error writing the content file '%s' (in flush before crc). %s.\n", serrorfile(f), strerror(errno));
    3950             :                 goto bail;
    3951             :                 /* LCOV_EXCL_STOP */
    3952             :         }
    3953             : 
    3954             :         /* get the file crc */
    3955         179 :         crc = scrc(f);
    3956             : 
    3957             :         /*
    3958             :          * Compare the crc of the data written to file
    3959             :          * with the one of the data written to the stream
    3960             :          */
    3961         179 :         if (crc != scrc_stream(f)) {
    3962             :                 /* LCOV_EXCL_START */
    3963             :                 log_fatal(ECONTENT, "CRC mismatch while writing the content stream.\n");
    3964             :                 log_fatal(ECONTENT, "DANGER! Your RAM memory is faulty! DO NOT PROCEED UNTIL FIXED!\n");
    3965             :                 log_fatal(ECONTENT, "Try running a memory test like http://www.memtest86.com/\n");
    3966             :                 goto bail;
    3967             :                 /* LCOV_EXCL_STOP */
    3968             :         }
    3969             : 
    3970         179 :         sputble32(crc, f);
    3971         179 :         if (serror(f)) {
    3972             :                 /* LCOV_EXCL_START */
    3973             :                 log_fatal(errno, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3974             :                 goto bail;
    3975             :                 /* LCOV_EXCL_STOP */
    3976             :         }
    3977             : 
    3978             :         /* set output variables */
    3979         179 :         context->crc = crc;
    3980         179 :         context->count_file = count_file;
    3981         179 :         context->count_hardlink = count_hardlink;
    3982         179 :         context->count_symlink = count_symlink;
    3983         179 :         context->count_dir = count_dir;
    3984         179 :         context->count_bad = count_bad;
    3985         179 :         context->count_rehash = count_rehash;
    3986         179 :         context->count_unsynced = count_unsynced;
    3987         179 :         context->count_unscrubbed = count_unscrubbed;
    3988             : 
    3989             :         /* store the bucket info list */
    3990         179 :         if (context->first)
    3991         179 :                 bucket_to_list(&bucket_hash, &state->bucketlist, &state->bucketcount);
    3992             : 
    3993         179 :         tommy_hashdyn_done(&bucket_hash);
    3994         179 :         return 0;
    3995             : 
    3996           0 : bail:
    3997           0 :         tommy_hashdyn_done(&bucket_hash);
    3998           0 :         return context;
    3999             : }
    4000             : 
    4001         179 : static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
    4002             : {
    4003             : #if HAVE_MT_WRITE
    4004             :         int fail;
    4005             :         int first;
    4006             : #else
    4007             :         STREAM* f;
    4008             :         unsigned count_content;
    4009             :         unsigned k;
    4010             :         struct state_write_thread_context* context;
    4011             :         void* retval;
    4012             : #endif
    4013             :         tommy_node* i;
    4014             :         block_off_t blockmax;
    4015             :         time_t info_oldest;
    4016             :         time_t info_now;
    4017             :         int info_has_rehash;
    4018             :         int mapping_idx;
    4019             :         block_off_t idx;
    4020             :         uint32_t crc;
    4021             :         unsigned count_file;
    4022             :         unsigned count_hardlink;
    4023             :         unsigned count_symlink;
    4024             :         unsigned count_dir;
    4025             :         unsigned count_bad;
    4026             :         unsigned count_rehash;
    4027             :         unsigned count_unsynced;
    4028             :         unsigned count_unscrubbed;
    4029             : 
    4030             :         /* blocks of all array */
    4031         179 :         blockmax = parity_allocated_size(state);
    4032             : 
    4033             :         /* check the file-system on all disks */
    4034         179 :         state_fscheck(state, "before write");
    4035             : 
    4036             :         /*
    4037             :          * Clear the info for unused blocks
    4038             :          * and get some other info
    4039             :          */
    4040         179 :         info_oldest = 0; /* oldest time in info */
    4041         179 :         info_now = time(0); /* get the present time */
    4042         179 :         info_has_rehash = 0; /* if there is a rehash info */
    4043      848159 :         for (idx = 0; idx < blockmax; ++idx) {
    4044             :                 /* if the position is used */
    4045      847980 :                 if (fs_position_is_required(state, idx)) {
    4046      846102 :                         snapraid_info info = info_get(&state->infoarr, idx);
    4047             : 
    4048             :                         /* only if there is some info to store */
    4049      846102 :                         if (info) {
    4050      798511 :                                 time_t info_time = info_get_time(info);
    4051             : 
    4052      798511 :                                 if (!info_oldest || info_time < info_oldest)
    4053         182 :                                         info_oldest = info_time;
    4054             : 
    4055      798511 :                                 if (info_get_rehash(info))
    4056       13912 :                                         info_has_rehash = 1;
    4057             :                         }
    4058             :                 } else {
    4059             :                         /* clear any previous info */
    4060        1878 :                         info_set(&state->infoarr, idx, 0);
    4061             : 
    4062             :                         /* and clear any deleted blocks */
    4063        1878 :                         fs_position_clear_deleted(state, idx);
    4064             :                 }
    4065             :         }
    4066             : 
    4067             :         /* map disks */
    4068         179 :         mapping_idx = 0;
    4069        1202 :         for (i = state->maplist; i != 0; i = i->next) {
    4070        1023 :                 struct snapraid_map* map = i->data;
    4071             :                 struct snapraid_disk* disk;
    4072             : 
    4073             :                 /* find the disk for this mapping */
    4074        1023 :                 disk = find_disk_by_name(state, map->name);
    4075        1023 :                 if (!disk) {
    4076             :                         /* LCOV_EXCL_START */
    4077             :                         log_fatal(EINTERNAL, "Internal inconsistency: Unmapped disk '%s'\n", map->name);
    4078             :                         os_abort();
    4079             :                         /* LCOV_EXCL_STOP */
    4080             :                 }
    4081             : 
    4082             :                 /* save the mapping only for not empty disks */
    4083        1023 :                 if (!fs_is_empty(disk, blockmax)) {
    4084             :                         /* assign the mapping index used to identify disks */
    4085         931 :                         disk->mapping_idx = mapping_idx;
    4086         931 :                         ++mapping_idx;
    4087             :                 } else {
    4088             :                         /* mark the disk as without mapping */
    4089          92 :                         disk->mapping_idx = -1;
    4090             :                 }
    4091             :         }
    4092             : 
    4093             : #if HAVE_MT_WRITE
    4094             :         /* start all writing threads */
    4095             :         first = 1;
    4096             :         i = tommy_list_head(&state->contentlist);
    4097             :         while (i) {
    4098             :                 struct snapraid_content* content = i->data;
    4099             :                 struct state_write_thread_context* context;
    4100             :                 char tmp[PATH_MAX];
    4101             :                 char esc_buffer[ESC_MAX];
    4102             :                 STREAM* f;
    4103             : 
    4104             :                 msg_progress("Saving state to %s...\n", content->content);
    4105             :                 log_tag("content_write:%s\n", esc_tag(content->content, esc_buffer));
    4106             : 
    4107             :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    4108             : 
    4109             :                 /* ensure to delete a previous stale file */
    4110             :                 if (remove(tmp) != 0) {
    4111             :                         if (errno != ENOENT) {
    4112             :                                 /* LCOV_EXCL_START */
    4113             :                                 log_fatal(errno, "Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
    4114             :                                 exit(EXIT_FAILURE);
    4115             :                                 /* LCOV_EXCL_STOP */
    4116             :                         }
    4117             :                 }
    4118             : 
    4119             :                 f = sopen_write(tmp);
    4120             :                 if (f == 0) {
    4121             :                         /* LCOV_EXCL_START */
    4122             :                         log_fatal(errno, "Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
    4123             :                         exit(EXIT_FAILURE);
    4124             :                         /* LCOV_EXCL_STOP */
    4125             :                 }
    4126             : 
    4127             :                 /* allocate the thread context */
    4128             :                 context = malloc_nofail(sizeof(struct state_write_thread_context));
    4129             :                 content->context = context;
    4130             : 
    4131             :                 /* initialize */
    4132             :                 context->state = state;
    4133             :                 context->blockmax = blockmax;
    4134             :                 context->info_oldest = info_oldest;
    4135             :                 context->info_now = info_now;
    4136             :                 context->info_has_rehash = info_has_rehash;
    4137             :                 context->f = f;
    4138             :                 context->first = first;
    4139             :                 first = 0;
    4140             : 
    4141             :                 thread_create(&context->thread, state_write_thread, context);
    4142             : 
    4143             :                 i = i->next;
    4144             :         }
    4145             : 
    4146             :         /* join all thread */
    4147             :         fail = 0;
    4148             :         first = 1;
    4149             :         crc = 0;
    4150             :         count_file = 0;
    4151             :         count_hardlink = 0;
    4152             :         count_symlink = 0;
    4153             :         count_dir = 0;
    4154             :         count_bad = 0;
    4155             :         count_rehash = 0;
    4156             :         count_unsynced = 0;
    4157             :         count_unscrubbed = 0;
    4158             :         i = tommy_list_head(&state->contentlist);
    4159             :         while (i) {
    4160             :                 struct snapraid_content* content = i->data;
    4161             :                 struct state_write_thread_context* context = content->context;
    4162             :                 void* retval;
    4163             : 
    4164             :                 thread_join(context->thread, &retval);
    4165             : 
    4166             :                 if (retval) {
    4167             :                         /* LCOV_EXCL_START */
    4168             :                         fail = 1;
    4169             :                         /* LCOV_EXCL_STOP */
    4170             :                 } else {
    4171             :                         STREAM* f = context->f;
    4172             : 
    4173             :                         /*
    4174             :                          * Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure
    4175             :                          * than even in a system crash event we have one valid copy of the file.
    4176             :                          */
    4177             :                         if (sflush(f) != 0) {
    4178             :                                 /* LCOV_EXCL_START */
    4179             :                                 log_fatal(errno, "Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
    4180             :                                 exit(EXIT_FAILURE);
    4181             :                                 /* LCOV_EXCL_STOP */
    4182             :                         }
    4183             : 
    4184             : #if HAVE_FSYNC
    4185             :                         if (ssync(f) != 0) {
    4186             :                                 /* LCOV_EXCL_START */
    4187             :                                 log_fatal(errno, "Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
    4188             :                                 exit(EXIT_FAILURE);
    4189             :                                 /* LCOV_EXCL_STOP */
    4190             :                         }
    4191             : #endif
    4192             : 
    4193             :                         if (sclose(f) != 0) {
    4194             :                                 /* LCOV_EXCL_START */
    4195             :                                 log_fatal(errno, "Error closing the content file. %s.\n", strerror(errno));
    4196             :                                 exit(EXIT_FAILURE);
    4197             :                                 /* LCOV_EXCL_STOP */
    4198             :                         }
    4199             : 
    4200             :                         if (first) {
    4201             :                                 first = 0;
    4202             :                                 crc = context->crc;
    4203             :                                 count_file = context->count_file;
    4204             :                                 count_hardlink = context->count_hardlink;
    4205             :                                 count_symlink = context->count_symlink;
    4206             :                                 count_dir = context->count_dir;
    4207             :                                 cound_bad = context->count_bad;
    4208             :                                 count_rehash = context->count_rehash;
    4209             :                                 count_unsynced = context->count_unsynced;
    4210             :                                 count_unscrubbed = context->count_unscrubbed;
    4211             :                         } else {
    4212             :                                 if (crc != context->crc) {
    4213             :                                         /* LCOV_EXCL_START */
    4214             :                                         log_fatal(ECONTENT, "Different CRCs writing content streams.\n");
    4215             :                                         log_fatal(ECONTENT, "DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
    4216             :                                         log_fatal(ECONTENT, "Try running a memory test like http://www.memtest86.com/\n");
    4217             :                                         exit(EXIT_FAILURE);
    4218             :                                         /* LCOV_EXCL_STOP */
    4219             :                                 }
    4220             :                         }
    4221             :                 }
    4222             : 
    4223             :                 free(context);
    4224             : 
    4225             :                 i = i->next;
    4226             :         }
    4227             : 
    4228             :         /* abort on failure */
    4229             :         if (fail) {
    4230             :                 /* LCOV_EXCL_START */
    4231             :                 exit(EXIT_FAILURE);
    4232             :                 /* LCOV_EXCL_STOP */
    4233             :         }
    4234             : #else
    4235             :         /* count the content files */
    4236         179 :         count_content = 0;
    4237         179 :         i = tommy_list_head(&state->contentlist);
    4238        1292 :         while (i) {
    4239        1113 :                 struct snapraid_content* content = i->data;
    4240        1113 :                 msg_progress("Saving state to %s...\n", content->content);
    4241        1113 :                 ++count_content;
    4242        1113 :                 i = i->next;
    4243             :         }
    4244             : 
    4245             :         /* open all the content files */
    4246         179 :         f = sopen_multi_write(count_content, STREAM_FLAGS_SEQUENTIAL | STREAM_FLAGS_CRC);
    4247         179 :         if (!f) {
    4248             :                 /* LCOV_EXCL_START */
    4249             :                 log_fatal(errno, "Error opening the content files.\n");
    4250             :                 exit(EXIT_FAILURE);
    4251             :                 /* LCOV_EXCL_STOP */
    4252             :         }
    4253             : 
    4254         179 :         k = 0;
    4255         179 :         i = tommy_list_head(&state->contentlist);
    4256        1292 :         while (i) {
    4257        1113 :                 struct snapraid_content* content = i->data;
    4258             :                 char tmp[PATH_MAX];
    4259        1113 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    4260             : 
    4261             :                 /* ensure to delete a previous stale file */
    4262        1113 :                 if (remove(tmp) != 0) {
    4263        1113 :                         if (errno != ENOENT) {
    4264             :                                 /* LCOV_EXCL_START */
    4265             :                                 log_fatal(errno, "Error removing the stale content file '%s'. %s.\n", tmp, strerror(errno));
    4266             :                                 exit(EXIT_FAILURE);
    4267             :                                 /* LCOV_EXCL_STOP */
    4268             :                         }
    4269             :                 }
    4270             : 
    4271        1113 :                 if (sopen_multi_file(f, k, tmp) != 0) {
    4272             :                         /* LCOV_EXCL_START */
    4273             :                         log_fatal(errno, "Error opening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
    4274             :                         exit(EXIT_FAILURE);
    4275             :                         /* LCOV_EXCL_STOP */
    4276             :                 }
    4277             : 
    4278        1113 :                 ++k;
    4279        1113 :                 i = i->next;
    4280             :         }
    4281             : 
    4282             :         /* allocate the thread context */
    4283         179 :         context = malloc_nofail(sizeof(struct state_write_thread_context));
    4284             : 
    4285             :         /* initialize */
    4286         179 :         context->state = state;
    4287         179 :         context->blockmax = blockmax;
    4288         179 :         context->info_oldest = info_oldest;
    4289         179 :         context->info_now = info_now;
    4290         179 :         context->info_has_rehash = info_has_rehash;
    4291         179 :         context->f = f;
    4292         179 :         context->first = 1;
    4293             : 
    4294         179 :         retval = state_write_thread(context);
    4295             : 
    4296             :         /* abort on failure */
    4297         179 :         if (retval) {
    4298             :                 /* LCOV_EXCL_START */
    4299             :                 exit(EXIT_FAILURE);
    4300             :                 /* LCOV_EXCL_STOP */
    4301             :         }
    4302             : 
    4303             :         /*
    4304             :          * Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure
    4305             :          * than even in a system crash event we have one valid copy of the file.
    4306             :          */
    4307         179 :         if (sflush(f) != 0) {
    4308             :                 /* LCOV_EXCL_START */
    4309             :                 log_fatal(errno, "Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
    4310             :                 exit(EXIT_FAILURE);
    4311             :                 /* LCOV_EXCL_STOP */
    4312             :         }
    4313             : 
    4314             : #if HAVE_FSYNC
    4315         179 :         if (ssync(f) != 0) {
    4316             :                 /* LCOV_EXCL_START */
    4317             :                 log_fatal(errno, "Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
    4318             :                 exit(EXIT_FAILURE);
    4319             :                 /* LCOV_EXCL_STOP */
    4320             :         }
    4321             : #endif
    4322             : 
    4323         179 :         if (sclose(f) != 0) {
    4324             :                 /* LCOV_EXCL_START */
    4325             :                 log_fatal(errno, "Error closing the content file. %s.\n", strerror(errno));
    4326             :                 exit(EXIT_FAILURE);
    4327             :                 /* LCOV_EXCL_STOP */
    4328             :         }
    4329             : 
    4330         179 :         crc = context->crc;
    4331         179 :         count_file = context->count_file;
    4332         179 :         count_hardlink = context->count_hardlink;
    4333         179 :         count_symlink = context->count_symlink;
    4334         179 :         count_dir = context->count_dir;
    4335         179 :         count_bad = context->count_bad;
    4336         179 :         count_rehash = context->count_rehash;
    4337         179 :         count_unsynced = context->count_unsynced;
    4338         179 :         count_unscrubbed = context->count_unscrubbed;
    4339             : 
    4340         179 :         free(context);
    4341             : #endif
    4342             : 
    4343         179 :         msg_verbose("%8u files\n", count_file);
    4344         179 :         msg_verbose("%8u hardlinks\n", count_hardlink);
    4345         179 :         msg_verbose("%8u symlinks\n", count_symlink);
    4346         179 :         msg_verbose("%8u empty dirs\n", count_dir);
    4347             : 
    4348         179 :         log_tag("content_info:file:%u\n", count_file);
    4349         179 :         log_tag("content_info:hardlink:%u\n", count_hardlink);
    4350         179 :         log_tag("content_info:symlink:%u\n", count_symlink);
    4351         179 :         log_tag("content_info:dir_empty:%u\n", count_dir);
    4352             : 
    4353         179 :         log_tag("content_info:block:%u\n", blockmax);
    4354         179 :         log_tag("content_info:block_bad:%u\n", count_bad);
    4355         179 :         log_tag("content_info:block_rehash:%u\n", count_rehash);
    4356         179 :         log_tag("content_info:block_unsynced:%u\n", count_unsynced);
    4357         179 :         log_tag("content_info:block_unscrubbed:%u\n", count_unscrubbed);
    4358             : 
    4359             :         /* store the blocks counters */
    4360         179 :         state->rehash_blocks = count_rehash;
    4361         179 :         state->bad_blocks = count_bad;
    4362         179 :         state->unsynced_blocks = count_unsynced;
    4363         179 :         state->unscrubbed_blocks = count_unscrubbed;
    4364             : 
    4365         179 :         *out_crc = crc;
    4366         179 : }
    4367             : 
    4368           4 : void state_probe(struct snapraid_state* state)
    4369             : {
    4370             :         char path[PATH_MAX];
    4371             :         struct stat st;
    4372             :         tommy_node* node;
    4373             : 
    4374             :         /* iterate over all the available content files and check the first one present */
    4375           4 :         node = tommy_list_head(&state->contentlist);
    4376           4 :         while (node) {
    4377           4 :                 struct snapraid_content* content = node->data;
    4378             : 
    4379           4 :                 pathcpy(path, sizeof(path), content->content);
    4380             : 
    4381           4 :                 if (stat(path, &st) == 0) {
    4382             :                         /* if found stop the search */
    4383           4 :                         break;
    4384             :                 }
    4385             : 
    4386             :                 /* next content file */
    4387           0 :                 node = node->next;
    4388             :         }
    4389             : 
    4390             :         /* if not found, assume empty */
    4391           4 :         if (!node)
    4392           0 :                 return;
    4393             : 
    4394           4 :         if (!state->no_conf) {
    4395             :                 /* intentionally don't output the content path because it's not really read */
    4396           4 :                 log_tag("content_info:probe_unixtime:%" PRId64 "\n", (int64_t)st.st_mtime);
    4397           4 :                 log_flush();
    4398             :         }
    4399             : }
    4400             : 
    4401         303 : void state_read(struct snapraid_state* state)
    4402             : {
    4403             :         STREAM* f;
    4404             :         char path[PATH_MAX];
    4405             :         struct stat st;
    4406             :         tommy_node* node;
    4407             :         int ret;
    4408             :         int c;
    4409             : 
    4410             :         /* iterate over all the available content files and load the first one present */
    4411         303 :         f = 0;
    4412         303 :         node = tommy_list_head(&state->contentlist);
    4413         355 :         while (node) {
    4414         347 :                 struct snapraid_content* content = node->data;
    4415         347 :                 pathcpy(path, sizeof(path), content->content);
    4416             : 
    4417         347 :                 msg_progress("Loading state from %s...\n", path);
    4418             : 
    4419         347 :                 f = sopen_read(path, STREAM_FLAGS_SEQUENTIAL | STREAM_FLAGS_CRC);
    4420         347 :                 if (f != 0) {
    4421             :                         /* if found stop the search */
    4422         295 :                         break;
    4423             :                 } else {
    4424             :                         /* if it's real error of an existing file, abort */
    4425          52 :                         if (errno != ENOENT) {
    4426             :                                 /* LCOV_EXCL_START */
    4427             :                                 log_fatal(errno, "Error opening the content file '%s'. %s.\n", path, strerror(errno));
    4428             :                                 exit(EXIT_FAILURE);
    4429             :                                 /* LCOV_EXCL_STOP */
    4430             :                         }
    4431             : 
    4432             :                         /* otherwise continue */
    4433          52 :                         if (node->next) {
    4434          44 :                                 log_fatal(errno, "WARNING! Content file '%s' not found, attempting with another copy...\n", path);
    4435             : 
    4436             :                                 /* ensure to rewrite all the content files */
    4437          44 :                                 state->need_write = 1;
    4438             :                         }
    4439             :                 }
    4440             : 
    4441             :                 /* next content file */
    4442          52 :                 node = node->next;
    4443             :         }
    4444             : 
    4445             :         /* if not found, assume empty */
    4446         303 :         if (!f) {
    4447           8 :                 log_fatal(EUSER, "No content file found. Assuming empty.\n");
    4448             : 
    4449             :                 /* create the initial mapping */
    4450           8 :                 state_map(state);
    4451           8 :                 return;
    4452             :         }
    4453             : 
    4454             :         /* get the stat of the content file */
    4455         295 :         ret = fstat(shandle(f), &st);
    4456         295 :         if (ret != 0) {
    4457             :                 /* LCOV_EXCL_START */
    4458             :                 log_fatal(errno, "Error stating the content file '%s'. %s.\n", path, strerror(errno));
    4459             :                 exit(EXIT_FAILURE);
    4460             :                 /* LCOV_EXCL_STOP */
    4461             :         }
    4462             : 
    4463         295 :         if (!state->no_conf) {
    4464             :                 char esc_buffer[ESC_MAX];
    4465         294 :                 log_tag("content:%s\n", esc_tag(path, esc_buffer));
    4466         294 :                 log_tag("content_info:read_unixtime:%" PRId64 "\n", (int64_t)st.st_mtime);
    4467         294 :                 log_flush();
    4468             :         }
    4469             : 
    4470             :         /* go further to check other content files */
    4471        1890 :         while (node) {
    4472             :                 char other_path[PATH_MAX];
    4473             :                 struct stat other_st;
    4474        1595 :                 struct snapraid_content* content = node->data;
    4475        1595 :                 pathcpy(other_path, sizeof(other_path), content->content);
    4476             : 
    4477        1595 :                 ret = stat(other_path, &other_st);
    4478        1595 :                 if (ret != 0) {
    4479             :                         /* allow missing content files, but not any other kind of error */
    4480           1 :                         if (errno != ENOENT) {
    4481             :                                 /* LCOV_EXCL_START */
    4482             :                                 log_fatal(errno, "Error stating the content file '%s'. %s.\n", other_path, strerror(errno));
    4483             :                                 exit(EXIT_FAILURE);
    4484             :                                 /* LCOV_EXCL_STOP */
    4485             :                         }
    4486             : 
    4487             :                         /* ensure to rewrite all the content files */
    4488           1 :                         state->need_write = 1;
    4489             :                 } else {
    4490             :                         /* if the size is different */
    4491        1594 :                         if (other_st.st_size != st.st_size) {
    4492          39 :                                 log_fatal(ECONTENT, "WARNING! Content files '%s' and '%s' have a different size!\n", path, other_path);
    4493          39 :                                 log_fatal(ECONTENT, "Likely one of the two is broken!\n");
    4494             : 
    4495             :                                 /* ensure to rewrite all the content files */
    4496          39 :                                 state->need_write = 1;
    4497             :                         }
    4498             :                 }
    4499             : 
    4500             :                 /* next content file */
    4501        1595 :                 node = node->next;
    4502             :         }
    4503             : 
    4504             :         /*
    4505             :          * Start with a undefined default.
    4506             :          * it's for compatibility with version 1.0 where MD5 was implicit.
    4507             :          */
    4508         295 :         state->hash = HASH_UNDEFINED;
    4509             : 
    4510             :         /* start with a zero seed, it was the default in old versions */
    4511         295 :         memset(state->hashseed, 0, HASH_MAX);
    4512             : 
    4513             :         /* previous hash, start with an undefined value */
    4514         295 :         state->prevhash = HASH_UNDEFINED;
    4515             : 
    4516             :         /* intentionally not set the prevhashseed, if used valgrind will warn about it */
    4517             : 
    4518             :         /* get the first char to detect the file type */
    4519         295 :         c = sgetc(f);
    4520         295 :         sungetc(c, f);
    4521             : 
    4522             :         /* guess the file type from the first char */
    4523         295 :         if (c == 'S') {
    4524         295 :                 state_read_content(state, path, f);
    4525             :         } else {
    4526             :                 /* LCOV_EXCL_START */
    4527             :                 log_fatal(EUSER, "From SnapRAID v9.0 the text content file is not supported anymore.\n");
    4528             :                 log_fatal(EUSER, "You have first to upgrade to SnapRAID v8.1 to convert it to binary format.\n");
    4529             :                 exit(EXIT_FAILURE);
    4530             :                 /* LCOV_EXCL_STOP */
    4531             :         }
    4532             : 
    4533         294 :         sclose(f);
    4534             : 
    4535         294 :         if (state->hash == HASH_UNDEFINED) {
    4536             :                 /* LCOV_EXCL_START */
    4537             :                 log_fatal(EUSER, "The checksum to use is not specified.\n");
    4538             :                 log_fatal(EUSER, "This happens because you are likely upgrading from SnapRAID 1.0.\n");
    4539             :                 log_fatal(EUSER, "To use a new SnapRAID you must restart from scratch,\n");
    4540             :                 log_fatal(EUSER, "deleting all the content and parity files.\n");
    4541             :                 exit(EXIT_FAILURE);
    4542             :                 /* LCOV_EXCL_STOP */
    4543             :         }
    4544             : 
    4545         294 :         if (state->unsynced_blocks)
    4546          51 :                 msg_progress("WARNING! The latest sync was interrupted!\n");
    4547             : 
    4548             :         /* update the mapping */
    4549         294 :         state_map(state);
    4550             : 
    4551         294 :         state_content_check(state, path);
    4552             : 
    4553             :         /* mark that we read the content file, and it passed all the checks */
    4554         294 :         state->checked_read = 1;
    4555             : }
    4556             : 
    4557             : struct state_verify_thread_context {
    4558             :         struct snapraid_state* state;
    4559             :         struct snapraid_content* content;
    4560             : #if HAVE_MT_VERIFY
    4561             :         thread_id_t thread;
    4562             : #else
    4563             :         void* retval;
    4564             : #endif
    4565             :         /* input */
    4566             :         uint32_t crc;
    4567             :         STREAM* f;
    4568             : };
    4569             : 
    4570        1113 : static void* state_verify_thread(void* arg)
    4571             : {
    4572        1113 :         struct state_verify_thread_context* context = arg;
    4573        1113 :         struct snapraid_content* content = context->content;
    4574        1113 :         STREAM* f = context->f;
    4575             :         unsigned char buf[4];
    4576             :         uint32_t crc_stored;
    4577             :         uint32_t crc_computed;
    4578             :         uint64_t start;
    4579             : 
    4580        1113 :         start = os_tick_ms();
    4581             : 
    4582        1113 :         if (sdeplete(f, buf) != 0) {
    4583             :                 /* LCOV_EXCL_START */
    4584             :                 log_fatal(errno, "Failed to flush content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    4585             :                 return context;
    4586             :                 /* LCOV_EXCL_STOP */
    4587             :         }
    4588             : 
    4589             :         /* get the stored crc from the last four bytes */
    4590        1113 :         crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
    4591             : 
    4592        1113 :         if (crc_stored != context->crc) {
    4593             :                 /* LCOV_EXCL_START */
    4594             :                 log_fatal(ECONTENT, "DANGER! Wrong stored CRC in '%s'\n", serrorfile(f));
    4595             :                 return context;
    4596             :                 /* LCOV_EXCL_STOP */
    4597             :         }
    4598             : 
    4599             :         /* get the computed crc */
    4600        1113 :         crc_computed = scrc(f);
    4601             : 
    4602             :         /* adjust the stored crc to include itself */
    4603        1113 :         crc_stored = crc32c(crc_stored, buf, 4);
    4604             : 
    4605        1113 :         if (crc_computed != crc_stored) {
    4606             :                 /* LCOV_EXCL_START */
    4607             :                 log_fatal(ECONTENT, "DANGER! Wrong file CRC in '%s'\n", serrorfile(f));
    4608             :                 return context;
    4609             :                 /* LCOV_EXCL_STOP */
    4610             :         }
    4611             : 
    4612        1113 :         msg_progress("Verified %s in %" PRIu64 " seconds\n", content->content, (os_tick_ms() - start) / 1000);
    4613             : 
    4614        1113 :         return 0;
    4615             : }
    4616             : 
    4617         179 : static void state_verify_content(struct snapraid_state* state, uint32_t crc)
    4618             : {
    4619             :         tommy_node* i;
    4620             :         int fail;
    4621             : 
    4622         179 :         msg_progress("Verifying...\n");
    4623             : 
    4624             :         /* start all reading threads */
    4625         179 :         i = tommy_list_head(&state->contentlist);
    4626        1292 :         while (i) {
    4627        1113 :                 struct snapraid_content* content = i->data;
    4628             :                 struct state_verify_thread_context* context;
    4629             :                 char tmp[PATH_MAX];
    4630             :                 STREAM* f;
    4631             : 
    4632        1113 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    4633        1113 :                 f = sopen_read(tmp, STREAM_FLAGS_SEQUENTIAL | STREAM_FLAGS_CRC);
    4634        1113 :                 if (f == 0) {
    4635             :                         /* LCOV_EXCL_START */
    4636             :                         log_fatal(errno, "Error reopening the temporary content file '%s'. %s.\n", tmp, strerror(errno));
    4637             :                         exit(EXIT_FAILURE);
    4638             :                         /* LCOV_EXCL_STOP */
    4639             :                 }
    4640             : 
    4641             :                 /* allocate the thread context */
    4642        1113 :                 context = malloc_nofail(sizeof(struct state_verify_thread_context));
    4643        1113 :                 content->context = context;
    4644             : 
    4645             :                 /* initialize */
    4646        1113 :                 context->state = state;
    4647        1113 :                 context->content = content;
    4648        1113 :                 context->crc = crc;
    4649        1113 :                 context->f = f;
    4650             : 
    4651             : #if HAVE_MT_VERIFY
    4652        1113 :                 thread_create(&context->thread, state_verify_thread, context);
    4653             : #else
    4654             :                 context->retval = state_verify_thread(context);
    4655             : #endif
    4656             : 
    4657        1113 :                 i = i->next;
    4658             :         }
    4659             : 
    4660             :         /* join all thread */
    4661         179 :         fail = 0;
    4662         179 :         i = tommy_list_head(&state->contentlist);
    4663        1292 :         while (i) {
    4664        1113 :                 struct snapraid_content* content = i->data;
    4665        1113 :                 struct state_verify_thread_context* context = content->context;
    4666             :                 void* retval;
    4667             : 
    4668             : #if HAVE_MT_VERIFY
    4669        1113 :                 thread_join(context->thread, &retval);
    4670             : #else
    4671             :                 retval = context->retval;
    4672             : #endif
    4673        1113 :                 if (retval) {
    4674             :                         /* LCOV_EXCL_START */
    4675             :                         fail = 1;
    4676             :                         /* LCOV_EXCL_STOP */
    4677             :                 } else {
    4678        1113 :                         STREAM* f = context->f;
    4679             : 
    4680        1113 :                         if (sclose(f) != 0) {
    4681             :                                 /* LCOV_EXCL_START */
    4682             :                                 log_fatal(errno, "Error closing the content file. %s.\n", strerror(errno));
    4683             :                                 exit(EXIT_FAILURE);
    4684             :                                 /* LCOV_EXCL_STOP */
    4685             :                         }
    4686             :                 }
    4687             : 
    4688        1113 :                 free(context);
    4689             : 
    4690        1113 :                 i = i->next;
    4691             :         }
    4692             : 
    4693             :         /* abort on failure */
    4694         179 :         if (fail) {
    4695             :                 /* LCOV_EXCL_START */
    4696             :                 exit(EXIT_FAILURE);
    4697             :                 /* LCOV_EXCL_STOP */
    4698             :         }
    4699         179 : }
    4700             : 
    4701         179 : static void state_rename_content(struct snapraid_state* state)
    4702             : {
    4703             :         tommy_node* i;
    4704             : 
    4705             : #if defined(_linux) /* this sequence is linux specific */
    4706             :         i = tommy_list_head(&state->contentlist);
    4707             :         while (i) {
    4708             :                 struct snapraid_content* content = i->data;
    4709             :                 char tmp[PATH_MAX];
    4710             :                 char dir[PATH_MAX];
    4711             :                 char* slash;
    4712             :                 int handle;
    4713             : 
    4714             :                 pathcpy(dir, sizeof(dir), content->content);
    4715             : 
    4716             :                 slash = strrchr(tmp, '/');
    4717             :                 if (slash)
    4718             :                         *slash = 0;
    4719             :                 else
    4720             :                         pathcpy(dir, sizeof(dir), ".");
    4721             : 
    4722             :                 /* open the directory to get the handle */
    4723             :                 handle = open(dir, O_RDONLY | O_DIRECTORY);
    4724             :                 if (handle < 0) {
    4725             :                         /* LCOV_EXCL_START */
    4726             :                         log_fatal(errno, "Error opening the directory '%s'. %s.\n", dir, strerror(errno));
    4727             :                         exit(EXIT_FAILURE);
    4728             :                         /* LCOV_EXCL_STOP */
    4729             :                 }
    4730             : 
    4731             :                 /* now rename the just written copy with the correct name */
    4732             :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    4733             :                 if (rename(tmp, content->content) != 0) {
    4734             :                         /* LCOV_EXCL_START */
    4735             :                         log_fatal(errno, "Error renaming the content file '%s' to '%s'. %s.\n", tmp, content->content, strerror(errno));
    4736             :                         exit(EXIT_FAILURE);
    4737             :                         /* LCOV_EXCL_STOP */
    4738             :                 }
    4739             : 
    4740             :                 /* sync the directory */
    4741             :                 if (fsync(handle) != 0) {
    4742             :                         /* LCOV_EXCL_START */
    4743             :                         log_fatal(errno, "Error syncing the directory '%s'. %s.\n", dir, strerror(errno));
    4744             :                         exit(EXIT_FAILURE);
    4745             :                         /* LCOV_EXCL_STOP */
    4746             :                 }
    4747             : 
    4748             :                 if (close(handle) != 0) {
    4749             :                         /* LCOV_EXCL_START */
    4750             :                         log_fatal(errno, "Error closing the directory '%s'. %s.\n", dir, strerror(errno));
    4751             :                         exit(EXIT_FAILURE);
    4752             :                         /* LCOV_EXCL_STOP */
    4753             :                 }
    4754             : 
    4755             :                 i = i->next;
    4756             :         }
    4757             : #else
    4758         179 :         i = tommy_list_head(&state->contentlist);
    4759        1292 :         while (i) {
    4760        1113 :                 struct snapraid_content* content = i->data;
    4761             :                 char tmp[PATH_MAX];
    4762             : 
    4763             :                 /* now renames the just written copy with the correct name */
    4764        1113 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    4765        1113 :                 if (rename(tmp, content->content) != 0) {
    4766             :                         /* LCOV_EXCL_START */
    4767             :                         log_fatal(errno, "Error renaming the content file '%s' to '%s' in rename(). %s.\n", tmp, content->content, strerror(errno));
    4768             :                         exit(EXIT_FAILURE);
    4769             :                         /* LCOV_EXCL_STOP */
    4770             :                 }
    4771             : 
    4772        1113 :                 i = i->next;
    4773             :         }
    4774             : #endif
    4775         179 : }
    4776             : 
    4777         179 : void state_write(struct snapraid_state* state)
    4778             : {
    4779             :         uint32_t crc;
    4780             :         time_t now;
    4781             : 
    4782             :         /* write all the content files */
    4783         179 :         state_write_content(state, &crc);
    4784             : 
    4785             :         /* verify the just written files */
    4786         179 :         state_verify_content(state, crc);
    4787             : 
    4788             :         /* rename the new files, over the old ones */
    4789         179 :         state_rename_content(state);
    4790             : 
    4791             :         /* log the write time of the content file */
    4792         179 :         now = time(0);
    4793         179 :         log_tag("content_info:write_unixtime:%" PRId64 "\n", (int64_t)now);
    4794         179 :         log_flush();
    4795             : 
    4796         179 :         state->need_write = 0; /* no write needed anymore */
    4797         179 :         state->checked_read = 0; /* what we wrote is not checked in read */
    4798         179 :         state->written = 1;
    4799         179 : }
    4800             : 
    4801         100 : void state_commit(struct snapraid_state* state)
    4802             : {
    4803             :         tommy_node* i;
    4804             : 
    4805             :         /* for each disk */
    4806         675 :         for (i = state->disklist; i != 0; i = i->next) {
    4807         575 :                 struct snapraid_disk* disk = i->data;
    4808             : 
    4809             :                 /* clear the dealloc list */
    4810         575 :                 tommy_list_foreach(&disk->dealloclist, (tommy_foreach_func*)dealloc_free);
    4811         575 :                 tommy_list_init(&disk->dealloclist);
    4812             :         }
    4813         100 : }
    4814             : 
    4815         137 : void state_skip(struct snapraid_state* state)
    4816             : {
    4817             :         tommy_node* i;
    4818             : 
    4819             :         /* for each disk */
    4820         925 :         for (i = state->disklist; i != 0; i = i->next) {
    4821             :                 tommy_node* j;
    4822         788 :                 struct snapraid_disk* disk = i->data;
    4823             : 
    4824         788 :                 if (!disk->skip_access)
    4825         787 :                         continue;
    4826             : 
    4827             :                 /* for each file */
    4828        1885 :                 for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
    4829        1884 :                         struct snapraid_file* file = j->data;
    4830        1884 :                         file_flag_set(file, FILE_IS_EXCLUDED);
    4831             :                 }
    4832             : 
    4833             :                 /* for each link */
    4834          57 :                 for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
    4835          56 :                         struct snapraid_link* slink = j->data;
    4836          56 :                         link_flag_set(slink, FILE_IS_EXCLUDED);
    4837             :                 }
    4838             : 
    4839             :                 /* for each dir */
    4840           1 :                 for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
    4841           0 :                         struct snapraid_dir* dir = j->data;
    4842           0 :                         dir_flag_set(dir, FILE_IS_EXCLUDED);
    4843             :                 }
    4844             :         }
    4845         137 : }
    4846             : 
    4847         137 : void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing, int filter_error)
    4848             : {
    4849             :         tommy_node* i;
    4850             :         unsigned l;
    4851             : 
    4852             :         /* if no filter, include all */
    4853         137 :         if (!filter_missing && !filter_error && tommy_list_empty(filterlist_file) && tommy_list_empty(filterlist_disk))
    4854         122 :                 return;
    4855             : 
    4856          15 :         msg_progress("Selecting...\n");
    4857             : 
    4858          29 :         for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
    4859          14 :                 struct snapraid_filter* filter = i->data;
    4860          14 :                 msg_verbose("\t%s%s\n", filter->pattern, filter->is_disk ? "//" : "");
    4861             :         }
    4862          18 :         for (i = tommy_list_head(filterlist_file); i != 0; i = i->next) {
    4863           3 :                 struct snapraid_filter* filter = i->data;
    4864           3 :                 msg_verbose("\t%s%s\n", filter->pattern, filter->is_dir ? "/" : "");
    4865             :         }
    4866          15 :         if (filter_missing)
    4867           2 :                 msg_verbose("\t<missing>\n");
    4868          15 :         if (filter_error)
    4869           2 :                 msg_verbose("\t<error>\n");
    4870             : 
    4871             :         /* for each disk */
    4872          90 :         for (i = state->disklist; i != 0; i = i->next) {
    4873             :                 tommy_node* j;
    4874          75 :                 struct snapraid_disk* disk = i->data;
    4875             : 
    4876             :                 /* if we filter for presence, we have to access the disk, so better to print something */
    4877          75 :                 if (filter_missing)
    4878          12 :                         msg_progress("Scanning disk %s...\n", disk->name);
    4879             : 
    4880             :                 /* for each file */
    4881      164081 :                 for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
    4882      164006 :                         struct snapraid_file* file = j->data;
    4883             : 
    4884      164006 :                         if (filter_path(filterlist_disk, 0, disk->name, file->sub) != 0
    4885       85645 :                                 || filter_path(filterlist_file, 0, disk->name, file->sub) != 0
    4886       82801 :                                 || filter_existence(filter_missing, disk->dir, file->sub) != 0
    4887       43605 :                                 || filter_correctness(filter_error, &state->infoarr, disk, file) != 0
    4888             :                         ) {
    4889      134658 :                                 file_flag_set(file, FILE_IS_EXCLUDED);
    4890             :                         }
    4891             :                 }
    4892             : 
    4893             :                 /* for each link */
    4894        5499 :                 for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
    4895        5424 :                         struct snapraid_link* slink = j->data;
    4896             : 
    4897        5424 :                         if (filter_path(filterlist_disk, 0, disk->name, slink->sub) != 0
    4898        2800 :                                 || filter_path(filterlist_file, 0, disk->name, slink->sub) != 0
    4899        2695 :                                 || filter_existence(filter_missing, disk->dir, slink->sub) != 0
    4900             :                         ) {
    4901        4113 :                                 link_flag_set(slink, FILE_IS_EXCLUDED);
    4902             :                         }
    4903             :                 }
    4904             : 
    4905             :                 /* for each empty dir */
    4906         107 :                 for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
    4907          32 :                         struct snapraid_dir* dir = j->data;
    4908             : 
    4909          32 :                         if (filter_emptydir(filterlist_disk, 0, disk->name, dir->sub) != 0
    4910          17 :                                 || filter_emptydir(filterlist_file, 0, disk->name, dir->sub) != 0
    4911          17 :                                 || filter_existence(filter_missing, disk->dir, dir->sub) != 0
    4912             :                         ) {
    4913          23 :                                 dir_flag_set(dir, FILE_IS_EXCLUDED);
    4914             :                         }
    4915             :                 }
    4916             :         }
    4917             : 
    4918             :         /* if we are filtering by disk, exclude any parity not explicitly included */
    4919          15 :         if (!tommy_list_empty(filterlist_disk)) {
    4920             :                 /* for each parity disk */
    4921          34 :                 for (l = 0; l < state->level; ++l) {
    4922             :                         /* check if the parity is excluded by name */
    4923          24 :                         if (filter_path(filterlist_disk, 0, lev_config_name(l), 0) != 0) {
    4924             :                                 /* excluded the parity from further operation */
    4925          18 :                                 state->parity[l].is_excluded_by_filter = 1;
    4926             :                         }
    4927             :                 }
    4928             :         } else {
    4929             :                 /* if we are filtering by file, exclude all parity */
    4930           5 :                 if (filter_missing || !tommy_list_empty(filterlist_file)) {
    4931             :                         /* for each parity disk */
    4932          16 :                         for (l = 0; l < state->level; ++l) {
    4933          13 :                                 state->parity[l].is_excluded_by_filter = 1;
    4934             :                         }
    4935             :                 }
    4936             :         }
    4937             : }
    4938             : 
    4939         254 : int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, block_off_t countmax)
    4940             : {
    4941             :         time_t now;
    4942             : 
    4943         254 :         if (state->opt.gui) {
    4944           5 :                 log_tag("run:begin:%u:%u:%u\n", blockstart, blockmax, countmax);
    4945           5 :                 log_flush();
    4946             :         }
    4947             : 
    4948         254 :         now = time(0);
    4949             : 
    4950         254 :         state->progress_whole_start = now;
    4951         254 :         state->progress_tick = 0;
    4952         254 :         state->progress_ptr = 0;
    4953         254 :         state->progress_wasted = 0;
    4954             : 
    4955             :         /* start of thermal control */
    4956         254 :         if (!state_thermal_begin(state, now))
    4957           0 :                 return -1;
    4958             : 
    4959             :         /* stop if requested */
    4960         254 :         if (global_interrupt) {
    4961             :                 /* LCOV_EXCL_START */
    4962             :                 if (!state->opt.gui) {
    4963             :                         msg_status("Not starting due to interruption\n");
    4964             :                 }
    4965             :                 log_tag("sigint:0: SIGINT received\n");
    4966             :                 log_flush();
    4967             :                 return 1;
    4968             :                 /* LCOV_EXCL_STOP */
    4969             :         }
    4970             : 
    4971         254 :         return 0;
    4972             : }
    4973             : 
    4974         254 : void state_progress_end(struct snapraid_state* state, block_off_t countpos, block_off_t countmax, data_off_t countsize, const char* msg)
    4975             : {
    4976         254 :         if (state->opt.gui) {
    4977           5 :                 log_tag("run:end\n");
    4978           5 :                 log_flush();
    4979         249 :         } else if (countmax == 0) {
    4980          21 :                 if (state->need_write || state->written) {
    4981          16 :                         msg_status("100%% completed\n");
    4982             :                 } else {
    4983           5 :                         msg_status("%s", msg);
    4984             :                 }
    4985             :         } else {
    4986             :                 time_t now;
    4987             :                 time_t elapsed;
    4988             : 
    4989         228 :                 unsigned countsize_MB = (countsize + MEGA - 1) / MEGA;
    4990             : 
    4991         228 :                 now = time(0);
    4992             : 
    4993         228 :                 elapsed = now - state->progress_whole_start - state->progress_wasted;
    4994             : 
    4995         228 :                 msg_bar("%u%% completed, %u MB accessed", muldiv(countpos, 100, countmax), countsize_MB);
    4996             : 
    4997         228 :                 msg_bar(" in %u:%02u", (unsigned)(elapsed / 3600), (unsigned)((elapsed % 3600) / 60));
    4998             : 
    4999         228 :                 msg_bar("\n");
    5000         228 :                 msg_flush();
    5001             :         }
    5002         254 : }
    5003             : 
    5004           1 : void state_progress_stop(struct snapraid_state* state)
    5005             : {
    5006             :         time_t now;
    5007             : 
    5008           1 :         now = time(0);
    5009             : 
    5010           1 :         state->progress_interruption = now;
    5011           1 : }
    5012             : 
    5013           1 : void state_progress_restart(struct snapraid_state* state)
    5014             : {
    5015             :         time_t now;
    5016             : 
    5017           1 :         now = time(0);
    5018             : 
    5019             :         /* reset the progress counter */
    5020           1 :         state->progress_tick = 0;
    5021           1 :         state->progress_ptr = 0;
    5022             : 
    5023           1 :         if (now >= state->progress_interruption) /* avoid degenerated cases when the clock is manually adjusted */
    5024           1 :                 state->progress_wasted += now - state->progress_interruption;
    5025           1 : }
    5026             : 
    5027             : /**
    5028             :  * Set the latest os_tick data in the progress vector.
    5029             :  */
    5030         551 : static void state_progress_latest(struct snapraid_state* state)
    5031             : {
    5032             :         tommy_node* i;
    5033             :         unsigned l;
    5034             : 
    5035         551 :         state->progress_tick_misc[state->progress_ptr] = state->tick_misc;
    5036         551 :         state->progress_tick_sched[state->progress_ptr] = state->tick_sched;
    5037         551 :         state->progress_tick_raid[state->progress_ptr] = state->tick_raid;
    5038         551 :         state->progress_tick_hash[state->progress_ptr] = state->tick_hash;
    5039         551 :         state->progress_tick_io[state->progress_ptr] = state->tick_io;
    5040        3732 :         for (i = state->disklist; i != 0; i = i->next) {
    5041        3181 :                 struct snapraid_disk* disk = i->data;
    5042        3181 :                 disk->progress_tick[state->progress_ptr] = disk->tick;
    5043             :         }
    5044        2933 :         for (l = 0; l < state->level; ++l)
    5045        2382 :                 state->parity[l].progress_tick[state->progress_ptr] = state->parity[l].tick;
    5046         551 : }
    5047             : 
    5048             : /**
    5049             :  * Number of columns
    5050             :  */
    5051             : #define GRAPH_COLUMN 78
    5052             : 
    5053             : /**
    5054             :  * Get the reference value, 0 if index is PROGRESS_MAX
    5055             :  */
    5056             : #define ref(map, index) (index < PROGRESS_MAX ? map[index] : 0)
    5057             : 
    5058           0 : static struct snapraid_thermal* state_thermal_find(struct snapraid_state* state, const char* name)
    5059             : {
    5060             :         tommy_node* i;
    5061           0 :         struct snapraid_thermal* found = 0;
    5062             : 
    5063           0 :         for (i = tommy_list_head(&state->thermallist); i != 0; i = i->next) {
    5064           0 :                 struct snapraid_thermal* thermal = i->data;
    5065           0 :                 if (strcmp(thermal->name, name) == 0) {
    5066           0 :                         if (found == 0
    5067             :                                 /* if multiple matches, return the one with highest temperature */
    5068           0 :                                 || found->latest_temperature < thermal->latest_temperature
    5069             :                                 /* or, if the temperature is equal, the one with the higher r_squared */
    5070           0 :                                 || (found->latest_temperature == thermal->latest_temperature && found->params.r_squared < thermal->params.r_squared)
    5071             :                         ) {
    5072           0 :                                 found = thermal;
    5073             :                         }
    5074             :                 }
    5075             :         }
    5076             : 
    5077           0 :         return found;
    5078             : }
    5079             : 
    5080             : #define THERMAL_PAD "        "
    5081             : 
    5082           1 : static void state_progress_graph(struct snapraid_state* state, struct snapraid_io* io, unsigned current, unsigned oldest)
    5083             : {
    5084             :         uint64_t v;
    5085             :         uint64_t tick_total;
    5086             :         unsigned bar;
    5087             :         tommy_node* i;
    5088             :         unsigned l;
    5089             :         size_t pad;
    5090             :         size_t pre;
    5091             : 
    5092           1 :         tick_total = 0;
    5093             : 
    5094           1 :         tick_total += state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
    5095           1 :         tick_total += state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
    5096           1 :         tick_total += state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
    5097           1 :         tick_total += state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
    5098           1 :         tick_total += state->progress_tick_io[current] - ref(state->progress_tick_io, oldest);
    5099           1 :         if (!tick_total)
    5100           0 :                 return;
    5101             : 
    5102           1 :         pad = 4;
    5103           7 :         for (i = state->disklist; i != 0; i = i->next) {
    5104           6 :                 struct snapraid_disk* disk = i->data;
    5105           6 :                 size_t len = strlen(disk->name);
    5106           6 :                 if (pad < len)
    5107           1 :                         pad = len;
    5108             :         }
    5109           7 :         for (l = 0; l < state->level; ++l) {
    5110           6 :                 size_t len = strlen(lev_config_name(l));
    5111           6 :                 if (pad < len)
    5112           2 :                         pad = len;
    5113             :         }
    5114             : 
    5115             :         /* extra space */
    5116           1 :         pad += 1;
    5117             : 
    5118           1 :         pre = 4;
    5119           1 :         if (pad + pre + 30 < GRAPH_COLUMN)
    5120           1 :                 bar = GRAPH_COLUMN - pad - pre;
    5121             :         else
    5122           0 :                 bar = 30; /* at least a bar of 30 */
    5123             : 
    5124           1 :         if (io) {
    5125           0 :                 const char* legend = "cached blocks (instant, more is better)";
    5126             : 
    5127             :                 /* refresh the cached blocks info */
    5128           0 :                 io_refresh(io);
    5129             : 
    5130           0 :                 printf("\n");
    5131             : 
    5132           0 :                 for (i = state->disklist; i != 0; i = i->next) {
    5133           0 :                         struct snapraid_disk* disk = i->data;
    5134           0 :                         v = disk->cached_blocks;
    5135           0 :                         printr(disk->name, pad);
    5136           0 :                         printf("%4" PRIu64 " | ", v);
    5137             : 
    5138           0 :                         if (disk->progress_file && disk->progress_file->sub)
    5139           0 :                                 printf("%s", disk->progress_file->sub);
    5140             :                         else
    5141           0 :                                 printf("-");
    5142             : 
    5143           0 :                         printf("\n");
    5144             :                 }
    5145             : 
    5146           0 :                 for (l = 0; l < state->level; ++l) {
    5147           0 :                         v = state->parity[l].cached_blocks;
    5148           0 :                         printr(lev_config_name(l), pad);
    5149           0 :                         printf("%4" PRIu64 " | ", v);
    5150           0 :                         printc('o', v * bar / io->io_max);
    5151           0 :                         printf("\n");
    5152             :                 }
    5153             : 
    5154           0 :                 printc(' ', pad);
    5155           0 :                 printf("     |_");
    5156           0 :                 printc('_', bar);
    5157           0 :                 printf("\n");
    5158             : 
    5159           0 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
    5160           0 :                 printf("%s", legend);
    5161           0 :                 printf("\n");
    5162             :         }
    5163             : 
    5164           1 :         printf("\n");
    5165             : 
    5166           1 :         pre = 4;
    5167           1 :         if (state->thermal_temperature_limit != 0)
    5168           0 :                 pre += strlen(THERMAL_PAD);
    5169           1 :         if (pad + pre + 30 < GRAPH_COLUMN)
    5170           1 :                 bar = GRAPH_COLUMN - pad - pre;
    5171             :         else
    5172           0 :                 bar = 30; /* at least a bar of 30 */
    5173             : 
    5174           7 :         for (i = state->disklist; i != 0; i = i->next) {
    5175           6 :                 struct snapraid_disk* disk = i->data;
    5176             : 
    5177           6 :                 v = disk->progress_tick[current] - ref(disk->progress_tick, oldest);
    5178           6 :                 printr(disk->name, pad);
    5179             : 
    5180           6 :                 if (state->thermal_temperature_limit != 0) {
    5181           0 :                         struct snapraid_thermal* thermal = state_thermal_find(state, disk->name);
    5182           0 :                         if (thermal) {
    5183           0 :                                 if (thermal->params.r_squared >= THERMAL_R_SQUARED_LIMIT)
    5184           0 :                                         printf(" %2u (%2u)", thermal->latest_temperature, (int)thermal->params.t_steady);
    5185             :                                 else
    5186           0 :                                         printf(" %2u     ", thermal->latest_temperature);
    5187             :                         } else {
    5188           0 :                                 printf(THERMAL_PAD);
    5189             :                         }
    5190             :                 }
    5191             : 
    5192           6 :                 printf("%3u%% | ", muldiv(v, 100, tick_total));
    5193           6 :                 printc('*', v * bar / tick_total);
    5194           6 :                 printf("\n");
    5195             : 
    5196             :                 /* clear the file in progress */
    5197           6 :                 disk->progress_file = 0;
    5198             :         }
    5199             : 
    5200           7 :         for (l = 0; l < state->level; ++l) {
    5201           6 :                 v = state->parity[l].progress_tick[current] - ref(state->parity[l].progress_tick, oldest);
    5202           6 :                 printr(lev_config_name(l), pad);
    5203             : 
    5204           6 :                 if (state->thermal_temperature_limit != 0) {
    5205           0 :                         struct snapraid_thermal* thermal = state_thermal_find(state, lev_config_name(l));
    5206           0 :                         if (thermal) {
    5207           0 :                                 if (thermal->params.r_squared >= THERMAL_R_SQUARED_LIMIT)
    5208           0 :                                         printf(" %2u (%2u)", thermal->latest_temperature, (int)thermal->params.t_steady);
    5209             :                                 else
    5210           0 :                                         printf(" %2u     ", thermal->latest_temperature);
    5211             :                         } else {
    5212           0 :                                 printf(THERMAL_PAD);
    5213             :                         }
    5214             :                 }
    5215             : 
    5216           6 :                 printf("%3u%% | ", muldiv(v, 100, tick_total));
    5217           6 :                 printc('*', v * bar / tick_total);
    5218           6 :                 printf("\n");
    5219             :         }
    5220             : 
    5221           1 :         v = state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
    5222           1 :         printr("raid", pad);
    5223           1 :         if (state->thermal_temperature_limit != 0)
    5224           0 :                 printf(THERMAL_PAD);
    5225           1 :         printf("%3u%% | ", muldiv(v, 100, tick_total));
    5226           1 :         printc('*', v * bar / tick_total);
    5227           1 :         printf("\n");
    5228             : 
    5229           1 :         v = state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
    5230           1 :         printr("hash", pad);
    5231           1 :         if (state->thermal_temperature_limit != 0)
    5232           0 :                 printf(THERMAL_PAD);
    5233           1 :         printf("%3u%% | ", muldiv(v, 100, tick_total));
    5234           1 :         printc('*', v * bar / tick_total);
    5235           1 :         printf("\n");
    5236             : 
    5237           1 :         v = state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
    5238           1 :         printr("sched", pad);
    5239           1 :         if (state->thermal_temperature_limit != 0)
    5240           0 :                 printf(THERMAL_PAD);
    5241           1 :         printf("%3u%% | ", muldiv(v, 100, tick_total));
    5242           1 :         printc('*', v * bar / tick_total);
    5243           1 :         printf("\n");
    5244             : 
    5245           1 :         v = state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
    5246           1 :         printr("misc", pad);
    5247           1 :         if (state->thermal_temperature_limit != 0)
    5248           0 :                 printf(THERMAL_PAD);
    5249           1 :         printf("%3u%% | ", muldiv(v, 100, tick_total));
    5250           1 :         printc('*', v * bar / tick_total);
    5251           1 :         printf("\n");
    5252             : 
    5253           1 :         printc(' ', pad);
    5254           1 :         if (state->thermal_temperature_limit != 0)
    5255           0 :                 printf(THERMAL_PAD);
    5256           1 :         printf("     |_");
    5257           1 :         printc('_', bar);
    5258           1 :         printf("\n");
    5259             : 
    5260           1 :         if (oldest == PROGRESS_MAX) {
    5261           1 :                 const char* legend = "wait time (total, less is better)";
    5262           1 :                 if (state->thermal_temperature_limit != 0)
    5263           0 :                         printf(THERMAL_PAD);
    5264           1 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
    5265           1 :                 printf("%s", legend);
    5266           1 :                 printf("\n");
    5267             :         } else {
    5268           0 :                 const char* legend_d = "wait time (last %d secs, less is better)";
    5269           0 :                 if (state->thermal_temperature_limit != 0)
    5270           0 :                         printf(THERMAL_PAD);
    5271           0 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend_d) / 2);
    5272           0 :                 printf(legend_d, PROGRESS_MAX);
    5273           0 :                 printf("\n");
    5274             :         }
    5275             : 
    5276           1 :         printf("\n");
    5277             : }
    5278             : 
    5279     1134270 : int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_off_t blockpos, block_off_t countpos, block_off_t countmax, data_off_t countsize)
    5280             : {
    5281             :         time_t now;
    5282             :         int pred;
    5283             : 
    5284     1134270 :         now = time(0);
    5285             : 
    5286             :         /* thermal measure */
    5287     1134270 :         if (now > state->thermal_latest + THERMAL_PERIOD_SECONDS || state->opt.fake_device) {
    5288        2641 :                 state->thermal_latest = now;
    5289        2641 :                 state_thermal(state, now);
    5290             :         }
    5291             : 
    5292             :         /* previous position */
    5293     1134270 :         pred = state->progress_ptr + PROGRESS_MAX - 1;
    5294     1134270 :         if (pred >= PROGRESS_MAX)
    5295     1134040 :                 pred -= PROGRESS_MAX;
    5296             : 
    5297             :         /* if the previous measure is different */
    5298     1134270 :         if (state->progress_tick == 0
    5299     1134040 :                 || state->progress_time[pred] != now
    5300             :         ) {
    5301             :                 time_t elapsed;
    5302         448 :                 unsigned out_perc = 0;
    5303         448 :                 unsigned out_size_speed = 0;
    5304         448 :                 unsigned out_block_speed = 0;
    5305         448 :                 unsigned out_cpu = 0;
    5306         448 :                 unsigned out_eta = 0;
    5307         448 :                 int out_temperature = 0;
    5308         448 :                 int out_steady = 0;
    5309         448 :                 int out_computed = 0;
    5310             : 
    5311             :                 /* store the new measure */
    5312         448 :                 state->progress_time[state->progress_ptr] = now;
    5313         448 :                 state->progress_pos[state->progress_ptr] = countpos;
    5314         448 :                 state->progress_size[state->progress_ptr] = countsize;
    5315         448 :                 state_progress_latest(state);
    5316             : 
    5317         448 :                 elapsed = now - state->progress_whole_start - state->progress_wasted;
    5318             : 
    5319             :                 /* completion percentage */
    5320         448 :                 if (countmax)
    5321         448 :                         out_perc = muldiv(countpos, 100, countmax);
    5322             : 
    5323             :                 /* if we have at least 5 measures */
    5324         448 :                 if (state->progress_tick >= 5
    5325             :                         /* or if we are running in test mode, with at least one measure */
    5326         421 :                         || (state->opt.force_progress && state->progress_tick >= 1)
    5327             :                 ) {
    5328             :                         int oldest;
    5329             :                         int past;
    5330             :                         time_t delta_time;
    5331             :                         block_off_t delta_pos;
    5332             :                         data_off_t delta_size;
    5333             :                         uint64_t tick_cpu;
    5334             :                         uint64_t tick_total;
    5335             :                         uint64_t delta_tick_cpu;
    5336             :                         uint64_t delta_tick_total;
    5337             :                         uint64_t oldest_tick_cpu;
    5338             :                         uint64_t oldest_tick_total;
    5339             :                         tommy_node* i;
    5340             : 
    5341             :                         /* number of past measures */
    5342         205 :                         past = state->progress_tick;
    5343             : 
    5344             :                         /*
    5345             :                          * Drop the oldest ones, to promptly
    5346             :                          * skip the startup phase
    5347             :                          */
    5348         205 :                         past -= past / 5;
    5349             : 
    5350             :                         /* check how much we can go in the past */
    5351         205 :                         if (past >= PROGRESS_MAX - 1) {
    5352             :                                 /*
    5353             :                                  * The vector is filled, so we are already in position
    5354             :                                  * to get the possible oldest one
    5355             :                                  */
    5356           0 :                                 oldest = state->progress_ptr + 1;
    5357             :                         } else {
    5358             :                                 /* go backward the number of positions selected */
    5359         205 :                                 oldest = state->progress_ptr + PROGRESS_MAX - past;
    5360             :                         }
    5361         205 :                         if (oldest >= PROGRESS_MAX)
    5362         205 :                                 oldest -= PROGRESS_MAX;
    5363             : 
    5364         205 :                         tick_cpu = state->progress_tick_misc[state->progress_ptr]
    5365         205 :                                 + state->progress_tick_sched[state->progress_ptr]
    5366         205 :                                 + state->progress_tick_raid[state->progress_ptr]
    5367         205 :                                 + state->progress_tick_hash[state->progress_ptr];
    5368         205 :                         tick_total = tick_cpu + state->progress_tick_io[state->progress_ptr];
    5369             : 
    5370         205 :                         oldest_tick_cpu = state->progress_tick_misc[oldest]
    5371         205 :                                 + state->progress_tick_sched[oldest]
    5372         205 :                                 + state->progress_tick_raid[oldest]
    5373         205 :                                 + state->progress_tick_hash[oldest];
    5374         205 :                         oldest_tick_total = oldest_tick_cpu + state->progress_tick_io[oldest];
    5375             : 
    5376         205 :                         delta_time = now - state->progress_time[oldest];
    5377         205 :                         delta_pos = countpos - state->progress_pos[oldest];
    5378         205 :                         delta_size = countsize - state->progress_size[oldest];
    5379         205 :                         delta_tick_cpu = tick_cpu - oldest_tick_cpu;
    5380         205 :                         delta_tick_total = tick_total - oldest_tick_total;
    5381             : 
    5382             :                         /* estimate the speed in MB/s */
    5383         205 :                         if (delta_time != 0)
    5384         205 :                                 out_size_speed = (unsigned)(delta_size / MEGA / delta_time);
    5385             : 
    5386             :                         /* estimate the speed in block/s */
    5387         205 :                         if (delta_time != 0)
    5388         205 :                                 out_block_speed = (unsigned)(delta_pos / delta_time);
    5389             : 
    5390             :                         /* estimate the cpu usage percentage */
    5391         205 :                         if (delta_tick_total != 0)
    5392          69 :                                 out_cpu = muldiv(delta_tick_cpu, 100, delta_tick_total);
    5393             : 
    5394             :                         /* estimate the remaining time in minutes */
    5395         205 :                         if (delta_pos != 0)
    5396         205 :                                 out_eta = muldiv(countmax - countpos, delta_time, delta_pos);
    5397             : 
    5398         205 :                         if (state->opt.force_stats) {
    5399           0 :                                 os_clear();
    5400           0 :                                 state_progress_graph(state, io, state->progress_ptr, oldest);
    5401             :                         }
    5402             : 
    5403         205 :                         if (state->thermal_temperature_limit != 0) {
    5404             :                                 /* get the max temperature */
    5405           4 :                                 out_temperature = 0;
    5406         124 :                                 for (i = tommy_list_head(&state->thermallist); i != 0; i = i->next) {
    5407         120 :                                         struct snapraid_thermal* thermal = i->data;
    5408         120 :                                         if (out_temperature < thermal->latest_temperature) {
    5409           4 :                                                 out_temperature = thermal->latest_temperature;
    5410           4 :                                                 if (thermal->params.r_squared >= THERMAL_R_SQUARED_LIMIT)
    5411           4 :                                                         out_steady = thermal->params.t_steady;
    5412             :                                                 else
    5413           0 :                                                         out_steady = 0;
    5414             :                                         }
    5415             :                                 }
    5416             :                         } else {
    5417         201 :                                 out_temperature = 0;
    5418         201 :                                 out_steady = 0;
    5419             :                         }
    5420             : 
    5421             :                         /* we have the output value */
    5422         205 :                         out_computed = 1;
    5423             :                 }
    5424             : 
    5425         448 :                 if (state->opt.gui) {
    5426             :                         char str_eta[32];
    5427             :                         char str_size_speed[32];
    5428             :                         char str_cpu[32];
    5429             :                         char str_temp[32];
    5430             :                         char str_steady[32];
    5431           6 :                         if (out_computed) {
    5432           1 :                                 snprintf(str_eta, sizeof(str_eta), "%u", out_eta);
    5433           1 :                                 snprintf(str_size_speed, sizeof(str_size_speed), "%u", out_size_speed);
    5434           1 :                                 snprintf(str_cpu, sizeof(str_cpu), "%u", out_cpu);
    5435             :                         } else {
    5436           5 :                                 str_eta[0] = 0;
    5437           5 :                                 str_size_speed[0] = 0;
    5438           5 :                                 str_cpu[0] = 0;
    5439             :                         }
    5440           6 :                         if (out_temperature) {
    5441           0 :                                 snprintf(str_temp, sizeof(str_eta), "%u", out_temperature);
    5442             :                         } else {
    5443           6 :                                 str_temp[0] = 0;
    5444             :                         }
    5445           6 :                         if (out_steady) {
    5446           0 :                                 snprintf(str_steady, sizeof(str_steady), "%u", out_steady);
    5447             :                         } else {
    5448           6 :                                 str_steady[0] = 0;
    5449             :                         }
    5450           6 :                         log_tag("run:pos:%u:%u:%" PRIu64 ":%u:%s:%s:%s:%" PRIu64 ":%s:%s\n", blockpos, countpos, countsize, out_perc, str_eta, str_size_speed, str_cpu, (uint64_t)elapsed, str_temp, str_steady);
    5451           6 :                         log_flush();
    5452             :                 } else {
    5453         442 :                         msg_bar("%u%%, %u MB", out_perc, (unsigned)(countsize / MEGA));
    5454         442 :                         if (out_computed) {
    5455         204 :                                 msg_bar(", %u MB/s", out_size_speed);
    5456         204 :                                 msg_bar(", %u stripe/s", out_block_speed);
    5457         204 :                                 msg_bar(", CPU %u%%", out_cpu);
    5458         204 :                                 if (out_temperature != 0) {
    5459           4 :                                         if (out_steady != 0)
    5460           4 :                                                 msg_bar(", Tmax %u (%u)", out_temperature, out_steady);
    5461             :                                         else
    5462           0 :                                                 msg_bar(", Tmax %u", out_temperature);
    5463             :                                 }
    5464         204 :                                 unsigned out_eta_minutes = out_eta / 60; /* minutes */
    5465         204 :                                 msg_bar(", %u:%02u ETA", out_eta_minutes / 60, out_eta_minutes % 60);
    5466             :                         }
    5467         442 :                         msg_bar("\r");
    5468         442 :                         msg_flush();
    5469             :                 }
    5470             : 
    5471             :                 /* next position to fill */
    5472         448 :                 ++state->progress_ptr;
    5473         448 :                 if (state->progress_ptr >= PROGRESS_MAX)
    5474           0 :                         state->progress_ptr -= PROGRESS_MAX;
    5475             : 
    5476             :                 /* one more measure */
    5477         448 :                 ++state->progress_tick;
    5478             :         }
    5479             : 
    5480             :         /* stop if requested */
    5481     1134270 :         if (global_interrupt) {
    5482             :                 /* LCOV_EXCL_START */
    5483             :                 if (!state->opt.gui) {
    5484             :                         log_fatal(EUSER, "\n");
    5485             :                         log_fatal(EUSER, "Stopping for interruption at block %u\n", blockpos);
    5486             :                 }
    5487             :                 log_tag("sigint:%u: SIGINT received\n", blockpos);
    5488             :                 log_flush();
    5489             :                 return 1;
    5490             :                 /* LCOV_EXCL_STOP */
    5491             :         }
    5492             : 
    5493     1134270 :         return 0;
    5494             : }
    5495             : 
    5496         123 : void state_usage_waste(struct snapraid_state* state)
    5497             : {
    5498         123 :         uint64_t now = os_tick();
    5499             : 
    5500         123 :         state->tick_last = now;
    5501         123 : }
    5502             : 
    5503     1170202 : void state_usage_misc(struct snapraid_state* state)
    5504             : {
    5505     1170202 :         uint64_t now = os_tick();
    5506     1170202 :         uint64_t delta = now - state->tick_last;
    5507             : 
    5508             :         /* increment the time spent in computations */
    5509     1170202 :         state->tick_misc += delta;
    5510             : 
    5511     1170202 :         state->tick_last = now;
    5512     1170202 : }
    5513             : 
    5514      168059 : void state_usage_sched(struct snapraid_state* state)
    5515             : {
    5516      168059 :         uint64_t now = os_tick();
    5517      168059 :         uint64_t delta = now - state->tick_last;
    5518             : 
    5519             :         /* increment the time spent in computations */
    5520      168059 :         state->tick_sched += delta;
    5521             : 
    5522      168059 :         state->tick_last = now;
    5523      168059 : }
    5524             : 
    5525      158455 : void state_usage_raid(struct snapraid_state* state)
    5526             : {
    5527      158455 :         uint64_t now = os_tick();
    5528      158455 :         uint64_t delta = now - state->tick_last;
    5529             : 
    5530             :         /* increment the time spent in computations */
    5531      158455 :         state->tick_raid += delta;
    5532             : 
    5533      158455 :         state->tick_last = now;
    5534      158455 : }
    5535             : 
    5536      831491 : void state_usage_hash(struct snapraid_state* state)
    5537             : {
    5538      831491 :         uint64_t now = os_tick();
    5539      831491 :         uint64_t delta = now - state->tick_last;
    5540             : 
    5541             :         /* increment the time spent in computations */
    5542      831491 :         state->tick_hash += delta;
    5543             : 
    5544      831491 :         state->tick_last = now;
    5545      831491 : }
    5546             : 
    5547      996594 : void state_usage_file(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_file* file)
    5548             : {
    5549             :         (void)state;
    5550             : 
    5551      996594 :         disk->progress_file = file;
    5552      996594 : }
    5553             : 
    5554     1001281 : void state_usage_disk(struct snapraid_state* state, struct snapraid_handle* handle_map, unsigned* waiting_map, unsigned waiting_mac)
    5555             : {
    5556     1001281 :         uint64_t now = os_tick();
    5557     1001281 :         uint64_t delta = now - state->tick_last;
    5558             :         unsigned i;
    5559             : 
    5560             :         /* increment the time spent in the data disks */
    5561     1071739 :         for (i = 0; i < waiting_mac; ++i) {
    5562       70458 :                 struct snapraid_disk* disk = handle_map[waiting_map[i]].disk;
    5563             : 
    5564       70458 :                 if (!disk)
    5565           0 :                         continue;
    5566             : 
    5567       70458 :                 disk->tick += delta;
    5568             :         }
    5569     1001281 :         state->tick_io += delta;
    5570             : 
    5571     1001281 :         state->tick_last = now;
    5572     1001281 : }
    5573             : 
    5574      802781 : void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, unsigned waiting_mac)
    5575             : {
    5576      802781 :         uint64_t now = os_tick();
    5577      802781 :         uint64_t delta = now - state->tick_last;
    5578             :         unsigned i;
    5579             : 
    5580             :         /* increment the time spent in the parity disk */
    5581      821832 :         for (i = 0; i < waiting_mac; ++i)
    5582       19051 :                 state->parity[waiting_map[i]].tick += delta;
    5583      802781 :         state->tick_io += delta;
    5584             : 
    5585      802781 :         state->tick_last = now;
    5586      802781 : }
    5587             : 
    5588         103 : void state_usage_print(struct snapraid_state* state)
    5589             : {
    5590             :         /* set the latest data */
    5591         103 :         state_progress_latest(state);
    5592             : 
    5593         103 :         if (msg_level < MSG_PROGRESS)
    5594         102 :                 return;
    5595             : 
    5596             :         /* print a graph for it */
    5597           1 :         state_progress_graph(state, 0, state->progress_ptr, PROGRESS_MAX);
    5598             : }
    5599             : 
    5600         587 : void state_fscheck(struct snapraid_state* state, const char* ope)
    5601             : {
    5602             :         tommy_node* i;
    5603             : 
    5604             :         /* check the file-system on all disks */
    5605        3956 :         for (i = state->disklist; i != 0; i = i->next) {
    5606        3369 :                 struct snapraid_disk* disk = i->data;
    5607             : 
    5608        3369 :                 if (fs_check(disk) != 0) {
    5609             :                         /* LCOV_EXCL_START */
    5610             :                         log_fatal(EINTERNAL, "Internal inconsistency: File-system check for disk '%s' %s\n", disk->name, ope);
    5611             :                         os_abort();
    5612             :                         /* LCOV_EXCL_STOP */
    5613             :                 }
    5614             :         }
    5615         587 : }
    5616             : 
    5617           1 : void generate_configuration(const char* path)
    5618             : {
    5619             :         struct snapraid_state state;
    5620             :         struct snapraid_content* content;
    5621             :         char esc_buffer[ESC_MAX];
    5622             :         unsigned l, s;
    5623             :         tommy_node* j;
    5624             : 
    5625           1 :         state_init(&state);
    5626             : 
    5627             :         /* mark that we are without a configuration file */
    5628           1 :         state.no_conf = 1;
    5629             : 
    5630             :         /* create the dummy content entry */
    5631           1 :         content = content_alloc(path, -1);
    5632             : 
    5633             :         /* adds the content entry */
    5634           1 :         tommy_list_insert_tail(&state.contentlist, &content->node, content);
    5635             : 
    5636             :         /* read the content file */
    5637           1 :         state_read(&state);
    5638             : 
    5639             :         /* output a dummy configuration file */
    5640           1 :         printf("# Configuration file generated from %s\n", path);
    5641           1 :         printf("\n");
    5642           1 :         printf("# Use this blocksize\n");
    5643           1 :         printf("blocksize %u\n", state.block_size / KIBI);
    5644           1 :         printf("\n");
    5645           1 :         printf("# Use this hashsize\n");
    5646           1 :         printf("hashsize %u\n", BLOCK_HASH_SIZE);
    5647           1 :         printf("\n");
    5648           7 :         for (l = 0; l < state.level; ++l) {
    5649           6 :                 printf("# Set the correct path for the %s files\n", lev_name(l));
    5650           6 :                 printf("# You had %u of them:\n", state.parity[l].split_mac);
    5651          30 :                 for (s = 0; s < state.parity[l].split_mac; ++s) {
    5652          24 :                         printf("# %u:\n", s);
    5653          24 :                         printf("# PATH:");
    5654          24 :                         if (state.parity[l].split_map[s].path[0])
    5655          24 :                                 printf("%s", state.parity[l].split_map[s].path);
    5656             :                         else
    5657           0 :                                 printf("?");
    5658          24 :                         printf("\n");
    5659          24 :                         printf("# SIZE:");
    5660          24 :                         if (state.parity[l].split_map[s].size != PARITY_SIZE_INVALID)
    5661          24 :                                 printf("%" PRIu64, state.parity[l].split_map[s].size);
    5662             :                         else
    5663           0 :                                 printf("?");
    5664          24 :                         printf("\n");
    5665          24 :                         printf("# UUID:");
    5666          24 :                         if (state.parity[l].split_map[s].uuid[0])
    5667          24 :                                 printf("%s", state.parity[l].split_map[s].uuid);
    5668             :                         else
    5669           0 :                                 printf("?");
    5670          24 :                         printf("\n");
    5671          24 :                         printf("#\n");
    5672             :                 }
    5673           6 :                 printf("%s ENTER_HERE_THE_PARITY_FILES_COMMA_SEPARATED\n", lev_config_name(l));
    5674           6 :                 printf("\n");
    5675             :         }
    5676           1 :         printf("# Add any other content file\n");
    5677           1 :         printf("content %s\n", path);
    5678           1 :         printf("\n");
    5679           7 :         for (j = state.maplist; j; j = j->next) {
    5680           6 :                 struct snapraid_map* map = j->data;
    5681             :                 struct snapraid_disk* disk;
    5682           6 :                 printf("# Set the correct dir for disk '%s'\n", map->name);
    5683           6 :                 if (map->uuid[0])
    5684           6 :                         printf("# Disk '%s' is the one with id '%s'\n", map->name, map->uuid);
    5685           6 :                 disk = find_disk_by_name(&state, map->name);
    5686           6 :                 if (disk && disk->filelist) {
    5687           6 :                         struct snapraid_file* file = disk->filelist->data;
    5688           6 :                         if (file) {
    5689           6 :                                 printf("# and containing: %s\n", fmt_poll(disk, file->sub, esc_buffer));
    5690             :                         }
    5691             :                 }
    5692           6 :                 printf("data %s ENTER_HERE_THE_DIR\n", map->name);
    5693           6 :                 printf("\n");
    5694             :         }
    5695             : 
    5696           1 :         state_done(&state);
    5697           1 : }
    5698             : 
    5699             : 
    5700             : /**
    5701             :  * Flush and sync the parity files.
    5702             :  */
    5703         101 : int state_flush(struct snapraid_state* state, struct snapraid_io* io, struct snapraid_parity_handle* parity_handle, block_off_t blockcur)
    5704             : {
    5705             :         unsigned l;
    5706             : 
    5707         101 :         if (io)
    5708         101 :                 io_flush(io);
    5709             : 
    5710             :         /* flush all parity handles to ensure data is written to disk */
    5711         622 :         for (l = 0; l < state->level; ++l) {
    5712         521 :                 int ret = parity_sync(&parity_handle[l]);
    5713         521 :                 if (ret == -1) {
    5714             :                         /* LCOV_EXCL_START */
    5715             :                         log_tag("parity_error_io:%u:%s: Sync error. %s.\n", blockcur, lev_config_name(l), strerror(errno));
    5716             :                         log_fatal(errno, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", lev_config_name(l));
    5717             :                         return -1;
    5718             :                         /* LCOV_EXCL_STOP */
    5719             :                 }
    5720             :         }
    5721             : 
    5722         101 :         return 0;
    5723             : }
    5724             : 
    5725          66 : void state_load_ignore_file(tommy_list* filter_list, const char* path, const char* sub)
    5726             : {
    5727             :         STREAM* f;
    5728             :         int line;
    5729             : 
    5730          66 :         f = sopen_read(path, 0);
    5731          66 :         if (!f) {
    5732             :                 /* LCOV_EXCL_START */
    5733             :                 log_error(errno, "Error opening the ignore file '%s'. %s.\n", path, strerror(errno));
    5734             :                 return;
    5735             :                 /* LCOV_EXCL_STOP */
    5736             :         }
    5737             : 
    5738          66 :         line = 1;
    5739          66 :         while (1) {
    5740             :                 char buffer[PATH_MAX];
    5741             :                 int ret;
    5742             :                 int c;
    5743             : 
    5744             :                 /* skip initial spaces */
    5745         132 :                 sgetspace(f);
    5746             : 
    5747             :                 /* get the whole line */
    5748         132 :                 ret = sgetline(f, buffer, sizeof(buffer));
    5749         132 :                 if (ret < 0) {
    5750             :                         /* LCOV_EXCL_START */
    5751             :                         log_error(EUSER, "Too long line in '%s' at line %u\n", path, line);
    5752             :                         break;
    5753             :                         /* LCOV_EXCL_STOP */
    5754             :                 }
    5755             : 
    5756         132 :                 if (buffer[0] == 0) {
    5757             :                         /* allow empty lines */
    5758          66 :                 } else if (buffer[0] == '#') {
    5759             :                         /* ignore comment lines */
    5760             :                 } else {
    5761             :                         struct snapraid_filter* filter;
    5762             : 
    5763          66 :                         filter = filter_alloc_file(-1, sub, buffer);
    5764          66 :                         if (!filter) {
    5765             :                                 /* LCOV_EXCL_START */
    5766             :                                 log_error(EUSER, "Invalid ignore specification '%s' in '%s' at line %u\n", buffer, path, line);
    5767             :                                 break;
    5768             :                                 /* LCOV_EXCL_STOP */
    5769             :                         }
    5770             : 
    5771          66 :                         tommy_list_insert_tail(filter_list, &filter->node, filter);
    5772             :                 }
    5773             : 
    5774             :                 /* next line */
    5775         132 :                 c = sgeteol(f);
    5776         132 :                 if (c == EOF) {
    5777          66 :                         break;
    5778             :                 }
    5779          66 :                 if (c != '\n') {
    5780             :                         /* LCOV_EXCL_START */
    5781             :                         log_error(EUSER, "Extra data in '%s' at line %u\n", path, line);
    5782             :                         break;
    5783             :                         /* LCOV_EXCL_STOP */
    5784             :                 }
    5785          66 :                 ++line;
    5786             :         }
    5787             : 
    5788          66 :         if (serror(f)) {
    5789             :                 /* LCOV_EXCL_START */
    5790             :                 log_error(errno, "Error reading the ignore file '%s' at line %u\n", path, line);
    5791             :                 /* LCOV_EXCL_STOP */
    5792             :         }
    5793             : 
    5794          66 :         sclose(f);
    5795             : }
    5796             : 
    5797             : #define SNAPSHOT_CONTAINER ".snapraid"
    5798             : #define SNAPSHOT_PENDING "pending"
    5799             : #define SNAPSHOT_STABLE "stable"
    5800             : 
    5801          54 : static int state_snapshot_dir(const char* dir, struct snapraid_disk* disk)
    5802             : {
    5803             :         struct stat st;
    5804             : 
    5805          54 :         if (lstat(dir, &st) != 0)
    5806           9 :                 return -1;
    5807             : 
    5808          45 :         if (!S_ISDIR(st.st_mode))
    5809           0 :                 return -1;
    5810             : 
    5811          45 :         if (disk) {
    5812          43 :                 pathcpy(disk->dir, sizeof(disk->dir), dir);
    5813          43 :                 disk->dir_device = st.st_dev;
    5814             :         }
    5815             : 
    5816          45 :         return 0;
    5817             : }
    5818             : 
    5819         107 : int state_snapshot_new(struct snapraid_state* state)
    5820             : {
    5821         107 :         if (!state->snapshot)
    5822          97 :                 return 0;
    5823             : 
    5824          40 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    5825          30 :                 struct snapraid_disk* disk = i->data;
    5826             :                 char root[PATH_MAX];
    5827             :                 char container[PATH_MAX];
    5828             :                 char vol[PATH_MAX];
    5829             :                 int ret;
    5830             : 
    5831             :                 /* check if it supports snapshot */
    5832          30 :                 if (fssnapshot(disk->mount_point, root, sizeof(root)) != 0)
    5833           0 :                         continue;
    5834             : 
    5835          30 :                 size_t root_len = strlen(root);
    5836             : 
    5837          30 :                 pathcpy(container, sizeof(container), root);
    5838          30 :                 pathcat(container, sizeof(container), SNAPSHOT_CONTAINER);
    5839             : 
    5840             :                 /* create the snapshot container directory if not existing */
    5841          30 :                 ret = mkdir(container, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
    5842          30 :                 if (ret != 0 && errno != EEXIST) {
    5843           0 :                         log_error(errno, "Error creating '%s'. %s.\n", container, strerror(errno));
    5844           0 :                         return -1;
    5845             :                 }
    5846             : 
    5847             :                 /* delete a potential previous pending snapshot */
    5848          30 :                 if (fssnapshot_delete(container, SNAPSHOT_PENDING) != 0) {
    5849           0 :                         log_fatal(errno, "Failed to delete pending snapshot in '%s'. %s.\n", container, strerror(errno));
    5850           0 :                         return -1;
    5851             :                 }
    5852             : 
    5853             :                 /* create a new snapshot */
    5854          30 :                 if (fssnapshot_create(root, container, SNAPSHOT_PENDING) != 0) {
    5855           0 :                         log_fatal(errno, "Failed to create pending snapshot '%s'. %s.\n", container, strerror(errno));
    5856           0 :                         return -1;
    5857             :                 }
    5858             : 
    5859          30 :                 msg_progress("Created disk %s pending snapshot...\n", disk->name);
    5860             : 
    5861             :                 /* setup the snapshot in use */
    5862          30 :                 pathcpy(vol, sizeof(vol), container);
    5863          30 :                 pathcat(vol, sizeof(vol), "/" SNAPSHOT_PENDING "/");
    5864          30 :                 pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    5865             : 
    5866          30 :                 if (state_snapshot_dir(vol, disk) != 0) {
    5867           0 :                         log_error(errno, "Error stating snapshot dir '%s'. %s.\n", vol, strerror(errno));
    5868           0 :                         return -1;
    5869             :                 }
    5870             : 
    5871             :                 /* store the snapshot root */
    5872          30 :                 pathcpy(disk->snapshot_root, sizeof(disk->snapshot_root), root);
    5873             :         }
    5874             : 
    5875          10 :         return 0;
    5876             : }
    5877             : 
    5878          80 : int state_snapshot_commit(struct snapraid_state* state)
    5879             : {
    5880          80 :         if (!state->snapshot)
    5881          74 :                 return 0;
    5882             : 
    5883          24 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    5884          18 :                 struct snapraid_disk* disk = i->data;
    5885             :                 char container[PATH_MAX];
    5886             : 
    5887             :                 /* check if it supports snapshot */
    5888          18 :                 if (disk->snapshot_root[0] == 0)
    5889           0 :                         continue;
    5890             : 
    5891          18 :                 pathcpy(container, sizeof(container), disk->snapshot_root);
    5892          18 :                 pathcat(container, sizeof(container), SNAPSHOT_CONTAINER);
    5893             : 
    5894             :                 /* delete a potential previous stable snapshot */
    5895          18 :                 if (fssnapshot_delete(container, SNAPSHOT_STABLE) != 0) {
    5896           0 :                         log_fatal(errno, "Failed to delete stable snapshot in '%s'. %s.\n", container, strerror(errno));
    5897           0 :                         return -1;
    5898             :                 }
    5899             : 
    5900             :                 /* rename pending to stable */
    5901          18 :                 if (fssnapshot_rename(container, SNAPSHOT_PENDING, SNAPSHOT_STABLE) != 0) {
    5902           0 :                         log_fatal(errno, "Failed to rename snapshot in '%s'. %s.\n", container, strerror(errno));
    5903           0 :                         return -1;
    5904             :                 }
    5905             : 
    5906          18 :                 msg_progress("Committed disk %s stable snapshot...\n", disk->name);
    5907             :         }
    5908             : 
    5909           6 :         return 0;
    5910             : }
    5911             : 
    5912          12 : void state_snapshot_read(struct snapraid_state* state)
    5913             : {
    5914          12 :         if (!state->snapshot)
    5915          11 :                 return;
    5916             : 
    5917           4 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    5918           3 :                 struct snapraid_disk* disk = i->data;
    5919             :                 char root[PATH_MAX];
    5920             :                 char container[PATH_MAX];
    5921             :                 char vol[PATH_MAX];
    5922             : 
    5923             :                 /* check if it supports snapshot */
    5924           3 :                 if (fssnapshot(disk->mount_point, root, sizeof(root)) != 0)
    5925           0 :                         continue;
    5926             : 
    5927           3 :                 size_t root_len = strlen(root);
    5928             : 
    5929           3 :                 pathcpy(container, sizeof(container), root);
    5930           3 :                 pathcat(container, sizeof(container), SNAPSHOT_CONTAINER);
    5931             : 
    5932           3 :                 pathcpy(vol, sizeof(vol), container);
    5933           3 :                 pathcat(vol, sizeof(vol), "/" SNAPSHOT_PENDING "/");
    5934           3 :                 pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    5935           3 :                 if (state_snapshot_dir(vol, disk) == 0) {
    5936           0 :                         msg_progress("Using disk %s pending snapshot...\n", disk->name);
    5937             :                 } else {
    5938           3 :                         pathcpy(vol, sizeof(vol), container);
    5939           3 :                         pathcat(vol, sizeof(vol), "/" SNAPSHOT_STABLE "/");
    5940           3 :                         pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    5941           3 :                         if (state_snapshot_dir(vol, disk) == 0) {
    5942           3 :                                 msg_progress("Using disk %s stable snapshot...\n", disk->name);
    5943             :                         } else {
    5944             :                                 /* fallback to standard mount point */
    5945           0 :                                 msg_progress("Using disk %s live filesystem...\n", disk->name);
    5946             :                         }
    5947             :                 }
    5948             :         }
    5949             : }
    5950             : 
    5951         134 : void state_snapshot_write(struct snapraid_state* state, tommy_list* filterlist_disk)
    5952             : {
    5953         134 :         if (!state->snapshot)
    5954         124 :                 return;
    5955             : 
    5956             :         /* if the filter disk is empty, all disks are potentially written */
    5957          10 :         if (tommy_list_empty(filterlist_disk)) {
    5958           5 :                 msg_progress("Using live filesystems...\n");
    5959           5 :                 return;
    5960             :         }
    5961             : 
    5962          20 :         for (tommy_node* i = state->disklist; i != 0; i = i->next) {
    5963          15 :                 struct snapraid_disk* disk = i->data;
    5964             :                 char root[PATH_MAX];
    5965             :                 char container[PATH_MAX];
    5966             :                 char vol[PATH_MAX];
    5967             : 
    5968             :                 /* check if it supports snapshot */
    5969          15 :                 if (fssnapshot(disk->mount_point, root, sizeof(root)) != 0)
    5970           5 :                         continue;
    5971             : 
    5972          15 :                 size_t root_len = strlen(root);
    5973             : 
    5974             :                 /* if the disk is filtered in, it could be written, then we cannot use the snapshot */
    5975          15 :                 if (filter_path(filterlist_disk, 0, disk->name, 0) == 0) {
    5976           5 :                         msg_progress("Using disk %s live filesystem...\n", disk->name);
    5977           5 :                         continue;
    5978             :                 }
    5979             : 
    5980          10 :                 pathcpy(container, sizeof(container), root);
    5981          10 :                 pathcat(container, sizeof(container), SNAPSHOT_CONTAINER);
    5982             : 
    5983          10 :                 pathcpy(vol, sizeof(vol), container);
    5984          10 :                 pathcat(vol, sizeof(vol), "/" SNAPSHOT_PENDING "/");
    5985          10 :                 pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    5986          10 :                 if (state_snapshot_dir(vol, disk) == 0) {
    5987           4 :                         msg_progress("Using disk %s pending snapshot...\n", disk->name);
    5988             : 
    5989             :                         /* if there is a dealloc list */
    5990           4 :                         if (!tommy_list_empty(&disk->dealloclist)) {
    5991           2 :                                 pathcpy(vol, sizeof(vol), container);
    5992           2 :                                 pathcat(vol, sizeof(vol), "/" SNAPSHOT_STABLE "/");
    5993           2 :                                 pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    5994             : 
    5995             :                                 /* if there is a previous snapshot */
    5996           2 :                                 if (state_snapshot_dir(vol, 0) == 0) {
    5997           2 :                                         msg_progress("Importing disk %s stable snapshot %" PRIu64 " deallocated files...\n", disk->name, tommy_list_count(&disk->dealloclist));
    5998             : 
    5999           2 :                                         state_dealloc(state, vol, &disk->dealloclist);
    6000             :                                 }
    6001             :                         }
    6002             :                 } else {
    6003           6 :                         pathcpy(vol, sizeof(vol), container);
    6004           6 :                         pathcat(vol, sizeof(vol), "/" SNAPSHOT_STABLE "/");
    6005           6 :                         pathcat(vol, sizeof(vol), disk->mount_point + root_len);
    6006           6 :                         if (state_snapshot_dir(vol, disk) == 0) {
    6007           6 :                                 msg_progress("Using disk %s stable snapshot...\n", disk->name);
    6008             :                         } else {
    6009             :                                 /* fallback to standard mount point */
    6010           0 :                                 msg_progress("Using disk %s live filesystem...\n", disk->name);
    6011             :                         }
    6012             :                 }
    6013             :         }
    6014             : }
    6015             : 

Generated by: LCOV version 1.0