LCOV - code coverage report
Current view: top level - cmdline - state.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1478 1614 91.6 %
Date: 2017-11-06 22:14:04 Functions: 45 46 97.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "elem.h"
      21             : #include "import.h"
      22             : #include "search.h"
      23             : #include "state.h"
      24             : #include "support.h"
      25             : #include "parity.h"
      26             : #include "stream.h"
      27             : #include "handle.h"
      28             : #include "io.h"
      29             : #include "raid/raid.h"
      30             : #include "raid/cpu.h"
      31             : 
      32             : /**
      33             :  * Configure the multithread support.
      34             :  *
      35             :  * Multi thread for write could be either faster or slower, depending
      36             :  * on the specific conditions. With multithreads it's likely faster
      37             :  * writing to disk, but you'll need to access multiple times the same data,
      38             :  * being potentially slower.
      39             :  *
      40             :  * For upcoming SnapRAID version it's planned to add a mutex protection
      41             :  * at the file-system structure, slowing down multiple data access,
      42             :  * so we disable it.
      43             :  *
      44             :  * Multi thread for verify is instead always generally faster,
      45             :  * so we enable it if possible.
      46             :  */
      47             : #if HAVE_PTHREAD
      48             : /* #define HAVE_MT_WRITE 1 */
      49             : #define HAVE_MT_VERIFY 1
      50             : #endif
      51             : 
      52           7 : const char* lev_name(unsigned l)
      53             : {
      54           7 :         switch (l) {
      55           2 :         case 0 : return "Parity";
      56           1 :         case 1 : return "2-Parity";
      57           1 :         case 2 : return "3-Parity";
      58           1 :         case 3 : return "4-Parity";
      59           1 :         case 4 : return "5-Parity";
      60           1 :         case 5 : return "6-Parity";
      61             :         }
      62             : 
      63           0 :         return 0;
      64             : }
      65             : 
      66      186393 : const char* lev_config_name(unsigned l)
      67             : {
      68      186393 :         switch (l) {
      69       87377 :         case 0 : return "parity";
      70       59910 :         case 1 : return "2-parity";
      71        9816 :         case 2 : return "3-parity";
      72        9791 :         case 3 : return "4-parity";
      73        9766 :         case 4 : return "5-parity";
      74        9733 :         case 5 : return "6-parity";
      75             :         }
      76             : 
      77           0 :         return 0;
      78             : }
      79             : 
      80        6345 : static int lev_config_scan(const char* s, unsigned* level, unsigned* mode)
      81             : {
      82        6345 :         if (strcmp(s, "parity") == 0 || strcmp(s, "1-parity") == 0) {
      83         449 :                 *level = 0;
      84         449 :                 return 0;
      85             :         }
      86             : 
      87        5896 :         if (strcmp(s, "q-parity") == 0 || strcmp(s, "2-parity") == 0) {
      88         225 :                 *level = 1;
      89         225 :                 return 0;
      90             :         }
      91             : 
      92        5671 :         if (strcmp(s, "r-parity") == 0 || strcmp(s, "3-parity") == 0) {
      93         200 :                 *level = 2;
      94         200 :                 return 0;
      95             :         }
      96             : 
      97        5471 :         if (strcmp(s, "4-parity") == 0) {
      98         197 :                 *level = 3;
      99         197 :                 return 0;
     100             :         }
     101             : 
     102        5274 :         if (strcmp(s, "5-parity") == 0) {
     103         194 :                 *level = 4;
     104         194 :                 return 0;
     105             :         }
     106             : 
     107        5080 :         if (strcmp(s, "6-parity") == 0) {
     108         190 :                 *level = 5;
     109         190 :                 return 0;
     110             :         }
     111             : 
     112        4890 :         if (strcmp(s, "z-parity") == 0) {
     113           0 :                 *level = 2;
     114           0 :                 if (mode)
     115           0 :                         *mode = RAID_MODE_VANDERMONDE;
     116           0 :                 return 0;
     117             :         }
     118             : 
     119        4890 :         return -1;
     120             : }
     121             : 
     122         265 : const char* lev_raid_name(unsigned mode, unsigned n)
     123             : {
     124         265 :         switch (n) {
     125          41 :         case 1 : return "par1";
     126          25 :         case 2 : return "par2";
     127           3 :         case 3 : if (mode == RAID_MODE_CAUCHY)
     128           3 :                         return "par3";
     129             :                 else
     130           0 :                         return "parz";
     131           3 :         case 4 : return "par4";
     132           4 :         case 5 : return "par5";
     133         189 :         case 6 : return "par6";
     134             :         }
     135             : 
     136           0 :         return 0;
     137             : }
     138             : 
     139         267 : void state_init(struct snapraid_state* state)
     140             : {
     141             :         unsigned l, s;
     142             : 
     143         267 :         memset(&state->opt, 0, sizeof(state->opt));
     144         267 :         state->filter_hidden = 0;
     145         267 :         state->autosave = 0;
     146         267 :         state->need_write = 0;
     147         267 :         state->checked_read = 0;
     148         267 :         state->block_size = 256 * KIBI; /* default 256 KiB */
     149         267 :         state->raid_mode = RAID_MODE_CAUCHY;
     150         267 :         state->file_mode = ADVISE_DEFAULT;
     151        1869 :         for (l = 0; l < LEV_MAX; ++l) {
     152        1602 :                 state->parity[l].split_mac = 0;
     153       14418 :                 for (s = 0; s < SPLIT_MAX; ++s) {
     154       12816 :                         state->parity[l].split_map[s].path[0] = 0;
     155       12816 :                         state->parity[l].split_map[s].uuid[0] = 0;
     156       12816 :                         state->parity[l].split_map[s].size = PARITY_SIZE_INVALID;
     157       12816 :                         state->parity[l].split_map[s].device = 0;
     158             :                 }
     159        1602 :                 state->parity[l].smartctl[0] = 0;
     160        1602 :                 state->parity[l].total_blocks = 0;
     161        1602 :                 state->parity[l].free_blocks = 0;
     162        1602 :                 state->parity[l].skip_access = 0;
     163        1602 :                 state->parity[l].tick = 0;
     164        1602 :                 state->parity[l].cached = 0;
     165        1602 :                 state->parity[l].is_excluded_by_filter = 0;
     166             :         }
     167         267 :         state->tick_io = 0;
     168         267 :         state->tick_misc = 0;
     169         267 :         state->tick_sched = 0;
     170         267 :         state->tick_raid = 0;
     171         267 :         state->tick_hash = 0;
     172         267 :         state->tick_last = tick();
     173         267 :         state->share[0] = 0;
     174         267 :         state->pool[0] = 0;
     175         267 :         state->pool_device = 0;
     176         267 :         state->lockfile[0] = 0;
     177         267 :         state->level = 1; /* default is the lowest protection */
     178         267 :         state->clear_past_hash = 0;
     179         267 :         state->no_conf = 0;
     180             : 
     181         267 :         tommy_list_init(&state->disklist);
     182         267 :         tommy_list_init(&state->maplist);
     183         267 :         tommy_list_init(&state->contentlist);
     184         267 :         tommy_list_init(&state->filterlist);
     185         267 :         tommy_list_init(&state->importlist);
     186         267 :         tommy_hashdyn_init(&state->importset);
     187         267 :         tommy_hashdyn_init(&state->previmportset);
     188         267 :         tommy_hashdyn_init(&state->searchset);
     189         267 :         tommy_arrayblkof_init(&state->infoarr, sizeof(snapraid_info));
     190         267 : }
     191             : 
     192         244 : void state_done(struct snapraid_state* state)
     193             : {
     194         244 :         tommy_list_foreach(&state->disklist, (tommy_foreach_func*)disk_free);
     195         244 :         tommy_list_foreach(&state->maplist, (tommy_foreach_func*)map_free);
     196         244 :         tommy_list_foreach(&state->contentlist, (tommy_foreach_func*)content_free);
     197         244 :         tommy_list_foreach(&state->filterlist, (tommy_foreach_func*)filter_free);
     198         244 :         tommy_list_foreach(&state->importlist, (tommy_foreach_func*)import_file_free);
     199         244 :         tommy_hashdyn_foreach(&state->searchset, (tommy_foreach_func*)search_file_free);
     200         244 :         tommy_hashdyn_done(&state->importset);
     201         244 :         tommy_hashdyn_done(&state->previmportset);
     202         244 :         tommy_hashdyn_done(&state->searchset);
     203         244 :         tommy_arrayblkof_done(&state->infoarr);
     204         244 : }
     205             : 
     206             : /**
     207             :  * Check the configuration.
     208             :  */
     209         265 : static void state_config_check(struct snapraid_state* state, const char* path, tommy_list* filterlist_disk)
     210             : {
     211             :         tommy_node* i;
     212             :         unsigned l, s;
     213             : 
     214             :         /* check for parity level */
     215         265 :         if (state->raid_mode == RAID_MODE_VANDERMONDE) {
     216           0 :                 if (state->level > 3) {
     217             :                         /* LCOV_EXCL_START */
     218             :                         log_fatal("If you use the z-parity you cannot have more than 3 parities.\n");
     219             :                         exit(EXIT_FAILURE);
     220             :                         /* LCOV_EXCL_STIO */
     221             :                 }
     222             :         }
     223             : 
     224             :         for (l = 0; l < state->level; ++l) {
     225             :                 if (state->parity[l].split_mac == 0) {
     226             :                         /* LCOV_EXCL_START */
     227             :                         log_fatal("No '%s' specification in '%s'\n", lev_config_name(l), path);
     228             :                         exit(EXIT_FAILURE);
     229             :                         /* LCOV_EXCL_STOP */
     230             :                 }
     231             :         }
     232             : 
     233         265 :         if (tommy_list_empty(&state->contentlist)) {
     234             :                 /* LCOV_EXCL_START */
     235             :                 log_fatal("No 'content' specification in '%s'\n", path);
     236             :                 exit(EXIT_FAILURE);
     237             :                 /* LCOV_EXCL_STOP */
     238             :         }
     239             : 
     240             :         /* check for equal paths */
     241        1796 :         for (i = state->contentlist; i != 0; i = i->next) {
     242        1531 :                 struct snapraid_content* content = i->data;
     243             : 
     244        9917 :                 for (l = 0; l < state->level; ++l) {
     245       41930 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     246       33544 :                                 if (pathcmp(state->parity[l].split_map[s].path, content->content) == 0) {
     247             :                                         /* LCOV_EXCL_START */
     248             :                                         log_fatal("Same path used for '%s' and 'content' as '%s'\n", lev_config_name(l), content->content);
     249             :                                         exit(EXIT_FAILURE);
     250             :                                         /* LCOV_EXCL_STOP */
     251             :                                 }
     252             :                         }
     253             :                 }
     254             :         }
     255             : 
     256             :         /* check device of data disks */
     257         265 :         if (!state->opt.skip_device && !state->opt.skip_disk_access) {
     258           0 :                 for (i = state->disklist; i != 0; i = i->next) {
     259             :                         tommy_node* j;
     260           0 :                         struct snapraid_disk* disk = i->data;
     261             : 
     262             :                         /* skip data disks that are not accessible */
     263           0 :                         if (disk->skip_access)
     264           0 :                                 continue;
     265             : 
     266             : #ifdef _WIN32
     267             :                         if (disk->device == 0) {
     268             :                                 /* LCOV_EXCL_START */
     269             :                                 log_fatal("Disk '%s' has a zero serial number.\n", disk->dir);
     270             :                                 log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
     271             :                                 log_fatal("it's better to change the serial number of the disk.\n");
     272             :                                 log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     273             :                                 exit(EXIT_FAILURE);
     274             :                                 /* LCOV_EXCL_STOP */
     275             :                         }
     276             : #endif
     277             : 
     278           0 :                         for (j = i->next; j != 0; j = j->next) {
     279           0 :                                 struct snapraid_disk* other = j->data;
     280           0 :                                 if (disk->device == other->device) {
     281           0 :                                         if (state->opt.force_device) {
     282             :                                                 /* note tha we just ignore the issue */
     283             :                                                 /* and we DON'T mark the disk to be skipped */
     284             :                                                 /* because we want to use these disks */
     285           0 :                                                 if (!state->opt.no_warnings)
     286           0 :                                                         log_fatal("DANGER! Ignoring that disks '%s' and '%s' are on the same device\n", disk->name, other->name);
     287             :                                         } else {
     288             :                                                 /* LCOV_EXCL_START */
     289             :                                                 log_fatal("Disks '%s' and '%s' are on the same device.\n", disk->dir, other->dir);
     290             : #ifdef _WIN32
     291             :                                                 log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
     292             :                                                 log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     293             :                                                 log_fatal("to change one of the disk serial.\n");
     294             : #endif
     295             :                                                 /* in "fix" we allow to continue anyway */
     296             :                                                 if (strcmp(state->command, "fix") == 0) {
     297             :                                                         log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     298             :                                                 }
     299             :                                                 exit(EXIT_FAILURE);
     300             :                                                 /* LCOV_EXCL_STOP */
     301             :                                         }
     302             :                                 }
     303             :                         }
     304             : 
     305             :                         /* skip data disks that are not accessible */
     306           0 :                         if (disk->skip_access)
     307           0 :                                 continue;
     308             : 
     309           0 :                         if (!state->opt.skip_parity_access) {
     310           0 :                                 for (l = 0; l < state->level; ++l) {
     311           0 :                                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     312           0 :                                                 if (disk->device == state->parity[l].split_map[s].device) {
     313             :                                                         /* LCOV_EXCL_START */
     314             :                                                         log_fatal("Disk '%s' and %s '%s' are on the same device.\n", disk->dir, lev_name(l), state->parity[l].split_map[s].path);
     315             : #ifdef _WIN32
     316             :                                                         log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
     317             :                                                         log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     318             :                                                         log_fatal("to change one of the disk serial.\n");
     319             : #endif
     320             :                                                         exit(EXIT_FAILURE);
     321             :                                                         /* LCOV_EXCL_STOP */
     322             :                                                 }
     323             :                                         }
     324             :                                 }
     325             :                         }
     326             : 
     327           0 :                         if (state->pool[0] != 0 && disk->device == state->pool_device) {
     328             :                                 /* LCOV_EXCL_START */
     329             :                                 log_fatal("Disk '%s' and pool '%s' are on the same device.\n", disk->dir, state->pool);
     330             : #ifdef _WIN32
     331             :                                 log_fatal("Both have the serial number '%" PRIx64 "'.\n", disk->device);
     332             :                                 log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     333             :                                 log_fatal("to change one of the disk serial.\n");
     334             : #endif
     335             :                                 exit(EXIT_FAILURE);
     336             :                                 /* LCOV_EXCL_STOP */
     337             :                         }
     338             :                 }
     339             :         }
     340             : 
     341             :         /* check device of parity disks */
     342         265 :         if (!state->opt.skip_device && !state->opt.skip_parity_access) {
     343           0 :                 for (l = 0; l < state->level; ++l) {
     344           0 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
     345             :                                 unsigned j, t;
     346             : 
     347             :                                 /* skip parity disks that are not accessible */
     348           0 :                                 if (state->parity[l].skip_access)
     349           0 :                                         continue;
     350             : 
     351             : #ifdef _WIN32
     352             :                                 if (state->parity[l].split_map[s].device == 0) {
     353             :                                         /* LCOV_EXCL_START */
     354             :                                         log_fatal("Disk '%s' has a zero serial number.\n", state->parity[l].split_map[s].path);
     355             :                                         log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
     356             :                                         log_fatal("it's better to change the serial number of the disk.\n");
     357             :                                         log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     358             :                                         exit(EXIT_FAILURE);
     359             :                                         /* LCOV_EXCL_STOP */
     360             :                                 }
     361             : #endif
     362             : 
     363           0 :                                 for (j = l + 1; j < state->level; ++j) {
     364           0 :                                         for (t = 0; t < state->parity[j].split_mac; ++t) {
     365           0 :                                                 if (state->parity[l].split_map[s].device == state->parity[j].split_map[t].device) {
     366           0 :                                                         if (state->opt.force_device) {
     367             :                                                                 /* note tha we just ignore the issue */
     368             :                                                                 /* and we DON'T mark the disk to be skipped */
     369             :                                                                 /* because we want to use these disks */
     370           0 :                                                                 if (!state->opt.no_warnings)
     371           0 :                                                                         log_fatal("DANGER! Skipping parities '%s' and '%s' on the same device\n", lev_config_name(l), lev_config_name(j));
     372             :                                                         } else {
     373             :                                                                 /* LCOV_EXCL_START */
     374             :                                                                 log_fatal("Parity '%s' and '%s' are on the same device.\n", state->parity[l].split_map[s].path, state->parity[j].split_map[t].path);
     375             : #ifdef _WIN32
     376             :                                                                 log_fatal("Both have the serial number '%" PRIx64 "'.\n", state->parity[l].split_map[s].device);
     377             :                                                                 log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     378             :                                                                 log_fatal("to change one of the disk serial.\n");
     379             : #endif
     380             :                                                                 /* in "fix" we allow to continue anyway */
     381             :                                                                 if (strcmp(state->command, "fix") == 0) {
     382             :                                                                         log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     383             :                                                                 }
     384             :                                                                 exit(EXIT_FAILURE);
     385             :                                                                 /* LCOV_EXCL_STOP */
     386             :                                                         }
     387             :                                                 }
     388             :                                         }
     389             :                                 }
     390             : 
     391           0 :                                 if (state->pool[0] != 0 && state->pool_device == state->parity[l].split_map[s].device) {
     392             :                                         /* LCOV_EXCL_START */
     393             :                                         log_fatal("Pool '%s' and parity '%s' are on the same device.\n", state->pool, state->parity[l].split_map[s].path);
     394             : #ifdef _WIN32
     395             :                                         log_fatal("Both have the serial number '%" PRIx64 "'.\n", state->pool_device);
     396             :                                         log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'\n");
     397             :                                         log_fatal("to change one of the disk serial.\n");
     398             : #endif
     399             :                                         exit(EXIT_FAILURE);
     400             :                                         /* LCOV_EXCL_STOP */
     401             :                                 }
     402             :                         }
     403             :                 }
     404             :         }
     405             : 
     406             :         /* check device of pool disk */
     407             : #ifdef _WIN32
     408             :         if (!state->opt.skip_device) {
     409             :                 if (state->pool[0] != 0 && state->pool_device == 0) {
     410             :                         /* LCOV_EXCL_START */
     411             :                         log_fatal("Disk '%s' has a zero serial number.\n", state->pool);
     412             :                         log_fatal("This is not necessarily wrong, but for using SnapRAID\n");
     413             :                         log_fatal("it's better to change the serial number of the disk.\n");
     414             :                         log_fatal("Try using the 'VolumeID' tool by 'Mark Russinovich'.\n");
     415             :                         exit(EXIT_FAILURE);
     416             :                         /* LCOV_EXCL_STOP */
     417             :                 }
     418             :         }
     419             : #endif
     420             : 
     421             :         /* count the content files */
     422         265 :         if (!state->opt.skip_device && !state->opt.skip_content_access) {
     423             :                 unsigned content_count;
     424             : 
     425           0 :                 content_count = 0;
     426           0 :                 for (i = state->contentlist; i != 0; i = i->next) {
     427             :                         tommy_node* j;
     428           0 :                         struct snapraid_content* content = i->data;
     429             : 
     430             :                         /* check if there are others in the same disk */
     431           0 :                         for (j = i->next; j != 0; j = j->next) {
     432           0 :                                 struct snapraid_content* other = j->data;
     433           0 :                                 if (content->device == other->device) {
     434           0 :                                         log_fatal("WARNING! Content files on the same disk: '%s' and '%s'.\n", content->content, other->content);
     435           0 :                                         break;
     436             :                                 }
     437             :                         }
     438           0 :                         if (j != 0) {
     439             :                                 /* skip it */
     440           0 :                                 continue;
     441             :                         }
     442             : 
     443           0 :                         ++content_count;
     444             :                 }
     445             : 
     446           0 :                 if (content_count < state->level + 1) {
     447             :                         /* LCOV_EXCL_START */
     448             :                         log_fatal("You must have at least %d 'content' files in different disks.\n", state->level + 1);
     449             :                         exit(EXIT_FAILURE);
     450             :                         /* LCOV_EXCL_STOP */
     451             :                 }
     452             :         }
     453             : 
     454             :         /* check for speed */
     455             : #ifdef CONFIG_X86
     456         265 :         if (!raid_cpu_has_ssse3())
     457             : #endif
     458           0 :         if (state->raid_mode == RAID_MODE_CAUCHY && !state->opt.no_warnings) {
     459           0 :                 if (state->level == 3) {
     460           0 :                         log_fatal("WARNING! Your CPU doesn't have a fast implementation for triple parity.\n");
     461           0 :                         log_fatal("WARNING! It's recommended to switch to 'z-parity' instead than '3-parity'.\n");
     462           0 :                 } else if (state->level > 3) {
     463           0 :                         log_fatal("WARNING! Your CPU doesn't have a fast implementation beyond triple parity.\n");
     464           0 :                         log_fatal("WARNING! It's recommended to reduce the parity levels to triple parity.\n");
     465             :                 }
     466             :         }
     467             : 
     468             :         /* ensure that specified filter disks are valid ones */
     469         275 :         for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
     470             :                 tommy_node* j;
     471          10 :                 struct snapraid_filter* filter = i->data;
     472          51 :                 for (j = state->disklist; j != 0; j = j->next) {
     473          45 :                         struct snapraid_disk* disk = j->data;
     474          45 :                         if (fnmatch(filter->pattern, disk->name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
     475           4 :                                 break;
     476             :                 }
     477          10 :                 if (j == 0) {
     478             :                         /* check matching with parity disks */
     479          21 :                         for (l = 0; l < state->level; ++l)
     480          21 :                                 if (fnmatch(filter->pattern, lev_config_name(l), FNM_CASEINSENSITIVE_FOR_WIN) == 0)
     481           6 :                                         break;
     482           6 :                         if (l == state->level) {
     483             :                                 /* LCOV_EXCL_START */
     484             :                                 log_fatal("Option -d, --filter-disk %s doesn't match any data or parity disk.\n", filter->pattern);
     485             :                                 exit(EXIT_FAILURE);
     486             :                                 /* LCOV_EXCL_STOP */
     487             :                         }
     488             :                 }
     489             :         }
     490         265 : }
     491             : 
     492             : /**
     493             :  * Validate the smartctl command.
     494             :  *
     495             :  * It must contains only one %s string, and not other % chars.
     496             :  */
     497         366 : static int validate_smartctl(const char* custom)
     498             : {
     499         366 :         const char* s = custom;
     500         366 :         int arg = 0;
     501             : 
     502        2562 :         while (*s) {
     503        1830 :                 if (s[0] == '%' && s[1] == 's') {
     504         183 :                         if (arg) {
     505             :                                 /* LCOV_EXCL_START */
     506             :                                 return -1;
     507             :                                 /* LCOV_EXCL_STOP */
     508             :                         }
     509         183 :                         arg = 1;
     510        1647 :                 } else if (s[0] == '%') {
     511             :                         /* LCOV_EXCL_START */
     512             :                         return -1;
     513             :                         /* LCOV_EXCL_STOP */
     514             :                 }
     515             : 
     516        1830 :                 ++s;
     517             :         }
     518             : 
     519         366 :         return 0;
     520             : }
     521             : 
     522         266 : void state_config(struct snapraid_state* state, const char* path, const char* command, struct snapraid_option* opt, tommy_list* filterlist_disk)
     523             : {
     524             :         STREAM* f;
     525             :         unsigned line;
     526             :         tommy_node* i;
     527             :         unsigned l, s;
     528             : 
     529             :         /* copy the options */
     530         266 :         state->opt = *opt;
     531             : 
     532             :         /* if unset, sort by physical order */
     533         266 :         if (!state->opt.force_order)
     534           1 :                 state->opt.force_order = SORT_PHYSICAL;
     535             : 
     536             :         /* adjust file mode */
     537         266 :         if (state->opt.file_mode != ADVISE_DEFAULT) {
     538           4 :                 state->file_mode = state->opt.file_mode;
     539             :         } else {
     540             :                 /* default mode, if nothing is specified */
     541         262 :                 state->file_mode = ADVISE_FLUSH;
     542             :         }
     543             : 
     544             :         /* store current command */
     545         266 :         state->command = command;
     546             : 
     547         266 :         log_tag("conf:file:%s\n", path);
     548             : 
     549         266 :         f = sopen_read(path);
     550         266 :         if (!f) {
     551             :                 /* LCOV_EXCL_START */
     552             :                 if (errno == ENOENT) {
     553             :                         if (state->opt.auto_conf) {
     554             :                                 log_tag("conf:missing:\n");
     555             :                                 /* mark that we are without a configuration file */
     556             :                                 state->no_conf = 1;
     557             :                                 state->level = 0;
     558             :                                 return;
     559             :                         }
     560             : 
     561             :                         log_fatal("No configuration file found at '%s'\n", path);
     562             :                 } else if (errno == EACCES) {
     563             :                         log_fatal("You do not have rights to access the configuration file '%s'\n", path);
     564             :                 } else {
     565             :                         log_fatal("Error opening the configuration file '%s'. %s.\n", path, strerror(errno));
     566             :                 }
     567             :                 exit(EXIT_FAILURE);
     568             :                 /* LCOV_EXCL_STOP */
     569             :         }
     570             : 
     571         266 :         line = 1;
     572             :         while (1) {
     573             :                 char tag[PATH_MAX];
     574             :                 char buffer[PATH_MAX];
     575             :                 int ret;
     576             :                 int c;
     577             :                 unsigned level;
     578             : 
     579             :                 /* skip initial spaces */
     580        6245 :                 sgetspace(f);
     581             : 
     582             :                 /* read the command */
     583        6245 :                 ret = sgettok(f, tag, sizeof(tag));
     584        6245 :                 if (ret < 0) {
     585             :                         /* LCOV_EXCL_START */
     586             :                         log_fatal("Error reading the configuration file '%s' at line %u\n", path, line);
     587             :                         exit(EXIT_FAILURE);
     588             :                         /* LCOV_EXCL_STOP */
     589             :                 }
     590             : 
     591             :                 /* skip spaces after the command */
     592        6245 :                 sgetspace(f);
     593             : 
     594        6245 :                 if (strcmp(tag, "blocksize") == 0
     595             :                         /* block_size is the old format of the option */
     596        5979 :                         || strcmp(tag, "block_size") == 0) {
     597             : 
     598         266 :                         ret = sgetu32(f, &state->block_size);
     599         266 :                         if (ret < 0) {
     600             :                                 /* LCOV_EXCL_START */
     601             :                                 log_fatal("Invalid 'blocksize' specification in '%s' at line %u\n", path, line);
     602             :                                 exit(EXIT_FAILURE);
     603             :                                 /* LCOV_EXCL_STOP */
     604             :                         }
     605         266 :                         if (state->block_size < 1) {
     606             :                                 /* LCOV_EXCL_START */
     607             :                                 log_fatal("Too small 'blocksize' specification in '%s' at line %u\n", path, line);
     608             :                                 exit(EXIT_FAILURE);
     609             :                                 /* LCOV_EXCL_STOP */
     610             :                         }
     611         266 :                         if (state->block_size > 16 * KIBI) {
     612             :                                 /* LCOV_EXCL_START */
     613             :                                 log_fatal("Too big 'blocksize' specification in '%s' at line %u\n", path, line);
     614             :                                 exit(EXIT_FAILURE);
     615             :                                 /* LCOV_EXCL_STOP */
     616             :                         }
     617             :                         /* check if it's a power of 2 */
     618         266 :                         if ((state->block_size & (state->block_size - 1)) != 0) {
     619             :                                 /* LCOV_EXCL_START */
     620             :                                 log_fatal("Not power of 2 'blocksize' specification in '%s' at line %u\n", path, line);
     621             :                                 exit(EXIT_FAILURE);
     622             :                                 /* LCOV_EXCL_STOP */
     623             :                         }
     624         266 :                         state->block_size *= KIBI;
     625        5979 :                 } else if (strcmp(tag, "hashsize") == 0
     626        5979 :                         || strcmp(tag, "hash_size") == 0 /* v11.0 used incorretly this one, kept now for backward compatibility */
     627           0 :                 ) {
     628             :                         uint32_t hash_size;
     629             : 
     630           0 :                         ret = sgetu32(f, &hash_size);
     631           0 :                         if (ret < 0) {
     632             :                                 /* LCOV_EXCL_START */
     633             :                                 log_fatal("Invalid 'hashsize' specification in '%s' at line %u\n", path, line);
     634             :                                 exit(EXIT_FAILURE);
     635             :                                 /* LCOV_EXCL_STOP */
     636             :                         }
     637           0 :                         if (hash_size < 2) {
     638             :                                 /* LCOV_EXCL_START */
     639             :                                 log_fatal("Too small 'hashsize' specification in '%s' at line %u\n", path, line);
     640             :                                 exit(EXIT_FAILURE);
     641             :                                 /* LCOV_EXCL_STOP */
     642             :                         }
     643           0 :                         if (hash_size > HASH_MAX) {
     644             :                                 /* LCOV_EXCL_START */
     645             :                                 log_fatal("Too big 'hashsize' specification in '%s' at line %u\n", path, line);
     646             :                                 exit(EXIT_FAILURE);
     647             :                                 /* LCOV_EXCL_STOP */
     648             :                         }
     649             :                         /* check if it's a power of 2 */
     650           0 :                         if ((hash_size & (hash_size - 1)) != 0) {
     651             :                                 /* LCOV_EXCL_START */
     652             :                                 log_fatal("Not power of 2 'hashsize' specification in '%s' at line %u\n", path, line);
     653             :                                 exit(EXIT_FAILURE);
     654             :                                 /* LCOV_EXCL_STOP */
     655             :                         }
     656             : 
     657           0 :                         BLOCK_HASH_SIZE = hash_size;
     658        5979 :                 } else if (lev_config_scan(tag, &level, &state->raid_mode) == 0) {
     659             :                         char device[PATH_MAX];
     660             :                         char* split_map[SPLIT_MAX + 1];
     661             :                         unsigned split_mac;
     662             :                         char* slash;
     663             :                         uint64_t dev;
     664             :                         int skip_access;
     665             : 
     666        1272 :                         if (state->parity[level].split_mac != 0) {
     667             :                                 /* LCOV_EXCL_START */
     668             :                                 log_fatal("Multiple '%s' specification in '%s' at line %u\n", tag, path, line);
     669             :                                 exit(EXIT_FAILURE);
     670             :                                 /* LCOV_EXCL_STOP */
     671             :                         }
     672             : 
     673        1272 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     674        1272 :                         if (ret < 0) {
     675             :                                 /* LCOV_EXCL_START */
     676             :                                 log_fatal("Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
     677             :                                 exit(EXIT_FAILURE);
     678             :                                 /* LCOV_EXCL_STOP */
     679             :                         }
     680             : 
     681        1272 :                         if (!*buffer) {
     682             :                                 /* LCOV_EXCL_START */
     683             :                                 log_fatal("Empty '%s' specification in '%s' at line %u\n", tag, path, line);
     684             :                                 exit(EXIT_FAILURE);
     685             :                                 /* LCOV_EXCL_STOP */
     686             :                         }
     687             : 
     688        1272 :                         split_mac = strsplit(split_map, SPLIT_MAX + 1, buffer, ",");
     689             : 
     690        1272 :                         if (split_mac > SPLIT_MAX) {
     691             :                                 /* LCOV_EXCL_START */
     692             :                                 log_fatal("Too many files in '%s' specification in '%s' at line %u\n", tag, path, line);
     693             :                                 exit(EXIT_FAILURE);
     694             :                                 /* LCOV_EXCL_STOP */
     695             :                         }
     696             : 
     697        1272 :                         skip_access = 0;
     698        1272 :                         state->parity[level].split_mac = split_mac;
     699        6356 :                         for (s = 0; s < split_mac; ++s) {
     700        5085 :                                 pathimport(state->parity[level].split_map[s].path, sizeof(state->parity[level].split_map[s].path), split_map[s]);
     701             : 
     702        5085 :                                 if (!state->opt.skip_parity_access) {
     703             :                                         struct stat st;
     704             : 
     705             :                                         /* get the device of the directory containing the parity file */
     706        4349 :                                         pathimport(device, sizeof(device), split_map[s]);
     707        4349 :                                         slash = strrchr(device, '/');
     708        4349 :                                         if (slash)
     709        4349 :                                                 *slash = 0;
     710             :                                         else
     711           0 :                                                 pathcpy(device, sizeof(device), ".");
     712             : 
     713        4349 :                                         if (stat(device, &st) == 0) {
     714        4344 :                                                 dev = st.st_dev;
     715             :                                         } else {
     716             :                                                 /* if the disk can be skipped */
     717           5 :                                                 if (state->opt.force_device) {
     718             :                                                         /* use a fake device, and mark the disk to be skipped */
     719           4 :                                                         dev = 0;
     720           4 :                                                         skip_access = 1;
     721           4 :                                                         log_fatal("DANGER! Skipping inaccessible parity disk '%s'...\n", tag);
     722             :                                                 } else {
     723             :                                                         /* LCOV_EXCL_START */
     724             :                                                         log_fatal("Error accessing 'parity' dir '%s' specification in '%s' at line %u\n", device, path, line);
     725             : 
     726             :                                                         /* in "fix" we allow to continue anyway */
     727             :                                                         if (strcmp(state->command, "fix") == 0) {
     728             :                                                                 log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     729             :                                                         }
     730             :                                                         exit(EXIT_FAILURE);
     731             :                                                         /* LCOV_EXCL_STOP */
     732             :                                                 }
     733             :                                         }
     734             :                                 } else {
     735             :                                         /* use a fake device */
     736         736 :                                         dev = 0;
     737             :                                 }
     738             : 
     739        5084 :                                 state->parity[level].split_map[s].device = dev;
     740             :                         }
     741             : 
     742             :                         /* store the global parity skip_access */
     743        1271 :                         state->parity[level].skip_access = skip_access;
     744             : 
     745             :                         /* adjust the level */
     746        1271 :                         if (state->level < level + 1)
     747        1005 :                                 state->level = level + 1;
     748        4707 :                 } else if (strcmp(tag, "share") == 0) {
     749          41 :                         if (*state->share) {
     750             :                                 /* LCOV_EXCL_START */
     751             :                                 log_fatal("Multiple 'share' specification in '%s' at line %u\n", path, line);
     752             :                                 exit(EXIT_FAILURE);
     753             :                                 /* LCOV_EXCL_STOP */
     754             :                         }
     755             : 
     756          41 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     757          41 :                         if (ret < 0) {
     758             :                                 /* LCOV_EXCL_START */
     759             :                                 log_fatal("Invalid 'share' specification in '%s' at line %u\n", path, line);
     760             :                                 exit(EXIT_FAILURE);
     761             :                                 /* LCOV_EXCL_STOP */
     762             :                         }
     763             : 
     764          41 :                         if (!*buffer) {
     765             :                                 /* LCOV_EXCL_START */
     766             :                                 log_fatal("Empty 'share' specification in '%s' at line %u\n", path, line);
     767             :                                 exit(EXIT_FAILURE);
     768             :                                 /* LCOV_EXCL_STOP */
     769             :                         }
     770             : 
     771          41 :                         pathimport(state->share, sizeof(state->share), buffer);
     772        4666 :                 } else if (strcmp(tag, "pool") == 0) {
     773             :                         struct stat st;
     774             : 
     775          41 :                         if (*state->pool) {
     776             :                                 /* LCOV_EXCL_START */
     777             :                                 log_fatal("Multiple 'pool' specification in '%s' at line %u\n", path, line);
     778             :                                 exit(EXIT_FAILURE);
     779             :                                 /* LCOV_EXCL_STOP */
     780             :                         }
     781             : 
     782          41 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     783          41 :                         if (ret < 0) {
     784             :                                 /* LCOV_EXCL_START */
     785             :                                 log_fatal("Invalid 'pool' specification in '%s' at line %u\n", path, line);
     786             :                                 exit(EXIT_FAILURE);
     787             :                                 /* LCOV_EXCL_STOP */
     788             :                         }
     789             : 
     790          41 :                         if (!*buffer) {
     791             :                                 /* LCOV_EXCL_START */
     792             :                                 log_fatal("Empty 'pool' specification in '%s' at line %u\n", path, line);
     793             :                                 exit(EXIT_FAILURE);
     794             :                                 /* LCOV_EXCL_STOP */
     795             :                         }
     796             : 
     797          41 :                         pathimport(state->pool, sizeof(state->pool), buffer);
     798             : 
     799             :                         /* get the device of the directory containing the pool tree */
     800          41 :                         if (stat(buffer, &st) != 0) {
     801             :                                 /* LCOV_EXCL_START */
     802             :                                 log_fatal("Error accessing 'pool' dir '%s' specification in '%s' at line %u\n", buffer, path, line);
     803             :                                 exit(EXIT_FAILURE);
     804             :                                 /* LCOV_EXCL_STOP */
     805             :                         }
     806             : 
     807          41 :                         state->pool_device = st.st_dev;
     808        4625 :                 } else if (strcmp(tag, "content") == 0) {
     809             :                         struct snapraid_content* content;
     810             :                         char device[PATH_MAX];
     811             :                         char* slash;
     812             :                         struct stat st;
     813             :                         uint64_t dev;
     814             : 
     815        1531 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
     816        1531 :                         if (ret < 0) {
     817             :                                 /* LCOV_EXCL_START */
     818             :                                 log_fatal("Invalid 'content' specification in '%s' at line %u\n", path, line);
     819             :                                 exit(EXIT_FAILURE);
     820             :                                 /* LCOV_EXCL_STOP */
     821             :                         }
     822             : 
     823        1531 :                         if (pathcmp(buffer, "/dev/null") == 0 || pathcmp(buffer, "NUL") == 0) {
     824             :                                 /* LCOV_EXCL_START */
     825             :                                 log_fatal("You cannot use the null device as 'content' specification in '%s' at line %u\n", path, line);
     826             :                                 exit(EXIT_FAILURE);
     827             :                                 /* LCOV_EXCL_STOP */
     828             :                         }
     829             : 
     830        1531 :                         if (!*buffer) {
     831             :                                 /* LCOV_EXCL_START */
     832             :                                 log_fatal("Empty 'content' specification in '%s' at line %u\n", path, line);
     833             :                                 exit(EXIT_FAILURE);
     834             :                                 /* LCOV_EXCL_STOP */
     835             :                         }
     836             : 
     837             :                         /* check if the content file is already specified */
     838        5724 :                         for (i = state->contentlist; i != 0; i = i->next) {
     839        4193 :                                 content = i->data;
     840        4193 :                                 if (pathcmp(content->content, buffer) == 0)
     841           0 :                                         break;
     842             :                         }
     843        1531 :                         if (i) {
     844             :                                 /* LCOV_EXCL_START */
     845             :                                 log_fatal("Duplicate 'content' specification in '%s' at line %u\n", path, line);
     846             :                                 exit(EXIT_FAILURE);
     847             :                                 /* LCOV_EXCL_STOP */
     848             :                         }
     849             : 
     850             :                         /* get the device of the directory containing the content file */
     851        1531 :                         pathimport(device, sizeof(device), buffer);
     852        1531 :                         slash = strrchr(device, '/');
     853        1531 :                         if (slash)
     854        1531 :                                 *slash = 0;
     855             :                         else
     856           0 :                                 pathcpy(device, sizeof(device), ".");
     857        1531 :                         if (stat(device, &st) == 0) {
     858        1530 :                                 dev = st.st_dev;
     859             :                         } else {
     860           1 :                                 if (state->opt.skip_content_access) {
     861             :                                         /* use a fake device */
     862           1 :                                         dev = 0;
     863           1 :                                         log_fatal("WARNING! Skipping inaccessible content file '%s'...\n", buffer);
     864             :                                 } else {
     865             :                                         /* LCOV_EXCL_START */
     866             :                                         log_fatal("Error accessing 'content' dir '%s' specification in '%s' at line %u\n", device, path, line);
     867             :                                         exit(EXIT_FAILURE);
     868             :                                         /* LCOV_EXCL_STOP */
     869             :                                 }
     870             :                         }
     871             : 
     872             :                         /* set the lock file at the first accessible content file */
     873        1531 :                         if (state->lockfile[0] == 0 && dev != 0) {
     874         265 :                                 pathcpy(state->lockfile, sizeof(state->lockfile), buffer);
     875         265 :                                 pathcat(state->lockfile, sizeof(state->lockfile), ".lock");
     876             :                         }
     877             : 
     878        1531 :                         content = content_alloc(buffer, dev);
     879             : 
     880        1531 :                         tommy_list_insert_tail(&state->contentlist, &content->node, content);
     881        4679 :                 } else if (strcmp(tag, "data") == 0 || strcmp(tag, "disk") == 0) {
     882             :                         /* "disk" is the deprecated name up to SnapRAID 9.x */
     883             :                         char dir[PATH_MAX];
     884             :                         char device[PATH_MAX];
     885             :                         char uuid[UUID_MAX];
     886             :                         struct snapraid_disk* disk;
     887             :                         uint64_t dev;
     888             :                         int skip_access;
     889             : 
     890        1585 :                         ret = sgettok(f, buffer, sizeof(buffer));
     891        1585 :                         if (ret < 0) {
     892             :                                 /* LCOV_EXCL_START */
     893             :                                 log_fatal("Invalid 'data' name specification in '%s' at line %u\n", path, line);
     894             :                                 exit(EXIT_FAILURE);
     895             :                                 /* LCOV_EXCL_STOP */
     896             :                         }
     897             : 
     898        1585 :                         if (!*buffer) {
     899             :                                 /* LCOV_EXCL_START */
     900             :                                 log_fatal("Empty 'data' name specification in '%s' at line %u\n", path, line);
     901             :                                 exit(EXIT_FAILURE);
     902             :                                 /* LCOV_EXCL_STOP */
     903             :                         }
     904             : 
     905        1585 :                         sgetspace(f);
     906             : 
     907        1585 :                         ret = sgetlasttok(f, dir, sizeof(dir));
     908        1585 :                         if (ret < 0) {
     909             :                                 /* LCOV_EXCL_START */
     910             :                                 log_fatal("Invalid 'data' dir specification in '%s' at line %u\n", path, line);
     911             :                                 exit(EXIT_FAILURE);
     912             :                                 /* LCOV_EXCL_STOP */
     913             :                         }
     914             : 
     915        1585 :                         if (!*dir) {
     916             :                                 /* LCOV_EXCL_START */
     917             :                                 log_fatal("Empty 'data' dir specification in '%s' at line %u\n", path, line);
     918             :                                 exit(EXIT_FAILURE);
     919             :                                 /* LCOV_EXCL_STOP */
     920             :                         }
     921             : 
     922             :                         /* get the device of the dir */
     923        1585 :                         pathimport(device, sizeof(device), dir);
     924             : 
     925             :                         /* check if the disk name already exists */
     926        5535 :                         for (i = state->disklist; i != 0; i = i->next) {
     927        3950 :                                 disk = i->data;
     928        3950 :                                 if (strcmp(disk->name, buffer) == 0)
     929           0 :                                         break;
     930             :                         }
     931        1585 :                         if (i) {
     932             :                                 /* LCOV_EXCL_START */
     933             :                                 log_fatal("Duplicate 'data' name '%s' at line %u\n", buffer, line);
     934             :                                 exit(EXIT_FAILURE);
     935             :                                 /* LCOV_EXCL_STOP */
     936             :                         }
     937             : 
     938             :                         /* if the disk has to be present */
     939        1585 :                         skip_access = 0;
     940        1585 :                         if (!state->opt.skip_disk_access) {
     941             :                                 struct stat st;
     942             : 
     943        1423 :                                 if (stat(device, &st) == 0) {
     944        1422 :                                         dev = st.st_dev;
     945             : 
     946             :                                         /* read the uuid, if unsupported use an empty one */
     947        1422 :                                         if (devuuid(dev, uuid, sizeof(uuid)) != 0) {
     948           0 :                                                 *uuid = 0;
     949             :                                         }
     950             : 
     951             :                                         /* fake a different UUID when testing */
     952        1422 :                                         if (state->opt.fake_uuid) {
     953           2 :                                                 snprintf(uuid, sizeof(uuid), "fake-uuid-%d", state->opt.fake_uuid);
     954           2 :                                                 --state->opt.fake_uuid;
     955             :                                         }
     956             :                                 } else {
     957             :                                         /* if the disk can be skipped */
     958           1 :                                         if (state->opt.force_device) {
     959             :                                                 /* use a fake device, and mark the disk to be skipped */
     960           1 :                                                 dev = 0;
     961           1 :                                                 *uuid = 0;
     962           1 :                                                 skip_access = 1;
     963           1 :                                                 log_fatal("DANGER! Skipping inaccessible data disk '%s'...\n", buffer);
     964             :                                         } else {
     965             :                                                 /* LCOV_EXCL_START */
     966             :                                                 log_fatal("Error accessing 'disk' '%s' specification in '%s' at line %u\n", dir, device, line);
     967             : 
     968             :                                                 /* in "fix" we allow to continue anyway */
     969             :                                                 if (strcmp(state->command, "fix") == 0) {
     970             :                                                         log_fatal("You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command);
     971             :                                                 }
     972             :                                                 exit(EXIT_FAILURE);
     973             :                                                 /* LCOV_EXCL_STOP */
     974             :                                         }
     975             :                                 }
     976             :                         } else {
     977             :                                 /* use a fake device */
     978         162 :                                 dev = 0;
     979         162 :                                 *uuid = 0;
     980             :                         }
     981             : 
     982        1585 :                         disk = disk_alloc(buffer, dir, dev, uuid, skip_access);
     983             : 
     984        1585 :                         tommy_list_insert_tail(&state->disklist, &disk->node, disk);
     985        1509 :                 } else if (strcmp(tag, "smartctl") == 0) {
     986             :                         char custom[PATH_MAX];
     987             : 
     988         366 :                         ret = sgettok(f, buffer, sizeof(buffer));
     989         366 :                         if (ret < 0) {
     990             :                                 /* LCOV_EXCL_START */
     991             :                                 log_fatal("Invalid 'smartctl' name specification in '%s' at line %u\n", path, line);
     992             :                                 exit(EXIT_FAILURE);
     993             :                                 /* LCOV_EXCL_STOP */
     994             :                         }
     995             : 
     996         366 :                         if (!*buffer) {
     997             :                                 /* LCOV_EXCL_START */
     998             :                                 log_fatal("Empty 'smartctl' name specification in '%s' at line %u\n", path, line);
     999             :                                 exit(EXIT_FAILURE);
    1000             :                                 /* LCOV_EXCL_STOP */
    1001             :                         }
    1002             : 
    1003         366 :                         sgetspace(f);
    1004             : 
    1005         366 :                         ret = sgetlasttok(f, custom, sizeof(custom));
    1006         366 :                         if (ret < 0) {
    1007             :                                 /* LCOV_EXCL_START */
    1008             :                                 log_fatal("Invalid 'smartctl' option specification in '%s' at line %u\n", path, line);
    1009             :                                 exit(EXIT_FAILURE);
    1010             :                                 /* LCOV_EXCL_STOP */
    1011             :                         }
    1012             : 
    1013         366 :                         if (!*custom) {
    1014             :                                 /* LCOV_EXCL_START */
    1015             :                                 log_fatal("Empty 'smartctl' option specification in '%s' at line %u\n", path, line);
    1016             :                                 exit(EXIT_FAILURE);
    1017             :                                 /* LCOV_EXCL_STOP */
    1018             :                         }
    1019             : 
    1020         366 :                         if (validate_smartctl(custom) != 0) {
    1021             :                                 /* LCOV_EXCL_START */
    1022             :                                 log_fatal("Invalid 'smartctl' option specification in '%s' at line %u\n", path, line);
    1023             :                                 exit(EXIT_FAILURE);
    1024             :                                 /* LCOV_EXCL_STOP */
    1025             :                         }
    1026             : 
    1027             :                         /* search for parity */
    1028         366 :                         if (lev_config_scan(buffer, &level, 0) == 0) {
    1029         183 :                                 if (state->parity[level].smartctl[0] != 0) {
    1030             :                                         /* LCOV_EXCL_START */
    1031             :                                         log_fatal("Duplicate parity smartctl '%s' at line %u\n", buffer, line);
    1032             :                                         exit(EXIT_FAILURE);
    1033             :                                         /* LCOV_EXCL_STOP */
    1034             :                                 }
    1035             : 
    1036         183 :                                 pathcpy(state->parity[level].smartctl, sizeof(state->parity[level].smartctl), custom);
    1037             :                         } else {
    1038             :                                 struct snapraid_disk* disk;
    1039             : 
    1040             :                                 /* search the disk */
    1041         183 :                                 disk = 0;
    1042         183 :                                 for (i = state->disklist; i != 0; i = i->next) {
    1043         183 :                                         disk = i->data;
    1044         183 :                                         if (strcmp(disk->name, buffer) == 0)
    1045         183 :                                                 break;
    1046             :                                 }
    1047         183 :                                 if (!disk) {
    1048             :                                         /* LCOV_EXCL_START */
    1049             :                                         log_fatal("Missing disk smartctl '%s' at line %u\n", buffer, line);
    1050             :                                         exit(EXIT_FAILURE);
    1051             :                                         /* LCOV_EXCL_STOP */
    1052             :                                 }
    1053         183 :                                 if (disk->smartctl[0] != 0) {
    1054             :                                         /* LCOV_EXCL_START */
    1055             :                                         log_fatal("Duplicate disk name '%s' at line %u\n", buffer, line);
    1056             :                                         exit(EXIT_FAILURE);
    1057             :                                         /* LCOV_EXCL_STOP */
    1058             :                                 }
    1059             : 
    1060         183 :                                 pathcpy(disk->smartctl, sizeof(disk->smartctl), custom);
    1061             :                         }
    1062        1143 :                 } else if (strcmp(tag, "nohidden") == 0) {
    1063           0 :                         state->filter_hidden = 1;
    1064        1143 :                 } else if (strcmp(tag, "exclude") == 0) {
    1065             :                         struct snapraid_filter* filter;
    1066             : 
    1067         265 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1068         265 :                         if (ret < 0) {
    1069             :                                 /* LCOV_EXCL_START */
    1070             :                                 log_fatal("Invalid 'exclude' specification in '%s' at line %u\n", path, line);
    1071             :                                 exit(EXIT_FAILURE);
    1072             :                                 /* LCOV_EXCL_STOP */
    1073             :                         }
    1074             : 
    1075         265 :                         if (!*buffer) {
    1076             :                                 /* LCOV_EXCL_START */
    1077             :                                 log_fatal("Empty 'exclude' specification in '%s' at line %u\n", path, line);
    1078             :                                 exit(EXIT_FAILURE);
    1079             :                                 /* LCOV_EXCL_STOP */
    1080             :                         }
    1081             : 
    1082         265 :                         filter = filter_alloc_file(-1, buffer);
    1083         265 :                         if (!filter) {
    1084             :                                 /* LCOV_EXCL_START */
    1085             :                                 log_fatal("Invalid 'exclude' specification '%s' in '%s' at line %u\n", buffer, path, line);
    1086             :                                 log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
    1087             :                                 exit(EXIT_FAILURE);
    1088             :                                 /* LCOV_EXCL_STOP */
    1089             :                         }
    1090         265 :                         tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
    1091         878 :                 } else if (strcmp(tag, "include") == 0) {
    1092             :                         struct snapraid_filter* filter;
    1093             : 
    1094         265 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1095         265 :                         if (ret < 0) {
    1096             :                                 /* LCOV_EXCL_START */
    1097             :                                 log_fatal("Invalid 'include' specification in '%s' at line %u\n", path, line);
    1098             :                                 exit(EXIT_FAILURE);
    1099             :                                 /* LCOV_EXCL_STOP */
    1100             :                         }
    1101             : 
    1102         265 :                         if (!*buffer) {
    1103             :                                 /* LCOV_EXCL_START */
    1104             :                                 log_fatal("Empty 'include' specification in '%s' at line %u\n", path, line);
    1105             :                                 exit(EXIT_FAILURE);
    1106             :                                 /* LCOV_EXCL_STOP */
    1107             :                         }
    1108             : 
    1109         265 :                         filter = filter_alloc_file(1, buffer);
    1110         265 :                         if (!filter) {
    1111             :                                 /* LCOV_EXCL_START */
    1112             :                                 log_fatal("Invalid 'include' specification '%s' in '%s' at line %u\n", buffer, path, line);
    1113             :                                 log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
    1114             :                                 exit(EXIT_FAILURE);
    1115             :                                 /* LCOV_EXCL_STOP */
    1116             :                         }
    1117         265 :                         tommy_list_insert_tail(&state->filterlist, &filter->node, filter);
    1118         613 :                 } else if (strcmp(tag, "autosave") == 0) {
    1119             :                         char* e;
    1120             : 
    1121          41 :                         ret = sgetlasttok(f, buffer, sizeof(buffer));
    1122          41 :                         if (ret < 0) {
    1123             :                                 /* LCOV_EXCL_START */
    1124             :                                 log_fatal("Invalid 'autosave' specification in '%s' at line %u\n", path, line);
    1125             :                                 exit(EXIT_FAILURE);
    1126             :                                 /* LCOV_EXCL_STOP */
    1127             :                         }
    1128             : 
    1129          41 :                         if (!*buffer) {
    1130             :                                 /* LCOV_EXCL_START */
    1131             :                                 log_fatal("Empty 'autosave' specification in '%s' at line %u\n", path, line);
    1132             :                                 exit(EXIT_FAILURE);
    1133             :                                 /* LCOV_EXCL_STOP */
    1134             :                         }
    1135             : 
    1136          41 :                         state->autosave = strtoul(buffer, &e, 0);
    1137             : 
    1138          41 :                         if (!e || *e) {
    1139             :                                 /* LCOV_EXCL_START */
    1140             :                                 log_fatal("Invalid 'autosave' specification in '%s' at line %u\n", path, line);
    1141             :                                 exit(EXIT_FAILURE);
    1142             :                                 /* LCOV_EXCL_STOP */
    1143             :                         }
    1144             : 
    1145             :                         /* convert to GB */
    1146          41 :                         state->autosave *= GIGA;
    1147         572 :                 } else if (tag[0] == 0) {
    1148             :                         /* allow empty lines */
    1149          41 :                 } else if (tag[0] == '#') {
    1150          41 :                         ret = sgetline(f, buffer, sizeof(buffer));
    1151          41 :                         if (ret < 0) {
    1152             :                                 /* LCOV_EXCL_START */
    1153             :                                 log_fatal("Invalid comment in '%s' at line %u\n", path, line);
    1154             :                                 exit(EXIT_FAILURE);
    1155             :                                 /* LCOV_EXCL_STOP */
    1156             :                         }
    1157             :                 } else {
    1158             :                         /* LCOV_EXCL_START */
    1159             :                         log_fatal("Invalid command '%s' in '%s' at line %u\n", tag, path, line);
    1160             :                         exit(EXIT_FAILURE);
    1161             :                         /* LCOV_EXCL_STOP */
    1162             :                 }
    1163             : 
    1164             :                 /* skip final spaces */
    1165        6244 :                 sgetspace(f);
    1166             : 
    1167             :                 /* next line */
    1168        6244 :                 c = sgeteol(f);
    1169        6244 :                 if (c == EOF) {
    1170         265 :                         break;
    1171             :                 }
    1172        5979 :                 if (c != '\n') {
    1173             :                         /* LCOV_EXCL_START */
    1174             :                         log_fatal("Extra data in '%s' at line %u\n", path, line);
    1175             :                         exit(EXIT_FAILURE);
    1176             :                         /* LCOV_EXCL_STOP */
    1177             :                 }
    1178        5979 :                 ++line;
    1179        5979 :         }
    1180             : 
    1181         265 :         if (serror(f)) {
    1182             :                 /* LCOV_EXCL_START */
    1183             :                 log_fatal("Error reading the configuration file '%s' at line %u\n", path, line);
    1184             :                 exit(EXIT_FAILURE);
    1185             :                 /* LCOV_EXCL_STOP */
    1186             :         }
    1187             : 
    1188         265 :         sclose(f);
    1189             : 
    1190         265 :         state_config_check(state, path, filterlist_disk);
    1191             : 
    1192             :         /* select the default hash */
    1193         265 :         if (state->opt.force_murmur3) {
    1194           1 :                 state->besthash = HASH_MURMUR3;
    1195         264 :         } else if (state->opt.force_spooky2) {
    1196           1 :                 state->besthash = HASH_SPOOKY2;
    1197             :         } else {
    1198             : #ifdef CONFIG_X86
    1199             :                 if (sizeof(void*) == 4 && !raid_cpu_has_slowmult())
    1200             :                         state->besthash = HASH_MURMUR3;
    1201             :                 else
    1202         263 :                         state->besthash = HASH_SPOOKY2;
    1203             : #else
    1204             :                 if (sizeof(void*) == 4)
    1205             :                         state->besthash = HASH_MURMUR3;
    1206             :                 else
    1207             :                         state->besthash = HASH_SPOOKY2;
    1208             : #endif
    1209             :         }
    1210             : 
    1211             :         /* by default use the best hash */
    1212         265 :         state->hash = state->besthash;
    1213             : 
    1214             :         /* by default use a random hash seed */
    1215         265 :         if (randomize(state->hashseed, HASH_MAX) != 0) {
    1216             :                 /* LCOV_EXCL_START */
    1217             :                 log_fatal("Failed to get random values.\n");
    1218             :                 exit(EXIT_FAILURE);
    1219             :                 /* LCOV_EXCL_STOP */
    1220             :         }
    1221             : 
    1222             :         /* no previous hash by default */
    1223         265 :         state->prevhash = HASH_UNDEFINED;
    1224             : 
    1225             :         /* intentionally not set the prevhashseed, if used valgrind will warn about it */
    1226             : 
    1227         265 :         log_tag("blocksize:%u\n", state->block_size);
    1228        1850 :         for (i = state->disklist; i != 0; i = i->next) {
    1229        1585 :                 struct snapraid_disk* disk = i->data;
    1230        1585 :                 log_tag("data:%s:%s\n", disk->name, disk->dir);
    1231             :         }
    1232             : 
    1233         265 :         log_tag("mode:%s\n", lev_raid_name(state->raid_mode, state->level));
    1234        1531 :         for (l = 0; l < state->level; ++l)
    1235        6330 :                 for (s = 0; s < state->parity[l].split_mac; ++s)
    1236        5064 :                         log_tag("%s:%u:%s\n", lev_config_name(l), s, state->parity[l].split_map[s].path);
    1237         265 :         if (state->pool[0] != 0)
    1238          41 :                 log_tag("pool:%s\n", state->pool);
    1239         265 :         if (state->share[0] != 0)
    1240          41 :                 log_tag("share:%s\n", state->share);
    1241         265 :         if (state->autosave != 0)
    1242          41 :                 log_tag("autosave:%" PRIu64 "\n", state->autosave);
    1243         795 :         for (i = tommy_list_head(&state->filterlist); i != 0; i = i->next) {
    1244             :                 char out[PATH_MAX];
    1245         530 :                 struct snapraid_filter* filter = i->data;
    1246         530 :                 log_tag("filter:%s\n", filter_type(filter, out, sizeof(out)));
    1247             :         }
    1248         265 :         if (state->filter_hidden)
    1249           0 :                 log_tag("filter:nohidden:\n");
    1250         265 :         log_flush();
    1251             : }
    1252             : 
    1253             : /**
    1254             :  * Find a disk by name.
    1255             :  */
    1256        7047 : static struct snapraid_disk* find_disk_by_name(struct snapraid_state* state, const char* name)
    1257             : {
    1258             :         tommy_node* i;
    1259             : 
    1260       24588 :         for (i = state->disklist; i != 0; i = i->next) {
    1261       24580 :                 struct snapraid_disk* disk = i->data;
    1262       24580 :                 if (strcmp(disk->name, name) == 0)
    1263        7039 :                         return disk;
    1264             :         }
    1265             : 
    1266           8 :         if (state->no_conf) {
    1267             :                 /* without a configuration file, add disks automatically */
    1268             :                 struct snapraid_disk* disk;
    1269             : 
    1270           6 :                 disk = disk_alloc(name, "DUMMY/", -1, "", 0);
    1271             : 
    1272           6 :                 tommy_list_insert_tail(&state->disklist, &disk->node, disk);
    1273             : 
    1274           6 :                 return disk;
    1275             :         }
    1276             : 
    1277           2 :         return 0;
    1278             : }
    1279             : 
    1280             : /**
    1281             :  * Find a disk by UUID.
    1282             :  */
    1283           2 : static struct snapraid_disk* find_disk_by_uuid(struct snapraid_state* state, const char* uuid)
    1284             : {
    1285             :         tommy_node* i;
    1286           2 :         struct snapraid_disk* found = 0;
    1287             : 
    1288             :         /* special test case to find the first matching UUID */
    1289             :         /* when testing UUID are all equal or not supported */
    1290             :         /* and we should handle this case specifically */
    1291           2 :         if (state->opt.match_first_uuid)
    1292           2 :                 return state->disklist->data;
    1293             : 
    1294             :         /* LCOV_EXCL_START */
    1295             :         /* never find an empty uuid */
    1296             :         if (!*uuid)
    1297             :                 return 0;
    1298             : 
    1299             :         for (i = state->disklist; i != 0; i = i->next) {
    1300             :                 struct snapraid_disk* disk = i->data;
    1301             :                 if (strcmp(disk->uuid, uuid) == 0) {
    1302             :                         /* never match duplicate UUID */
    1303             :                         if (found)
    1304             :                                 return 0;
    1305             :                         found = disk;
    1306             :                 }
    1307             :         }
    1308             : 
    1309             :         return found;
    1310             :         /* LCOV_EXCL_STOP */
    1311             : }
    1312             : 
    1313             : /**
    1314             :  * Update the disk mapping if required.
    1315             :  */
    1316         260 : static void state_map(struct snapraid_state* state)
    1317             : {
    1318             :         unsigned hole;
    1319             :         tommy_node* i;
    1320             :         unsigned uuid_mismatch;
    1321             :         unsigned diskcount;
    1322             :         unsigned l, s;
    1323             : 
    1324             :         /* remove all the mapping without a disk */
    1325             :         /* this happens when a disk is removed from the configuration file */
    1326             :         /* From SnapRAID 4.0 mappings are automatically removed if a disk is not used */
    1327             :         /* when saving the content file, but we keep this code to import older content files. */
    1328        1917 :         for (i = state->maplist; i != 0; ) {
    1329        1397 :                 struct snapraid_map* map = i->data;
    1330             :                 struct snapraid_disk* disk;
    1331             : 
    1332        1397 :                 disk = find_disk_by_name(state, map->name);
    1333             : 
    1334             :                 /* go to the next mapping before removing */
    1335        1397 :                 i = i->next;
    1336             : 
    1337        1397 :                 if (disk == 0) {
    1338             :                         /* disk not found, remove the mapping */
    1339           0 :                         tommy_list_remove_existing(&state->maplist, &map->node);
    1340           0 :                         map_free(map);
    1341             :                 }
    1342             :         }
    1343             : 
    1344             :         /* maps each unmapped disk present in the configuration file in the first available hole */
    1345             :         /* this happens when you add disks for the first time in the configuration file */
    1346         260 :         hole = 0; /* first position to try */
    1347        1815 :         for (i = state->disklist; i != 0; i = i->next) {
    1348        1555 :                 struct snapraid_disk* disk = i->data;
    1349             :                 struct snapraid_map* map;
    1350             :                 tommy_node* j;
    1351             : 
    1352             :                 /* check if the disk is already mapped */
    1353        5430 :                 for (j = state->maplist; j != 0; j = j->next) {
    1354        5272 :                         map = j->data;
    1355        5272 :                         if (strcmp(disk->name, map->name) == 0) {
    1356             :                                 /* mapping found */
    1357        1397 :                                 break;
    1358             :                         }
    1359             :                 }
    1360        1555 :                 if (j != 0) {
    1361             :                         /* mapping is present, then copy the free blocks into to disk */
    1362        1397 :                         disk->total_blocks = map->total_blocks;
    1363        1397 :                         disk->free_blocks = map->free_blocks;
    1364        1397 :                         continue;
    1365             :                 }
    1366             : 
    1367             :                 /* mapping not found, search for an hole */
    1368             :                 while (1) {
    1369        1100 :                         for (j = state->maplist; j != 0; j = j->next) {
    1370         942 :                                 map = j->data;
    1371         942 :                                 if (map->position == hole) {
    1372             :                                         /* position already used */
    1373         158 :                                         break;
    1374             :                                 }
    1375             :                         }
    1376         316 :                         if (j == 0) {
    1377             :                                 /* hole found */
    1378         158 :                                 break;
    1379             :                         }
    1380             : 
    1381             :                         /* try with the next one */
    1382         158 :                         ++hole;
    1383         158 :                 }
    1384             : 
    1385             :                 /* insert the new mapping */
    1386         158 :                 map = map_alloc(disk->name, hole, 0, 0, "");
    1387             : 
    1388         158 :                 tommy_list_insert_tail(&state->maplist, &map->node, map);
    1389             :         }
    1390             : 
    1391             :         /* without configuration don't check for number of data disks or uuid changes */
    1392         260 :         if (state->no_conf)
    1393           1 :                 return;
    1394             : 
    1395             :         /* counter for the number of UUID mismatches */
    1396         259 :         uuid_mismatch = 0;
    1397             : 
    1398             :         /* check if mapping match the disk uuid */
    1399         259 :         if (!state->opt.skip_disk_access) {
    1400        1626 :                 for (i = state->maplist; i != 0; i = i->next) {
    1401        1393 :                         struct snapraid_map* map = i->data;
    1402             :                         struct snapraid_disk* disk;
    1403             : 
    1404        1393 :                         disk = find_disk_by_name(state, map->name);
    1405        1393 :                         if (disk == 0) {
    1406             :                                 /* LCOV_EXCL_START */
    1407             :                                 log_fatal("Internal inconsistency for mapping '%s'\n", map->name);
    1408             :                                 os_abort();
    1409             :                                 /* LCOV_EXCL_STOP */
    1410             :                         }
    1411             : 
    1412        1393 :                         if (disk->has_unsupported_uuid) {
    1413             :                                 /* if uuid is not available, skip this one */
    1414           1 :                                 continue;
    1415             :                         }
    1416             : 
    1417             :                         /* if the uuid is changed */
    1418        1392 :                         if (strcmp(disk->uuid, map->uuid) != 0) {
    1419             :                                 /* mark the disk as with an UUID change */
    1420         113 :                                 disk->has_different_uuid = 1;
    1421             : 
    1422             :                                 /* if the previous uuid is available */
    1423         113 :                                 if (map->uuid[0] != 0) {
    1424             :                                         /* count the number of uuid change */
    1425           4 :                                         ++uuid_mismatch;
    1426           4 :                                         log_fatal("UUID change for disk '%s' from '%s' to '%s'\n", disk->name, map->uuid, disk->uuid);
    1427             :                                 } else {
    1428             :                                         /* no message here, because having a disk without */
    1429             :                                         /* UUID is the normal state of an empty disk */
    1430         109 :                                         disk->had_empty_uuid = 1;
    1431             :                                 }
    1432             : 
    1433             :                                 /* update the uuid in the mapping, */
    1434         113 :                                 pathcpy(map->uuid, sizeof(map->uuid), disk->uuid);
    1435             : 
    1436             :                                 /* write the new state with the new uuid */
    1437         113 :                                 state->need_write = 1;
    1438             :                         }
    1439             :                 }
    1440             :         }
    1441             : 
    1442             :         /* check the parity uuid */
    1443         259 :         if (!state->opt.skip_parity_access) {
    1444        1273 :                 for (l = 0; l < state->level; ++l) {
    1445        5260 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
    1446             :                                 char uuid[UUID_MAX];
    1447             :                                 int ret;
    1448             : 
    1449        4208 :                                 ret = devuuid(state->parity[l].split_map[s].device, uuid, sizeof(uuid));
    1450        4208 :                                 if (ret != 0) {
    1451             :                                         /* uuid not available, just ignore */
    1452           4 :                                         continue;
    1453             :                                 }
    1454             : 
    1455             :                                 /* if the uuid is changed */
    1456        4204 :                                 if (strcmp(uuid, state->parity[l].split_map[s].uuid) != 0) {
    1457             :                                         /* if the previous uuid is available */
    1458         184 :                                         if (state->parity[l].split_map[s].uuid[0] != 0) {
    1459             :                                                 /* count the number of uuid change */
    1460           0 :                                                 ++uuid_mismatch;
    1461           0 :                                                 log_fatal("UUID change for parity '%s[%u]' from '%s' to '%s'\n", lev_config_name(l), s, state->parity[l].split_map[s].uuid, uuid);
    1462             :                                         }
    1463             : 
    1464             :                                         /* update the uuid */
    1465         184 :                                         pathcpy(state->parity[l].split_map[s].uuid, sizeof(state->parity[l].split_map[s].uuid), uuid);
    1466             : 
    1467             :                                         /* write the new state with the new uuid */
    1468         184 :                                         state->need_write = 1;
    1469             :                                 }
    1470             :                         }
    1471             :                 }
    1472             :         }
    1473             : 
    1474         259 :         if (!state->opt.force_uuid && uuid_mismatch > state->level) {
    1475             :                 /* LCOV_EXCL_START */
    1476             :                 log_fatal("Too many disks have UUID changed from the latest 'sync'.\n");
    1477             :                 log_fatal("If this happens because you really replaced them,\n");
    1478             :                 log_fatal("you can '%s' anyway, using 'snapraid --force-uuid %s'.\n", state->command, state->command);
    1479             :                 log_fatal("Instead, it's possible that you messed up the disk mount points,\n");
    1480             :                 log_fatal("and you have to restore the mount points at the state of the latest sync.\n");
    1481             :                 exit(EXIT_FAILURE);
    1482             :                 /* LCOV_EXCL_STOP */
    1483             :         }
    1484             : 
    1485             :         /* count the number of data disks, including holes left after removing some */
    1486         259 :         diskcount = 0;
    1487        1808 :         for (i = state->maplist; i != 0; i = i->next) {
    1488        1549 :                 struct snapraid_map* map = i->data;
    1489             : 
    1490        1549 :                 if (map->position + 1 > diskcount)
    1491        1367 :                         diskcount = map->position + 1;
    1492             :         }
    1493             : 
    1494             :         /* ensure to don't go over the limit of the RAID engine */
    1495         259 :         if (diskcount > RAID_DATA_MAX) {
    1496             :                 /* LCOV_EXCL_START */
    1497             :                 log_fatal("Too many data disks. No more than %u.\n", RAID_DATA_MAX);
    1498             :                 exit(EXIT_FAILURE);
    1499             :                 /* LCOV_EXCL_STOP */
    1500             :         }
    1501             : 
    1502             :         /* now count the real number of data disks, excluding holes left after removing some */
    1503         259 :         diskcount = tommy_list_count(&state->maplist);
    1504             : 
    1505             :         /* recommend number of parities */
    1506         259 :         if (!state->opt.no_warnings) {
    1507             :                 /* intentionally use log_fatal() instead of log_error() to give more visibility at the warning */
    1508           1 :                 if (diskcount >= 36 && state->level < 6) {
    1509           0 :                         log_fatal("WARNING! With %u disks it's recommended to use six parity levels.\n", diskcount);
    1510           1 :                 } else if (diskcount >= 29 && state->level < 5) {
    1511           0 :                         log_fatal("WARNING! With %u disks it's recommended to use five parity levels.\n", diskcount);
    1512           1 :                 } else if (diskcount >= 22 && state->level < 4) {
    1513           0 :                         log_fatal("WARNING! With %u disks it's recommended to use four parity levels.\n", diskcount);
    1514           1 :                 } else if (diskcount >= 15 && state->level < 3) {
    1515           0 :                         log_fatal("WARNING! With %u disks it's recommended to use three parity levels.\n", diskcount);
    1516           1 :                 } else if (diskcount >= 5 && state->level < 2) {
    1517           0 :                         log_fatal("WARNING! With %u disks it's recommended to use two parity levels.\n", diskcount);
    1518             :                 }
    1519             :         }
    1520             : }
    1521             : 
    1522         173 : void state_refresh(struct snapraid_state* state)
    1523             : {
    1524             :         tommy_node* i;
    1525             :         unsigned l, s;
    1526             : 
    1527             :         /* for all disks */
    1528        1209 :         for (i = state->maplist; i != 0; i = i->next) {
    1529        1036 :                 struct snapraid_map* map = i->data;
    1530             :                 struct snapraid_disk* disk;
    1531             :                 uint64_t total_space;
    1532             :                 uint64_t free_space;
    1533             :                 int ret;
    1534             : 
    1535        1036 :                 disk = find_disk_by_name(state, map->name);
    1536        1036 :                 if (disk == 0) {
    1537             :                         /* LCOV_EXCL_START */
    1538             :                         log_fatal("Internal inconsistency for mapping '%s'\n", map->name);
    1539             :                         os_abort();
    1540             :                         /* LCOV_EXCL_STOP */
    1541             :                 }
    1542             : 
    1543        1036 :                 ret = fsinfo(disk->dir, 0, 0, &total_space, &free_space);
    1544        1036 :                 if (ret != 0) {
    1545             :                         /* LCOV_EXCL_START */
    1546             :                         log_fatal("Error accessing disk '%s' to get file-system info. %s.\n", disk->dir, strerror(errno));
    1547             :                         exit(EXIT_FAILURE);
    1548             :                         /* LCOV_EXCL_STOP */
    1549             :                 }
    1550             : 
    1551             :                 /* set the new free blocks */
    1552        1036 :                 map->total_blocks = total_space / state->block_size;
    1553        1036 :                 map->free_blocks = free_space / state->block_size;
    1554             : 
    1555             :                 /* also update the disk info */
    1556        1036 :                 disk->total_blocks = map->total_blocks;
    1557        1036 :                 disk->free_blocks = map->free_blocks;
    1558             :         }
    1559             : 
    1560             :         /* for all parities */
    1561        1141 :         for (l = 0; l < state->level; ++l) {
    1562             :                 /* set the new free blocks */
    1563         968 :                 state->parity[l].total_blocks = 0;
    1564         968 :                 state->parity[l].free_blocks = 0;
    1565             : 
    1566        4840 :                 for (s = 0; s < state->parity[l].split_mac; ++s) {
    1567             :                         uint64_t total_space;
    1568             :                         uint64_t free_space;
    1569             :                         int ret;
    1570             : 
    1571        3872 :                         ret = fsinfo(state->parity[l].split_map[s].path, 0, 0, &total_space, &free_space);
    1572        3872 :                         if (ret != 0) {
    1573             :                                 /* LCOV_EXCL_START */
    1574             :                                 log_fatal("Error accessing file '%s' to get file-system info. %s.\n", state->parity[l].split_map[s].path, strerror(errno));
    1575             :                                 exit(EXIT_FAILURE);
    1576             :                                 /* LCOV_EXCL_STOP */
    1577             :                         }
    1578             : 
    1579             :                         /* add the new free blocks */
    1580        3872 :                         state->parity[l].total_blocks += total_space / state->block_size;
    1581        3872 :                         state->parity[l].free_blocks += free_space / state->block_size;
    1582             :                 }
    1583             :         }
    1584             : 
    1585             :         /* note what we don't set need_write = 1, because we don't want */
    1586             :         /* to update the content file only for the free space info. */
    1587         173 : }
    1588             : 
    1589             : /**
    1590             :  * Check the content.
    1591             :  */
    1592         253 : static void state_content_check(struct snapraid_state* state, const char* path)
    1593             : {
    1594             :         tommy_node* i;
    1595             : 
    1596             :         /* check that any map has different name and position */
    1597        1766 :         for (i = state->maplist; i != 0; i = i->next) {
    1598        1513 :                 struct snapraid_map* map = i->data;
    1599             :                 tommy_node* j;
    1600        5283 :                 for (j = i->next; j != 0; j = j->next) {
    1601        3770 :                         struct snapraid_map* other = j->data;
    1602        3770 :                         if (strcmp(map->name, other->name) == 0) {
    1603             :                                 /* LCOV_EXCL_START */
    1604             :                                 log_fatal("Colliding 'map' disk specification in '%s'\n", path);
    1605             :                                 exit(EXIT_FAILURE);
    1606             :                                 /* LCOV_EXCL_STOP */
    1607             :                         }
    1608        3770 :                         if (map->position == other->position) {
    1609             :                                 /* LCOV_EXCL_START */
    1610             :                                 log_fatal("Colliding 'map' index specification in '%s'\n", path);
    1611             :                                 exit(EXIT_FAILURE);
    1612             :                                 /* LCOV_EXCL_STOP */
    1613             :                         }
    1614             :                 }
    1615             :         }
    1616         253 : }
    1617             : 
    1618             : /**
    1619             :  * Check if the position is REQUIRED, or we can completely clear it from the state.
    1620             :  *
    1621             :  * Note that position with only DELETED blocks are discarged.
    1622             :  */
    1623      756099 : static int fs_position_is_required(struct snapraid_state* state, block_off_t pos)
    1624             : {
    1625             :         tommy_node* i;
    1626             : 
    1627             :         /* check for each disk */
    1628      802725 :         for (i = state->disklist; i != 0; i = i->next) {
    1629      802725 :                 struct snapraid_disk* disk = i->data;
    1630      802725 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1631             : 
    1632             :                 /* if we have at least one file, the position is needed */
    1633      802725 :                 if (block_has_file(block))
    1634      756099 :                         return 1;
    1635             :         }
    1636             : 
    1637           0 :         return 0;
    1638             : }
    1639             : 
    1640             : /**
    1641             :  * Check if the info block is REQUIREQ.
    1642             :  *
    1643             :  * This is used to ensure that we keep the last check used for scrubbing.
    1644             :  * and that we add it when importing old context files.
    1645             :  *
    1646             :  * Note that you can have position without info blocks, for example
    1647             :  * if all the blocks are not synced.
    1648             :  *
    1649             :  * Note also that not requiring an info block, doesn't mean that if present it
    1650             :  * can be discarded.
    1651             :  */
    1652     1547923 : static int fs_info_is_required(struct snapraid_state* state, block_off_t pos)
    1653             : {
    1654             :         tommy_node* i;
    1655             : 
    1656             :         /* check for each disk */
    1657     2048488 :         for (i = state->disklist; i != 0; i = i->next) {
    1658     1989723 :                 struct snapraid_disk* disk = i->data;
    1659     1989723 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1660             : 
    1661             :                 /* if we have at least one synced file, the info is required */
    1662     1989723 :                 if (block_state_get(block) == BLOCK_STATE_BLK)
    1663     1489158 :                         return 1;
    1664             :         }
    1665             : 
    1666       58765 :         return 0;
    1667             : }
    1668             : 
    1669           0 : static void fs_position_clear_deleted(struct snapraid_state* state, block_off_t pos)
    1670             : {
    1671             :         tommy_node* i;
    1672             : 
    1673             :         /* check for each disk if block is really used */
    1674           0 :         for (i = state->disklist; i != 0; i = i->next) {
    1675           0 :                 struct snapraid_disk* disk = i->data;
    1676           0 :                 struct snapraid_block* block = fs_par2block_find(disk, pos);
    1677             : 
    1678             :                 /* if the block is deleted */
    1679           0 :                 if (block_state_get(block) == BLOCK_STATE_DELETED) {
    1680             :                         /* set it to empty */
    1681           0 :                         fs_deallocate(disk, pos);
    1682             :                 }
    1683             :         }
    1684           0 : }
    1685             : 
    1686             : /**
    1687             :  * Check if a block position in a disk is deleted.
    1688             :  */
    1689     4529621 : static int fs_is_block_deleted(struct snapraid_disk* disk, block_off_t pos)
    1690             : {
    1691     4529621 :         struct snapraid_block* block = fs_par2block_find(disk, pos);
    1692             : 
    1693     4529621 :         return block_state_get(block) == BLOCK_STATE_DELETED;
    1694             : }
    1695             : 
    1696             : /**
    1697             :  * Flush the file checking the final CRC.
    1698             :  * We exploit the fact that the CRC is always stored in the last 4 bytes.
    1699             :  */
    1700           1 : static void decoding_error(const char* path, STREAM* f)
    1701             : {
    1702             :         unsigned char buf[4];
    1703             :         uint32_t crc_stored;
    1704             :         uint32_t crc_computed;
    1705             : 
    1706           1 :         if (seof(f)) {
    1707             :                 /* LCOV_EXCL_START */
    1708             :                 log_fatal("Unexpected end of content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    1709             :                 log_fatal("This content file is truncated. Use an alternate copy.\n");
    1710             :                 exit(EXIT_FAILURE);
    1711             :                 /* LCOV_EXCL_STOP */
    1712             :         }
    1713             : 
    1714           1 :         if (serror(f)) {
    1715             :                 /* LCOV_EXCL_START */
    1716             :                 log_fatal("Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    1717             :                 exit(EXIT_FAILURE);
    1718             :                 /* LCOV_EXCL_STOP */
    1719             :         }
    1720             : 
    1721           1 :         log_fatal("Decoding error in '%s' at offset %" PRIi64 "\n", path, stell(f));
    1722             : 
    1723           1 :         if (sdeplete(f, buf) != 0) {
    1724             :                 /* LCOV_EXCL_START */
    1725             :                 log_fatal("Error flushing the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    1726             :                 exit(EXIT_FAILURE);
    1727             :                 /* LCOV_EXCL_STOP */
    1728             :         }
    1729             : 
    1730             :         /* get the stored crc from the last four bytes */
    1731           1 :         crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
    1732             : 
    1733             :         /* get the computed crc */
    1734           1 :         crc_computed = scrc(f);
    1735             : 
    1736             :         /* adjust the stored crc to include itself */
    1737           1 :         crc_stored = crc32c(crc_stored, buf, 4);
    1738             : 
    1739           1 :         if (crc_computed != crc_stored) {
    1740           1 :                 log_fatal("Mismatching CRC in '%s'\n", path);
    1741           1 :                 log_fatal("This content file is damaged! Use an alternate copy.\n");
    1742           1 :                 exit(EXIT_FAILURE);
    1743             :         } else {
    1744           0 :                 log_fatal("The file CRC is correct!\n");
    1745             :         }
    1746           0 : }
    1747             : 
    1748         254 : static void state_read_content(struct snapraid_state* state, const char* path, STREAM* f)
    1749             : {
    1750             :         block_off_t blockmax;
    1751             :         unsigned count_file;
    1752             :         unsigned count_hardlink;
    1753             :         unsigned count_symlink;
    1754             :         unsigned count_dir;
    1755             :         int crc_checked;
    1756             :         char buffer[PATH_MAX];
    1757             :         int ret;
    1758             :         tommy_array disk_mapping;
    1759             :         uint32_t mapping_max;
    1760             : 
    1761         254 :         blockmax = 0;
    1762         254 :         count_file = 0;
    1763         254 :         count_hardlink = 0;
    1764         254 :         count_symlink = 0;
    1765         254 :         count_dir = 0;
    1766         254 :         crc_checked = 0;
    1767         254 :         mapping_max = 0;
    1768         254 :         tommy_array_init(&disk_mapping);
    1769             : 
    1770         254 :         ret = sread(f, buffer, 12);
    1771         254 :         if (ret < 0) {
    1772             :                 /* LCOV_EXCL_START */
    1773             :                 decoding_error(path, f);
    1774             :                 log_fatal("Invalid header!\n");
    1775             :                 os_abort();
    1776             :                 /* LCOV_EXCL_STOP */
    1777             :         }
    1778             : 
    1779             :         /*
    1780             :          * File format versions:
    1781             :          *  - SNAPCNT1/SnapRAID 4.0 First version.
    1782             :          *  - SNAPCNT2/SnapRAID 7.0 Adds entries 'M' and 'P', to add free_blocks support.
    1783             :          *    The previous 'm' entry is now deprecated, but supported for importing.
    1784             :          *    Similarly for text file, we add 'mapping' and 'parity' deprecating 'map'.
    1785             :          *  - SNAPCNT3/SnapRAID 11.0 Adds entry 'y' for hash size.
    1786             :          *  - SNAPCNT3/SnapRAID 11.0 Adds entry 'Q' for multi parity file.
    1787             :          *    The previous 'P' entry is now deprecated, but supported for importing.
    1788             :          */
    1789         254 :         if (memcmp(buffer, "SNAPCNT1\n\3\0\0", 12) != 0
    1790         254 :                 && memcmp(buffer, "SNAPCNT2\n\3\0\0", 12) != 0
    1791         254 :                 && memcmp(buffer, "SNAPCNT3\n\3\0\0", 12) != 0
    1792             :         ) {
    1793             :                 /* LCOV_EXCL_START */
    1794             :                 if (memcmp(buffer, "SNAPCNT", 7) != 0) {
    1795             :                         decoding_error(path, f);
    1796             :                         log_fatal("Invalid header!\n");
    1797             :                         os_abort();
    1798             :                 } else {
    1799             :                         log_fatal("The content file '%s' was generated with a newer version of SnapRAID!\n", path);
    1800             :                         exit(EXIT_FAILURE);
    1801             :                 }
    1802             :                 /* LCOV_EXCL_STOP */
    1803             :         }
    1804             : 
    1805             :         while (1) {
    1806             :                 int c;
    1807             : 
    1808             :                 /* read the command */
    1809     3735611 :                 c = sgetc(f);
    1810     3735611 :                 if (c == EOF) {
    1811         253 :                         break;
    1812             :                 }
    1813             : 
    1814     3735358 :                 if (c == 'f') {
    1815             :                         /* file */
    1816             :                         char sub[PATH_MAX];
    1817             :                         uint64_t v_size;
    1818             :                         uint64_t v_mtime_sec;
    1819             :                         uint32_t v_mtime_nsec;
    1820             :                         uint64_t v_inode;
    1821             :                         uint32_t v_idx;
    1822             :                         struct snapraid_file* file;
    1823             :                         struct snapraid_disk* disk;
    1824             :                         uint32_t mapping;
    1825             : 
    1826     3611762 :                         ret = sgetb32(f, &mapping);
    1827     3611762 :                         if (ret < 0 || mapping >= mapping_max) {
    1828             :                                 /* LCOV_EXCL_START */
    1829             :                                 decoding_error(path, f);
    1830             :                                 log_fatal("Internal inconsistency in mapping index!\n");
    1831             :                                 os_abort();
    1832             :                                 /* LCOV_EXCL_STOP */
    1833             :                         }
    1834     3611762 :                         disk = tommy_array_get(&disk_mapping, mapping);
    1835             : 
    1836     3611762 :                         ret = sgetb64(f, &v_size);
    1837     3611762 :                         if (ret < 0) {
    1838             :                                 /* LCOV_EXCL_START */
    1839             :                                 decoding_error(path, f);
    1840             :                                 os_abort();
    1841             :                                 /* LCOV_EXCL_STOP */
    1842             :                         }
    1843             : 
    1844     3611762 :                         if (state->block_size == 0) {
    1845             :                                 /* LCOV_EXCL_START */
    1846             :                                 decoding_error(path, f);
    1847             :                                 log_fatal("Internal incosistency due zero blocksize!\n");
    1848             :                                 exit(EXIT_FAILURE);
    1849             :                                 /* LCOV_EXCL_STOP */
    1850             :                         }
    1851             : 
    1852             :                         /* check for impossible file size to avoid to crash for a too big allocation */
    1853     3611762 :                         if (v_size / state->block_size > blockmax) {
    1854             :                                 /* LCOV_EXCL_START */
    1855             :                                 decoding_error(path, f);
    1856             :                                 log_fatal("Internal inconsistency in file size too big!\n");
    1857             :                                 os_abort();
    1858             :                                 /* LCOV_EXCL_STOP */
    1859             :                         }
    1860             : 
    1861     3611762 :                         ret = sgetb64(f, &v_mtime_sec);
    1862     3611762 :                         if (ret < 0) {
    1863             :                                 /* LCOV_EXCL_START */
    1864             :                                 decoding_error(path, f);
    1865             :                                 os_abort();
    1866             :                                 /* LCOV_EXCL_STOP */
    1867             :                         }
    1868             : 
    1869     3611762 :                         ret = sgetb32(f, &v_mtime_nsec);
    1870     3611762 :                         if (ret < 0) {
    1871             :                                 /* LCOV_EXCL_START */
    1872             :                                 decoding_error(path, f);
    1873             :                                 os_abort();
    1874             :                                 /* LCOV_EXCL_STOP */
    1875             :                         }
    1876             : 
    1877             :                         /* STAT_NSEC_INVALID is encoded as 0 */
    1878     3611762 :                         if (v_mtime_nsec == 0)
    1879           0 :                                 v_mtime_nsec = STAT_NSEC_INVALID;
    1880             :                         else
    1881     3611762 :                                 --v_mtime_nsec;
    1882             : 
    1883     3611762 :                         ret = sgetb64(f, &v_inode);
    1884     3611762 :                         if (ret < 0) {
    1885             :                                 /* LCOV_EXCL_START */
    1886             :                                 decoding_error(path, f);
    1887             :                                 os_abort();
    1888             :                                 /* LCOV_EXCL_STOP */
    1889             :                         }
    1890             : 
    1891     3611762 :                         ret = sgetbs(f, sub, sizeof(sub));
    1892     3611762 :                         if (ret < 0) {
    1893             :                                 /* LCOV_EXCL_START */
    1894             :                                 decoding_error(path, f);
    1895             :                                 os_abort();
    1896             :                                 /* LCOV_EXCL_STOP */
    1897             :                         }
    1898     3611762 :                         if (!*sub) {
    1899             :                                 /* LCOV_EXCL_START */
    1900             :                                 decoding_error(path, f);
    1901             :                                 log_fatal("Internal inconsistency for null file!\n");
    1902             :                                 os_abort();
    1903             :                                 /* LCOV_EXCL_STOP */
    1904             :                         }
    1905             : 
    1906             :                         /* allocate the file */
    1907     3611762 :                         file = file_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec, v_inode, 0);
    1908             : 
    1909             :                         /* insert the file in the file containers */
    1910     3611762 :                         tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode));
    1911     3611762 :                         tommy_hashdyn_insert(&disk->pathset, &file->pathset, file, file_path_hash(file->sub));
    1912     3611762 :                         tommy_hashdyn_insert(&disk->stampset, &file->stampset, file, file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec));
    1913     3611762 :                         tommy_list_insert_tail(&disk->filelist, &file->nodelist, file);
    1914             : 
    1915             :                         /* read all the blocks */
    1916     3611762 :                         v_idx = 0;
    1917    10855957 :                         while (v_idx < file->blockmax) {
    1918             :                                 block_off_t v_pos;
    1919             :                                 uint32_t v_count;
    1920             : 
    1921             :                                 /* get the "subcommand */
    1922     3632433 :                                 c = sgetc(f);
    1923             : 
    1924     3632433 :                                 ret = sgetb32(f, &v_pos);
    1925     3632433 :                                 if (ret < 0) {
    1926             :                                         /* LCOV_EXCL_START */
    1927             :                                         decoding_error(path, f);
    1928             :                                         os_abort();
    1929             :                                         /* LCOV_EXCL_STOP */
    1930             :                                 }
    1931             : 
    1932     3632433 :                                 ret = sgetb32(f, &v_count);
    1933     3632433 :                                 if (ret < 0) {
    1934             :                                         /* LCOV_EXCL_START */
    1935             :                                         decoding_error(path, f);
    1936             :                                         os_abort();
    1937             :                                         /* LCOV_EXCL_STOP */
    1938             :                                 }
    1939             : 
    1940     3632433 :                                 if (v_idx + v_count > file->blockmax) {
    1941             :                                         /* LCOV_EXCL_START */
    1942             :                                         decoding_error(path, f);
    1943             :                                         log_fatal("Internal inconsistency in block number!\n");
    1944             :                                         os_abort();
    1945             :                                         /* LCOV_EXCL_STOP */
    1946             :                                 }
    1947             : 
    1948     3632433 :                                 if (v_pos + v_count > blockmax) {
    1949             :                                         /* LCOV_EXCL_START */
    1950             :                                         decoding_error(path, f);
    1951             :                                         log_fatal("Internal inconsistency in block size %u/%u!\n", blockmax, v_pos + v_count);
    1952             :                                         os_abort();
    1953             :                                         /* LCOV_EXCL_START */
    1954             :                                 }
    1955             : 
    1956             :                                 /* fill the blocks in the run */
    1957             :                                 while (v_count) {
    1958             :                                         struct snapraid_block* block = fs_file2block_get(file, v_idx);
    1959             : 
    1960             :                                         switch (c) {
    1961             :                                         case 'b' :
    1962             :                                                 block_state_set(block, BLOCK_STATE_BLK);
    1963             :                                                 break;
    1964             :                                         case 'n' :
    1965             :                                                 /* deprecated NEW blocks are converted to CHG ones */
    1966             :                                                 block_state_set(block, BLOCK_STATE_CHG);
    1967             :                                                 break;
    1968             :                                         case 'g' :
    1969             :                                                 block_state_set(block, BLOCK_STATE_CHG);
    1970             :                                                 break;
    1971             :                                         case 'p' :
    1972             :                                                 block_state_set(block, BLOCK_STATE_REP);
    1973             :                                                 break;
    1974             :                                         default :
    1975             :                                                 /* LCOV_EXCL_START */
    1976             :                                                 decoding_error(path, f);
    1977             :                                                 log_fatal("Invalid block type!\n");
    1978             :                                                 os_abort();
    1979             :                                                 /* LCOV_EXCL_STOP */
    1980             :                                         }
    1981             : 
    1982             :                                         /* read the hash only for 'blk/chg/rep', and not for 'new' */
    1983     8896100 :                                         if (c != 'n') {
    1984     8896100 :                                                 ret = sread(f, block->hash, BLOCK_HASH_SIZE);
    1985     8896100 :                                                 if (ret < 0) {
    1986             :                                                         /* LCOV_EXCL_START */
    1987             :                                                         decoding_error(path, f);
    1988             :                                                         os_abort();
    1989             :                                                         /* LCOV_EXCL_STOP */
    1990             :                                                 }
    1991             :                                         } else {
    1992             :                                                 /* set the ZERO hash for deprecated NEW blocks */
    1993           0 :                                                 hash_zero_set(block->hash);
    1994             :                                         }
    1995             : 
    1996             :                                         /* if the block contains a hash of past data */
    1997             :                                         /* and we are clearing such indeterminate hashes */
    1998     8896100 :                                         if (state->clear_past_hash
    1999     2542004 :                                                 && block_has_past_hash(block)
    2000             :                                         ) {
    2001             :                                                 /* set the hash value to INVALID */
    2002       28006 :                                                 hash_invalid_set(block->hash);
    2003             :                                         }
    2004             : 
    2005             :                                         /* if we are disabling the copy optimization */
    2006             :                                         /* we want also to clear any already previously stored information */
    2007             :                                         /* in other sync commands */
    2008             :                                         /* note that this is required only in sync, and we detect */
    2009             :                                         /* this using the clear_past_hash flag */
    2010     8896100 :                                         if (state->clear_past_hash
    2011     2542004 :                                                 && state->opt.force_nocopy
    2012       14195 :                                                 && block_state_get(block) == BLOCK_STATE_REP
    2013             :                                         ) {
    2014             :                                                 /* set the hash value to INVALID */
    2015           1 :                                                 hash_invalid_set(block->hash);
    2016             :                                                 /* convert from REP to CHG block */
    2017           1 :                                                 block_state_set(block, BLOCK_STATE_CHG);
    2018             :                                         }
    2019             : 
    2020             :                                         /* if we want a full reallocation, marks block as invalid parity */
    2021             :                                         /* note that we do this after the force_nocopy option */
    2022             :                                         /* to avoid to mixup the two things */
    2023     8896100 :                                         if (state->opt.force_realloc
    2024       27826 :                                                 && block_state_get(block) == BLOCK_STATE_BLK) {
    2025             :                                                 /* convert from BLK to REP */
    2026       27826 :                                                 block_state_set(block, BLOCK_STATE_REP);
    2027             :                                         }
    2028             : 
    2029             :                                         /* set the parity association */
    2030     8896100 :                                         fs_allocate(disk, v_pos, file, v_idx);
    2031             : 
    2032             :                                         /* go to the next block */
    2033     8896100 :                                         ++v_idx;
    2034     8896100 :                                         ++v_pos;
    2035     8896100 :                                         --v_count;
    2036             :                                 }
    2037             :                         }
    2038             : 
    2039             :                         /* stat */
    2040     3611762 :                         ++count_file;
    2041      123596 :                 } else if (c == 'i') {
    2042             :                         /* "inf" command */
    2043             :                         snapraid_info info;
    2044             :                         uint32_t v_pos;
    2045             :                         uint32_t v_oldest;
    2046             : 
    2047         253 :                         ret = sgetb32(f, &v_oldest);
    2048         253 :                         if (ret < 0) {
    2049             :                                 /* LCOV_EXCL_START */
    2050             :                                 decoding_error(path, f);
    2051             :                                 os_abort();
    2052             :                                 /* LCOV_EXCL_STOP */
    2053             :                         }
    2054             : 
    2055         253 :                         v_pos = 0;
    2056       55763 :                         while (v_pos < blockmax) {
    2057             :                                 int bad;
    2058             :                                 int rehash;
    2059             :                                 int justsynced;
    2060             :                                 uint32_t t;
    2061             :                                 uint32_t flag;
    2062             :                                 uint32_t v_count;
    2063             : 
    2064       55257 :                                 ret = sgetb32(f, &v_count);
    2065       55257 :                                 if (ret < 0) {
    2066             :                                         /* LCOV_EXCL_START */
    2067             :                                         decoding_error(path, f);
    2068             :                                         os_abort();
    2069             :                                         /* LCOV_EXCL_STOP */
    2070             :                                 }
    2071             : 
    2072       55257 :                                 if (v_pos + v_count > blockmax) {
    2073             :                                         /* LCOV_EXCL_START */
    2074             :                                         decoding_error(path, f);
    2075             :                                         log_fatal("Internal inconsistency in info size %u/%u!\n", blockmax, v_pos + v_count);
    2076             :                                         os_abort();
    2077             :                                         /* LCOV_EXCL_STOP */
    2078             :                                 }
    2079             : 
    2080       55257 :                                 ret = sgetb32(f, &flag);
    2081       55257 :                                 if (ret < 0) {
    2082             :                                         /* LCOV_EXCL_START */
    2083             :                                         decoding_error(path, f);
    2084             :                                         os_abort();
    2085             :                                         /* LCOV_EXCL_STOP */
    2086             :                                 }
    2087             : 
    2088             :                                 /* if there is an info */
    2089       55257 :                                 if ((flag & 1) != 0) {
    2090             :                                         /* read the time */
    2091       55242 :                                         ret = sgetb32(f, &t);
    2092       55242 :                                         if (ret < 0) {
    2093             :                                                 /* LCOV_EXCL_START */
    2094             :                                                 decoding_error(path, f);
    2095             :                                                 os_abort();
    2096             :                                                 /* LCOV_EXCL_STOP */
    2097             :                                         }
    2098             : 
    2099             :                                         /* analyze the flags */
    2100       55242 :                                         bad = (flag & 2) != 0;
    2101       55242 :                                         rehash = (flag & 4) != 0;
    2102       55242 :                                         justsynced = (flag & 8) != 0;
    2103             : 
    2104       55242 :                                         if (rehash && state->prevhash == HASH_UNDEFINED) {
    2105             :                                                 /* LCOV_EXCL_START */
    2106             :                                                 decoding_error(path, f);
    2107             :                                                 log_fatal("Internal inconsistency for missing previous checksum!\n");
    2108             :                                                 os_abort();
    2109             :                                                 /* LCOV_EXCL_STOP */
    2110             :                                         }
    2111             : 
    2112       55242 :                                         info = info_make(t + v_oldest, bad, rehash, justsynced);
    2113             :                                 } else {
    2114          15 :                                         info = 0;
    2115             :                                 }
    2116             : 
    2117     1658437 :                                 while (v_count) {
    2118             :                                         /* insert the info in the array */
    2119     1547923 :                                         info_set(&state->infoarr, v_pos, info);
    2120             : 
    2121             :                                         /* ensure that an info is present only for used positions */
    2122     1547923 :                                         if (fs_info_is_required(state, v_pos)) {
    2123     1489158 :                                                 if (!info) {
    2124             :                                                         /* LCOV_EXCL_START */
    2125             :                                                         decoding_error(path, f);
    2126             :                                                         log_fatal("Internal inconsistency for missing info!\n");
    2127             :                                                         os_abort();
    2128             :                                                         /* LCOV_EXCL_STOP */
    2129             :                                                 }
    2130             :                                         } else {
    2131             :                                                 /* extra info are accepted for backward compatibility */
    2132             :                                                 /* they are discarded at the first write */
    2133             :                                         }
    2134             : 
    2135             :                                         /* go to next block */
    2136     1547923 :                                         ++v_pos;
    2137     1547923 :                                         --v_count;
    2138             :                                 }
    2139             :                         }
    2140      123343 :                 } else if (c == 'h') {
    2141             :                         /* hole */
    2142             :                         uint32_t v_pos;
    2143             :                         struct snapraid_disk* disk;
    2144             :                         uint32_t mapping;
    2145             : 
    2146        1397 :                         ret = sgetb32(f, &mapping);
    2147        1397 :                         if (ret < 0 || mapping >= mapping_max) {
    2148             :                                 /* LCOV_EXCL_START */
    2149             :                                 decoding_error(path, f);
    2150             :                                 log_fatal("Internal inconsistency in mapping index!\n");
    2151             :                                 os_abort();
    2152             :                                 /* LCOV_EXCL_STOP */
    2153             :                         }
    2154        1397 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2155             : 
    2156        1397 :                         v_pos = 0;
    2157        4257 :                         while (v_pos < blockmax) {
    2158             :                                 uint32_t v_idx;
    2159             :                                 uint32_t v_count;
    2160             :                                 struct snapraid_file* deleted;
    2161             : 
    2162        1463 :                                 ret = sgetb32(f, &v_count);
    2163        1463 :                                 if (ret < 0) {
    2164             :                                         /* LCOV_EXCL_START */
    2165             :                                         decoding_error(path, f);
    2166             :                                         os_abort();
    2167             :                                         /* LCOV_EXCL_STOP */
    2168             :                                 }
    2169             : 
    2170        1463 :                                 if (v_pos + v_count > blockmax) {
    2171             :                                         /* LCOV_EXCL_START */
    2172             :                                         decoding_error(path, f);
    2173             :                                         log_fatal("Internal inconsistency in hole size %u/%u!\n", blockmax, v_pos + v_count);
    2174             :                                         os_abort();
    2175             :                                         /* LCOV_EXCL_STOP */
    2176             :                                 }
    2177             : 
    2178             :                                 /* get the sub-command */
    2179        1463 :                                 c = sgetc(f);
    2180             : 
    2181        1463 :                                 switch (c) {
    2182             :                                 case 'o' :
    2183             :                                         /* if it's a run of deleted blocks */
    2184             : 
    2185             :                                         /* allocate a fake deleted file */
    2186          35 :                                         deleted = file_alloc(state->block_size, "<deleted>", v_count * (data_off_t)state->block_size, 0, 0, 0, 0);
    2187             : 
    2188             :                                         /* mark the file as deleted */
    2189          35 :                                         file_flag_set(deleted, FILE_IS_DELETED);
    2190             : 
    2191             :                                         /* insert it in the list of deleted files */
    2192          35 :                                         tommy_list_insert_tail(&disk->deletedlist, &deleted->nodelist, deleted);
    2193             : 
    2194             :                                         /* process all blocks */
    2195          35 :                                         v_idx = 0;
    2196       27956 :                                         while (v_count) {
    2197       27886 :                                                 struct snapraid_block* block = fs_file2block_get(deleted, v_idx);
    2198             : 
    2199             :                                                 /* set the block as deleted */
    2200       27886 :                                                 block_state_set(block, BLOCK_STATE_DELETED);
    2201             : 
    2202             :                                                 /* read the hash */
    2203       27886 :                                                 ret = sread(f, block->hash, BLOCK_HASH_SIZE);
    2204       27886 :                                                 if (ret < 0) {
    2205             :                                                         /* LCOV_EXCL_START */
    2206             :                                                         decoding_error(path, f);
    2207             :                                                         os_abort();
    2208             :                                                         /* LCOV_EXCL_STOP */
    2209             :                                                 }
    2210             : 
    2211             :                                                 /* if we are clearing indeterminate hashes */
    2212       27886 :                                                 if (state->clear_past_hash) {
    2213             :                                                         /* set the hash value to INVALID */
    2214        9432 :                                                         hash_invalid_set(block->hash);
    2215             :                                                 }
    2216             : 
    2217             :                                                 /* insert the block in the block array */
    2218       27886 :                                                 fs_allocate(disk, v_pos, deleted, v_idx);
    2219             : 
    2220             :                                                 /* go to next block */
    2221       27886 :                                                 ++v_pos;
    2222       27886 :                                                 ++v_idx;
    2223       27886 :                                                 --v_count;
    2224             :                                         }
    2225          35 :                                         break;
    2226             :                                 case 'O' :
    2227             :                                         /* go to the next run */
    2228        1428 :                                         v_pos += v_count;
    2229        1428 :                                         break;
    2230             :                                 default :
    2231             :                                         /* LCOV_EXCL_START */
    2232             :                                         decoding_error(path, f);
    2233             :                                         log_fatal("Invalid hole type!\n");
    2234             :                                         os_abort();
    2235             :                                         /* LCOV_EXCL_STOP */
    2236             :                                 }
    2237             :                         }
    2238      121946 :                 } else if (c == 's') {
    2239             :                         /* symlink */
    2240             :                         char sub[PATH_MAX];
    2241             :                         char linkto[PATH_MAX];
    2242             :                         struct snapraid_link* slink;
    2243             :                         struct snapraid_disk* disk;
    2244             :                         uint32_t mapping;
    2245             : 
    2246      116468 :                         ret = sgetb32(f, &mapping);
    2247      116468 :                         if (ret < 0 || mapping >= mapping_max) {
    2248             :                                 /* LCOV_EXCL_START */
    2249             :                                 decoding_error(path, f);
    2250             :                                 log_fatal("Internal inconsistency in mapping index!\n");
    2251             :                                 os_abort();
    2252             :                                 /* LCOV_EXCL_STOP */
    2253             :                         }
    2254      116468 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2255             : 
    2256      116468 :                         ret = sgetbs(f, sub, sizeof(sub));
    2257      116468 :                         if (ret < 0) {
    2258             :                                 /* LCOV_EXCL_START */
    2259             :                                 decoding_error(path, f);
    2260             :                                 os_abort();
    2261             :                                 /* LCOV_EXCL_STOP */
    2262             :                         }
    2263             : 
    2264      116468 :                         if (!*sub) {
    2265             :                                 /* LCOV_EXCL_START */
    2266             :                                 decoding_error(path, f);
    2267             :                                 log_fatal("Internal inconsistency for null symlink!\n");
    2268             :                                 os_abort();
    2269             :                                 /* LCOV_EXCL_STOP */
    2270             :                         }
    2271             : 
    2272      116468 :                         ret = sgetbs(f, linkto, sizeof(linkto));
    2273      116468 :                         if (ret < 0) {
    2274             :                                 /* LCOV_EXCL_START */
    2275             :                                 decoding_error(path, f);
    2276             :                                 os_abort();
    2277             :                                 /* LCOV_EXCL_STOP */
    2278             :                         }
    2279             : 
    2280             :                         /* allocate the link as symbolic link */
    2281      116468 :                         slink = link_alloc(sub, linkto, FILE_IS_SYMLINK);
    2282             : 
    2283             :                         /* insert the link in the link containers */
    2284      116468 :                         tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
    2285      116468 :                         tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
    2286             : 
    2287             :                         /* stat */
    2288      116468 :                         ++count_symlink;
    2289        5478 :                 } else if (c == 'a') {
    2290             :                         /* hardlink */
    2291             :                         char sub[PATH_MAX];
    2292             :                         char linkto[PATH_MAX];
    2293             :                         struct snapraid_link* slink;
    2294             :                         struct snapraid_disk* disk;
    2295             :                         uint32_t mapping;
    2296             : 
    2297         597 :                         ret = sgetb32(f, &mapping);
    2298         597 :                         if (ret < 0 || mapping >= mapping_max) {
    2299             :                                 /* LCOV_EXCL_START */
    2300             :                                 decoding_error(path, f);
    2301             :                                 log_fatal("Internal inconsistency in mapping index!\n");
    2302             :                                 os_abort();
    2303             :                                 /* LCOV_EXCL_STOP */
    2304             :                         }
    2305         597 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2306             : 
    2307         597 :                         ret = sgetbs(f, sub, sizeof(sub));
    2308         597 :                         if (ret < 0) {
    2309             :                                 /* LCOV_EXCL_START */
    2310             :                                 decoding_error(path, f);
    2311             :                                 os_abort();
    2312             :                                 /* LCOV_EXCL_STOP */
    2313             :                         }
    2314             : 
    2315         597 :                         if (!*sub) {
    2316             :                                 /* LCOV_EXCL_START */
    2317             :                                 decoding_error(path, f);
    2318             :                                 log_fatal("Internal inconsistency for null hardlink!\n");
    2319             :                                 os_abort();
    2320             :                                 /* LCOV_EXCL_STOP */
    2321             :                         }
    2322             : 
    2323         597 :                         ret = sgetbs(f, linkto, sizeof(linkto));
    2324         597 :                         if (ret < 0) {
    2325             :                                 /* LCOV_EXCL_START */
    2326             :                                 decoding_error(path, f);
    2327             :                                 os_abort();
    2328             :                                 /* LCOV_EXCL_STOP */
    2329             :                         }
    2330             : 
    2331         597 :                         if (!*linkto) {
    2332             :                                 /* LCOV_EXCL_START */
    2333             :                                 decoding_error(path, f);
    2334             :                                 log_fatal("Internal inconsistency for empty hardlink '%s'!\n", sub);
    2335             :                                 os_abort();
    2336             :                                 /* LCOV_EXCL_STOP */
    2337             :                         }
    2338             : 
    2339             :                         /* allocate the link as hard link */
    2340         597 :                         slink = link_alloc(sub, linkto, FILE_IS_HARDLINK);
    2341             : 
    2342             :                         /* insert the link in the link containers */
    2343         597 :                         tommy_hashdyn_insert(&disk->linkset, &slink->nodeset, slink, link_name_hash(slink->sub));
    2344         597 :                         tommy_list_insert_tail(&disk->linklist, &slink->nodelist, slink);
    2345             : 
    2346             :                         /* stat */
    2347         597 :                         ++count_hardlink;
    2348        4881 :                 } else if (c == 'r') {
    2349             :                         /* dir */
    2350             :                         char sub[PATH_MAX];
    2351             :                         struct snapraid_dir* dir;
    2352             :                         struct snapraid_disk* disk;
    2353             :                         uint32_t mapping;
    2354             : 
    2355         790 :                         ret = sgetb32(f, &mapping);
    2356         790 :                         if (ret < 0 || mapping >= mapping_max) {
    2357             :                                 /* LCOV_EXCL_START */
    2358             :                                 decoding_error(path, f);
    2359             :                                 log_fatal("Internal inconsistency in mapping index!\n");
    2360             :                                 os_abort();
    2361             :                                 /* LCOV_EXCL_STOP */
    2362             :                         }
    2363         790 :                         disk = tommy_array_get(&disk_mapping, mapping);
    2364             : 
    2365         790 :                         ret = sgetbs(f, sub, sizeof(sub));
    2366         790 :                         if (ret < 0) {
    2367             :                                 /* LCOV_EXCL_START */
    2368             :                                 decoding_error(path, f);
    2369             :                                 os_abort();
    2370             :                                 /* LCOV_EXCL_STOP */
    2371             :                         }
    2372             : 
    2373         790 :                         if (!*sub) {
    2374             :                                 /* LCOV_EXCL_START */
    2375             :                                 decoding_error(path, f);
    2376             :                                 log_fatal("Internal inconsistency for null dir!\n");
    2377             :                                 os_abort();
    2378             :                                 /* LCOV_EXCL_STOP */
    2379             :                         }
    2380             : 
    2381             :                         /* allocate the dir */
    2382         790 :                         dir = dir_alloc(sub);
    2383             : 
    2384             :                         /* insert the dir in the dir containers */
    2385         790 :                         tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub));
    2386         790 :                         tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir);
    2387             : 
    2388             :                         /* stat */
    2389         790 :                         ++count_dir;
    2390        4091 :                 } else if (c == 'c') {
    2391             :                         /* get the subcommand */
    2392         254 :                         c = sgetc(f);
    2393             : 
    2394         254 :                         switch (c) {
    2395             :                         case 'u' :
    2396         139 :                                 state->hash = HASH_MURMUR3;
    2397         139 :                                 break;
    2398             :                         case 'k' :
    2399         115 :                                 state->hash = HASH_SPOOKY2;
    2400         115 :                                 break;
    2401             :                         default :
    2402             :                                 /* LCOV_EXCL_START */
    2403             :                                 decoding_error(path, f);
    2404             :                                 log_fatal("Invalid checksum!\n");
    2405             :                                 os_abort();
    2406             :                                 /* LCOV_EXCL_STOP */
    2407             :                         }
    2408             : 
    2409             :                         /* read the seed */
    2410         254 :                         ret = sread(f, state->hashseed, HASH_MAX);
    2411         254 :                         if (ret < 0) {
    2412             :                                 /* LCOV_EXCL_START */
    2413             :                                 decoding_error(path, f);
    2414             :                                 os_abort();
    2415             :                                 /* LCOV_EXCL_STOP */
    2416             :                         }
    2417        3837 :                 } else if (c == 'C') {
    2418             :                         /* get the sub-command */
    2419           4 :                         c = sgetc(f);
    2420             : 
    2421           4 :                         switch (c) {
    2422             :                         case 'u' :
    2423           4 :                                 state->prevhash = HASH_MURMUR3;
    2424           4 :                                 break;
    2425             :                         case 'k' :
    2426           0 :                                 state->prevhash = HASH_SPOOKY2;
    2427           0 :                                 break;
    2428             :                         default :
    2429             :                                 /* LCOV_EXCL_START */
    2430             :                                 decoding_error(path, f);
    2431             :                                 log_fatal("Invalid checksum!\n");
    2432             :                                 os_abort();
    2433             :                                 /* LCOV_EXCL_STOP */
    2434             :                         }
    2435             : 
    2436             :                         /* read the seed */
    2437           4 :                         ret = sread(f, state->prevhashseed, HASH_MAX);
    2438           4 :                         if (ret < 0) {
    2439             :                                 /* LCOV_EXCL_START */
    2440             :                                 decoding_error(path, f);
    2441             :                                 os_abort();
    2442             :                                 /* LCOV_EXCL_STOP */
    2443             :                         }
    2444        3833 :                 } else if (c == 'z') {
    2445             :                         uint32_t block_size;
    2446             : 
    2447         254 :                         ret = sgetb32(f, &block_size);
    2448         254 :                         if (ret < 0) {
    2449             :                                 /* LCOV_EXCL_START */
    2450             :                                 decoding_error(path, f);
    2451             :                                 os_abort();
    2452             :                                 /* LCOV_EXCL_STOP */
    2453             :                         }
    2454             : 
    2455         254 :                         if (block_size == 0) {
    2456             :                                 /* LCOV_EXCL_START */
    2457             :                                 decoding_error(path, f);
    2458             :                                 log_fatal("Zero 'blocksize' specification in the content file!\n");
    2459             :                                 exit(EXIT_FAILURE);
    2460             :                                 /* LCOV_EXCL_STOP */
    2461             :                         }
    2462             : 
    2463             :                         /* without configuration, auto assign the block size */
    2464         254 :                         if (state->no_conf) {
    2465           1 :                                 state->block_size = block_size;
    2466             :                         }
    2467             : 
    2468         254 :                         if (block_size != state->block_size) {
    2469             :                                 /* LCOV_EXCL_START */
    2470             :                                 decoding_error(path, f);
    2471             :                                 log_fatal("Mismatching 'blocksize' specification in the content file!\n");
    2472             :                                 log_fatal("Please restore the 'blocksize' value in the configuration file to '%u'\n", block_size / KIBI);
    2473             :                                 exit(EXIT_FAILURE);
    2474             :                                 /* LCOV_EXCL_STOP */
    2475             :                         }
    2476        3579 :                 } else if (c == 'y') {
    2477             :                         uint32_t hash_size;
    2478             : 
    2479         254 :                         ret = sgetb32(f, &hash_size);
    2480         254 :                         if (ret < 0) {
    2481             :                                 /* LCOV_EXCL_START */
    2482             :                                 decoding_error(path, f);
    2483             :                                 os_abort();
    2484             :                                 /* LCOV_EXCL_STOP */
    2485             :                         }
    2486             : 
    2487         254 :                         if (hash_size < 2 || hash_size > HASH_MAX) {
    2488             :                                 /* LCOV_EXCL_START */
    2489             :                                 decoding_error(path, f);
    2490             :                                 log_fatal("Invalid 'hashsize' specification in the content file!\n");
    2491             :                                 exit(EXIT_FAILURE);
    2492             :                                 /* LCOV_EXCL_STOP */
    2493             :                         }
    2494             : 
    2495             :                         /* without configuration, auto assign the block size */
    2496         254 :                         if (state->no_conf) {
    2497           1 :                                 BLOCK_HASH_SIZE = hash_size;
    2498             :                         }
    2499             : 
    2500         254 :                         if ((int)hash_size != BLOCK_HASH_SIZE) {
    2501             :                                 /* LCOV_EXCL_START */
    2502             :                                 decoding_error(path, f);
    2503             :                                 log_fatal("Mismatching 'hashsize' specification in the content file!\n");
    2504             :                                 log_fatal("Please restore the 'hashsize' value in the configuration file to '%u'\n", hash_size);
    2505             :                                 exit(EXIT_FAILURE);
    2506             :                                 /* LCOV_EXCL_STOP */
    2507             :                         }
    2508        3325 :                 } else if (c == 'x') {
    2509         254 :                         ret = sgetb32(f, &blockmax);
    2510         254 :                         if (ret < 0) {
    2511             :                                 /* LCOV_EXCL_START */
    2512             :                                 decoding_error(path, f);
    2513             :                                 os_abort();
    2514             :                                 /* LCOV_EXCL_STOP */
    2515             :                         }
    2516        4474 :                 } else if (c == 'm' || c == 'M') {
    2517             :                         struct snapraid_map* map;
    2518             :                         char uuid[UUID_MAX];
    2519             :                         uint32_t v_pos;
    2520             :                         uint32_t v_total_blocks;
    2521             :                         uint32_t v_free_blocks;
    2522             :                         struct snapraid_disk* disk;
    2523             : 
    2524        1403 :                         ret = sgetbs(f, buffer, sizeof(buffer));
    2525        1403 :                         if (ret < 0) {
    2526             :                                 /* LCOV_EXCL_START */
    2527             :                                 decoding_error(path, f);
    2528             :                                 os_abort();
    2529             :                                 /* LCOV_EXCL_STOP */
    2530             :                         }
    2531             : 
    2532        1403 :                         ret = sgetb32(f, &v_pos);
    2533        1403 :                         if (ret < 0) {
    2534             :                                 /* LCOV_EXCL_START */
    2535             :                                 decoding_error(path, f);
    2536             :                                 os_abort();
    2537             :                                 /* LCOV_EXCL_STOP */
    2538             :                         }
    2539             : 
    2540             :                         /* from SnapRAID 7.0 the 'M' command includes the free space */
    2541        1403 :                         if (c == 'M') {
    2542        1403 :                                 ret = sgetb32(f, &v_total_blocks);
    2543        1403 :                                 if (ret < 0) {
    2544             :                                         /* LCOV_EXCL_START */
    2545             :                                         decoding_error(path, f);
    2546             :                                         os_abort();
    2547             :                                         /* LCOV_EXCL_STOP */
    2548             :                                 }
    2549             : 
    2550        1403 :                                 ret = sgetb32(f, &v_free_blocks);
    2551        1403 :                                 if (ret < 0) {
    2552             :                                         /* LCOV_EXCL_START */
    2553             :                                         decoding_error(path, f);
    2554             :                                         os_abort();
    2555             :                                         /* LCOV_EXCL_STOP */
    2556             :                                 }
    2557             :                         } else {
    2558           0 :                                 v_total_blocks = 0;
    2559           0 :                                 v_free_blocks = 0;
    2560             :                         }
    2561             : 
    2562             :                         /* read the uuid */
    2563        1403 :                         ret = sgetbs(f, uuid, sizeof(uuid));
    2564        1403 :                         if (ret < 0) {
    2565             :                                 /* LCOV_EXCL_START */
    2566             :                                 decoding_error(path, f);
    2567             :                                 os_abort();
    2568             :                                 /* LCOV_EXCL_STOP */
    2569             :                         }
    2570             : 
    2571             :                         /* find the disk */
    2572        1403 :                         disk = find_disk_by_name(state, buffer);
    2573        1403 :                         if (!disk) {
    2574             :                                 /* search by UUID if renamed */
    2575           2 :                                 disk = find_disk_by_uuid(state, uuid);
    2576           2 :                                 if (disk) {
    2577           2 :                                         log_fatal("WARNING! Renaming disk '%s' to '%s'\n", buffer, disk->name);
    2578             : 
    2579             :                                         /* write the new state with the new name */
    2580           2 :                                         state->need_write = 1;
    2581             :                                 }
    2582             :                         }
    2583        1403 :                         if (!disk) {
    2584             :                                 /* LCOV_EXCL_START */
    2585             :                                 decoding_error(path, f);
    2586             :                                 log_fatal("Disk '%s' with uuid '%s' not present in the configuration file!\n", buffer, uuid);
    2587             :                                 log_fatal("If you have removed it from the configuration file, please restore it\n");
    2588             :                                 /* if it's a command without UUID, it cannot autorename using UUID */
    2589             :                                 if (state->opt.skip_disk_access)
    2590             :                                         log_fatal("If you have renamed it, run 'sync' to update the new name\n");
    2591             :                                 exit(EXIT_FAILURE);
    2592             :                                 /* LCOV_EXCL_STOP */
    2593             :                         }
    2594             : 
    2595        1403 :                         map = map_alloc(disk->name, v_pos, v_total_blocks, v_free_blocks, uuid);
    2596             : 
    2597        1403 :                         tommy_list_insert_tail(&state->maplist, &map->node, map);
    2598             : 
    2599             :                         /* insert in the mapping vector */
    2600        1403 :                         tommy_array_grow(&disk_mapping, mapping_max + 1);
    2601        1403 :                         tommy_array_set(&disk_mapping, mapping_max, disk);
    2602        1403 :                         ++mapping_max;
    2603        1668 :                 } else if (c == 'P') {
    2604             :                         /* from SnapRAID 7.0 the 'P' command includes the free space */
    2605             :                         /* from SnapRAID 11.0 the 'P' command is deprecated by 'Q' */
    2606             :                         char v_uuid[UUID_MAX];
    2607             :                         uint32_t v_level;
    2608             :                         uint32_t v_total_blocks;
    2609             :                         uint32_t v_free_blocks;
    2610             : 
    2611           0 :                         ret = sgetb32(f, &v_level);
    2612           0 :                         if (ret < 0) {
    2613             :                                 /* LCOV_EXCL_START */
    2614             :                                 decoding_error(path, f);
    2615             :                                 os_abort();
    2616             :                                 /* LCOV_EXCL_STOP */
    2617             :                         }
    2618             : 
    2619           0 :                         ret = sgetb32(f, &v_total_blocks);
    2620           0 :                         if (ret < 0) {
    2621             :                                 /* LCOV_EXCL_START */
    2622             :                                 decoding_error(path, f);
    2623             :                                 os_abort();
    2624             :                                 /* LCOV_EXCL_STOP */
    2625             :                         }
    2626             : 
    2627           0 :                         ret = sgetb32(f, &v_free_blocks);
    2628           0 :                         if (ret < 0) {
    2629             :                                 /* LCOV_EXCL_START */
    2630             :                                 decoding_error(path, f);
    2631             :                                 os_abort();
    2632             :                                 /* LCOV_EXCL_STOP */
    2633             :                         }
    2634             : 
    2635           0 :                         ret = sgetbs(f, v_uuid, sizeof(v_uuid));
    2636           0 :                         if (ret < 0) {
    2637             :                                 /* LCOV_EXCL_START */
    2638             :                                 decoding_error(path, f);
    2639             :                                 os_abort();
    2640             :                                 /* LCOV_EXCL_STOP */
    2641             :                         }
    2642             : 
    2643           0 :                         if (v_level >= LEV_MAX) {
    2644             :                                 /* LCOV_EXCL_START */
    2645             :                                 decoding_error(path, f);
    2646             :                                 log_fatal("Invalid parity level '%u' in the configuration file!\n", v_level);
    2647             :                                 exit(EXIT_FAILURE);
    2648             :                                 /* LCOV_EXCL_STOP */
    2649             :                         }
    2650             : 
    2651             :                         /* auto configure if configuration is missing */
    2652           0 :                         if (state->no_conf) {
    2653           0 :                                 if (v_level >= state->level)
    2654           0 :                                         state->level = v_level + 1;
    2655             :                         }
    2656             : 
    2657             :                         /* if we use this parity entry */
    2658           0 :                         if (v_level < state->level) {
    2659             :                                 /* if the configuration has more splits, keep them */
    2660           0 :                                 if (state->parity[v_level].split_mac < 1)
    2661           0 :                                         state->parity[v_level].split_mac = 1;
    2662             :                                 /* set the parity info */
    2663           0 :                                 pathcpy(state->parity[v_level].split_map[0].uuid, sizeof(state->parity[v_level].split_map[0].uuid), v_uuid);
    2664           0 :                                 state->parity[v_level].total_blocks = v_total_blocks;
    2665           0 :                                 state->parity[v_level].free_blocks = v_free_blocks;
    2666             :                         }
    2667        1668 :                 } else if (c == 'Q') {
    2668             :                         /* from SnapRAID 11.0 the 'Q' command include size info and multi file support  */
    2669             :                         uint32_t v_level;
    2670             :                         uint32_t v_total_blocks;
    2671             :                         uint32_t v_free_blocks;
    2672             :                         uint32_t v_split_mac;
    2673             :                         unsigned s;
    2674             : 
    2675        1415 :                         ret = sgetb32(f, &v_level);
    2676        1415 :                         if (ret < 0) {
    2677             :                                 /* LCOV_EXCL_START */
    2678             :                                 decoding_error(path, f);
    2679             :                                 os_abort();
    2680             :                                 /* LCOV_EXCL_STOP */
    2681             :                         }
    2682             : 
    2683        1415 :                         ret = sgetb32(f, &v_total_blocks);
    2684        1415 :                         if (ret < 0) {
    2685             :                                 /* LCOV_EXCL_START */
    2686             :                                 decoding_error(path, f);
    2687             :                                 os_abort();
    2688             :                                 /* LCOV_EXCL_STOP */
    2689             :                         }
    2690             : 
    2691        1415 :                         ret = sgetb32(f, &v_free_blocks);
    2692        1415 :                         if (ret < 0) {
    2693             :                                 /* LCOV_EXCL_START */
    2694             :                                 decoding_error(path, f);
    2695             :                                 os_abort();
    2696             :                                 /* LCOV_EXCL_STOP */
    2697             :                         }
    2698             : 
    2699        1415 :                         ret = sgetb32(f, &v_split_mac);
    2700        1415 :                         if (ret < 0) {
    2701             :                                 /* LCOV_EXCL_START */
    2702             :                                 decoding_error(path, f);
    2703             :                                 os_abort();
    2704             :                                 /* LCOV_EXCL_STOP */
    2705             :                         }
    2706             : 
    2707        1415 :                         if (v_level >= LEV_MAX) {
    2708             :                                 /* LCOV_EXCL_START */
    2709             :                                 decoding_error(path, f);
    2710             :                                 log_fatal("Invalid parity level '%u' in the configuration file!\n", v_level);
    2711             :                                 exit(EXIT_FAILURE);
    2712             :                                 /* LCOV_EXCL_STOP */
    2713             :                         }
    2714             : 
    2715             :                         /* auto configure if configuration is missing */
    2716        1415 :                         if (state->no_conf) {
    2717           6 :                                 if (v_level >= state->level)
    2718           5 :                                         state->level = v_level + 1;
    2719           6 :                                 if (state->parity[v_level].split_mac < v_split_mac)
    2720           6 :                                         state->parity[v_level].split_mac = v_split_mac;
    2721             :                         }
    2722             : 
    2723             :                         /* if we use this parity entry */
    2724        1415 :                         if (v_level < state->level) {
    2725             :                                 /* set the parity info */
    2726        1156 :                                 state->parity[v_level].total_blocks = v_total_blocks;
    2727        1156 :                                 state->parity[v_level].free_blocks = v_free_blocks;
    2728             :                         }
    2729             : 
    2730        7072 :                         for (s = 0; s < v_split_mac; ++s) {
    2731             :                                 char v_path[PATH_MAX];
    2732             :                                 char v_uuid[UUID_MAX];
    2733             :                                 uint64_t v_size;
    2734             : 
    2735        5658 :                                 ret = sgetbs(f, v_path, sizeof(v_path));
    2736        5658 :                                 if (ret < 0) {
    2737             :                                         /* LCOV_EXCL_START */
    2738             :                                         decoding_error(path, f);
    2739             :                                         os_abort();
    2740             :                                         /* LCOV_EXCL_STOP */
    2741             :                                 }
    2742             : 
    2743        5657 :                                 ret = sgetbs(f, v_uuid, sizeof(v_uuid));
    2744        5657 :                                 if (ret < 0) {
    2745             :                                         /* LCOV_EXCL_START */
    2746             :                                         decoding_error(path, f);
    2747             :                                         os_abort();
    2748             :                                         /* LCOV_EXCL_STOP */
    2749             :                                 }
    2750             : 
    2751        5657 :                                 ret = sgetb64(f, &v_size);
    2752        5657 :                                 if (ret < 0) {
    2753             :                                         /* LCOV_EXCL_START */
    2754             :                                         decoding_error(path, f);
    2755             :                                         os_abort();
    2756             :                                         /* LCOV_EXCL_STOP */
    2757             :                                 }
    2758             : 
    2759             :                                 /* if we use this parity entry */
    2760        5657 :                                 if (v_level < state->level) {
    2761             :                                         /* if this split was removed from the configuration */
    2762        4621 :                                         if (s >= state->parity[v_level].split_mac) {
    2763             :                                                 /* if the file is used, we really need it */
    2764           0 :                                                 if (v_size != 0) {
    2765             :                                                         /* LCOV_EXCL_START */
    2766             :                                                         decoding_error(path, f);
    2767             :                                                         log_fatal("Parity '%s' misses used file '%u'!\n", lev_config_name(v_level), s);
    2768             :                                                         log_fatal("If you have removed it from the configuration file, please restore it\n");
    2769             :                                                         exit(EXIT_FAILURE);
    2770             :                                                         /* LCOV_EXCL_STOP */
    2771             :                                                 }
    2772             : 
    2773             :                                                 /* otherwise we can drop it */
    2774           0 :                                                 log_fatal("WARNING! Dropping from '%s' unused split '%u'\n", lev_config_name(v_level), s);
    2775             :                                         } else {
    2776             :                                                 /* we copy the path only if without configuration file */
    2777        4621 :                                                 if (state->no_conf)
    2778          24 :                                                         pathcpy(state->parity[v_level].split_map[s].path, sizeof(state->parity[v_level].split_map[s].path), v_path);
    2779             : 
    2780             :                                                 /* set the split info */
    2781        4621 :                                                 pathcpy(state->parity[v_level].split_map[s].uuid, sizeof(state->parity[v_level].split_map[s].uuid), v_uuid);
    2782        4621 :                                                 state->parity[v_level].split_map[s].size = v_size;
    2783             : 
    2784             :                                                 /* log the info read from the content file */
    2785        9242 :                                                 log_tag("content:%s:%u:%s:%s:%" PRIi64 "\n", lev_config_name(v_level), s,
    2786        4621 :                                                         state->parity[v_level].split_map[s].path,
    2787        4621 :                                                         state->parity[v_level].split_map[s].uuid,
    2788             :                                                         state->parity[v_level].split_map[s].size);
    2789             :                                         }
    2790             :                                 }
    2791             :                         }
    2792         253 :                 } else if (c == 'N') {
    2793             :                         uint32_t crc_stored;
    2794             :                         uint32_t crc_computed;
    2795             : 
    2796             :                         /* get the crc before reading it from the file */
    2797         253 :                         crc_computed = scrc(f);
    2798             : 
    2799         253 :                         ret = sgetble32(f, &crc_stored);
    2800         253 :                         if (ret < 0) {
    2801             :                                 /* LCOV_EXCL_START */
    2802             :                                 /* here don't call decoding_error() because it's too late to get the crc */
    2803             :                                 log_fatal("Error reading the CRC in '%s' at offset %" PRIi64 "\n", path, stell(f));
    2804             :                                 log_fatal("This content file is damaged! Use an alternate copy.\n");
    2805             :                                 exit(EXIT_FAILURE);
    2806             :                                 /* LCOV_EXCL_STOP */
    2807             :                         }
    2808             : 
    2809         253 :                         if (crc_stored != crc_computed) {
    2810             :                                 /* LCOV_EXCL_START */
    2811             :                                 /* here don't call decoding_error() because it's too late to get the crc */
    2812             :                                 log_fatal("Mismatching CRC in '%s'\n", path);
    2813             :                                 log_fatal("This content file is damaged! Use an alternate copy.\n");
    2814             :                                 exit(EXIT_FAILURE);
    2815             :                                 /* LCOV_EXCL_STOP */
    2816             :                         }
    2817             : 
    2818         253 :                         crc_checked = 1;
    2819             :                 } else {
    2820             :                         /* LCOV_EXCL_START */
    2821             :                         decoding_error(path, f);
    2822             :                         log_fatal("Invalid command '%c'!\n", (char)c);
    2823             :                         os_abort();
    2824             :                         /* LCOV_EXCL_STOP */
    2825             :                 }
    2826     3735357 :         }
    2827             : 
    2828         253 :         tommy_array_done(&disk_mapping);
    2829             : 
    2830         253 :         if (serror(f)) {
    2831             :                 /* LCOV_EXCL_START */
    2832             :                 log_fatal("Error reading the content file '%s' at offset %" PRIi64 "\n", path, stell(f));
    2833             :                 exit(EXIT_FAILURE);
    2834             :                 /* LCOV_EXCL_STOP */
    2835             :         }
    2836             : 
    2837         253 :         if (!crc_checked) {
    2838             :                 /* LCOV_EXCL_START */
    2839             :                 log_fatal("Finished reading '%s' without finding the CRC\n", path);
    2840             :                 log_fatal("This content file is truncated or damaged! Use an alternate copy.\n");
    2841             :                 exit(EXIT_FAILURE);
    2842             :                 /* LCOV_EXCL_STOP */
    2843             :         }
    2844             : 
    2845             :         /* check the file-system on all disks */
    2846         253 :         state_fscheck(state, "after read");
    2847             : 
    2848             :         /* check that the stored parity size matches the loaded state */
    2849         253 :         if (blockmax != parity_allocated_size(state)) {
    2850             :                 /* LCOV_EXCL_START */
    2851             :                 log_fatal("Internal inconsistency in parity size %u/%u in '%s' at offset %" PRIi64 "\n", blockmax, parity_allocated_size(state), path, stell(f));
    2852             :                 if (state->opt.skip_content_check) {
    2853             :                         log_fatal("Overriding.\n");
    2854             :                         blockmax = parity_allocated_size(state);
    2855             :                 } else {
    2856             :                         exit(EXIT_FAILURE);
    2857             :                 }
    2858             :                 /* LCOV_EXCL_STOP */
    2859             :         }
    2860             : 
    2861         253 :         msg_verbose("%8u files\n", count_file);
    2862         253 :         msg_verbose("%8u hardlinks\n", count_hardlink);
    2863         253 :         msg_verbose("%8u symlinks\n", count_symlink);
    2864         253 :         msg_verbose("%8u empty dirs\n", count_dir);
    2865         253 : }
    2866             : 
    2867             : struct state_write_thread_context {
    2868             :         struct snapraid_state* state;
    2869             : #if HAVE_MT_WRITE
    2870             :         pthread_t thread;
    2871             : #endif
    2872             :         /* input */
    2873             :         block_off_t blockmax;
    2874             :         time_t info_oldest;
    2875             :         int info_has_rehash;
    2876             :         STREAM* f;
    2877             :         /* output */
    2878             :         uint32_t crc;
    2879             :         unsigned count_file;
    2880             :         unsigned count_hardlink;
    2881             :         unsigned count_symlink;
    2882             :         unsigned count_dir;
    2883             : };
    2884             : 
    2885         151 : static void* state_write_thread(void* arg)
    2886             : {
    2887         151 :         struct state_write_thread_context* context = arg;
    2888         151 :         struct snapraid_state* state = context->state;
    2889         151 :         block_off_t blockmax = context->blockmax;
    2890         151 :         time_t info_oldest = context->info_oldest;
    2891         151 :         int info_has_rehash = context->info_has_rehash;
    2892         151 :         STREAM* f = context->f;
    2893             :         uint32_t crc;
    2894             :         unsigned count_file;
    2895             :         unsigned count_hardlink;
    2896             :         unsigned count_symlink;
    2897             :         unsigned count_dir;
    2898             :         tommy_node* i;
    2899             :         block_off_t idx;
    2900             :         block_off_t begin;
    2901             :         unsigned l, s;
    2902             :         int version;
    2903             : 
    2904         151 :         count_file = 0;
    2905         151 :         count_hardlink = 0;
    2906         151 :         count_symlink = 0;
    2907         151 :         count_dir = 0;
    2908             : 
    2909             :         /* check what version to use */
    2910         151 :         version = 2;
    2911        1012 :         for (l = 0; l < state->level; ++l) {
    2912         861 :                 if (state->parity[l].split_mac > 1)
    2913         861 :                         version = 3;
    2914             :         }
    2915         151 :         if (BLOCK_HASH_SIZE != 16)
    2916           0 :                 version = 3;
    2917             : 
    2918             :         /* write header */
    2919         151 :         if (version == 3)
    2920         151 :                 swrite("SNAPCNT3\n\3\0\0", 12, f);
    2921             :         else
    2922           0 :                 swrite("SNAPCNT2\n\3\0\0", 12, f);
    2923             : 
    2924             :         /* write block size and block max */
    2925         151 :         sputc('z', f);
    2926         151 :         sputb32(state->block_size, f);
    2927         151 :         sputc('x', f);
    2928         151 :         sputb32(blockmax, f);
    2929             : 
    2930             :         /* hash size */
    2931         151 :         if (version == 3) {
    2932         151 :                 sputc('y', f);
    2933         151 :                 sputb32(BLOCK_HASH_SIZE, f);
    2934             :         }
    2935             : 
    2936         151 :         if (serror(f)) {
    2937             :                 /* LCOV_EXCL_START */
    2938             :                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    2939             :                 return context;
    2940             :                 /* LCOV_EXCL_STOP */
    2941             :         }
    2942             : 
    2943         151 :         sputc('c', f);
    2944         151 :         if (state->hash == HASH_MURMUR3) {
    2945          96 :                 sputc('u', f);
    2946          55 :         } else if (state->hash == HASH_SPOOKY2) {
    2947          55 :                 sputc('k', f);
    2948             :         } else {
    2949             :                 /* LCOV_EXCL_START */
    2950             :                 log_fatal("Unexpected hash when writing the content file '%s'.\n", serrorfile(f));
    2951             :                 return context;
    2952             :                 /* LCOV_EXCL_STOP */
    2953             :         }
    2954         151 :         swrite(state->hashseed, HASH_MAX, f);
    2955         151 :         if (serror(f)) {
    2956             :                 /* LCOV_EXCL_START */
    2957             :                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    2958             :                 return context;
    2959             :                 /* LCOV_EXCL_STOP */
    2960             :         }
    2961             : 
    2962             :         /* previous hash only present */
    2963         151 :         if (state->prevhash != HASH_UNDEFINED) {
    2964             :                 /* if at least one rehash tag found, we have to save the previous hash */
    2965           3 :                 if (info_has_rehash) {
    2966           2 :                         sputc('C', f);
    2967           2 :                         if (state->prevhash == HASH_MURMUR3) {
    2968           2 :                                 sputc('u', f);
    2969           0 :                         } else if (state->prevhash == HASH_SPOOKY2) {
    2970           0 :                                 sputc('k', f);
    2971             :                         } else {
    2972             :                                 /* LCOV_EXCL_START */
    2973             :                                 log_fatal("Unexpected prevhash when writing the content file '%s'.\n", serrorfile(f));
    2974             :                                 return context;
    2975             :                                 /* LCOV_EXCL_STOP */
    2976             :                         }
    2977           2 :                         swrite(state->prevhashseed, HASH_MAX, f);
    2978           2 :                         if (serror(f)) {
    2979             :                                 /* LCOV_EXCL_START */
    2980             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    2981             :                                 return context;
    2982             :                                 /* LCOV_EXCL_STOP */
    2983             :                         }
    2984             :                 }
    2985             :         }
    2986             : 
    2987             :         /* for each map */
    2988        1057 :         for (i = state->maplist; i != 0; i = i->next) {
    2989         906 :                 struct snapraid_map* map = i->data;
    2990             :                 struct snapraid_disk* disk;
    2991             : 
    2992             :                 /* find the disk for this mapping */
    2993         906 :                 disk = find_disk_by_name(state, map->name);
    2994         906 :                 if (!disk) {
    2995             :                         /* LCOV_EXCL_START */
    2996             :                         log_fatal("Internal inconsistency for unmapped disk '%s'\n", map->name);
    2997             :                         return context;
    2998             :                         /* LCOV_EXCL_STOP */
    2999             :                 }
    3000             : 
    3001             :                 /* save the mapping only if disk is mapped */
    3002         906 :                 if (disk->mapping_idx != -1) {
    3003         814 :                         sputc('M', f);
    3004         814 :                         sputbs(map->name, f);
    3005         814 :                         sputb32(map->position, f);
    3006         814 :                         sputb32(map->total_blocks, f);
    3007         814 :                         sputb32(map->free_blocks, f);
    3008         814 :                         sputbs(map->uuid, f);
    3009         814 :                         if (serror(f)) {
    3010             :                                 /* LCOV_EXCL_START */
    3011             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3012             :                                 return context;
    3013             :                                 /* LCOV_EXCL_STOP */
    3014             :                         }
    3015             :                 }
    3016             :         }
    3017             : 
    3018             :         /* for each parity */
    3019        1012 :         for (l = 0; l < state->level; ++l) {
    3020         861 :                 if (version == 3) {
    3021         861 :                         sputc('Q', f);
    3022         861 :                         sputb32(l, f);
    3023         861 :                         sputb32(state->parity[l].total_blocks, f);
    3024         861 :                         sputb32(state->parity[l].free_blocks, f);
    3025         861 :                         sputb32(state->parity[l].split_mac, f);
    3026        4305 :                         for (s = 0; s < state->parity[l].split_mac; ++s) {
    3027        3444 :                                 sputbs(state->parity[l].split_map[s].path, f);
    3028        3444 :                                 sputbs(state->parity[l].split_map[s].uuid, f);
    3029        3444 :                                 sputb64(state->parity[l].split_map[s].size, f);
    3030             :                         }
    3031             :                 } else {
    3032           0 :                         sputc('P', f);
    3033           0 :                         sputb32(l, f);
    3034           0 :                         sputb32(state->parity[l].total_blocks, f);
    3035           0 :                         sputb32(state->parity[l].free_blocks, f);
    3036           0 :                         sputbs(state->parity[l].split_map[0].uuid, f);
    3037             :                 }
    3038         861 :                 if (serror(f)) {
    3039             :                         /* LCOV_EXCL_START */
    3040             :                         log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3041             :                         return context;
    3042             :                         /* LCOV_EXCL_STOP */
    3043             :                 }
    3044             :         }
    3045             : 
    3046             :         /* for each disk */
    3047        1057 :         for (i = state->disklist; i != 0; i = i->next) {
    3048             :                 tommy_node* j;
    3049         906 :                 struct snapraid_disk* disk = i->data;
    3050             : 
    3051             :                 /* if the disk is not mapped, skip it */
    3052         906 :                 if (disk->mapping_idx < 0)
    3053          92 :                         continue;
    3054             : 
    3055             :                 /* for each file */
    3056     1771698 :                 for (j = disk->filelist; j != 0; j = j->next) {
    3057     1770884 :                         struct snapraid_file* file = j->data;
    3058             :                         uint64_t size;
    3059             :                         uint64_t mtime_sec;
    3060             :                         int32_t mtime_nsec;
    3061             :                         uint64_t inode;
    3062             : 
    3063     1770884 :                         size = file->size;
    3064     1770884 :                         mtime_sec = file->mtime_sec;
    3065     1770884 :                         mtime_nsec = file->mtime_nsec;
    3066     1770884 :                         inode = file->inode;
    3067             : 
    3068     1770884 :                         sputc('f', f);
    3069     1770884 :                         sputb32(disk->mapping_idx, f);
    3070     1770884 :                         sputb64(size, f);
    3071     1770884 :                         sputb64(mtime_sec, f);
    3072             :                         /* encode STAT_NSEC_INVALID as 0 */
    3073     1770884 :                         if (mtime_nsec == STAT_NSEC_INVALID)
    3074           0 :                                 sputb32(0, f);
    3075             :                         else
    3076     1770884 :                                 sputb32(mtime_nsec + 1, f);
    3077     1770884 :                         sputb64(inode, f);
    3078     1770884 :                         sputbs(file->sub, f);
    3079     1770884 :                         if (serror(f)) {
    3080             :                                 /* LCOV_EXCL_START */
    3081             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3082             :                                 return context;
    3083             :                                 /* LCOV_EXCL_STOP */
    3084             :                         }
    3085             : 
    3086             :                         /* for all the blocks of the file */
    3087     1770884 :                         begin = 0;
    3088     5322957 :                         while (begin < file->blockmax) {
    3089     1781189 :                                 unsigned v_state = block_state_get(fs_file2block_get(file, begin));
    3090     1781189 :                                 block_off_t v_pos = fs_file2par_get(disk, file, begin);
    3091             :                                 uint32_t v_count;
    3092             : 
    3093             :                                 block_off_t end;
    3094             : 
    3095             :                                 /* find the end of run of blocks */
    3096     1781189 :                                 end = begin + 1;
    3097     6146329 :                                 while (end < file->blockmax) {
    3098     2596728 :                                         if (v_state != block_state_get(fs_file2block_get(file, end)))
    3099          12 :                                                 break;
    3100     2596716 :                                         if (v_pos + (end - begin) != fs_file2par_get(disk, file, end))
    3101       12765 :                                                 break;
    3102     2583951 :                                         ++end;
    3103             :                                 }
    3104             : 
    3105     1781189 :                                 switch (v_state) {
    3106             :                                 case BLOCK_STATE_BLK :
    3107     1678557 :                                         sputc('b', f);
    3108     1678557 :                                         break;
    3109             :                                 case BLOCK_STATE_CHG :
    3110       67714 :                                         sputc('g', f);
    3111       67714 :                                         break;
    3112             :                                 case BLOCK_STATE_REP :
    3113       34918 :                                         sputc('p', f);
    3114       34918 :                                         break;
    3115             :                                 default :
    3116             :                                         /* LCOV_EXCL_START */
    3117             :                                         log_fatal("Internal inconsistency in state for block %u state %u\n", v_pos, v_state);
    3118             :                                         return context;
    3119             :                                         /* LCOV_EXCL_STOP */
    3120             :                                 }
    3121             : 
    3122     1781189 :                                 sputb32(v_pos, f);
    3123             : 
    3124     1781189 :                                 v_count = end - begin;
    3125     1781189 :                                 sputb32(v_count, f);
    3126             : 
    3127             :                                 /* write hashes */
    3128     6146329 :                                 for (idx = begin; idx < end; ++idx) {
    3129     4365140 :                                         struct snapraid_block* block = fs_file2block_get(file, idx);
    3130             : 
    3131     4365140 :                                         swrite(block->hash, BLOCK_HASH_SIZE, f);
    3132             :                                 }
    3133             : 
    3134     1781189 :                                 if (serror(f)) {
    3135             :                                         /* LCOV_EXCL_START */
    3136             :                                         log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3137             :                                         return context;
    3138             :                                         /* LCOV_EXCL_STOP */
    3139             :                                 }
    3140             : 
    3141             :                                 /* next begin position */
    3142     1781189 :                                 begin = end;
    3143             :                         }
    3144             : 
    3145     1770884 :                         ++count_file;
    3146             :                 }
    3147             : 
    3148             :                 /* for each link */
    3149       58887 :                 for (j = disk->linklist; j != 0; j = j->next) {
    3150       58073 :                         struct snapraid_link* slink = j->data;
    3151             : 
    3152       58073 :                         switch (link_flag_get(slink, FILE_IS_LINK_MASK)) {
    3153             :                         case FILE_IS_HARDLINK :
    3154         321 :                                 sputc('a', f);
    3155         321 :                                 ++count_hardlink;
    3156         321 :                                 break;
    3157             :                         case FILE_IS_SYMLINK :
    3158       57752 :                                 sputc('s', f);
    3159       57752 :                                 ++count_symlink;
    3160       57752 :                                 break;
    3161             :                         }
    3162             : 
    3163       58073 :                         sputb32(disk->mapping_idx, f);
    3164       58073 :                         sputbs(slink->sub, f);
    3165       58073 :                         sputbs(slink->linkto, f);
    3166       58073 :                         if (serror(f)) {
    3167             :                                 /* LCOV_EXCL_START */
    3168             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3169             :                                 return context;
    3170             :                                 /* LCOV_EXCL_STOP */
    3171             :                         }
    3172             :                 }
    3173             : 
    3174             :                 /* for each dir */
    3175        1241 :                 for (j = disk->dirlist; j != 0; j = j->next) {
    3176         427 :                         struct snapraid_dir* dir = j->data;
    3177             : 
    3178         427 :                         sputc('r', f);
    3179         427 :                         sputb32(disk->mapping_idx, f);
    3180         427 :                         sputbs(dir->sub, f);
    3181         427 :                         if (serror(f)) {
    3182             :                                 /* LCOV_EXCL_START */
    3183             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3184             :                                 return context;
    3185             :                                 /* LCOV_EXCL_STOP */
    3186             :                         }
    3187             : 
    3188         427 :                         ++count_dir;
    3189             :                 }
    3190             : 
    3191             :                 /* deleted blocks of the disk */
    3192         814 :                 sputc('h', f);
    3193         814 :                 sputb32(disk->mapping_idx, f);
    3194         814 :                 if (serror(f)) {
    3195             :                         /* LCOV_EXCL_START */
    3196             :                         log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3197             :                         return context;
    3198             :                         /* LCOV_EXCL_STOP */
    3199             :                 }
    3200         814 :                 begin = 0;
    3201        2516 :                 while (begin < blockmax) {
    3202             :                         int is_deleted;
    3203             :                         block_off_t end;
    3204             : 
    3205         888 :                         is_deleted = fs_is_block_deleted(disk, begin);
    3206             : 
    3207             :                         /* find the end of run of blocks */
    3208         888 :                         end = begin + 1;
    3209     4530435 :                         while (end < blockmax
    3210     4528733 :                                 && is_deleted == fs_is_block_deleted(disk, end)
    3211             :                         ) {
    3212     4528659 :                                 ++end;
    3213             :                         }
    3214             : 
    3215         888 :                         sputb32(end - begin, f);
    3216             : 
    3217         888 :                         if (is_deleted) {
    3218             :                                 /* write the run of deleted blocks with hash */
    3219          47 :                                 sputc('o', f);
    3220             : 
    3221             :                                 /* write all the hash */
    3222       15961 :                                 while (begin < end) {
    3223       15867 :                                         struct snapraid_block* block = fs_par2block_get(disk, begin);
    3224             : 
    3225       15867 :                                         swrite(block->hash, BLOCK_HASH_SIZE, f);
    3226             : 
    3227       15867 :                                         ++begin;
    3228             :                                 }
    3229             :                         } else {
    3230             :                                 /* write the run of blocks without hash */
    3231             :                                 /* they can be either used or empty blocks */
    3232         841 :                                 sputc('O', f);
    3233             : 
    3234             :                                 /* next begin position */
    3235         841 :                                 begin = end;
    3236             :                         }
    3237             : 
    3238         888 :                         if (serror(f)) {
    3239             :                                 /* LCOV_EXCL_START */
    3240             :                                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3241             :                                 return context;
    3242             :                                 /* LCOV_EXCL_STOP */
    3243             :                         }
    3244             :                 }
    3245             :         }
    3246             : 
    3247             :         /* write the info for each block */
    3248         151 :         sputc('i', f);
    3249         151 :         sputb32(info_oldest, f);
    3250         151 :         begin = 0;
    3251       19663 :         while (begin < blockmax) {
    3252             :                 snapraid_info info;
    3253             :                 block_off_t end;
    3254             :                 time_t t;
    3255             :                 unsigned flag;
    3256             : 
    3257       19361 :                 info = info_get(&state->infoarr, begin);
    3258             : 
    3259             :                 /* find the end of run of blocks */
    3260       19361 :                 end = begin + 1;
    3261      775460 :                 while (end < blockmax
    3262      755951 :                         && info == info_get(&state->infoarr, end)
    3263             :                 ) {
    3264      736738 :                         ++end;
    3265             :                 }
    3266             : 
    3267       19361 :                 sputb32(end - begin, f);
    3268             : 
    3269             :                 /* if there is info */
    3270       19361 :                 if (info) {
    3271             :                         /* other flags */
    3272       19338 :                         flag = 1; /* info is present */
    3273       19338 :                         if (info_get_bad(info))
    3274        1331 :                                 flag |= 2;
    3275       19338 :                         if (info_get_rehash(info))
    3276        5212 :                                 flag |= 4;
    3277       19338 :                         if (info_get_justsynced(info))
    3278       12001 :                                 flag |= 8;
    3279       19338 :                         sputb32(flag, f);
    3280             : 
    3281       19338 :                         t = info_get_time(info) - info_oldest;
    3282       19338 :                         sputb32(t, f);
    3283             :                 } else {
    3284             :                         /* write a special 0 flag to mark missing info */
    3285          23 :                         sputb32(0, f);
    3286             :                 }
    3287             : 
    3288       19361 :                 if (serror(f)) {
    3289             :                         /* LCOV_EXCL_START */
    3290             :                         log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3291             :                         return context;
    3292             :                         /* LCOV_EXCL_STOP */
    3293             :                 }
    3294             : 
    3295             :                 /* next begin position */
    3296       19361 :                 begin = end;
    3297             :         }
    3298             : 
    3299         151 :         sputc('N', f);
    3300             : 
    3301             :         /* flush data written to the disk */
    3302         151 :         if (sflush(f)) {
    3303             :                 /* LCOV_EXCL_START */
    3304             :                 log_fatal("Error writing the content file '%s' (in flush before crc). %s.\n", serrorfile(f), strerror(errno));
    3305             :                 return context;
    3306             :                 /* LCOV_EXCL_STOP */
    3307             :         }
    3308             : 
    3309             :         /* get the file crc */
    3310         151 :         crc = scrc(f);
    3311             : 
    3312             :         /* compare the crc of the data written to file */
    3313             :         /* with the one of the data written to the stream */
    3314         151 :         if (crc != scrc_stream(f)) {
    3315             :                 /* LCOV_EXCL_START */
    3316             :                 log_fatal("CRC mismatch writing the content stream.\n");
    3317             :                 log_fatal("DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
    3318             :                 log_fatal("Try running a memory test like http://www.memtest86.com/\n");
    3319             :                 return context;
    3320             :                 /* LCOV_EXCL_STOP */
    3321             :         }
    3322             : 
    3323         151 :         sputble32(crc, f);
    3324         151 :         if (serror(f)) {
    3325             :                 /* LCOV_EXCL_START */
    3326             :                 log_fatal("Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3327             :                 return context;
    3328             :                 /* LCOV_EXCL_STOP */
    3329             :         }
    3330             : 
    3331             :         /* set output variables */
    3332         151 :         context->crc = crc;
    3333         151 :         context->count_file = count_file;
    3334         151 :         context->count_hardlink = count_hardlink;
    3335         151 :         context->count_symlink = count_symlink;
    3336         151 :         context->count_dir = count_dir;
    3337             : 
    3338         151 :         return 0;
    3339             : }
    3340             : 
    3341         151 : static void state_write_content(struct snapraid_state* state, uint32_t* out_crc)
    3342             : {
    3343             : #if HAVE_MT_WRITE
    3344             :         int fail;
    3345             :         int first;
    3346             : #else
    3347             :         STREAM* f;
    3348             :         unsigned count_content;
    3349             :         unsigned k;
    3350             :         struct state_write_thread_context* context;
    3351             :         void* retval;
    3352             : #endif
    3353             :         tommy_node* i;
    3354             :         block_off_t blockmax;
    3355             :         time_t info_oldest;
    3356             :         int info_has_rehash;
    3357             :         int mapping_idx;
    3358             :         block_off_t idx;
    3359             :         uint32_t crc;
    3360             :         unsigned count_file;
    3361             :         unsigned count_hardlink;
    3362             :         unsigned count_symlink;
    3363             :         unsigned count_dir;
    3364             : 
    3365             :         /* blocks of all array */
    3366         151 :         blockmax = parity_allocated_size(state);
    3367             : 
    3368             :         /* check the file-system on all disks */
    3369         151 :         state_fscheck(state, "before write");
    3370             : 
    3371             :         /* clear the info for unused blocks */
    3372             :         /* and get some other info */
    3373         151 :         info_oldest = 0; /* oldest time in info */
    3374         151 :         info_has_rehash = 0; /* if there is a rehash info */
    3375      756250 :         for (idx = 0; idx < blockmax; ++idx) {
    3376             :                 /* if the position is used */
    3377      756099 :                 if (fs_position_is_required(state, idx)) {
    3378      756099 :                         snapraid_info info = info_get(&state->infoarr, idx);
    3379             : 
    3380             :                         /* only if there is some info to store */
    3381      756099 :                         if (info) {
    3382      711614 :                                 time_t info_time = info_get_time(info);
    3383             : 
    3384      711614 :                                 if (!info_oldest || info_time < info_oldest)
    3385         147 :                                         info_oldest = info_time;
    3386             : 
    3387      711614 :                                 if (info_get_rehash(info))
    3388       13822 :                                         info_has_rehash = 1;
    3389             :                         }
    3390             :                 } else {
    3391             :                         /* clear any previous info */
    3392           0 :                         info_set(&state->infoarr, idx, 0);
    3393             : 
    3394             :                         /* and clear any deleted blocks */
    3395           0 :                         fs_position_clear_deleted(state, idx);
    3396             :                 }
    3397             :         }
    3398             : 
    3399             :         /* map disks */
    3400         151 :         mapping_idx = 0;
    3401        1057 :         for (i = state->maplist; i != 0; i = i->next) {
    3402         906 :                 struct snapraid_map* map = i->data;
    3403             :                 struct snapraid_disk* disk;
    3404             : 
    3405             :                 /* find the disk for this mapping */
    3406         906 :                 disk = find_disk_by_name(state, map->name);
    3407         906 :                 if (!disk) {
    3408             :                         /* LCOV_EXCL_START */
    3409             :                         log_fatal("Internal inconsistency for unmapped disk '%s'\n", map->name);
    3410             :                         os_abort();
    3411             :                         /* LCOV_EXCL_STOP */
    3412             :                 }
    3413             : 
    3414             :                 /* save the mapping only for not empty disks */
    3415         906 :                 if (!fs_is_empty(disk, blockmax)) {
    3416             :                         /* assign the mapping index used to identify disks */
    3417         814 :                         disk->mapping_idx = mapping_idx;
    3418         814 :                         ++mapping_idx;
    3419             :                 } else {
    3420             :                         /* mark the disk as without mapping */
    3421          92 :                         disk->mapping_idx = -1;
    3422             :                 }
    3423             :         }
    3424             : 
    3425             : #if HAVE_MT_WRITE
    3426             :         /* start all writing threads */
    3427             :         i = tommy_list_head(&state->contentlist);
    3428             :         while (i) {
    3429             :                 struct snapraid_content* content = i->data;
    3430             :                 struct state_write_thread_context* context;
    3431             :                 char tmp[PATH_MAX];
    3432             :                 STREAM* f;
    3433             : 
    3434             :                 msg_progress("Saving state to %s...\n", content->content);
    3435             : 
    3436             :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    3437             :                 f = sopen_write(tmp);
    3438             :                 if (f == 0) {
    3439             :                         /* LCOV_EXCL_START */
    3440             :                         log_fatal("Error opening the content file '%s'. %s.\n", tmp, strerror(errno));
    3441             :                         exit(EXIT_FAILURE);
    3442             :                         /* LCOV_EXCL_STOP */
    3443             :                 }
    3444             : 
    3445             :                 /* allocate the thread context */
    3446             :                 context = malloc_nofail(sizeof(struct state_write_thread_context));
    3447             :                 content->context = context;
    3448             : 
    3449             :                 /* initialize */
    3450             :                 context->state = state;
    3451             :                 context->blockmax = blockmax;
    3452             :                 context->info_oldest = info_oldest;
    3453             :                 context->info_has_rehash = info_has_rehash;
    3454             :                 context->f = f;
    3455             : 
    3456             :                 thread_create(&context->thread, 0, state_write_thread, context);
    3457             : 
    3458             :                 i = i->next;
    3459             :         }
    3460             : 
    3461             :         /* join all thread */
    3462             :         fail = 0;
    3463             :         first = 1;
    3464             :         crc = 0;
    3465             :         count_file = 0;
    3466             :         count_hardlink = 0;
    3467             :         count_symlink = 0;
    3468             :         count_dir = 0;
    3469             :         i = tommy_list_head(&state->contentlist);
    3470             :         while (i) {
    3471             :                 struct snapraid_content* content = i->data;
    3472             :                 struct state_write_thread_context* context = content->context;
    3473             :                 void* retval;
    3474             : 
    3475             :                 thread_join(context->thread, &retval);
    3476             : 
    3477             :                 if (retval) {
    3478             :                         /* LCOV_EXCL_START */
    3479             :                         fail = 1;
    3480             :                         /* LCOV_EXCL_STOP */
    3481             :                 } else {
    3482             :                         STREAM* f = context->f;
    3483             : 
    3484             :                         /* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
    3485             :                         /* than even in a system crash event we have one valid copy of the file. */
    3486             :                         if (sflush(f) != 0) {
    3487             :                                 /* LCOV_EXCL_START */
    3488             :                                 log_fatal("Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
    3489             :                                 exit(EXIT_FAILURE);
    3490             :                                 /* LCOV_EXCL_STOP */
    3491             :                         }
    3492             : 
    3493             : #if HAVE_FSYNC
    3494             :                         if (ssync(f) != 0) {
    3495             :                                 /* LCOV_EXCL_START */
    3496             :                                 log_fatal("Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
    3497             :                                 exit(EXIT_FAILURE);
    3498             :                                 /* LCOV_EXCL_STOP */
    3499             :                         }
    3500             : #endif
    3501             : 
    3502             :                         if (sclose(f) != 0) {
    3503             :                                 /* LCOV_EXCL_START */
    3504             :                                 log_fatal("Error closing the content file. %s.\n", strerror(errno));
    3505             :                                 exit(EXIT_FAILURE);
    3506             :                                 /* LCOV_EXCL_STOP */
    3507             :                         }
    3508             : 
    3509             :                         if (first) {
    3510             :                                 first = 0;
    3511             :                                 crc = context->crc;
    3512             :                                 count_file = context->count_file;
    3513             :                                 count_hardlink = context->count_hardlink;
    3514             :                                 count_symlink = context->count_symlink;
    3515             :                                 count_dir = context->count_dir;
    3516             :                         } else {
    3517             :                                 if (crc != context->crc) {
    3518             :                                         /* LCOV_EXCL_START */
    3519             :                                         log_fatal("Different CRCs writing content streams.\n");
    3520             :                                         log_fatal("DANGER! Your RAM memory is broken! DO NOT PROCEED UNTIL FIXED!\n");
    3521             :                                         log_fatal("Try running a memory test like http://www.memtest86.com/\n");
    3522             :                                         exit(EXIT_FAILURE);
    3523             :                                         /* LCOV_EXCL_STOP */
    3524             :                                 }
    3525             :                         }
    3526             :                 }
    3527             : 
    3528             :                 free(context);
    3529             : 
    3530             :                 i = i->next;
    3531             :         }
    3532             : 
    3533             :         /* abort on failure */
    3534             :         if (fail) {
    3535             :                 /* LCOV_EXCL_START */
    3536             :                 exit(EXIT_FAILURE);
    3537             :                 /* LCOV_EXCL_STOP */
    3538             :         }
    3539             : #else
    3540             :         /* count the content files */
    3541         151 :         count_content = 0;
    3542         151 :         i = tommy_list_head(&state->contentlist);
    3543        1314 :         while (i) {
    3544        1012 :                 struct snapraid_content* content = i->data;
    3545        1012 :                 msg_progress("Saving state to %s...\n", content->content);
    3546        1012 :                 ++count_content;
    3547        1012 :                 i = i->next;
    3548             :         }
    3549             : 
    3550             :         /* open all the content files */
    3551         151 :         f = sopen_multi_write(count_content);
    3552         151 :         if (!f) {
    3553             :                 /* LCOV_EXCL_START */
    3554             :                 log_fatal("Error opening the content files.\n");
    3555             :                 exit(EXIT_FAILURE);
    3556             :                 /* LCOV_EXCL_STOP */
    3557             :         }
    3558             : 
    3559         151 :         k = 0;
    3560         151 :         i = tommy_list_head(&state->contentlist);
    3561        1314 :         while (i) {
    3562        1012 :                 struct snapraid_content* content = i->data;
    3563             :                 char tmp[PATH_MAX];
    3564        1012 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    3565        1012 :                 if (sopen_multi_file(f, k, tmp) != 0) {
    3566             :                         /* LCOV_EXCL_START */
    3567             :                         log_fatal("Error opening the content file '%s'. %s.\n", tmp, strerror(errno));
    3568             :                         exit(EXIT_FAILURE);
    3569             :                         /* LCOV_EXCL_STOP */
    3570             :                 }
    3571        1012 :                 ++k;
    3572        1012 :                 i = i->next;
    3573             :         }
    3574             : 
    3575             :         /* allocate the thread context */
    3576         151 :         context = malloc_nofail(sizeof(struct state_write_thread_context));
    3577             : 
    3578             :         /* initialize */
    3579         151 :         context->state = state;
    3580         151 :         context->blockmax = blockmax;
    3581         151 :         context->info_oldest = info_oldest;
    3582         151 :         context->info_has_rehash = info_has_rehash;
    3583         151 :         context->f = f;
    3584             : 
    3585         151 :         retval = state_write_thread(context);
    3586             : 
    3587             :         /* abort on failure */
    3588         151 :         if (retval) {
    3589             :                 /* LCOV_EXCL_START */
    3590             :                 exit(EXIT_FAILURE);
    3591             :                 /* LCOV_EXCL_STOP */
    3592             :         }
    3593             : 
    3594             :         /* Use the sequence fflush() -> fsync() -> fclose() -> rename() to ensure */
    3595             :         /* than even in a system crash event we have one valid copy of the file. */
    3596         151 :         if (sflush(f) != 0) {
    3597             :                 /* LCOV_EXCL_START */
    3598             :                 log_fatal("Error writing the content file '%s', in flush(). %s.\n", serrorfile(f), strerror(errno));
    3599             :                 exit(EXIT_FAILURE);
    3600             :                 /* LCOV_EXCL_STOP */
    3601             :         }
    3602             : 
    3603             : #if HAVE_FSYNC
    3604         151 :         if (ssync(f) != 0) {
    3605             :                 /* LCOV_EXCL_START */
    3606             :                 log_fatal("Error writing the content file '%s' in sync(). %s.\n", serrorfile(f), strerror(errno));
    3607             :                 exit(EXIT_FAILURE);
    3608             :                 /* LCOV_EXCL_STOP */
    3609             :         }
    3610             : #endif
    3611             : 
    3612         151 :         if (sclose(f) != 0) {
    3613             :                 /* LCOV_EXCL_START */
    3614             :                 log_fatal("Error closing the content file. %s.\n", strerror(errno));
    3615             :                 exit(EXIT_FAILURE);
    3616             :                 /* LCOV_EXCL_STOP */
    3617             :         }
    3618             : 
    3619         151 :         crc = context->crc;
    3620         151 :         count_file = context->count_file;
    3621         151 :         count_hardlink = context->count_hardlink;
    3622         151 :         count_symlink = context->count_symlink;
    3623         151 :         count_dir = context->count_dir;
    3624             : 
    3625         151 :         free(context);
    3626             : #endif
    3627             : 
    3628         151 :         msg_verbose("%8u files\n", count_file);
    3629         151 :         msg_verbose("%8u hardlinks\n", count_hardlink);
    3630         151 :         msg_verbose("%8u symlinks\n", count_symlink);
    3631         151 :         msg_verbose("%8u empty dirs\n", count_dir);
    3632             : 
    3633         151 :         *out_crc = crc;
    3634         151 : }
    3635             : 
    3636         261 : void state_read(struct snapraid_state* state)
    3637             : {
    3638             :         STREAM* f;
    3639             :         char path[PATH_MAX];
    3640             :         struct stat st;
    3641             :         tommy_node* node;
    3642             :         int ret;
    3643             :         int c;
    3644             : 
    3645             :         /* iterate over all the available content files and load the first one present */
    3646         261 :         f = 0;
    3647         261 :         node = tommy_list_head(&state->contentlist);
    3648         572 :         while (node) {
    3649         304 :                 struct snapraid_content* content = node->data;
    3650         304 :                 pathcpy(path, sizeof(path), content->content);
    3651             : 
    3652         304 :                 if (!state->no_conf) {
    3653         303 :                         log_tag("content:%s\n", path);
    3654         303 :                         log_flush();
    3655             :                 }
    3656         304 :                 msg_progress("Loading state from %s...\n", path);
    3657             : 
    3658         304 :                 f = sopen_read(path);
    3659         304 :                 if (f != 0) {
    3660             :                         /* if opened stop the search */
    3661         254 :                         break;
    3662             :                 } else {
    3663             :                         /* if it's real error of an existing file, abort */
    3664          50 :                         if (errno != ENOENT) {
    3665             :                                 /* LCOV_EXCL_START */
    3666             :                                 log_fatal("Error opening the content file '%s'. %s.\n", path, strerror(errno));
    3667             :                                 exit(EXIT_FAILURE);
    3668             :                                 /* LCOV_EXCL_STOP */
    3669             :                         }
    3670             : 
    3671             :                         /* otherwise continue */
    3672          50 :                         if (node->next) {
    3673          43 :                                 log_fatal("WARNING! Content file '%s' not found, trying with another copy...\n", path);
    3674             : 
    3675             :                                 /* ensure to rewrite all the content files */
    3676          43 :                                 state->need_write = 1;
    3677             :                         }
    3678             :                 }
    3679             : 
    3680             :                 /* next content file */
    3681          50 :                 node = node->next;
    3682             :         }
    3683             : 
    3684             :         /* if not found, assume empty */
    3685         261 :         if (!f) {
    3686           7 :                 log_fatal("No content file found. Assuming empty.\n");
    3687             : 
    3688             :                 /* create the initial mapping */
    3689           7 :                 state_map(state);
    3690           7 :                 return;
    3691             :         }
    3692             : 
    3693             :         /* get the stat of the content file */
    3694         254 :         ret = fstat(shandle(f), &st);
    3695         254 :         if (ret != 0) {
    3696             :                 /* LCOV_EXCL_START */
    3697             :                 log_fatal("Error stating the content file '%s'. %s.\n", path, strerror(errno));
    3698             :                 exit(EXIT_FAILURE);
    3699             :                 /* LCOV_EXCL_STOP */
    3700             :         }
    3701             : 
    3702             :         /* go further to check other content files */
    3703        1955 :         while (node) {
    3704             :                 char other_path[PATH_MAX];
    3705             :                 struct stat other_st;
    3706        1447 :                 struct snapraid_content* content = node->data;
    3707        1447 :                 pathcpy(other_path, sizeof(other_path), content->content);
    3708             : 
    3709        1447 :                 ret = stat(other_path, &other_st);
    3710        1447 :                 if (ret != 0) {
    3711             :                         /* allow missing content files, but not any other kind of error */
    3712           1 :                         if (errno != ENOENT) {
    3713             :                                 /* LCOV_EXCL_START */
    3714             :                                 log_fatal("Error stating the content file '%s'. %s.\n", other_path, strerror(errno));
    3715             :                                 exit(EXIT_FAILURE);
    3716             :                                 /* LCOV_EXCL_STOP */
    3717             :                         }
    3718             : 
    3719             :                         /* ensure to rewrite all the content files */
    3720           1 :                         state->need_write = 1;
    3721             :                 } else {
    3722             :                         /* if the size is different */
    3723        1446 :                         if (other_st.st_size != st.st_size) {
    3724          39 :                                 log_fatal("WARNING! Content files '%s' and '%s' have a different size!\n", path, other_path);
    3725          39 :                                 log_fatal("Likely one of the two is broken!\n");
    3726             : 
    3727             :                                 /* ensure to rewrite all the content files */
    3728          39 :                                 state->need_write = 1;
    3729             :                         }
    3730             :                 }
    3731             : 
    3732             :                 /* next content file */
    3733        1447 :                 node = node->next;
    3734             :         }
    3735             : 
    3736             :         /* start with a undefined default. */
    3737             :         /* it's for compatibility with version 1.0 where MD5 was implicit. */
    3738         254 :         state->hash = HASH_UNDEFINED;
    3739             : 
    3740             :         /* start with a zero seed, it was the default in old versions */
    3741         254 :         memset(state->hashseed, 0, HASH_MAX);
    3742             : 
    3743             :         /* previous hash, start with an undefined value */
    3744         254 :         state->prevhash = HASH_UNDEFINED;
    3745             : 
    3746             :         /* intentionally not set the prevhashseed, if used valgrind will warn about it */
    3747             : 
    3748             :         /* get the first char to detect the file type */
    3749         254 :         c = sgetc(f);
    3750         254 :         sungetc(c, f);
    3751             : 
    3752             :         /* guess the file type from the first char */
    3753         254 :         if (c == 'S') {
    3754         254 :                 state_read_content(state, path, f);
    3755             :         } else {
    3756             :                 /* LCOV_EXCL_START */
    3757             :                 log_fatal("From SnapRAID v9.0 the text content file is not supported anymore.\n");
    3758             :                 log_fatal("You have first to upgrade to SnapRAID v8.1 to convert it to binary format.\n");
    3759             :                 exit(EXIT_FAILURE);
    3760             :                 /* LCOV_EXCL_STOP */
    3761             :         }
    3762             : 
    3763         253 :         sclose(f);
    3764             : 
    3765         253 :         if (state->hash == HASH_UNDEFINED) {
    3766             :                 /* LCOV_EXCL_START */
    3767             :                 log_fatal("The checksum to use is not specified.\n");
    3768             :                 log_fatal("This happens because you are likely upgrading from SnapRAID 1.0.\n");
    3769             :                 log_fatal("To use a new SnapRAID you must restart from scratch,\n");
    3770             :                 log_fatal("deleting all the content and parity files.\n");
    3771             :                 exit(EXIT_FAILURE);
    3772             :                 /* LCOV_EXCL_STOP */
    3773             :         }
    3774             : 
    3775             :         /* update the mapping */
    3776         253 :         state_map(state);
    3777             : 
    3778         253 :         state_content_check(state, path);
    3779             : 
    3780             :         /* mark that we read the content file, and it passed all the checks */
    3781         253 :         state->checked_read = 1;
    3782             : }
    3783             : 
    3784             : struct state_verify_thread_context {
    3785             :         struct snapraid_state* state;
    3786             :         struct snapraid_content* content;
    3787             : #if HAVE_MT_VERIFY
    3788             :         pthread_t thread;
    3789             : #else
    3790             :         void* retval;
    3791             : #endif
    3792             :         /* input */
    3793             :         uint32_t crc;
    3794             :         STREAM* f;
    3795             : };
    3796             : 
    3797        1009 : static void* state_verify_thread(void* arg)
    3798             : {
    3799        1009 :         struct state_verify_thread_context* context = arg;
    3800        1009 :         STREAM* f = context->f;
    3801             :         unsigned char buf[4];
    3802             :         uint32_t crc_stored;
    3803             :         uint32_t crc_computed;
    3804             : 
    3805        1009 :         if (sdeplete(f, buf) != 0) {
    3806             :                 /* LCOV_EXCL_START */
    3807             :                 log_fatal("Error flushing the content file '%s'. %s.\n", serrorfile(f), strerror(errno));
    3808             :                 return context;
    3809             :                 /* LCOV_EXCL_STOP */
    3810             :         }
    3811             : 
    3812             :         /* get the stored crc from the last four bytes */
    3813        1012 :         crc_stored = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
    3814             : 
    3815        1012 :         if (crc_stored != context->crc) {
    3816             :                 /* LCOV_EXCL_START */
    3817             :                 log_fatal("DANGER! Wrong stored CRC in '%s'\n", serrorfile(f));
    3818             :                 return context;
    3819             :                 /* LCOV_EXCL_STOP */
    3820             :         }
    3821             : 
    3822             :         /* get the computed crc */
    3823        1012 :         crc_computed = scrc(f);
    3824             : 
    3825             :         /* adjust the stored crc to include itself */
    3826        1012 :         crc_stored = crc32c(crc_stored, buf, 4);
    3827             : 
    3828        1012 :         if (crc_computed != crc_stored) {
    3829             :                 /* LCOV_EXCL_START */
    3830             :                 log_fatal("DANGER! Wrong file CRC in '%s'\n", serrorfile(f));
    3831             :                 return context;
    3832             :                 /* LCOV_EXCL_STOP */
    3833             :         }
    3834             : 
    3835        1012 :         return 0;
    3836             : }
    3837             : 
    3838         151 : static void state_verify_content(struct snapraid_state* state, uint32_t crc)
    3839             : {
    3840             :         tommy_node* i;
    3841             :         int fail;
    3842             : 
    3843             :         /* start all reading threads */
    3844         151 :         i = tommy_list_head(&state->contentlist);
    3845        1314 :         while (i) {
    3846        1012 :                 struct snapraid_content* content = i->data;
    3847             :                 struct state_verify_thread_context* context;
    3848             :                 char tmp[PATH_MAX];
    3849             :                 STREAM* f;
    3850             : 
    3851        1012 :                 msg_progress("Verifying %s...\n", content->content);
    3852             : 
    3853        1012 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    3854             : 
    3855        1012 :                 f = sopen_read(tmp);
    3856        1012 :                 if (f == 0) {
    3857             :                         /* LCOV_EXCL_START */
    3858             :                         log_fatal("Error reopening the content file '%s'. %s.\n", tmp, strerror(errno));
    3859             :                         exit(EXIT_FAILURE);
    3860             :                         /* LCOV_EXCL_STOP */
    3861             :                 }
    3862             : 
    3863             :                 /* allocate the thread context */
    3864        1012 :                 context = malloc_nofail(sizeof(struct state_verify_thread_context));
    3865        1012 :                 content->context = context;
    3866             : 
    3867             :                 /* initialize */
    3868        1012 :                 context->state = state;
    3869        1012 :                 context->content = content;
    3870        1012 :                 context->crc = crc;
    3871        1012 :                 context->f = f;
    3872             : 
    3873             : #if HAVE_MT_VERIFY
    3874        1012 :                 thread_create(&context->thread, 0, state_verify_thread, context);
    3875             : #else
    3876             :                 context->retval = state_verify_thread(context);
    3877             : #endif
    3878             : 
    3879        1012 :                 i = i->next;
    3880             :         }
    3881             : 
    3882             :         /* join all thread */
    3883         151 :         fail = 0;
    3884         151 :         i = tommy_list_head(&state->contentlist);
    3885        1314 :         while (i) {
    3886        1012 :                 struct snapraid_content* content = i->data;
    3887        1012 :                 struct state_verify_thread_context* context = content->context;
    3888             :                 void* retval;
    3889             : 
    3890             : #if HAVE_MT_VERIFY
    3891        1012 :                 thread_join(context->thread, &retval);
    3892             : #else
    3893             :                 retval = context->retval;
    3894             : #endif
    3895        1012 :                 if (retval) {
    3896             :                         /* LCOV_EXCL_START */
    3897             :                         fail = 1;
    3898             :                         /* LCOV_EXCL_STOP */
    3899             :                 } else {
    3900        1012 :                         STREAM* f = context->f;
    3901             : 
    3902        1012 :                         if (sclose(f) != 0) {
    3903             :                                 /* LCOV_EXCL_START */
    3904             :                                 log_fatal("Error closing the content file. %s.\n", strerror(errno));
    3905             :                                 exit(EXIT_FAILURE);
    3906             :                                 /* LCOV_EXCL_STOP */
    3907             :                         }
    3908             :                 }
    3909             : 
    3910        1012 :                 free(context);
    3911             : 
    3912        1012 :                 i = i->next;
    3913             :         }
    3914             : 
    3915             :         /* abort on failure */
    3916         151 :         if (fail) {
    3917             :                 /* LCOV_EXCL_START */
    3918             :                 exit(EXIT_FAILURE);
    3919             :                 /* LCOV_EXCL_STOP */
    3920             :         }
    3921         151 : }
    3922             : 
    3923         151 : static void state_rename_content(struct snapraid_state* state)
    3924             : {
    3925             :         tommy_node* i;
    3926             : 
    3927         151 :         i = tommy_list_head(&state->contentlist);
    3928        1314 :         while (i) {
    3929        1012 :                 struct snapraid_content* content = i->data;
    3930             :                 char tmp[PATH_MAX];
    3931             : 
    3932             :                 /* now renames the just written copy with the correct name */
    3933        1012 :                 pathprint(tmp, sizeof(tmp), "%s.tmp", content->content);
    3934        1012 :                 if (rename(tmp, content->content) != 0) {
    3935             :                         /* LCOV_EXCL_START */
    3936             :                         log_fatal("Error renaming the content file '%s' to '%s' in rename(). %s.\n", tmp, content->content, strerror(errno));
    3937             :                         exit(EXIT_FAILURE);
    3938             :                         /* LCOV_EXCL_STOP */
    3939             :                 }
    3940             : 
    3941        1012 :                 i = i->next;
    3942             :         }
    3943         151 : }
    3944             : 
    3945         151 : void state_write(struct snapraid_state* state)
    3946             : {
    3947             :         uint32_t crc;
    3948             : 
    3949             :         /* write all the content files */
    3950         151 :         state_write_content(state, &crc);
    3951             : 
    3952             :         /* verify the just written files */
    3953         151 :         state_verify_content(state, crc);
    3954             : 
    3955             :         /* rename the new files, over the old ones */
    3956         151 :         state_rename_content(state);
    3957             : 
    3958         151 :         state->need_write = 0; /* no write needed anymore */
    3959         151 :         state->checked_read = 0; /* what we wrote is not checked in read */
    3960         151 : }
    3961             : 
    3962         122 : void state_skip(struct snapraid_state* state)
    3963             : {
    3964             :         tommy_node* i;
    3965             : 
    3966             :         /* for each disk */
    3967         850 :         for (i = state->disklist; i != 0; i = i->next) {
    3968             :                 tommy_node* j;
    3969         728 :                 struct snapraid_disk* disk = i->data;
    3970             : 
    3971         728 :                 if (!disk->skip_access)
    3972         727 :                         continue;
    3973             : 
    3974             :                 /* for each file */
    3975        1885 :                 for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
    3976        1884 :                         struct snapraid_file* file = j->data;
    3977        1884 :                         file_flag_set(file, FILE_IS_EXCLUDED);
    3978             :                 }
    3979             : 
    3980             :                 /* for each link */
    3981          57 :                 for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
    3982          56 :                         struct snapraid_link* slink = j->data;
    3983          56 :                         link_flag_set(slink, FILE_IS_EXCLUDED);
    3984             :                 }
    3985             : 
    3986             :                 /* for each dir */
    3987           1 :                 for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
    3988           0 :                         struct snapraid_dir* dir = j->data;
    3989           0 :                         dir_flag_set(dir, FILE_IS_EXCLUDED);
    3990             :                 }
    3991             :         }
    3992         122 : }
    3993             : 
    3994         122 : void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk, int filter_missing, int filter_error)
    3995             : {
    3996             :         tommy_node* i;
    3997             :         unsigned l;
    3998             : 
    3999             :         /* if no filter, include all */
    4000         122 :         if (!filter_missing && !filter_error && tommy_list_empty(filterlist_file) && tommy_list_empty(filterlist_disk))
    4001         113 :                 return;
    4002             : 
    4003           9 :         msg_progress("Filtering...\n");
    4004             : 
    4005          17 :         for (i = tommy_list_head(filterlist_disk); i != 0; i = i->next) {
    4006           8 :                 struct snapraid_filter* filter = i->data;
    4007           8 :                 msg_verbose("\t%s%s\n", filter->pattern, filter->is_disk ? "//" : "");
    4008             :         }
    4009          12 :         for (i = tommy_list_head(filterlist_file); i != 0; i = i->next) {
    4010           3 :                 struct snapraid_filter* filter = i->data;
    4011           3 :                 msg_verbose("\t%s%s\n", filter->pattern, filter->is_dir ? "/" : "");
    4012             :         }
    4013           9 :         if (filter_missing)
    4014           2 :                 msg_verbose("\t<missing>\n");
    4015           9 :         if (filter_error)
    4016           2 :                 msg_verbose("\t<error>\n");
    4017             : 
    4018             :         /* for each disk */
    4019          63 :         for (i = state->disklist; i != 0; i = i->next) {
    4020             :                 tommy_node* j;
    4021          54 :                 struct snapraid_disk* disk = i->data;
    4022             : 
    4023             :                 /* if we filter for presence, we have to access the disk, so better to print something */
    4024          54 :                 if (filter_missing)
    4025          12 :                         msg_progress("Scanning disk %s...\n", disk->name);
    4026             : 
    4027             :                 /* for each file */
    4028      133944 :                 for (j = tommy_list_head(&disk->filelist); j != 0; j = j->next) {
    4029      133890 :                         struct snapraid_file* file = j->data;
    4030             : 
    4031      133890 :                         if (filter_path(filterlist_disk, 0, disk->name, file->sub) != 0
    4032       80982 :                                 || filter_path(filterlist_file, 0, disk->name, file->sub) != 0
    4033       78138 :                                 || filter_existence(filter_missing, disk->dir, file->sub) != 0
    4034       38646 :                                 || filter_correctness(filter_error, &state->infoarr, disk, file) != 0
    4035             :                         ) {
    4036      109509 :                                 file_flag_set(file, FILE_IS_EXCLUDED);
    4037             :                         }
    4038             :                 }
    4039             : 
    4040             :                 /* for each link */
    4041        4414 :                 for (j = tommy_list_head(&disk->linklist); j != 0; j = j->next) {
    4042        4360 :                         struct snapraid_link* slink = j->data;
    4043             : 
    4044        4360 :                         if (filter_path(filterlist_disk, 0, disk->name, slink->sub) != 0
    4045        2641 :                                 || filter_path(filterlist_file, 0, disk->name, slink->sub) != 0
    4046        2536 :                                 || filter_existence(filter_missing, disk->dir, slink->sub) != 0
    4047             :                         ) {
    4048        3221 :                                 link_flag_set(slink, FILE_IS_EXCLUDED);
    4049             :                         }
    4050             :                 }
    4051             : 
    4052             :                 /* for each empty dir */
    4053          82 :                 for (j = tommy_list_head(&disk->dirlist); j != 0; j = j->next) {
    4054          28 :                         struct snapraid_dir* dir = j->data;
    4055             : 
    4056          28 :                         if (filter_emptydir(filterlist_disk, 0, disk->name, dir->sub) != 0
    4057          17 :                                 || filter_emptydir(filterlist_file, 0, disk->name, dir->sub) != 0
    4058          17 :                                 || filter_existence(filter_missing, disk->dir, dir->sub) != 0
    4059             :                         ) {
    4060          19 :                                 dir_flag_set(dir, FILE_IS_EXCLUDED);
    4061             :                         }
    4062             :                 }
    4063             :         }
    4064             : 
    4065             :         /* if we are filtering by disk, exclude any parity not explicitely included */
    4066           9 :         if (!tommy_list_empty(filterlist_disk)) {
    4067             :                 /* for each parity disk */
    4068          20 :                 for (l = 0; l < state->level; ++l) {
    4069             :                         /* check if the parity is excluded by name */
    4070          16 :                         if (filter_path(filterlist_disk, 0, lev_config_name(l), 0) != 0) {
    4071             :                                 /* excluded the parity from further operation */
    4072          11 :                                 state->parity[l].is_excluded_by_filter = 1;
    4073             :                         }
    4074             :                 }
    4075             :         } else {
    4076             :                 /* if we are filtering by file, exclude all parity */
    4077           5 :                 if (filter_missing || !tommy_list_empty(filterlist_file)) {
    4078             :                         /* for each parity disk */
    4079          16 :                         for (l = 0; l < state->level; ++l) {
    4080          13 :                                 state->parity[l].is_excluded_by_filter = 1;
    4081             :                         }
    4082             :                 }
    4083             :         }
    4084             : }
    4085             : 
    4086         221 : int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, block_off_t blockmax, block_off_t countmax)
    4087             : {
    4088             :         time_t now;
    4089             : 
    4090         221 :         if (state->opt.gui) {
    4091           5 :                 log_tag("run:begin:%u:%u:%u\n", blockstart, blockmax, countmax);
    4092           5 :                 log_flush();
    4093             :         }
    4094             : 
    4095         221 :         now = time(0);
    4096             : 
    4097         221 :         state->progress_whole_start = now;
    4098             : 
    4099         221 :         state->progress_tick = 0;
    4100         221 :         state->progress_ptr = 0;
    4101         221 :         state->progress_wasted = 0;
    4102             : 
    4103             :         /* stop if requested */
    4104         221 :         if (global_interrupt) {
    4105             :                 /* LCOV_EXCL_START */
    4106             :                 if (!state->opt.gui) {
    4107             :                         msg_status("Not starting for interruption\n");
    4108             :                 }
    4109             :                 log_tag("sigint:0: SIGINT received\n");
    4110             :                 log_flush();
    4111             :                 return 0;
    4112             :                 /* LCOV_EXCL_STOP */
    4113             :         }
    4114             : 
    4115         221 :         return 1;
    4116             : }
    4117             : 
    4118         221 : void state_progress_end(struct snapraid_state* state, block_off_t countpos, block_off_t countmax, data_off_t countsize)
    4119             : {
    4120         221 :         if (state->opt.gui) {
    4121           5 :                 log_tag("run:end\n");
    4122           5 :                 log_flush();
    4123         216 :         } else if (countmax == 0) {
    4124          19 :                 msg_status("Nothing to do\n");
    4125             :         } else {
    4126             :                 time_t now;
    4127             :                 time_t elapsed;
    4128             : 
    4129         197 :                 unsigned countsize_MB = (countsize + MEGA - 1) / MEGA;
    4130             : 
    4131         197 :                 now = time(0);
    4132             : 
    4133         197 :                 elapsed = now - state->progress_whole_start - state->progress_wasted;
    4134             : 
    4135         197 :                 msg_bar("%u%% completed, %u MB accessed", countpos * 100 / countmax, countsize_MB);
    4136             : 
    4137         197 :                 msg_bar(" in %u:%02u", (unsigned)(elapsed / 3600), (unsigned)((elapsed % 3600) / 60));
    4138             : 
    4139             :                 /* some extra spaces to ensure to overwrite the latest status line */
    4140         197 :                 msg_bar("    ");
    4141             : 
    4142         197 :                 msg_bar("\n");
    4143         197 :                 msg_flush();
    4144             :         }
    4145         221 : }
    4146             : 
    4147           1 : void state_progress_stop(struct snapraid_state* state)
    4148             : {
    4149             :         time_t now;
    4150             : 
    4151           1 :         now = time(0);
    4152             : 
    4153           1 :         if (!state->opt.gui) {
    4154           1 :                 msg_bar("\n");
    4155           1 :                 msg_flush();
    4156             :         }
    4157             : 
    4158           1 :         state->progress_interruption = now;
    4159           1 : }
    4160             : 
    4161           1 : void state_progress_restart(struct snapraid_state* state)
    4162             : {
    4163             :         time_t now;
    4164             : 
    4165           1 :         now = time(0);
    4166             : 
    4167             :         /* reset the progress counter */
    4168           1 :         state->progress_tick = 0;
    4169           1 :         state->progress_ptr = 0;
    4170             : 
    4171           1 :         if (now >= state->progress_interruption) /* avoid degenerated cases when the clock is manually adjusted */
    4172           1 :                 state->progress_wasted += now - state->progress_interruption;
    4173           1 : }
    4174             : 
    4175             : #define PROGRESS_CLEAR "          "
    4176             : 
    4177             : /**
    4178             :  * Set the latest tick data in the progress vector.
    4179             :  */
    4180         353 : static void state_progress_latest(struct snapraid_state* state)
    4181             : {
    4182             :         tommy_node* i;
    4183             :         unsigned l;
    4184             : 
    4185         353 :         state->progress_tick_misc[state->progress_ptr] = state->tick_misc;
    4186         353 :         state->progress_tick_sched[state->progress_ptr] = state->tick_sched;
    4187         353 :         state->progress_tick_raid[state->progress_ptr] = state->tick_raid;
    4188         353 :         state->progress_tick_hash[state->progress_ptr] = state->tick_hash;
    4189         353 :         state->progress_tick_io[state->progress_ptr] = state->tick_io;
    4190        2465 :         for (i = state->disklist; i != 0; i = i->next) {
    4191        2112 :                 struct snapraid_disk* disk = i->data;
    4192        2112 :                 disk->progress_tick[state->progress_ptr] = disk->tick;
    4193             :         }
    4194        2021 :         for (l = 0; l < state->level; ++l)
    4195        1668 :                 state->parity[l].progress_tick[state->progress_ptr] = state->parity[l].tick;
    4196         353 : }
    4197             : 
    4198             : /**
    4199             :  * Number of columns
    4200             :  */
    4201             : #define GRAPH_COLUMN 68
    4202             : 
    4203             : /**
    4204             :  * Get the reference value, 0 if index is PROGRESS_MAX
    4205             :  */
    4206             : #define ref(map, index) (index < PROGRESS_MAX ? map[index] : 0)
    4207             : 
    4208           2 : static void state_progress_graph(struct snapraid_state* state, struct snapraid_io* io, unsigned current, unsigned oldest)
    4209             : {
    4210             :         uint64_t v;
    4211             :         uint64_t tick_total;
    4212             :         unsigned bar;
    4213             :         tommy_node* i;
    4214             :         unsigned l;
    4215             :         size_t pad;
    4216             : 
    4217           2 :         tick_total = 0;
    4218             : 
    4219           2 :         tick_total += state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
    4220           2 :         tick_total += state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
    4221           2 :         tick_total += state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
    4222           2 :         tick_total += state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
    4223           2 :         tick_total += state->progress_tick_io[current] - ref(state->progress_tick_io, oldest);
    4224           2 :         if (!tick_total)
    4225           0 :                 return;
    4226             : 
    4227           2 :         pad = 4;
    4228          14 :         for (i = state->disklist; i != 0; i = i->next) {
    4229          12 :                 struct snapraid_disk* disk = i->data;
    4230          12 :                 size_t len = strlen(disk->name);
    4231          12 :                 if (pad < len)
    4232           2 :                         pad = len;
    4233             :         }
    4234           9 :         for (l = 0; l < state->level; ++l) {
    4235           7 :                 size_t len = strlen(lev_config_name(l));
    4236           7 :                 if (pad < len)
    4237           3 :                         pad = len;
    4238             :         }
    4239             : 
    4240             :         /* extra space */
    4241           2 :         pad += 1;
    4242             : 
    4243           2 :         if (pad + 30 < GRAPH_COLUMN)
    4244           2 :                 bar = GRAPH_COLUMN - pad;
    4245             :         else
    4246           0 :                 bar = 30;
    4247             : 
    4248           2 :         if (io) {
    4249           1 :                 const char* legend = "cached blocks (instant, more is better)";
    4250             : 
    4251             :                 /* refresh the cached blocks info */
    4252           1 :                 io_refresh(io);
    4253             : 
    4254           1 :                 printf("\n");
    4255             : 
    4256           7 :                 for (i = state->disklist; i != 0; i = i->next) {
    4257           6 :                         struct snapraid_disk* disk = i->data;
    4258           6 :                         v = disk->cached;
    4259           6 :                         printr(disk->name, pad);
    4260           6 :                         printf("%4" PRIu64 " | ", v);
    4261           6 :                         printc('o', v * bar / io->io_max);
    4262           6 :                         printf("\n");
    4263             :                 }
    4264             : 
    4265           2 :                 for (l = 0; l < state->level; ++l) {
    4266           1 :                         v = state->parity[l].cached;
    4267           1 :                         printr(lev_config_name(l), pad);
    4268           1 :                         printf("%4" PRIu64 " | ", v);
    4269           1 :                         printc('o', v * bar / io->io_max);
    4270           1 :                         printf("\n");
    4271             :                 }
    4272             : 
    4273           1 :                 printc(' ', pad);
    4274           1 :                 printf("     |_");
    4275           1 :                 printc('_', bar);
    4276           1 :                 printf("\n");
    4277             : 
    4278           1 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
    4279           1 :                 printf("%s", legend);
    4280           1 :                 printf("\n");
    4281             :         }
    4282             : 
    4283           2 :         printf("\n");
    4284             : 
    4285          14 :         for (i = state->disklist; i != 0; i = i->next) {
    4286          12 :                 struct snapraid_disk* disk = i->data;
    4287          12 :                 v = disk->progress_tick[current] - ref(disk->progress_tick, oldest);
    4288          12 :                 printr(disk->name, pad);
    4289          12 :                 printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4290          12 :                 printc('*', v * bar / tick_total);
    4291          12 :                 printf("\n");
    4292             :         }
    4293             : 
    4294           9 :         for (l = 0; l < state->level; ++l) {
    4295           7 :                 v = state->parity[l].progress_tick[current] - ref(state->parity[l].progress_tick, oldest);
    4296           7 :                 printr(lev_config_name(l), pad);
    4297           7 :                 printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4298           7 :                 printc('*', v * bar / tick_total);
    4299           7 :                 printf("\n");
    4300             :         }
    4301             : 
    4302           2 :         v = state->progress_tick_raid[current] - ref(state->progress_tick_raid, oldest);
    4303           2 :         printr("raid", pad);
    4304           2 :         printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4305           2 :         printc('*', v * bar / tick_total);
    4306           2 :         printf("\n");
    4307             : 
    4308           2 :         v = state->progress_tick_hash[current] - ref(state->progress_tick_hash, oldest);
    4309           2 :         printr("hash", pad);
    4310           2 :         printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4311           2 :         printc('*', v * bar / tick_total);
    4312           2 :         printf("\n");
    4313             : 
    4314           2 :         v = state->progress_tick_sched[current] - ref(state->progress_tick_sched, oldest);
    4315           2 :         printr("sched", pad);
    4316           2 :         printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4317           2 :         printc('*', v * bar / tick_total);
    4318           2 :         printf("\n");
    4319             : 
    4320           2 :         v = state->progress_tick_misc[current] - ref(state->progress_tick_misc, oldest);
    4321           2 :         printr("misc", pad);
    4322           2 :         printf("%3" PRIu64 "%% | ", v * 100 / tick_total);
    4323           2 :         printc('*', v * bar / tick_total);
    4324           2 :         printf("\n");
    4325             : 
    4326           2 :         printc(' ', pad);
    4327           2 :         printf("     |_");
    4328           2 :         printc('_', bar);
    4329           2 :         printf("\n");
    4330             : 
    4331           2 :         if (oldest == PROGRESS_MAX) {
    4332           1 :                 const char* legend = "wait time (total, less is better)";
    4333           1 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend) / 2);
    4334           1 :                 printf("%s", legend);
    4335           1 :                 printf("\n");
    4336             :         } else {
    4337           1 :                 const char* legend_d = "wait time (last %d secs, less is better)";
    4338           1 :                 printc(' ', 5 + pad + 1 + bar / 2 - strlen(legend_d) / 2);
    4339           1 :                 printf(legend_d, PROGRESS_MAX);
    4340           1 :                 printf("\n");
    4341             :         }
    4342             : 
    4343           2 :         printf("\n");
    4344             : }
    4345             : 
    4346     1073464 : 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)
    4347             : {
    4348             :         time_t now;
    4349             :         int pred;
    4350             : 
    4351     1073464 :         now = time(0);
    4352             : 
    4353             :         /* previous position */
    4354     1073464 :         pred = state->progress_ptr + PROGRESS_MAX - 1;
    4355     1073464 :         if (pred >= PROGRESS_MAX)
    4356     1073265 :                 pred -= PROGRESS_MAX;
    4357             : 
    4358             :         /* if the previous measure is different */
    4359     1073464 :         if (state->progress_tick == 0
    4360     1073265 :                 || state->progress_time[pred] != now
    4361             :         ) {
    4362             :                 time_t elapsed;
    4363         257 :                 unsigned out_perc = 0;
    4364         257 :                 unsigned out_speed = 0;
    4365         257 :                 unsigned out_cpu = 0;
    4366         257 :                 unsigned out_eta = 0;
    4367         257 :                 int out_computed = 0;
    4368             : 
    4369             :                 /* store the new measure */
    4370         257 :                 state->progress_time[state->progress_ptr] = now;
    4371         257 :                 state->progress_pos[state->progress_ptr] = countpos;
    4372         257 :                 state->progress_size[state->progress_ptr] = countsize;
    4373         257 :                 state_progress_latest(state);
    4374             : 
    4375         257 :                 elapsed = now - state->progress_whole_start - state->progress_wasted;
    4376             : 
    4377             :                 /* completion percentage */
    4378         257 :                 if (countmax)
    4379         257 :                         out_perc = countpos * 100 / countmax;
    4380             : 
    4381             :                 /* if we have at least 5 measures */
    4382         257 :                 if (state->progress_tick >= 5
    4383             :                         /* or if we are running in test mode, with at least one measure */
    4384         257 :                         || (state->opt.force_progress && state->progress_tick >= 1)
    4385             :                 ) {
    4386             :                         int oldest;
    4387             :                         int past;
    4388             :                         time_t delta_time;
    4389             :                         block_off_t delta_pos;
    4390             :                         data_off_t delta_size;
    4391             :                         uint64_t tick_cpu;
    4392             :                         uint64_t tick_total;
    4393             :                         uint64_t delta_tick_cpu;
    4394             :                         uint64_t delta_tick_total;
    4395             :                         uint64_t oldest_tick_cpu;
    4396             :                         uint64_t oldest_tick_total;
    4397             : 
    4398             :                         /* number of past measures */
    4399          58 :                         past = state->progress_tick;
    4400             : 
    4401             :                         /* drop the oldest ones, to promptly */
    4402             :                         /* skip the startup phase */
    4403          58 :                         past -= past / 5;
    4404             : 
    4405             :                         /* check how much we can go in the past */
    4406          58 :                         if (past >= PROGRESS_MAX - 1) {
    4407             :                                 /* the vector is filled, so we are already in position */
    4408             :                                 /* to get the possible oldest one */
    4409           0 :                                 oldest = state->progress_ptr + 1;
    4410             :                         } else {
    4411             :                                 /* go backward the number of positions selected */
    4412          58 :                                 oldest = state->progress_ptr + PROGRESS_MAX - past;
    4413             :                         }
    4414          58 :                         if (oldest >= PROGRESS_MAX)
    4415          58 :                                 oldest -= PROGRESS_MAX;
    4416             : 
    4417         116 :                         tick_cpu = state->progress_tick_misc[state->progress_ptr]
    4418          58 :                                 + state->progress_tick_sched[state->progress_ptr]
    4419          58 :                                 + state->progress_tick_raid[state->progress_ptr]
    4420          58 :                                 + state->progress_tick_hash[state->progress_ptr];
    4421          58 :                         tick_total = tick_cpu + state->progress_tick_io[state->progress_ptr];
    4422             : 
    4423         116 :                         oldest_tick_cpu = state->progress_tick_misc[oldest]
    4424          58 :                                 + state->progress_tick_sched[oldest]
    4425          58 :                                 + state->progress_tick_raid[oldest]
    4426          58 :                                 + state->progress_tick_hash[oldest];
    4427          58 :                         oldest_tick_total = oldest_tick_cpu + state->progress_tick_io[oldest];
    4428             : 
    4429          58 :                         delta_time = now - state->progress_time[oldest];
    4430          58 :                         delta_pos = countpos - state->progress_pos[oldest];
    4431          58 :                         delta_size = countsize - state->progress_size[oldest];
    4432          58 :                         delta_tick_cpu = tick_cpu - oldest_tick_cpu;
    4433          58 :                         delta_tick_total = tick_total - oldest_tick_total;
    4434             : 
    4435             :                         /* estimate the speed in MB/s */
    4436          58 :                         if (delta_time != 0)
    4437          58 :                                 out_speed = (unsigned)(delta_size / MEGA / delta_time);
    4438             : 
    4439             :                         /* estimate the cpu usage percentage */
    4440          58 :                         if (delta_tick_total != 0)
    4441          10 :                                 out_cpu = (unsigned)(delta_tick_cpu * 100U / delta_tick_total);
    4442             : 
    4443             :                         /* estimate the remaining time in minutes */
    4444          58 :                         if (delta_pos != 0)
    4445          58 :                                 out_eta = (countmax - countpos) * delta_time / (60 * delta_pos);
    4446             : 
    4447          58 :                         if (state->opt.force_stats) {
    4448           1 :                                 os_clear();
    4449           1 :                                 state_progress_graph(state, io, state->progress_ptr, oldest);
    4450             :                         }
    4451             : 
    4452             :                         /* we have the output value */
    4453          58 :                         out_computed = 1;
    4454             :                 }
    4455             : 
    4456         257 :                 if (state->opt.gui) {
    4457           6 :                         log_tag("run:pos:%u:%u:%" PRIu64 ":%u:%u:%u:%u:%" PRIu64 "\n", blockpos, countpos, countsize, out_perc, out_eta, out_speed, out_cpu, (uint64_t)elapsed);
    4458           6 :                         log_flush();
    4459             :                 } else {
    4460         251 :                         msg_bar("%u%%, %u MB", out_perc, (unsigned)(countsize / MEGA));
    4461         251 :                         if (out_computed) {
    4462          57 :                                 msg_bar(", %u MB/s", out_speed);
    4463          57 :                                 msg_bar(", CPU %u%%", out_cpu);
    4464          57 :                                 msg_bar(", %u:%02u ETA", out_eta / 60, out_eta % 60);
    4465             :                         }
    4466         251 :                         msg_bar("%s\r", PROGRESS_CLEAR);
    4467         251 :                         msg_flush();
    4468             :                 }
    4469             : 
    4470             :                 /* next position to fill */
    4471         257 :                 ++state->progress_ptr;
    4472         257 :                 if (state->progress_ptr >= PROGRESS_MAX)
    4473           0 :                         state->progress_ptr -= PROGRESS_MAX;
    4474             : 
    4475             :                 /* one more measure */
    4476         257 :                 ++state->progress_tick;
    4477             :         }
    4478             : 
    4479             :         /* stop if requested */
    4480     1073464 :         if (global_interrupt) {
    4481             :                 /* LCOV_EXCL_START */
    4482             :                 if (!state->opt.gui) {
    4483             :                         log_fatal("\n");
    4484             :                         log_fatal("Stopping for interruption at block %u\n", blockpos);
    4485             :                 }
    4486             :                 log_tag("sigint:%u: SIGINT received\n", blockpos);
    4487             :                 log_flush();
    4488             :                 return 1;
    4489             :                 /* LCOV_EXCL_STOP */
    4490             :         }
    4491             : 
    4492     1073464 :         return 0;
    4493             : }
    4494             : 
    4495         104 : void state_usage_waste(struct snapraid_state* state)
    4496             : {
    4497         104 :         uint64_t now = tick();
    4498             : 
    4499         104 :         state->tick_last = now;
    4500         104 : }
    4501             : 
    4502     1063434 : void state_usage_misc(struct snapraid_state* state)
    4503             : {
    4504     1063434 :         uint64_t now = tick();
    4505     1063434 :         uint64_t delta = now - state->tick_last;
    4506             : 
    4507             :         /* increment the time spent in computations */
    4508     1063434 :         state->tick_misc += delta;
    4509             : 
    4510     1063434 :         state->tick_last = now;
    4511     1063434 : }
    4512             : 
    4513      149890 : void state_usage_sched(struct snapraid_state* state)
    4514             : {
    4515      149890 :         uint64_t now = tick();
    4516      149890 :         uint64_t delta = now - state->tick_last;
    4517             : 
    4518             :         /* increment the time spent in computations */
    4519      149890 :         state->tick_sched += delta;
    4520             : 
    4521      149890 :         state->tick_last = now;
    4522      149890 : }
    4523             : 
    4524      140325 : void state_usage_raid(struct snapraid_state* state)
    4525             : {
    4526      140325 :         uint64_t now = tick();
    4527      140325 :         uint64_t delta = now - state->tick_last;
    4528             : 
    4529             :         /* increment the time spent in computations */
    4530      140325 :         state->tick_raid += delta;
    4531             : 
    4532      140325 :         state->tick_last = now;
    4533      140325 : }
    4534             : 
    4535      777134 : void state_usage_hash(struct snapraid_state* state)
    4536             : {
    4537      777134 :         uint64_t now = tick();
    4538      777134 :         uint64_t delta = now - state->tick_last;
    4539             : 
    4540             :         /* increment the time spent in computations */
    4541      777134 :         state->tick_hash += delta;
    4542             : 
    4543      777134 :         state->tick_last = now;
    4544      777134 : }
    4545             : 
    4546      912681 : void state_usage_disk(struct snapraid_state* state, struct snapraid_handle* handle_map, unsigned* waiting_map, unsigned waiting_mac)
    4547             : {
    4548      912681 :         uint64_t now = tick();
    4549      912681 :         uint64_t delta = now - state->tick_last;
    4550             :         unsigned i;
    4551             : 
    4552             :         /* increment the time spent in the data disks */
    4553      956391 :         for (i = 0; i < waiting_mac; ++i) {
    4554       43710 :                 struct snapraid_disk* disk = handle_map[waiting_map[i]].disk;
    4555             : 
    4556       43710 :                 if (!disk)
    4557           2 :                         continue;
    4558             : 
    4559       43708 :                 disk->tick += delta;
    4560             :         }
    4561      912681 :         state->tick_io += delta;
    4562             : 
    4563      912681 :         state->tick_last = now;
    4564      912681 : }
    4565             : 
    4566      774353 : void state_usage_parity(struct snapraid_state* state, unsigned* waiting_map, unsigned waiting_mac)
    4567             : {
    4568      774353 :         uint64_t now = tick();
    4569      774353 :         uint64_t delta = now - state->tick_last;
    4570             :         unsigned i;
    4571             : 
    4572             :         /* increment the time spent in the parity disk */
    4573      784293 :         for (i = 0; i < waiting_mac; ++i)
    4574        9940 :                 state->parity[waiting_map[i]].tick += delta;
    4575      774353 :         state->tick_io += delta;
    4576             : 
    4577      774353 :         state->tick_last = now;
    4578      774353 : }
    4579             : 
    4580          96 : void state_usage_print(struct snapraid_state* state)
    4581             : {
    4582             :         /* set the latest data */
    4583          96 :         state_progress_latest(state);
    4584             : 
    4585          96 :         if (msg_level < MSG_PROGRESS)
    4586          95 :                 return;
    4587             : 
    4588             :         /* print a graph for it */
    4589           1 :         state_progress_graph(state, 0, state->progress_ptr, PROGRESS_MAX);
    4590             : }
    4591             : 
    4592         499 : void state_fscheck(struct snapraid_state* state, const char* ope)
    4593             : {
    4594             :         tommy_node* i;
    4595             : 
    4596             :         /* check the file-system on all disks */
    4597        3487 :         for (i = state->disklist; i != 0; i = i->next) {
    4598        2988 :                 struct snapraid_disk* disk = i->data;
    4599             : 
    4600        2988 :                 if (fs_check(disk) != 0) {
    4601             :                         /* LCOV_EXCL_START */
    4602             :                         log_fatal("Internal inconsistency in file-system for disk '%s' %s\n", disk->name, ope);
    4603             :                         os_abort();
    4604             :                         /* LCOV_EXCL_STOP */
    4605             :                 }
    4606             :         }
    4607         499 : }
    4608             : 
    4609           1 : void generate_configuration(const char* path)
    4610             : {
    4611             :         struct snapraid_state state;
    4612             :         struct snapraid_content* content;
    4613             :         char esc_buffer[ESC_MAX];
    4614             :         unsigned l, s;
    4615             :         tommy_node* j;
    4616             : 
    4617           1 :         state_init(&state);
    4618             : 
    4619             :         /* mark that we are without a configuration file */
    4620           1 :         state.no_conf = 1;
    4621             : 
    4622             :         /* create the dummy content entry */
    4623           1 :         content = content_alloc(path, -1);
    4624             : 
    4625             :         /* adds the content entry */
    4626           1 :         tommy_list_insert_tail(&state.contentlist, &content->node, content);
    4627             : 
    4628             :         /* read the content file */
    4629           1 :         state_read(&state);
    4630             : 
    4631             :         /* output a dummy configuration file */
    4632           1 :         printf("# Configuration file generated from %s\n", path);
    4633           1 :         printf("\n");
    4634           1 :         printf("# Use this blocksize\n");
    4635           1 :         printf("blocksize %u\n", state.block_size / KIBI);
    4636           1 :         printf("\n");
    4637           1 :         printf("# Use this hashsize\n");
    4638           1 :         printf("hashsize %u\n", BLOCK_HASH_SIZE);
    4639           1 :         printf("\n");
    4640           7 :         for (l = 0; l < state.level; ++l) {
    4641           6 :                 printf("# Set the correct path for the %s files\n", lev_name(l));
    4642           6 :                 printf("# You had %u of them:\n", state.parity[l].split_mac);
    4643          30 :                 for (s = 0; s < state.parity[l].split_mac; ++s) {
    4644          24 :                         printf("# %u:\n", s);
    4645          24 :                         printf("# PATH:");
    4646          24 :                         if (state.parity[l].split_map[s].path[0])
    4647          24 :                                 printf("%s", state.parity[l].split_map[s].path);
    4648             :                         else
    4649           0 :                                 printf("?");
    4650          24 :                         printf("\n");
    4651          24 :                         printf("# SIZE:");
    4652          24 :                         if (state.parity[l].split_map[s].size != PARITY_SIZE_INVALID)
    4653          24 :                                 printf("%" PRIu64, state.parity[l].split_map[s].size);
    4654             :                         else
    4655           0 :                                 printf("?");
    4656          24 :                         printf("\n");
    4657          24 :                         printf("# UUID:");
    4658          24 :                         if (state.parity[l].split_map[s].uuid[0])
    4659          24 :                                 printf("%s", state.parity[l].split_map[s].uuid);
    4660             :                         else
    4661           0 :                                 printf("?");
    4662          24 :                         printf("\n");
    4663          24 :                         printf("#\n");
    4664             :                 }
    4665           6 :                 printf("%s ENTER_HERE_THE_PARITY_FILES_COMMA_SEPARATED\n", lev_config_name(l));
    4666           6 :                 printf("\n");
    4667             :         }
    4668           1 :         printf("# Add any other content file\n");
    4669           1 :         printf("content %s\n", path);
    4670           1 :         printf("\n");
    4671           7 :         for (j = state.maplist; j; j = j->next) {
    4672           6 :                 struct snapraid_map* map = j->data;
    4673             :                 struct snapraid_disk* disk;
    4674           6 :                 printf("# Set the correct dir for disk '%s'\n", map->name);
    4675           6 :                 if (map->uuid[0])
    4676           6 :                         printf("# Disk '%s' is the one with id '%s'\n", map->name, map->uuid);
    4677           6 :                 disk = find_disk_by_name(&state, map->name);
    4678           6 :                 if (disk && disk->filelist) {
    4679           6 :                         struct snapraid_file* file = disk->filelist->data;
    4680           6 :                         if (file) {
    4681           6 :                                 printf("# and containing: %s\n", fmt_poll(disk, file->sub, esc_buffer));
    4682             :                         }
    4683             :                 }
    4684           6 :                 printf("data %s ENTER_HERE_THE_DIR\n", map->name);
    4685           6 :                 printf("\n");
    4686             :         }
    4687             : 
    4688           1 :         state_done(&state);
    4689           1 : }
    4690             : 

Generated by: LCOV version 1.13